GraphData refactor

This commit is contained in:
Milos Kozak 2020-04-01 09:07:36 +02:00
parent 56d0d2850d
commit bc5a089c5f
5 changed files with 577 additions and 728 deletions

View file

@ -303,7 +303,7 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity {
// add basal data
if (pump.getPumpDescription().isTempBasalCapable && showBasal) {
graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2d);
graphData.addBasals(fromTime, toTime, lowLine / graphData.getMaxY() / 1.2d);
}
// **** NOW line ****

View file

@ -1463,7 +1463,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList
// add basal data
if (pump.getPumpDescription().isTempBasalCapable && sp.getBoolean("showbasals", true)) {
graphData.addBasals(fromTime, now, lowLine / graphData.maxY / 1.2d);
graphData.addBasals(fromTime, now, lowLine / graphData.getMaxY() / 1.2d);
}
// add target line

View file

@ -1,726 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.graphData;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.series.BarGraphSeries;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
import com.jjoe64.graphview.series.Series;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.ProfileSwitch;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.AreaGraphSeries;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DoubleDataPoint;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TimeAsXAxisLabelFormatter;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.BasalData;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.treatments.Treatment;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
/**
* Created by mike on 18.10.2017.
*/
public class GraphData {
@Inject AAPSLogger aapsLogger;
@Inject ProfileFunction profileFunction;
@Inject ResourceHelper resourceHelper;
@Inject ActivePluginProvider activePlugin;
private GraphView graph;
public double maxY = Double.MIN_VALUE;
private double minY = Double.MAX_VALUE;
private List<BgReading> bgReadingsArray;
private String units;
private List<Series> series = new ArrayList<>();
private TreatmentsInterface treatmentsPlugin;
private IobCobCalculatorPlugin iobCobCalculatorPlugin; // Cannot be injected: HistoryBrowser
public GraphData(HasAndroidInjector injector, GraphView graph, IobCobCalculatorPlugin iobCobCalculatorPlugin) {
injector.androidInjector().inject(this);
units = profileFunction.getUnits();
this.graph = graph;
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
treatmentsPlugin = activePlugin.getActiveTreatments();
}
public void addBgReadings(long fromTime, long toTime, double lowLine, double highLine, List<BgReading> predictions) {
double maxBgValue = Double.MIN_VALUE;
//bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true);
bgReadingsArray = iobCobCalculatorPlugin.getBgReadings();
List<DataPointWithLabelInterface> bgListArray = new ArrayList<>();
if (bgReadingsArray == null || bgReadingsArray.size() == 0) {
aapsLogger.debug(LTag.OVERVIEW, "No BG data.");
maxY = 10;
minY = 0;
return;
}
for (BgReading bg : bgReadingsArray) {
if (bg.date < fromTime || bg.date > toTime) continue;
if (bg.value > maxBgValue) maxBgValue = bg.value;
bgListArray.add(bg);
}
if (predictions != null) {
Collections.sort(predictions, (o1, o2) -> Double.compare(o1.getX(), o2.getX()));
for (BgReading prediction : predictions) {
if (prediction.value >= 40)
bgListArray.add(prediction);
}
}
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units);
maxBgValue = units.equals(Constants.MGDL) ? Round.roundTo(maxBgValue, 40d) + 80 : Round.roundTo(maxBgValue, 2d) + 4;
if (highLine > maxBgValue) maxBgValue = highLine;
int numOfVertLines = units.equals(Constants.MGDL) ? (int) (maxBgValue / 40 + 1) : (int) (maxBgValue / 2 + 1);
DataPointWithLabelInterface[] bg = new DataPointWithLabelInterface[bgListArray.size()];
bg = bgListArray.toArray(bg);
maxY = maxBgValue;
minY = 0;
// set manual y bounds to have nice steps
graph.getGridLabelRenderer().setNumVerticalLabels(numOfVertLines);
addSeries(new PointsWithLabelGraphSeries<>(bg));
}
public void addInRangeArea(long fromTime, long toTime, double lowLine, double highLine) {
AreaGraphSeries<DoubleDataPoint> inRangeAreaSeries;
DoubleDataPoint[] inRangeAreaDataPoints = new DoubleDataPoint[]{
new DoubleDataPoint(fromTime, lowLine, highLine),
new DoubleDataPoint(toTime, lowLine, highLine)
};
inRangeAreaSeries = new AreaGraphSeries<>(inRangeAreaDataPoints);
inRangeAreaSeries.setColor(0);
inRangeAreaSeries.setDrawBackground(true);
inRangeAreaSeries.setBackgroundColor(resourceHelper.gc(R.color.inrangebackground));
addSeries(inRangeAreaSeries);
}
// scale in % of vertical size (like 0.3)
public void addBasals(long fromTime, long toTime, double scale) {
LineGraphSeries<ScaledDataPoint> basalsLineSeries;
LineGraphSeries<ScaledDataPoint> absoluteBasalsLineSeries;
LineGraphSeries<ScaledDataPoint> baseBasalsSeries;
LineGraphSeries<ScaledDataPoint> tempBasalsSeries;
double maxBasalValueFound = 0d;
Scale basalScale = new Scale();
List<ScaledDataPoint> baseBasalArray = new ArrayList<>();
List<ScaledDataPoint> tempBasalArray = new ArrayList<>();
List<ScaledDataPoint> basalLineArray = new ArrayList<>();
List<ScaledDataPoint> absoluteBasalLineArray = new ArrayList<>();
double lastLineBasal = 0;
double lastAbsoluteLineBasal = -1;
double lastBaseBasal = 0;
double lastTempBasal = 0;
for (long time = fromTime; time < toTime; time += 60 * 1000L) {
Profile profile = profileFunction.getProfile(time);
if (profile == null) continue;
BasalData basalData = iobCobCalculatorPlugin.getBasalData(profile, time);
double baseBasalValue = basalData.basal;
double absoluteLineValue = baseBasalValue;
double tempBasalValue = 0;
double basal = 0d;
if (basalData.isTempBasalRunning) {
absoluteLineValue = tempBasalValue = basalData.tempBasalAbsolute;
if (tempBasalValue != lastTempBasal) {
tempBasalArray.add(new ScaledDataPoint(time, lastTempBasal, basalScale));
tempBasalArray.add(new ScaledDataPoint(time, basal = tempBasalValue, basalScale));
}
if (lastBaseBasal != 0d) {
baseBasalArray.add(new ScaledDataPoint(time, lastBaseBasal, basalScale));
baseBasalArray.add(new ScaledDataPoint(time, 0d, basalScale));
lastBaseBasal = 0d;
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(new ScaledDataPoint(time, lastBaseBasal, basalScale));
baseBasalArray.add(new ScaledDataPoint(time, basal = baseBasalValue, basalScale));
lastBaseBasal = baseBasalValue;
}
if (lastTempBasal != 0) {
tempBasalArray.add(new ScaledDataPoint(time, lastTempBasal, basalScale));
tempBasalArray.add(new ScaledDataPoint(time, 0d, basalScale));
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(new ScaledDataPoint(time, lastLineBasal, basalScale));
basalLineArray.add(new ScaledDataPoint(time, baseBasalValue, basalScale));
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(new ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale));
absoluteBasalLineArray.add(new ScaledDataPoint(time, basal, basalScale));
}
lastAbsoluteLineBasal = absoluteLineValue;
lastLineBasal = baseBasalValue;
lastTempBasal = tempBasalValue;
maxBasalValueFound = Math.max(maxBasalValueFound, Math.max(tempBasalValue, baseBasalValue));
}
basalLineArray.add(new ScaledDataPoint(toTime, lastLineBasal, basalScale));
baseBasalArray.add(new ScaledDataPoint(toTime, lastBaseBasal, basalScale));
tempBasalArray.add(new ScaledDataPoint(toTime, lastTempBasal, basalScale));
absoluteBasalLineArray.add(new ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale));
ScaledDataPoint[] baseBasal = new ScaledDataPoint[baseBasalArray.size()];
baseBasal = baseBasalArray.toArray(baseBasal);
baseBasalsSeries = new LineGraphSeries<>(baseBasal);
baseBasalsSeries.setDrawBackground(true);
baseBasalsSeries.setBackgroundColor(resourceHelper.gc(R.color.basebasal));
baseBasalsSeries.setThickness(0);
ScaledDataPoint[] tempBasal = new ScaledDataPoint[tempBasalArray.size()];
tempBasal = tempBasalArray.toArray(tempBasal);
tempBasalsSeries = new LineGraphSeries<>(tempBasal);
tempBasalsSeries.setDrawBackground(true);
tempBasalsSeries.setBackgroundColor(resourceHelper.gc(R.color.tempbasal));
tempBasalsSeries.setThickness(0);
ScaledDataPoint[] basalLine = new ScaledDataPoint[basalLineArray.size()];
basalLine = basalLineArray.toArray(basalLine);
basalsLineSeries = new LineGraphSeries<>(basalLine);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(resourceHelper.getDisplayMetrics().scaledDensity * 2);
paint.setPathEffect(new DashPathEffect(new float[]{2, 4}, 0));
paint.setColor(resourceHelper.gc(R.color.basal));
basalsLineSeries.setCustomPaint(paint);
ScaledDataPoint[] absoluteBasalLine = new ScaledDataPoint[absoluteBasalLineArray.size()];
absoluteBasalLine = absoluteBasalLineArray.toArray(absoluteBasalLine);
absoluteBasalsLineSeries = new LineGraphSeries<>(absoluteBasalLine);
Paint absolutePaint = new Paint();
absolutePaint.setStyle(Paint.Style.STROKE);
absolutePaint.setStrokeWidth(resourceHelper.getDisplayMetrics().scaledDensity * 2);
absolutePaint.setColor(resourceHelper.gc(R.color.basal));
absoluteBasalsLineSeries.setCustomPaint(absolutePaint);
basalScale.setMultiplier(maxY * scale / maxBasalValueFound);
addSeries(baseBasalsSeries);
addSeries(tempBasalsSeries);
addSeries(basalsLineSeries);
addSeries(absoluteBasalsLineSeries);
}
public void addTargetLine(long fromTime, long toTime, Profile profile, LoopPlugin.LastRun lastRun) {
LineGraphSeries<DataPoint> targetsSeries;
Scale targetsScale = new Scale();
targetsScale.setMultiplier(1);
List<DataPoint> targetsSeriesArray = new ArrayList<>();
double lastTarget = -1;
if (lastRun != null && lastRun.constraintsProcessed != null) {
APSResult apsResult = lastRun.constraintsProcessed;
long latestPredictionsTime = apsResult.getLatestPredictionsTime();
if (latestPredictionsTime > toTime) {
toTime = latestPredictionsTime;
}
}
for (long time = fromTime; time < toTime; time += 5 * 60 * 1000L) {
TempTarget tt = treatmentsPlugin.getTempTargetFromHistory(time);
double value;
if (tt == null) {
value = Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, profileFunction.getUnits());
} else {
value = Profile.fromMgdlToUnits(tt.target(), profileFunction.getUnits());
}
if (lastTarget != value) {
if (lastTarget != -1)
targetsSeriesArray.add(new DataPoint(time, lastTarget));
targetsSeriesArray.add(new DataPoint(time, value));
}
lastTarget = value;
}
targetsSeriesArray.add(new DataPoint(toTime, lastTarget));
DataPoint[] targets = new DataPoint[targetsSeriesArray.size()];
targets = targetsSeriesArray.toArray(targets);
targetsSeries = new LineGraphSeries<>(targets);
targetsSeries.setDrawBackground(false);
targetsSeries.setColor(resourceHelper.gc(R.color.tempTargetBackground));
targetsSeries.setThickness(2);
addSeries(targetsSeries);
}
public void addTreatments(long fromTime, long endTime) {
List<DataPointWithLabelInterface> filteredTreatments = new ArrayList<>();
List<Treatment> treatments = treatmentsPlugin.getTreatmentsFromHistory();
for (int tx = 0; tx < treatments.size(); tx++) {
Treatment t = treatments.get(tx);
if (t.getX() < fromTime || t.getX() > endTime) continue;
if (t.isSMB && !t.isValid) continue;
t.setY(getNearestBg((long) t.getX()));
filteredTreatments.add(t);
}
// ProfileSwitch
List<ProfileSwitch> profileSwitches = treatmentsPlugin.getProfileSwitchesFromHistory().getList();
for (int tx = 0; tx < profileSwitches.size(); tx++) {
DataPointWithLabelInterface t = profileSwitches.get(tx);
if (t.getX() < fromTime || t.getX() > endTime) continue;
filteredTreatments.add(t);
}
// Extended bolus
if (!activePlugin.getActivePump().isFakingTempsByExtendedBoluses()) {
List<ExtendedBolus> extendedBoluses = treatmentsPlugin.getExtendedBolusesFromHistory().getList();
for (int tx = 0; tx < extendedBoluses.size(); tx++) {
DataPointWithLabelInterface t = extendedBoluses.get(tx);
if (t.getX() + t.getDuration() < fromTime || t.getX() > endTime) continue;
if (t.getDuration() == 0) continue;
t.setY(getNearestBg((long) t.getX()));
filteredTreatments.add(t);
}
}
// Careportal
List<CareportalEvent> careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true);
for (int tx = 0; tx < careportalEvents.size(); tx++) {
DataPointWithLabelInterface t = careportalEvents.get(tx);
if (t.getX() + t.getDuration() < fromTime || t.getX() > endTime) continue;
t.setY(getNearestBg((long) t.getX()));
filteredTreatments.add(t);
}
DataPointWithLabelInterface[] treatmentsArray = new DataPointWithLabelInterface[filteredTreatments.size()];
treatmentsArray = filteredTreatments.toArray(treatmentsArray);
addSeries(new PointsWithLabelGraphSeries<>(treatmentsArray));
}
private double getNearestBg(long date) {
if (bgReadingsArray == null)
return Profile.fromMgdlToUnits(100, units);
for (int r = 0; r < bgReadingsArray.size(); r++) {
BgReading reading = bgReadingsArray.get(r);
if (reading.date > date) continue;
return Profile.fromMgdlToUnits(reading.value, units);
}
return bgReadingsArray.size() > 0
? Profile.fromMgdlToUnits(bgReadingsArray.get(0).value, units) : Profile.fromMgdlToUnits(100, units);
}
public void addActivity(long fromTime, long toTime, boolean useForScale, double scale) {
FixedLineGraphSeries<ScaledDataPoint> actSeriesHist;
List<ScaledDataPoint> actArrayHist = new ArrayList<>();
FixedLineGraphSeries<ScaledDataPoint> actSeriesPred;
List<ScaledDataPoint> actArrayPred = new ArrayList<>();
double now = System.currentTimeMillis();
Scale actScale = new Scale();
IobTotal total;
double maxIAValue = 0;
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
Profile profile = profileFunction.getProfile(time);
double act;
if (profile == null) continue;
total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile);
act = total.activity;
if (time <= now)
actArrayHist.add(new ScaledDataPoint(time, act, actScale));
else
actArrayPred.add(new ScaledDataPoint(time, act, actScale));
maxIAValue = Math.max(maxIAValue, Math.abs(act));
}
ScaledDataPoint[] actData = new ScaledDataPoint[actArrayHist.size()];
actData = actArrayHist.toArray(actData);
actSeriesHist = new FixedLineGraphSeries<>(actData);
actSeriesHist.setDrawBackground(false);
actSeriesHist.setColor(resourceHelper.gc(R.color.activity));
actSeriesHist.setThickness(3);
addSeries(actSeriesHist);
actData = new ScaledDataPoint[actArrayPred.size()];
actData = actArrayPred.toArray(actData);
actSeriesPred = new FixedLineGraphSeries<>(actData);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setPathEffect(new DashPathEffect(new float[]{4, 4}, 0));
paint.setColor(resourceHelper.gc(R.color.activity));
actSeriesPred.setCustomPaint(paint);
if (useForScale) {
maxY = maxIAValue;
minY = -maxIAValue;
}
actScale.setMultiplier(maxY * scale / maxIAValue);
addSeries(actSeriesPred);
}
// scale in % of vertical size (like 0.3)
public void addIob(long fromTime, long toTime, boolean useForScale, double scale, boolean showPrediction) {
FixedLineGraphSeries<ScaledDataPoint> iobSeries;
List<ScaledDataPoint> iobArray = new ArrayList<>();
Double maxIobValueFound = Double.MIN_VALUE;
double lastIob = 0;
Scale iobScale = new Scale();
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
Profile profile = profileFunction.getProfile(time);
double iob = 0d;
if (profile != null)
iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob;
if (Math.abs(lastIob - iob) > 0.02) {
if (Math.abs(lastIob - iob) > 0.2)
iobArray.add(new ScaledDataPoint(time, lastIob, iobScale));
iobArray.add(new ScaledDataPoint(time, iob, iobScale));
maxIobValueFound = Math.max(maxIobValueFound, Math.abs(iob));
lastIob = iob;
}
}
ScaledDataPoint[] iobData = new ScaledDataPoint[iobArray.size()];
iobData = iobArray.toArray(iobData);
iobSeries = new FixedLineGraphSeries<>(iobData);
iobSeries.setDrawBackground(true);
iobSeries.setBackgroundColor(0x80FFFFFF & resourceHelper.gc(R.color.iob)); //50%
iobSeries.setColor(resourceHelper.gc(R.color.iob));
iobSeries.setThickness(3);
if (showPrediction) {
AutosensResult lastAutosensResult;
AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData");
if (autosensData == null)
lastAutosensResult = new AutosensResult();
else
lastAutosensResult = autosensData.autosensResult;
boolean isTempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()) != null;
List<DataPointWithLabelInterface> iobPred = new ArrayList<>();
IobTotal[] iobPredArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget);
for (IobTotal i : iobPredArray) {
iobPred.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)));
maxIobValueFound = Math.max(maxIobValueFound, Math.abs(i.iob));
}
DataPointWithLabelInterface[] iobp = new DataPointWithLabelInterface[iobPred.size()];
iobp = iobPred.toArray(iobp);
addSeries(new PointsWithLabelGraphSeries<>(iobp));
List<DataPointWithLabelInterface> iobPred2 = new ArrayList<>();
IobTotal[] iobPredArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(new AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget);
for (IobTotal i : iobPredArray2) {
iobPred2.add(i.setColor(resourceHelper.gc(R.color.iobPred)));
maxIobValueFound = Math.max(maxIobValueFound, Math.abs(i.iob));
}
DataPointWithLabelInterface[] iobp2 = new DataPointWithLabelInterface[iobPred2.size()];
iobp2 = iobPred2.toArray(iobp2);
addSeries(new PointsWithLabelGraphSeries<>(iobp2));
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray));
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(1) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray2));
}
if (useForScale) {
maxY = maxIobValueFound;
minY = -maxIobValueFound;
}
iobScale.setMultiplier(maxY * scale / maxIobValueFound);
addSeries(iobSeries);
}
// scale in % of vertical size (like 0.3)
public void addCob(long fromTime, long toTime, boolean useForScale, double scale) {
List<DataPointWithLabelInterface> minFailoverActiveList = new ArrayList<>();
FixedLineGraphSeries<ScaledDataPoint> cobSeries;
List<ScaledDataPoint> cobArray = new ArrayList<>();
Double maxCobValueFound = 0d;
int lastCob = 0;
Scale cobScale = new Scale();
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time);
if (autosensData != null) {
int cob = (int) autosensData.cob;
if (cob != lastCob) {
if (autosensData.carbsFromBolus > 0)
cobArray.add(new ScaledDataPoint(time, lastCob, cobScale));
cobArray.add(new ScaledDataPoint(time, cob, cobScale));
maxCobValueFound = Math.max(maxCobValueFound, cob);
lastCob = cob;
}
if (autosensData.failoverToMinAbsorbtionRate) {
autosensData.setScale(cobScale);
autosensData.setChartTime(time);
minFailoverActiveList.add(autosensData);
}
}
}
// COB
ScaledDataPoint[] cobData = new ScaledDataPoint[cobArray.size()];
cobData = cobArray.toArray(cobData);
cobSeries = new FixedLineGraphSeries<>(cobData);
cobSeries.setDrawBackground(true);
cobSeries.setBackgroundColor(0x80FFFFFF & resourceHelper.gc(R.color.cob)); //50%
cobSeries.setColor(resourceHelper.gc(R.color.cob));
cobSeries.setThickness(3);
if (useForScale) {
maxY = maxCobValueFound;
minY = 0;
}
cobScale.setMultiplier(maxY * scale / maxCobValueFound);
addSeries(cobSeries);
DataPointWithLabelInterface[] minFailover = new DataPointWithLabelInterface[minFailoverActiveList.size()];
minFailover = minFailoverActiveList.toArray(minFailover);
addSeries(new PointsWithLabelGraphSeries<>(minFailover));
}
// scale in % of vertical size (like 0.3)
public void addDeviations(long fromTime, long toTime, boolean useForScale, double scale) {
class DeviationDataPoint extends ScaledDataPoint {
public int color;
private DeviationDataPoint(double x, double y, int color, Scale scale) {
super(x, y, scale);
this.color = color;
}
}
BarGraphSeries<DeviationDataPoint> devSeries;
List<DeviationDataPoint> devArray = new ArrayList<>();
Double maxDevValueFound = 0d;
Scale devScale = new Scale();
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time);
if (autosensData != null) {
int color = resourceHelper.gc(R.color.deviationblack); // "="
if (autosensData.type.equals("") || autosensData.type.equals("non-meal")) {
if (autosensData.pastSensitivity.equals("C"))
color = resourceHelper.gc(R.color.deviationgrey);
if (autosensData.pastSensitivity.equals("+"))
color = resourceHelper.gc(R.color.deviationgreen);
if (autosensData.pastSensitivity.equals("-"))
color = resourceHelper.gc(R.color.deviationred);
} else if (autosensData.type.equals("uam")) {
color = resourceHelper.gc(R.color.uam);
} else if (autosensData.type.equals("csf")) {
color = resourceHelper.gc(R.color.deviationgrey);
}
devArray.add(new DeviationDataPoint(time, autosensData.deviation, color, devScale));
maxDevValueFound = Math.max(maxDevValueFound, Math.abs(autosensData.deviation));
}
}
// DEVIATIONS
DeviationDataPoint[] devData = new DeviationDataPoint[devArray.size()];
devData = devArray.toArray(devData);
devSeries = new BarGraphSeries<>(devData);
devSeries.setValueDependentColor(data -> data.color);
if (useForScale) {
maxY = maxDevValueFound;
minY = -maxY;
}
devScale.setMultiplier(maxY * scale / maxDevValueFound);
addSeries(devSeries);
}
// scale in % of vertical size (like 0.3)
public void addRatio(long fromTime, long toTime, boolean useForScale, double scale) {
LineGraphSeries<ScaledDataPoint> ratioSeries;
List<ScaledDataPoint> ratioArray = new ArrayList<>();
double maxRatioValueFound = Double.MIN_VALUE;
double minRatioValueFound = Double.MAX_VALUE;
Scale ratioScale = new Scale();
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time);
if (autosensData != null) {
ratioArray.add(new ScaledDataPoint(time, autosensData.autosensResult.ratio - 1, ratioScale));
maxRatioValueFound = Math.max(maxRatioValueFound, autosensData.autosensResult.ratio - 1);
minRatioValueFound = Math.min(minRatioValueFound, autosensData.autosensResult.ratio - 1);
}
}
// RATIOS
ScaledDataPoint[] ratioData = new ScaledDataPoint[ratioArray.size()];
ratioData = ratioArray.toArray(ratioData);
ratioSeries = new LineGraphSeries<>(ratioData);
ratioSeries.setColor(resourceHelper.gc(R.color.ratio));
ratioSeries.setThickness(3);
if (useForScale) {
maxY = Math.max(maxRatioValueFound, Math.abs(minRatioValueFound));
minY = -maxY;
}
ratioScale.setMultiplier(maxY * scale / Math.max(maxRatioValueFound, Math.abs(minRatioValueFound)));
addSeries(ratioSeries);
}
// scale in % of vertical size (like 0.3)
public void addDeviationSlope(long fromTime, long toTime, boolean useForScale, double scale) {
LineGraphSeries<ScaledDataPoint> dsMaxSeries;
LineGraphSeries<ScaledDataPoint> dsMinSeries;
List<ScaledDataPoint> dsMaxArray = new ArrayList<>();
List<ScaledDataPoint> dsMinArray = new ArrayList<>();
double maxFromMaxValueFound = 0d;
double maxFromMinValueFound = 0d;
Scale dsMaxScale = new Scale();
Scale dsMinScale = new Scale();
for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) {
AutosensData autosensData = iobCobCalculatorPlugin.getAutosensData(time);
if (autosensData != null) {
dsMaxArray.add(new ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale));
dsMinArray.add(new ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale));
maxFromMaxValueFound = Math.max(maxFromMaxValueFound, Math.abs(autosensData.slopeFromMaxDeviation));
maxFromMinValueFound = Math.max(maxFromMinValueFound, Math.abs(autosensData.slopeFromMinDeviation));
}
}
// Slopes
ScaledDataPoint[] ratioMaxData = new ScaledDataPoint[dsMaxArray.size()];
ratioMaxData = dsMaxArray.toArray(ratioMaxData);
dsMaxSeries = new LineGraphSeries<>(ratioMaxData);
dsMaxSeries.setColor(resourceHelper.gc(R.color.devslopepos));
dsMaxSeries.setThickness(3);
ScaledDataPoint[] ratioMinData = new ScaledDataPoint[dsMinArray.size()];
ratioMinData = dsMinArray.toArray(ratioMinData);
dsMinSeries = new LineGraphSeries<>(ratioMinData);
dsMinSeries.setColor(resourceHelper.gc(R.color.devslopeneg));
dsMinSeries.setThickness(3);
if (useForScale) {
maxY = Math.max(maxFromMaxValueFound, maxFromMinValueFound);
minY = -maxY;
}
dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound);
dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound);
addSeries(dsMaxSeries);
addSeries(dsMinSeries);
}
// scale in % of vertical size (like 0.3)
public void addNowLine(long now) {
LineGraphSeries<DataPoint> seriesNow;
DataPoint[] nowPoints = new DataPoint[]{
new DataPoint(now, 0),
new DataPoint(now, maxY)
};
seriesNow = new LineGraphSeries<>(nowPoints);
seriesNow.setDrawDataPoints(false);
// custom paint to make a dotted line
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0));
paint.setColor(Color.WHITE);
seriesNow.setCustomPaint(paint);
addSeries(seriesNow);
}
public void formatAxis(long fromTime, long endTime) {
graph.getViewport().setMaxX(endTime);
graph.getViewport().setMinX(fromTime);
graph.getViewport().setXAxisBoundsManual(true);
graph.getGridLabelRenderer().setLabelFormatter(new TimeAsXAxisLabelFormatter("HH"));
graph.getGridLabelRenderer().setNumHorizontalLabels(7); // only 7 because of the space
}
private void addSeries(Series s) {
series.add(s);
}
public void performUpdate() {
// clear old data
graph.getSeries().clear();
// add precalculated series
for (Series s : series) {
if (!s.isEmpty()) {
s.onGraphViewAttached(graph);
graph.getSeries().add(s);
}
}
double step = 1d;
if (maxY < 1) step = 0.1d;
graph.getViewport().setMaxY(Round.ceilTo(maxY, step));
graph.getViewport().setMinY(Round.floorTo(minY, step));
graph.getViewport().setYAxisBoundsManual(true);
// draw it
graph.onDataChanged(false, false);
}
}

