lift out flat BG check from determinebasal to BgQualityCheckPlugin

This commit is contained in:
Milos Kozak 2022-12-04 23:46:06 +01:00
parent 1f1a2eae48
commit d5af299692
21 changed files with 197 additions and 94 deletions

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.implementation.constraints.ConstraintsImpl import info.nightscout.implementation.constraints.ConstraintsImpl
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
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.constraints.Objectives import info.nightscout.interfaces.constraints.Objectives
@ -71,6 +72,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@Mock lateinit var profileInstantiator: ProfileInstantiator @Mock lateinit var profileInstantiator: ProfileInstantiator
@Mock lateinit var danaHistoryDatabase: DanaHistoryDatabase @Mock lateinit var danaHistoryDatabase: DanaHistoryDatabase
@Mock lateinit var insightDatabase: InsightDatabase @Mock lateinit var insightDatabase: InsightDatabase
@Mock lateinit var bgQualityCheck: BgQualityCheck
private lateinit var hardLimits: HardLimits private lateinit var hardLimits: HardLimits
private lateinit var danaPump: DanaPump private lateinit var danaPump: DanaPump
@ -182,7 +184,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
sp, sp,
dateUtil, dateUtil,
repository, repository,
glucoseStatusProvider glucoseStatusProvider,
bgQualityCheck
) )
openAPSSMBDynamicISFPlugin = openAPSSMBDynamicISFPlugin =
OpenAPSSMBDynamicISFPlugin( OpenAPSSMBDynamicISFPlugin(
@ -201,7 +204,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
dateUtil, dateUtil,
repository, repository,
glucoseStatusProvider, glucoseStatusProvider,
config config,
bgQualityCheck
) )
openAPSAMAPlugin = openAPSAMAPlugin =
OpenAPSAMAPlugin( OpenAPSAMAPlugin(

View file

@ -7,6 +7,7 @@ import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Constants import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
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.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
@ -37,6 +38,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
@Mock lateinit var profiler: Profiler @Mock lateinit var profiler: Profiler
@Mock lateinit var repository: AppRepository @Mock lateinit var repository: AppRepository
@Mock lateinit var glucoseStatusProvider: GlucoseStatusProvider @Mock lateinit var glucoseStatusProvider: GlucoseStatusProvider
@Mock lateinit var bgQualityCheck: BgQualityCheck
private lateinit var hardLimits: HardLimits private lateinit var hardLimits: HardLimits
private lateinit var safetyPlugin: SafetyPlugin private lateinit var safetyPlugin: SafetyPlugin
@ -80,7 +82,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
) )
openAPSSMBPlugin = OpenAPSSMBPlugin( openAPSSMBPlugin = OpenAPSSMBPlugin(
injector, aapsLogger, rxBus, constraintChecker, rh, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, sp, injector, aapsLogger, rxBus, constraintChecker, rh, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, sp,
dateUtil, repository, glucoseStatusProvider dateUtil, repository, glucoseStatusProvider, bgQualityCheck
) )
} }

View file

@ -30,7 +30,7 @@ interface DetermineBasalAdapter {
microBolusAllowed: Boolean = false, microBolusAllowed: Boolean = false,
uamAllowed: Boolean = false, uamAllowed: Boolean = false,
advancedFiltering: Boolean = false, advancedFiltering: Boolean = false,
isSaveCgmSource: Boolean = false flatBGsDetected: Boolean = false
) )
operator fun invoke(): APSResult? operator fun invoke(): APSResult?

View file

