Merge pull request #2268 from nightscout/smoothing

Smoothing plugin
This commit is contained in:
Milos Kozak 2022-12-26 11:45:10 +01:00 committed by GitHub
commit 730fbd7001
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 903 additions and 304 deletions

View file

@ -177,7 +177,6 @@ allprojects {
} }
dependencies { dependencies {
implementation project(path: ':plugins:aps')
wearApp project(':wear') wearApp project(':wear')
// in order to use internet's versions you'd need to enable Jetifier again // 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:main')
implementation project(':plugins:openhumans') implementation project(':plugins:openhumans')
implementation project(':plugins:sensitivity') implementation project(':plugins:sensitivity')
implementation project(':plugins:smoothing')
implementation project(':plugins:source') implementation project(':plugins:source')
implementation project(':plugins:sync') implementation project(':plugins:sync')
implementation project(':implementation') implementation project(':implementation')

View file

@ -51,6 +51,9 @@ import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.sensitivity.SensitivityAAPSPlugin import info.nightscout.sensitivity.SensitivityAAPSPlugin
import info.nightscout.sensitivity.SensitivityOref1Plugin import info.nightscout.sensitivity.SensitivityOref1Plugin
import info.nightscout.sensitivity.SensitivityWeightedAveragePlugin 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.AidexPlugin
import info.nightscout.source.DexcomPlugin import info.nightscout.source.DexcomPlugin
import info.nightscout.source.GlimpPlugin import info.nightscout.source.GlimpPlugin
@ -434,6 +437,24 @@ abstract class PluginsListModule {
@IntKey(500) @IntKey(500)
abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase 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 @Qualifier
annotation class AllConfigs annotation class AllConfigs

View file

@ -23,7 +23,6 @@ import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.configBuilder.RunningConfiguration import info.nightscout.interfaces.configBuilder.RunningConfiguration
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.queue.Command import info.nightscout.interfaces.queue.Command
import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.queue.CommandQueue
@ -155,8 +154,7 @@ class KeepAliveWorker(
var shouldUploadStatus = false var shouldUploadStatus = false
if (config.NSCLIENT) return if (config.NSCLIENT) return
if (config.PUMPCONTROL) shouldUploadStatus = true if (config.PUMPCONTROL) shouldUploadStatus = true
else if (!(loop as PluginBase).isEnabled() || iobCobCalculator.ads.actualBg() == null) else if (!loop.isEnabled() || iobCobCalculator.ads.actualBg() == null) shouldUploadStatus = true
shouldUploadStatus = true
else if (dateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true else if (dateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
if (dateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) { if (dateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) {
lastIobUpload = dateUtil.now() lastIobUpload = dateUtil.now()

View file

@ -16,7 +16,9 @@ import info.nightscout.core.graph.data.ScaledDataPoint
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.TemporaryTarget import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.interfaces.aps.AutosensData import info.nightscout.interfaces.aps.AutosensData
import info.nightscout.interfaces.aps.AutosensDataStore
import info.nightscout.interfaces.iob.CobInfo import info.nightscout.interfaces.iob.CobInfo
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.iob.IobTotal import info.nightscout.interfaces.iob.IobTotal
@ -45,12 +47,12 @@ interface OverviewData {
* BG * BG
*/ */
val lastBg: GlucoseValue? fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue?
val isLow: Boolean fun isLow(autosensDataStore: AutosensDataStore): Boolean
val isHigh: Boolean fun isHigh(autosensDataStore: AutosensDataStore): Boolean
@ColorInt fun lastBgColor(context: Context?): Int @ColorInt fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int
val lastBgDescription: String fun lastBgDescription(autosensDataStore: AutosensDataStore): String
val isActualBg: Boolean fun isActualBg(autosensDataStore: AutosensDataStore): Boolean
/* /*
* TEMPORARY BASAL * TEMPORARY BASAL
*/ */

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.Bolus import info.nightscout.database.entities.Bolus
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.DefaultValueHelper import info.nightscout.interfaces.profile.DefaultValueHelper
@ -22,7 +23,7 @@ class BolusDataPoint(
get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh) get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
override val duration = 0L override val duration = 0L
override val size = 2f override val size = 2f
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
override val shape override val shape
get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.core.graph.R import info.nightscout.core.graph.R
import info.nightscout.database.entities.Carbs import info.nightscout.database.entities.Carbs
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
@ -18,6 +19,7 @@ class CarbsDataPoint(
override val duration = 0L override val duration = 0L
override val size = 2f override val size = 2f
override val shape = PointsWithLabelGraphSeries.Shape.CARBS override val shape = PointsWithLabelGraphSeries.Shape.CARBS
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
override fun color(context: Context?): Int { 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) 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)

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import com.jjoe64.graphview.series.DataPointInterface import com.jjoe64.graphview.series.DataPointInterface
interface DataPointWithLabelInterface : DataPointInterface { interface DataPointWithLabelInterface : DataPointInterface {
@ -13,5 +14,6 @@ interface DataPointWithLabelInterface : DataPointInterface {
val duration: Long val duration: Long
val shape: PointsWithLabelGraphSeries.Shape val shape: PointsWithLabelGraphSeries.Shape
val size: Float val size: Float
val paintStyle: Paint.Style
fun color(context: Context?): Int fun color(context: Context?): Int
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.EffectiveProfileSwitch import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
@ -22,6 +23,7 @@ class EffectiveProfileSwitchDataPoint(
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
override val size = 2f override val size = 2f
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
override fun color(context: Context?): Int { override fun color(context: Context?): Int {
return rh.gac(context, info.nightscout.core.ui.R.attr.profileSwitchColor) return rh.gac(context, info.nightscout.core.ui.R.attr.profileSwitchColor)
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.ExtendedBolus import info.nightscout.database.entities.ExtendedBolus
import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
@ -18,6 +19,7 @@ class ExtendedBolusDataPoint(
override val duration get() = data.duration override val duration get() = data.duration
override val size = 10f override val size = 10f
override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
override fun color(context: Context?): Int { override fun color(context: Context?): Int {
return rh.gac(context, info.nightscout.core.ui.R.attr.extBolusColor) return rh.gac(context, info.nightscout.core.ui.R.attr.extBolusColor)
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.GlucoseUnit
@ -16,7 +17,7 @@ class GlucoseValueDataPoint(
private val rh: ResourceHelper private val rh: ResourceHelper
) : DataPointWithLabelInterface { ) : 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 if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
override fun getX(): Double = data.timestamp.toDouble() override fun getX(): Double = data.timestamp.toDouble()
@ -26,16 +27,13 @@ class GlucoseValueDataPoint(
override val label: String = Profile.toCurrentUnitsString(profileFunction, data.value) override val label: String = Profile.toCurrentUnitsString(profileFunction, data.value)
override val duration = 0L override val duration = 0L
override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG 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 { override fun color(context: Context?): Int {
val units = profileFunction.getUnits()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
return when { return when {
isPrediction -> predictionColor(context) isPrediction -> predictionColor(context)
valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) else -> rh.gac(context, info.nightscout.core.ui.R.attr.originalBgValueColor)
valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
} }
} }

View file

@ -1,20 +1,23 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
class InMemoryGlucoseValueDataPoint( class InMemoryGlucoseValueDataPoint(
val data: InMemoryGlucoseValue, val data: InMemoryGlucoseValue,
private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val rh: ResourceHelper private val rh: ResourceHelper
) : DataPointWithLabelInterface { ) : 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 if (units == GlucoseUnit.MGDL) data.recalculated else data.recalculated * Constants.MGDL_TO_MMOLL
override fun getX(): Double = data.timestamp.toDouble() override fun getX(): Double = data.timestamp.toDouble()
override fun getY(): Double = valueToUnits(profileFunction.getUnits()) override fun getY(): Double = valueToUnits(profileFunction.getUnits())
@ -22,8 +25,18 @@ class InMemoryGlucoseValueDataPoint(
override val label: String = "" override val label: String = ""
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG 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 { 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)
}
} }
} }

View file

@ -179,20 +179,20 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
// draw data point // draw data point
if (!overdraw) { if (!overdraw) {
if (value.getShape() == Shape.BG || value.getShape() == Shape.COB_FAIL_OVER) { if (value.getShape() == Shape.BG || value.getShape() == Shape.COB_FAIL_OVER) {
mPaint.setStyle(Paint.Style.FILL); mPaint.setStyle(value.getPaintStyle());
mPaint.setStrokeWidth(0); mPaint.setStrokeWidth(0);
canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint); canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint);
} else if (value.getShape() == Shape.BG || value.getShape() == Shape.IOB_PREDICTION || value.getShape() == Shape.BUCKETED_BG) { } else if (value.getShape() == Shape.BG || value.getShape() == Shape.IOB_PREDICTION || value.getShape() == Shape.BUCKETED_BG) {
mPaint.setColor(value.color(graphView.getContext())); mPaint.setColor(value.color(graphView.getContext()));
mPaint.setStyle(Paint.Style.FILL); mPaint.setStyle(value.getPaintStyle());
mPaint.setStrokeWidth(0); mPaint.setStrokeWidth(0);
canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint); canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint);
} else if (value.getShape() == Shape.PREDICTION) { } else if (value.getShape() == Shape.PREDICTION) {
mPaint.setColor(value.color(graphView.getContext())); mPaint.setColor(value.color(graphView.getContext()));
mPaint.setStyle(Paint.Style.FILL); mPaint.setStyle(value.getPaintStyle());
mPaint.setStrokeWidth(0); mPaint.setStrokeWidth(0);
canvas.drawCircle(endX, endY, scaledPxSize, mPaint); canvas.drawCircle(endX, endY, scaledPxSize, mPaint);
mPaint.setStyle(Paint.Style.FILL); mPaint.setStyle(value.getPaintStyle());
mPaint.setStrokeWidth(0); mPaint.setStrokeWidth(0);
canvas.drawCircle(endX, endY, scaledPxSize / 3, mPaint); canvas.drawCircle(endX, endY, scaledPxSize / 3, mPaint);
} else if (value.getShape() == Shape.RECTANGLE) { } else if (value.getShape() == Shape.RECTANGLE) {

View file

@ -1,6 +1,7 @@
package info.nightscout.core.graph.data package info.nightscout.core.graph.data
import android.content.Context import android.content.Context
import android.graphics.Paint
import info.nightscout.database.entities.TherapyEvent import info.nightscout.database.entities.TherapyEvent
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.Translator import info.nightscout.interfaces.Translator
@ -55,6 +56,7 @@ class TherapyEventDataPoint(
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERAL_WITH_DURATION duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERAL_WITH_DURATION
else -> PointsWithLabelGraphSeries.Shape.GENERAL 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 val size get() = if (rh.gb(info.nightscout.shared.R.bool.isTablet)) 12.0f else 10.0f
override fun color(context: Context?): Int { override fun color(context: Context?): Int {

View file

@ -15,8 +15,18 @@ interface AutosensDataStore {
var bucketedData: MutableList<InMemoryGlucoseValue>? var bucketedData: MutableList<InMemoryGlucoseValue>?
var lastUsed5minCalculation: Boolean? 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 lastDataTime(dateUtil: DateUtil): String
fun clone(): AutosensDataStore fun clone(): AutosensDataStore
fun getBgReadingsDataTableCopy(): List<GlucoseValue> fun getBgReadingsDataTableCopy(): List<GlucoseValue>

View file

@ -2,8 +2,9 @@ package info.nightscout.interfaces.iob
import info.nightscout.database.entities.GlucoseValue 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) constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value, gv.trendArrow)
// var generated : value doesn't correspond to real value with timestamp close to real BG
val recalculated: Double get() = smoothed ?: value
} }

View file

@ -9,6 +9,7 @@ import info.nightscout.interfaces.insulin.Insulin
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.profile.ProfileSource
import info.nightscout.interfaces.pump.Pump import info.nightscout.interfaces.pump.Pump
import info.nightscout.interfaces.smoothing.Smoothing
import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
@ -74,6 +75,11 @@ interface ActivePlugin {
*/ */
val activeObjectives: Objectives? val activeObjectives: Objectives?
/**
* Smoothing plugin
*/
val activeSmoothing: Smoothing
/** /**
* Currently selected NsClient plugin * Currently selected NsClient plugin
*/ */

View file

@ -6,5 +6,5 @@ package info.nightscout.interfaces.plugin
* set by [info.nightscout.interfaces.PluginDescription.mainType] * set by [info.nightscout.interfaces.PluginDescription.mainType]
*/ */
enum class PluginType { enum class PluginType {
GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC, SMOOTHING
} }

View file

@ -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<InMemoryGlucoseValue>): MutableList<InMemoryGlucoseValue>
}

View file

@ -1,6 +1,8 @@
package info.nightscout.interfaces.utils package info.nightscout.interfaces.utils
import info.nightscout.database.entities.GlucoseValue 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 * Convert BG direction value to trend arrow or calculate it if not provided
@ -15,6 +17,20 @@ interface TrendCalculator {
* @return TrendArrow * @return TrendArrow
*/ */
fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.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 * Provide or calculate trend
@ -23,4 +39,11 @@ interface TrendCalculator {
* @return string description of TrendArrow * @return string description of TrendArrow
*/ */
fun getTrendDescription(glucoseValue: GlucoseValue?): String 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
} }

View file

@ -4,6 +4,7 @@ package info.nightscout.core.extensions
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject import org.json.JSONObject
@ -26,3 +27,12 @@ fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject =
.put("direction", trendArrow.text) .put("direction", trendArrow.text)
.put("type", "sgv") .put("type", "sgv")
.also { if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) } .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)

View file

@ -6,10 +6,10 @@ import info.nightscout.core.extensions.valueToUnits
import info.nightscout.core.iob.round import info.nightscout.core.iob.round
import info.nightscout.core.utils.MidnightUtils import info.nightscout.core.utils.MidnightUtils
import info.nightscout.database.ValueWrapper import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.aps.Loop import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.db.PersistenceLayer import info.nightscout.interfaces.db.PersistenceLayer
import info.nightscout.interfaces.iob.GlucoseStatusProvider import info.nightscout.interfaces.iob.GlucoseStatusProvider
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.profile.Profile 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 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 dbRecord = persistenceLayer.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null
//BG //BG

View file

@ -59,6 +59,7 @@ data class RemoteDeviceStatus(
@SerializedName("version") val version: String?, @SerializedName("version") val version: String?,
@SerializedName("insulin") val insulin: Int?, @SerializedName("insulin") val insulin: Int?,
@SerializedName("sensitivity") val sensitivity: Int?, @SerializedName("sensitivity") val sensitivity: Int?,
@SerializedName("smoothing") val smoothing: String?,
@Contextual @SerializedName("insulinConfiguration") val insulinConfiguration: JSONObject?, @Contextual @SerializedName("insulinConfiguration") val insulinConfiguration: JSONObject?,
@Contextual @SerializedName("sensitivityConfiguration") val sensitivityConfiguration: JSONObject?, @Contextual @SerializedName("sensitivityConfiguration") val sensitivityConfiguration: JSONObject?,
@Contextual @SerializedName("overviewConfiguration") val overviewConfiguration: JSONObject?, @Contextual @SerializedName("overviewConfiguration") val overviewConfiguration: JSONObject?,

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M23,8c0,1.1 -0.9,2 -2,2c-0.18,0 -0.35,-0.02 -0.51,-0.07l-3.56,3.55C16.98,13.64 17,13.82 17,14c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-0.18 0.02,-0.36 0.07,-0.52l-2.55,-2.55C10.36,10.98 10.18,11 10,11s-0.36,-0.02 -0.52,-0.07l-4.55,4.56C4.98,15.65 5,15.82 5,16c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2s0.9,-2 2,-2c0.18,0 0.35,0.02 0.51,0.07l4.56,-4.55C8.02,9.36 8,9.18 8,9c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,0.18 -0.02,0.36 -0.07,0.52l2.55,2.55C14.64,12.02 14.82,12 15,12s0.36,0.02 0.52,0.07l3.55,-3.56C19.02,8.35 19,8.18 19,8c0,-1.1 0.9,-2 2,-2S23,6.9 23,8z"/>
</vector>

View file

@ -206,7 +206,7 @@
<item name="smbColor">@color/tempbasal</item> <item name="smbColor">@color/tempbasal</item>
<item name="bolusDataPointColor">@color/pumpHistory</item> <item name="bolusDataPointColor">@color/pumpHistory</item>
<item name="profileSwitchColor">@color/profileSwitch</item> <item name="profileSwitchColor">@color/profileSwitch</item>
<item name="inMemoryColor">@color/white</item> <item name="originalBgValueColor">@color/white</item>
<item name="therapyEvent_NS_MBG">@color/red</item> <item name="therapyEvent_NS_MBG">@color/red</item>
<item name="therapyEvent_FINGER_STICK_BG_VALUE">@color/red</item> <item name="therapyEvent_FINGER_STICK_BG_VALUE">@color/red</item>
<item name="therapyEvent_EXERCISE">@color/blue</item> <item name="therapyEvent_EXERCISE">@color/blue</item>

View file

@ -180,7 +180,7 @@
<attr name="smbColor" format="reference|color" /> <attr name="smbColor" format="reference|color" />
<attr name="bolusDataPointColor" format="reference|color" /> <attr name="bolusDataPointColor" format="reference|color" />
<attr name="profileSwitchColor" format="reference|color" /> <attr name="profileSwitchColor" format="reference|color" />
<attr name="inMemoryColor" format="reference|color" /> <attr name="originalBgValueColor" format="reference|color" />
<attr name="therapyEvent_NS_MBG" format="reference|color" /> <attr name="therapyEvent_NS_MBG" format="reference|color" />
<attr name="therapyEvent_FINGER_STICK_BG_VALUE" format="reference|color" /> <attr name="therapyEvent_FINGER_STICK_BG_VALUE" format="reference|color" />
<attr name="therapyEvent_EXERCISE" format="reference|color" /> <attr name="therapyEvent_EXERCISE" format="reference|color" />

View file

@ -209,7 +209,7 @@
<item name="smbColor">@color/tempbasal</item> <item name="smbColor">@color/tempbasal</item>
<item name="bolusDataPointColor">@color/pumpHistory</item> <item name="bolusDataPointColor">@color/pumpHistory</item>
<item name="profileSwitchColor">@color/profileSwitch</item> <item name="profileSwitchColor">@color/profileSwitch</item>
<item name="inMemoryColor">@color/white</item> <item name="originalBgValueColor">@color/white</item>
<item name="therapyEvent_NS_MBG">@color/red</item> <item name="therapyEvent_NS_MBG">@color/red</item>
<item name="therapyEvent_FINGER_STICK_BG_VALUE">@color/red</item> <item name="therapyEvent_FINGER_STICK_BG_VALUE">@color/red</item>
<item name="therapyEvent_EXERCISE">@color/blue</item> <item name="therapyEvent_EXERCISE">@color/blue</item>

View file

@ -2,6 +2,8 @@ package info.nightscout.implementation
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.impl.AppRepository 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.interfaces.utils.TrendCalculator
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
@ -15,6 +17,13 @@ class TrendCalculatorImpl @Inject constructor(
) : TrendCalculator { ) : TrendCalculator {
override fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow = 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 { when {
glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE
glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow 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) 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 toTime = glucoseValue.timestamp
val readings = repository.compatGetBgReadingsDataFromTime(toTime - T.mins(10).msecs(), toTime, false).blockingGet() val readings = repository.compatGetBgReadingsDataFromTime(toTime - T.mins(10).msecs(), toTime, false).blockingGet()
@ -62,4 +71,55 @@ class TrendCalculatorImpl @Inject constructor(
else -> GlucoseValue.TrendArrow.NONE 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<InMemoryGlucoseValue>): 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
}
}
} }

View file

@ -25,7 +25,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
get() = getGlucoseStatusData() get() = getGlucoseStatusData()
override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? { override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? {
val data = iobCobCalculator.ads.getBgReadingsDataTableCopy() val data = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return null
val sizeRecords = data.size val sizeRecords = data.size
if (sizeRecords == 0) { if (sizeRecords == 0) {
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0") aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0")
@ -41,7 +41,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
if (sizeRecords == 1) { if (sizeRecords == 1) {
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1") aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1")
return GlucoseStatus( return GlucoseStatus(
glucose = now.value, glucose = now.recalculated,
noise = 0.0, noise = 0.0,
delta = 0.0, delta = 0.0,
shortAvgDelta = 0.0, shortAvgDelta = 0.0,
@ -49,31 +49,30 @@ class GlucoseStatusProviderImpl @Inject constructor(
date = nowDate date = nowDate
).asRounded() ).asRounded()
} }
val nowValueList = ArrayList<Double>()
val lastDeltas = ArrayList<Double>() val lastDeltas = ArrayList<Double>()
val shortDeltas = ArrayList<Double>() val shortDeltas = ArrayList<Double>()
val longDeltas = ArrayList<Double>() val longDeltas = ArrayList<Double>()
// Use the latest sgv value in the now calculations // Use the latest sgv value in the now calculations
nowValueList.add(now.value)
for (i in 1 until sizeRecords) { for (i in 1 until sizeRecords) {
if (data[i].value > 38) { if (data[i].recalculated > 38) {
val then = data[i] val then = data[i]
val thenDate = then.timestamp val thenDate = then.timestamp
val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong() val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong()
// multiply by 5 to get the same units as delta, i.e. mg/dL/5m // 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 val avgDel = change / minutesAgo * 5
aapsLogger.debug(LTag.GLUCOSE, "$then minutesAgo=$minutesAgo avgDelta=$avgDel") 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 // use the average of all data points in the last 2.5m for all further "now" calculations
if (0 < minutesAgo && minutesAgo < 2.5) { // if (0 < minutesAgo && minutesAgo < 2.5) {
// Keep and average all values within the last 2.5 minutes // // Keep and average all values within the last 2.5 minutes
nowValueList.add(then.value) // nowValueList.add(then.recalculated)
now.value = average(nowValueList) // now.value = average(nowValueList)
// short_deltas are calculated from everything ~5-15 minutes ago // // short_deltas are calculated from everything ~5-15 minutes ago
} else if (2.5 < minutesAgo && minutesAgo < 17.5) { // } else
if (2.5 < minutesAgo && minutesAgo < 17.5) {
//console.error(minutesAgo, avgDelta); //console.error(minutesAgo, avgDelta);
shortDeltas.add(avgDel) shortDeltas.add(avgDel)
// last_deltas are calculated from everything ~5 minutes ago // last_deltas are calculated from everything ~5 minutes ago
@ -96,7 +95,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
average(lastDeltas) average(lastDeltas)
} }
return GlucoseStatus( return GlucoseStatus(
glucose = now.value, glucose = now.recalculated,
date = nowDate, date = nowDate,
noise = 0.0, //for now set to nothing as not all CGMs report noise noise = 0.0, //for now set to nothing as not all CGMs report noise
shortAvgDelta = shortAverageDelta, shortAvgDelta = shortAverageDelta,
@ -105,6 +104,89 @@ class GlucoseStatusProviderImpl @Inject constructor(
).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded() ).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<Double>()
val lastDeltas = ArrayList<Double>()
val shortDeltas = ArrayList<Double>()
val longDeltas = ArrayList<Double>()
// 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 { companion object {
fun average(array: ArrayList<Double>): Double { fun average(array: ArrayList<Double>): Double {

View file

@ -26,7 +26,9 @@ import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.TemporaryTarget import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.aps.AutosensData import info.nightscout.interfaces.aps.AutosensData
import info.nightscout.interfaces.aps.AutosensDataStore
import info.nightscout.interfaces.iob.CobInfo import info.nightscout.interfaces.iob.CobInfo
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.iob.IobTotal import info.nightscout.interfaces.iob.IobTotal
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
@ -119,43 +121,42 @@ class OverviewDataImpl @Inject constructor(
* BG * BG
*/ */
override val lastBg: GlucoseValue? override fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue? =
get() = autosensDataStore.bucketedData?.let { if (it.size > 0) it[0] else null }
repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped -> // repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped ->
if (gvWrapped is ValueWrapper.Existing) gvWrapped.value // if (gvWrapped is ValueWrapper.Existing) gvWrapped.value
else null // else null
} // }
override val isLow: Boolean override fun isLow(autosensDataStore: AutosensDataStore): Boolean =
get() = lastBg?.let { lastBg -> lastBg(autosensDataStore)?.let { lastBg ->
lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine()
} ?: false } ?: false
override val isHigh: Boolean override fun isHigh(autosensDataStore: AutosensDataStore): Boolean =
get() = lastBg?.let { lastBg -> lastBg(autosensDataStore)?.let { lastBg ->
lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine()
} ?: false } ?: false
@ColorInt @ColorInt
override fun lastBgColor(context: Context?): Int = override fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int =
when { when {
isLow -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow) isLow(autosensDataStore) -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
isHigh -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor) isHigh(autosensDataStore) -> 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.bgInRange)
} }
override val lastBgDescription: String override fun lastBgDescription(autosensDataStore: AutosensDataStore): String =
get() = when { when {
isLow -> rh.gs(info.nightscout.core.ui.R.string.a11y_low) isLow(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_low)
isHigh -> rh.gs(info.nightscout.core.ui.R.string.a11y_high) isHigh(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_high)
else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange) else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange)
} }
override val isActualBg: Boolean override fun isActualBg(autosensDataStore: AutosensDataStore): Boolean =
get() = lastBg(autosensDataStore)?.let { lastBg ->
lastBg?.let { lastBg -> lastBg.timestamp > dateUtil.now() - T.mins(9).msecs()
lastBg.timestamp > dateUtil.now() - T.mins(9).msecs() } ?: false
} ?: false
/* /*
* TEMPORARY BASAL * TEMPORARY BASAL

View file

@ -14,6 +14,7 @@ import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.profile.ProfileSource
import info.nightscout.interfaces.pump.Pump import info.nightscout.interfaces.pump.Pump
import info.nightscout.interfaces.smoothing.Smoothing
import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync import info.nightscout.interfaces.sync.Sync
@ -36,6 +37,7 @@ class PluginStore @Inject constructor(
private var activeAPSStore: APS? = null private var activeAPSStore: APS? = null
private var activeInsulinStore: Insulin? = null private var activeInsulinStore: Insulin? = null
private var activeSensitivityStore: Sensitivity? = null private var activeSensitivityStore: Sensitivity? = null
private var activeSmoothingStore: Smoothing? = null
override fun loadDefaults() { override fun loadDefaults() {
verifySelectionInCategories() verifySelectionInCategories()
@ -106,6 +108,16 @@ class PluginStore @Inject constructor(
} }
setFragmentVisibilities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY) 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 // PluginType.PROFILE
pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE) pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE)
activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource? activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource?
@ -181,6 +193,10 @@ class PluginStore @Inject constructor(
get() = activeSensitivityStore get() = activeSensitivityStore
?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" } ?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" }
override val activeSmoothing: Smoothing
get() = activeSmoothingStore
?: checkNotNull(activeSmoothingStore) { "No smoothing selected" }
override val activeOverview: Overview override val activeOverview: Overview
get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview
@ -190,8 +206,7 @@ class PluginStore @Inject constructor(
override val activeIobCobCalculator: IobCobCalculator override val activeIobCobCalculator: IobCobCalculator
get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator
override val activeObjectives: Objectives? override val activeObjectives: Objectives?
get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives?
override val activeNsClient: NsClient? override val activeNsClient: NsClient?
get() = getTheOneEnabledInArray(getSpecificPluginsListByInterface(NsClient::class.java), PluginType.SYNC) as NsClient? get() = getTheOneEnabledInArray(getSpecificPluginsListByInterface(NsClient::class.java), PluginType.SYNC) as NsClient?

View file

@ -6,6 +6,7 @@ import info.nightscout.core.iob.log
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.aps.AutosensDataStore
import info.nightscout.interfaces.iob.GlucoseStatus import info.nightscout.interfaces.iob.GlucoseStatus
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
@ -40,7 +41,7 @@ class GlucoseStatusTest : TestBase() {
} }
@Test fun calculateValidGlucoseStatus() { @Test fun calculateValidGlucoseStatus() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateValidBgData())
val glucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! val glucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001) Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001)
Assertions.assertEquals(-2.0, glucoseStatus.delta, 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(-2.0, glucoseStatus.longAvgDelta, 0.001) // -2 -2 -2 -2
Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date
} }
/*
Not testing anymore, not valid for bucketed data
@Test fun calculateMostRecentGlucoseStatus() { @Test fun calculateMostRecentGlucoseStatus() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateMostRecentBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateMostRecentBgData())
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
Assertions.assertEquals(215.0, glucoseStatus.glucose, 0.001) // (214+216) / 2 Assertions.assertEquals(215.0, glucoseStatus.glucose, 0.001) // (214+216) / 2
Assertions.assertEquals(-1.0, glucoseStatus.delta, 0.001) 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 Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date, even when averaging
} }
private fun generateMostRecentBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<InMemoryGlucoseValue> = 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() { @Test fun oneRecordShouldProduceZeroDeltas() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001) Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001)
Assertions.assertEquals(0.0, glucoseStatus.delta, 0.001) Assertions.assertEquals(0.0, glucoseStatus.delta, 0.001)
@ -70,19 +82,19 @@ class GlucoseStatusTest : TestBase() {
} }
@Test fun insufficientDataShouldReturnNull() { @Test fun insufficientDataShouldReturnNull() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateInsufficientBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateInsufficientBgData())
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
Assertions.assertEquals(null, glucoseStatus) Assertions.assertEquals(null, glucoseStatus)
} }
@Test fun oldDataShouldReturnNull() { @Test fun oldDataShouldReturnNull() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData())
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
Assertions.assertEquals(null, glucoseStatus) Assertions.assertEquals(null, glucoseStatus)
} }
@Test fun returnOldDataIfAllowed() { @Test fun returnOldDataIfAllowed() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData())
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).getGlucoseStatusData(true) val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).getGlucoseStatusData(true)
Assertions.assertNotEquals(null, glucoseStatus) Assertions.assertNotEquals(null, glucoseStatus)
} }
@ -91,8 +103,11 @@ class GlucoseStatusTest : TestBase() {
Assertions.assertEquals(0.0, GlucoseStatusProviderImpl.average(ArrayList()), 0.001) Assertions.assertEquals(0.0, GlucoseStatusProviderImpl.average(ArrayList()), 0.001)
} }
/*
Not testing anymore, not valid for bucketed data
@Test fun calculateGlucoseStatusForLibreTestBgData() { @Test fun calculateGlucoseStatusForLibreTestBgData() {
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateLibreTestData()) Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateLibreTestData())
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!! val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
Assertions.assertEquals(100.0, glucoseStatus.glucose, 0.001) // Assertions.assertEquals(100.0, glucoseStatus.glucose, 0.001) //
Assertions.assertEquals(-10.0, glucoseStatus.delta, 0.001) Assertions.assertEquals(-10.0, glucoseStatus.delta, 0.001)
@ -101,6 +116,23 @@ class GlucoseStatusTest : TestBase() {
Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date
} }
private fun generateLibreTestData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<InMemoryGlucoseValue> = 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 @BeforeEach
fun initMocking() { fun initMocking() {
Mockito.`when`(dateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs()) 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}] // [{"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<GlucoseValue> { private fun generateValidBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = ArrayList() val list: MutableList<InMemoryGlucoseValue> = 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(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, 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(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766600000, 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(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, 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(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, 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(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, 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(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, 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(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, 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)) list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
return list return list
} }
private fun generateMostRecentBgData(): List<GlucoseValue> { private fun generateInsufficientBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = 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<GlucoseValue> {
return ArrayList() return ArrayList()
} }
private fun generateOldBgData(): List<GlucoseValue> { private fun generateOldBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = ArrayList() val list: MutableList<InMemoryGlucoseValue> = ArrayList()
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
return list return list
} }
private fun generateOneCurrentRecordBgData(): List<GlucoseValue> { private fun generateOneCurrentRecordBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = ArrayList() val list: MutableList<InMemoryGlucoseValue> = 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(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT))
return list
}
private fun generateLibreTestData(): List<GlucoseValue> {
val list: MutableList<GlucoseValue> = 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
)
)
return list return list
} }
} }

View file

@ -4,8 +4,9 @@ import com.google.common.base.Optional
import info.nightscout.automation.elements.Comparator import info.nightscout.automation.elements.Comparator
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import org.json.JSONObject 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.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
@ -22,39 +23,39 @@ class TriggerBgTest : TriggerTestBase() {
@Test @Test
fun shouldRunTest() { 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) 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) 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) 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) 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) 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) 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) 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) 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) t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
Assert.assertFalse(t.shouldRun()) Assertions.assertFalse(t.shouldRun())
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList()) `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList())
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) 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) t = TriggerBg(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
Assert.assertTrue(t.shouldRun()) Assertions.assertTrue(t.shouldRun())
} }
@Test @Test
fun copyConstructorTest() { fun copyConstructorTest() {
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
val t1 = t.duplicate() as TriggerBg val t1 = t.duplicate() as TriggerBg
Assert.assertEquals(213.0, t1.bg.value, 0.01) Assertions.assertEquals(213.0, t1.bg.value, 0.01)
Assert.assertEquals(GlucoseUnit.MGDL, t1.bg.units) Assertions.assertEquals(GlucoseUnit.MGDL, t1.bg.units)
Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) 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\"}" private var bgJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"bg\":4.1,\"units\":\"mmol\"},\"type\":\"TriggerBg\"}"
@ -62,35 +63,26 @@ class TriggerBgTest : TriggerTestBase() {
@Test @Test
fun toJSONTest() { fun toJSONTest() {
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL) 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 @Test
fun fromJSONTest() { fun fromJSONTest() {
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL) 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 val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerBg
Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
Assert.assertEquals(4.1, t2.bg.value, 0.01) Assertions.assertEquals(4.1, t2.bg.value, 0.01)
Assert.assertEquals(GlucoseUnit.MMOL, t2.bg.units) Assertions.assertEquals(GlucoseUnit.MMOL, t2.bg.units)
} }
@Test @Test
fun iconTest() { 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<GlucoseValue> { private fun generateOneCurrentRecordBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = ArrayList() val list: MutableList<InMemoryGlucoseValue> = ArrayList()
list.add( list.add(InMemoryGlucoseValue(value = 214.0, timestamp = now - 1, trendArrow = GlucoseValue.TrendArrow.FLAT))
GlucoseValue(
raw = 0.0,
noise = 0.0,
value = 214.0,
timestamp = now - 1,
sourceSensor = GlucoseValue.SourceSensor.UNKNOWN,
trendArrow = GlucoseValue.TrendArrow.FLAT
)
)
return list return list
} }
} }

View file

@ -6,8 +6,9 @@ import info.nightscout.automation.elements.Comparator
import info.nightscout.automation.elements.InputDelta.DeltaType import info.nightscout.automation.elements.InputDelta.DeltaType
import info.nightscout.database.entities.GlucoseValue import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.GlucoseUnit import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import org.json.JSONObject 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.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
@ -23,42 +24,42 @@ class TriggerDeltaTest : TriggerTestBase() {
} }
@Test fun shouldRunTest() { @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) var t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(73.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
Assert.assertFalse(t.shouldRun()) Assertions.assertFalse(t.shouldRun())
Assert.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType) 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) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-2.0, DeltaType.SHORT_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
Assert.assertFalse(t.shouldRun()) Assertions.assertFalse(t.shouldRun())
Assert.assertEquals(DeltaType.SHORT_AVERAGE, t.delta.deltaType) 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) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-3.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
Assert.assertTrue(t.shouldRun()) Assertions.assertTrue(t.shouldRun())
Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType) 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) 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) 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) 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) 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) 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) t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-0.2, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
Assert.assertTrue(t.shouldRun()) Assertions.assertTrue(t.shouldRun())
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList()) `when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList())
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) 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) t = TriggerDelta(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
Assert.assertTrue(t.shouldRun()) Assertions.assertTrue(t.shouldRun())
} }
@Test fun copyConstructorTest() { @Test fun copyConstructorTest() {
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) 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 val t1 = t.duplicate() as TriggerDelta
Assert.assertEquals(213.0, t1.delta.value, 0.01) Assertions.assertEquals(213.0, t1.delta.value, 0.01)
Assert.assertEquals(GlucoseUnit.MGDL, t1.units) Assertions.assertEquals(GlucoseUnit.MGDL, t1.units)
Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType) Assertions.assertEquals(DeltaType.DELTA, t.delta.deltaType)
Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value) 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\"}" 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 @Test
fun toJSONTest() { fun toJSONTest() {
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL) 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 @Test
fun fromJSONTest() { fun fromJSONTest() {
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MMOL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL) 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 val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerDelta
Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
Assert.assertEquals(4.1, t2.delta.value, 0.01) Assertions.assertEquals(4.1, t2.delta.value, 0.01)
Assert.assertEquals(GlucoseUnit.MMOL, t2.units) Assertions.assertEquals(GlucoseUnit.MMOL, t2.units)
Assert.assertEquals(DeltaType.DELTA, t2.delta.deltaType) Assertions.assertEquals(DeltaType.DELTA, t2.delta.deltaType)
} }
@Test fun iconTest() { @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() { @Test fun initializerTest() {
val t = TriggerDelta(injector) val t = TriggerDelta(injector)
Assert.assertTrue(t.units == GlucoseUnit.MGDL) Assertions.assertTrue(t.units == GlucoseUnit.MGDL)
} }
private fun generateValidBgData(): List<GlucoseValue> { private fun generateValidBgData(): MutableList<InMemoryGlucoseValue> {
val list: MutableList<GlucoseValue> = ArrayList() val list: MutableList<InMemoryGlucoseValue> = 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(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, 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(InMemoryGlucoseValue(value = 216.0, timestamp = 1514766600000, 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(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, 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(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, 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(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, 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(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, 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(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, 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)) list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
return list return list
} }
} }

View file

@ -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)) createViewsForPlugins(info.nightscout.core.ui.R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInList(PluginType.INSULIN))
if (!config.NSCLIENT) { if (!config.NSCLIENT) {
createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInList(PluginType.BGSOURCE)) 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)) createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, activePlugin.getSpecificPluginsVisibleInList(PluginType.PUMP))
} }
if (config.APS || config.PUMPCONTROL || config.isEngineeringMode()) if (config.APS || config.PUMPCONTROL || config.isEngineeringMode())

View file

@ -20,6 +20,7 @@ import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileSource import info.nightscout.interfaces.profile.ProfileSource
import info.nightscout.interfaces.pump.Pump import info.nightscout.interfaces.pump.Pump
import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.smoothing.Smoothing
import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.sync.NsClient
import info.nightscout.rx.bus.RxBus 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.CONSTRAINTS)) " CONSTRAINTS" else "") +
(if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") + (if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") +
(if (p.isEnabled(PluginType.BGSOURCE)) " BGSOURCE" 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) { when (type) {
PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java) PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java)
PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Sensitivity::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.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java)
PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java) PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java)
PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java) PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java)

View file

@ -11,6 +11,7 @@ import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.pump.defs.PumpType import info.nightscout.interfaces.pump.defs.PumpType
import info.nightscout.interfaces.smoothing.Smoothing
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
@ -52,11 +53,13 @@ class RunningConfigurationImpl @Inject constructor(
val sensitivityInterface = activePlugin.activeSensitivity val sensitivityInterface = activePlugin.activeSensitivity
val overviewInterface = activePlugin.activeOverview val overviewInterface = activePlugin.activeOverview
val safetyInterface = activePlugin.activeSafety val safetyInterface = activePlugin.activeSafety
val smoothingInterface = activePlugin.activeSmoothing
json.put("insulin", insulinInterface.id.value) json.put("insulin", insulinInterface.id.value)
json.put("insulinConfiguration", insulinInterface.configuration()) json.put("insulinConfiguration", insulinInterface.configuration())
json.put("sensitivity", sensitivityInterface.id.value) json.put("sensitivity", sensitivityInterface.id.value)
json.put("sensitivityConfiguration", sensitivityInterface.configuration()) json.put("sensitivityConfiguration", sensitivityInterface.configuration())
json.put("smoothing", smoothingInterface.javaClass.simpleName)
json.put("overviewConfiguration", overviewInterface.configuration()) json.put("overviewConfiguration", overviewInterface.configuration())
json.put("safetyConfiguration", safetyInterface.configuration()) json.put("safetyConfiguration", safetyInterface.configuration())
json.put("pump", pumpInterface.model().description) 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 { configuration.pump?.let {
if (sp.getString(info.nightscout.core.utils.R.string.key_virtualpump_type, "fake") != it) { 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) sp.putString(info.nightscout.core.utils.R.string.key_virtualpump_type, it)

View file

@ -77,6 +77,8 @@
<string name="configbuilder_insulin_description">Which type of insulin are you using?</string> <string name="configbuilder_insulin_description">Which type of insulin are you using?</string>
<string name="configbuilder_bgsource">BG Source</string> <string name="configbuilder_bgsource">BG Source</string>
<string name="configbuilder_bgsource_description">Where should AAPS gain it\'s data from?</string> <string name="configbuilder_bgsource_description">Where should AAPS gain it\'s data from?</string>
<string name="configbuilder_smoothing">Smoothing</string>
<string name="configbuilder_smoothing_description">Choose smoothing algorithm</string>
<string name="configbuilder_sensitivity">Sensitivity detection</string> <string name="configbuilder_sensitivity">Sensitivity detection</string>
<string name="configbuilder_sensitivity_description">Which sensitivity algorithm should be used?</string> <string name="configbuilder_sensitivity_description">Which sensitivity algorithm should be used?</string>
<string name="config_builder_shortname">CONF</string> <string name="config_builder_shortname">CONF</string>

View file

@ -8,6 +8,7 @@ import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.aps.AutosensDataStore
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.source.BgSource 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(-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)) 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`(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 // Test non-dexcom plugin on flat data
class OtherPlugin : BgSource { 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(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)) 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`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(incompleteData)
`when`(iobCobCalculator.ads.lastBg()).thenReturn(incompleteData[0]) `when`(iobCobCalculator.ads.lastBg()).thenReturn(InMemoryGlucoseValue(incompleteData[0]))
`when`(activePlugin.activeBgSource).thenReturn(OtherPlugin()) `when`(activePlugin.activeBgSource).thenReturn(OtherPlugin())
plugin.processBgData()// must be more than 5 values plugin.processBgData()// must be more than 5 values
Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state) Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state)

View file

@ -115,7 +115,7 @@ class DataBroadcastPlugin @Inject constructor(
val lastBG = iobCobCalculator.ads.lastBg() ?: return val lastBG = iobCobCalculator.ads.lastBg() ?: return
val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: 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.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp
bundle.putString("units", profileFunction.getUnits().asText) // units used in AAPS "mg/dl" or "mmol" bundle.putString("units", profileFunction.getUnits().asText) // units used in AAPS "mg/dl" or "mmol"
bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string

View file

@ -80,10 +80,10 @@ import info.nightscout.plugins.ui.StatusLightHandler
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAcceptOpenLoopChange import info.nightscout.rx.events.EventAcceptOpenLoopChange
import info.nightscout.rx.events.EventBucketedDataCreated
import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged
import info.nightscout.rx.events.EventExtendedBolusChange import info.nightscout.rx.events.EventExtendedBolusChange
import info.nightscout.rx.events.EventMobileToWear import info.nightscout.rx.events.EventMobileToWear
import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.events.EventNewOpenLoopNotification import info.nightscout.rx.events.EventNewOpenLoopNotification
import info.nightscout.rx.events.EventPreferenceChange import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventPumpStatusChanged 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.rx.weardata.EventData
import info.nightscout.shared.extensions.runOnUiThread import info.nightscout.shared.extensions.runOnUiThread
import info.nightscout.shared.extensions.toVisibility import info.nightscout.shared.extensions.toVisibility
import info.nightscout.shared.extensions.toVisibilityKeepSpace
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil 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) sp.putBoolean(info.nightscout.core.utils.R.string.key_objectiveusescale, true)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNewBG::class.java) .toObservable(EventBucketedDataCreated::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ updateBg() }, fabricPrivacy::logException) .subscribe({ updateBg() }, fabricPrivacy::logException)
@ -777,19 +778,19 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun updateBg() { fun updateBg() {
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val lastBg = overviewData.lastBg val lastBg = overviewData.lastBg(iobCobCalculator.ads)
val lastBgColor = overviewData.lastBgColor(context) val lastBgColor = overviewData.lastBgColor(context, iobCobCalculator.ads)
val isActualBg = overviewData.isActualBg val isActualBg = overviewData.isActualBg(iobCobCalculator.ads)
val glucoseStatus = glucoseStatusProvider.glucoseStatusData val glucoseStatus = glucoseStatusProvider.glucoseStatusData
val trendDescription = trendCalculator.getTrendDescription(lastBg) val trendDescription = trendCalculator.getTrendDescription(iobCobCalculator.ads)
val trendArrow = trendCalculator.getTrendArrow(lastBg) val trendArrow = trendCalculator.getTrendArrow(iobCobCalculator.ads)
val lastBgDescription = overviewData.lastBgDescription val lastBgDescription = overviewData.lastBgDescription(iobCobCalculator.ads)
runOnUiThread { runOnUiThread {
_binding ?: return@runOnUiThread _binding ?: return@runOnUiThread
binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) ?: ""
?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)
binding.infoLayout.bg.setTextColor(lastBgColor) 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.setColorFilter(lastBgColor)
binding.infoLayout.arrow.contentDescription = lastBgDescription + " " + rh.gs(info.nightscout.core.ui.R.string.and) + " " + trendDescription binding.infoLayout.arrow.contentDescription = lastBgDescription + " " + rh.gs(info.nightscout.core.ui.R.string.and) + " " + trendDescription

View file

@ -34,6 +34,7 @@ import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.iob.GlucoseStatusProvider import info.nightscout.interfaces.iob.GlucoseStatusProvider
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.logging.UserEntryLogger import info.nightscout.interfaces.logging.UserEntryLogger
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
@ -744,8 +745,9 @@ class DataHandlerMobile @Inject constructor(
) )
) )
// GraphData // GraphData
val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong() iobCobCalculator.ads.getBucketedDataTableCopy()?.let { bucketedData ->
rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet().map { getSingleBG(it) })))) rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(bucketedData.map { getSingleBG(it) }))))
}
// Treatments // Treatments
sendTreatments() sendTreatments()
// Status // Status
@ -933,7 +935,7 @@ class DataHandlerMobile @Inject constructor(
return deltaString return deltaString
} }
private fun getSingleBG(glucoseValue: GlucoseValue): EventData.SingleBg { private fun getSingleBG(glucoseValue: InMemoryGlucoseValue): EventData.SingleBg {
val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true) val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true)
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units) val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units)
@ -946,8 +948,8 @@ class DataHandlerMobile @Inject constructor(
slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol, slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol,
delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--", 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) } ?: "--", 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, sgvLevel = if (glucoseValue.recalculated > highLine) 1L else if (glucoseValue.recalculated < lowLine) -1L else 0L,
sgv = glucoseValue.value, sgv = glucoseValue.recalculated,
high = highLine, high = highLine,
low = lowLine, low = lowLine,
color = 0 color = 0

View file

@ -155,7 +155,7 @@ class IobCobCalculatorPlugin @Inject constructor(
overviewData = overviewData, overviewData = overviewData,
reason = reason, reason = reason,
end = System.currentTimeMillis(), end = System.currentTimeMillis(),
bgDataReload = false, bgDataReload = true,
cause = event cause = event
) )
} }

View file

@ -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) { synchronized(dataLock) {
if (bgReadings.isNotEmpty()) bgReadings[0] bucketedData?.let { bucketedData ->
else null 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 val lastBg = lastBg() ?: return null
return if (lastBg.timestamp > System.currentTimeMillis() - T.mins(9).msecs()) lastBg else 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 bgDelta = newer.value - older.value
val timeDiffToNew = newer.timestamp - currentTime val timeDiffToNew = newer.timestamp - currentTime
val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta 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) 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() + ")"); //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 val gapDelta = bgReadings[i].value - lastBg
//console.error(gapDelta, lastBg, elapsed_minutes); //console.error(gapDelta, lastBg, elapsed_minutes);
val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta 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]); //console.error("Interpolated", bData[j]);
bData.add(newBgReading) bData.add(newBgReading)
aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" } aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" }

View file

@ -11,7 +11,7 @@
android:id="@+id/bg" android:id="@+id/bg"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="00.0" tools:text="00.0"
android:textSize="60sp" android:textSize="60sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/delta_large" app:layout_constraintEnd_toStartOf="@+id/delta_large"
@ -32,7 +32,7 @@
android:id="@+id/delta_large" android:id="@+id/delta_large"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="(+1.0)" tools:text="(+1.0)"
android:textSize="60sp" android:textSize="60sp"
android:textStyle="bold" android:textStyle="bold"
android:visibility="gone" android:visibility="gone"
@ -60,7 +60,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:srcCompat="@drawable/ic_flat"
android:contentDescription="@string/trend_arrow" /> android:contentDescription="@string/trend_arrow" />
<TextView <TextView
@ -69,7 +68,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
app:layout_constraintTop_toTopOf="@+id/long_avg_delta" app:layout_constraintTop_toTopOf="@+id/long_avg_delta"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -106,7 +105,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -123,7 +122,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:text="15m Δ: " android:text="Δ15: "
android:contentDescription="15 minutes delta" android:contentDescription="15 minutes delta"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -134,7 +133,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -151,7 +150,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:text="40m Δ: " android:text="Δ40: "
android:contentDescription="40 minutes delta" android:contentDescription="40 minutes delta"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -162,7 +161,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:textAlignment="textEnd" android:textAlignment="textEnd"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -226,7 +225,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="-5dp" android:layout_marginTop="-5dp"
android:text="8:00 PM" tools:text="8:00 PM"
android:textSize="25sp" android:textSize="25sp"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -238,7 +237,7 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="-10dp" android:layout_marginTop="-10dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="(-5)" tools:text="(-5)"
android:textSize="19sp" android:textSize="19sp"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -271,7 +270,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="3dp" android:paddingTop="3dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -304,7 +303,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="3dp" android:paddingTop="3dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -336,7 +335,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="3dp" android:paddingTop="3dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -367,7 +366,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="3dp" android:paddingTop="3dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -400,7 +399,7 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="3dp" android:paddingTop="3dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
@ -412,7 +411,7 @@
android:layout_marginTop="-9dp" android:layout_marginTop="-9dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:text="n/a" tools:text="n/a"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textStyle="bold" android:textStyle="bold"
android:visibility="visible" android:visibility="visible"

View file

@ -21,6 +21,7 @@ import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.iob.CobInfo import info.nightscout.interfaces.iob.CobInfo
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.iob.IobTotal import info.nightscout.interfaces.iob.IobTotal
import info.nightscout.interfaces.logging.UserEntryLogger import info.nightscout.interfaces.logging.UserEntryLogger
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
@ -68,7 +69,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
@Mock lateinit var autosensDataStore: AutosensDataStore @Mock lateinit var autosensDataStore: AutosensDataStore
@Mock lateinit var smsManager: SmsManager @Mock lateinit var smsManager: SmsManager
var injector: HasAndroidInjector = HasAndroidInjector { private var injector: HasAndroidInjector = HasAndroidInjector {
AndroidInjector { AndroidInjector {
if (it is PumpEnactResult) { if (it is PumpEnactResult) {
it.context = context 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.getCobInfo(false, "SMS COB")).thenReturn(CobInfo(0, 10.0, 2.0))
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore) `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") `when`(sp.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678")

1
plugins/smoothing/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -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')
}

View file

21
plugins/smoothing/proguard-rules.pro vendored Normal file
View file

@ -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

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View file

@ -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<InMemoryGlucoseValue>): MutableList<InMemoryGlucoseValue> {
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
}
}

View file

@ -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<InMemoryGlucoseValue>): MutableList<InMemoryGlucoseValue> {
/**
* 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<Double> = ArrayList() //MP array for 1st order Smoothed Blood Glucose
val o2_sBG: ArrayList<Double> = ArrayList() //MP array for 2nd order Smoothed Blood Glucose
val o2_sD: ArrayList<Double> = ArrayList() //MP array for 2nd order Smoothed delta
val ssBG: ArrayList<Double> = ArrayList() //MP array for weighted averaged, doubly smoothed Blood Glucose
//val ssD: ArrayList<Double> = 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
}
}

View file

@ -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<InMemoryGlucoseValue>): MutableList<InMemoryGlucoseValue> = data
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="smoothing_shortname">SMOOTH</string>
<string name="exponential_smoothing_name">Exponential smoothing</string>
<string name="description_exponential_smoothing">"Second-order exponential smoothing algorithm"</string>
<string name="avg_smoothing_name">Average smoothing</string>
<string name="description_avg_smoothing">"Average smoothing algorithm, newest value is not affected"</string>
<string name="no_smoothing_name">No smoothing</string>
<string name="description_no_smoothing">"No smoothing performed on input glucose data. Use this when you already have filtered data e.g. from BYODA G6."</string>
</resources>

View file

@ -23,6 +23,7 @@ import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.lang.Math.random
import java.util.Calendar import java.util.Calendar
import java.util.GregorianCalendar import java.util.GregorianCalendar
import javax.inject.Inject import javax.inject.Inject
@ -101,7 +102,7 @@ class RandomBgPlugin @Inject constructor(
val cal = GregorianCalendar() val cal = GregorianCalendar()
val currentMinute = cal[Calendar.MINUTE] + (cal[Calendar.HOUR_OF_DAY] % 2) * 60 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.MILLISECOND] = 0
cal[Calendar.SECOND] = 0 cal[Calendar.SECOND] = 0

View file

@ -13,17 +13,17 @@ include ':core:ui'
include ':core:validators' include ':core:validators'
include ':database:entities' include ':database:entities'
include ':database:impl' include ':database:impl'
include ':ui'
include ':implementation'
include ':plugins:aps' include ':plugins:aps'
include ':plugins:automation' include ':plugins:automation'
include ':plugins:configuration' include ':plugins:configuration'
include ':plugins:constraints'
include ':plugins:insulin' include ':plugins:insulin'
include ':plugins:main' include ':plugins:main'
include ':plugins:openhumans' include ':plugins:openhumans'
include ':plugins:sensitivity' include ':plugins:sensitivity'
include ':plugins:smoothing'
include ':plugins:source' include ':plugins:source'
include ':plugins:constraints' include ':plugins:sync'
include ':pump:combo' include ':pump:combo'
include ':pump:combov2' include ':pump:combov2'
include ':pump:combov2:comboctl' include ':pump:combov2:comboctl'
@ -42,5 +42,6 @@ include ':pump:pump-common'
include ':pump:pump-core' include ':pump:pump-core'
include ':pump:rileylink' include ':pump:rileylink'
include ':pump:virtual' include ':pump:virtual'
include ':plugins:sync' include ':implementation'
include ':ui'
include ':workflow' include ':workflow'

View file

@ -192,7 +192,7 @@ class CarbsDialog : DialogFragmentWithDate() {
} }
iobCobCalculator.ads.actualBg()?.let { bgReading -> iobCobCalculator.ads.actualBg()?.let { bgReading ->
if (bgReading.value < 72) if (bgReading.recalculated < 72)
binding.hypoTt.isChecked = true binding.hypoTt.isChecked = true
} }
binding.hypoTt.setOnClickListener { binding.hypoTt.setOnClickListener {

View file

@ -35,6 +35,7 @@ import info.nightscout.interfaces.utils.TrendCalculator
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.shared.extensions.toVisibility import info.nightscout.shared.extensions.toVisibility
import info.nightscout.shared.extensions.toVisibilityKeepSpace
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
@ -125,20 +126,23 @@ class Widget : AppWidgetProvider() {
private fun updateBg(views: RemoteViews) { private fun updateBg(views: RemoteViews) {
val units = profileFunction.getUnits() 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( views.setTextColor(
R.id.bg, when { R.id.bg, when {
overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low) overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high) 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) 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( views.setInt(
R.id.arrow, "setColorFilter", when { R.id.arrow, "setColorFilter", when {
overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low) overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high) 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) else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange)
} }
) )
@ -154,10 +158,10 @@ class Widget : AppWidgetProvider() {
} }
// strike through if BG is old // 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) 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) + ")") //views.setTextViewText(R.id.time_ago_short, "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")")
} }

View file

@ -8,6 +8,7 @@ import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.aps.AutosensDataStore import info.nightscout.interfaces.aps.AutosensDataStore
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventBucketedDataCreated import info.nightscout.rx.events.EventBucketedDataCreated
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
@ -25,6 +26,7 @@ class LoadBgDataWorker(
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin
class LoadBgData( class LoadBgData(
val iobCobCalculator: IobCobCalculator, 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) { synchronized(dataLock) {
val start = to - T.hours((24 + 10 /* max dia */).toLong()).msecs() 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) // 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 } .filter { it.value >= 39 }
aapsLogger.debug(LTag.AUTOSENS) { "BG data loaded. Size: ${bgReadings.size} Start date: ${dateUtil.dateAndTimeString(start)} End date: ${dateUtil.dateAndTimeString(to)}" } aapsLogger.debug(LTag.AUTOSENS) { "BG data loaded. Size: ${bgReadings.size} Start date: ${dateUtil.dateAndTimeString(start)} End date: ${dateUtil.dateAndTimeString(to)}" }
createBucketedData(aapsLogger, dateUtil) 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? val data = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as LoadBgData?
?: return Result.failure(workDataOf("Error" to "missing input data")) ?: 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() data.iobCobCalculator.clearCache()
return Result.success() return Result.success()
} }

View file

@ -10,6 +10,7 @@ import info.nightscout.core.graph.data.PointsWithLabelGraphSeries
import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
@ -22,6 +23,7 @@ class PrepareBucketedDataWorker(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper @Inject lateinit var rh: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper
class PrepareBucketedData( class PrepareBucketedData(
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
@ -41,7 +43,7 @@ class PrepareBucketedDataWorker(
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList() val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (inMemoryGlucoseValue in bucketedData) { for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < data.overviewData.fromTime || inMemoryGlucoseValue.timestamp > data.overviewData.toTime) continue 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) } bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] }) data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })

View file

@ -85,6 +85,7 @@ class PrepareIobAutosensGraphDataWorker(
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.IOB_PREDICTION override val shape = PointsWithLabelGraphSeries.Shape.IOB_PREDICTION
override val size = 0.5f override val size = 0.5f
override val paintStyle: Paint.Style = Paint.Style.FILL
override fun color(context: Context?): Int = color override fun color(context: Context?): Int = color
fun setColor(color: Int): IobTotalDataPoint { fun setColor(color: Int): IobTotalDataPoint {
@ -107,6 +108,7 @@ class PrepareIobAutosensGraphDataWorker(
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.COB_FAIL_OVER override val shape = PointsWithLabelGraphSeries.Shape.COB_FAIL_OVER
override val size = 0.5f override val size = 0.5f
override val paintStyle: Paint.Style = Paint.Style.FILL
override fun color(context: Context?): Int { override fun color(context: Context?): Int {
return rh.gac(context, info.nightscout.core.ui.R.attr.cobColor) return rh.gac(context, info.nightscout.core.ui.R.attr.cobColor)
} }

View file

@ -122,14 +122,14 @@ class IobCobOref1Worker(
//console.error(bgTime , bucketed_data[i].glucose); //console.error(bgTime , bucketed_data[i].glucose);
var avgDelta: Double var avgDelta: Double
var delta: Double var delta: Double
val bg: Double = bucketedData[i].value val bg: Double = bucketedData[i].recalculated
if (bg < 39 || bucketedData[i + 3].value < 39) { if (bg < 39 || bucketedData[i + 3].recalculated < 39) {
aapsLogger.error("! value < 39") aapsLogger.error("! value < 39")
continue continue
} }
autosensData.bg = bg autosensData.bg = bg
delta = bg - bucketedData[i + 1].value delta = bg - bucketedData[i + 1].recalculated
avgDelta = (bg - bucketedData[i + 3].value) / 3 avgDelta = (bg - bucketedData[i + 3].recalculated) / 3
val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile) val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
val bgi = -iob.activity * sens * 5 val bgi = -iob.activity * sens * 5
val deviation = delta - bgi val deviation = delta - bgi

View file

@ -117,14 +117,14 @@ class IobCobOrefWorker @Inject internal constructor(
//console.error(bgTime , bucketed_data[i].glucose); //console.error(bgTime , bucketed_data[i].glucose);
var avgDelta: Double var avgDelta: Double
var delta: Double var delta: Double
val bg: Double = bucketedData[i].value val bg: Double = bucketedData[i].recalculated
if (bg < 39 || bucketedData[i + 3].value < 39) { if (bg < 39 || bucketedData[i + 3].recalculated < 39) {
aapsLogger.error("! value < 39") aapsLogger.error("! value < 39")
continue continue
} }
autosensData.bg = bg autosensData.bg = bg
delta = bg - bucketedData[i + 1].value delta = bg - bucketedData[i + 1].recalculated
avgDelta = (bg - bucketedData[i + 3].value) / 3 avgDelta = (bg - bucketedData[i + 3].recalculated) / 3
val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile) val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
val bgi = -iob.activity * sens * 5 val bgi = -iob.activity * sens * 5
val deviation = delta - bgi val deviation = delta - bgi