diff --git a/app/build.gradle b/app/build.gradle index 25a419aac7..96035fce19 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -177,7 +177,6 @@ allprojects { } dependencies { - implementation project(path: ':plugins:aps') wearApp project(':wear') // in order to use internet's versions you'd need to enable Jetifier again @@ -203,6 +202,7 @@ dependencies { implementation project(':plugins:main') implementation project(':plugins:openhumans') implementation project(':plugins:sensitivity') + implementation project(':plugins:smoothing') implementation project(':plugins:source') implementation project(':plugins:sync') implementation project(':implementation') diff --git a/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt b/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt index 868b3b5c5a..26e345574b 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/PluginsListModule.kt @@ -51,6 +51,9 @@ import info.nightscout.pump.virtual.VirtualPumpPlugin import info.nightscout.sensitivity.SensitivityAAPSPlugin import info.nightscout.sensitivity.SensitivityOref1Plugin import info.nightscout.sensitivity.SensitivityWeightedAveragePlugin +import info.nightscout.smoothing.ExponentialSmoothingPlugin +import info.nightscout.smoothing.AvgSmoothingPlugin +import info.nightscout.smoothing.NoSmoothingPlugin import info.nightscout.source.AidexPlugin import info.nightscout.source.DexcomPlugin import info.nightscout.source.GlimpPlugin @@ -434,6 +437,24 @@ abstract class PluginsListModule { @IntKey(500) abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase + @Binds + @AllConfigs + @IntoMap + @IntKey(600) + abstract fun bindNoSmoothingPlugin(plugin: NoSmoothingPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(605) + abstract fun bindExponentialSmoothingPlugin(plugin: ExponentialSmoothingPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(610) + abstract fun bindAvgSmoothingPlugin(plugin: AvgSmoothingPlugin): PluginBase + @Qualifier annotation class AllConfigs diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt index 539eb6f7d1..b4c84e5193 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveWorker.kt @@ -23,7 +23,6 @@ import info.nightscout.interfaces.aps.Loop import info.nightscout.interfaces.configBuilder.RunningConfiguration import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.plugin.ActivePlugin -import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.queue.Command import info.nightscout.interfaces.queue.CommandQueue @@ -155,8 +154,7 @@ class KeepAliveWorker( var shouldUploadStatus = false if (config.NSCLIENT) return if (config.PUMPCONTROL) shouldUploadStatus = true - else if (!(loop as PluginBase).isEnabled() || iobCobCalculator.ads.actualBg() == null) - shouldUploadStatus = true + else if (!loop.isEnabled() || iobCobCalculator.ads.actualBg() == null) shouldUploadStatus = true else if (dateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true if (dateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) { lastIobUpload = dateUtil.now() diff --git a/core/graph/src/main/java/info/nightscout/core/graph/OverviewData.kt b/core/graph/src/main/java/info/nightscout/core/graph/OverviewData.kt index bdb89991eb..ae6b753751 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/OverviewData.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/OverviewData.kt @@ -16,7 +16,9 @@ import info.nightscout.core.graph.data.ScaledDataPoint import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.TemporaryTarget import info.nightscout.interfaces.aps.AutosensData +import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.iob.CobInfo +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobTotal @@ -45,12 +47,12 @@ interface OverviewData { * BG */ - val lastBg: GlucoseValue? - val isLow: Boolean - val isHigh: Boolean - @ColorInt fun lastBgColor(context: Context?): Int - val lastBgDescription: String - val isActualBg: Boolean + fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue? + fun isLow(autosensDataStore: AutosensDataStore): Boolean + fun isHigh(autosensDataStore: AutosensDataStore): Boolean + @ColorInt fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int + fun lastBgDescription(autosensDataStore: AutosensDataStore): String + fun isActualBg(autosensDataStore: AutosensDataStore): Boolean /* * TEMPORARY BASAL */ diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/BolusDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/BolusDataPoint.kt index 9d497a7bc3..c1f0cd165c 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/BolusDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/BolusDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.database.entities.Bolus import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.profile.DefaultValueHelper @@ -22,7 +23,7 @@ class BolusDataPoint( get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh) override val duration = 0L override val size = 2f - + override val paintStyle: Paint.Style = Paint.Style.FILL // not used override val shape get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/CarbsDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/CarbsDataPoint.kt index 834d7f02b3..934e49bc8c 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/CarbsDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/CarbsDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.core.graph.R import info.nightscout.database.entities.Carbs import info.nightscout.shared.interfaces.ResourceHelper @@ -18,6 +19,7 @@ class CarbsDataPoint( override val duration = 0L override val size = 2f override val shape = PointsWithLabelGraphSeries.Shape.CARBS + override val paintStyle: Paint.Style = Paint.Style.FILL // not used override fun color(context: Context?): Int { return if (data.isValid) rh.gac(context, info.nightscout.core.ui.R.attr.cobColor) else rh.gac(context, info.nightscout.core.ui.R.attr.alarmColor) diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/DataPointWithLabelInterface.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/DataPointWithLabelInterface.kt index c31c33271c..4ad39f4bf6 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/DataPointWithLabelInterface.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/DataPointWithLabelInterface.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import com.jjoe64.graphview.series.DataPointInterface interface DataPointWithLabelInterface : DataPointInterface { @@ -13,5 +14,6 @@ interface DataPointWithLabelInterface : DataPointInterface { val duration: Long val shape: PointsWithLabelGraphSeries.Shape val size: Float + val paintStyle: Paint.Style fun color(context: Context?): Int } \ No newline at end of file diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/EffectiveProfileSwitchDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/EffectiveProfileSwitchDataPoint.kt index f53a821b80..6588a0d24e 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/EffectiveProfileSwitchDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/EffectiveProfileSwitchDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.database.entities.EffectiveProfileSwitch import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.utils.T @@ -22,6 +23,7 @@ class EffectiveProfileSwitchDataPoint( override val duration = 0L override val shape = PointsWithLabelGraphSeries.Shape.PROFILE override val size = 2f + override val paintStyle: Paint.Style = Paint.Style.FILL // not used override fun color(context: Context?): Int { return rh.gac(context, info.nightscout.core.ui.R.attr.profileSwitchColor) } diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/ExtendedBolusDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/ExtendedBolusDataPoint.kt index 6d044d49e1..a8d1b4a5da 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/ExtendedBolusDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/ExtendedBolusDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.database.entities.ExtendedBolus import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.shared.interfaces.ResourceHelper @@ -18,6 +19,7 @@ class ExtendedBolusDataPoint( override val duration get() = data.duration override val size = 10f override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS + override val paintStyle: Paint.Style = Paint.Style.FILL // not used override fun color(context: Context?): Int { return rh.gac(context, info.nightscout.core.ui.R.attr.extBolusColor) } diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/GlucoseValueDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/GlucoseValueDataPoint.kt index 172d6881a4..556ddb9715 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/GlucoseValueDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/GlucoseValueDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.Constants import info.nightscout.interfaces.GlucoseUnit @@ -16,7 +17,7 @@ class GlucoseValueDataPoint( private val rh: ResourceHelper ) : DataPointWithLabelInterface { - fun valueToUnits(units: GlucoseUnit): Double = + private fun valueToUnits(units: GlucoseUnit): Double = if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL override fun getX(): Double = data.timestamp.toDouble() @@ -26,16 +27,13 @@ class GlucoseValueDataPoint( override val label: String = Profile.toCurrentUnitsString(profileFunction, data.value) override val duration = 0L override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG - override val size = 1f + override val size = if (isPrediction) 1f else 0.6f + override val paintStyle: Paint.Style = if (isPrediction) Paint.Style.FILL else Paint.Style.STROKE + override fun color(context: Context?): Int { - val units = profileFunction.getUnits() - val lowLine = defaultValueHelper.determineLowLine() - val highLine = defaultValueHelper.determineHighLine() return when { isPrediction -> predictionColor(context) - valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) - valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor) - else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange) + else -> rh.gac(context, info.nightscout.core.ui.R.attr.originalBgValueColor) } } diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/InMemoryGlucoseValueDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/InMemoryGlucoseValueDataPoint.kt index 15c2b79f02..65bce5e8ae 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/InMemoryGlucoseValueDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/InMemoryGlucoseValueDataPoint.kt @@ -1,20 +1,23 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.interfaces.Constants import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.iob.InMemoryGlucoseValue +import info.nightscout.interfaces.profile.DefaultValueHelper import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.shared.interfaces.ResourceHelper class InMemoryGlucoseValueDataPoint( val data: InMemoryGlucoseValue, + private val defaultValueHelper: DefaultValueHelper, private val profileFunction: ProfileFunction, private val rh: ResourceHelper ) : DataPointWithLabelInterface { - fun valueToUnits(units: GlucoseUnit): Double = - if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL + private fun valueToUnits(units: GlucoseUnit): Double = + if (units == GlucoseUnit.MGDL) data.recalculated else data.recalculated * Constants.MGDL_TO_MMOLL override fun getX(): Double = data.timestamp.toDouble() override fun getY(): Double = valueToUnits(profileFunction.getUnits()) @@ -22,8 +25,18 @@ class InMemoryGlucoseValueDataPoint( override val label: String = "" override val duration = 0L override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG - override val size = 0.3f + override val size = 1f + override val paintStyle: Paint.Style = Paint.Style.FILL + override fun color(context: Context?): Int { - return rh.gac(context, info.nightscout.core.ui.R.attr.inMemoryColor) + val units = profileFunction.getUnits() + val lowLine = defaultValueHelper.determineLowLine() + val highLine = defaultValueHelper.determineHighLine() + return when { + valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) + valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor) + else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange) + } } + } \ No newline at end of file diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/PointsWithLabelGraphSeries.java b/core/graph/src/main/java/info/nightscout/core/graph/data/PointsWithLabelGraphSeries.java index 0446f19113..47f744a9fa 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/PointsWithLabelGraphSeries.java +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/PointsWithLabelGraphSeries.java @@ -179,20 +179,20 @@ public class PointsWithLabelGraphSeries e // draw data point if (!overdraw) { if (value.getShape() == Shape.BG || value.getShape() == Shape.COB_FAIL_OVER) { - mPaint.setStyle(Paint.Style.FILL); + mPaint.setStyle(value.getPaintStyle()); mPaint.setStrokeWidth(0); canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint); } else if (value.getShape() == Shape.BG || value.getShape() == Shape.IOB_PREDICTION || value.getShape() == Shape.BUCKETED_BG) { mPaint.setColor(value.color(graphView.getContext())); - mPaint.setStyle(Paint.Style.FILL); + mPaint.setStyle(value.getPaintStyle()); mPaint.setStrokeWidth(0); canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint); } else if (value.getShape() == Shape.PREDICTION) { mPaint.setColor(value.color(graphView.getContext())); - mPaint.setStyle(Paint.Style.FILL); + mPaint.setStyle(value.getPaintStyle()); mPaint.setStrokeWidth(0); canvas.drawCircle(endX, endY, scaledPxSize, mPaint); - mPaint.setStyle(Paint.Style.FILL); + mPaint.setStyle(value.getPaintStyle()); mPaint.setStrokeWidth(0); canvas.drawCircle(endX, endY, scaledPxSize / 3, mPaint); } else if (value.getShape() == Shape.RECTANGLE) { diff --git a/core/graph/src/main/java/info/nightscout/core/graph/data/TherapyEventDataPoint.kt b/core/graph/src/main/java/info/nightscout/core/graph/data/TherapyEventDataPoint.kt index 021ba19145..5f75092ecd 100644 --- a/core/graph/src/main/java/info/nightscout/core/graph/data/TherapyEventDataPoint.kt +++ b/core/graph/src/main/java/info/nightscout/core/graph/data/TherapyEventDataPoint.kt @@ -1,6 +1,7 @@ package info.nightscout.core.graph.data import android.content.Context +import android.graphics.Paint import info.nightscout.database.entities.TherapyEvent import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Translator @@ -55,6 +56,7 @@ class TherapyEventDataPoint( duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERAL_WITH_DURATION else -> PointsWithLabelGraphSeries.Shape.GENERAL } + override val paintStyle: Paint.Style = Paint.Style.FILL // not used override val size get() = if (rh.gb(info.nightscout.shared.R.bool.isTablet)) 12.0f else 10.0f override fun color(context: Context?): Int { diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/aps/AutosensDataStore.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/aps/AutosensDataStore.kt index 3e88091c12..d38a7aef20 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/aps/AutosensDataStore.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/aps/AutosensDataStore.kt @@ -15,8 +15,18 @@ interface AutosensDataStore { var bucketedData: MutableList? var lastUsed5minCalculation: Boolean? - fun lastBg(): GlucoseValue? - fun actualBg(): GlucoseValue? + /** + * Return last valid (>39) InMemoryGlucoseValue from bucketed data or null if db is empty + * + * @return InMemoryGlucoseValue or null + */ + fun lastBg(): InMemoryGlucoseValue? + /** + * Provide last bucketed InMemoryGlucoseValue or null if none exists within the last 9 minutes + * + * @return InMemoryGlucoseValue or null + */ + fun actualBg(): InMemoryGlucoseValue? fun lastDataTime(dateUtil: DateUtil): String fun clone(): AutosensDataStore fun getBgReadingsDataTableCopy(): List diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/iob/InMemoryGlucoseValue.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/iob/InMemoryGlucoseValue.kt index 502936840b..12b3c9bde1 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/iob/InMemoryGlucoseValue.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/iob/InMemoryGlucoseValue.kt @@ -2,8 +2,9 @@ package info.nightscout.interfaces.iob import info.nightscout.database.entities.GlucoseValue -class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated: Boolean = false) { +class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var trendArrow: GlucoseValue.TrendArrow = GlucoseValue.TrendArrow.NONE, var smoothed: Double? = null) { - constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value) - // var generated : value doesn't correspond to real value with timestamp close to real BG + constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value, gv.trendArrow) + + val recalculated: Double get() = smoothed ?: value } \ No newline at end of file diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/ActivePlugin.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/ActivePlugin.kt index bcf3e2b360..eccb24cccf 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/ActivePlugin.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/ActivePlugin.kt @@ -9,6 +9,7 @@ import info.nightscout.interfaces.insulin.Insulin import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.pump.Pump +import info.nightscout.interfaces.smoothing.Smoothing import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.Sync @@ -74,6 +75,11 @@ interface ActivePlugin { */ val activeObjectives: Objectives? + /** + * Smoothing plugin + */ + val activeSmoothing: Smoothing + /** * Currently selected NsClient plugin */ diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/PluginType.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/PluginType.kt index 8b900ea2b6..19145d91b3 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/PluginType.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/plugin/PluginType.kt @@ -6,5 +6,5 @@ package info.nightscout.interfaces.plugin * set by [info.nightscout.interfaces.PluginDescription.mainType] */ enum class PluginType { - GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC + GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC, SMOOTHING } \ No newline at end of file diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/smoothing/Smoothing.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/smoothing/Smoothing.kt new file mode 100644 index 0000000000..752f64bbda --- /dev/null +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/smoothing/Smoothing.kt @@ -0,0 +1,16 @@ +package info.nightscout.interfaces.smoothing + +import info.nightscout.interfaces.iob.InMemoryGlucoseValue + +interface Smoothing { + + /** + * Smooth values in List + * + * @param data input glucose values ([0] to be the most recent one) + * @param updateWindow amount of values to the past to smooth + * + * @return new List with smoothed values (smoothed values are stored in [InMemoryGlucoseValue.smoothed]) + */ + fun smooth(data: MutableList): MutableList +} \ No newline at end of file diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/utils/TrendCalculator.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/utils/TrendCalculator.kt index 9c0150f22d..13e753b8af 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/utils/TrendCalculator.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/utils/TrendCalculator.kt @@ -1,6 +1,8 @@ package info.nightscout.interfaces.utils import info.nightscout.database.entities.GlucoseValue +import info.nightscout.interfaces.aps.AutosensDataStore +import info.nightscout.interfaces.iob.InMemoryGlucoseValue /** * Convert BG direction value to trend arrow or calculate it if not provided @@ -15,6 +17,20 @@ interface TrendCalculator { * @return TrendArrow */ fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow + /** + * Provide or calculate trend + * + * @param glucoseValue BG + * @return TrendArrow + */ + fun getTrendArrow(glucoseValue: InMemoryGlucoseValue?): GlucoseValue.TrendArrow + /** + * Provide or calculate trend from newest bucketed data + * + * @param autosensDataStore current store from IobCobCalculator + * @return TrendArrow + */ + fun getTrendArrow(autosensDataStore: AutosensDataStore): GlucoseValue.TrendArrow? /** * Provide or calculate trend @@ -23,4 +39,11 @@ interface TrendCalculator { * @return string description of TrendArrow */ fun getTrendDescription(glucoseValue: GlucoseValue?): String + /** + * Provide or calculate trend from newest bucketed data + * + * @param autosensDataStore current store from IobCobCalculator + * @return string description of TrendArrow + */ + fun getTrendDescription(autosensDataStore: AutosensDataStore): String } \ No newline at end of file diff --git a/core/main/src/main/java/info/nightscout/core/extensions/GlucoseValueExtension.kt b/core/main/src/main/java/info/nightscout/core/extensions/GlucoseValueExtension.kt index 3cfa51eb29..845f749458 100644 --- a/core/main/src/main/java/info/nightscout/core/extensions/GlucoseValueExtension.kt +++ b/core/main/src/main/java/info/nightscout/core/extensions/GlucoseValueExtension.kt @@ -4,6 +4,7 @@ package info.nightscout.core.extensions import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.Constants import info.nightscout.interfaces.GlucoseUnit +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.shared.utils.DateUtil import org.json.JSONObject @@ -26,3 +27,12 @@ fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject = .put("direction", trendArrow.text) .put("type", "sgv") .also { if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) } + +fun InMemoryGlucoseValue.valueToUnits(units: GlucoseUnit): Double = + if (units == GlucoseUnit.MGDL) recalculated + else recalculated * Constants.MGDL_TO_MMOLL + +fun InMemoryGlucoseValue.valueToUnitsString(units: GlucoseUnit): String = + if (units == GlucoseUnit.MGDL) DecimalFormatter.to0Decimal(recalculated) + else DecimalFormatter.to1Decimal(recalculated * Constants.MGDL_TO_MMOLL) + diff --git a/core/main/src/main/java/info/nightscout/core/wizard/QuickWizardEntry.kt b/core/main/src/main/java/info/nightscout/core/wizard/QuickWizardEntry.kt index 1a489bd7af..0f46f6f50c 100644 --- a/core/main/src/main/java/info/nightscout/core/wizard/QuickWizardEntry.kt +++ b/core/main/src/main/java/info/nightscout/core/wizard/QuickWizardEntry.kt @@ -6,10 +6,10 @@ import info.nightscout.core.extensions.valueToUnits import info.nightscout.core.iob.round import info.nightscout.core.utils.MidnightUtils import info.nightscout.database.ValueWrapper -import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.aps.Loop import info.nightscout.interfaces.db.PersistenceLayer import info.nightscout.interfaces.iob.GlucoseStatusProvider +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.profile.Profile @@ -107,7 +107,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec fun isActive(): Boolean = time.secondsFromMidnight() >= validFrom() && time.secondsFromMidnight() <= validTo() && forDevice(DEVICE_PHONE) - fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard { + fun doCalc(profile: Profile, profileName: String, lastBG: InMemoryGlucoseValue, _synchronized: Boolean): BolusWizard { val dbRecord = persistenceLayer.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null //BG diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt index 26ff3364f0..70558483b1 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/remotemodel/RemoteDeviceStatus.kt @@ -59,6 +59,7 @@ data class RemoteDeviceStatus( @SerializedName("version") val version: String?, @SerializedName("insulin") val insulin: Int?, @SerializedName("sensitivity") val sensitivity: Int?, + @SerializedName("smoothing") val smoothing: String?, @Contextual @SerializedName("insulinConfiguration") val insulinConfiguration: JSONObject?, @Contextual @SerializedName("sensitivityConfiguration") val sensitivityConfiguration: JSONObject?, @Contextual @SerializedName("overviewConfiguration") val overviewConfiguration: JSONObject?, diff --git a/core/ui/src/main/res/drawable/ic_timeline_24.xml b/core/ui/src/main/res/drawable/ic_timeline_24.xml new file mode 100644 index 0000000000..3d4b78abea --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_timeline_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/core/ui/src/main/res/values-night/styles.xml b/core/ui/src/main/res/values-night/styles.xml index 76ebef4e7d..6e3ca1508f 100644 --- a/core/ui/src/main/res/values-night/styles.xml +++ b/core/ui/src/main/res/values-night/styles.xml @@ -206,7 +206,7 @@ @color/tempbasal @color/pumpHistory @color/profileSwitch - @color/white + @color/white @color/red @color/red @color/blue diff --git a/core/ui/src/main/res/values/attrs.xml b/core/ui/src/main/res/values/attrs.xml index 49670026d9..380bb21a83 100644 --- a/core/ui/src/main/res/values/attrs.xml +++ b/core/ui/src/main/res/values/attrs.xml @@ -180,7 +180,7 @@ - + diff --git a/core/ui/src/main/res/values/styles.xml b/core/ui/src/main/res/values/styles.xml index 318c420aa1..a1bf69ad9d 100644 --- a/core/ui/src/main/res/values/styles.xml +++ b/core/ui/src/main/res/values/styles.xml @@ -209,7 +209,7 @@ @color/tempbasal @color/pumpHistory @color/profileSwitch - @color/white + @color/white @color/red @color/red @color/blue diff --git a/implementation/src/main/java/info/nightscout/implementation/TrendCalculatorImpl.kt b/implementation/src/main/java/info/nightscout/implementation/TrendCalculatorImpl.kt index d299905a8a..ead7b57577 100644 --- a/implementation/src/main/java/info/nightscout/implementation/TrendCalculatorImpl.kt +++ b/implementation/src/main/java/info/nightscout/implementation/TrendCalculatorImpl.kt @@ -2,6 +2,8 @@ package info.nightscout.implementation import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.impl.AppRepository +import info.nightscout.interfaces.aps.AutosensDataStore +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.utils.TrendCalculator import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.utils.T @@ -15,6 +17,13 @@ class TrendCalculatorImpl @Inject constructor( ) : TrendCalculator { override fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow = + when { + glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE + glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow + else -> calculateDirection(InMemoryGlucoseValue(glucoseValue)) + } + + override fun getTrendArrow(glucoseValue: InMemoryGlucoseValue?): GlucoseValue.TrendArrow = when { glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow @@ -34,7 +43,7 @@ class TrendCalculatorImpl @Inject constructor( else -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_unknown) } - private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow { + private fun calculateDirection(glucoseValue: InMemoryGlucoseValue): GlucoseValue.TrendArrow { val toTime = glucoseValue.timestamp val readings = repository.compatGetBgReadingsDataFromTime(toTime - T.mins(10).msecs(), toTime, false).blockingGet() @@ -62,4 +71,55 @@ class TrendCalculatorImpl @Inject constructor( else -> GlucoseValue.TrendArrow.NONE } } + + override fun getTrendArrow(autosensDataStore: AutosensDataStore): GlucoseValue.TrendArrow? { + val data = autosensDataStore.getBucketedDataTableCopy() ?: return null + if (data.size == 0) return null + val glucoseValue = data[0] + return when { + glucoseValue.value != glucoseValue.recalculated -> calculateDirection(data) // always recalculate after smoothing + glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow + else -> calculateDirection(data) + } + } + + override fun getTrendDescription(autosensDataStore: AutosensDataStore): String { + return when (getTrendArrow(autosensDataStore)) { + GlucoseValue.TrendArrow.DOUBLE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_double_down) + GlucoseValue.TrendArrow.SINGLE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_single_down) + GlucoseValue.TrendArrow.FORTY_FIVE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_forty_five_down) + GlucoseValue.TrendArrow.FLAT -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_flat) + GlucoseValue.TrendArrow.FORTY_FIVE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_forty_five_up) + GlucoseValue.TrendArrow.SINGLE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_single_up) + GlucoseValue.TrendArrow.DOUBLE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_double_up) + GlucoseValue.TrendArrow.NONE -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_none) + else -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_unknown) + } + } + + private fun calculateDirection(readings: MutableList): GlucoseValue.TrendArrow { + + if (readings.size < 2) + return GlucoseValue.TrendArrow.NONE + val current = readings[0] + val previous = readings[1] + + // Avoid division by 0 + val slope = + if (current.timestamp == previous.timestamp) 0.0 + else (previous.recalculated - current.recalculated) / (previous.timestamp - current.timestamp) + + val slopeByMinute = slope * 60000 + + return when { + slopeByMinute <= -3.5 -> GlucoseValue.TrendArrow.DOUBLE_DOWN + slopeByMinute <= -2 -> GlucoseValue.TrendArrow.SINGLE_DOWN + slopeByMinute <= -1 -> GlucoseValue.TrendArrow.FORTY_FIVE_DOWN + slopeByMinute <= 1 -> GlucoseValue.TrendArrow.FLAT + slopeByMinute <= 2 -> GlucoseValue.TrendArrow.FORTY_FIVE_UP + slopeByMinute <= 3.5 -> GlucoseValue.TrendArrow.SINGLE_UP + slopeByMinute <= 40 -> GlucoseValue.TrendArrow.DOUBLE_UP + else -> GlucoseValue.TrendArrow.NONE + } + } } \ No newline at end of file diff --git a/implementation/src/main/java/info/nightscout/implementation/iob/GlucoseStatusProviderImpl.kt b/implementation/src/main/java/info/nightscout/implementation/iob/GlucoseStatusProviderImpl.kt index 881550c74f..2013c5800d 100644 --- a/implementation/src/main/java/info/nightscout/implementation/iob/GlucoseStatusProviderImpl.kt +++ b/implementation/src/main/java/info/nightscout/implementation/iob/GlucoseStatusProviderImpl.kt @@ -25,7 +25,7 @@ class GlucoseStatusProviderImpl @Inject constructor( get() = getGlucoseStatusData() override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? { - val data = iobCobCalculator.ads.getBgReadingsDataTableCopy() + val data = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return null val sizeRecords = data.size if (sizeRecords == 0) { aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0") @@ -41,7 +41,7 @@ class GlucoseStatusProviderImpl @Inject constructor( if (sizeRecords == 1) { aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1") return GlucoseStatus( - glucose = now.value, + glucose = now.recalculated, noise = 0.0, delta = 0.0, shortAvgDelta = 0.0, @@ -49,31 +49,30 @@ class GlucoseStatusProviderImpl @Inject constructor( date = nowDate ).asRounded() } - val nowValueList = ArrayList() val lastDeltas = ArrayList() val shortDeltas = ArrayList() val longDeltas = ArrayList() // Use the latest sgv value in the now calculations - nowValueList.add(now.value) for (i in 1 until sizeRecords) { - if (data[i].value > 38) { + if (data[i].recalculated > 38) { val then = data[i] val thenDate = then.timestamp val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong() // multiply by 5 to get the same units as delta, i.e. mg/dL/5m - change = now.value - then.value + change = now.recalculated - then.recalculated val avgDel = change / minutesAgo * 5 aapsLogger.debug(LTag.GLUCOSE, "$then minutesAgo=$minutesAgo avgDelta=$avgDel") // use the average of all data points in the last 2.5m for all further "now" calculations - if (0 < minutesAgo && minutesAgo < 2.5) { - // Keep and average all values within the last 2.5 minutes - nowValueList.add(then.value) - now.value = average(nowValueList) - // short_deltas are calculated from everything ~5-15 minutes ago - } else if (2.5 < minutesAgo && minutesAgo < 17.5) { + // if (0 < minutesAgo && minutesAgo < 2.5) { + // // Keep and average all values within the last 2.5 minutes + // nowValueList.add(then.recalculated) + // now.value = average(nowValueList) + // // short_deltas are calculated from everything ~5-15 minutes ago + // } else + if (2.5 < minutesAgo && minutesAgo < 17.5) { //console.error(minutesAgo, avgDelta); shortDeltas.add(avgDel) // last_deltas are calculated from everything ~5 minutes ago @@ -96,7 +95,7 @@ class GlucoseStatusProviderImpl @Inject constructor( average(lastDeltas) } return GlucoseStatus( - glucose = now.value, + glucose = now.recalculated, date = nowDate, noise = 0.0, //for now set to nothing as not all CGMs report noise shortAvgDelta = shortAverageDelta, @@ -105,6 +104,89 @@ class GlucoseStatusProviderImpl @Inject constructor( ).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded() } + /* Real BG (previous) version + override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? { + val data = iobCobCalculator.ads.getBgReadingsDataTableCopy() + val sizeRecords = data.size + if (sizeRecords == 0) { + aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0") + return null + } + if (data[0].timestamp < dateUtil.now() - 7 * 60 * 1000L && !allowOldData) { + aapsLogger.debug(LTag.GLUCOSE, "oldData") + return null + } + val now = data[0] + val nowDate = now.timestamp + var change: Double + if (sizeRecords == 1) { + aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1") + return GlucoseStatus( + glucose = now.value, + noise = 0.0, + delta = 0.0, + shortAvgDelta = 0.0, + longAvgDelta = 0.0, + date = nowDate + ).asRounded() + } + val nowValueList = ArrayList() + val lastDeltas = ArrayList() + val shortDeltas = ArrayList() + val longDeltas = ArrayList() + + // Use the latest sgv value in the now calculations + nowValueList.add(now.value) + for (i in 1 until sizeRecords) { + if (data[i].value > 38) { + val then = data[i] + val thenDate = then.timestamp + + val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong() + // multiply by 5 to get the same units as delta, i.e. mg/dL/5m + change = now.value - then.value + val avgDel = change / minutesAgo * 5 + aapsLogger.debug(LTag.GLUCOSE, "$then minutesAgo=$minutesAgo avgDelta=$avgDel") + + // use the average of all data points in the last 2.5m for all further "now" calculations + if (0 < minutesAgo && minutesAgo < 2.5) { + // Keep and average all values within the last 2.5 minutes + nowValueList.add(then.value) + now.value = average(nowValueList) + // short_deltas are calculated from everything ~5-15 minutes ago + } else if (2.5 < minutesAgo && minutesAgo < 17.5) { + //console.error(minutesAgo, avgDelta); + shortDeltas.add(avgDel) + // last_deltas are calculated from everything ~5 minutes ago + if (2.5 < minutesAgo && minutesAgo < 7.5) { + lastDeltas.add(avgDel) + } + // long_deltas are calculated from everything ~20-40 minutes ago + } else if (17.5 < minutesAgo && minutesAgo < 42.5) { + longDeltas.add(avgDel) + } else { + // Do not process any more records after >= 42.5 minutes + break + } + } + } + val shortAverageDelta = average(shortDeltas) + val delta = if (lastDeltas.isEmpty()) { + shortAverageDelta + } else { + average(lastDeltas) + } + return GlucoseStatus( + glucose = now.value, + date = nowDate, + noise = 0.0, //for now set to nothing as not all CGMs report noise + shortAvgDelta = shortAverageDelta, + delta = delta, + longAvgDelta = average(longDeltas), + ).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded() + } + + */ companion object { fun average(array: ArrayList): Double { diff --git a/implementation/src/main/java/info/nightscout/implementation/overview/OverviewDataImpl.kt b/implementation/src/main/java/info/nightscout/implementation/overview/OverviewDataImpl.kt index aaa7d81a7e..3e88e76034 100644 --- a/implementation/src/main/java/info/nightscout/implementation/overview/OverviewDataImpl.kt +++ b/implementation/src/main/java/info/nightscout/implementation/overview/OverviewDataImpl.kt @@ -26,7 +26,9 @@ import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.TemporaryTarget import info.nightscout.database.impl.AppRepository import info.nightscout.interfaces.aps.AutosensData +import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.iob.CobInfo +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobTotal import info.nightscout.interfaces.plugin.ActivePlugin @@ -119,43 +121,42 @@ class OverviewDataImpl @Inject constructor( * BG */ - override val lastBg: GlucoseValue? - get() = - repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped -> - if (gvWrapped is ValueWrapper.Existing) gvWrapped.value - else null - } + override fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue? = + autosensDataStore.bucketedData?.let { if (it.size > 0) it[0] else null } + // repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped -> + // if (gvWrapped is ValueWrapper.Existing) gvWrapped.value + // else null + // } - override val isLow: Boolean - get() = lastBg?.let { lastBg -> + override fun isLow(autosensDataStore: AutosensDataStore): Boolean = + lastBg(autosensDataStore)?.let { lastBg -> lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() } ?: false - override val isHigh: Boolean - get() = lastBg?.let { lastBg -> + override fun isHigh(autosensDataStore: AutosensDataStore): Boolean = + lastBg(autosensDataStore)?.let { lastBg -> lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() } ?: false @ColorInt - override fun lastBgColor(context: Context?): Int = + override fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int = when { - isLow -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) - isHigh -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor) - else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange) + isLow(autosensDataStore) -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) + isHigh(autosensDataStore) -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor) + else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange) } - override val lastBgDescription: String - get() = when { - isLow -> rh.gs(info.nightscout.core.ui.R.string.a11y_low) - isHigh -> rh.gs(info.nightscout.core.ui.R.string.a11y_high) - else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange) + override fun lastBgDescription(autosensDataStore: AutosensDataStore): String = + when { + isLow(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_low) + isHigh(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_high) + else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange) } - override val isActualBg: Boolean - get() = - lastBg?.let { lastBg -> - lastBg.timestamp > dateUtil.now() - T.mins(9).msecs() - } ?: false + override fun isActualBg(autosensDataStore: AutosensDataStore): Boolean = + lastBg(autosensDataStore)?.let { lastBg -> + lastBg.timestamp > dateUtil.now() - T.mins(9).msecs() + } ?: false /* * TEMPORARY BASAL diff --git a/implementation/src/main/java/info/nightscout/implementation/plugin/PluginStore.kt b/implementation/src/main/java/info/nightscout/implementation/plugin/PluginStore.kt index 6778b0b51d..62b70ab7ce 100644 --- a/implementation/src/main/java/info/nightscout/implementation/plugin/PluginStore.kt +++ b/implementation/src/main/java/info/nightscout/implementation/plugin/PluginStore.kt @@ -14,6 +14,7 @@ import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.pump.Pump +import info.nightscout.interfaces.smoothing.Smoothing import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.Sync @@ -36,6 +37,7 @@ class PluginStore @Inject constructor( private var activeAPSStore: APS? = null private var activeInsulinStore: Insulin? = null private var activeSensitivityStore: Sensitivity? = null + private var activeSmoothingStore: Smoothing? = null override fun loadDefaults() { verifySelectionInCategories() @@ -106,6 +108,16 @@ class PluginStore @Inject constructor( } setFragmentVisibilities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY) + // PluginType.SMOOTHING + pluginsInCategory = getSpecificPluginsList(PluginType.SMOOTHING) + activeSmoothingStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SMOOTHING) as Smoothing? + if (activeSmoothingStore == null) { + activeSmoothingStore = getDefaultPlugin(PluginType.SMOOTHING) as Smoothing + (activeSmoothingStore as PluginBase).setPluginEnabled(PluginType.SMOOTHING, true) + aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting SmoothingInterface") + } + setFragmentVisibilities((activeSmoothingStore as PluginBase).name, pluginsInCategory, PluginType.SMOOTHING) + // PluginType.PROFILE pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE) activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource? @@ -181,6 +193,10 @@ class PluginStore @Inject constructor( get() = activeSensitivityStore ?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" } + override val activeSmoothing: Smoothing + get() = activeSmoothingStore + ?: checkNotNull(activeSmoothingStore) { "No smoothing selected" } + override val activeOverview: Overview get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview @@ -190,8 +206,7 @@ class PluginStore @Inject constructor( override val activeIobCobCalculator: IobCobCalculator get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator override val activeObjectives: Objectives? - get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives - + get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives? override val activeNsClient: NsClient? get() = getTheOneEnabledInArray(getSpecificPluginsListByInterface(NsClient::class.java), PluginType.SYNC) as NsClient? diff --git a/implementation/src/test/java/info/nightscout/implementation/iob/GlucoseStatusTest.kt b/implementation/src/test/java/info/nightscout/implementation/iob/GlucoseStatusTest.kt index bb823a51e3..040ed19d88 100644 --- a/implementation/src/test/java/info/nightscout/implementation/iob/GlucoseStatusTest.kt +++ b/implementation/src/test/java/info/nightscout/implementation/iob/GlucoseStatusTest.kt @@ -6,6 +6,7 @@ import info.nightscout.core.iob.log import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.iob.GlucoseStatus +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.T @@ -40,7 +41,7 @@ class GlucoseStatusTest : TestBase() { } @Test fun calculateValidGlucoseStatus() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateValidBgData()) val glucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001) Assertions.assertEquals(-2.0, glucoseStatus.delta, 0.001) @@ -48,9 +49,11 @@ class GlucoseStatusTest : TestBase() { Assertions.assertEquals(-2.0, glucoseStatus.longAvgDelta, 0.001) // -2 -2 -2 -2 Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date } +/* + Not testing anymore, not valid for bucketed data @Test fun calculateMostRecentGlucoseStatus() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateMostRecentBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateMostRecentBgData()) val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! Assertions.assertEquals(215.0, glucoseStatus.glucose, 0.001) // (214+216) / 2 Assertions.assertEquals(-1.0, glucoseStatus.delta, 0.001) @@ -59,8 +62,17 @@ class GlucoseStatusTest : TestBase() { Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date, even when averaging } + private fun generateMostRecentBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766800000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766600000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + return list + } +*/ + @Test fun oneRecordShouldProduceZeroDeltas() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOneCurrentRecordBgData()) val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001) Assertions.assertEquals(0.0, glucoseStatus.delta, 0.001) @@ -70,19 +82,19 @@ class GlucoseStatusTest : TestBase() { } @Test fun insufficientDataShouldReturnNull() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateInsufficientBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateInsufficientBgData()) val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData Assertions.assertEquals(null, glucoseStatus) } @Test fun oldDataShouldReturnNull() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData()) val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData Assertions.assertEquals(null, glucoseStatus) } @Test fun returnOldDataIfAllowed() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData()) val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).getGlucoseStatusData(true) Assertions.assertNotEquals(null, glucoseStatus) } @@ -91,8 +103,11 @@ class GlucoseStatusTest : TestBase() { Assertions.assertEquals(0.0, GlucoseStatusProviderImpl.average(ArrayList()), 0.001) } +/* + Not testing anymore, not valid for bucketed data + @Test fun calculateGlucoseStatusForLibreTestBgData() { - Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateLibreTestData()) + Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateLibreTestData()) val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! Assertions.assertEquals(100.0, glucoseStatus.glucose, 0.001) // Assertions.assertEquals(-10.0, glucoseStatus.delta, 0.001) @@ -101,6 +116,23 @@ class GlucoseStatusTest : TestBase() { Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date } + private fun generateLibreTestData(): MutableList { + val list: MutableList = ArrayList() + val endTime = 1514766900000L + val latestReading = 100.0 + // Now + list.add(InMemoryGlucoseValue(value = latestReading, timestamp = endTime, trendArrow = GlucoseValue.TrendArrow.FLAT)) + // One minute ago + list.add(InMemoryGlucoseValue(value = latestReading, timestamp = endTime - 1000 * 60 * 1, trendArrow = GlucoseValue.TrendArrow.FLAT)) + // Two minutes ago + list.add(InMemoryGlucoseValue(value = latestReading, timestamp = endTime - 1000 * 60 * 2, trendArrow = GlucoseValue.TrendArrow.FLAT)) + // Three minutes and beyond at constant rate + for (i in 3..49) + list.add(InMemoryGlucoseValue(value = latestReading + i * 2, timestamp = endTime - 1000 * 60 * i, trendArrow = GlucoseValue.TrendArrow.FLAT)) + return list + } +*/ + @BeforeEach fun initMocking() { Mockito.`when`(dateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs()) @@ -108,84 +140,32 @@ class GlucoseStatusTest : TestBase() { } // [{"mgdl":214,"mills":1521895773113,"device":"xDrip-DexcomG5","direction":"Flat","filtered":191040,"unfiltered":205024,"noise":1,"rssi":100},{"mgdl":219,"mills":1521896073352,"device":"xDrip-DexcomG5","direction":"Flat","filtered":200160,"unfiltered":209760,"noise":1,"rssi":100},{"mgdl":222,"mills":1521896372890,"device":"xDrip-DexcomG5","direction":"Flat","filtered":207360,"unfiltered":212512,"noise":1,"rssi":100},{"mgdl":220,"mills":1521896673062,"device":"xDrip-DexcomG5","direction":"Flat","filtered":211488,"unfiltered":210688,"noise":1,"rssi":100},{"mgdl":193,"mills":1521896972933,"device":"xDrip-DexcomG5","direction":"Flat","filtered":212384,"unfiltered":208960,"noise":1,"rssi":100},{"mgdl":181,"mills":1521897273336,"device":"xDrip-DexcomG5","direction":"SingleDown","filtered":210592,"unfiltered":204320,"noise":1,"rssi":100},{"mgdl":176,"mills":1521897572875,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":206720,"unfiltered":197440,"noise":1,"rssi":100},{"mgdl":168,"mills":1521897872929,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":201024,"unfiltered":187904,"noise":1,"rssi":100},{"mgdl":161,"mills":1521898172814,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":193376,"unfiltered":178144,"noise":1,"rssi":100},{"mgdl":148,"mills":1521898472879,"device":"xDrip-DexcomG5","direction":"SingleDown","filtered":183264,"unfiltered":161216,"noise":1,"rssi":100},{"mgdl":139,"mills":1521898772862,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":170784,"unfiltered":148928,"noise":1,"rssi":100},{"mgdl":132,"mills":1521899072896,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":157248,"unfiltered":139552,"noise":1,"rssi":100},{"mgdl":125,"mills":1521899372834,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":144416,"unfiltered":129616.00000000001,"noise":1,"rssi":100},{"mgdl":128,"mills":1521899973456,"device":"xDrip-DexcomG5","direction":"Flat","filtered":130240.00000000001,"unfiltered":133536,"noise":1,"rssi":100},{"mgdl":132,"mills":1521900573287,"device":"xDrip-DexcomG5","direction":"Flat","filtered":133504,"unfiltered":138720,"noise":1,"rssi":100},{"mgdl":127,"mills":1521900873711,"device":"xDrip-DexcomG5","direction":"Flat","filtered":136480,"unfiltered":132992,"noise":1,"rssi":100},{"mgdl":127,"mills":1521901180151,"device":"xDrip-DexcomG5","direction":"Flat","filtered":136896,"unfiltered":132128,"noise":1,"rssi":100},{"mgdl":125,"mills":1521901473582,"device":"xDrip-DexcomG5","direction":"Flat","filtered":134624,"unfiltered":129696,"noise":1,"rssi":100},{"mgdl":120,"mills":1521901773597,"device":"xDrip-DexcomG5","direction":"Flat","filtered":130704.00000000001,"unfiltered":123376,"noise":1,"rssi":100},{"mgdl":116,"mills":1521902075855,"device":"xDrip-DexcomG5","direction":"Flat","filtered":126272,"unfiltered":118448,"noise":1,"rssi":100}] - private fun generateValidBgData(): List { - val list: MutableList = ArrayList() - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 214.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 216.0, timestamp = 1514766600000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 219.0, timestamp = 1514766300000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 223.0, timestamp = 1514766000000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 222.0, timestamp = 1514765700000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 224.0, timestamp = 1514765400000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 226.0, timestamp = 1514765100000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + private fun generateValidBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766600000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT)) return list } - private fun generateMostRecentBgData(): List { - val list: MutableList = ArrayList() - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 214.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 216.0, timestamp = 1514766800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 216.0, timestamp = 1514766600000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - return list - } - - private fun generateInsufficientBgData(): List { + private fun generateInsufficientBgData(): MutableList { return ArrayList() } - private fun generateOldBgData(): List { - val list: MutableList = ArrayList() - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + private fun generateOldBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT)) return list } - private fun generateOneCurrentRecordBgData(): List { - val list: MutableList = ArrayList() - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 214.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - return list - } - - private fun generateLibreTestData(): List { - val list: MutableList = ArrayList() - val endTime = 1514766900000L - val latestReading = 100.0 - // Now - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = latestReading, timestamp = endTime, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - // One minute ago - list.add( - GlucoseValue( - raw = 0.0, - noise = 0.0, - value = latestReading, - timestamp = endTime - 1000 * 60 * 1, - sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, - trendArrow = GlucoseValue.TrendArrow.FLAT - ) - ) - // Two minutes ago - list.add( - GlucoseValue( - raw = 0.0, - noise = 0.0, - value = latestReading, - timestamp = endTime - 1000 * 60 * 2, - sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, - trendArrow = GlucoseValue.TrendArrow.FLAT - ) - ) - - // Three minutes and beyond at constant rate - for (i in 3..49) - list.add( - GlucoseValue( - raw = 0.0, - noise = 0.0, - value = latestReading + i * 2, - timestamp = endTime - 1000 * 60 * i, - sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, - trendArrow = GlucoseValue.TrendArrow.FLAT - ) - ) + private fun generateOneCurrentRecordBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT)) return list } } \ No newline at end of file diff --git a/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerBgTest.kt b/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerBgTest.kt index 4bb36ae0c8..d9487f616d 100644 --- a/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerBgTest.kt +++ b/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerBgTest.kt @@ -4,8 +4,9 @@ import com.google.common.base.Optional import info.nightscout.automation.elements.Comparator import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.GlucoseUnit +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import org.json.JSONObject -import org.junit.Assert +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` @@ -22,39 +23,39 @@ class TriggerBgTest : TriggerTestBase() { @Test fun shouldRunTest() { - `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData()) + `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOneCurrentRecordBgData()) var t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertFalse(t.shouldRun()) - `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList()) + Assertions.assertFalse(t.shouldRun()) + `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList()) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerBg(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) } @Test fun copyConstructorTest() { val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) val t1 = t.duplicate() as TriggerBg - Assert.assertEquals(213.0, t1.bg.value, 0.01) - Assert.assertEquals(GlucoseUnit.MGDL, t1.bg.units) - Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) + Assertions.assertEquals(213.0, t1.bg.value, 0.01) + Assertions.assertEquals(GlucoseUnit.MGDL, t1.bg.units) + Assertions.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) } private var bgJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"bg\":4.1,\"units\":\"mmol\"},\"type\":\"TriggerBg\"}" @@ -62,35 +63,26 @@ class TriggerBgTest : TriggerTestBase() { @Test fun toJSONTest() { val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertEquals(bgJson, t.toJSON()) + Assertions.assertEquals(bgJson, t.toJSON()) } @Test fun fromJSONTest() { val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL) val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerBg - Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) - Assert.assertEquals(4.1, t2.bg.value, 0.01) - Assert.assertEquals(GlucoseUnit.MMOL, t2.bg.units) + Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) + Assertions.assertEquals(4.1, t2.bg.value, 0.01) + Assertions.assertEquals(GlucoseUnit.MMOL, t2.bg.units) } @Test fun iconTest() { - Assert.assertEquals(Optional.of(info.nightscout.core.main.R.drawable.ic_cp_bgcheck), TriggerBg(injector).icon()) + Assertions.assertEquals(Optional.of(info.nightscout.core.main.R.drawable.ic_cp_bgcheck), TriggerBg(injector).icon()) } - private fun generateOneCurrentRecordBgData(): List { - val list: MutableList = ArrayList() - list.add( - GlucoseValue( - raw = 0.0, - noise = 0.0, - value = 214.0, - timestamp = now - 1, - sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, - trendArrow = GlucoseValue.TrendArrow.FLAT - ) - ) + private fun generateOneCurrentRecordBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 214.0, timestamp = now - 1, trendArrow = GlucoseValue.TrendArrow.FLAT)) return list } } \ No newline at end of file diff --git a/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerDeltaTest.kt b/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerDeltaTest.kt index f5f636028a..1a51a05df7 100644 --- a/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerDeltaTest.kt +++ b/plugins/automation/src/test/java/info/nightscout/automation/triggers/TriggerDeltaTest.kt @@ -6,8 +6,9 @@ import info.nightscout.automation.elements.Comparator import info.nightscout.automation.elements.InputDelta.DeltaType import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.GlucoseUnit +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import org.json.JSONObject -import org.junit.Assert +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mockito.`when` @@ -23,42 +24,42 @@ class TriggerDeltaTest : TriggerTestBase() { } @Test fun shouldRunTest() { - `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData()) + `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateValidBgData()) var t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(73.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertFalse(t.shouldRun()) - Assert.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType) + Assertions.assertFalse(t.shouldRun()) + Assertions.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-2.0, DeltaType.SHORT_AVERAGE).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertFalse(t.shouldRun()) - Assert.assertEquals(DeltaType.SHORT_AVERAGE, t.delta.deltaType) + Assertions.assertFalse(t.shouldRun()) + Assertions.assertEquals(DeltaType.SHORT_AVERAGE, t.delta.deltaType) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-3.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertTrue(t.shouldRun()) - Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType) + Assertions.assertTrue(t.shouldRun()) + Assertions.assertEquals(DeltaType.DELTA, t.delta.deltaType) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(2.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(2.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(0.3, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(0.1, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-0.5, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-0.2, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertTrue(t.shouldRun()) - `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList()) + Assertions.assertTrue(t.shouldRun()) + `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList()) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) - Assert.assertFalse(t.shouldRun()) + Assertions.assertFalse(t.shouldRun()) t = TriggerDelta(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE) - Assert.assertTrue(t.shouldRun()) + Assertions.assertTrue(t.shouldRun()) } @Test fun copyConstructorTest() { val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) val t1 = t.duplicate() as TriggerDelta - Assert.assertEquals(213.0, t1.delta.value, 0.01) - Assert.assertEquals(GlucoseUnit.MGDL, t1.units) - Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType) - Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) + Assertions.assertEquals(213.0, t1.delta.value, 0.01) + Assertions.assertEquals(GlucoseUnit.MGDL, t1.units) + Assertions.assertEquals(DeltaType.DELTA, t.delta.deltaType) + Assertions.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) } private var deltaJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"deltaType\":\"DELTA\",\"units\":\"mg/dl\",\"value\":4.1},\"type\":\"TriggerDelta\"}" @@ -66,38 +67,38 @@ class TriggerDeltaTest : TriggerTestBase() { @Test fun toJSONTest() { val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL) - Assert.assertEquals(deltaJson, t.toJSON()) + Assertions.assertEquals(deltaJson, t.toJSON()) } @Test fun fromJSONTest() { val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MMOL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL) val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerDelta - Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) - Assert.assertEquals(4.1, t2.delta.value, 0.01) - Assert.assertEquals(GlucoseUnit.MMOL, t2.units) - Assert.assertEquals(DeltaType.DELTA, t2.delta.deltaType) + Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) + Assertions.assertEquals(4.1, t2.delta.value, 0.01) + Assertions.assertEquals(GlucoseUnit.MMOL, t2.units) + Assertions.assertEquals(DeltaType.DELTA, t2.delta.deltaType) } @Test fun iconTest() { - Assert.assertEquals(Optional.of(R.drawable.ic_auto_delta), TriggerDelta(injector).icon()) + Assertions.assertEquals(Optional.of(R.drawable.ic_auto_delta), TriggerDelta(injector).icon()) } @Test fun initializerTest() { val t = TriggerDelta(injector) - Assert.assertTrue(t.units == GlucoseUnit.MGDL) + Assertions.assertTrue(t.units == GlucoseUnit.MGDL) } - private fun generateValidBgData(): List { - val list: MutableList = ArrayList() - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 214.0, timestamp = 1514766900000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 216.0, timestamp = 1514766600000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 219.0, timestamp = 1514766300000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 223.0, timestamp = 1514766000000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 222.0, timestamp = 1514765700000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 224.0, timestamp = 1514765400000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 226.0, timestamp = 1514765100000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) - list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + private fun generateValidBgData(): MutableList { + val list: MutableList = ArrayList() + list.add(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766600000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, trendArrow = GlucoseValue.TrendArrow.FLAT)) + list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT)) return list } } \ No newline at end of file diff --git a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderFragment.kt b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderFragment.kt index b6a0c9a001..34ea082c1c 100644 --- a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderFragment.kt +++ b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderFragment.kt @@ -103,6 +103,7 @@ class ConfigBuilderFragment : DaggerFragment() { createViewsForPlugins(info.nightscout.core.ui.R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInList(PluginType.INSULIN)) if (!config.NSCLIENT) { createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInList(PluginType.BGSOURCE)) + createViewsForPlugins(R.string.configbuilder_smoothing, R.string.configbuilder_smoothing_description, PluginType.SMOOTHING, activePlugin.getSpecificPluginsVisibleInList(PluginType.SMOOTHING)) createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, activePlugin.getSpecificPluginsVisibleInList(PluginType.PUMP)) } if (config.APS || config.PUMPCONTROL || config.isEngineeringMode()) diff --git a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderPlugin.kt b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderPlugin.kt index d78b99fba6..242ab995db 100644 --- a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderPlugin.kt +++ b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/ConfigBuilderPlugin.kt @@ -20,6 +20,7 @@ import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.pump.Pump import info.nightscout.interfaces.pump.PumpSync +import info.nightscout.interfaces.smoothing.Smoothing import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.sync.NsClient import info.nightscout.rx.bus.RxBus @@ -131,7 +132,9 @@ class ConfigBuilderPlugin @Inject constructor( (if (p.isEnabled(PluginType.CONSTRAINTS)) " CONSTRAINTS" else "") + (if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") + (if (p.isEnabled(PluginType.BGSOURCE)) " BGSOURCE" else "") + - if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else "" + (if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else "") + + (if (p.isEnabled(PluginType.SYNC)) " SYNC" else "") + + if (p.isEnabled(PluginType.SMOOTHING)) " SMOOTHING" else "" ) } } @@ -195,6 +198,7 @@ class ConfigBuilderPlugin @Inject constructor( when (type) { PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java) PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Sensitivity::class.java) + PluginType.SMOOTHING -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Smoothing::class.java) PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java) PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java) PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java) diff --git a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt index 6ee49e4e96..7eb66ef859 100644 --- a/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt +++ b/plugins/configuration/src/main/java/info/nightscout/configuration/configBuilder/RunningConfigurationImpl.kt @@ -11,6 +11,7 @@ import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.pump.defs.PumpType +import info.nightscout.interfaces.smoothing.Smoothing import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventNSClientNewLog @@ -52,11 +53,13 @@ class RunningConfigurationImpl @Inject constructor( val sensitivityInterface = activePlugin.activeSensitivity val overviewInterface = activePlugin.activeOverview val safetyInterface = activePlugin.activeSafety + val smoothingInterface = activePlugin.activeSmoothing json.put("insulin", insulinInterface.id.value) json.put("insulinConfiguration", insulinInterface.configuration()) json.put("sensitivity", sensitivityInterface.id.value) json.put("sensitivityConfiguration", sensitivityInterface.configuration()) + json.put("smoothing", smoothingInterface.javaClass.simpleName) json.put("overviewConfiguration", overviewInterface.configuration()) json.put("safetyConfiguration", safetyInterface.configuration()) json.put("pump", pumpInterface.model().description) @@ -104,6 +107,18 @@ class RunningConfigurationImpl @Inject constructor( } } + configuration.smoothing?.let { + for (p in activePlugin.getSpecificPluginsListByInterface(Smoothing::class.java)) { + val smoothingPlugin = p as Smoothing + if (smoothingPlugin.javaClass.simpleName == it) { + if (!p.isEnabled()) { + aapsLogger.debug(LTag.CORE, "Changing smoothing plugin to ${smoothingPlugin.javaClass.simpleName}") + configBuilder.performPluginSwitch(p, true, PluginType.SMOOTHING) + } + } + } + } + configuration.pump?.let { if (sp.getString(info.nightscout.core.utils.R.string.key_virtualpump_type, "fake") != it) { sp.putString(info.nightscout.core.utils.R.string.key_virtualpump_type, it) diff --git a/plugins/configuration/src/main/res/values/strings.xml b/plugins/configuration/src/main/res/values/strings.xml index ceec78a3e7..08c1597fb8 100644 --- a/plugins/configuration/src/main/res/values/strings.xml +++ b/plugins/configuration/src/main/res/values/strings.xml @@ -77,6 +77,8 @@ Which type of insulin are you using? BG Source Where should AAPS gain it\'s data from? + Smoothing + Choose smoothing algorithm Sensitivity detection Which sensitivity algorithm should be used? CONF diff --git a/plugins/constraints/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt b/plugins/constraints/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt index 1dfe6d458d..befaebebd7 100644 --- a/plugins/constraints/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt +++ b/plugins/constraints/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt @@ -8,6 +8,7 @@ import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.source.BgSource @@ -218,7 +219,7 @@ class BgQualityCheckPluginTest : TestBase() { flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-40).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-45).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(flatData) - `when`(iobCobCalculator.ads.lastBg()).thenReturn(flatData[0]) + `when`(iobCobCalculator.ads.lastBg()).thenReturn(InMemoryGlucoseValue(flatData[0])) // Test non-dexcom plugin on flat data class OtherPlugin : BgSource { @@ -247,7 +248,7 @@ class BgQualityCheckPluginTest : TestBase() { incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(incompleteData) - `when`(iobCobCalculator.ads.lastBg()).thenReturn(incompleteData[0]) + `when`(iobCobCalculator.ads.lastBg()).thenReturn(InMemoryGlucoseValue(incompleteData[0])) `when`(activePlugin.activeBgSource).thenReturn(OtherPlugin()) plugin.processBgData()// must be more than 5 values Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state) diff --git a/plugins/main/src/main/java/info/nightscout/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt index 5341b0dbe3..b2d10916c2 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt @@ -115,7 +115,7 @@ class DataBroadcastPlugin @Inject constructor( val lastBG = iobCobCalculator.ads.lastBg() ?: return val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: return - bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl + bundle.putDouble("glucoseMgdl", lastBG.recalculated) // last BG in mgdl bundle.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp bundle.putString("units", profileFunction.getUnits().asText) // units used in AAPS "mg/dl" or "mmol" bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string diff --git a/plugins/main/src/main/java/info/nightscout/plugins/general/overview/OverviewFragment.kt b/plugins/main/src/main/java/info/nightscout/plugins/general/overview/OverviewFragment.kt index b62121342c..17f8d4e50a 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/general/overview/OverviewFragment.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/general/overview/OverviewFragment.kt @@ -80,10 +80,10 @@ import info.nightscout.plugins.ui.StatusLightHandler import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventAcceptOpenLoopChange +import info.nightscout.rx.events.EventBucketedDataCreated import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged import info.nightscout.rx.events.EventExtendedBolusChange import info.nightscout.rx.events.EventMobileToWear -import info.nightscout.rx.events.EventNewBG import info.nightscout.rx.events.EventNewOpenLoopNotification import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPumpStatusChanged @@ -99,6 +99,7 @@ import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.weardata.EventData import info.nightscout.shared.extensions.runOnUiThread import info.nightscout.shared.extensions.toVisibility +import info.nightscout.shared.extensions.toVisibilityKeepSpace import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.utils.DateUtil @@ -275,7 +276,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList sp.putBoolean(info.nightscout.core.utils.R.string.key_objectiveusescale, true) }, fabricPrivacy::logException) disposable += rxBus - .toObservable(EventNewBG::class.java) + .toObservable(EventBucketedDataCreated::class.java) .debounce(1L, TimeUnit.SECONDS) .observeOn(aapsSchedulers.io) .subscribe({ updateBg() }, fabricPrivacy::logException) @@ -777,19 +778,19 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @SuppressLint("SetTextI18n") fun updateBg() { val units = profileFunction.getUnits() - val lastBg = overviewData.lastBg - val lastBgColor = overviewData.lastBgColor(context) - val isActualBg = overviewData.isActualBg + val lastBg = overviewData.lastBg(iobCobCalculator.ads) + val lastBgColor = overviewData.lastBgColor(context, iobCobCalculator.ads) + val isActualBg = overviewData.isActualBg(iobCobCalculator.ads) val glucoseStatus = glucoseStatusProvider.glucoseStatusData - val trendDescription = trendCalculator.getTrendDescription(lastBg) - val trendArrow = trendCalculator.getTrendArrow(lastBg) - val lastBgDescription = overviewData.lastBgDescription + val trendDescription = trendCalculator.getTrendDescription(iobCobCalculator.ads) + val trendArrow = trendCalculator.getTrendArrow(iobCobCalculator.ads) + val lastBgDescription = overviewData.lastBgDescription(iobCobCalculator.ads) runOnUiThread { _binding ?: return@runOnUiThread - binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) - ?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short) + binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) ?: "" binding.infoLayout.bg.setTextColor(lastBgColor) - binding.infoLayout.arrow.setImageResource(trendArrow.directionToIcon()) + trendArrow?.let { binding.infoLayout.arrow.setImageResource(it.directionToIcon()) } + binding.infoLayout.arrow.visibility = (trendArrow != null).toVisibilityKeepSpace() binding.infoLayout.arrow.setColorFilter(lastBgColor) binding.infoLayout.arrow.contentDescription = lastBgDescription + " " + rh.gs(info.nightscout.core.ui.R.string.and) + " " + trendDescription diff --git a/plugins/main/src/main/java/info/nightscout/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/plugins/main/src/main/java/info/nightscout/plugins/general/wear/wearintegration/DataHandlerMobile.kt index 8efbf91571..875d1b68b4 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/general/wear/wearintegration/DataHandlerMobile.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -34,6 +34,7 @@ import info.nightscout.interfaces.aps.Loop import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.iob.GlucoseStatusProvider +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.logging.UserEntryLogger import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData @@ -744,8 +745,9 @@ class DataHandlerMobile @Inject constructor( ) ) // GraphData - val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong() - rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet().map { getSingleBG(it) })))) + iobCobCalculator.ads.getBucketedDataTableCopy()?.let { bucketedData -> + rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(bucketedData.map { getSingleBG(it) })))) + } // Treatments sendTreatments() // Status @@ -933,7 +935,7 @@ class DataHandlerMobile @Inject constructor( return deltaString } - private fun getSingleBG(glucoseValue: GlucoseValue): EventData.SingleBg { + private fun getSingleBG(glucoseValue: InMemoryGlucoseValue): EventData.SingleBg { val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true) val units = profileFunction.getUnits() val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units) @@ -946,8 +948,8 @@ class DataHandlerMobile @Inject constructor( slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol, delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--", avgDelta = glucoseStatus?.let { deltaString(it.shortAvgDelta, it.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) } ?: "--", - sgvLevel = if (glucoseValue.value > highLine) 1L else if (glucoseValue.value < lowLine) -1L else 0L, - sgv = glucoseValue.value, + sgvLevel = if (glucoseValue.recalculated > highLine) 1L else if (glucoseValue.recalculated < lowLine) -1L else 0L, + sgv = glucoseValue.recalculated, high = highLine, low = lowLine, color = 0 diff --git a/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt index b63d5f81e8..68bb0df2fc 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt @@ -155,7 +155,7 @@ class IobCobCalculatorPlugin @Inject constructor( overviewData = overviewData, reason = reason, end = System.currentTimeMillis(), - bgDataReload = false, + bgDataReload = true, cause = event ) } diff --git a/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/data/AutosensDataStoreObject.kt b/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/data/AutosensDataStoreObject.kt index ebf1fae6b9..a31c28441e 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/data/AutosensDataStoreObject.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/iob/iobCobCalculator/data/AutosensDataStoreObject.kt @@ -71,22 +71,24 @@ class AutosensDataStoreObject : AutosensDataStore { } /** - * Return last valid (>39) GlucoseValue from database or null if db is empty + * Return last valid (>39) InMemoryGlucoseValue from bucketed data or null if db is empty * - * @return GlucoseValue or null + * @return InMemoryGlucoseValue or null */ - override fun lastBg(): GlucoseValue? = + override fun lastBg(): InMemoryGlucoseValue? = synchronized(dataLock) { - if (bgReadings.isNotEmpty()) bgReadings[0] - else null + bucketedData?.let { bucketedData -> + if (bucketedData.isNotEmpty()) bucketedData[0] + else null + } } /** - * Provide last GlucoseValue or null if none exists within the last 9 minutes + * Provide last bucketed InMemoryGlucoseValue or null if none exists within the last 9 minutes * - * @return GlucoseValue or null + * @return InMemoryGlucoseValue or null */ - override fun actualBg(): GlucoseValue? { + override fun actualBg(): InMemoryGlucoseValue? { val lastBg = lastBg() ?: return null return if (lastBg.timestamp > System.currentTimeMillis() - T.mins(9).msecs()) lastBg else null } @@ -237,7 +239,7 @@ class AutosensDataStoreObject : AutosensDataStore { val bgDelta = newer.value - older.value val timeDiffToNew = newer.timestamp - currentTime val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta - val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble(), true) + val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble()) newBucketedData.add(newBgReading) //log.debug("BG: " + newBgReading.value + " (" + new Date(newBgReading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")"); } @@ -273,7 +275,7 @@ class AutosensDataStoreObject : AutosensDataStore { val gapDelta = bgReadings[i].value - lastBg //console.error(gapDelta, lastBg, elapsed_minutes); val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta - val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble(), true) + val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble()) //console.error("Interpolated", bData[j]); bData.add(newBgReading) aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" } diff --git a/plugins/main/src/main/res/layout/overview_info_layout.xml b/plugins/main/src/main/res/layout/overview_info_layout.xml index fccf69d89f..6d0ff6e9b1 100644 --- a/plugins/main/src/main/res/layout/overview_info_layout.xml +++ b/plugins/main/src/main/res/layout/overview_info_layout.xml @@ -11,7 +11,7 @@ android:id="@+id/bg" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="00.0" + tools:text="00.0" android:textSize="60sp" android:textStyle="bold" app:layout_constraintEnd_toStartOf="@+id/delta_large" @@ -32,7 +32,7 @@ android:id="@+id/delta_large" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="(+1.0)" + tools:text="(+1.0)" android:textSize="60sp" android:textStyle="bold" android:visibility="gone" @@ -60,7 +60,6 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" app:layout_constraintHorizontal_bias="0.0" - app:srcCompat="@drawable/ic_flat" android:contentDescription="@string/trend_arrow" /> @@ -106,7 +105,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:textAlignment="textEnd" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Small" tools:ignore="HardcodedText" /> @@ -123,7 +122,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:textAlignment="textEnd" - android:text="15m Δ: " + android:text="Δ15: " android:contentDescription="15 minutes delta" android:textAppearance="@style/TextAppearance.AppCompat.Small" tools:ignore="HardcodedText" /> @@ -134,7 +133,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:textAlignment="textEnd" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Small" tools:ignore="HardcodedText" /> @@ -151,7 +150,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:textAlignment="textEnd" - android:text="40m Δ: " + android:text="Δ40: " android:contentDescription="40 minutes delta" android:textAppearance="@style/TextAppearance.AppCompat.Small" tools:ignore="HardcodedText" /> @@ -162,7 +161,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:textAlignment="textEnd" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Small" tools:ignore="HardcodedText" /> @@ -226,7 +225,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="-5dp" - android:text="8:00 PM" + tools:text="8:00 PM" android:textSize="25sp" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -238,7 +237,7 @@ android:layout_gravity="center_horizontal" android:layout_marginTop="-10dp" android:gravity="center_vertical" - android:text="(-5)" + tools:text="(-5)" android:textSize="19sp" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -271,7 +270,7 @@ android:gravity="center_horizontal" android:paddingTop="3dp" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -304,7 +303,7 @@ android:gravity="center_horizontal" android:paddingTop="3dp" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -336,7 +335,7 @@ android:gravity="center_horizontal" android:paddingTop="3dp" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -367,7 +366,7 @@ android:gravity="center_horizontal" android:paddingTop="3dp" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -400,7 +399,7 @@ android:gravity="center_horizontal" android:paddingTop="3dp" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" tools:ignore="HardcodedText" /> @@ -412,7 +411,7 @@ android:layout_marginTop="-9dp" android:gravity="center_horizontal" android:paddingBottom="3dp" - android:text="n/a" + tools:text="n/a" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" android:visibility="visible" diff --git a/plugins/main/src/test/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt b/plugins/main/src/test/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt index dd44bc7b56..c596bfd9fe 100644 --- a/plugins/main/src/test/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt +++ b/plugins/main/src/test/java/info/nightscout/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt @@ -21,6 +21,7 @@ import info.nightscout.interfaces.aps.Loop import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.iob.CobInfo +import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.IobTotal import info.nightscout.interfaces.logging.UserEntryLogger import info.nightscout.interfaces.plugin.ActivePlugin @@ -68,7 +69,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { @Mock lateinit var autosensDataStore: AutosensDataStore @Mock lateinit var smsManager: SmsManager - var injector: HasAndroidInjector = HasAndroidInjector { + private var injector: HasAndroidInjector = HasAndroidInjector { AndroidInjector { if (it is PumpEnactResult) { it.context = context @@ -98,7 +99,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { `when`(iobCobCalculator.getCobInfo(false, "SMS COB")).thenReturn(CobInfo(0, 10.0, 2.0)) `when`(iobCobCalculator.ads).thenReturn(autosensDataStore) - `when`(autosensDataStore.lastBg()).thenReturn(reading) + `when`(autosensDataStore.lastBg()).thenReturn(InMemoryGlucoseValue(reading)) `when`(sp.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678") diff --git a/plugins/smoothing/.gitignore b/plugins/smoothing/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/plugins/smoothing/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/plugins/smoothing/build.gradle b/plugins/smoothing/build.gradle new file mode 100644 index 0000000000..6dc158f091 --- /dev/null +++ b/plugins/smoothing/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-allopen' + id 'com.hiya.jacoco-android' +} + +apply from: "${project.rootDir}/core/main/android_dependencies.gradle" +apply from: "${project.rootDir}/core/main/android_module_dependencies.gradle" +apply from: "${project.rootDir}/core/main/allopen_dependencies.gradle" +apply from: "${project.rootDir}/core/main/test_dependencies.gradle" +apply from: "${project.rootDir}/core/main/jacoco_global.gradle" +android { + namespace 'info.nightscout.smoothing' +} + + +dependencies { + implementation project(':app-wear-shared:shared') + implementation project(':core:interfaces') + implementation project(':core:ui') +} \ No newline at end of file diff --git a/plugins/smoothing/consumer-rules.pro b/plugins/smoothing/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/smoothing/proguard-rules.pro b/plugins/smoothing/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/plugins/smoothing/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/plugins/smoothing/src/main/AndroidManifest.xml b/plugins/smoothing/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/plugins/smoothing/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/plugins/smoothing/src/main/java/info/nightscout/smoothing/AvgSmoothingPlugin.kt b/plugins/smoothing/src/main/java/info/nightscout/smoothing/AvgSmoothingPlugin.kt new file mode 100644 index 0000000000..f9e0791bbb --- /dev/null +++ b/plugins/smoothing/src/main/java/info/nightscout/smoothing/AvgSmoothingPlugin.kt @@ -0,0 +1,69 @@ +package info.nightscout.smoothing + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.annotations.OpenForTesting +import info.nightscout.interfaces.iob.InMemoryGlucoseValue +import info.nightscout.interfaces.plugin.PluginBase +import info.nightscout.interfaces.plugin.PluginDescription +import info.nightscout.interfaces.plugin.PluginType +import info.nightscout.interfaces.smoothing.Smoothing +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import info.nightscout.shared.interfaces.ResourceHelper +import info.nightscout.shared.utils.T +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.max +import kotlin.math.round + +@OpenForTesting +@Singleton +class AvgSmoothingPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rh: ResourceHelper +) : PluginBase( + PluginDescription() + .mainType(PluginType.SMOOTHING) + .pluginIcon(info.nightscout.core.ui.R.drawable.ic_timeline_24) + .pluginName(R.string.avg_smoothing_name) + .shortName(R.string.smoothing_shortname) + .description(R.string.description_avg_smoothing), + aapsLogger, rh, injector +), Smoothing { + + @Suppress("LocalVariableName") + override fun smooth(data: MutableList): MutableList { + if (data.lastIndex < 4) + { + aapsLogger.debug(LTag.GLUCOSE, "Not enough value's to smooth!") + return data + } + + for (i in data.lastIndex -1 downTo 1) { + // Check if value's are in a valid range + // Bucketed is always calculated to 5 min, we still check if our data is evenly spaced with an allowance of 30 seconds + if (isValid(data[i].value) && isValid(data[i - 1].value) && isValid(data[i + 1].value) + && Math.abs(data[i].timestamp - data[i - 1].timestamp - (data[i + 1].timestamp - data[i].timestamp)) < T.secs(30).msecs()) + { + // We could further improve this by adding a weight to the neighbours, for simplicity this is not done. + data[i].smoothed = ((data[i - 1].value + data[i].value + data[i + 1].value) / 3.0) + } + else + { + // data[i].smoothed = data[i].value + val currentTime = data[i].timestamp + val value = data[i].value + aapsLogger.debug(LTag.GLUCOSE, "Value: $value at $currentTime not smoothed") + } + } + // We leave the data we can not smooth as is, alternativly we could provide raw value's to the smoothed value's: + // data[data.lastIndex].smoothed = data[data.lastIndex].value + // data[0].smoothed = data[0].value + return data + } + private fun isValid(n: Double): Boolean { + // For Dexcom: Below 39 is LOW, above 401 Dexcom just says HI + return n > 39 && n < 401 + } +} \ No newline at end of file diff --git a/plugins/smoothing/src/main/java/info/nightscout/smoothing/ExponentialSmoothingPlugin.kt b/plugins/smoothing/src/main/java/info/nightscout/smoothing/ExponentialSmoothingPlugin.kt new file mode 100644 index 0000000000..23120d7b05 --- /dev/null +++ b/plugins/smoothing/src/main/java/info/nightscout/smoothing/ExponentialSmoothingPlugin.kt @@ -0,0 +1,141 @@ +package info.nightscout.smoothing + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.annotations.OpenForTesting +import info.nightscout.interfaces.iob.InMemoryGlucoseValue +import info.nightscout.interfaces.plugin.PluginBase +import info.nightscout.interfaces.plugin.PluginDescription +import info.nightscout.interfaces.plugin.PluginType +import info.nightscout.interfaces.smoothing.Smoothing +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.shared.interfaces.ResourceHelper +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.max +import kotlin.math.round + +@OpenForTesting +@Singleton +class ExponentialSmoothingPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rh: ResourceHelper +) : PluginBase( + PluginDescription() + .mainType(PluginType.SMOOTHING) + .pluginIcon(info.nightscout.core.ui.R.drawable.ic_timeline_24) + .pluginName(R.string.exponential_smoothing_name) + .shortName(R.string.smoothing_shortname) + .description(R.string.description_exponential_smoothing), + aapsLogger, rh, injector +), Smoothing { + + @Suppress("LocalVariableName") + override fun smooth(data: MutableList): MutableList { + /** + * TSUNAMI DATA SMOOTHING CORE + * + * Calculated a weighted average of 1st and 2nd order exponential smoothing functions + * to reduce the effect of sensor noise on APS performance. The weighted average + * is a compromise between the fast response to changing BGs at the cost of smoothness + * as offered by 1st order exponential smoothing, and the predictive, trend-sensitive but + * slower-to-respond smoothing as offered by 2nd order functions. + * + */ + val sizeRecords = data.size + val o1_sBG: ArrayList = ArrayList() //MP array for 1st order Smoothed Blood Glucose + val o2_sBG: ArrayList = ArrayList() //MP array for 2nd order Smoothed Blood Glucose + val o2_sD: ArrayList = ArrayList() //MP array for 2nd order Smoothed delta + val ssBG: ArrayList = ArrayList() //MP array for weighted averaged, doubly smoothed Blood Glucose + //val ssD: ArrayList = ArrayList() //MP array for deltas of doubly smoothed Blood Glucose + var windowSize = data.size //MP number of bg readings to include in smoothing window + val o1_weight = 0.4 + val o1_a = 0.5 + val o2_a = 0.4 + val o2_b = 1.0 + var insufficientSmoothingData = false + + // ADJUST SMOOTHING WINDOW TO ONLY INCLUDE VALID READINGS + // Valid readings include: + // - Values that actually exist (windowSize may not be larger than sizeRecords) + // - Values that come in approx. every 5 min. If the time gap between two readings is larger, this is likely due to a sensor error or warmup of a new sensor.d + // - Values that are not 38 mg/dl; 38 mg/dl reflects an xDrip error state (according to a comment in determine-basal.js) + + //MP: Adjust smoothing window if database size is smaller than the default value + 1 (+1 because the reading before the oldest reading to be smoothed will be used in the calculations + if (sizeRecords <= windowSize) { //MP standard smoothing window + windowSize = + (sizeRecords - 1).coerceAtLeast(0) //MP Adjust smoothing window to the size of database if it is smaller than the original window size; -1 to always have at least one older value to compare against as a buffer to prevent app crashes + } + + //MP: Adjust smoothing window further if a gap in the BG database is detected, e.g. due to sensor errors of sensor swaps, or if 38 mg/dl are reported (xDrip error state) + for (i in 0 until windowSize) { + if (round((data[i].timestamp - data[i + 1].timestamp) / (1000.0 * 60)) >= 12) { //MP: 12 min because a missed reading (i.e. readings coming in after 10 min) can occur for various reasons, like walking away from the phone or reinstalling AAPS + //if (Math.round((data.get(i).date - data.get(i + 1).date) / 60000L) <= 7) { //MP crashes the app, useful for testing + windowSize = + i + 1 //MP: If time difference between two readings exceeds 7 min, adjust windowSize to *include* the more recent reading (i = reading; +1 because windowSize reflects number of valid readings); + break + } else if (data[i].value == 38.0) { + windowSize = i //MP: 38 mg/dl reflects an xDrip error state; Chain of valid readings ends here, *exclude* this value (windowSize = i; i + 1 would include the current value) + break + } + } + + // CALCULATE SMOOTHING WINDOW - 1st order exponential smoothing + o1_sBG.clear() // MP reset smoothed bg array + + if (windowSize >= 4) { //MP: Require a valid windowSize of at least 4 readings + o1_sBG.add(data[windowSize - 1].value) //MP: Initialise smoothing with the oldest valid data point + for (i in 0 until windowSize) { //MP calculate smoothed bg window of valid readings + o1_sBG.add( + 0, + o1_sBG[0] + o1_a * (data[windowSize - 1 - i].value - o1_sBG[0]) + ) //MP build array of 1st order smoothed bgs + } + } else { + insufficientSmoothingData = true + } + + // CALCULATE SMOOTHING WINDOW - 2nd order exponential smoothing + if (windowSize >= 4) { //MP: Require a valid windowSize of at least 4 readings + o2_sBG.add(data[windowSize - 1].value) //MP Start 2nd order exponential data smoothing with the oldest valid bg + o2_sD.add(data[windowSize - 2].value - data[windowSize - 1].value) //MP Start 2nd order exponential data smoothing with the oldest valid delta + for (i in 0 until windowSize - 1) { //MP calculated smoothed bg window of last 1 h + o2_sBG.add( + 0, + o2_a * data[windowSize - 2 - i].value + (1 - o2_a) * (o2_sBG[0] + o2_sD[0]) + ) //MP build array of 2nd order smoothed bgs; windowSize-1 is the oldest valid bg value, so windowSize-2 is from when on the smoothing begins; + o2_sD.add( + 0, + o2_b * (o2_sBG[0] - o2_sBG[1]) + (1 - o2_b) * o2_sD[0] + ) //MP build array of 1st order smoothed bgs + } + } else { + insufficientSmoothingData = true + } + + // CALCULATE WEIGHTED AVERAGES OF GLUCOSE & DELTAS + //ssBG.clear() // MP reset doubly smoothed bg array + //ssD.clear() // MP reset doubly smoothed delta array + + if (!insufficientSmoothingData) { //MP Build doubly smoothed array only if there is enough valid readings + for (i in o2_sBG.indices) { //MP calculated doubly smoothed bg of all o1/o2 smoothed data available; o2 & o1 smoothbg array sizes are equal in size, so only one is used as a condition here + ssBG.add(o1_weight * o1_sBG[i] + (1 - o1_weight) * o2_sBG[i]) //MP build array of doubly smoothed bgs + } + /* + for (i in 0 until ssBG.size - 1) { + ssD.add(ssBG[i] - ssBG[i + 1]) //MP build array of doubly smoothed bg deltas + } + */ + for (i in 0 until minOf(ssBG.size, data.size)) { // noise at the beginning of the smoothing window is the greatest, so only include the 10 most recent values in the output + data[i].smoothed = max(round(ssBG[i]), 39.0) //Make 39 the smallest value as smaller values trigger errors (xDrip error state = 38) + } + } else { + for (i in 0 until data.size) { // noise at the beginning of the smoothing window is the greatest, so only include the 10 most recent values in the output + data[i].smoothed = max(data[i].value, 39.0) // if insufficient smoothing data, copy 'value' into 'smoothed' data column so that it isn't empty; Make 39 the smallest value as smaller + // values trigger errors (xDrip error state = 38) + } + } + + return data + } +} \ No newline at end of file diff --git a/plugins/smoothing/src/main/java/info/nightscout/smoothing/NoSmoothingPlugin.kt b/plugins/smoothing/src/main/java/info/nightscout/smoothing/NoSmoothingPlugin.kt new file mode 100644 index 0000000000..04efe0f078 --- /dev/null +++ b/plugins/smoothing/src/main/java/info/nightscout/smoothing/NoSmoothingPlugin.kt @@ -0,0 +1,33 @@ +package info.nightscout.smoothing + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.annotations.OpenForTesting +import info.nightscout.interfaces.iob.InMemoryGlucoseValue +import info.nightscout.interfaces.plugin.PluginBase +import info.nightscout.interfaces.plugin.PluginDescription +import info.nightscout.interfaces.plugin.PluginType +import info.nightscout.interfaces.smoothing.Smoothing +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.shared.interfaces.ResourceHelper +import javax.inject.Inject +import javax.inject.Singleton + +@OpenForTesting +@Singleton +class NoSmoothingPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rh: ResourceHelper +) : PluginBase( + PluginDescription() + .mainType(PluginType.SMOOTHING) + .pluginIcon(info.nightscout.core.ui.R.drawable.ic_timeline_24) + .setDefault(true) + .pluginName(R.string.no_smoothing_name) + .shortName(R.string.smoothing_shortname) + .description(R.string.description_no_smoothing), + aapsLogger, rh, injector +), Smoothing { + + override fun smooth(data: MutableList): MutableList = data +} \ No newline at end of file diff --git a/plugins/smoothing/src/main/res/values/strings.xml b/plugins/smoothing/src/main/res/values/strings.xml new file mode 100644 index 0000000000..19b45a22bd --- /dev/null +++ b/plugins/smoothing/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + + + SMOOTH + Exponential smoothing + "Second-order exponential smoothing algorithm" + Average smoothing + "Average smoothing algorithm, newest value is not affected" + No smoothing + "No smoothing performed on input glucose data. Use this when you already have filtered data e.g. from BYODA G6." + \ No newline at end of file diff --git a/plugins/source/src/main/java/info/nightscout/source/RandomBgPlugin.kt b/plugins/source/src/main/java/info/nightscout/source/RandomBgPlugin.kt index 2aeaf1b002..5888ec983b 100644 --- a/plugins/source/src/main/java/info/nightscout/source/RandomBgPlugin.kt +++ b/plugins/source/src/main/java/info/nightscout/source/RandomBgPlugin.kt @@ -23,6 +23,7 @@ import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.utils.T import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign +import java.lang.Math.random import java.util.Calendar import java.util.GregorianCalendar import javax.inject.Inject @@ -101,7 +102,7 @@ class RandomBgPlugin @Inject constructor( val cal = GregorianCalendar() val currentMinute = cal[Calendar.MINUTE] + (cal[Calendar.HOUR_OF_DAY] % 2) * 60 - val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 + val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 + (random() - 0.5) * (max - min) * 0.4 cal[Calendar.MILLISECOND] = 0 cal[Calendar.SECOND] = 0 diff --git a/settings.gradle b/settings.gradle index 994e5fa516..34dc7baf44 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,17 +13,17 @@ include ':core:ui' include ':core:validators' include ':database:entities' include ':database:impl' -include ':ui' -include ':implementation' include ':plugins:aps' include ':plugins:automation' include ':plugins:configuration' +include ':plugins:constraints' include ':plugins:insulin' include ':plugins:main' include ':plugins:openhumans' include ':plugins:sensitivity' +include ':plugins:smoothing' include ':plugins:source' -include ':plugins:constraints' +include ':plugins:sync' include ':pump:combo' include ':pump:combov2' include ':pump:combov2:comboctl' @@ -42,5 +42,6 @@ include ':pump:pump-common' include ':pump:pump-core' include ':pump:rileylink' include ':pump:virtual' -include ':plugins:sync' +include ':implementation' +include ':ui' include ':workflow' diff --git a/ui/src/main/java/info/nightscout/ui/dialogs/CarbsDialog.kt b/ui/src/main/java/info/nightscout/ui/dialogs/CarbsDialog.kt index 54444f68ec..6f764eabb3 100644 --- a/ui/src/main/java/info/nightscout/ui/dialogs/CarbsDialog.kt +++ b/ui/src/main/java/info/nightscout/ui/dialogs/CarbsDialog.kt @@ -192,7 +192,7 @@ class CarbsDialog : DialogFragmentWithDate() { } iobCobCalculator.ads.actualBg()?.let { bgReading -> - if (bgReading.value < 72) + if (bgReading.recalculated < 72) binding.hypoTt.isChecked = true } binding.hypoTt.setOnClickListener { diff --git a/ui/src/main/java/info/nightscout/ui/widget/Widget.kt b/ui/src/main/java/info/nightscout/ui/widget/Widget.kt index be5899e126..b5ff219736 100644 --- a/ui/src/main/java/info/nightscout/ui/widget/Widget.kt +++ b/ui/src/main/java/info/nightscout/ui/widget/Widget.kt @@ -35,6 +35,7 @@ import info.nightscout.interfaces.utils.TrendCalculator import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.extensions.toVisibility +import info.nightscout.shared.extensions.toVisibilityKeepSpace import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.utils.DateUtil @@ -125,20 +126,23 @@ class Widget : AppWidgetProvider() { private fun updateBg(views: RemoteViews) { val units = profileFunction.getUnits() - views.setTextViewText(R.id.bg, overviewData.lastBg?.valueToUnitsString(units) ?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)) + views.setTextViewText(R.id.bg, overviewData.lastBg(iobCobCalculator.ads)?.valueToUnitsString(units) ?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)) views.setTextColor( R.id.bg, when { - overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low) - overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high) - else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange) + overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low) + overviewData.isHigh(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_high) + else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange) } ) - views.setImageViewResource(R.id.arrow, trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon()) + trendCalculator.getTrendArrow(iobCobCalculator.ads)?.let { + views.setImageViewResource(R.id.arrow, it.directionToIcon()) + } + views.setViewVisibility(R.id.arrow, (trendCalculator.getTrendArrow(iobCobCalculator.ads) != null).toVisibilityKeepSpace()) views.setInt( R.id.arrow, "setColorFilter", when { - overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low) - overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high) - else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange) + overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low) + overviewData.isHigh(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_high) + else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange) } ) @@ -154,10 +158,10 @@ class Widget : AppWidgetProvider() { } // strike through if BG is old - if (!overviewData.isActualBg) views.setInt(R.id.bg, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG or Paint.ANTI_ALIAS_FLAG) + if (!overviewData.isActualBg(iobCobCalculator.ads)) views.setInt(R.id.bg, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG or Paint.ANTI_ALIAS_FLAG) else views.setInt(R.id.bg, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) - views.setTextViewText(R.id.time_ago, dateUtil.minAgo(rh, overviewData.lastBg?.timestamp)) + views.setTextViewText(R.id.time_ago, dateUtil.minAgo(rh, overviewData.lastBg(iobCobCalculator.ads)?.timestamp)) //views.setTextViewText(R.id.time_ago_short, "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")") } diff --git a/workflow/src/main/java/info/nightscout/workflow/LoadBgDataWorker.kt b/workflow/src/main/java/info/nightscout/workflow/LoadBgDataWorker.kt index 5d343f64cf..f577a6c40a 100644 --- a/workflow/src/main/java/info/nightscout/workflow/LoadBgDataWorker.kt +++ b/workflow/src/main/java/info/nightscout/workflow/LoadBgDataWorker.kt @@ -8,6 +8,7 @@ import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.database.impl.AppRepository import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.iob.IobCobCalculator +import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventBucketedDataCreated import info.nightscout.rx.logging.AAPSLogger @@ -25,6 +26,7 @@ class LoadBgDataWorker( @Inject lateinit var dateUtil: DateUtil @Inject lateinit var rxBus: RxBus @Inject lateinit var repository: AppRepository + @Inject lateinit var activePlugin: ActivePlugin class LoadBgData( val iobCobCalculator: IobCobCalculator, @@ -32,7 +34,7 @@ class LoadBgDataWorker( ) - private fun AutosensDataStore.loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil, rxBus: RxBus) { + private fun AutosensDataStore.loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil) { synchronized(dataLock) { val start = to - T.hours((24 + 10 /* max dia */).toLong()).msecs() // there can be some readings with time in close future (caused by wrong time setting on sensor) @@ -43,7 +45,15 @@ class LoadBgDataWorker( .filter { it.value >= 39 } aapsLogger.debug(LTag.AUTOSENS) { "BG data loaded. Size: ${bgReadings.size} Start date: ${dateUtil.dateAndTimeString(start)} End date: ${dateUtil.dateAndTimeString(to)}" } createBucketedData(aapsLogger, dateUtil) - rxBus.send(EventBucketedDataCreated()) + } + } + + private fun AutosensDataStore.smoothData(activePlugin: ActivePlugin) { + synchronized(dataLock) { + bucketedData?.let { + val smoothedData = activePlugin.activeSmoothing.smooth(it) + bucketedData = smoothedData + } } } @@ -52,7 +62,9 @@ class LoadBgDataWorker( val data = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as LoadBgData? ?: return Result.failure(workDataOf("Error" to "missing input data")) - data.iobCobCalculator.ads.loadBgData(data.end, repository, aapsLogger, dateUtil, rxBus) + data.iobCobCalculator.ads.loadBgData(data.end, repository, aapsLogger, dateUtil) + data.iobCobCalculator.ads.smoothData(activePlugin) + rxBus.send(EventBucketedDataCreated()) data.iobCobCalculator.clearCache() return Result.success() } diff --git a/workflow/src/main/java/info/nightscout/workflow/PrepareBucketedDataWorker.kt b/workflow/src/main/java/info/nightscout/workflow/PrepareBucketedDataWorker.kt index e05caf2414..ad86cd956f 100644 --- a/workflow/src/main/java/info/nightscout/workflow/PrepareBucketedDataWorker.kt +++ b/workflow/src/main/java/info/nightscout/workflow/PrepareBucketedDataWorker.kt @@ -10,6 +10,7 @@ import info.nightscout.core.graph.data.PointsWithLabelGraphSeries import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.interfaces.iob.IobCobCalculator +import info.nightscout.interfaces.profile.DefaultValueHelper import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.shared.interfaces.ResourceHelper import javax.inject.Inject @@ -22,6 +23,7 @@ class PrepareBucketedDataWorker( @Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var rh: ResourceHelper + @Inject lateinit var defaultValueHelper: DefaultValueHelper class PrepareBucketedData( val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance @@ -41,7 +43,7 @@ class PrepareBucketedDataWorker( val bucketedListArray: MutableList = ArrayList() for (inMemoryGlucoseValue in bucketedData) { if (inMemoryGlucoseValue.timestamp < data.overviewData.fromTime || inMemoryGlucoseValue.timestamp > data.overviewData.toTime) continue - bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh)) + bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, defaultValueHelper , profileFunction, rh)) } bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) } data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] }) diff --git a/workflow/src/main/java/info/nightscout/workflow/PrepareIobAutosensGraphDataWorker.kt b/workflow/src/main/java/info/nightscout/workflow/PrepareIobAutosensGraphDataWorker.kt index 27898f8724..2ae2fb32bc 100644 --- a/workflow/src/main/java/info/nightscout/workflow/PrepareIobAutosensGraphDataWorker.kt +++ b/workflow/src/main/java/info/nightscout/workflow/PrepareIobAutosensGraphDataWorker.kt @@ -85,6 +85,7 @@ class PrepareIobAutosensGraphDataWorker( override val duration = 0L override val shape = PointsWithLabelGraphSeries.Shape.IOB_PREDICTION override val size = 0.5f + override val paintStyle: Paint.Style = Paint.Style.FILL override fun color(context: Context?): Int = color fun setColor(color: Int): IobTotalDataPoint { @@ -107,6 +108,7 @@ class PrepareIobAutosensGraphDataWorker( override val duration = 0L override val shape = PointsWithLabelGraphSeries.Shape.COB_FAIL_OVER override val size = 0.5f + override val paintStyle: Paint.Style = Paint.Style.FILL override fun color(context: Context?): Int { return rh.gac(context, info.nightscout.core.ui.R.attr.cobColor) } diff --git a/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOref1Worker.kt b/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOref1Worker.kt index 9ce169cf21..ba77bddf29 100644 --- a/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOref1Worker.kt +++ b/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOref1Worker.kt @@ -122,14 +122,14 @@ class IobCobOref1Worker( //console.error(bgTime , bucketed_data[i].glucose); var avgDelta: Double var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { + val bg: Double = bucketedData[i].recalculated + if (bg < 39 || bucketedData[i + 3].recalculated < 39) { aapsLogger.error("! value < 39") continue } autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 + delta = bg - bucketedData[i + 1].recalculated + avgDelta = (bg - bucketedData[i + 3].recalculated) / 3 val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile) val bgi = -iob.activity * sens * 5 val deviation = delta - bgi diff --git a/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOrefWorker.kt b/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOrefWorker.kt index 538c179fe0..78a48d891c 100644 --- a/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOrefWorker.kt +++ b/workflow/src/main/java/info/nightscout/workflow/iob/IobCobOrefWorker.kt @@ -117,14 +117,14 @@ class IobCobOrefWorker @Inject internal constructor( //console.error(bgTime , bucketed_data[i].glucose); var avgDelta: Double var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { + val bg: Double = bucketedData[i].recalculated + if (bg < 39 || bucketedData[i + 3].recalculated < 39) { aapsLogger.error("! value < 39") continue } autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 + delta = bg - bucketedData[i + 1].recalculated + avgDelta = (bg - bucketedData[i + 3].recalculated) / 3 val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile) val bgi = -iob.activity * sens * 5 val deviation = delta - bgi