@ -3,6 +3,15 @@ package info.nightscout.interfaces.bgQualityCheck
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
interface BgQualityCheck { interface BgQualityCheck {
enum class State {
UNKNOWN,
FIVE_MIN_DATA,
RECALCULATED,
DOUBLED,
FLAT // stale data for 45 min
}
var state: State
var message: String var message: String
@DrawableRes fun icon(): Int @DrawableRes fun icon(): Int
fun stateDescription(): String fun stateDescription(): String

View file

@ -0,0 +1,3 @@
package info.nightscout.interfaces.source
interface DexcomBoyda

View file

@ -110,7 +110,7 @@ function enable_smb(
return false; return false;
} }
var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, isSaveCgmSource) { var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, flatBGsDetected) {
var rT = {}; //short for requestedTemp var rT = {}; //short for requestedTemp
var deliverAt = new Date(); var deliverAt = new Date();
@ -142,16 +142,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future
rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime;
// if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc } else if ( bg > 60 && flatBGsDetected) {
} else if ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 && !isSaveCgmSource) {
if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) {
rT.reason = "CGM was just calibrated"; rT.reason = "CGM was just calibrated";
} else { } else {
rT.reason = "Error: CGM data is unchanged for the past ~45m"; rT.reason = "Error: CGM data is unchanged for the past ~45m";
} }
} }
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && flatBGsDetected )) {
if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) && !isSaveCgmSource ) {
if (currenttemp.rate > basal) { // high temp is running if (currenttemp.rate > basal) { // high temp is running
rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal;
rT.deliverAt = deliverAt; rT.deliverAt = deliverAt;

View file

@ -110,7 +110,7 @@ function enable_smb(
return false; return false;
} }
var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, isSaveCgmSource) { var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, flatBGsDetected) {
var rT = {}; //short for requestedTemp var rT = {}; //short for requestedTemp
var deliverAt = new Date(); var deliverAt = new Date();
@ -142,16 +142,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future
rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime;
// if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc } else if ( bg > 60 && flatBGsDetected) {
} else if ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 && !isSaveCgmSource) {
if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) {
rT.reason = "CGM was just calibrated"; rT.reason = "CGM was just calibrated";
} else { } else {
rT.reason = "Error: CGM data is unchanged for the past ~45m"; rT.reason = "Error: CGM data is unchanged for the past ~45m";
} }
} }
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && flatBGsDetected )) {
if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) && !isSaveCgmSource ) {
if (currenttemp.rate > basal) { // high temp is running if (currenttemp.rate > basal) { // high temp is running
rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal;
rT.deliverAt = deliverAt; rT.deliverAt = deliverAt;

View file

@ -162,7 +162,7 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader
microBolusAllowed: Boolean, microBolusAllowed: Boolean,
uamAllowed: Boolean, uamAllowed: Boolean,
advancedFiltering: Boolean, advancedFiltering: Boolean,
isSaveCgmSource: Boolean flatBGsDetected: Boolean
) { ) {
this.profile = JSONObject() this.profile = JSONObject()
this.profile.put("max_iob", maxIob) this.profile.put("max_iob", maxIob)

View file

@ -57,7 +57,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
private var microBolusAllowed = false private var microBolusAllowed = false
private var smbAlwaysAllowed = false private var smbAlwaysAllowed = false
private var currentTime: Long = 0 private var currentTime: Long = 0
private var saveCgmSource = false private var flatBGsDetected = false
override var currentTempParam: String? = null override var currentTempParam: String? = null
override var iobDataParam: String? = null override var iobDataParam: String? = null
@ -79,7 +79,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed") aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed")
aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed") aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed")
aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime") aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime")
aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource") aapsLogger.debug(LTag.APS, "flatBGsDetected: $flatBGsDetected")
var determineBasalResultSMB: DetermineBasalResultSMB? = null var determineBasalResultSMB: DetermineBasalResultSMB? = null
val rhino = Context.enter() val rhino = Context.enter()
val scope: Scriptable = rhino.initStandardObjects() val scope: Scriptable = rhino.initStandardObjects()
@ -119,7 +119,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
java.lang.Boolean.valueOf(microBolusAllowed), java.lang.Boolean.valueOf(microBolusAllowed),
makeParam(null, rhino, scope), // reservoir data as undefined makeParam(null, rhino, scope), // reservoir data as undefined
java.lang.Long.valueOf(currentTime), java.lang.Long.valueOf(currentTime),
java.lang.Boolean.valueOf(saveCgmSource) java.lang.Boolean.valueOf(flatBGsDetected)
) )
val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
scriptDebug = LoggerCallback.scriptDebug scriptDebug = LoggerCallback.scriptDebug
@ -174,7 +174,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
microBolusAllowed: Boolean, microBolusAllowed: Boolean,
uamAllowed: Boolean, uamAllowed: Boolean,
advancedFiltering: Boolean, advancedFiltering: Boolean,
isSaveCgmSource: Boolean flatBGsDetected: Boolean
) { ) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
val pumpBolusStep = pump.pumpDescription.bolusStep val pumpBolusStep = pump.pumpDescription.bolusStep
@ -262,7 +262,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader:
this.microBolusAllowed = microBolusAllowed this.microBolusAllowed = microBolusAllowed
smbAlwaysAllowed = advancedFiltering smbAlwaysAllowed = advancedFiltering
currentTime = now currentTime = now
saveCgmSource = isSaveCgmSource this.flatBGsDetected = flatBGsDetected
} }
private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {

View file

@ -14,6 +14,7 @@ import info.nightscout.interfaces.aps.APS
import info.nightscout.interfaces.aps.AutosensResult import info.nightscout.interfaces.aps.AutosensResult
import info.nightscout.interfaces.aps.DetermineBasalAdapter import info.nightscout.interfaces.aps.DetermineBasalAdapter
import info.nightscout.interfaces.aps.SMBDefaults import info.nightscout.interfaces.aps.SMBDefaults
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
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.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
@ -55,7 +56,8 @@ class OpenAPSSMBPlugin @Inject constructor(
private val sp: SP, private val sp: SP,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val repository: AppRepository, private val repository: AppRepository,
private val glucoseStatusProvider: GlucoseStatusProvider private val glucoseStatusProvider: GlucoseStatusProvider,
private val bgQualityCheck: BgQualityCheck
) : PluginBase( ) : PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.APS) .mainType(PluginType.APS)
@ -191,6 +193,7 @@ class OpenAPSSMBPlugin @Inject constructor(
constraintChecker.isUAMEnabled(it) constraintChecker.isUAMEnabled(it)
inputConstraints.copyReasons(it) inputConstraints.copyReasons(it)
} }
val flatBGsDetected = bgQualityCheck.state == BgQualityCheck.State.FLAT
profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart) profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart)
profiler.log(LTag.APS, "SMB data gathering", start) profiler.log(LTag.APS, "SMB data gathering", start)
start = System.currentTimeMillis() start = System.currentTimeMillis()
@ -207,7 +210,7 @@ class OpenAPSSMBPlugin @Inject constructor(
smbAllowed.value(), smbAllowed.value(),
uam.value(), uam.value(),
advancedFiltering.value(), advancedFiltering.value(),
activePlugin.activeBgSource.javaClass.simpleName == "DexcomPlugin" flatBGsDetected
) )
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke() val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke()

