IobCobCalculator refactor
This commit is contained in:
parent
c637b7072c
commit
36040b7ed2
|
@ -2,6 +2,22 @@
|
|||
<code_scheme name="Project" version="173">
|
||||
<option name="AUTODETECT_INDENTS" value="false" />
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="6" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="6" />
|
||||
|
|
|
@ -144,7 +144,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
validateInputs()
|
||||
}
|
||||
|
||||
iobCobCalculator.actualBg()?.let { bgReading ->
|
||||
iobCobCalculator.ads.actualBg()?.let { bgReading ->
|
||||
if (bgReading.value < 72)
|
||||
binding.hypoTt.isChecked = true
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ class WizardDialog : DaggerDialogFragment() {
|
|||
binding.bgInput.setStep(0.1)
|
||||
|
||||
// Set BG if not old
|
||||
binding.bgInput.value = iobCobCalculator.actualBg()?.valueToUnits(units) ?: 0.0
|
||||
binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0
|
||||
binding.ttcheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
||||
|
||||
// IOB calculation
|
||||
|
|
|
@ -124,7 +124,7 @@ open class LoopPlugin @Inject constructor(
|
|||
.subscribe({ event: EventAutosensCalculationFinished ->
|
||||
// Autosens calculation not triggered by a new BG
|
||||
if (event.cause !is EventNewBG) return@subscribe
|
||||
val glucoseValue = iobCobCalculator.actualBg() ?: return@subscribe
|
||||
val glucoseValue = iobCobCalculator.ads.actualBg() ?: return@subscribe
|
||||
// BG outdated
|
||||
// already looped with that value
|
||||
if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
|
||||
|
|
|
@ -48,7 +48,7 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R
|
|||
})
|
||||
tasks.add(object : Task(this, R.string.hasbgdata) {
|
||||
override fun isCompleted(): Boolean {
|
||||
return iobCobCalculator.lastBg() != null
|
||||
return iobCobCalculator.ads.lastBg() != null
|
||||
}
|
||||
})
|
||||
tasks.add(object : Task(this, R.string.loopenabled) {
|
||||
|
|
|
@ -119,7 +119,7 @@ class DataBroadcastPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
private fun bgStatus(bundle: Bundle) {
|
||||
val lastBG = iobCobCalculator.lastBg() ?: return
|
||||
val lastBG = iobCobCalculator.ads.lastBg() ?: return
|
||||
val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: return
|
||||
|
||||
bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl
|
||||
|
|
|
@ -411,7 +411,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
}
|
||||
|
||||
private fun onClickQuickWizard() {
|
||||
val actualBg = iobCobCalculator.actualBg()
|
||||
val actualBg = iobCobCalculator.ads.actualBg()
|
||||
val profile = profileFunction.getProfile()
|
||||
val profileName = profileFunction.getProfileName()
|
||||
val pump = activePlugin.activePump
|
||||
|
@ -446,11 +446,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun processButtonsVisibility() {
|
||||
val lastBG = iobCobCalculator.lastBg()
|
||||
val lastBG = iobCobCalculator.ads.lastBg()
|
||||
val pump = activePlugin.activePump
|
||||
val profile = profileFunction.getProfile()
|
||||
val profileName = profileFunction.getProfileName()
|
||||
val actualBG = iobCobCalculator.actualBg()
|
||||
val actualBG = iobCobCalculator.ads.actualBg()
|
||||
|
||||
// QuickWizard button
|
||||
val quickWizardEntry = quickWizard.getActive()
|
||||
|
@ -562,8 +562,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
|
||||
|
||||
val profile = profileFunction.getProfile() ?: return
|
||||
val actualBG = iobCobCalculator.actualBg()
|
||||
val lastBG = iobCobCalculator.lastBg()
|
||||
val actualBG = iobCobCalculator.ads.actualBg()
|
||||
val lastBG = iobCobCalculator.ads.lastBg()
|
||||
val pump = activePlugin.activePump
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
|
@ -812,7 +812,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
|||
}
|
||||
|
||||
binding.infoLayout.sensitivity.text =
|
||||
iobCobCalculator.getLastAutosensData("Overview")?.let { autosensData ->
|
||||
iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)?.let { autosensData ->
|
||||
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
|
||||
} ?: ""
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ class GraphData(
|
|||
}
|
||||
|
||||
fun addBucketedData(fromTime: Long, toTime: Long) {
|
||||
val bucketedData = iobCobCalculator.getBucketedDataTableCopy() ?: return
|
||||
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
|
||||
if (bucketedData.isEmpty()) {
|
||||
aapsLogger.debug("No bucketed data.")
|
||||
return
|
||||
|
@ -77,7 +77,7 @@ class GraphData(
|
|||
|
||||
fun addBgReadings(fromTime: Long, toTime: Long, highLine: Double, predictions: MutableList<GlucoseValueDataPoint>?) {
|
||||
var maxBgValue = Double.MIN_VALUE
|
||||
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, true).blockingGet()
|
||||
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
|
||||
if (bgReadingsArray?.isEmpty() != false) {
|
||||
aapsLogger.debug("No BG data.")
|
||||
maxY = if (units == Constants.MGDL) 180.0 else 10.0
|
||||
|
@ -302,8 +302,7 @@ class GraphData(
|
|||
|
||||
private fun getNearestBg(date: Long): Double {
|
||||
bgReadingsArray?.let { bgReadingsArray ->
|
||||
for (r in bgReadingsArray.indices) {
|
||||
val reading = bgReadingsArray[r]
|
||||
for (reading in bgReadingsArray) {
|
||||
if (reading.timestamp > date) continue
|
||||
return Profile.fromMgdlToUnits(reading.value, units)
|
||||
}
|
||||
|
@ -325,7 +324,7 @@ class GraphData(
|
|||
time += 5 * 60 * 1000L
|
||||
continue
|
||||
}
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(time, profile)
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||
val act: Double = total.activity
|
||||
if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPrediction.add(ScaledDataPoint(time, act, actScale))
|
||||
maxIAValue = max(maxIAValue, abs(act))
|
||||
|
@ -366,10 +365,10 @@ class GraphData(
|
|||
time += 5 * 60 * 1000L
|
||||
continue
|
||||
}
|
||||
val deviation = if (devBgiScale) iobCobCalculator.getAutosensData(time)?.deviation
|
||||
val deviation = if (devBgiScale) iobCobCalculator.ads.getAutosensDataAtTime(time)?.deviation
|
||||
?: 0.0 else 0.0
|
||||
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(time, profile)
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||
val bgi: Double = total.activity * profile.getIsfMgdl(time) * 5.0
|
||||
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale)) else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
|
||||
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
|
||||
|
@ -408,8 +407,8 @@ class GraphData(
|
|||
var iob = 0.0
|
||||
var absIob = 0.0
|
||||
if (profile != null) {
|
||||
iob = iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob
|
||||
if (absScale) absIob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTempsSynchronized(time).iob
|
||||
iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile).iob
|
||||
if (absScale) absIob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
|
||||
}
|
||||
if (abs(lastIob - iob) > 0.02) {
|
||||
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
|
||||
|
@ -465,7 +464,7 @@ class GraphData(
|
|||
while (time <= toTime) {
|
||||
val profile = profileFunction.getProfile(time)
|
||||
var iob = 0.0
|
||||
if (profile != null) iob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTempsSynchronized(time).iob
|
||||
if (profile != null) iob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
|
||||
if (abs(lastIob - iob) > 0.02) {
|
||||
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
|
||||
iobArray.add(ScaledDataPoint(time, iob, iobScale))
|
||||
|
@ -497,7 +496,7 @@ class GraphData(
|
|||
val cobScale = Scale()
|
||||
var time = fromTime
|
||||
while (time <= toTime) {
|
||||
iobCobCalculator.getAutosensData(time)?.let { autosensData ->
|
||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
||||
val cob = autosensData.cob.toInt()
|
||||
if (cob != lastCob) {
|
||||
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
|
||||
|
@ -543,11 +542,11 @@ class GraphData(
|
|||
// if align Dev Scale with BGI scale, then calculate BGI value, else bgi = 0.0
|
||||
val bgi: Double = if (devBgiScale) {
|
||||
val profile = profileFunction.getProfile(time) ?: continue
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(time, profile)
|
||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||
total.activity * profile.getIsfMgdl(time) * 5.0
|
||||
} else 0.0
|
||||
|
||||
iobCobCalculator.getAutosensData(time)?.let { autosensData ->
|
||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
||||
var color = resourceHelper.gc(R.color.deviationblack) // "="
|
||||
if (autosensData.type == "" || autosensData.type == "non-meal") {
|
||||
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
|
||||
|
@ -583,7 +582,7 @@ class GraphData(
|
|||
val ratioScale = Scale()
|
||||
var time = fromTime
|
||||
while (time <= toTime) {
|
||||
iobCobCalculator.getAutosensData(time)?.let { autosensData ->
|
||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
||||
ratioArray.add(ScaledDataPoint(time, autosensData.autosensResult.ratio - 1, ratioScale))
|
||||
maxRatioValueFound = max(maxRatioValueFound, autosensData.autosensResult.ratio - 1)
|
||||
minRatioValueFound = min(minRatioValueFound, autosensData.autosensResult.ratio - 1)
|
||||
|
@ -613,7 +612,7 @@ class GraphData(
|
|||
val dsMinScale = Scale()
|
||||
var time = fromTime
|
||||
while (time <= toTime) {
|
||||
iobCobCalculator.getAutosensData(time)?.let { autosensData ->
|
||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
||||
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
|
||||
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
|
||||
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
|
||||
|
|
|
@ -130,7 +130,7 @@ class PersistentNotificationPlugin @Inject constructor(
|
|||
if (profileFunction.isProfileValid("Notification")) {
|
||||
var line1aa: String
|
||||
val units = profileFunction.getUnits()
|
||||
val lastBG = iobCobCalculator.lastBg()
|
||||
val lastBG = iobCobCalculator.ads.lastBg()
|
||||
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
|
||||
if (lastBG != null) {
|
||||
line1aa = lastBG.valueToUnitsString(units)
|
||||
|
|
|
@ -310,8 +310,8 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
private fun processBG(receivedSms: Sms) {
|
||||
val actualBG = iobCobCalculator.actualBg()
|
||||
val lastBG = iobCobCalculator.lastBg()
|
||||
val actualBG = iobCobCalculator.ads.actualBg()
|
||||
val lastBG = iobCobCalculator.ads.lastBg()
|
||||
var reply = ""
|
||||
val units = profileFunction.getUnits()
|
||||
if (actualBG != null) {
|
||||
|
@ -854,7 +854,7 @@ class SmsCommunicatorPlugin @Inject constructor(
|
|||
} else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat)))
|
||||
}
|
||||
|
||||
fun toTodayTime(hh_colon_mm: String): Long {
|
||||
private fun toTodayTime(hh_colon_mm: String): Long {
|
||||
val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)")
|
||||
val m = p.matcher(hh_colon_mm)
|
||||
var retval: Long = 0
|
||||
|
|
|
@ -207,7 +207,7 @@ class ActionStringHandler @Inject constructor(
|
|||
sendError("No profile found!")
|
||||
return
|
||||
}
|
||||
val bgReading = iobCobCalculator.actualBg()
|
||||
val bgReading = iobCobCalculator.ads.actualBg()
|
||||
if (bgReading == null) {
|
||||
sendError("No recent BG to base calculation on!")
|
||||
return
|
||||
|
|
|
@ -279,7 +279,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
|
|||
|
||||
private void sendData() {
|
||||
|
||||
GlucoseValue lastBG = iobCobCalculator.lastBg();
|
||||
GlucoseValue lastBG = iobCobCalculator.getAds().lastBg();
|
||||
// Log.d(TAG, logPrefix + "LastBg=" + lastBG);
|
||||
if (lastBG != null) {
|
||||
GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData();
|
||||
|
@ -381,7 +381,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
|
|||
googleApiConnect();
|
||||
}
|
||||
long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5);
|
||||
GlucoseValue last_bg = iobCobCalculator.lastBg();
|
||||
GlucoseValue last_bg = iobCobCalculator.getAds().lastBg();
|
||||
|
||||
if (last_bg == null) return;
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import androidx.collection.LongSparseArray
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.data.InMemoryGlucoseValue
|
||||
import info.nightscout.androidaps.data.IobTotal
|
||||
import info.nightscout.androidaps.data.MealData
|
||||
import info.nightscout.androidaps.data.Profile
|
||||
|
@ -13,7 +12,6 @@ import info.nightscout.androidaps.database.AppRepository
|
|||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
import info.nightscout.androidaps.database.entities.Bolus
|
||||
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||
import info.nightscout.androidaps.database.interfaces.end
|
||||
import info.nightscout.androidaps.events.*
|
||||
|
@ -41,11 +39,9 @@ import io.reactivex.rxkotlin.plusAssign
|
|||
import org.json.JSONArray
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
@Singleton
|
||||
open class IobCobCalculatorPlugin @Inject constructor(
|
||||
|
@ -76,17 +72,11 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
|
||||
private var iobTable = LongSparseArray<IobTotal>() // oldest at index 0
|
||||
private var absIobTable = LongSparseArray<IobTotal>() // oldest at index 0, absolute insulin in the body
|
||||
private var autosensDataTable = LongSparseArray<AutosensData>() // oldest at index 0
|
||||
private var basalDataTable = LongSparseArray<BasalData>() // oldest at index 0
|
||||
internal var bgReadings: List<GlucoseValue> = listOf() // newest at index 0
|
||||
|
||||
internal var bucketedData: MutableList<InMemoryGlucoseValue>? = null
|
||||
override var ads: AutosensDataStore = AutosensDataStore()
|
||||
|
||||
// we need to make sure that bucketed_data will always have the same timestamp for correct use of cached values
|
||||
// once referenceTime != null all bucketed data should be (x * 5min) from referenceTime
|
||||
var referenceTime: Long = -1
|
||||
private var lastUsed5minCalculation: Boolean? = null // true if used 5min bucketed data
|
||||
override val dataLock = Any()
|
||||
private val dataLock = Any()
|
||||
var stopCalculationTrigger = false
|
||||
private var thread: Thread? = null
|
||||
|
||||
|
@ -96,12 +86,12 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
disposable += rxBus
|
||||
.toObservable(EventConfigBuilderChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event -> resetData("onEventConfigBuilderChange", event) }, fabricPrivacy::logException)
|
||||
.subscribe({ event -> resetDataAndRunCalculation("onEventConfigBuilderChange", event) }, fabricPrivacy::logException)
|
||||
// EventNewBasalProfile
|
||||
disposable += rxBus
|
||||
.toObservable(EventNewBasalProfile::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event -> resetData("onNewProfile", event) }, fabricPrivacy::logException)
|
||||
.subscribe({ event -> resetDataAndRunCalculation("onNewProfile", event) }, fabricPrivacy::logException)
|
||||
// EventPreferenceChange
|
||||
disposable += rxBus
|
||||
.toObservable(EventPreferenceChange::class.java)
|
||||
|
@ -115,7 +105,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
event.isChanged(resourceHelper, R.string.key_openapsama_autosens_max) ||
|
||||
event.isChanged(resourceHelper, R.string.key_openapsama_autosens_min) ||
|
||||
event.isChanged(resourceHelper, R.string.key_insulin_oref_peak)) {
|
||||
resetData("onEventPreferenceChange", event)
|
||||
resetDataAndRunCalculation("onEventPreferenceChange", event)
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
// EventAppInitialized
|
||||
|
@ -135,217 +125,19 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
super.onStop()
|
||||
}
|
||||
|
||||
override fun getBgReadingsDataTableCopy(): List<GlucoseValue> = bgReadings.toList()
|
||||
override fun getBucketedDataTableCopy(): MutableList<InMemoryGlucoseValue>? = bucketedData?.toMutableList()
|
||||
override fun getAutosensDataTable(): LongSparseArray<AutosensData> = autosensDataTable
|
||||
|
||||
private fun adjustToReferenceTime(someTime: Long): Long {
|
||||
if (referenceTime == -1L) {
|
||||
referenceTime = someTime
|
||||
return someTime
|
||||
}
|
||||
var diff = abs(someTime - referenceTime)
|
||||
diff %= T.mins(5).msecs()
|
||||
if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs()
|
||||
return someTime + diff
|
||||
}
|
||||
|
||||
fun loadBgData(to: Long) {
|
||||
val profile = profileFunction.getProfile(to)
|
||||
var dia = Constants.defaultDIA
|
||||
if (profile != null) dia = profile.dia
|
||||
val start = to - T.hours((24 + dia).toLong()).msecs()
|
||||
if (dateUtil.isCloseToNow(to)) {
|
||||
// if close to now expect there can be some readings with time in close future (caused by wrong time setting)
|
||||
// so read all records
|
||||
bgReadings = repository.compatGetBgReadingsDataFromTime(start, false).blockingGet()
|
||||
aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start))
|
||||
} else {
|
||||
bgReadings = repository.compatGetBgReadingsDataFromTime(start, to, false).blockingGet()
|
||||
aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start) + " End date: " + dateUtil.dateAndTimeString(to))
|
||||
}
|
||||
}
|
||||
|
||||
val isAbout5minData: Boolean
|
||||
get() {
|
||||
synchronized(dataLock) {
|
||||
if (bgReadings.size < 3) return true
|
||||
|
||||
var totalDiff: Long = 0
|
||||
for (i in 1 until bgReadings.size) {
|
||||
val bgTime = bgReadings[i].timestamp
|
||||
val lastBgTime = bgReadings[i - 1].timestamp
|
||||
var diff = lastBgTime - bgTime
|
||||
diff %= T.mins(5).msecs()
|
||||
if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs()
|
||||
totalDiff += diff
|
||||
diff = abs(diff)
|
||||
if (diff > T.secs(30).msecs()) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " diff: " + diff / 1000 + "[s] is5minData: " + false)
|
||||
return false
|
||||
}
|
||||
}
|
||||
val averageDiff = totalDiff / bgReadings.size / 1000
|
||||
val is5minData = averageDiff < 1
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " averageDiff: " + averageDiff + "[s] is5minData: " + is5minData)
|
||||
return is5minData
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetData(reason: String, event: Event?) {
|
||||
private fun resetDataAndRunCalculation(reason: String, event: Event?) {
|
||||
stopCalculation(reason)
|
||||
synchronized(dataLock) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of $reason.")
|
||||
synchronized(dataLock) {
|
||||
iobTable = LongSparseArray()
|
||||
autosensDataTable = LongSparseArray()
|
||||
basalDataTable = LongSparseArray()
|
||||
absIobTable = LongSparseArray()
|
||||
}
|
||||
}
|
||||
clearCache()
|
||||
ads.reset()
|
||||
runCalculation(reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event)
|
||||
}
|
||||
|
||||
fun createBucketedData() {
|
||||
val fiveMinData = isAbout5minData
|
||||
if (lastUsed5minCalculation != null && lastUsed5minCalculation != fiveMinData) {
|
||||
// changing mode => clear cache
|
||||
aapsLogger.debug("Invalidating cached data because of changed mode.")
|
||||
resetData("changed mode", null)
|
||||
fun clearCache() {
|
||||
synchronized(dataLock) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Clearing cached data.")
|
||||
iobTable = LongSparseArray()
|
||||
basalDataTable = LongSparseArray()
|
||||
}
|
||||
lastUsed5minCalculation = fiveMinData
|
||||
if (isAbout5minData) createBucketedData5min() else createBucketedDataRecalculated()
|
||||
}
|
||||
|
||||
fun findNewer(time: Long): GlucoseValue? {
|
||||
var lastFound = bgReadings[0]
|
||||
if (lastFound.timestamp < time) return null
|
||||
for (i in 1 until bgReadings.size) {
|
||||
if (bgReadings[i].timestamp == time) return bgReadings[i]
|
||||
if (bgReadings[i].timestamp > time) continue
|
||||
lastFound = bgReadings[i - 1]
|
||||
if (bgReadings[i].timestamp < time) break
|
||||
}
|
||||
return lastFound
|
||||
}
|
||||
|
||||
fun findOlder(time: Long): GlucoseValue? {
|
||||
var lastFound = bgReadings[bgReadings.size - 1]
|
||||
if (lastFound.timestamp > time) return null
|
||||
for (i in bgReadings.size - 2 downTo 0) {
|
||||
if (bgReadings[i].timestamp == time) return bgReadings[i]
|
||||
if (bgReadings[i].timestamp < time) continue
|
||||
lastFound = bgReadings[i + 1]
|
||||
if (bgReadings[i].timestamp > time) break
|
||||
}
|
||||
return lastFound
|
||||
}
|
||||
|
||||
private fun createBucketedDataRecalculated() {
|
||||
if (bgReadings.size < 3) {
|
||||
bucketedData = null
|
||||
return
|
||||
}
|
||||
bucketedData = ArrayList()
|
||||
var currentTime = bgReadings[0].timestamp - bgReadings[0].timestamp % T.mins(5).msecs()
|
||||
currentTime = adjustToReferenceTime(currentTime)
|
||||
aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(currentTime))
|
||||
//log.debug("First reading: " + new Date(currentTime).toLocaleString());
|
||||
while (true) {
|
||||
// test if current value is older than current time
|
||||
val newer = findNewer(currentTime)
|
||||
val older = findOlder(currentTime)
|
||||
if (newer == null || older == null) break
|
||||
if (older.timestamp == newer.timestamp) { // direct hit
|
||||
bucketedData?.add(InMemoryGlucoseValue(newer))
|
||||
} else {
|
||||
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)
|
||||
bucketedData?.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() + ")");
|
||||
}
|
||||
currentTime -= T.mins(5).msecs()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBucketedData5min() {
|
||||
if (bgReadings.size < 3) {
|
||||
bucketedData = null
|
||||
return
|
||||
}
|
||||
val bData: MutableList<InMemoryGlucoseValue> = ArrayList()
|
||||
bData.add(InMemoryGlucoseValue(bgReadings[0]))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgReadings[0].timestamp) + " lastBgTime: " + "none-first-value" + " " + bgReadings[0].toString())
|
||||
var j = 0
|
||||
for (i in 1 until bgReadings.size) {
|
||||
val bgTime = bgReadings[i].timestamp
|
||||
var lastBgTime = bgReadings[i - 1].timestamp
|
||||
//log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastBgTime).toString() + " " + bgReadings.get(i - 1).value);
|
||||
check(!(bgReadings[i].value < 39 || bgReadings[i - 1].value < 39)) { "<39" }
|
||||
var elapsedMinutes = (bgTime - lastBgTime) / (60 * 1000)
|
||||
when {
|
||||
abs(elapsedMinutes) > 8 -> {
|
||||
// interpolate missing data points
|
||||
var lastBg = bgReadings[i - 1].value
|
||||
elapsedMinutes = abs(elapsedMinutes)
|
||||
//console.error(elapsed_minutes);
|
||||
var nextBgTime: Long
|
||||
while (elapsedMinutes > 5) {
|
||||
nextBgTime = lastBgTime - 5 * 60 * 1000
|
||||
j++
|
||||
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)
|
||||
//console.error("Interpolated", bData[j]);
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
elapsedMinutes -= 5
|
||||
lastBg = nextBg
|
||||
lastBgTime = nextBgTime
|
||||
}
|
||||
j++
|
||||
val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value)
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
}
|
||||
|
||||
abs(elapsedMinutes) > 2 -> {
|
||||
j++
|
||||
val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value)
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
}
|
||||
|
||||
else -> {
|
||||
bData[j].value = (bData[j].value + bgReadings[i].value) / 2
|
||||
//log.error("***** Average");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize bucketed data
|
||||
val oldest = bData[bData.size - 1]
|
||||
oldest.timestamp = adjustToReferenceTime(oldest.timestamp)
|
||||
aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(oldest.timestamp))
|
||||
for (i in bData.size - 2 downTo 0) {
|
||||
val current = bData[i]
|
||||
val previous = bData[i + 1]
|
||||
val mSecDiff = current.timestamp - previous.timestamp
|
||||
val adjusted = (mSecDiff - T.mins(5).msecs()) / 1000
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adjusting bucketed data time. Current: " + dateUtil.dateAndTimeAndSecondsString(current.timestamp) + " to: " + dateUtil.dateAndTimeAndSecondsString(previous.timestamp + T.mins(5).msecs()) + " by " + adjusted + " sec")
|
||||
if (abs(adjusted) > 90) {
|
||||
// too big adjustment, fallback to non 5 min data
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Fallback to non 5 min data")
|
||||
createBucketedDataRecalculated()
|
||||
return
|
||||
}
|
||||
current.timestamp = previous.timestamp + T.mins(5).msecs()
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Bucketed data created. Size: " + bData.size)
|
||||
bucketedData = bData
|
||||
}
|
||||
|
||||
private fun oldestDataAvailable(): Long {
|
||||
|
@ -375,17 +167,9 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
return getBGDataFrom
|
||||
}
|
||||
|
||||
override fun calculateFromTreatmentsAndTempsSynchronized(time: Long, profile: Profile): IobTotal {
|
||||
synchronized(dataLock) { return calculateFromTreatmentsAndTemps(time, profile) }
|
||||
}
|
||||
|
||||
private fun calculateFromTreatmentsAndTempsSynchronized(time: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal {
|
||||
synchronized(dataLock) { return calculateFromTreatmentsAndTemps(time, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget) }
|
||||
}
|
||||
|
||||
fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile): IobTotal {
|
||||
override fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile): IobTotal {
|
||||
val now = System.currentTimeMillis()
|
||||
val time = roundUpTime(fromTime)
|
||||
val time = ads.roundUpTime(fromTime)
|
||||
val cacheHit = iobTable[time]
|
||||
if (time < now && cacheHit != null) {
|
||||
//og.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString());
|
||||
|
@ -414,10 +198,10 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
return iobTotal
|
||||
}
|
||||
|
||||
override fun calculateAbsInsulinFromTreatmentsAndTempsSynchronized(fromTime: Long): IobTotal {
|
||||
override fun calculateAbsInsulinFromTreatmentsAndTemps(fromTime: Long): IobTotal {
|
||||
synchronized(dataLock) {
|
||||
val now = System.currentTimeMillis()
|
||||
val time = roundUpTime(fromTime)
|
||||
val time = ads.roundUpTime(fromTime)
|
||||
val cacheHit = absIobTable[time]
|
||||
if (time < now && cacheHit != null) {
|
||||
//log.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString());
|
||||
|
@ -457,18 +241,10 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
return IobTotal.combine(bolusIob, basalIob).round()
|
||||
}
|
||||
|
||||
fun findPreviousTimeFromBucketedData(time: Long): Long? {
|
||||
val bData = bucketedData ?: return null
|
||||
for (index in bData.indices) {
|
||||
if (bData[index].timestamp <= time) return bData[index].timestamp
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getBasalData(profile: Profile, fromTime: Long): BasalData {
|
||||
synchronized(dataLock) {
|
||||
val now = System.currentTimeMillis()
|
||||
val time = roundUpTime(fromTime)
|
||||
val time = ads.roundUpTime(fromTime)
|
||||
var retVal = basalDataTable[time]
|
||||
if (retVal == null) {
|
||||
//log.debug(">>> getBasalData Cache miss " + new Date(time).toLocaleString());
|
||||
|
@ -490,19 +266,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getAutosensData(fromTime: Long): AutosensData? {
|
||||
var time = fromTime
|
||||
synchronized(dataLock) {
|
||||
val now = System.currentTimeMillis()
|
||||
if (time > now) {
|
||||
return null
|
||||
}
|
||||
val previous = findPreviousTimeFromBucketedData(time) ?: return null
|
||||
time = roundUpTime(previous)
|
||||
return autosensDataTable[time]
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData? {
|
||||
if (thread?.isAlive == true) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA is waiting for calculation thread: $reason")
|
||||
|
@ -512,13 +275,13 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA finished waiting for calculation thread: $reason")
|
||||
}
|
||||
synchronized(dataLock) { return getLastAutosensData(reason) }
|
||||
return ads.getLastAutosensData(reason, aapsLogger, dateUtil)
|
||||
}
|
||||
|
||||
override fun getCobInfo(waitForCalculationFinish: Boolean, reason: String): CobInfo {
|
||||
val autosensData =
|
||||
if (waitForCalculationFinish) getLastAutosensDataWithWaitForCalculationFinish(reason)
|
||||
else getLastAutosensData(reason)
|
||||
else ads.getLastAutosensData(reason, aapsLogger, dateUtil)
|
||||
var displayCob: Double? = null
|
||||
var futureCarbs = 0.0
|
||||
val now = dateUtil.now()
|
||||
|
@ -526,7 +289,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
if (autosensData != null) {
|
||||
displayCob = autosensData.cob
|
||||
carbs.forEach { carb ->
|
||||
if (roundUpTime(carb.timestamp) > roundUpTime(autosensData.time) && carb.timestamp <= now) {
|
||||
if (ads.roundUpTime(carb.timestamp) > ads.roundUpTime(autosensData.time) && carb.timestamp <= now) {
|
||||
displayCob += carb.amount
|
||||
}
|
||||
}
|
||||
|
@ -536,52 +299,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
return CobInfo(displayCob, futureCarbs)
|
||||
}
|
||||
|
||||
override fun slowAbsorptionPercentage(timeInMinutes: Int): Double {
|
||||
var sum = 0.0
|
||||
var count = 0
|
||||
val valuesToProcess = timeInMinutes / 5
|
||||
synchronized(dataLock) {
|
||||
var i = autosensDataTable.size() - 1
|
||||
while (i >= 0 && count < valuesToProcess) {
|
||||
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++
|
||||
count++
|
||||
i--
|
||||
}
|
||||
}
|
||||
return sum / count
|
||||
}
|
||||
|
||||
override fun getLastAutosensData(reason: String): AutosensData? {
|
||||
if (autosensDataTable.size() < 1) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: autosensDataTable empty ($reason)")
|
||||
return null
|
||||
}
|
||||
val data: AutosensData? = try {
|
||||
autosensDataTable.valueAt(autosensDataTable.size() - 1)
|
||||
} catch (e: Exception) {
|
||||
// data can be processed on the background
|
||||
// in this rare case better return null and do not block UI
|
||||
// APS plugin should use getLastAutosensDataSynchronized where the blocking is not an issue
|
||||
aapsLogger.error("AUTOSENSDATA null: Exception caught ($reason)")
|
||||
return null
|
||||
}
|
||||
if (data == null) {
|
||||
aapsLogger.error("AUTOSENSDATA null: data==null")
|
||||
return null
|
||||
}
|
||||
return if (data.time < System.currentTimeMillis() - 11 * 60 * 1000) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: data is old (" + reason + ") size()=" + autosensDataTable.size() + " lastData=" + dateUtil.dateAndTimeAndSecondsString(data.time))
|
||||
null
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA ($reason) $data")
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
override fun lastDataTime(): String =
|
||||
if (autosensDataTable.size() > 0) dateUtil.dateAndTimeAndSecondsString(autosensDataTable.valueAt(autosensDataTable.size() - 1).time)
|
||||
else "autosensDataTable empty"
|
||||
|
||||
override fun getMealDataWithWaitingForCalculationFinish(): MealData {
|
||||
val result = MealData()
|
||||
val now = System.currentTimeMillis()
|
||||
|
@ -614,12 +331,12 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
override fun calculateIobArrayInDia(profile: Profile): Array<IobTotal> {
|
||||
// predict IOB out to DIA plus 30m
|
||||
var time = System.currentTimeMillis()
|
||||
time = roundUpTime(time)
|
||||
time = ads.roundUpTime(time)
|
||||
val len = ((profile.dia * 60 + 30) / 5).toInt()
|
||||
val array = Array(len) { IobTotal(0) }
|
||||
for ((pos, i) in (0 until len).withIndex()) {
|
||||
val t = time + i * 5 * 60000
|
||||
val iob = calculateFromTreatmentsAndTempsSynchronized(t, profile)
|
||||
val iob = calculateFromTreatmentsAndTemps(t, profile)
|
||||
array[pos] = iob
|
||||
}
|
||||
return array
|
||||
|
@ -632,7 +349,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
val array = Array(len) { IobTotal(0) }
|
||||
for ((pos, i) in (0 until len).withIndex()) {
|
||||
val t = now + i * 5 * 60000
|
||||
val iob = calculateFromTreatmentsAndTempsSynchronized(t, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget)
|
||||
val iob = calculateFromTreatmentsAndTemps(t, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget)
|
||||
array[pos] = iob
|
||||
}
|
||||
return array
|
||||
|
@ -649,10 +366,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
return sb.toString()
|
||||
}
|
||||
|
||||
fun detectSensitivityWithLock(fromTime: Long, toTime: Long): AutosensResult {
|
||||
synchronized(dataLock) { return activePlugin.activeSensitivity.detectSensitivity(this, fromTime, toTime) }
|
||||
}
|
||||
|
||||
fun stopCalculation(from: String) {
|
||||
if (thread?.state != Thread.State.TERMINATED) {
|
||||
stopCalculationTrigger = true
|
||||
|
@ -700,14 +413,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
break
|
||||
}
|
||||
}
|
||||
for (index in autosensDataTable.size() - 1 downTo 0) {
|
||||
if (autosensDataTable.keyAt(index) > time) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Removing from autosensDataTable: " + dateUtil.dateAndTimeAndSecondsString(autosensDataTable.keyAt(index)))
|
||||
autosensDataTable.removeAt(index)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for (index in basalDataTable.size() - 1 downTo 0) {
|
||||
if (basalDataTable.keyAt(index) > time) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Removing from basalDataTable: " + dateUtil.dateAndTimeAndSecondsString(basalDataTable.keyAt(index)))
|
||||
|
@ -716,39 +421,12 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
break
|
||||
}
|
||||
}
|
||||
ads.newHistoryData(time, aapsLogger, dateUtil)
|
||||
}
|
||||
runCalculation("onEventNewHistoryData", System.currentTimeMillis(), bgDataReload, true, event)
|
||||
//log.debug("Releasing onNewHistoryData");
|
||||
}
|
||||
|
||||
fun clearCache() {
|
||||
synchronized(dataLock) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Clearing cached data.")
|
||||
iobTable = LongSparseArray()
|
||||
autosensDataTable = LongSparseArray()
|
||||
basalDataTable = LongSparseArray()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return last BgReading from database or null if db is empty
|
||||
*/
|
||||
override fun lastBg(): GlucoseValue? {
|
||||
val bgList = bgReadings
|
||||
for (i in bgList.indices) if (bgList[i].value >= 39) return bgList[i]
|
||||
return null
|
||||
}
|
||||
|
||||
override fun actualBg(): GlucoseValue? {
|
||||
val lastBg = lastBg() ?: return null
|
||||
return if (lastBg.timestamp > System.currentTimeMillis() - T.mins(9).msecs()) lastBg else null
|
||||
}
|
||||
|
||||
// roundup to whole minute
|
||||
fun roundUpTime(time: Long): Long {
|
||||
return if (time % 60000 == 0L) time else (time / 60000 + 1) * 60000
|
||||
}
|
||||
|
||||
override fun convertToJSONArray(iobArray: Array<IobTotal>): JSONArray {
|
||||
val array = JSONArray()
|
||||
for (i in iobArray.indices) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import info.nightscout.androidaps.database.AppRepository
|
|||
import info.nightscout.androidaps.database.ValueWrapper
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.extensions.target
|
||||
import info.nightscout.androidaps.interfaces.ActivePluginProvider
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
|
@ -21,9 +23,12 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
|||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.Profiler
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.extensions.target
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.*
|
||||
|
@ -51,6 +56,7 @@ class IobCobOref1Thread internal constructor(
|
|||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Inject lateinit var activePlugin: ActivePluginProvider
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var profiler: Profiler
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
|
@ -75,247 +81,248 @@ class IobCobOref1Thread internal constructor(
|
|||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||
synchronized(iobCobCalculatorPlugin.dataLock) {
|
||||
aapsLogger.debug("XXXXXXXXXXXXX START $from")
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
}
|
||||
val bucketedData = iobCobCalculatorPlugin.getBucketedDataTableCopy()
|
||||
val autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable()
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
aapsLogger.debug("XXXXXXXXXXXXX START $from")
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil)
|
||||
iobCobCalculatorPlugin.clearCache()
|
||||
}
|
||||
// work on local copy and set back when finished
|
||||
val ads = iobCobCalculatorPlugin.ads.clone()
|
||||
val bucketedData = ads.bucketedData
|
||||
val autosensDataTable = ads.autosensDataTable
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
return
|
||||
}
|
||||
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
aapsLogger.debug("XXXXXXXXXXXXX STOP")
|
||||
return
|
||||
}
|
||||
val prevDataTime = iobCobCalculatorPlugin.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
aapsLogger.debug("XXXXXXXXXXXXX STOP")
|
||||
return
|
||||
}
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = iobCobCalculatorPlugin.roundUpTime(bgTime)
|
||||
if (bgTime > iobCobCalculatorPlugin.roundUpTime(dateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug("XXXXXXXXXXXXX FOR $bgTime ${dateUtil.dateAndTimeString(bgTime)}")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
|
||||
//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) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
//aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
//aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
}
|
||||
val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet()
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
aapsLogger.debug("XXXXXXXXXXXXX $bgTime ${dateUtil.dateAndTimeString(bgTime)} $recentCarbTreatment")
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.amount
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
val totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.mealCarbs = previous.mealCarbs
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
autosensData.absorbing = previous.absorbing
|
||||
autosensData.mealStartCounter = previous.mealStartCounter
|
||||
autosensData.type = previous.type
|
||||
autosensData.uam = previous.uam
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.mealCarbs += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens
|
||||
if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) {
|
||||
autosensData.absorbing = deviation > 0
|
||||
// stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h
|
||||
if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) {
|
||||
autosensData.absorbing = false
|
||||
}
|
||||
if (!autosensData.absorbing && autosensData.cob < 0.5) {
|
||||
autosensData.mealCarbs = 0.0
|
||||
}
|
||||
// check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag
|
||||
if (autosensData.type != "csf") {
|
||||
// process.stderr.write("(");
|
||||
autosensData.mealStartCounter = 0
|
||||
}
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.type = "csf"
|
||||
} else {
|
||||
// check previous "type" value, and if it was csf, set a mealAbsorption end flag
|
||||
val currentBasal = profile.getBasal(bgTime)
|
||||
// always exclude the first 45m after each carb entry
|
||||
//if (iob.iob > currentBasal || uam ) {
|
||||
if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) {
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.uam = deviation > 0
|
||||
autosensData.type = "uam"
|
||||
} else {
|
||||
autosensData.type = "non-meal"
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude meal-related deviations (carb absorption) from autosens
|
||||
when (autosensData.type) {
|
||||
"non-meal" -> {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"uam" -> {
|
||||
autosensData.pastSensitivity += "u"
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "x"
|
||||
}
|
||||
}
|
||||
|
||||
// add an extra negative deviation if a high temp target is running and exercise mode is set
|
||||
// TODO AS-FIX
|
||||
@Suppress("SimplifyBooleanWithConstants")
|
||||
if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) {
|
||||
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
||||
if (tempTarget is ValueWrapper.Existing && tempTarget.value.target() >= 100) {
|
||||
autosensData.extraDeviation.add(-(tempTarget.value.target() - 100) / 20)
|
||||
}
|
||||
}
|
||||
|
||||
// add one neutral deviation every 2 hours to help decay over long exclusion periods
|
||||
val calendar = GregorianCalendar()
|
||||
calendar.timeInMillis = bgTime
|
||||
val min = calendar[Calendar.MINUTE]
|
||||
val hours = calendar[Calendar.HOUR_OF_DAY]
|
||||
if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0)
|
||||
previous = autosensData
|
||||
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime())
|
||||
val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = ads.roundUpTime(bgTime)
|
||||
if (bgTime > ads.roundUpTime(dateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug("XXXXXXXXXXXXX FOR $bgTime ${dateUtil.dateAndTimeString(bgTime)}")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
|
||||
//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) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = ads.getAutosensDataAtTime(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
//aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
//aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
}
|
||||
val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet()
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
aapsLogger.debug("XXXXXXXXXXXXX $bgTime ${dateUtil.dateAndTimeString(bgTime)} $recentCarbTreatment")
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.amount
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
val totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.mealCarbs = previous.mealCarbs
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
autosensData.absorbing = previous.absorbing
|
||||
autosensData.mealStartCounter = previous.mealStartCounter
|
||||
autosensData.type = previous.type
|
||||
autosensData.uam = previous.uam
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.mealCarbs += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens
|
||||
if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) {
|
||||
autosensData.absorbing = deviation > 0
|
||||
// stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h
|
||||
if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) {
|
||||
autosensData.absorbing = false
|
||||
}
|
||||
if (!autosensData.absorbing && autosensData.cob < 0.5) {
|
||||
autosensData.mealCarbs = 0.0
|
||||
}
|
||||
// check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag
|
||||
if (autosensData.type != "csf") {
|
||||
// process.stderr.write("(");
|
||||
autosensData.mealStartCounter = 0
|
||||
}
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.type = "csf"
|
||||
} else {
|
||||
// check previous "type" value, and if it was csf, set a mealAbsorption end flag
|
||||
val currentBasal = profile.getBasal(bgTime)
|
||||
// always exclude the first 45m after each carb entry
|
||||
//if (iob.iob > currentBasal || uam ) {
|
||||
if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) {
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.uam = deviation > 0
|
||||
autosensData.type = "uam"
|
||||
} else {
|
||||
autosensData.type = "non-meal"
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude meal-related deviations (carb absorption) from autosens
|
||||
when (autosensData.type) {
|
||||
"non-meal" -> {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"uam" -> {
|
||||
autosensData.pastSensitivity += "u"
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "x"
|
||||
}
|
||||
}
|
||||
|
||||
// add an extra negative deviation if a high temp target is running and exercise mode is set
|
||||
// TODO AS-FIX
|
||||
@Suppress("SimplifyBooleanWithConstants")
|
||||
if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) {
|
||||
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
||||
if (tempTarget is ValueWrapper.Existing && tempTarget.value.target() >= 100) {
|
||||
autosensData.extraDeviation.add(-(tempTarget.value.target() - 100) / 20)
|
||||
}
|
||||
}
|
||||
|
||||
// add one neutral deviation every 2 hours to help decay over long exclusion periods
|
||||
val calendar = GregorianCalendar()
|
||||
calendar.timeInMillis = bgTime
|
||||
val min = calendar[Calendar.MINUTE]
|
||||
val hours = calendar[Calendar.HOUR_OF_DAY]
|
||||
if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0)
|
||||
previous = autosensData
|
||||
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil))
|
||||
val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
}
|
||||
iobCobCalculatorPlugin.ads = ads
|
||||
Thread {
|
||||
SystemClock.sleep(1000)
|
||||
rxBus.send(EventAutosensCalculationFinished(cause))
|
||||
|
@ -324,7 +331,6 @@ class IobCobOref1Thread internal constructor(
|
|||
mWakeLock?.release()
|
||||
rxBus.send(EventIobCalculationProgress(""))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log())
|
||||
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import info.nightscout.androidaps.R
|
|||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.interfaces.ActivePluginProvider
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
|
@ -21,7 +22,11 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
|||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.Profiler
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
@ -50,6 +55,7 @@ class IobCobThread @Inject internal constructor(
|
|||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Inject lateinit var activePlugin: ActivePluginProvider
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var profiler: Profiler
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
|
@ -74,191 +80,192 @@ class IobCobThread @Inject internal constructor(
|
|||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||
synchronized(iobCobCalculatorPlugin.dataLock) {
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
}
|
||||
val bucketedData = iobCobCalculatorPlugin.getBucketedDataTableCopy()
|
||||
val autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable()
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil)
|
||||
iobCobCalculatorPlugin.clearCache()
|
||||
}
|
||||
// work on local copy and set back when finished
|
||||
val ads = iobCobCalculatorPlugin.ads.clone()
|
||||
val bucketedData = ads.bucketedData
|
||||
val autosensDataTable = ads.autosensDataTable
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
return
|
||||
}
|
||||
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
return
|
||||
}
|
||||
val prevDataTime = iobCobCalculatorPlugin.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
return
|
||||
}
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = iobCobCalculatorPlugin.roundUpTime(bgTime)
|
||||
if (bgTime > iobCobCalculatorPlugin.roundUpTime(dateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = ads.roundUpTime(bgTime)
|
||||
if (bgTime > ads.roundUpTime(dateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
|
||||
//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) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
//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) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
//aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = ads.getAutosensDataAtTime(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
//aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
//aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
}
|
||||
val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet()
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.amount
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
var totalMinCarbsImpact = 0.0
|
||||
if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) {
|
||||
//when the impact depends on a max time, sum them up as smaller carb sizes make them smaller
|
||||
for (ii in autosensData.activeCarbsList.indices) {
|
||||
val c = autosensData.activeCarbsList[ii]
|
||||
totalMinCarbsImpact += c.min5minCarbImpact
|
||||
}
|
||||
} else {
|
||||
//Oref sensitivity
|
||||
totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
}
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// calculate autosens only without COB
|
||||
if (autosensData.cob <= 0) {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
//aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
|
||||
val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
autosensData.pastSensitivity += "C"
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
previous = autosensData
|
||||
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime())
|
||||
val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
}
|
||||
val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet()
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.amount
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
var totalMinCarbsImpact = 0.0
|
||||
if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) {
|
||||
//when the impact depends on a max time, sum them up as smaller carb sizes make them smaller
|
||||
for (ii in autosensData.activeCarbsList.indices) {
|
||||
val c = autosensData.activeCarbsList[ii]
|
||||
totalMinCarbsImpact += c.min5minCarbImpact
|
||||
}
|
||||
} else {
|
||||
//Oref sensitivity
|
||||
totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
}
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// calculate autosens only without COB
|
||||
if (autosensData.cob <= 0) {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
autosensData.pastSensitivity += "C"
|
||||
}
|
||||
previous = autosensData
|
||||
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil))
|
||||
val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
}
|
||||
iobCobCalculatorPlugin.ads = ads
|
||||
Thread {
|
||||
SystemClock.sleep(1000)
|
||||
rxBus.send(EventAutosensCalculationFinished(cause))
|
||||
|
@ -267,7 +274,6 @@ class IobCobThread @Inject internal constructor(
|
|||
mWakeLock?.release()
|
||||
rxBus.send(EventIobCalculationProgress(""))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log())
|
||||
profiler.log(LTag.AUTOSENS, "IobCobThread", start)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ package info.nightscout.androidaps.plugins.sensitivity
|
|||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PluginBase
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.utils.Round
|
||||
import info.nightscout.androidaps.utils.SafeParse
|
||||
|
@ -24,7 +24,7 @@ abstract class AbstractSensitivityPlugin(
|
|||
val sp: SP
|
||||
) : PluginBase(pluginDescription, aapsLogger, resourceHelper, injector), SensitivityInterface {
|
||||
|
||||
abstract override fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult
|
||||
abstract override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult
|
||||
|
||||
fun fillResult(ratio: Double, carbsAbsorbed: Double, pastSensitivity: String,
|
||||
ratioLimit: String, sensResult: String, deviationsArraySize: Int): AutosensResult {
|
||||
|
|
|
@ -9,13 +9,13 @@ import info.nightscout.androidaps.database.entities.TherapyEvent
|
|||
import info.nightscout.androidaps.db.ProfileSwitch
|
||||
import info.nightscout.androidaps.extensions.isEvent5minBack
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
|
@ -48,8 +48,7 @@ open class SensitivityAAPSPlugin @Inject constructor(
|
|||
injector, aapsLogger, resourceHelper, sp
|
||||
) {
|
||||
|
||||
override fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult {
|
||||
val autosensDataTable = plugin.getAutosensDataTable()
|
||||
override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult {
|
||||
val age = sp.getString(R.string.key_age, "")
|
||||
var defaultHours = 24
|
||||
if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24
|
||||
|
@ -61,13 +60,13 @@ open class SensitivityAAPSPlugin @Inject constructor(
|
|||
aapsLogger.error("No profile")
|
||||
return AutosensResult()
|
||||
}
|
||||
if (autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||
if (ads.autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||
val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already
|
||||
if (current == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
val siteChanges = repository.getTherapyEventDataFromTime(fromTime, TherapyEvent.Type.CANNULA_CHANGE, true).blockingGet()
|
||||
|
@ -75,8 +74,8 @@ open class SensitivityAAPSPlugin @Inject constructor(
|
|||
val deviationsArray: MutableList<Double> = ArrayList()
|
||||
var pastSensitivity = ""
|
||||
var index = 0
|
||||
while (index < autosensDataTable.size()) {
|
||||
val autosensData = autosensDataTable.valueAt(index)
|
||||
while (index < ads.autosensDataTable.size()) {
|
||||
val autosensData = ads.autosensDataTable.valueAt(index)
|
||||
if (autosensData.time < fromTime) {
|
||||
index++
|
||||
continue
|
||||
|
|
|
@ -9,7 +9,6 @@ import info.nightscout.androidaps.database.entities.TherapyEvent
|
|||
import info.nightscout.androidaps.db.ProfileSwitch
|
||||
import info.nightscout.androidaps.extensions.isEvent5minBack
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
|
@ -17,6 +16,7 @@ import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityTyp
|
|||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
|
@ -51,24 +51,23 @@ open class SensitivityOref1Plugin @Inject constructor(
|
|||
injector, aapsLogger, resourceHelper, sp
|
||||
) {
|
||||
|
||||
override fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult {
|
||||
override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult {
|
||||
// todo this method is called from the IobCobCalculatorPlugin, which leads to a circular
|
||||
// dependency, this should be avoided
|
||||
val autosensDataTable = plugin.getAutosensDataTable()
|
||||
val profile = profileFunction.getProfile()
|
||||
if (profile == null) {
|
||||
aapsLogger.error("No profile")
|
||||
return AutosensResult()
|
||||
}
|
||||
if (autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||
if (ads.autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
|
||||
// the current
|
||||
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||
val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already
|
||||
if (current == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
val siteChanges = repository.getTherapyEventDataFromTime(fromTime, TherapyEvent.Type.CANNULA_CHANGE, true).blockingGet()
|
||||
|
@ -85,8 +84,8 @@ open class SensitivityOref1Plugin @Inject constructor(
|
|||
val ratioLimitArray = mutableListOf("", "")
|
||||
val hoursDetection = listOf(8.0, 24.0)
|
||||
var index = 0
|
||||
while (index < autosensDataTable.size()) {
|
||||
val autosensData = autosensDataTable.valueAt(index)
|
||||
while (index < ads.autosensDataTable.size()) {
|
||||
val autosensData = ads.autosensDataTable.valueAt(index)
|
||||
if (autosensData.time < fromTime) {
|
||||
index++
|
||||
continue
|
||||
|
|
|
@ -8,17 +8,17 @@ import info.nightscout.androidaps.data.Profile
|
|||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.TherapyEvent
|
||||
import info.nightscout.androidaps.db.ProfileSwitch
|
||||
import info.nightscout.androidaps.extensions.isEvent5minBack
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.extensions.isEvent5minBack
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import org.json.JSONException
|
||||
|
@ -47,21 +47,20 @@ open class SensitivityWeightedAveragePlugin @Inject constructor(
|
|||
injector, aapsLogger, resourceHelper, sp
|
||||
) {
|
||||
|
||||
override fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult {
|
||||
val autosensDataTable = plugin.getAutosensDataTable()
|
||||
override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult {
|
||||
val age = sp.getString(R.string.key_age, "")
|
||||
var defaultHours = 24
|
||||
if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24
|
||||
if (age == resourceHelper.gs(R.string.key_teenage)) defaultHours = 4
|
||||
if (age == resourceHelper.gs(R.string.key_child)) defaultHours = 4
|
||||
val hoursForDetection = sp.getInt(R.string.key_openapsama_autosens_period, defaultHours)
|
||||
if (autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||
if (ads.autosensDataTable.size() < 4) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||
val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already
|
||||
if (current == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil))
|
||||
return AutosensResult()
|
||||
}
|
||||
val profile = profileFunction.getProfile()
|
||||
|
@ -74,8 +73,8 @@ open class SensitivityWeightedAveragePlugin @Inject constructor(
|
|||
var pastSensitivity = ""
|
||||
var index = 0
|
||||
val data = LongSparseArray<Double>()
|
||||
while (index < autosensDataTable.size()) {
|
||||
val autosensData = autosensDataTable.valueAt(index)
|
||||
while (index < ads.autosensDataTable.size()) {
|
||||
val autosensData = ads.autosensDataTable.valueAt(index)
|
||||
if (autosensData.time < fromTime) {
|
||||
index++
|
||||
continue
|
||||
|
|
|
@ -102,7 +102,7 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
|
|||
var shouldUploadStatus = false
|
||||
if (config.NSCLIENT) return
|
||||
if (config.PUMPCONTROL) shouldUploadStatus = true
|
||||
else if (!loopPlugin.isEnabled() || iobCobCalculator.actualBg() == null)
|
||||
else if (!loopPlugin.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) {
|
||||
|
|
|
@ -110,7 +110,7 @@ class LocalAlertUtils @Inject constructor(
|
|||
}
|
||||
|
||||
fun checkStaleBGAlert() {
|
||||
val bgReading = iobCobCalculator.lastBg()
|
||||
val bgReading = iobCobCalculator.ads.lastBg()
|
||||
if (sp.getBoolean(R.string.key_enable_missed_bg_readings_alert, false)
|
||||
&& bgReading != null && bgReading.timestamp + missedReadingsThreshold() < System.currentTimeMillis() && sp.getLong("nextMissedReadingsAlarm", 0L) < System.currentTimeMillis()) {
|
||||
val n = Notification(Notification.BG_READINGS_MISSED, resourceHelper.gs(R.string.missed_bg_readings), Notification.URGENT)
|
||||
|
|
|
@ -299,7 +299,7 @@ class BolusWizard @Inject constructor(
|
|||
}
|
||||
if (insulinFromCOB > 0) {
|
||||
actions.add(resourceHelper.gs(R.string.cobvsiob) + ": " + resourceHelper.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(resourceHelper, R.color.cobAlert))
|
||||
val absorptionRate = iobCobCalculator.slowAbsorptionPercentage(60)
|
||||
val absorptionRate = iobCobCalculator.ads.slowAbsorptionPercentage(60)
|
||||
if (absorptionRate > .25)
|
||||
actions.add(resourceHelper.gs(R.string.slowabsorptiondetected, resourceHelper.gc(R.color.cobAlert), (absorptionRate * 100).toInt()))
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
|||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePasswordValidationResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
||||
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
|
||||
|
@ -59,7 +60,7 @@ import java.util.*
|
|||
ConstraintChecker::class, FabricPrivacy::class, VirtualPumpPlugin::class, XdripCalibrations::class,
|
||||
SmsManager::class, CommandQueue::class, LocalProfilePlugin::class, DateUtil::class,
|
||||
OneTimePassword::class, UserEntryLogger::class, LoopPlugin::class,
|
||||
AppRepository::class, DateUtil::class)
|
||||
AppRepository::class, DateUtil::class, AutosensDataStore::class)
|
||||
class SmsCommunicatorPluginTest : TestBaseWithProfile() {
|
||||
|
||||
@Mock lateinit var sp: SP
|
||||
|
@ -75,6 +76,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
|
|||
@Mock lateinit var uel: UserEntryLogger
|
||||
@Mock lateinit var repository: AppRepository
|
||||
@Mock lateinit var dateUtilMocked: DateUtil
|
||||
@Mock lateinit var autosensDataStore: AutosensDataStore
|
||||
|
||||
var injector: HasAndroidInjector = HasAndroidInjector {
|
||||
AndroidInjector {
|
||||
|
@ -100,7 +102,8 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
|
|||
bgList.add(reading)
|
||||
|
||||
`when`(iobCobCalculator.getCobInfo(false, "SMS COB")).thenReturn(CobInfo(10.0, 2.0))
|
||||
`when`(iobCobCalculator.lastBg()).thenReturn(reading)
|
||||
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore)
|
||||
`when`(autosensDataStore.lastBg()).thenReturn(reading)
|
||||
|
||||
PowerMockito.mockStatic(SmsManager::class.java)
|
||||
val smsManager = PowerMockito.mock(SmsManager::class.java)
|
||||
|
|
|
@ -3,10 +3,10 @@ package info.nightscout.androidaps.plugins.sensitivity
|
|||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.TestBase
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.interfaces.SensitivityInterface
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
@ -26,7 +26,7 @@ class AbstractSensitivityPluginTest : TestBase() {
|
|||
|
||||
private inner class SensitivityTestClass(pluginDescription: PluginDescription, aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, sp: SP) : AbstractSensitivityPlugin(pluginDescription, HasAndroidInjector { AndroidInjector { } }, aapsLogger, resourceHelper, sp) {
|
||||
|
||||
override fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult {
|
||||
override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult {
|
||||
return AutosensResult()
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ class AbstractSensitivityPluginTest : TestBase() {
|
|||
|
||||
override fun configuration(): JSONObject = JSONObject()
|
||||
|
||||
override fun applyConfiguration(configuration: JSONObject) { }
|
||||
override fun applyConfiguration(configuration: JSONObject) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -11,6 +11,7 @@ import info.nightscout.androidaps.interfaces.*
|
|||
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
|
@ -27,7 +28,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest
|
|||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
@RunWith(PowerMockRunner::class)
|
||||
@PrepareForTest(ConstraintChecker::class, VirtualPumpPlugin::class, DateUtil::class)
|
||||
@PrepareForTest(ConstraintChecker::class, VirtualPumpPlugin::class, DateUtil::class, AutosensDataStore::class)
|
||||
class BolusWizardTest : TestBase() {
|
||||
|
||||
private val pumpBolusStep = 0.1
|
||||
|
@ -43,6 +44,7 @@ class BolusWizardTest : TestBase() {
|
|||
@Mock lateinit var treatmentsPlugin: TreatmentsPlugin
|
||||
@Mock lateinit var virtualPumpPlugin: VirtualPumpPlugin
|
||||
@Mock lateinit var dateUtil: DateUtil
|
||||
@Mock lateinit var autosensDataStore: AutosensDataStore
|
||||
|
||||
val injector = HasAndroidInjector {
|
||||
AndroidInjector {
|
||||
|
@ -77,6 +79,7 @@ class BolusWizardTest : TestBase() {
|
|||
val pumpDescription = PumpDescription()
|
||||
pumpDescription.bolusStep = pumpBolusStep
|
||||
`when`(virtualPumpPlugin.pumpDescription).thenReturn(pumpDescription)
|
||||
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore)
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
invocation.getArgument<Constraint<Double>>(0)
|
||||
|
|
|
@ -30,7 +30,7 @@ class TriggerAutosensValue(injector: HasAndroidInjector) : Trigger(injector) {
|
|||
}
|
||||
|
||||
override fun shouldRun(): Boolean {
|
||||
val autosensData = iobCobCalculator.getLastAutosensData("Automation trigger")
|
||||
val autosensData = iobCobCalculator.ads.getLastAutosensData("Automation trigger", aapsLogger, dateUtil)
|
||||
?: return if (comparator.value == Comparator.Compare.IS_NOT_AVAILABLE) {
|
||||
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
|
||||
true
|
||||
|
|
|
@ -34,7 +34,7 @@ class TriggerIob(injector: HasAndroidInjector) : Trigger(injector) {
|
|||
|
||||
override fun shouldRun(): Boolean {
|
||||
val profile = profileFunction.getProfile() ?: return false
|
||||
val iob = iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(dateUtil.now(), profile)
|
||||
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(dateUtil.now(), profile)
|
||||
if (comparator.value.check(iob.iob, insulin.value)) {
|
||||
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
|
||||
return true
|
||||
|
|
|
@ -25,7 +25,7 @@ class TriggerAutosensValueTest : TriggerTestBase() {
|
|||
@Test fun shouldRunTest() {
|
||||
`when`(sp.getDouble(Mockito.eq(R.string.key_openapsama_autosens_max), ArgumentMatchers.anyDouble())).thenReturn(1.2)
|
||||
`when`(sp.getDouble(Mockito.eq(R.string.key_openapsama_autosens_min), ArgumentMatchers.anyDouble())).thenReturn(0.7)
|
||||
`when`(iobCobCalculator.getLastAutosensData("Automation trigger")).thenReturn(generateAutosensData())
|
||||
`when`(autosensDataStore.getLastAutosensData(anyObject(), anyObject(), anyObject())).thenReturn(generateAutosensData())
|
||||
var t = TriggerAutosensValue(injector)
|
||||
t.autosens.value = 110.0
|
||||
t.comparator.value = Comparator.Compare.IS_EQUAL
|
||||
|
@ -65,14 +65,14 @@ class TriggerAutosensValueTest : TriggerTestBase() {
|
|||
t.autosens.value = 390.0
|
||||
t.comparator.value = Comparator.Compare.IS_EQUAL_OR_LESSER
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
PowerMockito.`when`(iobCobCalculator.getLastAutosensData("Automation trigger")).thenReturn(AutosensData(injector))
|
||||
PowerMockito.`when`(autosensDataStore.getLastAutosensData(anyObject(), anyObject(), anyObject())).thenReturn(AutosensData(injector))
|
||||
t = TriggerAutosensValue(injector)
|
||||
t.autosens.value = 80.0
|
||||
t.comparator.value = Comparator.Compare.IS_EQUAL_OR_LESSER
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
|
||||
// Test autosensData == null and Comparator == IS_NOT_AVAILABLE
|
||||
PowerMockito.`when`(iobCobCalculator.getLastAutosensData("Automation trigger")).thenReturn(null)
|
||||
PowerMockito.`when`(autosensDataStore.getLastAutosensData(anyObject(), anyObject(), anyObject())).thenReturn(null)
|
||||
t = TriggerAutosensValue(injector)
|
||||
t.comparator.value = Comparator.Compare.IS_NOT_AVAILABLE
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
|
@ -109,7 +109,7 @@ class TriggerAutosensValueTest : TriggerTestBase() {
|
|||
}
|
||||
|
||||
@Test fun iconTest() {
|
||||
Assert.assertEquals(Optional.of(R.drawable.`ic_as`), TriggerAutosensValue(injector).icon())
|
||||
Assert.assertEquals(Optional.of(R.drawable.ic_as), TriggerAutosensValue(injector).icon())
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -31,7 +31,7 @@ class TriggerBgTest : TriggerTestBase() {
|
|||
|
||||
@Test
|
||||
fun shouldRunTest() {
|
||||
`when`(iobCobCalculator.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
var t: TriggerBg = TriggerBg(injector).setUnits(Constants.MMOL).setValue(4.1).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(Constants.MGDL).setValue(214.0).comparator(Comparator.Compare.IS_EQUAL)
|
||||
|
@ -50,7 +50,7 @@ class TriggerBgTest : TriggerTestBase() {
|
|||
Assert.assertTrue(t.shouldRun())
|
||||
t = TriggerBg(injector).setUnits(Constants.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
`when`(iobCobCalculator.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
t = TriggerBg(injector).setUnits(Constants.MGDL).setValue(213.0).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
t = TriggerBg(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
|
||||
|
|
|
@ -32,7 +32,7 @@ class TriggerDeltaTest : TriggerTestBase() {
|
|||
}
|
||||
|
||||
@Test fun shouldRunTest() {
|
||||
`when`(iobCobCalculator.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
var t = TriggerDelta(injector).units(Constants.MGDL).setValue(73.0, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
Assert.assertEquals(DeltaType.LONG_AVERAGE, t.delta.deltaType)
|
||||
|
@ -54,7 +54,7 @@ class TriggerDeltaTest : TriggerTestBase() {
|
|||
Assert.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).units(Constants.MGDL).setValue(-0.2, DeltaType.LONG_AVERAGE).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertTrue(t.shouldRun())
|
||||
`when`(iobCobCalculator.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(ArrayList())
|
||||
t = TriggerDelta(injector).units(Constants.MGDL).setValue(213.0, DeltaType.DELTA).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
t = TriggerDelta(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
|
||||
|
|
|
@ -27,7 +27,7 @@ class TriggerIobTest : TriggerTestBase() {
|
|||
}
|
||||
|
||||
@Test fun shouldRunTest() {
|
||||
`when`(iobCobCalculator.calculateFromTreatmentsAndTempsSynchronized(ArgumentMatchers.anyLong(), anyObject())).thenReturn(generateIobRecordData())
|
||||
`when`(iobCobCalculator.calculateFromTreatmentsAndTemps(ArgumentMatchers.anyLong(), anyObject())).thenReturn(generateIobRecordData())
|
||||
var t: TriggerIob = TriggerIob(injector).setValue(1.1).comparator(Comparator.Compare.IS_EQUAL)
|
||||
Assert.assertFalse(t.shouldRun())
|
||||
t = TriggerIob(injector).setValue(1.0).comparator(Comparator.Compare.IS_EQUAL)
|
||||
|
|
|
@ -10,6 +10,7 @@ import info.nightscout.androidaps.interfaces.IobCobCalculator
|
|||
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
||||
import info.nightscout.androidaps.receivers.ReceiverStatusStore
|
||||
import info.nightscout.androidaps.services.LastLocationDataContainer
|
||||
|
@ -19,13 +20,14 @@ import org.mockito.Mock
|
|||
import org.mockito.Mockito.`when`
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
|
||||
@PrepareForTest(LastLocationDataContainer::class, AutomationPlugin::class)
|
||||
@PrepareForTest(LastLocationDataContainer::class, AutomationPlugin::class, AutosensDataStore::class)
|
||||
open class TriggerTestBase : TestBaseWithProfile() {
|
||||
|
||||
@Mock lateinit var sp: SP
|
||||
@Mock lateinit var locationDataContainer: LastLocationDataContainer
|
||||
@Mock lateinit var activePlugin: ActivePluginProvider
|
||||
@Mock lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Mock lateinit var autosensDataStore: AutosensDataStore
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var automationPlugin: AutomationPlugin
|
||||
|
||||
|
@ -38,6 +40,7 @@ open class TriggerTestBase : TestBaseWithProfile() {
|
|||
receiverStatusStore = ReceiverStatusStore(context, rxBus)
|
||||
testPumpPlugin = TestPumpPlugin(pluginDescription, aapsLogger, resourceHelper, injector)
|
||||
`when`(activePlugin.activePump).thenReturn(testPumpPlugin)
|
||||
`when`(iobCobCalculator.ads).thenReturn(autosensDataStore)
|
||||
}
|
||||
|
||||
var injector: HasAndroidInjector = HasAndroidInjector {
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package info.nightscout.androidaps.interfaces
|
||||
|
||||
import androidx.collection.LongSparseArray
|
||||
import info.nightscout.androidaps.data.InMemoryGlucoseValue
|
||||
import info.nightscout.androidaps.data.IobTotal
|
||||
import info.nightscout.androidaps.data.MealData
|
||||
import info.nightscout.androidaps.data.Profile
|
||||
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.BasalData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
|
||||
|
@ -16,36 +14,21 @@ import org.json.JSONArray
|
|||
|
||||
interface IobCobCalculator {
|
||||
|
||||
val dataLock: Any
|
||||
|
||||
fun getBgReadingsDataTableCopy(): List<GlucoseValue>
|
||||
fun getBucketedDataTableCopy(): MutableList<InMemoryGlucoseValue>?
|
||||
fun getAutosensDataTable(): LongSparseArray<AutosensData>
|
||||
|
||||
var ads: AutosensDataStore
|
||||
|
||||
fun getMealDataWithWaitingForCalculationFinish(): MealData
|
||||
fun lastDataTime(): String
|
||||
fun getAutosensData(fromTime: Long): AutosensData?
|
||||
fun getLastAutosensData(reason: String): AutosensData?
|
||||
fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData?
|
||||
|
||||
fun calculateIobArrayInDia(profile: Profile): Array<IobTotal>
|
||||
fun calculateAbsInsulinFromTreatmentsAndTempsSynchronized(fromTime: Long): IobTotal
|
||||
fun calculateFromTreatmentsAndTempsSynchronized(time: Long, profile: Profile): IobTotal
|
||||
fun calculateAbsInsulinFromTreatmentsAndTemps(fromTime: Long): IobTotal
|
||||
fun calculateFromTreatmentsAndTemps(time: Long, profile: Profile): IobTotal
|
||||
|
||||
fun getBasalData(profile: Profile, fromTime: Long): BasalData
|
||||
|
||||
fun calculateIobArrayInDia(profile: Profile): Array<IobTotal>
|
||||
fun calculateIobArrayForSMB(lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): Array<IobTotal>
|
||||
fun iobArrayToString(array: Array<IobTotal>): String
|
||||
fun slowAbsorptionPercentage(timeInMinutes: Int): Double
|
||||
fun convertToJSONArray(iobArray: Array<IobTotal>): JSONArray
|
||||
|
||||
/**
|
||||
* Return last valid (>39) GlucoseValue from database or null if db is empty
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
*/
|
||||
fun lastBg(): GlucoseValue?
|
||||
|
||||
/**
|
||||
* Calculate CobInfo to now()
|
||||
*
|
||||
|
@ -55,13 +38,6 @@ interface IobCobCalculator {
|
|||
*/
|
||||
fun getCobInfo(waitForCalculationFinish: Boolean, reason: String): CobInfo
|
||||
|
||||
/**
|
||||
* Provide last GlucoseValue or null if none exists withing last 9 minutes
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
*/
|
||||
fun actualBg(): GlucoseValue?
|
||||
|
||||
/**
|
||||
* Calculate IobTotal from boluses and extended boluses to now().
|
||||
* NOTE: Only isValid == true boluses are included
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.interfaces
|
||||
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
|
||||
interface SensitivityInterface : ConfigExportImportInterface {
|
||||
|
@ -11,15 +12,17 @@ interface SensitivityInterface : ConfigExportImportInterface {
|
|||
SENSITIVITY_OREF1(2);
|
||||
|
||||
companion object {
|
||||
|
||||
private val map = values().associateBy(SensitivityType::value)
|
||||
fun fromInt(type: Int) = map[type]
|
||||
}
|
||||
}
|
||||
|
||||
val id: SensitivityType
|
||||
fun detectSensitivity(plugin: IobCobCalculator, fromTime: Long, toTime: Long): AutosensResult
|
||||
fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult
|
||||
|
||||
companion object {
|
||||
|
||||
const val MIN_HOURS = 1.0
|
||||
const val MIN_HOURS_FULL_AUTOSENS = 4.0
|
||||
}
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
|
||||
|
||||
import androidx.collection.LongSparseArray
|
||||
import info.nightscout.androidaps.data.InMemoryGlucoseValue
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
class AutosensDataStore {
|
||||
|
||||
private val dataLock = Any()
|
||||
private var lastUsed5minCalculation: Boolean? = null // true if used 5min bucketed data
|
||||
|
||||
// we need to make sure that bucketed_data will always have the same timestamp for correct use of cached values
|
||||
// once referenceTime != null all bucketed data should be (x * 5min) from referenceTime
|
||||
var referenceTime: Long = -1
|
||||
|
||||
var bgReadings: List<GlucoseValue> = listOf() // newest at index 0
|
||||
@Synchronized set
|
||||
@Synchronized get
|
||||
|
||||
var autosensDataTable = LongSparseArray<AutosensData>() // oldest at index 0
|
||||
@Synchronized set
|
||||
@Synchronized get
|
||||
|
||||
var bucketedData: MutableList<InMemoryGlucoseValue>? = null
|
||||
@Synchronized set
|
||||
@Synchronized get
|
||||
|
||||
fun clone(): AutosensDataStore =
|
||||
AutosensDataStore().also {
|
||||
synchronized(dataLock) {
|
||||
it.bgReadings = this.bgReadings.toMutableList()
|
||||
it.autosensDataTable = this.autosensDataTable.clone()
|
||||
it.bucketedData = this.bucketedData?.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun getBucketedDataTableCopy(): MutableList<InMemoryGlucoseValue>? = synchronized(dataLock) { bucketedData?.toMutableList() }
|
||||
fun getBgReadingsDataTableCopy(): List<GlucoseValue> = synchronized(dataLock) { bgReadings.toMutableList() }
|
||||
|
||||
fun reset() {
|
||||
synchronized(autosensDataTable) { autosensDataTable = LongSparseArray() }
|
||||
}
|
||||
|
||||
fun newHistoryData(time: Long, aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
||||
synchronized(autosensDataTable) {
|
||||
for (index in autosensDataTable.size() - 1 downTo 0) {
|
||||
if (autosensDataTable.keyAt(index) > time) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Removing from autosensDataTable: " + dateUtil.dateAndTimeAndSecondsString(autosensDataTable.keyAt(index)))
|
||||
autosensDataTable.removeAt(index)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// roundup to whole minute
|
||||
fun roundUpTime(time: Long): Long {
|
||||
return if (time % 60000 == 0L) time else (time / 60000 + 1) * 60000
|
||||
}
|
||||
|
||||
/**
|
||||
* Return last valid (>39) GlucoseValue from database or null if db is empty
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
*/
|
||||
fun lastBg(): GlucoseValue? =
|
||||
synchronized(dataLock) {
|
||||
if (bgReadings.isNotEmpty()) bgReadings[0]
|
||||
else null
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide last GlucoseValue or null if none exists withing last 9 minutes
|
||||
*
|
||||
* @return GlucoseValue or null
|
||||
*/
|
||||
fun actualBg(): GlucoseValue? {
|
||||
val lastBg = lastBg() ?: return null
|
||||
return if (lastBg.timestamp > System.currentTimeMillis() - T.mins(9).msecs()) lastBg else null
|
||||
}
|
||||
|
||||
fun lastDataTime(dateUtil: DateUtil): String =
|
||||
synchronized(dataLock) {
|
||||
if (autosensDataTable.size() > 0) dateUtil.dateAndTimeAndSecondsString(autosensDataTable.valueAt(autosensDataTable.size() - 1).time)
|
||||
else "autosensDataTable empty"
|
||||
}
|
||||
|
||||
fun findPreviousTimeFromBucketedData(time: Long): Long? {
|
||||
val bData = bucketedData ?: return null
|
||||
for (index in bData.indices) {
|
||||
if (bData[index].timestamp <= time) return bData[index].timestamp
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getAutosensDataAtTime(fromTime: Long): AutosensData? {
|
||||
synchronized(dataLock) {
|
||||
val now = System.currentTimeMillis()
|
||||
if (fromTime > now) return null
|
||||
val previous = findPreviousTimeFromBucketedData(fromTime) ?: return null
|
||||
return autosensDataTable[roundUpTime(previous)]
|
||||
}
|
||||
}
|
||||
|
||||
fun getLastAutosensData(reason: String, aapsLogger: AAPSLogger, dateUtil: DateUtil): AutosensData? {
|
||||
synchronized(dataLock) {
|
||||
if (autosensDataTable.size() < 1) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: autosensDataTable empty ($reason)")
|
||||
return null
|
||||
}
|
||||
val data: AutosensData? = try {
|
||||
autosensDataTable.valueAt(autosensDataTable.size() - 1)
|
||||
} catch (e: Exception) {
|
||||
// data can be processed on the background
|
||||
// in this rare case better return null and do not block UI
|
||||
// APS plugin should use getLastAutosensDataSynchronized where the blocking is not an issue
|
||||
aapsLogger.error("AUTOSENSDATA null: Exception caught ($reason)")
|
||||
return null
|
||||
}
|
||||
if (data == null) {
|
||||
aapsLogger.error("AUTOSENSDATA null: data==null")
|
||||
return null
|
||||
}
|
||||
return if (data.time < System.currentTimeMillis() - 11 * 60 * 1000) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: data is old (" + reason + ") size()=" + autosensDataTable.size() + " lastData=" + dateUtil.dateAndTimeAndSecondsString(data.time))
|
||||
null
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA ($reason) $data")
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun adjustToReferenceTime(someTime: Long): Long {
|
||||
if (referenceTime == -1L) {
|
||||
referenceTime = someTime
|
||||
return someTime
|
||||
}
|
||||
var diff = abs(someTime - referenceTime)
|
||||
diff %= T.mins(5).msecs()
|
||||
if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs()
|
||||
return someTime + diff
|
||||
}
|
||||
|
||||
fun 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)
|
||||
// so add 2 minutes
|
||||
bgReadings = repository
|
||||
.compatGetBgReadingsDataFromTime(start, to + T.mins(2).msecs(), false)
|
||||
.blockingGet()
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
||||
fun isAbout5minData(aapsLogger: AAPSLogger): Boolean {
|
||||
synchronized(dataLock) {
|
||||
if (bgReadings.size < 3) return true
|
||||
|
||||
var totalDiff: Long = 0
|
||||
for (i in 1 until bgReadings.size) {
|
||||
val bgTime = bgReadings[i].timestamp
|
||||
val lastBgTime = bgReadings[i - 1].timestamp
|
||||
var diff = lastBgTime - bgTime
|
||||
diff %= T.mins(5).msecs()
|
||||
if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs()
|
||||
totalDiff += diff
|
||||
diff = abs(diff)
|
||||
if (diff > T.secs(30).msecs()) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " diff: " + diff / 1000 + "[s] is5minData: " + false)
|
||||
return false
|
||||
}
|
||||
}
|
||||
val averageDiff = totalDiff / bgReadings.size / 1000
|
||||
val is5minData = averageDiff < 1
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " averageDiff: " + averageDiff + "[s] is5minData: " + is5minData)
|
||||
return is5minData
|
||||
}
|
||||
}
|
||||
|
||||
fun createBucketedData(aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
||||
val fiveMinData = isAbout5minData(aapsLogger)
|
||||
if (lastUsed5minCalculation != null && lastUsed5minCalculation != fiveMinData) {
|
||||
// changing mode => clear cache
|
||||
aapsLogger.debug("Invalidating cached data because of changed mode.")
|
||||
reset()
|
||||
}
|
||||
lastUsed5minCalculation = fiveMinData
|
||||
if (fiveMinData) createBucketedData5min(aapsLogger, dateUtil) else createBucketedDataRecalculated(aapsLogger, dateUtil)
|
||||
}
|
||||
|
||||
fun findNewer(time: Long): GlucoseValue? {
|
||||
var lastFound = bgReadings[0]
|
||||
if (lastFound.timestamp < time) return null
|
||||
for (i in 1 until bgReadings.size) {
|
||||
if (bgReadings[i].timestamp == time) return bgReadings[i]
|
||||
if (bgReadings[i].timestamp > time) continue
|
||||
lastFound = bgReadings[i - 1]
|
||||
if (bgReadings[i].timestamp < time) break
|
||||
}
|
||||
return lastFound
|
||||
}
|
||||
|
||||
fun findOlder(time: Long): GlucoseValue? {
|
||||
var lastFound = bgReadings[bgReadings.size - 1]
|
||||
if (lastFound.timestamp > time) return null
|
||||
for (i in bgReadings.size - 2 downTo 0) {
|
||||
if (bgReadings[i].timestamp == time) return bgReadings[i]
|
||||
if (bgReadings[i].timestamp < time) continue
|
||||
lastFound = bgReadings[i + 1]
|
||||
if (bgReadings[i].timestamp > time) break
|
||||
}
|
||||
return lastFound
|
||||
}
|
||||
|
||||
private fun createBucketedDataRecalculated(aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
||||
if (bgReadings.size < 3) {
|
||||
bucketedData = null
|
||||
return
|
||||
}
|
||||
val newBucketedData = ArrayList<InMemoryGlucoseValue>()
|
||||
var currentTime = bgReadings[0].timestamp - bgReadings[0].timestamp % T.mins(5).msecs()
|
||||
currentTime = adjustToReferenceTime(currentTime)
|
||||
aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(currentTime))
|
||||
//log.debug("First reading: " + new Date(currentTime).toLocaleString());
|
||||
while (true) {
|
||||
// test if current value is older than current time
|
||||
val newer = findNewer(currentTime)
|
||||
val older = findOlder(currentTime)
|
||||
if (newer == null || older == null) break
|
||||
if (older.timestamp == newer.timestamp) { // direct hit
|
||||
newBucketedData.add(InMemoryGlucoseValue(newer))
|
||||
} else {
|
||||
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)
|
||||
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() + ")");
|
||||
}
|
||||
currentTime -= T.mins(5).msecs()
|
||||
}
|
||||
bucketedData = newBucketedData
|
||||
}
|
||||
|
||||
private fun createBucketedData5min(aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
||||
if (bgReadings.size < 3) {
|
||||
bucketedData = null
|
||||
return
|
||||
}
|
||||
val bData: MutableList<InMemoryGlucoseValue> = ArrayList()
|
||||
bData.add(InMemoryGlucoseValue(bgReadings[0]))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgReadings[0].timestamp) + " lastBgTime: " + "none-first-value" + " " + bgReadings[0].toString())
|
||||
var j = 0
|
||||
for (i in 1 until bgReadings.size) {
|
||||
val bgTime = bgReadings[i].timestamp
|
||||
var lastBgTime = bgReadings[i - 1].timestamp
|
||||
//log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastBgTime).toString() + " " + bgReadings.get(i - 1).value);
|
||||
var elapsedMinutes = (bgTime - lastBgTime) / (60 * 1000)
|
||||
when {
|
||||
abs(elapsedMinutes) > 8 -> {
|
||||
// interpolate missing data points
|
||||
var lastBg = bgReadings[i - 1].value
|
||||
elapsedMinutes = abs(elapsedMinutes)
|
||||
//console.error(elapsed_minutes);
|
||||
var nextBgTime: Long
|
||||
while (elapsedMinutes > 5) {
|
||||
nextBgTime = lastBgTime - 5 * 60 * 1000
|
||||
j++
|
||||
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)
|
||||
//console.error("Interpolated", bData[j]);
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
elapsedMinutes -= 5
|
||||
lastBg = nextBg
|
||||
lastBgTime = nextBgTime
|
||||
}
|
||||
j++
|
||||
val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value)
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
}
|
||||
|
||||
abs(elapsedMinutes) > 2 -> {
|
||||
j++
|
||||
val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value)
|
||||
bData.add(newBgReading)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + dateUtil.toISOString(bgTime) + " lastBgTime: " + dateUtil.toISOString(lastBgTime) + " " + newBgReading.toString())
|
||||
}
|
||||
|
||||
else -> {
|
||||
bData[j].value = (bData[j].value + bgReadings[i].value) / 2
|
||||
//log.error("***** Average");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize bucketed data
|
||||
val oldest = bData[bData.size - 1]
|
||||
oldest.timestamp = adjustToReferenceTime(oldest.timestamp)
|
||||
aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(oldest.timestamp))
|
||||
for (i in bData.size - 2 downTo 0) {
|
||||
val current = bData[i]
|
||||
val previous = bData[i + 1]
|
||||
val mSecDiff = current.timestamp - previous.timestamp
|
||||
val adjusted = (mSecDiff - T.mins(5).msecs()) / 1000
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Adjusting bucketed data time. Current: " + dateUtil.dateAndTimeAndSecondsString(current.timestamp) + " to: " + dateUtil.dateAndTimeAndSecondsString(previous.timestamp + T.mins(5).msecs()) + " by " + adjusted + " sec")
|
||||
if (abs(adjusted) > 90) {
|
||||
// too big adjustment, fallback to non 5 min data
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Fallback to non 5 min data")
|
||||
createBucketedDataRecalculated(aapsLogger, dateUtil)
|
||||
return
|
||||
}
|
||||
current.timestamp = previous.timestamp + T.mins(5).msecs()
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Bucketed data created. Size: " + bData.size)
|
||||
bucketedData = bData
|
||||
}
|
||||
|
||||
fun slowAbsorptionPercentage(timeInMinutes: Int): Double {
|
||||
var sum = 0.0
|
||||
var count = 0
|
||||
val valuesToProcess = timeInMinutes / 5
|
||||
synchronized(dataLock) {
|
||||
var i = autosensDataTable.size() - 1
|
||||
while (i >= 0 && count < valuesToProcess) {
|
||||
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++
|
||||
count++
|
||||
i--
|
||||
}
|
||||
}
|
||||
return if (count != 0) sum / count else 0.0
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@ class GlucoseStatusProvider @Inject constructor(
|
|||
get() = getGlucoseStatusData()
|
||||
|
||||
fun getGlucoseStatusData(allowOldData: Boolean = false): GlucoseStatus? {
|
||||
val data = iobCobCalculator.getBgReadingsDataTableCopy()
|
||||
val data = iobCobCalculator.ads.getBgReadingsDataTableCopy()
|
||||
val sizeRecords = data.size
|
||||
if (sizeRecords == 0) {
|
||||
aapsLogger.debug(LTag.GLUCOSE, "sizeRecords==0")
|
||||
|
|
|
@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.iob.iobCalculator
|
|||
import info.nightscout.androidaps.TestBase
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.asRounded
|
||||
|
@ -13,6 +14,7 @@ import org.junit.Before
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.powermock.api.mockito.PowerMockito
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
@ -23,11 +25,17 @@ import java.util.*
|
|||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@RunWith(PowerMockRunner::class)
|
||||
@PrepareForTest(DateUtil::class)
|
||||
@PrepareForTest(DateUtil::class, AutosensDataStore::class)
|
||||
class GlucoseStatusTest : TestBase() {
|
||||
|
||||
@Mock lateinit var dateUtil: DateUtil
|
||||
@Mock lateinit var iobCobCalculatorPlugin: IobCobCalculator
|
||||
@Mock lateinit var autosensDataStore: AutosensDataStore
|
||||
|
||||
@Before
|
||||
fun prepare() {
|
||||
`when`(iobCobCalculatorPlugin.ads).thenReturn(autosensDataStore)
|
||||
}
|
||||
|
||||
@Test fun toStringShouldBeOverloaded() {
|
||||
val glucoseStatus = GlucoseStatus(glucose = 0.0, noise = 0.0, delta = 0.0, shortAvgDelta = 0.0, longAvgDelta = 0.0, date = 0)
|
||||
|
@ -40,7 +48,7 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun calculateValidGlucoseStatus() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateValidBgData())
|
||||
val glucoseStatus = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assert.assertEquals(214.0, glucoseStatus.glucose, 0.001)
|
||||
Assert.assertEquals(-2.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -50,7 +58,7 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun calculateMostRecentGlucoseStatus() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateMostRecentBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateMostRecentBgData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assert.assertEquals(215.0, glucoseStatus.glucose, 0.001) // (214+216) / 2
|
||||
Assert.assertEquals(-1.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -60,7 +68,7 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun oneRecordShouldProduceZeroDeltas() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOneCurrentRecordBgData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assert.assertEquals(214.0, glucoseStatus.glucose, 0.001)
|
||||
Assert.assertEquals(0.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -70,19 +78,19 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun insufficientDataShouldReturnNull() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateInsufficientBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateInsufficientBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
|
||||
Assert.assertEquals(null, glucoseStatus)
|
||||
}
|
||||
|
||||
@Test fun oldDataShouldReturnNull() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData
|
||||
Assert.assertEquals(null, glucoseStatus)
|
||||
}
|
||||
|
||||
@Test fun returnOldDataIfAllowed() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateOldBgData())
|
||||
val glucoseStatus: GlucoseStatus? = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).getGlucoseStatusData(true)
|
||||
Assert.assertNotEquals(null, glucoseStatus)
|
||||
}
|
||||
|
@ -92,7 +100,7 @@ class GlucoseStatusTest : TestBase() {
|
|||
}
|
||||
|
||||
@Test fun calculateGlucoseStatusForLibreTestBgData() {
|
||||
PowerMockito.`when`(iobCobCalculatorPlugin.getBgReadingsDataTableCopy()).thenReturn(generateLibreTestData())
|
||||
PowerMockito.`when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(generateLibreTestData())
|
||||
val glucoseStatus: GlucoseStatus = GlucoseStatusProvider(aapsLogger, iobCobCalculatorPlugin, dateUtil).glucoseStatusData!!
|
||||
Assert.assertEquals(100.0, glucoseStatus.glucose, 0.001) //
|
||||
Assert.assertEquals(-10.0, glucoseStatus.delta, 0.001)
|
||||
|
@ -103,7 +111,8 @@ class GlucoseStatusTest : TestBase() {
|
|||
|
||||
@Before
|
||||
fun initMocking() {
|
||||
PowerMockito.`when`(dateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs())
|
||||
`when`(dateUtil.now()).thenReturn(1514766900000L + T.mins(1).msecs())
|
||||
`when`(iobCobCalculatorPlugin.ads).thenReturn(autosensDataStore)
|
||||
}
|
||||
|
||||
// [{"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}]
|
||||
|
|
|
@ -1,68 +1,27 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
|
||||
|
||||
import android.content.Context
|
||||
import android.os.PowerManager
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.TestBase
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||
import info.nightscout.androidaps.interfaces.ActivePluginProvider
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
import java.util.*
|
||||
|
||||
@RunWith(PowerMockRunner::class)
|
||||
@PrepareForTest(FabricPrivacy::class, AppRepository::class)
|
||||
class IobCobCalculatorPluginTest : TestBase() {
|
||||
class AutosensDataStoreTest : TestBase() {
|
||||
|
||||
@Mock lateinit var sp: SP
|
||||
private val rxBus = RxBusWrapper(aapsSchedulers)
|
||||
@Mock lateinit var resourceHelper: ResourceHelper
|
||||
@Mock lateinit var profileFunction: ProfileFunction
|
||||
@Mock lateinit var activePlugin: ActivePluginProvider
|
||||
@Mock lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
|
||||
@Mock lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Mock lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Mock lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Mock lateinit var repository: AppRepository
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var powerManager: PowerManager
|
||||
|
||||
lateinit var dateUtil: DateUtil
|
||||
private lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
|
||||
|
||||
val injector = HasAndroidInjector {
|
||||
AndroidInjector {
|
||||
if (it is IobCobThread) {
|
||||
it.context = context
|
||||
it.resourceHelper = resourceHelper
|
||||
}
|
||||
}
|
||||
}
|
||||
private val autosensDataStore = AutosensDataStore()
|
||||
|
||||
@Before
|
||||
fun mock() {
|
||||
dateUtil = DateUtil(context)
|
||||
`when`(context.applicationContext).thenReturn(context)
|
||||
`when`(context.getSystemService(anyObject())).thenReturn(powerManager)
|
||||
iobCobCalculatorPlugin = IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction, activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -75,8 +34,8 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
|
||||
// too much shifted data should return false
|
||||
bgReadingList.clear()
|
||||
|
@ -84,16 +43,16 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(9).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
|
||||
// too much shifted and missing data should return false
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(9).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
|
||||
// too much shifted and missing data should return false
|
||||
bgReadingList.clear()
|
||||
|
@ -111,8 +70,8 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(28).plus(T.secs(0)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(23).plus(T.secs(0)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(16).plus(T.secs(36)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
|
||||
// slightly shifted data should return true
|
||||
bgReadingList.clear()
|
||||
|
@ -120,16 +79,16 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
|
||||
// slightly shifted and missing data should return true
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -142,24 +101,24 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(bgReadingList[0].timestamp, iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(bgReadingList[3].timestamp, iobCobCalculatorPlugin.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size.toLong(), iobCobCalculatorPlugin.bucketedData!!.size.toLong())
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(bgReadingList[0].timestamp, autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(bgReadingList[3].timestamp, autosensDataStore.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size.toLong(), autosensDataStore.bucketedData!!.size.toLong())
|
||||
|
||||
// Missing value should be replaced
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(bgReadingList[0].timestamp, iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(bgReadingList[2].timestamp, iobCobCalculatorPlugin.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size + 1.toLong(), iobCobCalculatorPlugin.bucketedData!!.size.toLong())
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(bgReadingList[0].timestamp, autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(bgReadingList[2].timestamp, autosensDataStore.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size + 1.toLong(), autosensDataStore.bucketedData!!.size.toLong())
|
||||
|
||||
// drift should be cleared
|
||||
bgReadingList.clear()
|
||||
|
@ -168,55 +127,55 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs() + T.secs(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs() + T.secs(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(15).msecs(), iobCobCalculatorPlugin.bucketedData!![1].timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), iobCobCalculatorPlugin.bucketedData!![2].timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), iobCobCalculatorPlugin.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size.toLong(), iobCobCalculatorPlugin.bucketedData!!.size.toLong())
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(15).msecs(), autosensDataStore.bucketedData!![1].timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.bucketedData!![2].timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.bucketedData!![3].timestamp)
|
||||
Assert.assertEquals(bgReadingList.size.toLong(), autosensDataStore.bucketedData!!.size.toLong())
|
||||
|
||||
// bucketed data should return null if not enough bg data
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(30).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.bucketedData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(null, autosensDataStore.bucketedData)
|
||||
|
||||
// data should be reconstructed
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(50).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 90.0, timestamp = T.mins(45).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 40.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(T.mins(50).msecs(), iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.bucketedData!![6].timestamp)
|
||||
Assert.assertEquals(7, iobCobCalculatorPlugin.bucketedData!!.size.toLong())
|
||||
Assert.assertEquals(100.0, iobCobCalculatorPlugin.bucketedData!![0].value, 1.0)
|
||||
Assert.assertEquals(90.0, iobCobCalculatorPlugin.bucketedData!![1].value, 1.0)
|
||||
Assert.assertEquals(50.0, iobCobCalculatorPlugin.bucketedData!![5].value, 1.0)
|
||||
Assert.assertEquals(40.0, iobCobCalculatorPlugin.bucketedData!![6].value, 1.0)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(T.mins(50).msecs(), autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![6].timestamp)
|
||||
Assert.assertEquals(7, autosensDataStore.bucketedData!!.size.toLong())
|
||||
Assert.assertEquals(100.0, autosensDataStore.bucketedData!![0].value, 1.0)
|
||||
Assert.assertEquals(90.0, autosensDataStore.bucketedData!![1].value, 1.0)
|
||||
Assert.assertEquals(50.0, autosensDataStore.bucketedData!![5].value, 1.0)
|
||||
Assert.assertEquals(40.0, autosensDataStore.bucketedData!![6].value, 1.0)
|
||||
|
||||
// non 5min data should be reconstructed
|
||||
bgReadingList.clear()
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(50).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 96.0, timestamp = T.mins(48).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 40.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(T.mins(50).msecs(), iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.bucketedData!![6].timestamp)
|
||||
Assert.assertEquals(7, iobCobCalculatorPlugin.bucketedData!!.size.toLong())
|
||||
Assert.assertEquals(100.0, iobCobCalculatorPlugin.bucketedData!![0].value, 1.0)
|
||||
Assert.assertEquals(90.0, iobCobCalculatorPlugin.bucketedData!![1].value, 1.0)
|
||||
Assert.assertEquals(50.0, iobCobCalculatorPlugin.bucketedData!![5].value, 1.0)
|
||||
Assert.assertEquals(40.0, iobCobCalculatorPlugin.bucketedData!![6].value, 1.0)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(T.mins(50).msecs(), autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![6].timestamp)
|
||||
Assert.assertEquals(7, autosensDataStore.bucketedData!!.size.toLong())
|
||||
Assert.assertEquals(100.0, autosensDataStore.bucketedData!![0].value, 1.0)
|
||||
Assert.assertEquals(90.0, autosensDataStore.bucketedData!![1].value, 1.0)
|
||||
Assert.assertEquals(50.0, autosensDataStore.bucketedData!![5].value, 1.0)
|
||||
Assert.assertEquals(40.0, autosensDataStore.bucketedData!![6].value, 1.0)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -224,9 +183,9 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
|
||||
|
||||
//bucketed data should be null if no bg data available
|
||||
iobCobCalculatorPlugin.bgReadings = ArrayList()
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.bucketedData)
|
||||
autosensDataStore.bgReadings = ArrayList()
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(null, autosensDataStore.bucketedData)
|
||||
|
||||
// real data gap test
|
||||
bgReadingList.clear()
|
||||
|
@ -258,12 +217,12 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:54:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:50:03Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:44:57Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
iobCobCalculatorPlugin.referenceTime = -1
|
||||
Assert.assertEquals(true, iobCobCalculatorPlugin.isAbout5minData)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T13:34:57Z"), iobCobCalculatorPlugin.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T03:44:57Z"), iobCobCalculatorPlugin.bucketedData!![iobCobCalculatorPlugin.bucketedData!!.size - 1].timestamp)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
autosensDataStore.referenceTime = -1
|
||||
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T13:34:57Z"), autosensDataStore.bucketedData!![0].timestamp)
|
||||
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T03:44:57Z"), autosensDataStore.bucketedData!![autosensDataStore.bucketedData!!.size - 1].timestamp)
|
||||
|
||||
// 5min 4sec data
|
||||
bgReadingList.clear()
|
||||
|
@ -288,20 +247,20 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:02:26Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T04:57:21Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T04:52:17Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, iobCobCalculatorPlugin.isAbout5minData)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bgReadingsTest() {
|
||||
val bgReadingList: List<GlucoseValue> = ArrayList()
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(bgReadingList, iobCobCalculatorPlugin.bgReadings)
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(bgReadingList, autosensDataStore.bgReadings)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roundUpTimeTest() {
|
||||
Assert.assertEquals(T.mins(3).msecs(), iobCobCalculatorPlugin.roundUpTime(T.secs(155).msecs()))
|
||||
Assert.assertEquals(T.mins(3).msecs(), autosensDataStore.roundUpTime(T.secs(155).msecs()))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -311,12 +270,12 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(T.mins(10).msecs(), iobCobCalculatorPlugin.findNewer(T.mins(8).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), iobCobCalculatorPlugin.findNewer(T.mins(5).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), iobCobCalculatorPlugin.findNewer(T.mins(10).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.findNewer(T.mins(20).msecs())!!.timestamp)
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.findNewer(T.mins(22).msecs()))
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findNewer(T.mins(8).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findNewer(T.mins(5).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findNewer(T.mins(10).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findNewer(T.mins(20).msecs())!!.timestamp)
|
||||
Assert.assertEquals(null, autosensDataStore.findNewer(T.mins(22).msecs()))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -326,20 +285,20 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
Assert.assertEquals(T.mins(5).msecs(), iobCobCalculatorPlugin.findOlder(T.mins(8).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), iobCobCalculatorPlugin.findOlder(T.mins(5).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), iobCobCalculatorPlugin.findOlder(T.mins(10).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.findOlder(T.mins(20).msecs())!!.timestamp)
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.findOlder(T.mins(4).msecs()))
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findOlder(T.mins(8).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findOlder(T.mins(5).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findOlder(T.mins(10).msecs())!!.timestamp)
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findOlder(T.mins(20).msecs())!!.timestamp)
|
||||
Assert.assertEquals(null, autosensDataStore.findOlder(T.mins(4).msecs()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun findPreviousTimeFromBucketedDataTest() {
|
||||
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.findPreviousTimeFromBucketedData(1000))
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(null, autosensDataStore.findPreviousTimeFromBucketedData(1000))
|
||||
|
||||
// Super data should not be touched
|
||||
bgReadingList.clear()
|
||||
|
@ -347,11 +306,11 @@ class IobCobCalculatorPluginTest : TestBase() {
|
|||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
|
||||
iobCobCalculatorPlugin.bgReadings = bgReadingList
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
Assert.assertEquals(null, iobCobCalculatorPlugin.findPreviousTimeFromBucketedData(T.mins(4).msecs()))
|
||||
Assert.assertEquals(T.mins(5).msecs(), iobCobCalculatorPlugin.findPreviousTimeFromBucketedData(T.mins(6).msecs()))
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.findPreviousTimeFromBucketedData(T.mins(20).msecs()))
|
||||
Assert.assertEquals(T.mins(20).msecs(), iobCobCalculatorPlugin.findPreviousTimeFromBucketedData(T.mins(25).msecs()))
|
||||
autosensDataStore.bgReadings = bgReadingList
|
||||
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
|
||||
Assert.assertEquals(null, autosensDataStore.findPreviousTimeFromBucketedData(T.mins(4).msecs()))
|
||||
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(6).msecs()))
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(20).msecs()))
|
||||
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(25).msecs()))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue