commit
730fbd7001
63 changed files with 903 additions and 304 deletions
|
@ -177,7 +177,6 @@ allprojects {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':plugins:aps')
|
||||
wearApp project(':wear')
|
||||
|
||||
// in order to use internet's versions you'd need to enable Jetifier again
|
||||
|
@ -203,6 +202,7 @@ dependencies {
|
|||
implementation project(':plugins:main')
|
||||
implementation project(':plugins:openhumans')
|
||||
implementation project(':plugins:sensitivity')
|
||||
implementation project(':plugins:smoothing')
|
||||
implementation project(':plugins:source')
|
||||
implementation project(':plugins:sync')
|
||||
implementation project(':implementation')
|
||||
|
|
|
@ -51,6 +51,9 @@ import info.nightscout.pump.virtual.VirtualPumpPlugin
|
|||
import info.nightscout.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.sensitivity.SensitivityOref1Plugin
|
||||
import info.nightscout.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.smoothing.ExponentialSmoothingPlugin
|
||||
import info.nightscout.smoothing.AvgSmoothingPlugin
|
||||
import info.nightscout.smoothing.NoSmoothingPlugin
|
||||
import info.nightscout.source.AidexPlugin
|
||||
import info.nightscout.source.DexcomPlugin
|
||||
import info.nightscout.source.GlimpPlugin
|
||||
|
@ -434,6 +437,24 @@ abstract class PluginsListModule {
|
|||
@IntKey(500)
|
||||
abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@AllConfigs
|
||||
@IntoMap
|
||||
@IntKey(600)
|
||||
abstract fun bindNoSmoothingPlugin(plugin: NoSmoothingPlugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@AllConfigs
|
||||
@IntoMap
|
||||
@IntKey(605)
|
||||
abstract fun bindExponentialSmoothingPlugin(plugin: ExponentialSmoothingPlugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@AllConfigs
|
||||
@IntoMap
|
||||
@IntKey(610)
|
||||
abstract fun bindAvgSmoothingPlugin(plugin: AvgSmoothingPlugin): PluginBase
|
||||
|
||||
@Qualifier
|
||||
annotation class AllConfigs
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import info.nightscout.interfaces.aps.Loop
|
|||
import info.nightscout.interfaces.configBuilder.RunningConfiguration
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.plugin.PluginBase
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.interfaces.queue.Command
|
||||
import info.nightscout.interfaces.queue.CommandQueue
|
||||
|
@ -155,8 +154,7 @@ class KeepAliveWorker(
|
|||
var shouldUploadStatus = false
|
||||
if (config.NSCLIENT) return
|
||||
if (config.PUMPCONTROL) shouldUploadStatus = true
|
||||
else if (!(loop as PluginBase).isEnabled() || iobCobCalculator.ads.actualBg() == null)
|
||||
shouldUploadStatus = true
|
||||
else if (!loop.isEnabled() || iobCobCalculator.ads.actualBg() == null) shouldUploadStatus = true
|
||||
else if (dateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
|
||||
if (dateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) {
|
||||
lastIobUpload = dateUtil.now()
|
||||
|
|
|
@ -16,7 +16,9 @@ import info.nightscout.core.graph.data.ScaledDataPoint
|
|||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.database.entities.TemporaryTarget
|
||||
import info.nightscout.interfaces.aps.AutosensData
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.CobInfo
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.iob.IobTotal
|
||||
|
||||
|
@ -45,12 +47,12 @@ interface OverviewData {
|
|||
* BG
|
||||
*/
|
||||
|
||||
val lastBg: GlucoseValue?
|
||||
val isLow: Boolean
|
||||
val isHigh: Boolean
|
||||
@ColorInt fun lastBgColor(context: Context?): Int
|
||||
val lastBgDescription: String
|
||||
val isActualBg: Boolean
|
||||
fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue?
|
||||
fun isLow(autosensDataStore: AutosensDataStore): Boolean
|
||||
fun isHigh(autosensDataStore: AutosensDataStore): Boolean
|
||||
@ColorInt fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int
|
||||
fun lastBgDescription(autosensDataStore: AutosensDataStore): String
|
||||
fun isActualBg(autosensDataStore: AutosensDataStore): Boolean
|
||||
/*
|
||||
* TEMPORARY BASAL
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.database.entities.Bolus
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.profile.DefaultValueHelper
|
||||
|
@ -22,7 +23,7 @@ class BolusDataPoint(
|
|||
get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
|
||||
override val duration = 0L
|
||||
override val size = 2f
|
||||
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
|
||||
override val shape
|
||||
get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.core.graph.R
|
||||
import info.nightscout.database.entities.Carbs
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
|
@ -18,6 +19,7 @@ class CarbsDataPoint(
|
|||
override val duration = 0L
|
||||
override val size = 2f
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.CARBS
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
|
||||
|
||||
override fun color(context: Context?): Int {
|
||||
return if (data.isValid) rh.gac(context, info.nightscout.core.ui.R.attr.cobColor) else rh.gac(context, info.nightscout.core.ui.R.attr.alarmColor)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import com.jjoe64.graphview.series.DataPointInterface
|
||||
|
||||
interface DataPointWithLabelInterface : DataPointInterface {
|
||||
|
@ -13,5 +14,6 @@ interface DataPointWithLabelInterface : DataPointInterface {
|
|||
val duration: Long
|
||||
val shape: PointsWithLabelGraphSeries.Shape
|
||||
val size: Float
|
||||
val paintStyle: Paint.Style
|
||||
fun color(context: Context?): Int
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.database.entities.EffectiveProfileSwitch
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.utils.T
|
||||
|
@ -22,6 +23,7 @@ class EffectiveProfileSwitchDataPoint(
|
|||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
|
||||
override val size = 2f
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
|
||||
override fun color(context: Context?): Int {
|
||||
return rh.gac(context, info.nightscout.core.ui.R.attr.profileSwitchColor)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.database.entities.ExtendedBolus
|
||||
import info.nightscout.interfaces.utils.DecimalFormatter
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
|
@ -18,6 +19,7 @@ class ExtendedBolusDataPoint(
|
|||
override val duration get() = data.duration
|
||||
override val size = 10f
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
|
||||
override fun color(context: Context?): Int {
|
||||
return rh.gac(context, info.nightscout.core.ui.R.attr.extBolusColor)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.Constants
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
|
@ -16,7 +17,7 @@ class GlucoseValueDataPoint(
|
|||
private val rh: ResourceHelper
|
||||
) : DataPointWithLabelInterface {
|
||||
|
||||
fun valueToUnits(units: GlucoseUnit): Double =
|
||||
private fun valueToUnits(units: GlucoseUnit): Double =
|
||||
if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
|
||||
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
|
@ -26,16 +27,13 @@ class GlucoseValueDataPoint(
|
|||
override val label: String = Profile.toCurrentUnitsString(profileFunction, data.value)
|
||||
override val duration = 0L
|
||||
override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG
|
||||
override val size = 1f
|
||||
override val size = if (isPrediction) 1f else 0.6f
|
||||
override val paintStyle: Paint.Style = if (isPrediction) Paint.Style.FILL else Paint.Style.STROKE
|
||||
|
||||
override fun color(context: Context?): Int {
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
val highLine = defaultValueHelper.determineHighLine()
|
||||
return when {
|
||||
isPrediction -> predictionColor(context)
|
||||
valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
|
||||
valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
|
||||
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
|
||||
else -> rh.gac(context, info.nightscout.core.ui.R.attr.originalBgValueColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.interfaces.Constants
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.profile.DefaultValueHelper
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
|
||||
class InMemoryGlucoseValueDataPoint(
|
||||
val data: InMemoryGlucoseValue,
|
||||
private val defaultValueHelper: DefaultValueHelper,
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val rh: ResourceHelper
|
||||
) : DataPointWithLabelInterface {
|
||||
|
||||
fun valueToUnits(units: GlucoseUnit): Double =
|
||||
if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
|
||||
private fun valueToUnits(units: GlucoseUnit): Double =
|
||||
if (units == GlucoseUnit.MGDL) data.recalculated else data.recalculated * Constants.MGDL_TO_MMOLL
|
||||
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = valueToUnits(profileFunction.getUnits())
|
||||
|
@ -22,8 +25,18 @@ class InMemoryGlucoseValueDataPoint(
|
|||
override val label: String = ""
|
||||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
|
||||
override val size = 0.3f
|
||||
override val size = 1f
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL
|
||||
|
||||
override fun color(context: Context?): Int {
|
||||
return rh.gac(context, info.nightscout.core.ui.R.attr.inMemoryColor)
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
val highLine = defaultValueHelper.determineHighLine()
|
||||
return when {
|
||||
valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
|
||||
valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
|
||||
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -179,20 +179,20 @@ public class PointsWithLabelGraphSeries<E extends DataPointWithLabelInterface> e
|
|||
// draw data point
|
||||
if (!overdraw) {
|
||||
if (value.getShape() == Shape.BG || value.getShape() == Shape.COB_FAIL_OVER) {
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
mPaint.setStyle(value.getPaintStyle());
|
||||
mPaint.setStrokeWidth(0);
|
||||
canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint);
|
||||
} else if (value.getShape() == Shape.BG || value.getShape() == Shape.IOB_PREDICTION || value.getShape() == Shape.BUCKETED_BG) {
|
||||
mPaint.setColor(value.color(graphView.getContext()));
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
mPaint.setStyle(value.getPaintStyle());
|
||||
mPaint.setStrokeWidth(0);
|
||||
canvas.drawCircle(endX, endY, value.getSize() * scaledPxSize, mPaint);
|
||||
} else if (value.getShape() == Shape.PREDICTION) {
|
||||
mPaint.setColor(value.color(graphView.getContext()));
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
mPaint.setStyle(value.getPaintStyle());
|
||||
mPaint.setStrokeWidth(0);
|
||||
canvas.drawCircle(endX, endY, scaledPxSize, mPaint);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
mPaint.setStyle(value.getPaintStyle());
|
||||
mPaint.setStrokeWidth(0);
|
||||
canvas.drawCircle(endX, endY, scaledPxSize / 3, mPaint);
|
||||
} else if (value.getShape() == Shape.RECTANGLE) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.core.graph.data
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Paint
|
||||
import info.nightscout.database.entities.TherapyEvent
|
||||
import info.nightscout.interfaces.Constants
|
||||
import info.nightscout.interfaces.Translator
|
||||
|
@ -55,6 +56,7 @@ class TherapyEventDataPoint(
|
|||
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERAL_WITH_DURATION
|
||||
else -> PointsWithLabelGraphSeries.Shape.GENERAL
|
||||
}
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL // not used
|
||||
|
||||
override val size get() = if (rh.gb(info.nightscout.shared.R.bool.isTablet)) 12.0f else 10.0f
|
||||
override fun color(context: Context?): Int {
|
||||
|
|
|
@ -15,8 +15,18 @@ interface AutosensDataStore {
|
|||
var bucketedData: MutableList<InMemoryGlucoseValue>?
|
||||
var lastUsed5minCalculation: Boolean?
|
||||
|
||||
fun lastBg(): GlucoseValue?
|
||||
fun actualBg(): GlucoseValue?
|
||||
/**
|
||||
* Return last valid (>39) InMemoryGlucoseValue from bucketed data or null if db is empty
|
||||
*
|
||||
* @return InMemoryGlucoseValue or null
|
||||
*/
|
||||
fun lastBg(): InMemoryGlucoseValue?
|
||||
/**
|
||||
* Provide last bucketed InMemoryGlucoseValue or null if none exists within the last 9 minutes
|
||||
*
|
||||
* @return InMemoryGlucoseValue or null
|
||||
*/
|
||||
fun actualBg(): InMemoryGlucoseValue?
|
||||
fun lastDataTime(dateUtil: DateUtil): String
|
||||
fun clone(): AutosensDataStore
|
||||
fun getBgReadingsDataTableCopy(): List<GlucoseValue>
|
||||
|
|
|
@ -2,8 +2,9 @@ package info.nightscout.interfaces.iob
|
|||
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
|
||||
class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated: Boolean = false) {
|
||||
class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var trendArrow: GlucoseValue.TrendArrow = GlucoseValue.TrendArrow.NONE, var smoothed: Double? = null) {
|
||||
|
||||
constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value)
|
||||
// var generated : value doesn't correspond to real value with timestamp close to real BG
|
||||
constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value, gv.trendArrow)
|
||||
|
||||
val recalculated: Double get() = smoothed ?: value
|
||||
}
|
|
@ -9,6 +9,7 @@ import info.nightscout.interfaces.insulin.Insulin
|
|||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.profile.ProfileSource
|
||||
import info.nightscout.interfaces.pump.Pump
|
||||
import info.nightscout.interfaces.smoothing.Smoothing
|
||||
import info.nightscout.interfaces.source.BgSource
|
||||
import info.nightscout.interfaces.sync.NsClient
|
||||
import info.nightscout.interfaces.sync.Sync
|
||||
|
@ -74,6 +75,11 @@ interface ActivePlugin {
|
|||
*/
|
||||
val activeObjectives: Objectives?
|
||||
|
||||
/**
|
||||
* Smoothing plugin
|
||||
*/
|
||||
val activeSmoothing: Smoothing
|
||||
|
||||
/**
|
||||
* Currently selected NsClient plugin
|
||||
*/
|
||||
|
|
|
@ -6,5 +6,5 @@ package info.nightscout.interfaces.plugin
|
|||
* set by [info.nightscout.interfaces.PluginDescription.mainType]
|
||||
*/
|
||||
enum class PluginType {
|
||||
GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC
|
||||
GENERAL, SENSITIVITY, PROFILE, APS, PUMP, CONSTRAINTS, LOOP, BGSOURCE, INSULIN, SYNC, SMOOTHING
|
||||
}
|
|
@ -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>
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package info.nightscout.interfaces.utils
|
||||
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
|
||||
/**
|
||||
* Convert BG direction value to trend arrow or calculate it if not provided
|
||||
|
@ -15,6 +17,20 @@ interface TrendCalculator {
|
|||
* @return TrendArrow
|
||||
*/
|
||||
fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow
|
||||
/**
|
||||
* Provide or calculate trend
|
||||
*
|
||||
* @param glucoseValue BG
|
||||
* @return TrendArrow
|
||||
*/
|
||||
fun getTrendArrow(glucoseValue: InMemoryGlucoseValue?): GlucoseValue.TrendArrow
|
||||
/**
|
||||
* Provide or calculate trend from newest bucketed data
|
||||
*
|
||||
* @param autosensDataStore current store from IobCobCalculator
|
||||
* @return TrendArrow
|
||||
*/
|
||||
fun getTrendArrow(autosensDataStore: AutosensDataStore): GlucoseValue.TrendArrow?
|
||||
|
||||
/**
|
||||
* Provide or calculate trend
|
||||
|
@ -23,4 +39,11 @@ interface TrendCalculator {
|
|||
* @return string description of TrendArrow
|
||||
*/
|
||||
fun getTrendDescription(glucoseValue: GlucoseValue?): String
|
||||
/**
|
||||
* Provide or calculate trend from newest bucketed data
|
||||
*
|
||||
* @param autosensDataStore current store from IobCobCalculator
|
||||
* @return string description of TrendArrow
|
||||
*/
|
||||
fun getTrendDescription(autosensDataStore: AutosensDataStore): String
|
||||
}
|
|
@ -4,6 +4,7 @@ package info.nightscout.core.extensions
|
|||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.Constants
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.utils.DecimalFormatter
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import org.json.JSONObject
|
||||
|
@ -26,3 +27,12 @@ fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject =
|
|||
.put("direction", trendArrow.text)
|
||||
.put("type", "sgv")
|
||||
.also { if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) }
|
||||
|
||||
fun InMemoryGlucoseValue.valueToUnits(units: GlucoseUnit): Double =
|
||||
if (units == GlucoseUnit.MGDL) recalculated
|
||||
else recalculated * Constants.MGDL_TO_MMOLL
|
||||
|
||||
fun InMemoryGlucoseValue.valueToUnitsString(units: GlucoseUnit): String =
|
||||
if (units == GlucoseUnit.MGDL) DecimalFormatter.to0Decimal(recalculated)
|
||||
else DecimalFormatter.to1Decimal(recalculated * Constants.MGDL_TO_MMOLL)
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ import info.nightscout.core.extensions.valueToUnits
|
|||
import info.nightscout.core.iob.round
|
||||
import info.nightscout.core.utils.MidnightUtils
|
||||
import info.nightscout.database.ValueWrapper
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.aps.Loop
|
||||
import info.nightscout.interfaces.db.PersistenceLayer
|
||||
import info.nightscout.interfaces.iob.GlucoseStatusProvider
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.plugin.PluginBase
|
||||
import info.nightscout.interfaces.profile.Profile
|
||||
|
@ -107,7 +107,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
|
|||
|
||||
fun isActive(): Boolean = time.secondsFromMidnight() >= validFrom() && time.secondsFromMidnight() <= validTo() && forDevice(DEVICE_PHONE)
|
||||
|
||||
fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard {
|
||||
fun doCalc(profile: Profile, profileName: String, lastBG: InMemoryGlucoseValue, _synchronized: Boolean): BolusWizard {
|
||||
val dbRecord = persistenceLayer.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
||||
val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null
|
||||
//BG
|
||||
|
|
|
@ -59,6 +59,7 @@ data class RemoteDeviceStatus(
|
|||
@SerializedName("version") val version: String?,
|
||||
@SerializedName("insulin") val insulin: Int?,
|
||||
@SerializedName("sensitivity") val sensitivity: Int?,
|
||||
@SerializedName("smoothing") val smoothing: String?,
|
||||
@Contextual @SerializedName("insulinConfiguration") val insulinConfiguration: JSONObject?,
|
||||
@Contextual @SerializedName("sensitivityConfiguration") val sensitivityConfiguration: JSONObject?,
|
||||
@Contextual @SerializedName("overviewConfiguration") val overviewConfiguration: JSONObject?,
|
||||
|
|
5
core/ui/src/main/res/drawable/ic_timeline_24.xml
Normal file
5
core/ui/src/main/res/drawable/ic_timeline_24.xml
Normal 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>
|
|
@ -206,7 +206,7 @@
|
|||
<item name="smbColor">@color/tempbasal</item>
|
||||
<item name="bolusDataPointColor">@color/pumpHistory</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_FINGER_STICK_BG_VALUE">@color/red</item>
|
||||
<item name="therapyEvent_EXERCISE">@color/blue</item>
|
||||
|
|
|
@ -180,7 +180,7 @@
|
|||
<attr name="smbColor" format="reference|color" />
|
||||
<attr name="bolusDataPointColor" 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_FINGER_STICK_BG_VALUE" format="reference|color" />
|
||||
<attr name="therapyEvent_EXERCISE" format="reference|color" />
|
||||
|
|
|
@ -209,7 +209,7 @@
|
|||
<item name="smbColor">@color/tempbasal</item>
|
||||
<item name="bolusDataPointColor">@color/pumpHistory</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_FINGER_STICK_BG_VALUE">@color/red</item>
|
||||
<item name="therapyEvent_EXERCISE">@color/blue</item>
|
||||
|
|
|
@ -2,6 +2,8 @@ package info.nightscout.implementation
|
|||
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.utils.TrendCalculator
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.utils.T
|
||||
|
@ -15,6 +17,13 @@ class TrendCalculatorImpl @Inject constructor(
|
|||
) : TrendCalculator {
|
||||
|
||||
override fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow =
|
||||
when {
|
||||
glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE
|
||||
glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow
|
||||
else -> calculateDirection(InMemoryGlucoseValue(glucoseValue))
|
||||
}
|
||||
|
||||
override fun getTrendArrow(glucoseValue: InMemoryGlucoseValue?): GlucoseValue.TrendArrow =
|
||||
when {
|
||||
glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE
|
||||
glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow
|
||||
|
@ -34,7 +43,7 @@ class TrendCalculatorImpl @Inject constructor(
|
|||
else -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_unknown)
|
||||
}
|
||||
|
||||
private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow {
|
||||
private fun calculateDirection(glucoseValue: InMemoryGlucoseValue): GlucoseValue.TrendArrow {
|
||||
|
||||
val toTime = glucoseValue.timestamp
|
||||
val readings = repository.compatGetBgReadingsDataFromTime(toTime - T.mins(10).msecs(), toTime, false).blockingGet()
|
||||
|
@ -62,4 +71,55 @@ class TrendCalculatorImpl @Inject constructor(
|
|||
else -> GlucoseValue.TrendArrow.NONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTrendArrow(autosensDataStore: AutosensDataStore): GlucoseValue.TrendArrow? {
|
||||
val data = autosensDataStore.getBucketedDataTableCopy() ?: return null
|
||||
if (data.size == 0) return null
|
||||
val glucoseValue = data[0]
|
||||
return when {
|
||||
glucoseValue.value != glucoseValue.recalculated -> calculateDirection(data) // always recalculate after smoothing
|
||||
glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow
|
||||
else -> calculateDirection(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTrendDescription(autosensDataStore: AutosensDataStore): String {
|
||||
return when (getTrendArrow(autosensDataStore)) {
|
||||
GlucoseValue.TrendArrow.DOUBLE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_double_down)
|
||||
GlucoseValue.TrendArrow.SINGLE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_single_down)
|
||||
GlucoseValue.TrendArrow.FORTY_FIVE_DOWN -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_forty_five_down)
|
||||
GlucoseValue.TrendArrow.FLAT -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_flat)
|
||||
GlucoseValue.TrendArrow.FORTY_FIVE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_forty_five_up)
|
||||
GlucoseValue.TrendArrow.SINGLE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_single_up)
|
||||
GlucoseValue.TrendArrow.DOUBLE_UP -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_double_up)
|
||||
GlucoseValue.TrendArrow.NONE -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_none)
|
||||
else -> rh.gs(info.nightscout.core.ui.R.string.a11y_arrow_unknown)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateDirection(readings: MutableList<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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
|
|||
get() = getGlucoseStatusData()
|
||||
|
||||
override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? {
|
||||
val data = iobCobCalculator.ads.getBgReadingsDataTableCopy()
|
||||
val data = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return null
|
||||
val sizeRecords = data.size
|
||||
if (sizeRecords == 0) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0")
|
||||
|
@ -41,7 +41,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
|
|||
if (sizeRecords == 1) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1")
|
||||
return GlucoseStatus(
|
||||
glucose = now.value,
|
||||
glucose = now.recalculated,
|
||||
noise = 0.0,
|
||||
delta = 0.0,
|
||||
shortAvgDelta = 0.0,
|
||||
|
@ -49,31 +49,30 @@ class GlucoseStatusProviderImpl @Inject constructor(
|
|||
date = nowDate
|
||||
).asRounded()
|
||||
}
|
||||
val nowValueList = ArrayList<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) {
|
||||
if (data[i].recalculated > 38) {
|
||||
val then = data[i]
|
||||
val thenDate = then.timestamp
|
||||
|
||||
val minutesAgo = ((nowDate - thenDate) / (1000.0 * 60)).roundToLong()
|
||||
// multiply by 5 to get the same units as delta, i.e. mg/dL/5m
|
||||
change = now.value - then.value
|
||||
change = now.recalculated - then.recalculated
|
||||
val avgDel = change / minutesAgo * 5
|
||||
aapsLogger.debug(LTag.GLUCOSE, "$then minutesAgo=$minutesAgo avgDelta=$avgDel")
|
||||
|
||||
// use the average of all data points in the last 2.5m for all further "now" calculations
|
||||
if (0 < minutesAgo && minutesAgo < 2.5) {
|
||||
// Keep and average all values within the last 2.5 minutes
|
||||
nowValueList.add(then.value)
|
||||
now.value = average(nowValueList)
|
||||
// short_deltas are calculated from everything ~5-15 minutes ago
|
||||
} else if (2.5 < minutesAgo && minutesAgo < 17.5) {
|
||||
// if (0 < minutesAgo && minutesAgo < 2.5) {
|
||||
// // Keep and average all values within the last 2.5 minutes
|
||||
// nowValueList.add(then.recalculated)
|
||||
// now.value = average(nowValueList)
|
||||
// // short_deltas are calculated from everything ~5-15 minutes ago
|
||||
// } else
|
||||
if (2.5 < minutesAgo && minutesAgo < 17.5) {
|
||||
//console.error(minutesAgo, avgDelta);
|
||||
shortDeltas.add(avgDel)
|
||||
// last_deltas are calculated from everything ~5 minutes ago
|
||||
|
@ -96,7 +95,7 @@ class GlucoseStatusProviderImpl @Inject constructor(
|
|||
average(lastDeltas)
|
||||
}
|
||||
return GlucoseStatus(
|
||||
glucose = now.value,
|
||||
glucose = now.recalculated,
|
||||
date = nowDate,
|
||||
noise = 0.0, //for now set to nothing as not all CGMs report noise
|
||||
shortAvgDelta = shortAverageDelta,
|
||||
|
@ -105,6 +104,89 @@ class GlucoseStatusProviderImpl @Inject constructor(
|
|||
).also { aapsLogger.debug(LTag.GLUCOSE, it.log()) }.asRounded()
|
||||
}
|
||||
|
||||
/* Real BG (previous) version
|
||||
override fun getGlucoseStatusData(allowOldData: Boolean): GlucoseStatus? {
|
||||
val data = iobCobCalculator.ads.getBgReadingsDataTableCopy()
|
||||
val sizeRecords = data.size
|
||||
if (sizeRecords == 0) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0")
|
||||
return null
|
||||
}
|
||||
if (data[0].timestamp < dateUtil.now() - 7 * 60 * 1000L && !allowOldData) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "oldData")
|
||||
return null
|
||||
}
|
||||
val now = data[0]
|
||||
val nowDate = now.timestamp
|
||||
var change: Double
|
||||
if (sizeRecords == 1) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==1")
|
||||
return GlucoseStatus(
|
||||
glucose = now.value,
|
||||
noise = 0.0,
|
||||
delta = 0.0,
|
||||
shortAvgDelta = 0.0,
|
||||
longAvgDelta = 0.0,
|
||||
date = nowDate
|
||||
).asRounded()
|
||||
}
|
||||
val nowValueList = ArrayList<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 {
|
||||
|
||||
fun average(array: ArrayList<Double>): Double {
|
||||
|
|
|
@ -26,7 +26,9 @@ import info.nightscout.database.entities.GlucoseValue
|
|||
import info.nightscout.database.entities.TemporaryTarget
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.interfaces.aps.AutosensData
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.CobInfo
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.iob.IobTotal
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
|
@ -119,43 +121,42 @@ class OverviewDataImpl @Inject constructor(
|
|||
* BG
|
||||
*/
|
||||
|
||||
override val lastBg: GlucoseValue?
|
||||
get() =
|
||||
repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped ->
|
||||
if (gvWrapped is ValueWrapper.Existing) gvWrapped.value
|
||||
else null
|
||||
}
|
||||
override fun lastBg(autosensDataStore: AutosensDataStore): InMemoryGlucoseValue? =
|
||||
autosensDataStore.bucketedData?.let { if (it.size > 0) it[0] else null }
|
||||
// repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped ->
|
||||
// if (gvWrapped is ValueWrapper.Existing) gvWrapped.value
|
||||
// else null
|
||||
// }
|
||||
|
||||
override val isLow: Boolean
|
||||
get() = lastBg?.let { lastBg ->
|
||||
override fun isLow(autosensDataStore: AutosensDataStore): Boolean =
|
||||
lastBg(autosensDataStore)?.let { lastBg ->
|
||||
lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine()
|
||||
} ?: false
|
||||
|
||||
override val isHigh: Boolean
|
||||
get() = lastBg?.let { lastBg ->
|
||||
override fun isHigh(autosensDataStore: AutosensDataStore): Boolean =
|
||||
lastBg(autosensDataStore)?.let { lastBg ->
|
||||
lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine()
|
||||
} ?: false
|
||||
|
||||
@ColorInt
|
||||
override fun lastBgColor(context: Context?): Int =
|
||||
override fun lastBgColor(context: Context?, autosensDataStore: AutosensDataStore): Int =
|
||||
when {
|
||||
isLow -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
|
||||
isHigh -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
|
||||
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
|
||||
isLow(autosensDataStore) -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
|
||||
isHigh(autosensDataStore) -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
|
||||
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
|
||||
}
|
||||
|
||||
override val lastBgDescription: String
|
||||
get() = when {
|
||||
isLow -> rh.gs(info.nightscout.core.ui.R.string.a11y_low)
|
||||
isHigh -> rh.gs(info.nightscout.core.ui.R.string.a11y_high)
|
||||
else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange)
|
||||
override fun lastBgDescription(autosensDataStore: AutosensDataStore): String =
|
||||
when {
|
||||
isLow(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_low)
|
||||
isHigh(autosensDataStore) -> rh.gs(info.nightscout.core.ui.R.string.a11y_high)
|
||||
else -> rh.gs(info.nightscout.core.ui.R.string.a11y_inrange)
|
||||
}
|
||||
|
||||
override val isActualBg: Boolean
|
||||
get() =
|
||||
lastBg?.let { lastBg ->
|
||||
lastBg.timestamp > dateUtil.now() - T.mins(9).msecs()
|
||||
} ?: false
|
||||
override fun isActualBg(autosensDataStore: AutosensDataStore): Boolean =
|
||||
lastBg(autosensDataStore)?.let { lastBg ->
|
||||
lastBg.timestamp > dateUtil.now() - T.mins(9).msecs()
|
||||
} ?: false
|
||||
|
||||
/*
|
||||
* TEMPORARY BASAL
|
||||
|
|
|
@ -14,6 +14,7 @@ import info.nightscout.interfaces.plugin.PluginBase
|
|||
import info.nightscout.interfaces.plugin.PluginType
|
||||
import info.nightscout.interfaces.profile.ProfileSource
|
||||
import info.nightscout.interfaces.pump.Pump
|
||||
import info.nightscout.interfaces.smoothing.Smoothing
|
||||
import info.nightscout.interfaces.source.BgSource
|
||||
import info.nightscout.interfaces.sync.NsClient
|
||||
import info.nightscout.interfaces.sync.Sync
|
||||
|
@ -36,6 +37,7 @@ class PluginStore @Inject constructor(
|
|||
private var activeAPSStore: APS? = null
|
||||
private var activeInsulinStore: Insulin? = null
|
||||
private var activeSensitivityStore: Sensitivity? = null
|
||||
private var activeSmoothingStore: Smoothing? = null
|
||||
|
||||
override fun loadDefaults() {
|
||||
verifySelectionInCategories()
|
||||
|
@ -106,6 +108,16 @@ class PluginStore @Inject constructor(
|
|||
}
|
||||
setFragmentVisibilities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY)
|
||||
|
||||
// PluginType.SMOOTHING
|
||||
pluginsInCategory = getSpecificPluginsList(PluginType.SMOOTHING)
|
||||
activeSmoothingStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SMOOTHING) as Smoothing?
|
||||
if (activeSmoothingStore == null) {
|
||||
activeSmoothingStore = getDefaultPlugin(PluginType.SMOOTHING) as Smoothing
|
||||
(activeSmoothingStore as PluginBase).setPluginEnabled(PluginType.SMOOTHING, true)
|
||||
aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting SmoothingInterface")
|
||||
}
|
||||
setFragmentVisibilities((activeSmoothingStore as PluginBase).name, pluginsInCategory, PluginType.SMOOTHING)
|
||||
|
||||
// PluginType.PROFILE
|
||||
pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE)
|
||||
activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource?
|
||||
|
@ -181,6 +193,10 @@ class PluginStore @Inject constructor(
|
|||
get() = activeSensitivityStore
|
||||
?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" }
|
||||
|
||||
override val activeSmoothing: Smoothing
|
||||
get() = activeSmoothingStore
|
||||
?: checkNotNull(activeSmoothingStore) { "No smoothing selected" }
|
||||
|
||||
override val activeOverview: Overview
|
||||
get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview
|
||||
|
||||
|
@ -190,8 +206,7 @@ class PluginStore @Inject constructor(
|
|||
override val activeIobCobCalculator: IobCobCalculator
|
||||
get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator
|
||||
override val activeObjectives: Objectives?
|
||||
get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives
|
||||
|
||||
get() = getSpecificPluginsListByInterface(Objectives::class.java).firstOrNull() as Objectives?
|
||||
override val activeNsClient: NsClient?
|
||||
get() = getTheOneEnabledInArray(getSpecificPluginsListByInterface(NsClient::class.java), PluginType.SYNC) as NsClient?
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import info.nightscout.core.iob.log
|
|||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.GlucoseStatus
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import info.nightscout.shared.utils.T
|
||||
|
@ -40,7 +41,7 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun calculateValidGlucoseStatus() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateValidBgData())
|
||||
val glucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001)
|
||||
Assertions.assertEquals(-2.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -48,9 +49,11 @@ class GlucoseStatusTest : TestBase() {
|
|||
Assertions.assertEquals(-2.0, glucoseStatus.longAvgDelta, 0.001) // -2 -2 -2 -2
|
||||
Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date
|
||||
}
|
||||
/*
|
||||
Not testing anymore, not valid for bucketed data
|
||||
|
||||
@Test fun calculateMostRecentGlucoseStatus() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateMostRecentBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateMostRecentBgData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assertions.assertEquals(215.0, glucoseStatus.glucose, 0.001) // (214+216) / 2
|
||||
Assertions.assertEquals(-1.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -59,8 +62,17 @@ class GlucoseStatusTest : TestBase() {
|
|||
Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date, even when averaging
|
||||
}
|
||||
|
||||
private fun generateMostRecentBgData(): MutableList<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() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assertions.assertEquals(214.0, glucoseStatus.glucose, 0.001)
|
||||
Assertions.assertEquals(0.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -70,19 +82,19 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun insufficientDataShouldReturnNull() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateInsufficientBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateInsufficientBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
|
||||
Assertions.assertEquals(null, glucoseStatus)
|
||||
}
|
||||
|
||||
@Test fun oldDataShouldReturnNull() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
|
||||
Assertions.assertEquals(null, glucoseStatus)
|
||||
}
|
||||
|
||||
@Test fun returnOldDataIfAllowed() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOldBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).getGlucoseStatusData(true)
|
||||
Assertions.assertNotEquals(null, glucoseStatus)
|
||||
}
|
||||
|
@ -91,8 +103,11 @@ class GlucoseStatusTest : TestBase() {
|
|||
Assertions.assertEquals(0.0, GlucoseStatusProviderImpl.average(ArrayList()), 0.001)
|
||||
}
|
||||
|
||||
/*
|
||||
Not testing anymore, not valid for bucketed data
|
||||
|
||||
@Test fun calculateGlucoseStatusForLibreTestBgData() {
|
||||
Mockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateLibreTestData())
|
||||
Mockito.`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateLibreTestData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProviderImpl(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assertions.assertEquals(100.0, glucoseStatus.glucose, 0.001) //
|
||||
Assertions.assertEquals(-10.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -101,6 +116,23 @@ class GlucoseStatusTest : TestBase() {
|
|||
Assertions.assertEquals(1514766900000L, glucoseStatus.date) // latest date
|
||||
}
|
||||
|
||||
private fun generateLibreTestData(): MutableList<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
|
||||
fun initMocking() {
|
||||
Mockito.`when`(dateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs())
|
||||
|
@ -108,84 +140,32 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
// [{"mgdl":214,"mills":1521895773113,"device":"xDrip-DexcomG5","direction":"Flat","filtered":191040,"unfiltered":205024,"noise":1,"rssi":100},{"mgdl":219,"mills":1521896073352,"device":"xDrip-DexcomG5","direction":"Flat","filtered":200160,"unfiltered":209760,"noise":1,"rssi":100},{"mgdl":222,"mills":1521896372890,"device":"xDrip-DexcomG5","direction":"Flat","filtered":207360,"unfiltered":212512,"noise":1,"rssi":100},{"mgdl":220,"mills":1521896673062,"device":"xDrip-DexcomG5","direction":"Flat","filtered":211488,"unfiltered":210688,"noise":1,"rssi":100},{"mgdl":193,"mills":1521896972933,"device":"xDrip-DexcomG5","direction":"Flat","filtered":212384,"unfiltered":208960,"noise":1,"rssi":100},{"mgdl":181,"mills":1521897273336,"device":"xDrip-DexcomG5","direction":"SingleDown","filtered":210592,"unfiltered":204320,"noise":1,"rssi":100},{"mgdl":176,"mills":1521897572875,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":206720,"unfiltered":197440,"noise":1,"rssi":100},{"mgdl":168,"mills":1521897872929,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":201024,"unfiltered":187904,"noise":1,"rssi":100},{"mgdl":161,"mills":1521898172814,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":193376,"unfiltered":178144,"noise":1,"rssi":100},{"mgdl":148,"mills":1521898472879,"device":"xDrip-DexcomG5","direction":"SingleDown","filtered":183264,"unfiltered":161216,"noise":1,"rssi":100},{"mgdl":139,"mills":1521898772862,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":170784,"unfiltered":148928,"noise":1,"rssi":100},{"mgdl":132,"mills":1521899072896,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":157248,"unfiltered":139552,"noise":1,"rssi":100},{"mgdl":125,"mills":1521899372834,"device":"xDrip-DexcomG5","direction":"FortyFiveDown","filtered":144416,"unfiltered":129616.00000000001,"noise":1,"rssi":100},{"mgdl":128,"mills":1521899973456,"device":"xDrip-DexcomG5","direction":"Flat","filtered":130240.00000000001,"unfiltered":133536,"noise":1,"rssi":100},{"mgdl":132,"mills":1521900573287,"device":"xDrip-DexcomG5","direction":"Flat","filtered":133504,"unfiltered":138720,"noise":1,"rssi":100},{"mgdl":127,"mills":1521900873711,"device":"xDrip-DexcomG5","direction":"Flat","filtered":136480,"unfiltered":132992,"noise":1,"rssi":100},{"mgdl":127,"mills":1521901180151,"device":"xDrip-DexcomG5","direction":"Flat","filtered":136896,"unfiltered":132128,"noise":1,"rssi":100},{"mgdl":125,"mills":1521901473582,"device":"xDrip-DexcomG5","direction":"Flat","filtered":134624,"unfiltered":129696,"noise":1,"rssi":100},{"mgdl":120,"mills":1521901773597,"device":"xDrip-DexcomG5","direction":"Flat","filtered":130704.00000000001,"unfiltered":123376,"noise":1,"rssi":100},{"mgdl":116,"mills":1521902075855,"device":"xDrip-DexcomG5","direction":"Flat","filtered":126272,"unfiltered":118448,"noise":1,"rssi":100}]
|
||||
private fun generateValidBgData(): List<GlucoseValue> {
|
||||
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 = 1514766600000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 219.0, timestamp = 1514766300000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 223.0, timestamp = 1514766000000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 222.0, timestamp = 1514765700000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 224.0, timestamp = 1514765400000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 226.0, timestamp = 1514765100000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
private fun generateValidBgData(): MutableList<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 = 1514766600000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
return list
|
||||
}
|
||||
|
||||
private fun generateMostRecentBgData(): List<GlucoseValue> {
|
||||
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> {
|
||||
private fun generateInsufficientBgData(): MutableList<InMemoryGlucoseValue> {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
private fun generateOldBgData(): List<GlucoseValue> {
|
||||
val list: MutableList<GlucoseValue> = ArrayList()
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
private fun generateOldBgData(): MutableList<InMemoryGlucoseValue> {
|
||||
val list: MutableList<InMemoryGlucoseValue> = ArrayList()
|
||||
list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
return list
|
||||
}
|
||||
|
||||
private fun generateOneCurrentRecordBgData(): List<GlucoseValue> {
|
||||
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))
|
||||
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
|
||||
)
|
||||
)
|
||||
private fun generateOneCurrentRecordBgData(): MutableList<InMemoryGlucoseValue> {
|
||||
val list: MutableList<InMemoryGlucoseValue> = ArrayList()
|
||||
list.add(InMemoryGlucoseValue(value = 214.0, timestamp = 1514766900000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
return list
|
||||
}
|
||||
}
|
|
@ -4,8 +4,9 @@ import com.google.common.base.Optional
|
|||
import info.nightscout.automation.elements.Comparator
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import org.json.JSONObject
|
||||
import org.junit.Assert
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.`when`
|
||||
|
@ -22,39 +23,39 @@ class TriggerBgTest : TriggerTestBase() {
|
|||
|
||||
@Test
|
||||
fun shouldRunTest() {
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
var t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(215.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList())
|
||||
t = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun copyConstructorTest() {
|
||||
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
val t1 = t.duplicate() as TriggerBg
|
||||
Assert.assertEquals(213.0, t1.bg.value, 0.01)
|
||||
Assert.assertEquals(GlucoseUnit.MGDL, t1.bg.units)
|
||||
Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value)
|
||||
Assertions.assertEquals(213.0, t1.bg.value, 0.01)
|
||||
Assertions.assertEquals(GlucoseUnit.MGDL, t1.bg.units)
|
||||
Assertions.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value)
|
||||
}
|
||||
|
||||
private var bgJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"bg\":4.1,\"units\":\"mmol\"},\"type\":\"TriggerBg\"}"
|
||||
|
@ -62,35 +63,26 @@ class TriggerBgTest : TriggerTestBase() {
|
|||
@Test
|
||||
fun toJSONTest() {
|
||||
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertEquals(bgJson, t.toJSON())
|
||||
Assertions.assertEquals(bgJson, t.toJSON())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fromJSONTest() {
|
||||
val t: TriggerBg = TriggerBg(injector).setUnits(GlucoseUnit.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL)
|
||||
val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerBg
|
||||
Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
|
||||
Assert.assertEquals(4.1, t2.bg.value, 0.01)
|
||||
Assert.assertEquals(GlucoseUnit.MMOL, t2.bg.units)
|
||||
Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
|
||||
Assertions.assertEquals(4.1, t2.bg.value, 0.01)
|
||||
Assertions.assertEquals(GlucoseUnit.MMOL, t2.bg.units)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iconTest() {
|
||||
Assert.assertEquals(Optional.of(info.nightscout.core.main.R.drawable.ic_cp_bgcheck), TriggerBg(injector).icon())
|
||||
Assertions.assertEquals(Optional.of(info.nightscout.core.main.R.drawable.ic_cp_bgcheck), TriggerBg(injector).icon())
|
||||
}
|
||||
|
||||
private fun generateOneCurrentRecordBgData(): List<GlucoseValue> {
|
||||
val list: MutableList<GlucoseValue> = ArrayList()
|
||||
list.add(
|
||||
GlucoseValue(
|
||||
raw = 0.0,
|
||||
noise = 0.0,
|
||||
value = 214.0,
|
||||
timestamp = now - 1,
|
||||
sourceSensor = GlucoseValue.SourceSensor.UNKNOWN,
|
||||
trendArrow = GlucoseValue.TrendArrow.FLAT
|
||||
)
|
||||
)
|
||||
private fun generateOneCurrentRecordBgData(): MutableList<InMemoryGlucoseValue> {
|
||||
val list: MutableList<InMemoryGlucoseValue> = ArrayList()
|
||||
list.add(InMemoryGlucoseValue(value = 214.0, timestamp = now - 1, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
return list
|
||||
}
|
||||
}
|
|
@ -6,8 +6,9 @@ import info.nightscout.automation.elements.Comparator
|
|||
import info.nightscout.automation.elements.InputDelta.DeltaType
|
||||
import info.nightscout.database.entities.GlucoseValue
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import org.json.JSONObject
|
||||
import org.junit.Assert
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.`when`
|
||||
|
@ -23,42 +24,42 @@ class TriggerDeltaTest : TriggerTestBase() {
|
|||
}
|
||||
|
||||
@Test fun shouldRunTest() {
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(generateValidBgData())
|
||||
var t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(73.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assert.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType)
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
Assertions.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType)
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-2.0, DeltaType.SHORT_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assert.assertEquals(DeltaType.SHORT_AVERAGE, t.delta.deltaType)
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
Assertions.assertEquals(DeltaType.SHORT_AVERAGE, t.delta.deltaType)
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-3.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType)
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
Assertions.assertEquals(DeltaType.DELTA, t.delta.deltaType)
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(2.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(2.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(0.3, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(0.1, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-0.5, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(-0.2, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
`when`(autosensDataStore.getBucketedDataTableCopy()).thenReturn(ArrayList())
|
||||
t = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assertions.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
Assertions.assertTrue(t.shouldRun())
|
||||
}
|
||||
|
||||
@Test fun copyConstructorTest() {
|
||||
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
val t1 = t.duplicate() as TriggerDelta
|
||||
Assert.assertEquals(213.0, t1.delta.value, 0.01)
|
||||
Assert.assertEquals(GlucoseUnit.MGDL, t1.units)
|
||||
Assert.assertEquals(DeltaType.DELTA, t.delta.deltaType)
|
||||
Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value)
|
||||
Assertions.assertEquals(213.0, t1.delta.value, 0.01)
|
||||
Assertions.assertEquals(GlucoseUnit.MGDL, t1.units)
|
||||
Assertions.assertEquals(DeltaType.DELTA, t.delta.deltaType)
|
||||
Assertions.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.comparator.value)
|
||||
}
|
||||
|
||||
private var deltaJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"deltaType\":\"DELTA\",\"units\":\"mg/dl\",\"value\":4.1},\"type\":\"TriggerDelta\"}"
|
||||
|
@ -66,38 +67,38 @@ class TriggerDeltaTest : TriggerTestBase() {
|
|||
@Test
|
||||
fun toJSONTest() {
|
||||
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MGDL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertEquals(deltaJson, t.toJSON())
|
||||
Assertions.assertEquals(deltaJson, t.toJSON())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fromJSONTest() {
|
||||
val t: TriggerDelta = TriggerDelta(injector).units(GlucoseUnit.MMOL).setValue(4.1, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL)
|
||||
val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerDelta
|
||||
Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
|
||||
Assert.assertEquals(4.1, t2.delta.value, 0.01)
|
||||
Assert.assertEquals(GlucoseUnit.MMOL, t2.units)
|
||||
Assert.assertEquals(DeltaType.DELTA, t2.delta.deltaType)
|
||||
Assertions.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)
|
||||
Assertions.assertEquals(4.1, t2.delta.value, 0.01)
|
||||
Assertions.assertEquals(GlucoseUnit.MMOL, t2.units)
|
||||
Assertions.assertEquals(DeltaType.DELTA, t2.delta.deltaType)
|
||||
}
|
||||
|
||||
@Test fun iconTest() {
|
||||
Assert.assertEquals(Optional.of(R.drawable.ic_auto_delta), TriggerDelta(injector).icon())
|
||||
Assertions.assertEquals(Optional.of(R.drawable.ic_auto_delta), TriggerDelta(injector).icon())
|
||||
}
|
||||
|
||||
@Test fun initializerTest() {
|
||||
val t = TriggerDelta(injector)
|
||||
Assert.assertTrue(t.units == GlucoseUnit.MGDL)
|
||||
Assertions.assertTrue(t.units == GlucoseUnit.MGDL)
|
||||
}
|
||||
|
||||
private fun generateValidBgData(): List<GlucoseValue> {
|
||||
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 = 1514766600000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 219.0, timestamp = 1514766300000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 223.0, timestamp = 1514766000000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 222.0, timestamp = 1514765700000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 224.0, timestamp = 1514765400000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 226.0, timestamp = 1514765100000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 228.0, timestamp = 1514764800000, sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
private fun generateValidBgData(): MutableList<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 = 1514766600000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 219.0, timestamp = 1514766300000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 223.0, timestamp = 1514766000000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 222.0, timestamp = 1514765700000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 224.0, timestamp = 1514765400000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 226.0, timestamp = 1514765100000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
list.add(InMemoryGlucoseValue(value = 228.0, timestamp = 1514764800000, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
return list
|
||||
}
|
||||
}
|
|
@ -103,6 +103,7 @@ class ConfigBuilderFragment : DaggerFragment() {
|
|||
createViewsForPlugins(info.nightscout.core.ui.R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInList(PluginType.INSULIN))
|
||||
if (!config.NSCLIENT) {
|
||||
createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInList(PluginType.BGSOURCE))
|
||||
createViewsForPlugins(R.string.configbuilder_smoothing, R.string.configbuilder_smoothing_description, PluginType.SMOOTHING, activePlugin.getSpecificPluginsVisibleInList(PluginType.SMOOTHING))
|
||||
createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, activePlugin.getSpecificPluginsVisibleInList(PluginType.PUMP))
|
||||
}
|
||||
if (config.APS || config.PUMPCONTROL || config.isEngineeringMode())
|
||||
|
|
|
@ -20,6 +20,7 @@ import info.nightscout.interfaces.plugin.PluginType
|
|||
import info.nightscout.interfaces.profile.ProfileSource
|
||||
import info.nightscout.interfaces.pump.Pump
|
||||
import info.nightscout.interfaces.pump.PumpSync
|
||||
import info.nightscout.interfaces.smoothing.Smoothing
|
||||
import info.nightscout.interfaces.source.BgSource
|
||||
import info.nightscout.interfaces.sync.NsClient
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
|
@ -131,7 +132,9 @@ class ConfigBuilderPlugin @Inject constructor(
|
|||
(if (p.isEnabled(PluginType.CONSTRAINTS)) " CONSTRAINTS" else "") +
|
||||
(if (p.isEnabled(PluginType.LOOP)) " LOOP" else "") +
|
||||
(if (p.isEnabled(PluginType.BGSOURCE)) " BGSOURCE" else "") +
|
||||
if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else ""
|
||||
(if (p.isEnabled(PluginType.INSULIN)) " INSULIN" else "") +
|
||||
(if (p.isEnabled(PluginType.SYNC)) " SYNC" else "") +
|
||||
if (p.isEnabled(PluginType.SMOOTHING)) " SMOOTHING" else ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -195,6 +198,7 @@ class ConfigBuilderPlugin @Inject constructor(
|
|||
when (type) {
|
||||
PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java)
|
||||
PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Sensitivity::class.java)
|
||||
PluginType.SMOOTHING -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Smoothing::class.java)
|
||||
PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java)
|
||||
PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java)
|
||||
PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java)
|
||||
|
|
|
@ -11,6 +11,7 @@ import info.nightscout.interfaces.plugin.ActivePlugin
|
|||
import info.nightscout.interfaces.plugin.PluginType
|
||||
import info.nightscout.interfaces.pump.PumpSync
|
||||
import info.nightscout.interfaces.pump.defs.PumpType
|
||||
import info.nightscout.interfaces.smoothing.Smoothing
|
||||
import info.nightscout.interfaces.ui.UiInteraction
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventNSClientNewLog
|
||||
|
@ -52,11 +53,13 @@ class RunningConfigurationImpl @Inject constructor(
|
|||
val sensitivityInterface = activePlugin.activeSensitivity
|
||||
val overviewInterface = activePlugin.activeOverview
|
||||
val safetyInterface = activePlugin.activeSafety
|
||||
val smoothingInterface = activePlugin.activeSmoothing
|
||||
|
||||
json.put("insulin", insulinInterface.id.value)
|
||||
json.put("insulinConfiguration", insulinInterface.configuration())
|
||||
json.put("sensitivity", sensitivityInterface.id.value)
|
||||
json.put("sensitivityConfiguration", sensitivityInterface.configuration())
|
||||
json.put("smoothing", smoothingInterface.javaClass.simpleName)
|
||||
json.put("overviewConfiguration", overviewInterface.configuration())
|
||||
json.put("safetyConfiguration", safetyInterface.configuration())
|
||||
json.put("pump", pumpInterface.model().description)
|
||||
|
@ -104,6 +107,18 @@ class RunningConfigurationImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
configuration.smoothing?.let {
|
||||
for (p in activePlugin.getSpecificPluginsListByInterface(Smoothing::class.java)) {
|
||||
val smoothingPlugin = p as Smoothing
|
||||
if (smoothingPlugin.javaClass.simpleName == it) {
|
||||
if (!p.isEnabled()) {
|
||||
aapsLogger.debug(LTag.CORE, "Changing smoothing plugin to ${smoothingPlugin.javaClass.simpleName}")
|
||||
configBuilder.performPluginSwitch(p, true, PluginType.SMOOTHING)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configuration.pump?.let {
|
||||
if (sp.getString(info.nightscout.core.utils.R.string.key_virtualpump_type, "fake") != it) {
|
||||
sp.putString(info.nightscout.core.utils.R.string.key_virtualpump_type, it)
|
||||
|
|
|
@ -77,6 +77,8 @@
|
|||
<string name="configbuilder_insulin_description">Which type of insulin are you using?</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_smoothing">Smoothing</string>
|
||||
<string name="configbuilder_smoothing_description">Choose smoothing algorithm</string>
|
||||
<string name="configbuilder_sensitivity">Sensitivity detection</string>
|
||||
<string name="configbuilder_sensitivity_description">Which sensitivity algorithm should be used?</string>
|
||||
<string name="config_builder_shortname">CONF</string>
|
||||
|
|
|
@ -8,6 +8,7 @@ import info.nightscout.database.entities.GlucoseValue
|
|||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
|
||||
import info.nightscout.interfaces.constraints.Constraint
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.source.BgSource
|
||||
|
@ -218,7 +219,7 @@ class BgQualityCheckPluginTest : TestBase() {
|
|||
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-40).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-45).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(flatData)
|
||||
`when`(iobCobCalculator.ads.lastBg()).thenReturn(flatData[0])
|
||||
`when`(iobCobCalculator.ads.lastBg()).thenReturn(InMemoryGlucoseValue(flatData[0]))
|
||||
|
||||
// Test non-dexcom plugin on flat data
|
||||
class OtherPlugin : BgSource {
|
||||
|
@ -247,7 +248,7 @@ class BgQualityCheckPluginTest : TestBase() {
|
|||
incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(incompleteData)
|
||||
`when`(iobCobCalculator.ads.lastBg()).thenReturn(incompleteData[0])
|
||||
`when`(iobCobCalculator.ads.lastBg()).thenReturn(InMemoryGlucoseValue(incompleteData[0]))
|
||||
`when`(activePlugin.activeBgSource).thenReturn(OtherPlugin())
|
||||
plugin.processBgData()// must be more than 5 values
|
||||
Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state)
|
||||
|
|
|
@ -115,7 +115,7 @@ class DataBroadcastPlugin @Inject constructor(
|
|||
val lastBG = iobCobCalculator.ads.lastBg() ?: return
|
||||
val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: return
|
||||
|
||||
bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl
|
||||
bundle.putDouble("glucoseMgdl", lastBG.recalculated) // last BG in mgdl
|
||||
bundle.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp
|
||||
bundle.putString("units", profileFunction.getUnits().asText) // units used in AAPS "mg/dl" or "mmol"
|
||||
bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string
|
||||
|
|
|
@ -80,10 +80,10 @@ import info.nightscout.plugins.ui.StatusLightHandler
|
|||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventAcceptOpenLoopChange
|
||||
import info.nightscout.rx.events.EventBucketedDataCreated
|
||||
import info.nightscout.rx.events.EventEffectiveProfileSwitchChanged
|
||||
import info.nightscout.rx.events.EventExtendedBolusChange
|
||||
import info.nightscout.rx.events.EventMobileToWear
|
||||
import info.nightscout.rx.events.EventNewBG
|
||||
import info.nightscout.rx.events.EventNewOpenLoopNotification
|
||||
import info.nightscout.rx.events.EventPreferenceChange
|
||||
import info.nightscout.rx.events.EventPumpStatusChanged
|
||||
|
@ -99,6 +99,7 @@ import info.nightscout.rx.logging.AAPSLogger
|
|||
import info.nightscout.rx.weardata.EventData
|
||||
import info.nightscout.shared.extensions.runOnUiThread
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.extensions.toVisibilityKeepSpace
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
|
@ -275,7 +276,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
sp.putBoolean(info.nightscout.core.utils.R.string.key_objectiveusescale, true)
|
||||
}, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventNewBG::class.java)
|
||||
.toObservable(EventBucketedDataCreated::class.java)
|
||||
.debounce(1L, TimeUnit.SECONDS)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ updateBg() }, fabricPrivacy::logException)
|
||||
|
@ -777,19 +778,19 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
@SuppressLint("SetTextI18n")
|
||||
fun updateBg() {
|
||||
val units = profileFunction.getUnits()
|
||||
val lastBg = overviewData.lastBg
|
||||
val lastBgColor = overviewData.lastBgColor(context)
|
||||
val isActualBg = overviewData.isActualBg
|
||||
val lastBg = overviewData.lastBg(iobCobCalculator.ads)
|
||||
val lastBgColor = overviewData.lastBgColor(context, iobCobCalculator.ads)
|
||||
val isActualBg = overviewData.isActualBg(iobCobCalculator.ads)
|
||||
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
|
||||
val trendDescription = trendCalculator.getTrendDescription(lastBg)
|
||||
val trendArrow = trendCalculator.getTrendArrow(lastBg)
|
||||
val lastBgDescription = overviewData.lastBgDescription
|
||||
val trendDescription = trendCalculator.getTrendDescription(iobCobCalculator.ads)
|
||||
val trendArrow = trendCalculator.getTrendArrow(iobCobCalculator.ads)
|
||||
val lastBgDescription = overviewData.lastBgDescription(iobCobCalculator.ads)
|
||||
runOnUiThread {
|
||||
_binding ?: return@runOnUiThread
|
||||
binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units)
|
||||
?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)
|
||||
binding.infoLayout.bg.text = lastBg?.valueToUnitsString(units) ?: ""
|
||||
binding.infoLayout.bg.setTextColor(lastBgColor)
|
||||
binding.infoLayout.arrow.setImageResource(trendArrow.directionToIcon())
|
||||
trendArrow?.let { binding.infoLayout.arrow.setImageResource(it.directionToIcon()) }
|
||||
binding.infoLayout.arrow.visibility = (trendArrow != null).toVisibilityKeepSpace()
|
||||
binding.infoLayout.arrow.setColorFilter(lastBgColor)
|
||||
binding.infoLayout.arrow.contentDescription = lastBgDescription + " " + rh.gs(info.nightscout.core.ui.R.string.and) + " " + trendDescription
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import info.nightscout.interfaces.aps.Loop
|
|||
import info.nightscout.interfaces.constraints.Constraint
|
||||
import info.nightscout.interfaces.constraints.Constraints
|
||||
import info.nightscout.interfaces.iob.GlucoseStatusProvider
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.logging.UserEntryLogger
|
||||
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
|
||||
|
@ -744,8 +745,9 @@ class DataHandlerMobile @Inject constructor(
|
|||
)
|
||||
)
|
||||
// GraphData
|
||||
val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong()
|
||||
rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet().map { getSingleBG(it) }))))
|
||||
iobCobCalculator.ads.getBucketedDataTableCopy()?.let { bucketedData ->
|
||||
rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(bucketedData.map { getSingleBG(it) }))))
|
||||
}
|
||||
// Treatments
|
||||
sendTreatments()
|
||||
// Status
|
||||
|
@ -933,7 +935,7 @@ class DataHandlerMobile @Inject constructor(
|
|||
return deltaString
|
||||
}
|
||||
|
||||
private fun getSingleBG(glucoseValue: GlucoseValue): EventData.SingleBg {
|
||||
private fun getSingleBG(glucoseValue: InMemoryGlucoseValue): EventData.SingleBg {
|
||||
val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true)
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units)
|
||||
|
@ -946,8 +948,8 @@ class DataHandlerMobile @Inject constructor(
|
|||
slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol,
|
||||
delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--",
|
||||
avgDelta = glucoseStatus?.let { deltaString(it.shortAvgDelta, it.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) } ?: "--",
|
||||
sgvLevel = if (glucoseValue.value > highLine) 1L else if (glucoseValue.value < lowLine) -1L else 0L,
|
||||
sgv = glucoseValue.value,
|
||||
sgvLevel = if (glucoseValue.recalculated > highLine) 1L else if (glucoseValue.recalculated < lowLine) -1L else 0L,
|
||||
sgv = glucoseValue.recalculated,
|
||||
high = highLine,
|
||||
low = lowLine,
|
||||
color = 0
|
||||
|
|
|
@ -155,7 +155,7 @@ class IobCobCalculatorPlugin @Inject constructor(
|
|||
overviewData = overviewData,
|
||||
reason = reason,
|
||||
end = System.currentTimeMillis(),
|
||||
bgDataReload = false,
|
||||
bgDataReload = true,
|
||||
cause = event
|
||||
)
|
||||
}
|
||||
|
|
|
@ -71,22 +71,24 @@ class AutosensDataStoreObject : AutosensDataStore {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return last valid (>39) GlucoseValue from database or null if db is empty
|
||||
* Return last valid (>39) InMemoryGlucoseValue from bucketed data or null if db is empty
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
* @return InMemoryGlucoseValue or null
|
||||
*/
|
||||
override fun lastBg(): GlucoseValue? =
|
||||
override fun lastBg(): InMemoryGlucoseValue? =
|
||||
synchronized(dataLock) {
|
||||
if (bgReadings.isNotEmpty()) bgReadings[0]
|
||||
else null
|
||||
bucketedData?.let { bucketedData ->
|
||||
if (bucketedData.isNotEmpty()) bucketedData[0]
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide last GlucoseValue or null if none exists within the last 9 minutes
|
||||
* Provide last bucketed InMemoryGlucoseValue or null if none exists within the last 9 minutes
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
* @return InMemoryGlucoseValue or null
|
||||
*/
|
||||
override fun actualBg(): GlucoseValue? {
|
||||
override fun actualBg(): InMemoryGlucoseValue? {
|
||||
val lastBg = lastBg() ?: return null
|
||||
return if (lastBg.timestamp > System.currentTimeMillis() - T.mins(9).msecs()) lastBg else null
|
||||
}
|
||||
|
@ -237,7 +239,7 @@ class AutosensDataStoreObject : AutosensDataStore {
|
|||
val bgDelta = newer.value - older.value
|
||||
val timeDiffToNew = newer.timestamp - currentTime
|
||||
val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta
|
||||
val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble(), true)
|
||||
val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble())
|
||||
newBucketedData.add(newBgReading)
|
||||
//log.debug("BG: " + newBgReading.value + " (" + new Date(newBgReading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")");
|
||||
}
|
||||
|
@ -273,7 +275,7 @@ class AutosensDataStoreObject : AutosensDataStore {
|
|||
val gapDelta = bgReadings[i].value - lastBg
|
||||
//console.error(gapDelta, lastBg, elapsed_minutes);
|
||||
val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta
|
||||
val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble(), true)
|
||||
val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble())
|
||||
//console.error("Interpolated", bData[j]);
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" }
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
android:id="@+id/bg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="00.0"
|
||||
tools:text="00.0"
|
||||
android:textSize="60sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@+id/delta_large"
|
||||
|
@ -32,7 +32,7 @@
|
|||
android:id="@+id/delta_large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="(+1.0)"
|
||||
tools:text="(+1.0)"
|
||||
android:textSize="60sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
|
@ -60,7 +60,6 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:srcCompat="@drawable/ic_flat"
|
||||
android:contentDescription="@string/trend_arrow" />
|
||||
|
||||
<TextView
|
||||
|
@ -69,7 +68,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
app:layout_constraintTop_toTopOf="@+id/long_avg_delta"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
|
@ -106,7 +105,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
|
@ -123,7 +122,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="15m Δ: "
|
||||
android:text="Δ15: "
|
||||
android:contentDescription="15 minutes delta"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -134,7 +133,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
|
@ -151,7 +150,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="40m Δ: "
|
||||
android:text="Δ40: "
|
||||
android:contentDescription="40 minutes delta"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -162,7 +161,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textAlignment="textEnd"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
|
@ -226,7 +225,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="-5dp"
|
||||
android:text="8:00 PM"
|
||||
tools:text="8:00 PM"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -238,7 +237,7 @@
|
|||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="-10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="(-5)"
|
||||
tools:text="(-5)"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -271,7 +270,7 @@
|
|||
android:gravity="center_horizontal"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -304,7 +303,7 @@
|
|||
android:gravity="center_horizontal"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -336,7 +335,7 @@
|
|||
android:gravity="center_horizontal"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -367,7 +366,7 @@
|
|||
android:gravity="center_horizontal"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -400,7 +399,7 @@
|
|||
android:gravity="center_horizontal"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
@ -412,7 +411,7 @@
|
|||
android:layout_marginTop="-9dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="n/a"
|
||||
tools:text="n/a"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
|
|
|
@ -21,6 +21,7 @@ import info.nightscout.interfaces.aps.Loop
|
|||
import info.nightscout.interfaces.constraints.Constraint
|
||||
import info.nightscout.interfaces.constraints.Constraints
|
||||
import info.nightscout.interfaces.iob.CobInfo
|
||||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobTotal
|
||||
import info.nightscout.interfaces.logging.UserEntryLogger
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
|
@ -68,7 +69,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
|
|||
@Mock lateinit var autosensDataStore: AutosensDataStore
|
||||
@Mock lateinit var smsManager: SmsManager
|
||||
|
||||
var injector: HasAndroidInjector = HasAndroidInjector {
|
||||
private var injector: HasAndroidInjector = HasAndroidInjector {
|
||||
AndroidInjector {
|
||||
if (it is PumpEnactResult) {
|
||||
it.context = context
|
||||
|
@ -98,7 +99,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
|
|||
|
||||
`when`(iobCobCalculator.getCobInfo(false, "SMS COB")).thenReturn(CobInfo(0, 10.0, 2.0))
|
||||
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore)
|
||||
`when`(autosensDataStore.lastBg()).thenReturn(reading)
|
||||
`when`(autosensDataStore.lastBg()).thenReturn(InMemoryGlucoseValue(reading))
|
||||
|
||||
`when`(sp.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678")
|
||||
|
||||
|
|
1
plugins/smoothing/.gitignore
vendored
Normal file
1
plugins/smoothing/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
23
plugins/smoothing/build.gradle
Normal file
23
plugins/smoothing/build.gradle
Normal 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')
|
||||
}
|
0
plugins/smoothing/consumer-rules.pro
Normal file
0
plugins/smoothing/consumer-rules.pro
Normal file
21
plugins/smoothing/proguard-rules.pro
vendored
Normal file
21
plugins/smoothing/proguard-rules.pro
vendored
Normal 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
|
4
plugins/smoothing/src/main/AndroidManifest.xml
Normal file
4
plugins/smoothing/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
11
plugins/smoothing/src/main/res/values/strings.xml
Normal file
11
plugins/smoothing/src/main/res/values/strings.xml
Normal 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>
|
|
@ -23,6 +23,7 @@ import info.nightscout.shared.sharedPreferences.SP
|
|||
import info.nightscout.shared.utils.T
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import java.lang.Math.random
|
||||
import java.util.Calendar
|
||||
import java.util.GregorianCalendar
|
||||
import javax.inject.Inject
|
||||
|
@ -101,7 +102,7 @@ class RandomBgPlugin @Inject constructor(
|
|||
|
||||
val cal = GregorianCalendar()
|
||||
val currentMinute = cal[Calendar.MINUTE] + (cal[Calendar.HOUR_OF_DAY] % 2) * 60
|
||||
val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2
|
||||
val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 + (random() - 0.5) * (max - min) * 0.4
|
||||
|
||||
cal[Calendar.MILLISECOND] = 0
|
||||
cal[Calendar.SECOND] = 0
|
||||
|
|
|
@ -13,17 +13,17 @@ include ':core:ui'
|
|||
include ':core:validators'
|
||||
include ':database:entities'
|
||||
include ':database:impl'
|
||||
include ':ui'
|
||||
include ':implementation'
|
||||
include ':plugins:aps'
|
||||
include ':plugins:automation'
|
||||
include ':plugins:configuration'
|
||||
include ':plugins:constraints'
|
||||
include ':plugins:insulin'
|
||||
include ':plugins:main'
|
||||
include ':plugins:openhumans'
|
||||
include ':plugins:sensitivity'
|
||||
include ':plugins:smoothing'
|
||||
include ':plugins:source'
|
||||
include ':plugins:constraints'
|
||||
include ':plugins:sync'
|
||||
include ':pump:combo'
|
||||
include ':pump:combov2'
|
||||
include ':pump:combov2:comboctl'
|
||||
|
@ -42,5 +42,6 @@ include ':pump:pump-common'
|
|||
include ':pump:pump-core'
|
||||
include ':pump:rileylink'
|
||||
include ':pump:virtual'
|
||||
include ':plugins:sync'
|
||||
include ':implementation'
|
||||
include ':ui'
|
||||
include ':workflow'
|
||||
|
|
|
@ -192,7 +192,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
}
|
||||
|
||||
iobCobCalculator.ads.actualBg()?.let { bgReading ->
|
||||
if (bgReading.value < 72)
|
||||
if (bgReading.recalculated < 72)
|
||||
binding.hypoTt.isChecked = true
|
||||
}
|
||||
binding.hypoTt.setOnClickListener {
|
||||
|
|
|
@ -35,6 +35,7 @@ import info.nightscout.interfaces.utils.TrendCalculator
|
|||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.extensions.toVisibilityKeepSpace
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
|
@ -125,20 +126,23 @@ class Widget : AppWidgetProvider() {
|
|||
|
||||
private fun updateBg(views: RemoteViews) {
|
||||
val units = profileFunction.getUnits()
|
||||
views.setTextViewText(R.id.bg, overviewData.lastBg?.valueToUnitsString(units) ?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short))
|
||||
views.setTextViewText(R.id.bg, overviewData.lastBg(iobCobCalculator.ads)?.valueToUnitsString(units) ?: rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short))
|
||||
views.setTextColor(
|
||||
R.id.bg, when {
|
||||
overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
|
||||
overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high)
|
||||
else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange)
|
||||
overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
|
||||
overviewData.isHigh(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_high)
|
||||
else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange)
|
||||
}
|
||||
)
|
||||
views.setImageViewResource(R.id.arrow, trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon())
|
||||
trendCalculator.getTrendArrow(iobCobCalculator.ads)?.let {
|
||||
views.setImageViewResource(R.id.arrow, it.directionToIcon())
|
||||
}
|
||||
views.setViewVisibility(R.id.arrow, (trendCalculator.getTrendArrow(iobCobCalculator.ads) != null).toVisibilityKeepSpace())
|
||||
views.setInt(
|
||||
R.id.arrow, "setColorFilter", when {
|
||||
overviewData.isLow -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
|
||||
overviewData.isHigh -> rh.gc(info.nightscout.core.ui.R.color.widget_high)
|
||||
else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange)
|
||||
overviewData.isLow(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_low)
|
||||
overviewData.isHigh(iobCobCalculator.ads) -> rh.gc(info.nightscout.core.ui.R.color.widget_high)
|
||||
else -> rh.gc(info.nightscout.core.ui.R.color.widget_inrange)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -154,10 +158,10 @@ class Widget : AppWidgetProvider() {
|
|||
}
|
||||
|
||||
// strike through if BG is old
|
||||
if (!overviewData.isActualBg) views.setInt(R.id.bg, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG or Paint.ANTI_ALIAS_FLAG)
|
||||
if (!overviewData.isActualBg(iobCobCalculator.ads)) views.setInt(R.id.bg, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG or Paint.ANTI_ALIAS_FLAG)
|
||||
else views.setInt(R.id.bg, "setPaintFlags", Paint.ANTI_ALIAS_FLAG)
|
||||
|
||||
views.setTextViewText(R.id.time_ago, dateUtil.minAgo(rh, overviewData.lastBg?.timestamp))
|
||||
views.setTextViewText(R.id.time_ago, dateUtil.minAgo(rh, overviewData.lastBg(iobCobCalculator.ads)?.timestamp))
|
||||
//views.setTextViewText(R.id.time_ago_short, "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")")
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import info.nightscout.core.utils.worker.LoggingWorker
|
|||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.interfaces.aps.AutosensDataStore
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventBucketedDataCreated
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
|
@ -25,6 +26,7 @@ class LoadBgDataWorker(
|
|||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
|
||||
class LoadBgData(
|
||||
val iobCobCalculator: IobCobCalculator,
|
||||
|
@ -32,7 +34,7 @@ class LoadBgDataWorker(
|
|||
)
|
||||
|
||||
|
||||
private fun AutosensDataStore.loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil, rxBus: RxBus) {
|
||||
private fun AutosensDataStore.loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
||||
synchronized(dataLock) {
|
||||
val start = to - T.hours((24 + 10 /* max dia */).toLong()).msecs()
|
||||
// there can be some readings with time in close future (caused by wrong time setting on sensor)
|
||||
|
@ -43,7 +45,15 @@ class LoadBgDataWorker(
|
|||
.filter { it.value >= 39 }
|
||||
aapsLogger.debug(LTag.AUTOSENS) { "BG data loaded. Size: ${bgReadings.size} Start date: ${dateUtil.dateAndTimeString(start)} End date: ${dateUtil.dateAndTimeString(to)}" }
|
||||
createBucketedData(aapsLogger, dateUtil)
|
||||
rxBus.send(EventBucketedDataCreated())
|
||||
}
|
||||
}
|
||||
|
||||
private fun AutosensDataStore.smoothData(activePlugin: ActivePlugin) {
|
||||
synchronized(dataLock) {
|
||||
bucketedData?.let {
|
||||
val smoothedData = activePlugin.activeSmoothing.smooth(it)
|
||||
bucketedData = smoothedData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +62,9 @@ class LoadBgDataWorker(
|
|||
val data = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as LoadBgData?
|
||||
?: return Result.failure(workDataOf("Error" to "missing input data"))
|
||||
|
||||
data.iobCobCalculator.ads.loadBgData(data.end, repository, aapsLogger, dateUtil, rxBus)
|
||||
data.iobCobCalculator.ads.loadBgData(data.end, repository, aapsLogger, dateUtil)
|
||||
data.iobCobCalculator.ads.smoothData(activePlugin)
|
||||
rxBus.send(EventBucketedDataCreated())
|
||||
data.iobCobCalculator.clearCache()
|
||||
return Result.success()
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import info.nightscout.core.graph.data.PointsWithLabelGraphSeries
|
|||
import info.nightscout.core.utils.receivers.DataWorkerStorage
|
||||
import info.nightscout.core.utils.worker.LoggingWorker
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.profile.DefaultValueHelper
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import javax.inject.Inject
|
||||
|
@ -22,6 +23,7 @@ class PrepareBucketedDataWorker(
|
|||
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
|
||||
class PrepareBucketedData(
|
||||
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
|
||||
|
@ -41,7 +43,7 @@ class PrepareBucketedDataWorker(
|
|||
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||
for (inMemoryGlucoseValue in bucketedData) {
|
||||
if (inMemoryGlucoseValue.timestamp < data.overviewData.fromTime || inMemoryGlucoseValue.timestamp > data.overviewData.toTime) continue
|
||||
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh))
|
||||
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, defaultValueHelper , profileFunction, rh))
|
||||
}
|
||||
bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
|
||||
data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
|
||||
|
|
|
@ -85,6 +85,7 @@ class PrepareIobAutosensGraphDataWorker(
|
|||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.IOB_PREDICTION
|
||||
override val size = 0.5f
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL
|
||||
|
||||
override fun color(context: Context?): Int = color
|
||||
fun setColor(color: Int): IobTotalDataPoint {
|
||||
|
@ -107,6 +108,7 @@ class PrepareIobAutosensGraphDataWorker(
|
|||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.COB_FAIL_OVER
|
||||
override val size = 0.5f
|
||||
override val paintStyle: Paint.Style = Paint.Style.FILL
|
||||
override fun color(context: Context?): Int {
|
||||
return rh.gac(context, info.nightscout.core.ui.R.attr.cobColor)
|
||||
}
|
||||
|
|
|
@ -122,14 +122,14 @@ class IobCobOref1Worker(
|
|||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
var avgDelta: Double
|
||||
var delta: Double
|
||||
val bg: Double = bucketedData[i].value
|
||||
if (bg < 39 || bucketedData[i + 3].value < 39) {
|
||||
val bg: Double = bucketedData[i].recalculated
|
||||
if (bg < 39 || bucketedData[i + 3].recalculated < 39) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
delta = bg - bucketedData[i + 1].recalculated
|
||||
avgDelta = (bg - bucketedData[i + 3].recalculated) / 3
|
||||
val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
|
|
|
@ -117,14 +117,14 @@ class IobCobOrefWorker @Inject internal constructor(
|
|||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
var avgDelta: Double
|
||||
var delta: Double
|
||||
val bg: Double = bucketedData[i].value
|
||||
if (bg < 39 || bucketedData[i + 3].value < 39) {
|
||||
val bg: Double = bucketedData[i].recalculated
|
||||
if (bg < 39 || bucketedData[i + 3].recalculated < 39) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
delta = bg - bucketedData[i + 1].recalculated
|
||||
avgDelta = (bg - bucketedData[i + 3].recalculated) / 3
|
||||
val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
|
|
Loading…
Reference in a new issue