GraphData refactor
This commit is contained in:
parent
56d0d2850d
commit
bc5a089c5f
|
@ -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 ****
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue