Merge branch 'dev' into smoothing

This commit is contained in:
Milos Kozak 2022-12-26 11:10:57 +01:00
commit 9c0775c826
22 changed files with 582 additions and 101 deletions

View file

@ -7,10 +7,10 @@ import info.nightscout.database.entities.TotalDailyDose
interface TddCalculator { interface TddCalculator {
fun calculate(days: Long): LongSparseArray<TotalDailyDose> fun calculate(days: Long, allowMissingDays: Boolean): LongSparseArray<TotalDailyDose>?
fun calculateToday(): TotalDailyDose fun calculateToday(): TotalDailyDose?
fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose?
fun calculate(startTime: Long, endTime: Long): TotalDailyDose fun calculate(startTime: Long, endTime: Long, allowMissingData: Boolean): TotalDailyDose?
fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): TotalDailyDose? fun averageTDD(tdds: LongSparseArray<TotalDailyDose>?): TotalDailyDose?
fun stats(context: Context): TableLayout fun stats(context: Context): TableLayout
} }

View file

@ -21,7 +21,8 @@ data class NSBolus(
override val pumpSerial: String?, override val pumpSerial: String?,
override var app: String? = null, override var app: String? = null,
val insulin: Double, val insulin: Double,
val type: BolusType val type: BolusType,
val isBasalInsulin: Boolean
) : NSTreatment { ) : NSTreatment {
enum class BolusType { enum class BolusType {

View file

@ -17,10 +17,21 @@ import info.nightscout.sdk.remotemodel.RemoteTreatment
import org.json.JSONObject import org.json.JSONObject
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/**
* Convert to [RemoteTreatment] and back to [NSTreatment]
* testing purpose only
*
* @param treatment original
*
* @return treatment after double conversion
*/
fun NSTreatment.convertToRemoteAndBack(): NSTreatment? =
toRemoteTreatment()?.toTreatment()
internal fun RemoteTreatment.toTreatment(): NSTreatment? { internal fun RemoteTreatment.toTreatment(): NSTreatment? {
val treatmentTimestamp = timestamp() val treatmentTimestamp = timestamp()
when { when {
insulin != null && insulin > 0 -> insulin != null && insulin > 0 ->
return NSBolus( return NSBolus(
date = treatmentTimestamp, date = treatmentTimestamp,
device = this.device, device = this.device,
@ -40,6 +51,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpSerial = this.pumpSerial, pumpSerial = this.pumpSerial,
insulin = this.insulin, insulin = this.insulin,
type = NSBolus.BolusType.fromString(this.type), type = NSBolus.BolusType.fromString(this.type),
isBasalInsulin = isBasalInsulin ?: false
) )
carbs != null && carbs > 0 -> carbs != null && carbs > 0 ->
@ -355,7 +367,8 @@ internal fun NSTreatment.toRemoteTreatment(): RemoteTreatment? =
pumpType = pumpType, pumpType = pumpType,
pumpSerial = pumpSerial, pumpSerial = pumpSerial,
insulin = insulin, insulin = insulin,
type = type.name type = type.name,
isBasalInsulin = isBasalInsulin
) )
is NSCarbs -> RemoteTreatment( is NSCarbs -> RemoteTreatment(

View file

@ -76,7 +76,8 @@ internal data class RemoteTreatment(
@SerializedName("rate") val rate: Double? = null, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...) @SerializedName("rate") val rate: Double? = null, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment? = null, // Gson of emulated EB @SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment? = null, // Gson of emulated EB
@SerializedName("timeshift") val timeshift: Long? = null, // integer "Profile Switch" @SerializedName("timeshift") val timeshift: Long? = null, // integer "Profile Switch"
@SerializedName("percentage") val percentage: Int? = null // integer "Profile Switch" @SerializedName("percentage") val percentage: Int? = null, // integer "Profile Switch"
@SerializedName("isBasalInsulin") val isBasalInsulin: Boolean? = null // boolean "Bolus"
) { ) {
fun timestamp(): Long { fun timestamp(): Long {

View file

@ -49,7 +49,7 @@ data class Bolus(
var insulinConfiguration: InsulinConfiguration? = null var insulinConfiguration: InsulinConfiguration? = null
) : TraceableDBEntry, DBEntryWithTime { ) : TraceableDBEntry, DBEntryWithTime {
private fun contentEqualsTo(other: Bolus): Boolean = fun contentEqualsTo(other: Bolus): Boolean =
isValid == other.isValid && isValid == other.isValid &&
timestamp == other.timestamp && timestamp == other.timestamp &&
utcOffset == other.utcOffset && utcOffset == other.utcOffset &&

View file

@ -38,7 +38,7 @@ data class Carbs(
var notes: String? = null var notes: String? = null
) : TraceableDBEntry, DBEntryWithTimeAndDuration { ) : TraceableDBEntry, DBEntryWithTimeAndDuration {
private fun contentEqualsTo(other: Carbs): Boolean = fun contentEqualsTo(other: Carbs): Boolean =
isValid == other.isValid && isValid == other.isValid &&
timestamp == other.timestamp && timestamp == other.timestamp &&
utcOffset == other.utcOffset && utcOffset == other.utcOffset &&

View file

@ -49,12 +49,12 @@ data class EffectiveProfileSwitch(
var originalTimeshift: Long, // [milliseconds] var originalTimeshift: Long, // [milliseconds]
var originalPercentage: Int, // 1 ~ XXX [%] var originalPercentage: Int, // 1 ~ XXX [%]
var originalDuration: Long, // [milliseconds] var originalDuration: Long, // [milliseconds]
var originalEnd: Long, var originalEnd: Long, // not used (calculated from duration)
@Embedded @Embedded
var insulinConfiguration: InsulinConfiguration var insulinConfiguration: InsulinConfiguration
) : TraceableDBEntry, DBEntryWithTime { ) : TraceableDBEntry, DBEntryWithTime {
private fun contentEqualsTo(other: EffectiveProfileSwitch): Boolean = fun contentEqualsTo(other: EffectiveProfileSwitch): Boolean =
isValid == other.isValid && isValid == other.isValid &&
timestamp == other.timestamp && timestamp == other.timestamp &&
utcOffset == other.utcOffset && utcOffset == other.utcOffset &&

View file

@ -52,7 +52,25 @@ data class ProfileSwitch(
var insulinConfiguration: InsulinConfiguration var insulinConfiguration: InsulinConfiguration
) : TraceableDBEntry, DBEntryWithTimeAndDuration { ) : TraceableDBEntry, DBEntryWithTimeAndDuration {
private fun contentEqualsTo(other: ProfileSwitch): Boolean = fun copy(): ProfileSwitch =
ProfileSwitch(
isValid = isValid,
timestamp = timestamp,
utcOffset = utcOffset,
basalBlocks = basalBlocks,
isfBlocks = isfBlocks,
icBlocks = icBlocks,
targetBlocks = targetBlocks,
glucoseUnit = glucoseUnit,
profileName = profileName,
timeshift = timeshift,
percentage = percentage,
duration = duration,
insulinConfiguration = insulinConfiguration,
interfaceIDs_backing = interfaceIDs_backing
)
fun contentEqualsTo(other: ProfileSwitch): Boolean =
isValid == other.isValid && isValid == other.isValid &&
timestamp == other.timestamp && timestamp == other.timestamp &&
utcOffset == other.utcOffset && utcOffset == other.utcOffset &&

View file

@ -25,4 +25,15 @@ interface TraceableDBEntry: DBEntry {
set(value) { set(value) {
interfaceIDs_backing = value interfaceIDs_backing = value
} }
fun interfaceIdsEqualsTo(other: TraceableDBEntry): Boolean =
interfaceIDs.nightscoutId == interfaceIDs.nightscoutId &&
interfaceIDs.nightscoutSystemId == interfaceIDs.nightscoutSystemId &&
interfaceIDs.pumpType == interfaceIDs.pumpType &&
interfaceIDs.pumpSerial == interfaceIDs.pumpSerial &&
interfaceIDs.temporaryId == interfaceIDs.temporaryId &&
interfaceIDs.pumpId == interfaceIDs.pumpId &&
interfaceIDs.startId == interfaceIDs.startId &&
interfaceIDs.endId == interfaceIDs.endId
} }

View file

@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.widget.TableLayout import android.widget.TableLayout
import android.widget.TableRow import android.widget.TableRow
import android.widget.TextView import android.widget.TextView
import androidx.core.util.size
import info.nightscout.database.ValueWrapper import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.Bolus import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.TotalDailyDose import info.nightscout.database.entities.TotalDailyDose
@ -37,7 +38,7 @@ class TddCalculatorImpl @Inject constructor(
private val repository: AppRepository private val repository: AppRepository
) : TddCalculator { ) : TddCalculator {
override fun calculate(days: Long): LongSparseArray<TotalDailyDose> { override fun calculate(days: Long, allowMissingDays: Boolean): LongSparseArray<TotalDailyDose>? {
var startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) var startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs())
val endTime = MidnightTime.calc(dateUtil.now()) val endTime = MidnightTime.calc(dateUtil.now())
//val stepSize = T.hours(24).msecs() // this is not true on DST change //val stepSize = T.hours(24).msecs() // this is not true on DST change
@ -55,8 +56,8 @@ class TddCalculatorImpl @Inject constructor(
if (endTime > startTime) { if (endTime > startTime) {
var midnight = startTime var midnight = startTime
while (midnight < endTime) { while (midnight < endTime) {
val tdd = calculate(midnight, midnight + T.hours(24).msecs()) val tdd = calculate(midnight, midnight + T.hours(24).msecs(), allowMissingData = false)
result.put(midnight, tdd) if (tdd != null) result.put(midnight, tdd)
midnight = MidnightTime.calc(midnight + T.hours(27).msecs()) // be sure we find correct midnight midnight = MidnightTime.calc(midnight + T.hours(27).msecs()) // be sure we find correct midnight
} }
} }
@ -68,25 +69,28 @@ class TddCalculatorImpl @Inject constructor(
repository.insertTotalDailyDose(tdd) repository.insertTotalDailyDose(tdd)
} }
} }
return result if (result.size.toLong() == days || allowMissingDays) return result
return null
} }
override fun calculateToday(): TotalDailyDose { override fun calculateToday(): TotalDailyDose? {
val startTime = MidnightTime.calc(dateUtil.now()) val startTime = MidnightTime.calc(dateUtil.now())
val endTime = dateUtil.now() val endTime = dateUtil.now()
return calculate(startTime, endTime) return calculate(startTime, endTime, allowMissingData = true)
} }
override fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose { override fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose? {
val startTime = dateUtil.now() + T.hours(hour = startHours).msecs() val startTime = dateUtil.now() + T.hours(hour = startHours).msecs()
val endTime = dateUtil.now() + T.hours(hour = endHours).msecs() val endTime = dateUtil.now() + T.hours(hour = endHours).msecs()
return calculate(startTime, endTime) return calculate(startTime, endTime, allowMissingData = false)
} }
override fun calculate(startTime: Long, endTime: Long): TotalDailyDose { override fun calculate(startTime: Long, endTime: Long, allowMissingData: Boolean): TotalDailyDose? {
val isWholeDay = (endTime - startTime) >= T.days(1).msecs()
val startTimeAligned = startTime - startTime % (5 * 60 * 1000) val startTimeAligned = startTime - startTime % (5 * 60 * 1000)
val endTimeAligned = endTime - endTime % (5 * 60 * 1000) val endTimeAligned = endTime - endTime % (5 * 60 * 1000)
val tdd = TotalDailyDose(timestamp = startTimeAligned) val tdd = TotalDailyDose(timestamp = startTimeAligned)
var tbrFound = false
repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet() repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet()
.filter { it.type != Bolus.Type.PRIMING } .filter { it.type != Bolus.Type.PRIMING }
.forEach { t -> .forEach { t ->
@ -98,8 +102,9 @@ class TddCalculatorImpl @Inject constructor(
val calculationStep = T.mins(5).msecs() val calculationStep = T.mins(5).msecs()
for (t in startTimeAligned until endTimeAligned step calculationStep) { for (t in startTimeAligned until endTimeAligned step calculationStep) {
val profile = profileFunction.getProfile(t) ?: continue val profile = profileFunction.getProfile(t) ?: if (allowMissingData) continue else return null
val tbr = iobCobCalculator.getBasalData(profile, t) val tbr = iobCobCalculator.getBasalData(profile, t)
if (tbr.isTempBasalRunning) tbrFound = true
val absoluteRate = tbr.tempBasalAbsolute val absoluteRate = tbr.tempBasalAbsolute
tdd.basalAmount += absoluteRate / 60.0 * 5.0 tdd.basalAmount += absoluteRate / 60.0 * 5.0
@ -111,11 +116,13 @@ class TddCalculatorImpl @Inject constructor(
} }
tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount
aapsLogger.debug(LTag.CORE, tdd.toString()) aapsLogger.debug(LTag.CORE, tdd.toString())
return tdd if (tdd.bolusAmount > 0 || tdd.basalAmount > 0 || tbrFound) return tdd
return null
} }
override fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): TotalDailyDose? { override fun averageTDD(tdds: LongSparseArray<TotalDailyDose>?): TotalDailyDose? {
val totalTdd = TotalDailyDose(timestamp = dateUtil.now()) val totalTdd = TotalDailyDose(timestamp = dateUtil.now())
tdds ?: return null
if (tdds.size() == 0) return null if (tdds.size() == 0) return null
for (i in 0 until tdds.size()) { for (i in 0 until tdds.size()) {
val tdd = tdds.valueAt(i) val tdd = tdds.valueAt(i)
@ -132,7 +139,7 @@ class TddCalculatorImpl @Inject constructor(
} }
override fun stats(context: Context): TableLayout { override fun stats(context: Context): TableLayout {
val tdds = calculate(7) val tdds = calculate(7, allowMissingDays = true) ?: return TableLayout(context)
val averageTdd = averageTDD(tdds) val averageTdd = averageTDD(tdds)
val todayTdd = calculateToday() val todayTdd = calculateToday()
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT) val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT)
@ -156,13 +163,15 @@ class TddCalculatorImpl @Inject constructor(
}) })
layout.addView(averageTdd.toTableRow(context, rh, tdds.size(), includeCarbs = true)) layout.addView(averageTdd.toTableRow(context, rh, tdds.size(), includeCarbs = true))
} }
layout.addView(TextView(context).apply { todayTdd?.let {
text = rh.gs(info.nightscout.shared.R.string.today) layout.addView(TextView(context).apply {
setTypeface(typeface, Typeface.BOLD) text = rh.gs(info.nightscout.shared.R.string.today)
gravity = Gravity.CENTER_HORIZONTAL setTypeface(typeface, Typeface.BOLD)
setTextAppearance(android.R.style.TextAppearance_Material_Medium) gravity = Gravity.CENTER_HORIZONTAL
}) setTextAppearance(android.R.style.TextAppearance_Material_Medium)
layout.addView(todayTdd.toTableRow(context, rh, dateUtil, includeCarbs = true)) })
layout.addView(todayTdd.toTableRow(context, rh, dateUtil, includeCarbs = true))
}
} }
} }
} }

View file

@ -577,14 +577,12 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
// for IOBpredBGs, predicted deviation impact drops linearly from current deviation down to zero // for IOBpredBGs, predicted deviation impact drops linearly from current deviation down to zero
// over 60 minutes (data points every 5m) // over 60 minutes (data points every 5m)
var predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) ); var predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) );
//IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev; if (!TDD) IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev;
IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log((Math.max( IOBpredBGs[IOBpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) else IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log((Math.max( IOBpredBGs[IOBpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + predDev;
* 5 ),2)) + predDev;
// calculate predBGs with long zero temp without deviations // calculate predBGs with long zero temp without deviations
//var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI; if (!TDD) var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI;
var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + (round(( -iobTick.iobWithZeroTemp.activity * (1800 / ( TDD * (Math.log(( Math.max(ZTpredBGs[ZTpredBGs.length-1],39) / else var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + (round(( -iobTick.iobWithZeroTemp.activity * (1800 / ( TDD * (Math.log(( Math.max(ZTpredBGs[ZTpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ), 2));
insulinDivisor ) + 1 ) ) )) * 5 ), 2));
// for COBpredBGs, predicted carb impact drops linearly from current carb impact down to zero // for COBpredBGs, predicted carb impact drops linearly from current carb impact down to zero
// eventually accounting for all carbs (if they can be absorbed over DIA) // eventually accounting for all carbs (if they can be absorbed over DIA)
var predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); var predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) );
@ -613,9 +611,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
//console.error(UAMpredBGs.length,slopeFromDeviations, predUCI); //console.error(UAMpredBGs.length,slopeFromDeviations, predUCI);
UAMduration=round((UAMpredBGs.length+1)*5/60,1); UAMduration=round((UAMpredBGs.length+1)*5/60,1);
} }
//UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI; if (!TDD) UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI;
UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD else UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log(( Math.max(UAMpredBGs[UAMpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + Math.min(0, predDev) + predUCI;
* (Math.log(( Math.max(UAMpredBGs[UAMpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + Math.min(0, predDev) + predUCI;
//console.error(predBGI, predCI, predUCI); //console.error(predBGI, predCI, predUCI);
// truncate all BG predictions at 4 hours // truncate all BG predictions at 4 hours
if ( IOBpredBGs.length < 48) { IOBpredBGs.push(IOBpredBG); } if ( IOBpredBGs.length < 48) { IOBpredBGs.push(IOBpredBG); }
@ -727,27 +724,29 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
var fSensBG = Math.min(minPredBG,bg); var fSensBG = Math.min(minPredBG,bg);
if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) { if (TDD) {
var future_sens = ( 1800 / (Math.log((((fSensBG * 0.5) + (bg * 0.5))/insulinDivisor)+1)*TDD)); if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) {
//var future_sens_old = ( 277700 / (TDD * ((bg * 0.5) + (eventualBG * 0.5 )))); var future_sens = ( 1800 / (Math.log((((fSensBG * 0.5) + (bg * 0.5))/insulinDivisor)+1)*TDD));
console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target"); //var future_sens_old = ( 277700 / (TDD * ((bg * 0.5) + (eventualBG * 0.5 ))));
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;"; console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target");
} rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
else if( glucose_status.delta > 0 && eventualBG > target_bg || eventualBG > bg) {
var future_sens = ( 1800 / (Math.log((bg/insulinDivisor)+1)*TDD));
//var future_sens_old = ( 277700 / (TDD * bg));
console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation");
rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
} }
else { else if( glucose_status.delta > 0 && eventualBG > target_bg || eventualBG > bg) {
var future_sens = ( 1800 / (Math.log((fSensBG/insulinDivisor)+1)*TDD)); var future_sens = ( 1800 / (Math.log((bg/insulinDivisor)+1)*TDD));
//var future_sens_old = ( 277700 / (TDD * eventualBG)); //var future_sens_old = ( 277700 / (TDD * bg));
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta"); console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation");
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;"; rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
} }
future_sens = round(future_sens,1);
else {
var future_sens = ( 1800 / (Math.log((fSensBG/insulinDivisor)+1)*TDD));
//var future_sens_old = ( 277700 / (TDD * eventualBG));
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta");
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
}
future_sens = round(future_sens,1);
} else future_sens = variable_sens
var fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs; var fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs;

View file

@ -262,23 +262,11 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
this.mealData.put("lastBolusTime", mealData.lastBolusTime) this.mealData.put("lastBolusTime", mealData.lastBolusTime)
this.mealData.put("lastCarbTime", mealData.lastCarbTime) this.mealData.put("lastCarbTime", mealData.lastCarbTime)
val tdd1D = tddCalculator.averageTDD(tddCalculator.calculate(1))?.totalAmount val tdd1D = tddCalculator.averageTDD(tddCalculator.calculate(1, allowMissingDays = false))?.totalAmount
val tdd7D = tddCalculator.averageTDD(tddCalculator.calculate(7))?.totalAmount val tdd7D = tddCalculator.averageTDD(tddCalculator.calculate(7, allowMissingDays = false))?.totalAmount
val tddLast24H = tddCalculator.calculateDaily(-24, 0).totalAmount val tddLast24H = tddCalculator.calculateDaily(-24, 0)?.totalAmount
val tddLast4H = tddCalculator.calculateDaily(-4, 0).totalAmount val tddLast4H = tddCalculator.calculateDaily(-4, 0)?.totalAmount
val tddLast8to4H = tddCalculator.calculateDaily(-8, -4).totalAmount val tddLast8to4H = tddCalculator.calculateDaily(-8, -4)?.totalAmount
val tddWeightedFromLast8H = ((1.4 * tddLast4H) + (0.6 * tddLast8to4H)) * 3
// console.error("Rolling 8 hours weight average: " + tdd_last8_wt + "; ");
// console.error("1-day average TDD is: " + tdd1 + "; ");
// console.error("7-day average TDD is: " + tdd7 + "; ");
var tdd =
if (tdd1D != null && tdd7D != null) (tddWeightedFromLast8H * 0.33) + (tdd7D * 0.34) + (tdd1D * 0.33)
else tddWeightedFromLast8H
// console.log("TDD = " + TDD + " using average of 7-day, 1-day and weighted 8hr average");
// console.log("Insulin Peak = " + insulin.peak + "; ");
val insulin = activePlugin.activeInsulin val insulin = activePlugin.activeInsulin
val insulinDivisor = when { val insulinDivisor = when {
@ -286,25 +274,35 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
insulin.peak > 50 -> 65 // ultra rapid peak: 55 insulin.peak > 50 -> 65 // ultra rapid peak: 55
else -> 75 // rapid peak: 75 else -> 75 // rapid peak: 75
} }
// console.log("For " + insulin.friendlyName + " (insulin peak: " + insulin.peak + ") insulin divisor is: " + ins_val + "; ");
val dynISFadjust = SafeParse.stringToDouble(sp.getString(R.string.key_DynISFAdjust, "100")) / 100.0 var tdd: Double? = null
tdd *= dynISFadjust var variableSensitivity: Double
if (tddLast24H != null && tddLast4H != null && tddLast8to4H != null) {
val tddWeightedFromLast8H = ((1.4 * tddLast4H) + (0.6 * tddLast8to4H)) * 3
// console.error("Rolling 8 hours weight average: " + tdd_last8_wt + "; ");
// console.error("1-day average TDD is: " + tdd1 + "; ");
// console.error("7-day average TDD is: " + tdd7 + "; ");
var variableSensitivity = 1800 / (tdd * (ln((glucoseStatus.glucose / insulinDivisor) + 1))) tdd =
variableSensitivity = Round.roundTo(variableSensitivity, 0.1) if (tdd1D != null && tdd7D != null) (tddWeightedFromLast8H * 0.33) + (tdd7D * 0.34) + (tdd1D * 0.33)
else tddWeightedFromLast8H
// console.log("TDD = " + TDD + " using average of 7-day, 1-day and weighted 8hr average");
if (dynISFadjust != 1.0) { val dynISFadjust = SafeParse.stringToDouble(sp.getString(R.string.key_DynISFAdjust, "100")) / 100.0
// console.log("TDD adjusted to " + TDD + " using adjustment factor of " + dynISFadjust + "; "); tdd *= dynISFadjust
variableSensitivity = 1800 / (tdd * (ln((glucoseStatus.glucose / insulinDivisor) + 1)))
variableSensitivity = Round.roundTo(variableSensitivity, 0.1)
} else {
variableSensitivity = profile.getIsfMgdl()
} }
// console.log("Current sensitivity for predictions is " + variable_sens + " based on current bg");
this.profile.put("variable_sens", variableSensitivity) this.profile.put("variable_sens", variableSensitivity)
this.profile.put("insulinDivisor", insulinDivisor) this.profile.put("insulinDivisor", insulinDivisor)
this.profile.put("TDD", tdd) tdd?.let { this.profile.put("TDD", tdd) }
if (sp.getBoolean(R.string.key_adjust_sensitivity, false) && tdd7D != null && tdd7D != 0.0) if (sp.getBoolean(R.string.key_adjust_sensitivity, false) && tdd7D != null && tddLast24H != null)
autosensData.put("ratio", tddLast24H / tdd7D) autosensData.put("ratio", tddLast24H / tdd7D)
else else
autosensData.put("ratio", 1.0) autosensData.put("ratio", 1.0)

View file

@ -134,11 +134,10 @@ class StatusLightHandler @Inject constructor(
private fun handleUsage(view: TextView?, units: String) { private fun handleUsage(view: TextView?, units: String) {
handler.post { handler.post {
val therapyEvent = repository.getLastTherapyRecordUpToNow(TherapyEvent.Type.CANNULA_CHANGE).blockingGet() val therapyEvent = repository.getLastTherapyRecordUpToNow(TherapyEvent.Type.CANNULA_CHANGE).blockingGet()
var usage = 0.0 var usage =
if (therapyEvent is ValueWrapper.Existing) { if (therapyEvent is ValueWrapper.Existing) {
val tdd = tddCalculator.calculate(therapyEvent.value.timestamp, dateUtil.now()) tddCalculator.calculate(therapyEvent.value.timestamp, dateUtil.now(), allowMissingData = false)?.totalAmount ?: 0.0
usage = tdd.totalAmount } else 0.0
}
runOnUiThread { runOnUiThread {
view?.text = DecimalFormatter.to0Decimal(usage, units) view?.text = DecimalFormatter.to0Decimal(usage, units)
} }

View file

@ -26,6 +26,7 @@ dependencies {
implementation project(':core:utils') implementation project(':core:utils')
implementation project(':core:validators') implementation project(':core:validators')
testImplementation project(':implementation')
// NSClient, Tidepool // NSClient, Tidepool
api("io.socket:socket.io-client:1.0.2") { api("io.socket:socket.io-client:1.0.2") {

View file

@ -611,6 +611,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
offlineEvents.clear()
result.inserted.forEach { oe -> result.inserted.forEach { oe ->
if (config.NSCLIENT.not()) userEntries.add( if (config.NSCLIENT.not()) userEntries.add(
UserEntry( UserEntry(
@ -679,6 +680,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
extendedBoluses.clear()
result.inserted.forEach { result.inserted.forEach {
if (config.NSCLIENT.not()) userEntries.add( if (config.NSCLIENT.not()) userEntries.add(
UserEntry( UserEntry(
@ -775,6 +777,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdTemporaryTargets.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryTarget $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryTarget $it")
nsIdUpdated.inc(TemporaryTarget::class.java.simpleName) nsIdUpdated.inc(TemporaryTarget::class.java.simpleName)
@ -787,6 +790,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdGlucoseValues.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of GlucoseValue $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of GlucoseValue $it")
nsIdUpdated.inc(GlucoseValue::class.java.simpleName) nsIdUpdated.inc(GlucoseValue::class.java.simpleName)
@ -799,6 +803,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdFoods.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Food $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of Food $it")
nsIdUpdated.inc(Food::class.java.simpleName) nsIdUpdated.inc(Food::class.java.simpleName)
@ -811,6 +816,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdTherapyEvents.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TherapyEvent $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of TherapyEvent $it")
nsIdUpdated.inc(TherapyEvent::class.java.simpleName) nsIdUpdated.inc(TherapyEvent::class.java.simpleName)
@ -823,6 +829,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdBoluses.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Bolus $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of Bolus $it")
nsIdUpdated.inc(Bolus::class.java.simpleName) nsIdUpdated.inc(Bolus::class.java.simpleName)
@ -835,6 +842,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdCarbs.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Carbs $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of Carbs $it")
nsIdUpdated.inc(Carbs::class.java.simpleName) nsIdUpdated.inc(Carbs::class.java.simpleName)
@ -847,6 +855,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdBolusCalculatorResults.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of BolusCalculatorResult $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of BolusCalculatorResult $it")
nsIdUpdated.inc(BolusCalculatorResult::class.java.simpleName) nsIdUpdated.inc(BolusCalculatorResult::class.java.simpleName)
@ -859,6 +868,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdTemporaryBasals.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryBasal $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryBasal $it")
nsIdUpdated.inc(TemporaryBasal::class.java.simpleName) nsIdUpdated.inc(TemporaryBasal::class.java.simpleName)
@ -871,6 +881,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdExtendedBoluses.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ExtendedBolus $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of ExtendedBolus $it")
nsIdUpdated.inc(ExtendedBolus::class.java.simpleName) nsIdUpdated.inc(ExtendedBolus::class.java.simpleName)
@ -883,6 +894,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdProfileSwitches.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ProfileSwitch $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of ProfileSwitch $it")
nsIdUpdated.inc(ProfileSwitch::class.java.simpleName) nsIdUpdated.inc(ProfileSwitch::class.java.simpleName)
@ -895,6 +907,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdEffectiveProfileSwitches.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of EffectiveProfileSwitch $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of EffectiveProfileSwitch $it")
nsIdUpdated.inc(EffectiveProfileSwitch::class.java.simpleName) nsIdUpdated.inc(EffectiveProfileSwitch::class.java.simpleName)
@ -907,6 +920,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdDeviceStatuses.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of DeviceStatus $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of DeviceStatus $it")
nsIdUpdated.inc(DeviceStatus::class.java.simpleName) nsIdUpdated.inc(DeviceStatus::class.java.simpleName)
@ -919,6 +933,7 @@ class StoreDataForDbImpl @Inject constructor(
} }
.blockingGet() .blockingGet()
.also { result -> .also { result ->
nsIdOfflineEvents.clear()
result.updatedNsId.forEach { result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId of OfflineEvent $it") aapsLogger.debug(LTag.DATABASE, "Updated nsId of OfflineEvent $it")
nsIdUpdated.inc(OfflineEvent::class.java.simpleName) nsIdUpdated.inc(OfflineEvent::class.java.simpleName)

View file

@ -14,6 +14,7 @@ fun NSBolus.toBolus(): Bolus =
amount = insulin, amount = insulin,
type = type.toBolusType(), type = type.toBolusType(),
notes = notes, notes = notes,
isBasalInsulin = isBasalInsulin,
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId) interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
) )
@ -34,6 +35,7 @@ fun Bolus.toNSBolus(): NSBolus =
insulin = amount, insulin = amount,
type = type.toBolusType(), type = type.toBolusType(),
notes = notes, notes = notes,
isBasalInsulin = isBasalInsulin,
identifier = interfaceIDs.nightscoutId, identifier = interfaceIDs.nightscoutId,
pumpId = interfaceIDs.pumpId, pumpId = interfaceIDs.pumpId,
pumpType = interfaceIDs.pumpType?.name, pumpType = interfaceIDs.pumpType?.name,

View file

@ -41,23 +41,22 @@ fun NSProfileSwitch.toProfileSwitch(activePlugin: ActivePlugin, dateUtil: DateUt
fun ProfileSwitch.toNSProfileSwitch(dateUtil: DateUtil): NSProfileSwitch { fun ProfileSwitch.toNSProfileSwitch(dateUtil: DateUtil): NSProfileSwitch {
val unmodifiedCustomizedName = getCustomizedName() val unmodifiedCustomizedName = getCustomizedName()
// ProfileSealed.PS doesn't provide unmodified json -> reset it // ProfileSealed.PS doesn't provide unmodified json -> reset it
val unmodifiedTimeshift = timeshift val notCustomized = this.copy()
val unmodifiedPercentage = percentage notCustomized.timeshift = 0
timeshift = 0 notCustomized.percentage = 100
percentage = 100
return NSProfileSwitch( return NSProfileSwitch(
eventType = EventType.fromString(TherapyEvent.Type.PROFILE_SWITCH.text), eventType = EventType.fromString(TherapyEvent.Type.PROFILE_SWITCH.text),
isValid = isValid, isValid = isValid,
date = timestamp, date = timestamp,
utcOffset = utcOffset, utcOffset = utcOffset,
timeShift = unmodifiedTimeshift, timeShift = timeshift,
percentage = unmodifiedPercentage, percentage = percentage,
duration = T.mins(duration).msecs(), duration = T.mins(duration).msecs(),
profile = unmodifiedCustomizedName, profile = unmodifiedCustomizedName,
originalProfileName = profileName, originalProfileName = profileName,
originalDuration = duration, originalDuration = duration,
profileJson = ProfileSealed.PS(this).toPureNsJson(dateUtil), profileJson = ProfileSealed.PS(notCustomized).toPureNsJson(dateUtil),
identifier = interfaceIDs.nightscoutId, identifier = interfaceIDs.nightscoutId,
pumpId = interfaceIDs.pumpId, pumpId = interfaceIDs.pumpId,
pumpType = interfaceIDs.pumpType?.name, pumpType = interfaceIDs.pumpType?.name,

View file

@ -0,0 +1,193 @@
package info.nightscout.androidaps
import android.content.Context
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.embedments.InsulinConfiguration
import info.nightscout.database.impl.AppRepository
import info.nightscout.implementation.profile.ProfileFunctionImpl
import info.nightscout.implementation.profile.ProfileStoreObject
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.insulin.Insulin
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject
import org.junit.jupiter.api.BeforeEach
import org.mockito.ArgumentMatchers.anyDouble
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.invocation.InvocationOnMock
@Suppress("SpellCheckingInspection")
open class TestBaseWithProfile : TestBase() {
@Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var rh: ResourceHelper
@Mock lateinit var iobCobCalculator: IobCobCalculator
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var config: Config
@Mock lateinit var context: Context
@Mock lateinit var sp: SP
@Mock lateinit var repository: AppRepository
@Mock lateinit var hardLimits: HardLimits
@Mock lateinit var processedDeviceStatusData: ProcessedDeviceStatusData
@Mock lateinit var insulin: Insulin
lateinit var profileFunction: ProfileFunction
lateinit var dateUtil: DateUtil
var insulinConfiguration: InsulinConfiguration = InsulinConfiguration("Insulin", 360 * 60 * 1000, 60 * 60 * 1000)
val rxBus = RxBus(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector { AndroidInjector { } }
private lateinit var validProfileJSON: String
lateinit var validProfile: ProfileSealed.Pure
lateinit var effectiveProfileSwitch: EffectiveProfileSwitch
@Suppress("PropertyName") val TESTPROFILENAME = "someProfile"
@BeforeEach
fun prepareMock() {
validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
dateUtil = Mockito.spy(DateUtil(context))
`when`(dateUtil.now()).thenReturn(1656358822000)
`when`(insulin.insulinConfiguration).thenReturn(insulinConfiguration)
`when`(activePlugin.activeInsulin).thenReturn(insulin)
profileFunction = ProfileFunctionImpl(aapsLogger, sp, rxBus, rh, activePlugin, repository, dateUtil, config, hardLimits, aapsSchedulers, fabricPrivacy, processedDeviceStatusData)
validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!)
effectiveProfileSwitch = EffectiveProfileSwitch(
timestamp = dateUtil.now(),
basalBlocks = validProfile.basalBlocks,
isfBlocks = validProfile.isfBlocks,
icBlocks = validProfile.icBlocks,
targetBlocks = validProfile.targetBlocks,
glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.MMOL,
originalProfileName = "",
originalCustomizedName = "",
originalTimeshift = 0,
originalPercentage = 100,
originalDuration = 0,
originalEnd = 0,
insulinConfiguration = InsulinConfiguration("", 0, 0)
)
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyDouble())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
String.format(rh.gs(string), arg1)
}.`when`(rh).gs(anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyString(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
String.format(rh.gs(string), arg1, arg2)
}.`when`(rh).gs(anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Int?>(1)
val arg2 = invocation.getArgument<String?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyInt(), anyString(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<Double?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt(), anyString())
Mockito.doAnswer { invocation: InvocationOnMock ->
val string = invocation.getArgument<Int>(0)
val arg1 = invocation.getArgument<String?>(1)
val arg2 = invocation.getArgument<Int?>(2)
val arg3 = invocation.getArgument<String?>(3)
String.format(rh.gs(string), arg1, arg2, arg3)
}.`when`(rh).gs(anyInt(), anyString(), anyInt(), anyString())
}
fun getValidProfileStore(): ProfileStore {
val json = JSONObject()
val store = JSONObject()
store.put(TESTPROFILENAME, JSONObject(validProfileJSON))
json.put("defaultProfile", TESTPROFILENAME)
json.put("store", store)
return ProfileStoreObject(profileInjector, json, dateUtil)
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.sdk.localmodel.treatment.NSBolus
import info.nightscout.sdk.mapper.convertToRemoteAndBack
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
@Suppress("SpellCheckingInspection")
internal class BolusExtensionKtTest {
@Test
fun toBolus() {
var bolus = Bolus(
timestamp = 10000,
isValid = true,
amount = 1.0,
type = Bolus.Type.SMB,
notes = "aaaa",
isBasalInsulin = false,
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
var bolus2 = (bolus.toNSBolus().convertToRemoteAndBack() as NSBolus).toBolus()
Assertions.assertTrue(bolus.contentEqualsTo(bolus2))
Assertions.assertTrue(bolus.interfaceIdsEqualsTo(bolus2))
bolus = Bolus(
timestamp = 10000,
isValid = false,
amount = 1.0,
type = Bolus.Type.NORMAL,
notes = "aaaa",
isBasalInsulin = true,
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
bolus2 = (bolus.toNSBolus().convertToRemoteAndBack() as NSBolus).toBolus()
Assertions.assertTrue(bolus.contentEqualsTo(bolus2))
Assertions.assertTrue(bolus.interfaceIdsEqualsTo(bolus2))
}
}

View file

@ -0,0 +1,51 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.sdk.localmodel.treatment.NSCarbs
import info.nightscout.sdk.mapper.convertToRemoteAndBack
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
@Suppress("SpellCheckingInspection")
internal class CarbsExtensionKtTest {
@Test
fun toCarbs() {
var carbs = Carbs(
timestamp = 10000,
isValid = true,
amount = 1.0,
duration = 0,
notes = "aaaa",
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
var carbs2 = (carbs.toNSCarbs().convertToRemoteAndBack() as NSCarbs).toCarbs()
Assertions.assertTrue(carbs.contentEqualsTo(carbs2))
Assertions.assertTrue(carbs.interfaceIdsEqualsTo(carbs2))
carbs = Carbs(
timestamp = 10000,
isValid = false,
amount = 1.0,
duration = 60000,
notes = null,
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
carbs2 = (carbs.toNSCarbs().convertToRemoteAndBack() as NSCarbs).toCarbs()
Assertions.assertTrue(carbs.contentEqualsTo(carbs2))
Assertions.assertTrue(carbs.interfaceIdsEqualsTo(carbs2))
}
}

View file

@ -0,0 +1,46 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.plugins.sync.nsclient.extensions.fromConstant
import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch
import info.nightscout.sdk.mapper.convertToRemoteAndBack
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
@Suppress("SpellCheckingInspection")
internal class EffectiveProfileSwitchExtensionKtTest : TestBaseWithProfile() {
@Test
fun toEffectiveProfileSwitch() {
val profileSwitch = EffectiveProfileSwitch(
timestamp = 10000,
isValid = true,
basalBlocks = validProfile.basalBlocks,
isfBlocks = validProfile.isfBlocks,
icBlocks = validProfile.icBlocks,
targetBlocks = validProfile.targetBlocks,
glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
originalProfileName = "SomeProfile",
originalCustomizedName = "SomeProfile (150%, 1h)",
originalTimeshift = 3600000,
originalPercentage = 150,
originalDuration = 3600000,
originalEnd = 0,
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
},
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
val profileSwitch2 = (profileSwitch.toNSEffectiveProfileSwitch(dateUtil).convertToRemoteAndBack() as NSEffectiveProfileSwitch).toEffectiveProfileSwitch(dateUtil)!!
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
}
}

View file

@ -0,0 +1,71 @@
package info.nightscout.plugins.sync.nsclientV3.extensions
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.extensions.fromConstant
import info.nightscout.database.entities.ProfileSwitch
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch
import info.nightscout.sdk.mapper.convertToRemoteAndBack
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
@Suppress("SpellCheckingInspection")
internal class ProfileSwitchExtensionKtTest : TestBaseWithProfile() {
@Test
fun toProfileSwitch() {
var profileSwitch = ProfileSwitch(
timestamp = 10000,
isValid = true,
basalBlocks = validProfile.basalBlocks,
isfBlocks = validProfile.isfBlocks,
icBlocks = validProfile.icBlocks,
targetBlocks = validProfile.targetBlocks,
glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
profileName = "SomeProfile",
timeshift = 0,
percentage = 100,
duration = 0,
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
},
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
var profileSwitch2 = (profileSwitch.toNSProfileSwitch(dateUtil).convertToRemoteAndBack() as NSProfileSwitch).toProfileSwitch(activePlugin, dateUtil)!!
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
profileSwitch = ProfileSwitch(
timestamp = 10000,
isValid = true,
basalBlocks = validProfile.basalBlocks,
isfBlocks = validProfile.isfBlocks,
icBlocks = validProfile.icBlocks,
targetBlocks = validProfile.targetBlocks,
glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
profileName = "SomeProfile",
timeshift = -3600000,
percentage = 150,
duration = 3600000,
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
},
interfaceIDs_backing = InterfaceIDs(
nightscoutId = "nightscoutId",
pumpId = 11000,
pumpType = InterfaceIDs.PumpType.DANA_I,
pumpSerial = "bbbb"
)
)
profileSwitch2 = (profileSwitch.toNSProfileSwitch(dateUtil).convertToRemoteAndBack() as NSProfileSwitch).toProfileSwitch(activePlugin, dateUtil)!!
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
}
}