View file

@ -59,7 +59,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
private var microBolusAllowed = false private var microBolusAllowed = false
private var smbAlwaysAllowed = false private var smbAlwaysAllowed = false
private var currentTime: Long = 0 private var currentTime: Long = 0
private var saveCgmSource = false private var flatBGsDetected = false
override var currentTempParam: String? = null override var currentTempParam: String? = null
override var iobDataParam: String? = null override var iobDataParam: String? = null
@ -81,7 +81,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed") aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed")
aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed") aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed")
aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime") aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime")
aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource") aapsLogger.debug(LTag.APS, "flatBGsDetected: $flatBGsDetected")
var determineBasalResultSMB: DetermineBasalResultSMB? = null var determineBasalResultSMB: DetermineBasalResultSMB? = null
val rhino = Context.enter() val rhino = Context.enter()
val scope: Scriptable = rhino.initStandardObjects() val scope: Scriptable = rhino.initStandardObjects()
@ -121,7 +121,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
java.lang.Boolean.valueOf(microBolusAllowed), java.lang.Boolean.valueOf(microBolusAllowed),
makeParam(null, rhino, scope), // reservoir data as undefined makeParam(null, rhino, scope), // reservoir data as undefined
java.lang.Long.valueOf(currentTime), java.lang.Long.valueOf(currentTime),
java.lang.Boolean.valueOf(saveCgmSource) java.lang.Boolean.valueOf(flatBGsDetected)
) )
val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
scriptDebug = LoggerCallback.scriptDebug scriptDebug = LoggerCallback.scriptDebug
@ -176,7 +176,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
microBolusAllowed: Boolean, microBolusAllowed: Boolean,
uamAllowed: Boolean, uamAllowed: Boolean,
advancedFiltering: Boolean, advancedFiltering: Boolean,
isSaveCgmSource: Boolean flatBGsDetected: Boolean
) { ) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
val pumpBolusStep = pump.pumpDescription.bolusStep val pumpBolusStep = pump.pumpDescription.bolusStep
@ -312,7 +312,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
this.microBolusAllowed = microBolusAllowed this.microBolusAllowed = microBolusAllowed
smbAlwaysAllowed = advancedFiltering smbAlwaysAllowed = advancedFiltering
currentTime = now currentTime = now
saveCgmSource = isSaveCgmSource this.flatBGsDetected = flatBGsDetected
} }
private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {

View file

@ -7,6 +7,7 @@ import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.database.impl.AppRepository import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.interfaces.aps.DetermineBasalAdapter import info.nightscout.interfaces.aps.DetermineBasalAdapter
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.ActivePlugin
@ -42,7 +43,8 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor(
dateUtil: DateUtil, dateUtil: DateUtil,
repository: AppRepository, repository: AppRepository,
glucoseStatusProvider: GlucoseStatusProvider, glucoseStatusProvider: GlucoseStatusProvider,
private val config: Config private val config: Config,
private val bgQualityCheck: BgQualityCheck
) : OpenAPSSMBPlugin( ) : OpenAPSSMBPlugin(
injector, injector,
aapsLogger, aapsLogger,
@ -58,7 +60,8 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor(
sp, sp,
dateUtil, dateUtil,
repository, repository,
glucoseStatusProvider glucoseStatusProvider,
bgQualityCheck
) { ) {
init { init {

View file

@ -41,17 +41,11 @@ class AidexPlugin @Inject constructor(
aapsLogger, rh, injector aapsLogger, rh, injector
), BgSource { ), BgSource {
private var advancedFiltering = false
/** /**
* Aidex App doesn't have upload to NS * Aidex App doesn't have upload to NS
*/ */
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true
override fun advancedFilteringSupported(): Boolean {
return advancedFiltering
}
// Allow only for pumpcontrol or dev & engineering_mode // Allow only for pumpcontrol or dev & engineering_mode
override fun specialEnableCondition(): Boolean { override fun specialEnableCondition(): Boolean {
return config.APS.not() || config.isDev() && config.isEngineeringMode() return config.APS.not() || config.isDev() && config.isEngineeringMode()

View file

@ -27,6 +27,7 @@ import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.Profile import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.source.BgSource import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.source.DexcomBoyda
import info.nightscout.plugins.R import info.nightscout.plugins.R
import info.nightscout.plugins.source.activities.RequestDexcomPermissionActivity import info.nightscout.plugins.source.activities.RequestDexcomPermissionActivity
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
@ -58,7 +59,7 @@ class DexcomPlugin @Inject constructor(
.preferencesId(R.xml.pref_dexcom) .preferencesId(R.xml.pref_dexcom)
.description(R.string.description_source_dexcom), .description(R.string.description_source_dexcom),
aapsLogger, rh, injector aapsLogger, rh, injector
), BgSource { ), BgSource, DexcomBoyda {
init { init {
if (!config.NSCLIENT) { if (!config.NSCLIENT) {
@ -66,9 +67,7 @@ class DexcomPlugin @Inject constructor(
} }
} }
override fun advancedFilteringSupported(): Boolean { override fun advancedFilteringSupported(): Boolean = true
return true
}
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean =
(glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE || (glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE ||

View file

@ -64,9 +64,7 @@ class NSClientSourcePlugin @Inject constructor(
} }
} }
override fun advancedFilteringSupported(): Boolean { override fun advancedFilteringSupported(): Boolean = isAdvancedFilteringEnabled
return isAdvancedFilteringEnabled
}
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false

View file

@ -73,9 +73,7 @@ class RandomBgPlugin @Inject constructor(
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
override fun advancedFilteringSupported(): Boolean { override fun advancedFilteringSupported(): Boolean = true
return true
}
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean =
glucoseValue.sourceSensor == GlucoseValue.SourceSensor.RANDOM && sp.getBoolean(info.nightscout.core.utils.R.string.key_do_ns_upload, false) glucoseValue.sourceSensor == GlucoseValue.SourceSensor.RANDOM && sp.getBoolean(info.nightscout.core.utils.R.string.key_do_ns_upload, false)

View file

@ -43,9 +43,7 @@ class XdripPlugin @Inject constructor(
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false
override fun advancedFilteringSupported(): Boolean { override fun advancedFilteringSupported(): Boolean = advancedFiltering
return advancedFiltering
}
private fun detectSource(glucoseValue: GlucoseValue) { private fun detectSource(glucoseValue: GlucoseValue) {
advancedFiltering = arrayOf( advancedFiltering = arrayOf(

View file

@ -7,9 +7,11 @@ import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
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.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.source.DexcomBoyda
import info.nightscout.plugins.support.R import info.nightscout.plugins.support.R
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
@ -33,10 +35,11 @@ class BgQualityCheckPlugin @Inject constructor(
rh: ResourceHelper, rh: ResourceHelper,
private val rxBus: RxBus, private val rxBus: RxBus,
private val iobCobCalculator: IobCobCalculator, private val iobCobCalculator: IobCobCalculator,
private val aapsSchedulers: private val aapsSchedulers:
AapsSchedulers, AapsSchedulers,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil private val dateUtil: DateUtil,
private val activePlugin: ActivePlugin
) : PluginBase( ) : PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.CONSTRAINTS) .mainType(PluginType.CONSTRAINTS)
@ -49,13 +52,6 @@ class BgQualityCheckPlugin @Inject constructor(
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
enum class State {
UNKNOWN,
FIVE_MIN_DATA,
RECALCULATED,
DOUBLED
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
disposable += rxBus disposable += rxBus
@ -69,51 +65,83 @@ class BgQualityCheckPlugin @Inject constructor(
disposable.clear() disposable.clear()
} }
var state: State = State.UNKNOWN override var state: BgQualityCheck.State = BgQualityCheck.State.UNKNOWN
override var message: String = "" override var message: String = ""
// Fallback to LGS if BG values are doubled // Fallback to LGS if BG values are doubled
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> = override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> =
if (state == State.DOUBLED) if (state == BgQualityCheck.State.DOUBLED)
maxIob.set(aapsLogger, 0.0, "Doubled values in BGSource", this) maxIob.set(aapsLogger, 0.0, "Doubled values in BGSource", this)
else else
maxIob maxIob
@Suppress("CascadeIf") fun processBgData() { fun processBgData() {
val readings = iobCobCalculator.ads.getBgReadingsDataTableCopy() val readings = iobCobCalculator.ads.getBgReadingsDataTableCopy()
for (i in readings.indices) for (i in readings.indices)
// Deltas are calculated from last ~50 min. Detect RED state only on this interval // Deltas are calculated from last ~50 min. Detect RED state only on this interval
if (i < min(readings.size - 2, 10)) if (i < min(readings.size - 2, 10))
if (abs(readings[i].timestamp - readings[i + 1].timestamp) <= T.secs(20).msecs()) { if (abs(readings[i].timestamp - readings[i + 1].timestamp) <= T.secs(20).msecs()) {
state = State.DOUBLED state = BgQualityCheck.State.DOUBLED
aapsLogger.debug(LTag.CORE, "BG similar. Turning on red state.\n${readings[i]}\n${readings[i + 1]}") aapsLogger.debug(LTag.CORE, "BG similar. Turning on red state.\n${readings[i]}\n${readings[i + 1]}")
message = rh.gs(R.string.bg_too_close, dateUtil.dateAndTimeAndSecondsString(readings[i].timestamp), dateUtil.dateAndTimeAndSecondsString(readings[i + 1].timestamp)) message = rh.gs(R.string.bg_too_close, dateUtil.dateAndTimeAndSecondsString(readings[i].timestamp), dateUtil.dateAndTimeAndSecondsString(readings[i + 1].timestamp))
return return
} }
if (iobCobCalculator.ads.lastUsed5minCalculation == true) { if (activePlugin.activeBgSource !is DexcomBoyda && isBgFlatForInterval(staleBgCheckPeriodMinutes, staleBgMaxDeltaMgdl) == true) {
state = State.FIVE_MIN_DATA state = BgQualityCheck.State.FLAT
message = rh.gs(R.string.a11y_bg_quality_flat)
} else if (iobCobCalculator.ads.lastUsed5minCalculation == true) {
state = BgQualityCheck.State.FIVE_MIN_DATA
message = "Data is clean" message = "Data is clean"
} else if (iobCobCalculator.ads.lastUsed5minCalculation == false) { } else if (iobCobCalculator.ads.lastUsed5minCalculation == false) {
state = State.RECALCULATED state = BgQualityCheck.State.RECALCULATED
message = rh.gs(R.string.recalculated_data_used) message = rh.gs(R.string.recalculated_data_used)
} else { } else {
state = State.UNKNOWN state = BgQualityCheck.State.UNKNOWN
message = "" message = ""
} }
} }
// inspired by @justmara
@Suppress("SpellCheckingInspection", "SameParameterValue")
private fun isBgFlatForInterval(minutes: Long, maxDelta: Double): Boolean? {
val data = iobCobCalculator.ads.getBgReadingsDataTableCopy()
val lastBg = iobCobCalculator.ads.lastBg()?.value
val now = dateUtil.now()
val offset = now - T.mins(minutes).msecs()
val sizeRecords = data.size
lastBg ?: return null
if (sizeRecords < 5) return null // not enough data
if (data[data.size - 1].timestamp > now - 45 * 60 * 1000L) return null // data too fresh to detect
if (data[0].timestamp < now - 7 * 60 * 1000L) return null // data is old
for (bg in data) {
if (bg.timestamp < offset) break
if (abs(lastBg - bg.value) > maxDelta) return false
}
return true
}
@DrawableRes override fun icon(): Int = @DrawableRes override fun icon(): Int =
when (state) { when (state) {
State.UNKNOWN -> 0 BgQualityCheck.State.UNKNOWN -> 0
State.FIVE_MIN_DATA -> 0 BgQualityCheck.State.FIVE_MIN_DATA -> 0
State.RECALCULATED -> R.drawable.ic_baseline_warning_24_yellow BgQualityCheck.State.RECALCULATED -> R.drawable.ic_baseline_warning_24_yellow
State.DOUBLED -> R.drawable.ic_baseline_warning_24_red BgQualityCheck.State.DOUBLED -> R.drawable.ic_baseline_warning_24_red
BgQualityCheck.State.FLAT -> R.drawable.ic_baseline_trending_flat_24
} }
override fun stateDescription(): String = override fun stateDescription(): String =
when (state) { when (state) {
State.RECALCULATED -> rh.gs(R.string.a11y_bg_quality_recalculated) BgQualityCheck.State.RECALCULATED -> rh.gs(R.string.a11y_bg_quality_recalculated)
State.DOUBLED -> rh.gs(R.string.a11y_bg_quality_doubles) BgQualityCheck.State.DOUBLED -> rh.gs(R.string.a11y_bg_quality_doubles)
else -> "" BgQualityCheck.State.FLAT -> rh.gs(R.string.a11y_bg_quality_flat)
else -> ""
} }
companion object {
const val staleBgCheckPeriodMinutes = 45L
const val staleBgMaxDeltaMgdl = 1.0
}
} }

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FF0000"
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="M22,12l-4,-4v3H3v2h15v3z"/>
</vector>

View file

@ -39,5 +39,6 @@
<string name="bg_too_close">BG too close:\n%1$s\n%2$s</string> <string name="bg_too_close">BG too close:\n%1$s\n%2$s</string>
<string name="a11y_bg_quality_recalculated">recalculated</string> <string name="a11y_bg_quality_recalculated">recalculated</string>
<string name="a11y_bg_quality_doubles">double entries</string> <string name="a11y_bg_quality_doubles">double entries</string>
<string name="a11y_bg_quality_flat">Flat data. Considered to be wrong</string>
</resources> </resources>

View file

@ -6,14 +6,18 @@ import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.core.utils.fabric.FabricPrivacy
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.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.source.DexcomBoyda
import info.nightscout.plugins.support.R import info.nightscout.plugins.support.R
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
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.ArgumentMatchers.any import org.mockito.ArgumentMatchers.any
@ -28,10 +32,12 @@ class BgQualityCheckPluginTest : TestBase() {
@Mock lateinit var fabricPrivacy: FabricPrivacy @Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var dateUtil: DateUtil @Mock lateinit var dateUtil: DateUtil
@Mock lateinit var autosensDataStore: AutosensDataStore @Mock lateinit var autosensDataStore: AutosensDataStore
@Mock lateinit var activePlugin: ActivePlugin
private lateinit var plugin: BgQualityCheckPlugin private lateinit var plugin: BgQualityCheckPlugin
val injector = HasAndroidInjector { AndroidInjector { } } private val injector = HasAndroidInjector { AndroidInjector { } }
val now = 100000000L
//private val autosensDataStore = AutosensDataStoreObject() //private val autosensDataStore = AutosensDataStoreObject()
@BeforeEach @BeforeEach
@ -45,27 +51,29 @@ class BgQualityCheckPluginTest : TestBase() {
iobCobCalculator, iobCobCalculator,
aapsSchedulers, aapsSchedulers,
fabricPrivacy, fabricPrivacy,
dateUtil dateUtil,
activePlugin
) )
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore) `when`(iobCobCalculator.ads).thenReturn(autosensDataStore)
`when`(rh.gs(anyInt())).thenReturn("") `when`(rh.gs(anyInt())).thenReturn("")
`when`(rh.gs(anyInt(), any(), any())).thenReturn("") `when`(rh.gs(anyInt(), any(), any())).thenReturn("")
`when`(dateUtil.now()).thenReturn(now)
} }
@Test @Test
fun runTest() { fun runTest() {
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(null) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(null)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.UNKNOWN, plugin.state) Assertions.assertEquals(BgQualityCheck.State.UNKNOWN, plugin.state)
Assert.assertEquals(0, plugin.icon()) Assertions.assertEquals(0, plugin.icon())
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.FIVE_MIN_DATA, plugin.state) Assertions.assertEquals(BgQualityCheck.State.FIVE_MIN_DATA, plugin.state)
Assert.assertEquals(0, plugin.icon()) Assertions.assertEquals(0, plugin.icon())
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.RECALCULATED, plugin.state) Assertions.assertEquals(BgQualityCheck.State.RECALCULATED, plugin.state)
Assert.assertEquals(R.drawable.ic_baseline_warning_24_yellow, plugin.icon()) Assertions.assertEquals(R.drawable.ic_baseline_warning_24_yellow, plugin.icon())
val superData: MutableList<GlucoseValue> = ArrayList() val superData: MutableList<GlucoseValue> = ArrayList()
superData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) superData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
@ -76,10 +84,10 @@ class BgQualityCheckPluginTest : TestBase() {
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.FIVE_MIN_DATA, plugin.state) Assertions.assertEquals(BgQualityCheck.State.FIVE_MIN_DATA, plugin.state)
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.RECALCULATED, plugin.state) Assertions.assertEquals(BgQualityCheck.State.RECALCULATED, plugin.state)
val duplicatedData: MutableList<GlucoseValue> = ArrayList() val duplicatedData: MutableList<GlucoseValue> = ArrayList()
duplicatedData.add( duplicatedData.add(
@ -136,8 +144,8 @@ class BgQualityCheckPluginTest : TestBase() {
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.DOUBLED, plugin.state) Assertions.assertEquals(BgQualityCheck.State.DOUBLED, plugin.state)
Assert.assertEquals(R.drawable.ic_baseline_warning_24_red, plugin.icon()) Assertions.assertEquals(R.drawable.ic_baseline_warning_24_red, plugin.icon())
val identicalData: MutableList<GlucoseValue> = ArrayList() val identicalData: MutableList<GlucoseValue> = ArrayList()
identicalData.add( identicalData.add(
@ -194,19 +202,73 @@ class BgQualityCheckPluginTest : TestBase() {
`when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false)
plugin.processBgData() plugin.processBgData()
Assert.assertEquals(BgQualityCheckPlugin.State.DOUBLED, plugin.state) Assertions.assertEquals(BgQualityCheck.State.DOUBLED, plugin.state)
// Flat data
val flatData: MutableList<GlucoseValue> = ArrayList()
flatData.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))
flatData.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))
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 101.0, timestamp = now + T.mins(-10).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(-15).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(-20).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(-25).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 99.0, timestamp = now + T.mins(-30).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(-35).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))
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(flatData)
`when`(iobCobCalculator.ads.lastBg()).thenReturn(flatData[0])
// Test non-dexcom plugin on flat data
class OtherPlugin : BgSource {
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true
}
`when`(activePlugin.activeBgSource).thenReturn(OtherPlugin())
plugin.processBgData()
Assertions.assertEquals(BgQualityCheck.State.FLAT, plugin.state)
Assertions.assertEquals(R.drawable.ic_baseline_trending_flat_24, plugin.icon())
// Test dexcom plugin on flat data
class DexcomPlugin : BgSource, DexcomBoyda {
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true
}
`when`(activePlugin.activeBgSource).thenReturn(DexcomPlugin())
plugin.processBgData()
Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state)
// not enough data
val incompleteData: MutableList<GlucoseValue> = ArrayList()
incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(incompleteData)
`when`(iobCobCalculator.ads.lastBg()).thenReturn(incompleteData[0])
`when`(activePlugin.activeBgSource).thenReturn(OtherPlugin())
plugin.processBgData()// must be more than 5 values
Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state)
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 101.0, timestamp = now + T.mins(-10).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(-15).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(-20).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(-25).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 99.0, timestamp = now + T.mins(-30).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(-35).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))
plugin.processBgData() // must be at least 45 min old
Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state)
} }
@Test @Test
fun applyMaxIOBConstraintsTest() { fun applyMaxIOBConstraintsTest() {
plugin.state = BgQualityCheckPlugin.State.UNKNOWN plugin.state = BgQualityCheck.State.UNKNOWN
Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001)
plugin.state = BgQualityCheckPlugin.State.FIVE_MIN_DATA plugin.state = BgQualityCheck.State.FIVE_MIN_DATA
Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001)
plugin.state = BgQualityCheckPlugin.State.RECALCULATED plugin.state = BgQualityCheck.State.RECALCULATED
Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001)
plugin.state = BgQualityCheckPlugin.State.DOUBLED plugin.state = BgQualityCheck.State.DOUBLED
Assert.assertEquals(0.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) Assertions.assertEquals(0.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001)
} }
} }