View file

@ -0,0 +1,569 @@
package info.nightscout.androidaps.plugins.general.overview.graphData
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import com.jjoe64.graphview.GraphView
import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries
import com.jjoe64.graphview.series.Series
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.TreatmentsInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin.LastRun
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class GraphData(injector: HasAndroidInjector, private val graph: GraphView, private val iobCobCalculatorPlugin: IobCobCalculatorPlugin) {
// IobCobCalculatorPlugin Cannot be injected: HistoryBrowser
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider
private val treatmentsPlugin: TreatmentsInterface
var maxY = Double.MIN_VALUE
private var minY = Double.MAX_VALUE
private var bgReadingsArray: List<BgReading>? = null
private val units: String
private val series: MutableList<Series<*>> = ArrayList()
init {
injector.androidInjector().inject(this)
units = profileFunction.getUnits()
treatmentsPlugin = activePlugin.activeTreatments
}
@Suppress("UNUSED_PARAMETER")
fun addBgReadings(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double, predictions: MutableList<BgReading>?) {
var maxBgValue = Double.MIN_VALUE
bgReadingsArray = iobCobCalculatorPlugin.bgReadings
if (bgReadingsArray?.isEmpty() != false) {
aapsLogger.debug(LTag.OVERVIEW, "No BG data.")
maxY = 10.0
minY = 0.0
return
}
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (bg in bgReadingsArray!!) {
if (bg.date < fromTime || bg.date > toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
bgListArray.add(bg)
}
if (predictions != null) {
predictions.sortWith(Comparator { o1: BgReading, o2: BgReading -> o1.x.compareTo(o2.x) })
for (prediction in predictions) if (prediction.value >= 40) bgListArray.add(prediction)
}
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units)
maxBgValue = if (units == Constants.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
if (highLine > maxBgValue) maxBgValue = highLine
val numOfVerticalLines = if (units == Constants.MGDL) (maxBgValue / 40 + 1).toInt() else (maxBgValue / 2 + 1).toInt()
maxY = maxBgValue
minY = 0.0
// set manual y bounds to have nice steps
graph.gridLabelRenderer.numVerticalLabels = numOfVerticalLines
addSeries(PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }))
}
fun addInRangeArea(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double) {
val inRangeAreaSeries: AreaGraphSeries<DoubleDataPoint>
val inRangeAreaDataPoints = arrayOf(
DoubleDataPoint(fromTime.toDouble(), lowLine, highLine),
DoubleDataPoint(toTime.toDouble(), lowLine, highLine)
)
inRangeAreaSeries = AreaGraphSeries(inRangeAreaDataPoints)
inRangeAreaSeries.color = 0
inRangeAreaSeries.isDrawBackground = true
inRangeAreaSeries.backgroundColor = resourceHelper.gc(R.color.inrangebackground)
addSeries(inRangeAreaSeries)
}
// scale in % of vertical size (like 0.3)
fun addBasals(fromTime: Long, toTime: Long, scale: Double) {
var maxBasalValueFound = 0.0
val basalScale = Scale()
val baseBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = ArrayList()
var lastLineBasal = 0.0
var lastAbsoluteLineBasal = -1.0
var lastBaseBasal = 0.0
var lastTempBasal = 0.0
var time = fromTime
while (time < toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 60 * 1000L
continue
}
val basalData = iobCobCalculatorPlugin.getBasalData(profile, time)
val baseBasalValue = basalData.basal
var absoluteLineValue = baseBasalValue
var tempBasalValue = 0.0
var basal = 0.0
if (basalData.isTempBasalRunning) {
tempBasalValue = basalData.tempBasalAbsolute
absoluteLineValue = tempBasalValue
if (tempBasalValue != lastTempBasal) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale))
}
if (lastBaseBasal != 0.0) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
lastBaseBasal = 0.0
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale))
lastBaseBasal = baseBasalValue
}
if (lastTempBasal != 0.0) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale))
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale))
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale))
}
lastAbsoluteLineBasal = absoluteLineValue
lastLineBasal = baseBasalValue
lastTempBasal = tempBasalValue
maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue))
time += 60 * 1000L
}
// final points
basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale))
// create series
addSeries(LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
it.thickness = 0
})
addSeries(LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
it.thickness = 0
})
addSeries(LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.basal)
})
})
addSeries(LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = resourceHelper.gc(R.color.basal)
})
})
basalScale.setMultiplier(maxY * scale / maxBasalValueFound)
}
fun addTargetLine(fromTime: Long, toTimeParam: Long, profile: Profile, lastRun: LastRun?) {
var toTime = toTimeParam
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
var lastTarget = -1.0
lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
var time = fromTime
while (time < toTime) {
val tt = treatmentsPlugin.getTempTargetFromHistory(time)
var value: Double
value = if (tt == null) {
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
} else {
Profile.fromMgdlToUnits(tt.target(), units)
}
if (lastTarget != value) {
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
}
lastTarget = value
time += 5 * 60 * 1000L
}
// final point
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
// create series
addSeries(LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.tempTargetBackground)
it.thickness = 2
})
}
fun addTreatments(fromTime: Long, endTime: Long) {
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
val treatments = treatmentsPlugin.treatmentsFromHistory
for (tx in treatments.indices) {
val t = treatments[tx]
if (t.x < fromTime || t.x > endTime) continue
if (t.isSMB && !t.isValid) continue
t.y = getNearestBg(t.x.toLong())
filteredTreatments.add(t)
}
// ProfileSwitch
val profileSwitches = treatmentsPlugin.profileSwitchesFromHistory.list
for (tx in profileSwitches.indices) {
val t: DataPointWithLabelInterface = profileSwitches[tx]
if (t.x < fromTime || t.x > endTime) continue
filteredTreatments.add(t)
}
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
val extendedBoluses = treatmentsPlugin.extendedBolusesFromHistory.list
for (tx in extendedBoluses.indices) {
val t: DataPointWithLabelInterface = extendedBoluses[tx]
if (t.x + t.duration < fromTime || t.x > endTime) continue
if (t.duration == 0L) continue
t.y = getNearestBg(t.x.toLong())
filteredTreatments.add(t)
}
}
// Careportal
val careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true)
for (tx in careportalEvents.indices) {
val t: DataPointWithLabelInterface = careportalEvents[tx]
if (t.x + t.duration < fromTime || t.x > endTime) continue
t.y = getNearestBg(t.x.toLong())
filteredTreatments.add(t)
}
addSeries(PointsWithLabelGraphSeries(Array(filteredTreatments.size) { i -> filteredTreatments[i] }))
}
private fun getNearestBg(date: Long): Double {
bgReadingsArray?.let { bgReadingsArray ->
for (r in bgReadingsArray.indices) {
val reading = bgReadingsArray[r]
if (reading.date > date) continue
return Profile.fromMgdlToUnits(reading.value, units)
}
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, units) else Profile.fromMgdlToUnits(100.0, units)
} ?: return Profile.fromMgdlToUnits(100.0, units)
}
fun addActivity(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val actArrayPred: MutableList<ScaledDataPoint> = ArrayList()
val now = System.currentTimeMillis().toDouble()
val actScale = Scale()
var total: IobTotal
var maxIAValue = 0.0
var time = fromTime
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile)
val act: Double = total.activity
if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPred.add(ScaledDataPoint(time, act, actScale))
maxIAValue = max(maxIAValue, abs(act))
time += 5 * 60 * 1000L
}
addSeries(FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.activity)
it.thickness = 3
})
addSeries(FixedLineGraphSeries(Array(actArrayPred.size) { i -> actArrayPred[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.activity)
})
})
if (useForScale) {
maxY = maxIAValue
minY = -maxIAValue
}
actScale.setMultiplier(maxY * scale / maxIAValue)
}
// scale in % of vertical size (like 0.3)
fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean) {
val iobSeries: FixedLineGraphSeries<ScaledDataPoint?>
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
var maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
val iobScale = Scale()
var time = fromTime
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
var iob = 0.0
if (profile != null) iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob
if (abs(lastIob - iob) > 0.02) {
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob, iobScale))
maxIobValueFound = max(maxIobValueFound, abs(iob))
lastIob = iob
}
time += 5 * 60 * 1000L
}
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
it.color = resourceHelper.gc(R.color.iob)
it.thickness = 3
}
if (showPrediction) {
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData")
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
val isTempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()) != null
val iobPred: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredArray) {
iobPred.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
addSeries(PointsWithLabelGraphSeries(Array(iobPred.size) { i -> iobPred[i] }))
val iobPred2: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredArray2) {
iobPred2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
addSeries(PointsWithLabelGraphSeries(Array(iobPred2.size) { i -> iobPred2[i] }))
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray))
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray2))
}
if (useForScale) {
maxY = maxIobValueFound
minY = -maxIobValueFound
}
iobScale.setMultiplier(maxY * scale / maxIobValueFound)
addSeries(iobSeries)
}
// scale in % of vertical size (like 0.3)
fun addCob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
var maxCobValueFound = 0.0
var lastCob = 0
val cobScale = Scale()
var time = fromTime
while (time <= toTime) {
iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData ->
val cob = autosensData.cob.toInt()
if (cob != lastCob) {
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failoverToMinAbsorbtionRate) {
autosensData.setScale(cobScale)
autosensData.setChartTime(time)
minFailOverActiveList.add(autosensData)
}
}
time += 5 * 60 * 1000L
}
// COB
addSeries(FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
it.color = resourceHelper.gc(R.color.cob)
it.thickness = 3
})
if (useForScale) {
maxY = maxCobValueFound
minY = 0.0
}
cobScale.setMultiplier(maxY * scale / maxCobValueFound)
addSeries(PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] }))
}
// scale in % of vertical size (like 0.3)
fun addDeviations(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale)
val devArray: MutableList<DeviationDataPoint> = ArrayList()
var maxDevValueFound = 0.0
val devScale = Scale()
var time = fromTime
while (time <= toTime) {
iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData ->
var color = resourceHelper.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = resourceHelper.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = resourceHelper.gc(R.color.deviationgrey)
}
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
maxDevValueFound = max(maxDevValueFound, abs(autosensData.deviation))
}
time += 5 * 60 * 1000L
}
// DEVIATIONS
addSeries(BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: DeviationDataPoint -> data.color }
})
if (useForScale) {
maxY = maxDevValueFound
minY = -maxY
}
devScale.setMultiplier(maxY * scale / maxDevValueFound)
}
// scale in % of vertical size (like 0.3)
fun addRatio(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
var maxRatioValueFound = Double.MIN_VALUE
var minRatioValueFound = Double.MAX_VALUE
val ratioScale = Scale()
var time = fromTime
while (time <= toTime) {
iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData ->
ratioArray.add(ScaledDataPoint(time, autosensData.autosensResult.ratio - 1, ratioScale))
maxRatioValueFound = max(maxRatioValueFound, autosensData.autosensResult.ratio - 1)
minRatioValueFound = min(minRatioValueFound, autosensData.autosensResult.ratio - 1)
}
time += 5 * 60 * 1000L
}
// RATIOS
addSeries(LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = resourceHelper.gc(R.color.ratio)
it.thickness = 3
})
if (useForScale) {
maxY = max(maxRatioValueFound, abs(minRatioValueFound))
minY = -maxY
}
ratioScale.setMultiplier(maxY * scale / max(maxRatioValueFound, abs(minRatioValueFound)))
}
// scale in % of vertical size (like 0.3)
fun addDeviationSlope(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
var maxFromMaxValueFound = 0.0
var maxFromMinValueFound = 0.0
val dsMaxScale = Scale()
val dsMinScale = Scale()
var time = fromTime
while (time <= toTime) {
iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData ->
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
}
time += 5 * 60 * 1000L
}
// Slopes
addSeries(LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopepos)
it.thickness = 3
})
addSeries(LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = resourceHelper.gc(R.color.devslopeneg)
it.thickness = 3
})
if (useForScale) {
maxY = max(maxFromMaxValueFound, maxFromMinValueFound)
minY = -maxY
}
dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound)
dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound)
}
// scale in % of vertical size (like 0.3)
fun addNowLine(now: Long) {
val nowPoints = arrayOf(
DataPoint(now.toDouble(), 0.0),
DataPoint(now.toDouble(), maxY)
)
addSeries(LineGraphSeries(nowPoints).also {
it.isDrawDataPoints = false
// custom paint to make a dotted line
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 2f
paint.pathEffect = DashPathEffect(floatArrayOf(10f, 20f), 0f)
paint.color = Color.WHITE
})
})
}
fun formatAxis(fromTime: Long, endTime: Long) {
graph.viewport.setMaxX(endTime.toDouble())
graph.viewport.setMinX(fromTime.toDouble())
graph.viewport.isXAxisBoundsManual = true
graph.gridLabelRenderer.labelFormatter = TimeAsXAxisLabelFormatter("HH")
graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space
}
private fun addSeries(s: Series<*>) = series.add(s)
fun performUpdate() {
// clear old data
graph.series.clear()
// add pre calculated series
for (s in series) {
if (!s.isEmpty) {
s.onGraphViewAttached(graph)
graph.series.add(s)
}
}
var step = 1.0
if (maxY < 1) step = 0.1
graph.viewport.setMaxY(Round.ceilTo(maxY, step))
graph.viewport.setMinY(Round.floorTo(minY, step))
graph.viewport.isYAxisBoundsManual = true
// draw it
graph.onDataChanged(false, false)
}
}

View file

@ -23,6 +23,12 @@ public class ScaledDataPoint implements DataPointInterface, Serializable {
this.scale = scale;
}
public ScaledDataPoint(long x, double y, Scale scale) {
this.x=x;
this.y=y;
this.scale = scale;
}
public ScaledDataPoint(Date x, double y, Scale scale) {
this.x = x.getTime();
this.y = y;