From bc5a089c5f89dd5a2467bd08924fa9e147ea4f28 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 1 Apr 2020 09:07:36 +0200 Subject: [PATCH] GraphData refactor --- .../historyBrowser/HistoryBrowseActivity.java | 2 +- .../general/overview/OverviewFragment.java | 2 +- .../general/overview/graphData/GraphData.java | 726 ------------------ .../general/overview/graphData/GraphData.kt | 569 ++++++++++++++ .../graphExtensions/ScaledDataPoint.java | 6 + 5 files changed, 577 insertions(+), 728 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java index 3c081e3309..6a47e3749b 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java @@ -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 **** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index eef9521d09..a3d86716bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -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 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java deleted file mode 100644 index bcb11363c8..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.java +++ /dev/null @@ -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 bgReadingsArray; - private String units; - private List 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 predictions) { - double maxBgValue = Double.MIN_VALUE; - //bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true); - bgReadingsArray = iobCobCalculatorPlugin.getBgReadings(); - List 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 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 basalsLineSeries; - LineGraphSeries absoluteBasalsLineSeries; - LineGraphSeries baseBasalsSeries; - LineGraphSeries tempBasalsSeries; - - double maxBasalValueFound = 0d; - Scale basalScale = new Scale(); - - List baseBasalArray = new ArrayList<>(); - List tempBasalArray = new ArrayList<>(); - List basalLineArray = new ArrayList<>(); - List 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 targetsSeries; - - Scale targetsScale = new Scale(); - targetsScale.setMultiplier(1); - - List 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 filteredTreatments = new ArrayList<>(); - - List 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 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 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 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 actSeriesHist; - List actArrayHist = new ArrayList<>(); - FixedLineGraphSeries actSeriesPred; - List 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 iobSeries; - List 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 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 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 minFailoverActiveList = new ArrayList<>(); - FixedLineGraphSeries cobSeries; - List 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 devSeries; - List 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 ratioSeries; - List 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 dsMaxSeries; - LineGraphSeries dsMinSeries; - List dsMaxArray = new ArrayList<>(); - List 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 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); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt new file mode 100644 index 0000000000..fc7d1eb2a1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -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? = null + private val units: String + private val series: MutableList> = 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?) { + 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 = 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 + 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 = ArrayList() + val tempBasalArray: MutableList = ArrayList() + val basalLineArray: MutableList = ArrayList() + val absoluteBasalLineArray: MutableList = 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 = 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 = 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 = ArrayList() + val actArrayPred: MutableList = 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 + val iobArray: MutableList = 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 = 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 = 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 = ArrayList() + val cobArray: MutableList = 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 = 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 = 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 = ArrayList() + val dsMinArray: MutableList = 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java index ac63790a71..8a00446092 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ScaledDataPoint.java @@ -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;