pull upstream/dev
This commit is contained in:
commit
f0e7134d29
|
@ -153,6 +153,7 @@ android {
|
||||||
//Deleting it causes a binding error
|
//Deleting it causes a binding error
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
dataBinding = true
|
dataBinding = true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
|
||||||
})
|
})
|
||||||
// Setup views on 2nd and next activity start
|
// Setup views on 2nd and next activity start
|
||||||
// On 1st start app is still initializing, start() is delayed and run from EventAppInitialized
|
// On 1st start app is still initializing, start() is delayed and run from EventAppInitialized
|
||||||
if (config.appInitialized) start()
|
if (config.appInitialized) setupViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:8.1.4")
|
classpath("com.android.tools.build:gradle:8.2.0")
|
||||||
classpath("com.google.gms:google-services:4.4.0")
|
classpath("com.google.gms:google-services:4.4.0")
|
||||||
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
|
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
|
id("org.jlleitschuh.gradle.ktlint") version "12.0.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
object KtsBuildVersions {
|
object KtsBuildVersions {
|
||||||
|
|
||||||
const val gradle = "8.1.3"
|
const val gradle = "8.2.0"
|
||||||
const val kotlin = "1.9.0"
|
const val kotlin = "1.9.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package app.aaps.core.interfaces.pump
|
package app.aaps.core.interfaces.pump
|
||||||
|
|
||||||
|
import app.aaps.core.interfaces.db.GlucoseUnit
|
||||||
import app.aaps.core.interfaces.profile.Profile
|
import app.aaps.core.interfaces.profile.Profile
|
||||||
import app.aaps.core.interfaces.pump.defs.PumpType
|
import app.aaps.core.interfaces.pump.defs.PumpType
|
||||||
import app.aaps.core.interfaces.utils.DateUtil
|
import app.aaps.core.interfaces.utils.DateUtil
|
||||||
|
@ -257,6 +258,26 @@ interface PumpSync {
|
||||||
**/
|
**/
|
||||||
fun insertTherapyEventIfNewWithTimestamp(timestamp: Long, type: DetailedBolusInfo.EventType, note: String? = null, pumpId: Long? = null, pumpType: PumpType, pumpSerial: String): Boolean
|
fun insertTherapyEventIfNewWithTimestamp(timestamp: Long, type: DetailedBolusInfo.EventType, note: String? = null, pumpId: Long? = null, pumpType: PumpType, pumpSerial: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronization of FINGER_STICK_BG_VALUE events
|
||||||
|
*
|
||||||
|
* Assuming there will be no clash on timestamp from different pumps
|
||||||
|
* only timestamp and type is compared
|
||||||
|
*
|
||||||
|
* If db record doesn't exist, new record is created.
|
||||||
|
* If exists, data is ignored
|
||||||
|
*
|
||||||
|
* @param timestamp timestamp of event from pump history
|
||||||
|
* @param glucose glucose value
|
||||||
|
* @param glucoseUnit glucose unit
|
||||||
|
* @param note note
|
||||||
|
* @param pumpId pump id from history if available
|
||||||
|
* @param pumpType pump type like PumpType.ACCU_CHEK_COMBO
|
||||||
|
* @param pumpSerial pump serial number
|
||||||
|
* @return true if new record is created
|
||||||
|
**/
|
||||||
|
fun insertFingerBgIfNewWithTimestamp(timestamp: Long, glucose: Double, glucoseUnit: GlucoseUnit, note: String? = null, pumpId: Long? = null, pumpType: PumpType, pumpSerial: String): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an announcement
|
* Create an announcement
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,5 +32,4 @@ android.nonTransitiveRClass=true
|
||||||
# null: KtCallExpression
|
# null: KtCallExpression
|
||||||
# https://youtrack.jetbrains.com/issue/KT-58027
|
# https://youtrack.jetbrains.com/issue/KT-58027
|
||||||
kapt.use.jvm.ir=false
|
kapt.use.jvm.ir=false
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
|
||||||
android.nonFinalResIds=true
|
android.nonFinalResIds=true
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package app.aaps.implementation.pump
|
package app.aaps.implementation.pump
|
||||||
|
|
||||||
|
import app.aaps.core.interfaces.db.GlucoseUnit
|
||||||
import app.aaps.core.interfaces.logging.AAPSLogger
|
import app.aaps.core.interfaces.logging.AAPSLogger
|
||||||
import app.aaps.core.interfaces.logging.LTag
|
import app.aaps.core.interfaces.logging.LTag
|
||||||
import app.aaps.core.interfaces.logging.UserEntryLogger
|
import app.aaps.core.interfaces.logging.UserEntryLogger
|
||||||
|
@ -16,6 +17,7 @@ import app.aaps.core.interfaces.sharedPreferences.SP
|
||||||
import app.aaps.core.interfaces.utils.DateUtil
|
import app.aaps.core.interfaces.utils.DateUtil
|
||||||
import app.aaps.core.interfaces.utils.T
|
import app.aaps.core.interfaces.utils.T
|
||||||
import app.aaps.core.main.events.EventNewNotification
|
import app.aaps.core.main.events.EventNewNotification
|
||||||
|
import app.aaps.core.main.extensions.fromConstant
|
||||||
import app.aaps.core.main.pump.fromDbPumpType
|
import app.aaps.core.main.pump.fromDbPumpType
|
||||||
import app.aaps.core.main.pump.toDbPumpType
|
import app.aaps.core.main.pump.toDbPumpType
|
||||||
import app.aaps.core.main.pump.toDbSource
|
import app.aaps.core.main.pump.toDbSource
|
||||||
|
@ -291,6 +293,42 @@ class PumpSyncImplementation @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun insertFingerBgIfNewWithTimestamp(timestamp: Long, glucose: Double, glucoseUnit: GlucoseUnit, note: String?, pumpId: Long?, pumpType: PumpType, pumpSerial: String): Boolean {
|
||||||
|
if (!confirmActivePump(timestamp, pumpType, pumpSerial)) return false
|
||||||
|
var type = TherapyEvent.Type.FINGER_STICK_BG_VALUE
|
||||||
|
val therapyEvent = TherapyEvent(
|
||||||
|
timestamp = timestamp,
|
||||||
|
type = type,
|
||||||
|
duration = 0,
|
||||||
|
note = note,
|
||||||
|
enteredBy = "AndroidAPS",
|
||||||
|
glucose = glucose,
|
||||||
|
glucoseType = TherapyEvent.MeterType.FINGER,
|
||||||
|
glucoseUnit = TherapyEvent.GlucoseUnit.fromConstant(glucoseUnit),
|
||||||
|
interfaceIDs_backing = InterfaceIDs(
|
||||||
|
pumpId = pumpId,
|
||||||
|
pumpType = pumpType.toDbPumpType(),
|
||||||
|
pumpSerial = pumpSerial
|
||||||
|
)
|
||||||
|
)
|
||||||
|
uel.log(
|
||||||
|
action = UserEntry.Action.CAREPORTAL,
|
||||||
|
source = pumpType.source.toDbSource(),
|
||||||
|
note = note,
|
||||||
|
timestamp = timestamp,
|
||||||
|
ValueWithUnit.Timestamp(timestamp), ValueWithUnit.TherapyEventType(type)
|
||||||
|
)
|
||||||
|
repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent))
|
||||||
|
.doOnError {
|
||||||
|
aapsLogger.error(LTag.DATABASE, "Error while saving TherapyEvent", it)
|
||||||
|
}
|
||||||
|
.blockingGet()
|
||||||
|
.also { result ->
|
||||||
|
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $it") }
|
||||||
|
return result.inserted.size > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun insertAnnouncement(error: String, pumpId: Long?, pumpType: PumpType, pumpSerial: String) {
|
override fun insertAnnouncement(error: String, pumpId: Long?, pumpType: PumpType, pumpSerial: String) {
|
||||||
if (!confirmActivePump(dateUtil.now(), pumpType, pumpSerial)) return
|
if (!confirmActivePump(dateUtil.now(), pumpType, pumpSerial)) return
|
||||||
disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(error, pumpId, pumpType.toDbPumpType(), pumpSerial))
|
disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(error, pumpId, pumpType.toDbPumpType(), pumpSerial))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="dynisf_adjust_sensitivity">Habilitar la relación de sensibilidad basada en TDD para modificar las basales y el objetivo de glucosa</string>
|
<string name="dynisf_adjust_sensitivity">Habilitar la relación de sensibilidad basada en TDD para modificar las basales y el objetivo de glucosa</string>
|
||||||
<string name="dynisf_adjust_sensitivity_summary">Utiliza las últimas 24h TDD/7D TDD para calcular el ratio de sensibilidad utilizado para aumentar o disminuir la tasa basal, y también ajustar el objetivo de glucosa si estas opciones están activadas, de la misma forma que lo hace Autosens. Se recomienda comenzar con esta opción desactivada</string>
|
<string name="dynisf_adjust_sensitivity_summary">Utiliza las últimas 24h TDD/7D TDD para calcular el factor de sensibilidad utilizado para aumentar o disminuir la tasa basal, y también ajusta el objetivo de glucosa si estas opciones están activadas, de la misma forma que lo hace Autosens. Se recomienda comenzar con esta opción desactivada</string>
|
||||||
<string name="DynISFAdjust_title" formatted="false">Factor de ajuste de ISF Dinámico %</string>
|
<string name="DynISFAdjust_title" formatted="false">Factor de ajuste de ISF Dinámico %</string>
|
||||||
<string name="DynISFAdjust_summary" formatted="false">Factor de ajuste para ISF Dinámico. Establezca más de 100% para una corrección más agresiva, y menos de 100% para correcciones más susves.</string>
|
<string name="DynISFAdjust_summary" formatted="false">Factor de ajuste para ISF Dinámico. Establezca más de 100% para una corrección más agresiva, y menos de 100% para correcciones más susves.</string>
|
||||||
<string name="high_temptarget_raises_sensitivity_title">Objetivo temporal alto aumenta la sensibilidad</string>
|
<string name="high_temptarget_raises_sensitivity_title">Objetivo temporal alto aumenta la sensibilidad</string>
|
||||||
|
|
|
@ -300,7 +300,7 @@ class ProfileFragment : DaggerFragment() {
|
||||||
|
|
||||||
binding.profileRemove.setOnClickListener {
|
binding.profileRemove.setOnClickListener {
|
||||||
activity?.let { activity ->
|
activity?.let { activity ->
|
||||||
OKDialog.showConfirmation(activity, rh.gs(R.string.delete_current_profile), {
|
OKDialog.showConfirmation(activity, rh.gs(R.string.delete_current_profile, profilePlugin.currentProfile()?.name), {
|
||||||
uel.log(
|
uel.log(
|
||||||
UserEntry.Action.PROFILE_REMOVED, UserEntry.Sources.LocalProfile, ValueWithUnit.SimpleString(
|
UserEntry.Action.PROFILE_REMOVED, UserEntry.Sources.LocalProfile, ValueWithUnit.SimpleString(
|
||||||
profilePlugin.currentProfile()?.name
|
profilePlugin.currentProfile()?.name
|
||||||
|
|
|
@ -149,7 +149,7 @@
|
||||||
<string name="a11y_add_new_to_list">add new to list</string>
|
<string name="a11y_add_new_to_list">add new to list</string>
|
||||||
<string name="do_you_want_switch_profile">Do you want to switch profile and discard changes made to current profile?</string>
|
<string name="do_you_want_switch_profile">Do you want to switch profile and discard changes made to current profile?</string>
|
||||||
<string name="save_or_reset_changes_first">Save or reset current changes first</string>
|
<string name="save_or_reset_changes_first">Save or reset current changes first</string>
|
||||||
<string name="delete_current_profile">Delete current profile?</string>
|
<string name="delete_current_profile">Delete profile \"%1$s\"?</string>
|
||||||
<string name="units_colon">Units:</string>
|
<string name="units_colon">Units:</string>
|
||||||
<string name="missing_profile_name">Missing profile name</string>
|
<string name="missing_profile_name">Missing profile name</string>
|
||||||
<string name="error_in_ic_values">Error in IC values</string>
|
<string name="error_in_ic_values">Error in IC values</string>
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="smoothing_shortname">UTJEVNING</string>
|
<string name="smoothing_shortname">UTJEVNING</string>
|
||||||
<string name="exponential_smoothing_name">Eksponentiell utjevning</string>
|
<string name="exponential_smoothing_name">Eksponentiell utjevning</string>
|
||||||
<string name="description_exponential_smoothing">"Andre ordens algoritme for eksponentiell utjevning"</string>
|
<string name="description_exponential_smoothing">"Algoritme for eksponentiell utjevning, nyeste BS-verdi påvirkes"</string>
|
||||||
<string name="avg_smoothing_name">Gjennomsnittlig utjevning</string>
|
<string name="avg_smoothing_name">Gjennomsnittlig utjevning</string>
|
||||||
<string name="description_avg_smoothing">"Gjennomsnittlig utjevnings-algoritme, nyeste verdi påvirkes ikke"</string>
|
<string name="description_avg_smoothing">"Algoritme for gjennomsnittlig utjevning, nyeste BS-verdi påvirkes ikke. Kan minne om BYODA G6 sin utjevningsalgoritme"</string>
|
||||||
<string name="no_smoothing_name">Ingen utjevning</string>
|
<string name="no_smoothing_name">Ingen utjevning</string>
|
||||||
<string name="description_no_smoothing">"Ingen utjevning utføres på motatte blodsukkerverdier. Bruk dette valget når du allerede har filtrerte data, f.eks. fra BYODA G6."</string>
|
<string name="description_no_smoothing">"Ingen utjevning utføres på mottatte blodsukkerverdier. Bruk dette valget når du allerede har filtrerte data, f.eks. fra BYODA G6."</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -15,10 +15,14 @@ import app.aaps.core.interfaces.rx.events.EventPreferenceChange
|
||||||
import app.aaps.core.interfaces.sharedPreferences.SP
|
import app.aaps.core.interfaces.sharedPreferences.SP
|
||||||
import app.aaps.database.entities.GlucoseValue
|
import app.aaps.database.entities.GlucoseValue
|
||||||
import app.aaps.plugins.sync.R
|
import app.aaps.plugins.sync.R
|
||||||
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.MathContext
|
||||||
|
import java.math.RoundingMode
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.SocketAddress
|
import java.net.SocketAddress
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -123,7 +127,12 @@ class GarminPlugin @Inject constructor(
|
||||||
setupGarminMessenger()
|
setupGarminMessenger()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupHttpServer() {
|
private fun setupHttpServer() {
|
||||||
|
setupHttpServer(Duration.ZERO)
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun setupHttpServer(wait: Duration) {
|
||||||
if (sp.getBoolean("communication_http", false)) {
|
if (sp.getBoolean("communication_http", false)) {
|
||||||
val port = sp.getInt("communication_http_port", 28891)
|
val port = sp.getInt("communication_http_port", 28891)
|
||||||
if (server != null && server?.port == port) return
|
if (server != null && server?.port == port) return
|
||||||
|
@ -133,6 +142,8 @@ class GarminPlugin @Inject constructor(
|
||||||
registerEndpoint("/get", requestHandler(::onGetBloodGlucose))
|
registerEndpoint("/get", requestHandler(::onGetBloodGlucose))
|
||||||
registerEndpoint("/carbs", requestHandler(::onPostCarbs))
|
registerEndpoint("/carbs", requestHandler(::onPostCarbs))
|
||||||
registerEndpoint("/connect", requestHandler(::onConnectPump))
|
registerEndpoint("/connect", requestHandler(::onConnectPump))
|
||||||
|
registerEndpoint("/connect", requestHandler(::onSgv))
|
||||||
|
awaitReady(wait)
|
||||||
}
|
}
|
||||||
} else if (server != null) {
|
} else if (server != null) {
|
||||||
aapsLogger.info(LTag.GARMIN, "stopping HTTP server")
|
aapsLogger.info(LTag.GARMIN, "stopping HTTP server")
|
||||||
|
@ -363,4 +374,62 @@ class GarminPlugin @Inject constructor(
|
||||||
jo.addProperty("connected", loopHub.isConnected)
|
jo.addProperty("connected", loopHub.isConnected)
|
||||||
return jo.toString()
|
return jo.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun glucoseSlopeMgDlPerMilli(glucose1: GlucoseValue, glucose2: GlucoseValue): Double {
|
||||||
|
return (glucose2.value - glucose1.value) / (glucose2.timestamp - glucose1.timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns glucose values in Nightscout/Xdrip format. */
|
||||||
|
@VisibleForTesting
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
fun onSgv(uri: URI): CharSequence {
|
||||||
|
val count = getQueryParameter(uri,"count", 24L)
|
||||||
|
.toInt().coerceAtMost(1000).coerceAtLeast(1)
|
||||||
|
val briefMode = getQueryParameter(uri, "brief_mode", false)
|
||||||
|
|
||||||
|
// Guess a start time to get [count+1] readings. This is a heuristic that only works if we get readings
|
||||||
|
// every 5 minutes and we're not missing readings. We truncate in case we get more readings but we'll
|
||||||
|
// get less, e.g., in case we're missing readings for the last half hour. We get one extra reading,
|
||||||
|
// to compute the glucose delta.
|
||||||
|
val from = clock.instant().minus(Duration.ofMinutes(5L * (count + 1)))
|
||||||
|
val glucoseValues = loopHub.getGlucoseValues(from, false)
|
||||||
|
val joa = JsonArray()
|
||||||
|
for (i in 0 until count.coerceAtMost(glucoseValues.size)) {
|
||||||
|
val jo = JsonObject()
|
||||||
|
val glucose = glucoseValues[i]
|
||||||
|
if (!briefMode) {
|
||||||
|
jo.addProperty("_id", glucose.id.toString())
|
||||||
|
jo.addProperty("device", glucose.sourceSensor.toString())
|
||||||
|
val timestamp = Instant.ofEpochMilli(glucose.timestamp)
|
||||||
|
jo.addProperty("deviceString", timestamp.toString())
|
||||||
|
jo.addProperty("sysTime", timestamp.toString())
|
||||||
|
glucose.raw?.let { raw -> jo.addProperty("unfiltered", raw) }
|
||||||
|
}
|
||||||
|
jo.addProperty("date", glucose.timestamp)
|
||||||
|
jo.addProperty("sgv", glucose.value.roundToInt())
|
||||||
|
if (i + 1 < glucoseValues.size) {
|
||||||
|
// Compute the 5 minute delta.
|
||||||
|
val delta = 300_000.0 * glucoseSlopeMgDlPerMilli(glucoseValues[i + 1], glucose)
|
||||||
|
jo.addProperty("delta", BigDecimal(delta, MathContext(3, RoundingMode.HALF_UP)))
|
||||||
|
}
|
||||||
|
jo.addProperty("direction", glucose.trendArrow.text)
|
||||||
|
glucose.noise?.let { n -> jo.addProperty("noise", n) }
|
||||||
|
if (i == 0) {
|
||||||
|
when (loopHub.glucoseUnit) {
|
||||||
|
GlucoseUnit.MGDL -> jo.addProperty("units_hint", "mgdl")
|
||||||
|
GlucoseUnit.MMOL -> jo.addProperty("units_hint", "mmol")
|
||||||
|
}
|
||||||
|
jo.addProperty("iob", loopHub.insulinOnboard + loopHub.insulinBasalOnboard)
|
||||||
|
loopHub.temporaryBasal.also {
|
||||||
|
if (!it.isNaN()) {
|
||||||
|
val temporaryBasalRateInPercent = (it * 100.0).toInt()
|
||||||
|
jo.addProperty("tbr", temporaryBasalRateInPercent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jo.addProperty("cob", loopHub.carbsOnboard)
|
||||||
|
}
|
||||||
|
joa.add(jo)
|
||||||
|
}
|
||||||
|
return joa.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,12 @@ interface LoopHub {
|
||||||
/** Returns the remaining bolus insulin on board. */
|
/** Returns the remaining bolus insulin on board. */
|
||||||
val insulinOnboard: Double
|
val insulinOnboard: Double
|
||||||
|
|
||||||
|
/** Returns the basal insulin on board. */
|
||||||
|
val insulinBasalOnboard: Double
|
||||||
|
|
||||||
|
/** Returns the remaining carbs on board. */
|
||||||
|
val carbsOnboard: Double?
|
||||||
|
|
||||||
/** Returns true if the pump is connected. */
|
/** Returns true if the pump is connected. */
|
||||||
val isConnected: Boolean
|
val isConnected: Boolean
|
||||||
|
|
||||||
|
@ -48,4 +54,4 @@ interface LoopHub {
|
||||||
avgHeartRate: Int,
|
avgHeartRate: Int,
|
||||||
device: String?
|
device: String?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import app.aaps.core.interfaces.profile.ProfileFunction
|
||||||
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
||||||
import app.aaps.core.interfaces.queue.CommandQueue
|
import app.aaps.core.interfaces.queue.CommandQueue
|
||||||
import app.aaps.core.interfaces.sharedPreferences.SP
|
import app.aaps.core.interfaces.sharedPreferences.SP
|
||||||
|
import app.aaps.core.main.graph.OverviewData
|
||||||
import app.aaps.database.ValueWrapper
|
import app.aaps.database.ValueWrapper
|
||||||
import app.aaps.database.entities.EffectiveProfileSwitch
|
import app.aaps.database.entities.EffectiveProfileSwitch
|
||||||
import app.aaps.database.entities.GlucoseValue
|
import app.aaps.database.entities.GlucoseValue
|
||||||
|
@ -42,6 +43,7 @@ class LoopHubImpl @Inject constructor(
|
||||||
private val repo: AppRepository,
|
private val repo: AppRepository,
|
||||||
private val userEntryLogger: UserEntryLogger,
|
private val userEntryLogger: UserEntryLogger,
|
||||||
private val sp: SP,
|
private val sp: SP,
|
||||||
|
private val overviewData: OverviewData,
|
||||||
) : LoopHub {
|
) : LoopHub {
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -64,6 +66,14 @@ class LoopHubImpl @Inject constructor(
|
||||||
override val insulinOnboard: Double
|
override val insulinOnboard: Double
|
||||||
get() = iobCobCalculator.calculateIobFromBolus().iob
|
get() = iobCobCalculator.calculateIobFromBolus().iob
|
||||||
|
|
||||||
|
/** Returns the remaining bolus and basal insulin on board. */
|
||||||
|
override val insulinBasalOnboard :Double
|
||||||
|
get() = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().basaliob
|
||||||
|
|
||||||
|
/** Returns the remaining carbs on board. */
|
||||||
|
override val carbsOnboard: Double?
|
||||||
|
get() = overviewData.cobInfo(iobCobCalculator).displayCob
|
||||||
|
|
||||||
/** Returns true if the pump is connected. */
|
/** Returns true if the pump is connected. */
|
||||||
override val isConnected: Boolean get() = !loop.isDisconnected
|
override val isConnected: Boolean get() = !loop.isDisconnected
|
||||||
|
|
||||||
|
@ -142,4 +152,4 @@ class LoopHubImpl @Inject constructor(
|
||||||
)
|
)
|
||||||
repo.runTransaction(InsertOrUpdateHeartRateTransaction(hr)).blockingAwait()
|
repo.runTransaction(InsertOrUpdateHeartRateTransaction(hr)).blockingAwait()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
<string name="description_wear">Supervisar y controlar AAPS usando un reloj WearOS</string>
|
<string name="description_wear">Supervisar y controlar AAPS usando un reloj WearOS</string>
|
||||||
<string name="no_watch_connected">(Ningún reloj conectado)</string>
|
<string name="no_watch_connected">(Ningún reloj conectado)</string>
|
||||||
<string name="pump_status">Estado de la bomba de insulina</string>
|
<string name="pump_status">Estado de la bomba de insulina</string>
|
||||||
<string name="loop_status">Estado del lazo</string>
|
<string name="loop_status">Estado del bucle</string>
|
||||||
<string name="wizard_result">Calc. Asistente:\nInsulina: %1$.2fU\nCarbohidratos: %2$dg</string>
|
<string name="wizard_result">Calc. Asistente:\nInsulina: %1$.2fU\nCarbohidratos: %2$dg</string>
|
||||||
<string name="quick_wizard_not_available">El asistente rápido seleccionado ya no está disponible, por favor actualice su tarjeta</string>
|
<string name="quick_wizard_not_available">El asistente rápido seleccionado ya no está disponible, por favor actualice su tarjeta</string>
|
||||||
<string name="quick_wizard_message">Asistente Rápido: %1$s\nInsulina: %2$.2fU\nCarbohidratos: %3$dg</string>
|
<string name="quick_wizard_message">Asistente Rápido: %1$s\nInsulina: %2$.2fU\nCarbohidratos: %3$dg</string>
|
||||||
|
|
|
@ -14,13 +14,13 @@ import org.junit.jupiter.api.Assertions.assertArrayEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.RepeatedTest
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
import org.mockito.ArgumentMatchers.anyBoolean
|
import org.mockito.ArgumentMatchers.anyBoolean
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.ArgumentMatchers.anyLong
|
import org.mockito.ArgumentMatchers.anyLong
|
||||||
import org.mockito.ArgumentMatchers.anyString
|
import org.mockito.ArgumentMatchers.anyString
|
||||||
import org.mockito.ArgumentMatchers.eq
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.atMost
|
import org.mockito.Mockito.atMost
|
||||||
import org.mockito.Mockito.mock
|
import org.mockito.Mockito.mock
|
||||||
|
@ -28,15 +28,21 @@ import org.mockito.Mockito.times
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.atLeastOnce
|
||||||
|
import org.mockito.kotlin.eq
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.SocketAddress
|
import java.net.SocketAddress
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.concurrent.locks.Condition
|
import java.util.concurrent.locks.Condition
|
||||||
|
import kotlin.ranges.LongProgression.Companion.fromClosedRange
|
||||||
|
|
||||||
class GarminPluginTest: TestBase() {
|
class GarminPluginTest: TestBase() {
|
||||||
private lateinit var gp: GarminPlugin
|
private lateinit var gp: GarminPlugin
|
||||||
|
@ -60,13 +66,17 @@ class GarminPluginTest: TestBase() {
|
||||||
`when`(sp.getBoolean(anyString(), anyBoolean())).thenAnswer { i -> i.arguments[1] }
|
`when`(sp.getBoolean(anyString(), anyBoolean())).thenAnswer { i -> i.arguments[1] }
|
||||||
`when`(sp.getString(anyString(), anyString())).thenAnswer { i -> i.arguments[1] }
|
`when`(sp.getString(anyString(), anyString())).thenAnswer { i -> i.arguments[1] }
|
||||||
`when`(sp.getInt(anyString(), anyInt())).thenAnswer { i -> i.arguments[1] }
|
`when`(sp.getInt(anyString(), anyInt())).thenAnswer { i -> i.arguments[1] }
|
||||||
`when`(sp.getInt(eq("communication_http_port") ?: "", anyInt()))
|
`when`(sp.getInt(eq("communication_http_port"), anyInt()))
|
||||||
.thenReturn(28890)
|
.thenReturn(28890)
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun verifyNoFurtherInteractions() {
|
fun verifyNoFurtherInteractions() {
|
||||||
verify(loopHub, atMost(2)).currentProfileName
|
verify(loopHub, atMost(2)).currentProfileName
|
||||||
|
verify(loopHub, atMost(3)).insulinOnboard
|
||||||
|
verify(loopHub, atMost(3)).insulinBasalOnboard
|
||||||
|
verify(loopHub, atMost(3)).temporaryBasal
|
||||||
|
verify(loopHub, atMost(3)).carbsOnboard
|
||||||
verifyNoMoreInteractions(loopHub)
|
verifyNoMoreInteractions(loopHub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +96,9 @@ class GarminPluginTest: TestBase() {
|
||||||
"device" to "Test_Device")
|
"device" to "Test_Device")
|
||||||
|
|
||||||
private fun createGlucoseValue(timestamp: Instant, value: Double = 93.0) = GlucoseValue(
|
private fun createGlucoseValue(timestamp: Instant, value: Double = 93.0) = GlucoseValue(
|
||||||
|
id = 10 * timestamp.toEpochMilli(),
|
||||||
timestamp = timestamp.toEpochMilli(), raw = 90.0, value = value,
|
timestamp = timestamp.toEpochMilli(), raw = 90.0, value = value,
|
||||||
trendArrow = GlucoseValue.TrendArrow.FLAT, noise = null,
|
trendArrow = GlucoseValue.TrendArrow.FLAT, noise = 4.5,
|
||||||
sourceSensor = GlucoseValue.SourceSensor.RANDOM
|
sourceSensor = GlucoseValue.SourceSensor.RANDOM
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -149,20 +160,20 @@ class GarminPluginTest: TestBase() {
|
||||||
fun setupHttpServer_enabled() {
|
fun setupHttpServer_enabled() {
|
||||||
`when`(sp.getBoolean("communication_http", false)).thenReturn(true)
|
`when`(sp.getBoolean("communication_http", false)).thenReturn(true)
|
||||||
`when`(sp.getInt("communication_http_port", 28891)).thenReturn(28892)
|
`when`(sp.getInt("communication_http_port", 28891)).thenReturn(28892)
|
||||||
gp.setupHttpServer()
|
gp.setupHttpServer(Duration.ofSeconds(10))
|
||||||
val reqUri = URI("http://127.0.0.1:28892/get")
|
val reqUri = URI("http://127.0.0.1:28892/get")
|
||||||
val resp = reqUri.toURL().openConnection() as HttpURLConnection
|
val resp = reqUri.toURL().openConnection() as HttpURLConnection
|
||||||
assertEquals(200, resp.responseCode)
|
assertEquals(200, resp.responseCode)
|
||||||
|
|
||||||
// Change port
|
// Change port
|
||||||
`when`(sp.getInt("communication_http_port", 28891)).thenReturn(28893)
|
`when`(sp.getInt("communication_http_port", 28891)).thenReturn(28893)
|
||||||
gp.setupHttpServer()
|
gp.setupHttpServer(Duration.ofSeconds(10))
|
||||||
val reqUri2 = URI("http://127.0.0.1:28893/get")
|
val reqUri2 = URI("http://127.0.0.1:28893/get")
|
||||||
val resp2 = reqUri2.toURL().openConnection() as HttpURLConnection
|
val resp2 = reqUri2.toURL().openConnection() as HttpURLConnection
|
||||||
assertEquals(200, resp2.responseCode)
|
assertEquals(200, resp2.responseCode)
|
||||||
|
|
||||||
`when`(sp.getBoolean("communication_http", false)).thenReturn(false)
|
`when`(sp.getBoolean("communication_http", false)).thenReturn(false)
|
||||||
gp.setupHttpServer()
|
gp.setupHttpServer(Duration.ofSeconds(10))
|
||||||
assertThrows(ConnectException::class.java) {
|
assertThrows(ConnectException::class.java) {
|
||||||
(reqUri2.toURL().openConnection() as HttpURLConnection).responseCode
|
(reqUri2.toURL().openConnection() as HttpURLConnection).responseCode
|
||||||
}
|
}
|
||||||
|
@ -177,7 +188,7 @@ class GarminPluginTest: TestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun setupHttpServer_disabled() {
|
fun setupHttpServer_disabled() {
|
||||||
gp.setupHttpServer()
|
gp.setupHttpServer(Duration.ofSeconds(10))
|
||||||
val reqUri = URI("http://127.0.0.1:28890/get")
|
val reqUri = URI("http://127.0.0.1:28890/get")
|
||||||
assertThrows(ConnectException::class.java) {
|
assertThrows(ConnectException::class.java) {
|
||||||
(reqUri.toURL().openConnection() as HttpURLConnection).responseCode
|
(reqUri.toURL().openConnection() as HttpURLConnection).responseCode
|
||||||
|
@ -252,7 +263,7 @@ class GarminPluginTest: TestBase() {
|
||||||
gp.onConnectDevice(device)
|
gp.onConnectDevice(device)
|
||||||
|
|
||||||
val captor = ArgumentCaptor.forClass(Any::class.java)
|
val captor = ArgumentCaptor.forClass(Any::class.java)
|
||||||
verify(gp.garminMessenger)!!.sendMessage(eq(device) ?: device, captor.capture() ?: "")
|
verify(gp.garminMessenger)!!.sendMessage(eq(device), captor.capture() ?: "")
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val r = captor.value as Map<String, Any>
|
val r = captor.value as Map<String, Any>
|
||||||
assertEquals("foo", r["key"])
|
assertEquals("foo", r["key"])
|
||||||
|
@ -357,4 +368,65 @@ class GarminPluginTest: TestBase() {
|
||||||
verify(loopHub).connectPump()
|
verify(loopHub).connectPump()
|
||||||
verify(loopHub).isConnected
|
verify(loopHub).isConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSgv_NoGlucose() {
|
||||||
|
whenever(loopHub.glucoseUnit).thenReturn(GlucoseUnit.MMOL)
|
||||||
|
whenever(loopHub.getGlucoseValues(any(), eq(false))).thenReturn(emptyList())
|
||||||
|
assertEquals("[]", gp.onSgv(createUri(mapOf())))
|
||||||
|
verify(loopHub).getGlucoseValues(clock.instant().minusSeconds(25L * 300L), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSgv_NoDelta() {
|
||||||
|
whenever(loopHub.glucoseUnit).thenReturn(GlucoseUnit.MMOL)
|
||||||
|
whenever(loopHub.insulinOnboard).thenReturn(2.7)
|
||||||
|
whenever(loopHub.insulinBasalOnboard).thenReturn(2.5)
|
||||||
|
whenever(loopHub.temporaryBasal).thenReturn(0.8)
|
||||||
|
whenever(loopHub.carbsOnboard).thenReturn(10.7)
|
||||||
|
whenever(loopHub.getGlucoseValues(any(), eq(false))).thenReturn(
|
||||||
|
listOf(createGlucoseValue(
|
||||||
|
clock.instant().minusSeconds(100L), 99.3)))
|
||||||
|
assertEquals(
|
||||||
|
"""[{"_id":"-900000","device":"RANDOM","deviceString":"1969-12-31T23:58:30Z","sysTime":"1969-12-31T23:58:30Z","unfiltered":90.0,"date":-90000,"sgv":99,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7}]""",
|
||||||
|
gp.onSgv(createUri(mapOf())))
|
||||||
|
verify(loopHub).getGlucoseValues(clock.instant().minusSeconds(25L * 300L), false)
|
||||||
|
verify(loopHub).glucoseUnit
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onSgv() {
|
||||||
|
whenever(loopHub.glucoseUnit).thenReturn(GlucoseUnit.MMOL)
|
||||||
|
whenever(loopHub.insulinOnboard).thenReturn(2.7)
|
||||||
|
whenever(loopHub.insulinBasalOnboard).thenReturn(2.5)
|
||||||
|
whenever(loopHub.temporaryBasal).thenReturn(0.8)
|
||||||
|
whenever(loopHub.carbsOnboard).thenReturn(10.7)
|
||||||
|
whenever(loopHub.getGlucoseValues(any(), eq(false))).thenAnswer { i ->
|
||||||
|
val from = i.getArgument<Instant>(0)
|
||||||
|
fromClosedRange(from.toEpochMilli(), clock.instant().toEpochMilli(), 300_000L)
|
||||||
|
.map(Instant::ofEpochMilli)
|
||||||
|
.mapIndexed { idx, ts -> createGlucoseValue(ts, 100.0+(10 * idx)) }.reversed()}
|
||||||
|
assertEquals(
|
||||||
|
"""[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7}]""",
|
||||||
|
gp.onSgv(createUri(mapOf("count" to "1"))))
|
||||||
|
verify(loopHub).getGlucoseValues(
|
||||||
|
clock.instant().minusSeconds(600L), false)
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"""[{"_id":"100000","device":"RANDOM","deviceString":"1970-01-01T00:00:10Z","sysTime":"1970-01-01T00:00:10Z","unfiltered":90.0,"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7},""" +
|
||||||
|
"""{"_id":"-2900000","device":"RANDOM","deviceString":"1969-12-31T23:55:10Z","sysTime":"1969-12-31T23:55:10Z","unfiltered":90.0,"date":-290000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5}]""",
|
||||||
|
gp.onSgv(createUri(mapOf("count" to "2"))))
|
||||||
|
verify(loopHub).getGlucoseValues(
|
||||||
|
clock.instant().minusSeconds(900L), false)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"""[{"date":10000,"sgv":130,"delta":10,"direction":"Flat","noise":4.5,"units_hint":"mmol","iob":5.2,"tbr":80,"cob":10.7},""" +
|
||||||
|
"""{"date":-290000,"sgv":120,"delta":10,"direction":"Flat","noise":4.5}]""",
|
||||||
|
gp.onSgv(createUri(mapOf("count" to "2", "brief_mode" to "true"))))
|
||||||
|
verify(loopHub, times(2)).getGlucoseValues(
|
||||||
|
clock.instant().minusSeconds(900L), false)
|
||||||
|
|
||||||
|
verify(loopHub, atLeastOnce()).glucoseUnit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import app.aaps.core.interfaces.aps.Loop
|
||||||
import app.aaps.core.interfaces.constraints.Constraint
|
import app.aaps.core.interfaces.constraints.Constraint
|
||||||
import app.aaps.core.interfaces.constraints.ConstraintsChecker
|
import app.aaps.core.interfaces.constraints.ConstraintsChecker
|
||||||
import app.aaps.core.interfaces.db.GlucoseUnit
|
import app.aaps.core.interfaces.db.GlucoseUnit
|
||||||
|
import app.aaps.core.interfaces.iob.CobInfo
|
||||||
import app.aaps.core.interfaces.iob.IobCobCalculator
|
import app.aaps.core.interfaces.iob.IobCobCalculator
|
||||||
import app.aaps.core.interfaces.iob.IobTotal
|
import app.aaps.core.interfaces.iob.IobTotal
|
||||||
import app.aaps.core.interfaces.logging.UserEntryLogger
|
import app.aaps.core.interfaces.logging.UserEntryLogger
|
||||||
|
@ -13,6 +14,7 @@ import app.aaps.core.interfaces.profile.ProfileFunction
|
||||||
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
||||||
import app.aaps.core.interfaces.queue.CommandQueue
|
import app.aaps.core.interfaces.queue.CommandQueue
|
||||||
import app.aaps.core.interfaces.sharedPreferences.SP
|
import app.aaps.core.interfaces.sharedPreferences.SP
|
||||||
|
import app.aaps.core.main.graph.OverviewData
|
||||||
import app.aaps.database.ValueWrapper
|
import app.aaps.database.ValueWrapper
|
||||||
import app.aaps.database.entities.EffectiveProfileSwitch
|
import app.aaps.database.entities.EffectiveProfileSwitch
|
||||||
import app.aaps.database.entities.GlucoseValue
|
import app.aaps.database.entities.GlucoseValue
|
||||||
|
@ -54,6 +56,7 @@ class LoopHubTest: TestBase() {
|
||||||
@Mock lateinit var repo: AppRepository
|
@Mock lateinit var repo: AppRepository
|
||||||
@Mock lateinit var userEntryLogger: UserEntryLogger
|
@Mock lateinit var userEntryLogger: UserEntryLogger
|
||||||
@Mock lateinit var sp: SP
|
@Mock lateinit var sp: SP
|
||||||
|
@Mock lateinit var overviewData: OverviewData
|
||||||
|
|
||||||
private lateinit var loopHub: LoopHubImpl
|
private lateinit var loopHub: LoopHubImpl
|
||||||
private val clock = Clock.fixed(Instant.ofEpochMilli(10_000), ZoneId.of("UTC"))
|
private val clock = Clock.fixed(Instant.ofEpochMilli(10_000), ZoneId.of("UTC"))
|
||||||
|
@ -62,7 +65,7 @@ class LoopHubTest: TestBase() {
|
||||||
fun setup() {
|
fun setup() {
|
||||||
loopHub = LoopHubImpl(
|
loopHub = LoopHubImpl(
|
||||||
aapsLogger, commandQueue, constraints, iobCobCalculator, loop,
|
aapsLogger, commandQueue, constraints, iobCobCalculator, loop,
|
||||||
profileFunction, repo, userEntryLogger, sp
|
profileFunction, repo, userEntryLogger, sp, overviewData
|
||||||
)
|
)
|
||||||
loopHub.clock = clock
|
loopHub.clock = clock
|
||||||
}
|
}
|
||||||
|
@ -76,9 +79,10 @@ class LoopHubTest: TestBase() {
|
||||||
verifyNoMoreInteractions(profileFunction)
|
verifyNoMoreInteractions(profileFunction)
|
||||||
verifyNoMoreInteractions(repo)
|
verifyNoMoreInteractions(repo)
|
||||||
verifyNoMoreInteractions(userEntryLogger)
|
verifyNoMoreInteractions(userEntryLogger)
|
||||||
|
verifyNoMoreInteractions(overviewData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCurrentProfile() {
|
fun testCurrentProfile() {
|
||||||
val profile = mock(Profile::class.java)
|
val profile = mock(Profile::class.java)
|
||||||
`when`(profileFunction.getProfile()).thenReturn(profile)
|
`when`(profileFunction.getProfile()).thenReturn(profile)
|
||||||
|
@ -109,6 +113,22 @@ class LoopHubTest: TestBase() {
|
||||||
verify(iobCobCalculator, times(1)).calculateIobFromBolus()
|
verify(iobCobCalculator, times(1)).calculateIobFromBolus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBasalOnBoard() {
|
||||||
|
val iobBasal = IobTotal(time = 0).apply { basaliob = 23.9 }
|
||||||
|
`when`(iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended()).thenReturn(iobBasal)
|
||||||
|
assertEquals(23.9, loopHub.insulinBasalOnboard, 1e-10)
|
||||||
|
verify(iobCobCalculator, times(1)).calculateIobFromTempBasalsIncludingConvertedExtended()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCarbsOnBoard() {
|
||||||
|
val cobInfo = CobInfo(0, 12.0, 0.0)
|
||||||
|
`when`(overviewData.cobInfo(iobCobCalculator)).thenReturn(cobInfo)
|
||||||
|
assertEquals(12.0, loopHub.carbsOnboard)
|
||||||
|
verify(overviewData, times(1)).cobInfo(iobCobCalculator)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testIsConnected() {
|
fun testIsConnected() {
|
||||||
`when`(loop.isDisconnected).thenReturn(false)
|
`when`(loop.isDisconnected).thenReturn(false)
|
||||||
|
@ -247,4 +267,4 @@ class LoopHubTest: TestBase() {
|
||||||
samplingStart, samplingEnd, 101, "Test Device")
|
samplingStart, samplingEnd, 101, "Test Device")
|
||||||
verify(repo).runTransaction(InsertOrUpdateHeartRateTransaction(hr))
|
verify(repo).runTransaction(InsertOrUpdateHeartRateTransaction(hr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,6 @@ class MedtronicPumpHistoryDecoder @Inject constructor(
|
||||||
PumpHistoryEntryType.ClearAlarm,
|
PumpHistoryEntryType.ClearAlarm,
|
||||||
PumpHistoryEntryType.ChangeAlarmNotifyMode,
|
PumpHistoryEntryType.ChangeAlarmNotifyMode,
|
||||||
PumpHistoryEntryType.EnableDisableRemote,
|
PumpHistoryEntryType.EnableDisableRemote,
|
||||||
PumpHistoryEntryType.BGReceived,
|
|
||||||
PumpHistoryEntryType.SensorAlert,
|
PumpHistoryEntryType.SensorAlert,
|
||||||
PumpHistoryEntryType.ChangeTimeFormat,
|
PumpHistoryEntryType.ChangeTimeFormat,
|
||||||
PumpHistoryEntryType.ChangeReservoirWarningTime,
|
PumpHistoryEntryType.ChangeReservoirWarningTime,
|
||||||
|
@ -188,7 +187,6 @@ class MedtronicPumpHistoryDecoder @Inject constructor(
|
||||||
PumpHistoryEntryType.ChangeWatchdogEnable,
|
PumpHistoryEntryType.ChangeWatchdogEnable,
|
||||||
PumpHistoryEntryType.ChangeOtherDeviceID,
|
PumpHistoryEntryType.ChangeOtherDeviceID,
|
||||||
PumpHistoryEntryType.ReadOtherDevicesIDs,
|
PumpHistoryEntryType.ReadOtherDevicesIDs,
|
||||||
PumpHistoryEntryType.BGReceived512,
|
|
||||||
PumpHistoryEntryType.SensorStatus,
|
PumpHistoryEntryType.SensorStatus,
|
||||||
PumpHistoryEntryType.ReadCaptureEventEnabled,
|
PumpHistoryEntryType.ReadCaptureEventEnabled,
|
||||||
PumpHistoryEntryType.ChangeCaptureEventEnable,
|
PumpHistoryEntryType.ChangeCaptureEventEnable,
|
||||||
|
@ -206,6 +204,12 @@ class MedtronicPumpHistoryDecoder @Inject constructor(
|
||||||
PumpHistoryEntryType.UnabsorbedInsulin,
|
PumpHistoryEntryType.UnabsorbedInsulin,
|
||||||
PumpHistoryEntryType.UnabsorbedInsulin512 -> RecordDecodeStatus.Ignored
|
PumpHistoryEntryType.UnabsorbedInsulin512 -> RecordDecodeStatus.Ignored
|
||||||
|
|
||||||
|
PumpHistoryEntryType.BGReceived,
|
||||||
|
PumpHistoryEntryType.BGReceived512 -> {
|
||||||
|
decodeBgReceived(entry)
|
||||||
|
RecordDecodeStatus.OK
|
||||||
|
}
|
||||||
|
|
||||||
PumpHistoryEntryType.DailyTotals522,
|
PumpHistoryEntryType.DailyTotals522,
|
||||||
PumpHistoryEntryType.DailyTotals523,
|
PumpHistoryEntryType.DailyTotals523,
|
||||||
PumpHistoryEntryType.DailyTotals515,
|
PumpHistoryEntryType.DailyTotals515,
|
||||||
|
@ -297,7 +301,9 @@ class MedtronicPumpHistoryDecoder @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeBatteryActivity(entry: PumpHistoryEntry) {
|
private fun decodeBatteryActivity(entry: PumpHistoryEntry) {
|
||||||
entry.displayableValue = if (entry.head[0] == 0.toByte()) "Battery Removed" else "Battery Replaced"
|
val isRemoved = entry.head[0] == 0.toByte()
|
||||||
|
entry.addDecodedData("isRemoved", isRemoved)
|
||||||
|
entry.displayableValue = if (isRemoved) "Battery Removed" else "Battery Replaced"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeBasalProfileStart(entry: PumpHistoryEntry): RecordDecodeStatus {
|
private fun decodeBasalProfileStart(entry: PumpHistoryEntry): RecordDecodeStatus {
|
||||||
|
@ -407,8 +413,11 @@ class MedtronicPumpHistoryDecoder @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeBgReceived(entry: PumpHistoryEntry) {
|
private fun decodeBgReceived(entry: PumpHistoryEntry) {
|
||||||
entry.addDecodedData("amount", (ByteUtil.asUINT8(entry.getRawDataByIndex(0)) shl 3) + (ByteUtil.asUINT8(entry.getRawDataByIndex(3)) shr 5))
|
val glucoseMgdl = (ByteUtil.asUINT8(entry.head[0]) shl 3) + (ByteUtil.asUINT8(entry.datetime[2]) shr 5)
|
||||||
entry.addDecodedData("meter", ByteUtil.substring(entry.rawData, 6, 3)) // index moved from 1 -> 0
|
val meterSerial = ByteUtil.shortHexStringWithoutSpaces(entry.body)
|
||||||
|
entry.addDecodedData("GlucoseMgdl", glucoseMgdl)
|
||||||
|
entry.addDecodedData("MeterSerial", meterSerial)
|
||||||
|
entry.displayableValue = String.format("Glucose: %d mg/dl, Meter Serial: %s", glucoseMgdl, meterSerial)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeCalBGForPH(entry: PumpHistoryEntry) {
|
private fun decodeCalBGForPH(entry: PumpHistoryEntry) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import app.aaps.core.interfaces.logging.AAPSLogger
|
||||||
import app.aaps.core.interfaces.logging.LTag
|
import app.aaps.core.interfaces.logging.LTag
|
||||||
import app.aaps.core.interfaces.notifications.Notification
|
import app.aaps.core.interfaces.notifications.Notification
|
||||||
import app.aaps.core.interfaces.plugin.ActivePlugin
|
import app.aaps.core.interfaces.plugin.ActivePlugin
|
||||||
|
import app.aaps.core.interfaces.profile.ProfileUtil
|
||||||
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
import app.aaps.core.interfaces.pump.DetailedBolusInfo
|
||||||
import app.aaps.core.interfaces.pump.PumpSync
|
import app.aaps.core.interfaces.pump.PumpSync
|
||||||
import app.aaps.core.interfaces.pump.defs.PumpType
|
import app.aaps.core.interfaces.pump.defs.PumpType
|
||||||
|
@ -67,7 +68,8 @@ class MedtronicHistoryData @Inject constructor(
|
||||||
val medtronicPumpStatus: MedtronicPumpStatus,
|
val medtronicPumpStatus: MedtronicPumpStatus,
|
||||||
private val pumpSync: PumpSync,
|
private val pumpSync: PumpSync,
|
||||||
private val pumpSyncStorage: PumpSyncStorage,
|
private val pumpSyncStorage: PumpSyncStorage,
|
||||||
private val uiInteraction: UiInteraction
|
private val uiInteraction: UiInteraction,
|
||||||
|
private val profileUtil: ProfileUtil
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val allHistory: MutableList<PumpHistoryEntry> = mutableListOf()
|
val allHistory: MutableList<PumpHistoryEntry> = mutableListOf()
|
||||||
|
@ -322,6 +324,17 @@ class MedtronicHistoryData @Inject constructor(
|
||||||
* Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime)
|
* Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime)
|
||||||
*/
|
*/
|
||||||
fun processNewHistoryData() {
|
fun processNewHistoryData() {
|
||||||
|
// Finger BG (for adding entry to careportal)
|
||||||
|
val bgRecords: MutableList<PumpHistoryEntry> = getFilteredItems(setOf(PumpHistoryEntryType.BGReceived, PumpHistoryEntryType.BGReceived512))
|
||||||
|
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: BGReceived [count=%d, items=%s]", bgRecords.size, gson.toJson(bgRecords)))
|
||||||
|
if (isCollectionNotEmpty(bgRecords)) {
|
||||||
|
try {
|
||||||
|
processBgReceived(bgRecords)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing BGReceived entries: " + ex.message, ex)
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prime (for resetting autosense)
|
// Prime (for resetting autosense)
|
||||||
val primeRecords: MutableList<PumpHistoryEntry> = getFilteredItems(PumpHistoryEntryType.Prime)
|
val primeRecords: MutableList<PumpHistoryEntry> = getFilteredItems(PumpHistoryEntryType.Prime)
|
||||||
|
@ -347,6 +360,18 @@ class MedtronicHistoryData @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatteryChange
|
||||||
|
val batteryChangeRecords: MutableList<PumpHistoryEntry> = getFilteredItems(PumpHistoryEntryType.BatteryChange)
|
||||||
|
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: BatteryChange [count=%d, items=%s]", batteryChangeRecords.size, gson.toJson(batteryChangeRecords)))
|
||||||
|
if (isCollectionNotEmpty(batteryChangeRecords)) {
|
||||||
|
try {
|
||||||
|
processBatteryChange(batteryChangeRecords)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing BatteryChange entries: " + ex.message, ex)
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TDD
|
// TDD
|
||||||
val tdds: MutableList<PumpHistoryEntry> = getFilteredItems(setOf(PumpHistoryEntryType.EndResultTotals, getTDDType()))
|
val tdds: MutableList<PumpHistoryEntry> = getFilteredItems(setOf(PumpHistoryEntryType.EndResultTotals, getTDDType()))
|
||||||
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TDD [count=%d, items=%s]", tdds.size, gson.toJson(tdds)))
|
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TDD [count=%d, items=%s]", tdds.size, gson.toJson(tdds)))
|
||||||
|
@ -407,6 +432,34 @@ class MedtronicHistoryData @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun processBgReceived(bgRecords: List<PumpHistoryEntry>) {
|
||||||
|
for (bgRecord in bgRecords) {
|
||||||
|
val glucoseMgdl = bgRecord.getDecodedDataEntry("GlucoseMgdl")
|
||||||
|
if (glucoseMgdl == null || glucoseMgdl as Int == 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val glucose = profileUtil.fromMgdlToUnits(glucoseMgdl.toDouble())
|
||||||
|
val glucoseUnit = profileUtil.units
|
||||||
|
|
||||||
|
val result = pumpSync.insertFingerBgIfNewWithTimestamp(
|
||||||
|
DateTimeUtil.toMillisFromATD(bgRecord.atechDateTime),
|
||||||
|
glucose, glucoseUnit, null,
|
||||||
|
bgRecord.pumpId,
|
||||||
|
medtronicPumpStatus.pumpType,
|
||||||
|
medtronicPumpStatus.serialNumber
|
||||||
|
)
|
||||||
|
|
||||||
|
aapsLogger.debug(
|
||||||
|
LTag.PUMP, String.format(
|
||||||
|
Locale.ROOT, "insertFingerBgIfNewWithTimestamp [date=%d, glucose=%f, glucoseUnit=%s, pumpId=%d, pumpSerial=%s] - Result: %b",
|
||||||
|
bgRecord.atechDateTime, glucose, glucoseUnit, bgRecord.pumpId,
|
||||||
|
medtronicPumpStatus.serialNumber, result
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun processPrime(primeRecords: List<PumpHistoryEntry>) {
|
private fun processPrime(primeRecords: List<PumpHistoryEntry>) {
|
||||||
val maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(GregorianCalendar(), -30)
|
val maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(GregorianCalendar(), -30)
|
||||||
var lastPrimeRecordTime = 0L
|
var lastPrimeRecordTime = 0L
|
||||||
|
@ -456,6 +509,35 @@ class MedtronicHistoryData @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processBatteryChange(batteryChangeRecords: List<PumpHistoryEntry>) {
|
||||||
|
val maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(GregorianCalendar(), -120)
|
||||||
|
var lastBatteryChangeRecordTime = 0L
|
||||||
|
var lastBatteryChangeRecord: PumpHistoryEntry? = null
|
||||||
|
for (batteryChangeRecord in batteryChangeRecords) {
|
||||||
|
val isRemoved = batteryChangeRecord.getDecodedDataEntry("isRemoved")
|
||||||
|
|
||||||
|
if (isRemoved != null && isRemoved as Boolean)
|
||||||
|
{
|
||||||
|
// we're interested in battery replacements, not battery removals
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batteryChangeRecord.atechDateTime > maxAllowedTimeInPast) {
|
||||||
|
if (lastBatteryChangeRecordTime < batteryChangeRecord.atechDateTime) {
|
||||||
|
lastBatteryChangeRecordTime = batteryChangeRecord.atechDateTime
|
||||||
|
lastBatteryChangeRecord = batteryChangeRecord
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastBatteryChangeRecord != null) {
|
||||||
|
uploadCareportalEventIfFoundInHistory(
|
||||||
|
lastBatteryChangeRecord,
|
||||||
|
MedtronicConst.Statistics.LastBatteryChange,
|
||||||
|
DetailedBolusInfo.EventType.PUMP_BATTERY_CHANGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun uploadCareportalEventIfFoundInHistory(historyRecord: PumpHistoryEntry, eventSP: String, eventType: DetailedBolusInfo.EventType) {
|
private fun uploadCareportalEventIfFoundInHistory(historyRecord: PumpHistoryEntry, eventSP: String, eventType: DetailedBolusInfo.EventType) {
|
||||||
val lastPrimeFromAAPS = sp.getLong(eventSP, 0L)
|
val lastPrimeFromAAPS = sp.getLong(eventSP, 0L)
|
||||||
if (historyRecord.atechDateTime != lastPrimeFromAAPS) {
|
if (historyRecord.atechDateTime != lastPrimeFromAAPS) {
|
||||||
|
|
|
@ -30,5 +30,6 @@ object MedtronicConst {
|
||||||
const val LastPumpHistoryEntry = StatsPrefix + "pump_history_entry"
|
const val LastPumpHistoryEntry = StatsPrefix + "pump_history_entry"
|
||||||
const val LastPrime = StatsPrefix + "last_sent_prime"
|
const val LastPrime = StatsPrefix + "last_sent_prime"
|
||||||
const val LastRewind = StatsPrefix + "last_sent_rewind"
|
const val LastRewind = StatsPrefix + "last_sent_rewind"
|
||||||
|
const val LastBatteryChange = StatsPrefix + "last_sent_battery_change"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ import app.aaps.core.interfaces.plugin.ActivePlugin
|
||||||
import app.aaps.core.interfaces.pump.PumpSync
|
import app.aaps.core.interfaces.pump.PumpSync
|
||||||
import app.aaps.core.interfaces.resources.ResourceHelper
|
import app.aaps.core.interfaces.resources.ResourceHelper
|
||||||
import app.aaps.core.interfaces.sharedPreferences.SP
|
import app.aaps.core.interfaces.sharedPreferences.SP
|
||||||
import app.aaps.shared.tests.TestBase
|
import app.aaps.shared.tests.TestBaseWithProfile
|
||||||
import dagger.android.AndroidInjector
|
import dagger.android.AndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil
|
||||||
|
@ -16,15 +16,13 @@ import info.nightscout.pump.common.sync.PumpSyncStorage
|
||||||
import org.mockito.Answers
|
import org.mockito.Answers
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
|
||||||
open class MedtronicTestBase : TestBase() {
|
open class MedtronicTestBase : TestBaseWithProfile() {
|
||||||
|
|
||||||
var rileyLinkUtil = RileyLinkUtil()
|
var rileyLinkUtil = RileyLinkUtil()
|
||||||
|
|
||||||
@Mock lateinit var pumpSync: PumpSync
|
@Mock lateinit var pumpSync: PumpSync
|
||||||
@Mock lateinit var pumpSyncStorage: PumpSyncStorage
|
@Mock lateinit var pumpSyncStorage: PumpSyncStorage
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS) lateinit var activePlugin: ActivePlugin
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS) override lateinit var activePlugin: ActivePlugin
|
||||||
@Mock lateinit var sp: SP
|
|
||||||
@Mock lateinit var rh: ResourceHelper
|
|
||||||
|
|
||||||
lateinit var medtronicUtil: MedtronicUtil
|
lateinit var medtronicUtil: MedtronicUtil
|
||||||
lateinit var decoder: MedtronicPumpHistoryDecoder
|
lateinit var decoder: MedtronicPumpHistoryDecoder
|
||||||
|
@ -53,6 +51,24 @@ open class MedtronicTestBase : TestBase() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPumpHistoryEntryFromData(vararg elements: Int): PumpHistoryEntry {
|
||||||
|
val data: MutableList<Byte> = ArrayList()
|
||||||
|
for (item in elements) {
|
||||||
|
var b = if (item > 128) item - 256 else item
|
||||||
|
data.add(b.toByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
val entryType = PumpHistoryEntryType.getByCode(data[0])
|
||||||
|
|
||||||
|
val phe = PumpHistoryEntry()
|
||||||
|
phe.setEntryType(medtronicUtil.medtronicPumpModel, entryType)
|
||||||
|
phe.setData(data, false)
|
||||||
|
|
||||||
|
decoder.decodeRecord(phe)
|
||||||
|
|
||||||
|
return phe
|
||||||
|
}
|
||||||
|
|
||||||
private fun preProcessTBRs(tbrsInput: MutableList<PumpHistoryEntry>): MutableList<PumpHistoryEntry> {
|
private fun preProcessTBRs(tbrsInput: MutableList<PumpHistoryEntry>): MutableList<PumpHistoryEntry> {
|
||||||
val tbrs: MutableList<PumpHistoryEntry> = mutableListOf()
|
val tbrs: MutableList<PumpHistoryEntry> = mutableListOf()
|
||||||
val map: MutableMap<String?, PumpHistoryEntry?> = HashMap()
|
val map: MutableMap<String?, PumpHistoryEntry?> = HashMap()
|
||||||
|
|
|
@ -40,7 +40,7 @@ import org.mockito.Mock
|
||||||
decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil)
|
decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil)
|
||||||
medtronicHistoryData = MedtronicHistoryData(
|
medtronicHistoryData = MedtronicHistoryData(
|
||||||
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
||||||
medtronicUtil, decoder, medtronicPumpStatus, pumpSync, pumpSyncStorage, uiInteraction
|
medtronicUtil, decoder, medtronicPumpStatus, pumpSync, pumpSyncStorage, uiInteraction, profileUtil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump
|
||||||
|
|
||||||
|
import app.aaps.core.interfaces.ui.UiInteraction
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicTestBase
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicTestBase
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by andy on 4/9/19.
|
* Created by andy on 4/9/19.
|
||||||
|
@ -10,6 +18,16 @@ import org.junit.jupiter.api.Test
|
||||||
*/
|
*/
|
||||||
class PumpHistoryEntryUTest : MedtronicTestBase() {
|
class PumpHistoryEntryUTest : MedtronicTestBase() {
|
||||||
|
|
||||||
|
@Mock lateinit var medtronicPumpStatus: MedtronicPumpStatus
|
||||||
|
@Mock lateinit var uiInteraction: UiInteraction
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setUp() {
|
||||||
|
medtronicUtil = MedtronicUtil(aapsLogger, rxBus, rileyLinkUtil, medtronicPumpStatus, uiInteraction)
|
||||||
|
`when`(medtronicUtil.medtronicPumpModel).thenReturn(MedtronicDeviceType.Medtronic_723_Revel)
|
||||||
|
decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun checkIsAfter() {
|
fun checkIsAfter() {
|
||||||
val dateObject = 20191010000000L
|
val dateObject = 20191010000000L
|
||||||
|
@ -18,4 +36,21 @@ class PumpHistoryEntryUTest : MedtronicTestBase() {
|
||||||
phe.atechDateTime = dateObject
|
phe.atechDateTime = dateObject
|
||||||
assertThat(phe.isAfter(queryObject)).isTrue()
|
assertThat(phe.isAfter(queryObject)).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decodeBgReceived() {
|
||||||
|
val bgRecord = getPumpHistoryEntryFromData(
|
||||||
|
// head
|
||||||
|
0x39, 0x15,
|
||||||
|
// datetime (combined with glucose in mg/dl)
|
||||||
|
0xC2, 0x25, 0xF3, 0x61, 0x17,
|
||||||
|
// serial number
|
||||||
|
0x12, 0x34, 0x56
|
||||||
|
)
|
||||||
|
val expectedGlucoseMgdl = 175
|
||||||
|
val expectedMeterSerial = "123456"
|
||||||
|
|
||||||
|
assertThat(bgRecord.getDecodedDataEntry("GlucoseMgdl")).isEqualTo(expectedGlucoseMgdl)
|
||||||
|
assertThat(bgRecord.getDecodedDataEntry("MeterSerial")).isEqualTo(expectedMeterSerial)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.medtronic.data
|
package info.nightscout.androidaps.plugins.pump.medtronic.data
|
||||||
|
|
||||||
|
import app.aaps.core.interfaces.db.GlucoseUnit
|
||||||
import app.aaps.core.interfaces.ui.UiInteraction
|
import app.aaps.core.interfaces.ui.UiInteraction
|
||||||
|
import app.aaps.core.utils.DateTimeUtil
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.internal.LinkedTreeMap
|
import com.google.gson.internal.LinkedTreeMap
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicTestBase
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicTestBase
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder
|
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry
|
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair
|
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus
|
import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.`when`
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@ -24,6 +30,7 @@ class MedtronicHistoryDataUTest : MedtronicTestBase() {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
medtronicUtil = MedtronicUtil(aapsLogger, rxBus, rileyLinkUtil, medtronicPumpStatus, uiInteraction)
|
medtronicUtil = MedtronicUtil(aapsLogger, rxBus, rileyLinkUtil, medtronicPumpStatus, uiInteraction)
|
||||||
|
`when`(medtronicUtil.medtronicPumpModel).thenReturn(MedtronicDeviceType.Medtronic_723_Revel)
|
||||||
decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil)
|
decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +39,7 @@ class MedtronicHistoryDataUTest : MedtronicTestBase() {
|
||||||
|
|
||||||
val unitToTest = MedtronicHistoryData(
|
val unitToTest = MedtronicHistoryData(
|
||||||
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
||||||
medtronicUtil, decoder, medtronicPumpStatus, pumpSync, pumpSyncStorage, uiInteraction
|
medtronicUtil, decoder, medtronicPumpStatus, pumpSync, pumpSyncStorage, uiInteraction, profileUtil
|
||||||
)
|
)
|
||||||
|
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
|
@ -75,7 +82,7 @@ class MedtronicHistoryDataUTest : MedtronicTestBase() {
|
||||||
medtronicUtil, decoder,
|
medtronicUtil, decoder,
|
||||||
medtronicPumpStatus,
|
medtronicPumpStatus,
|
||||||
pumpSync,
|
pumpSync,
|
||||||
pumpSyncStorage, uiInteraction
|
pumpSyncStorage, uiInteraction, profileUtil
|
||||||
)
|
)
|
||||||
|
|
||||||
val gson = Gson()
|
val gson = Gson()
|
||||||
|
@ -110,4 +117,70 @@ class MedtronicHistoryDataUTest : MedtronicTestBase() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun processBgReceived_WithMgdl() {
|
||||||
|
|
||||||
|
val unitToTest = MedtronicHistoryData(
|
||||||
|
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
||||||
|
medtronicUtil, decoder,
|
||||||
|
medtronicPumpStatus,
|
||||||
|
pumpSync,
|
||||||
|
pumpSyncStorage, uiInteraction, profileUtil
|
||||||
|
)
|
||||||
|
|
||||||
|
val glucoseMgdl = 175
|
||||||
|
|
||||||
|
`when`(sp.getString(app.aaps.core.utils.R.string.key_units, GlucoseUnit.MGDL.asText)).thenReturn(GlucoseUnit.MGDL.asText)
|
||||||
|
|
||||||
|
val bgRecord = PumpHistoryEntry()
|
||||||
|
bgRecord.setEntryType(medtronicUtil.medtronicPumpModel, PumpHistoryEntryType.BGReceived)
|
||||||
|
bgRecord.addDecodedData("GlucoseMgdl", glucoseMgdl)
|
||||||
|
bgRecord.addDecodedData("MeterSerial", "123456")
|
||||||
|
|
||||||
|
unitToTest.processBgReceived(listOf(bgRecord))
|
||||||
|
|
||||||
|
verify(pumpSync).insertFingerBgIfNewWithTimestamp(
|
||||||
|
DateTimeUtil.toMillisFromATD(bgRecord.atechDateTime),
|
||||||
|
glucoseMgdl.toDouble(),
|
||||||
|
GlucoseUnit.MGDL, null,
|
||||||
|
bgRecord.pumpId,
|
||||||
|
medtronicPumpStatus.pumpType,
|
||||||
|
medtronicPumpStatus.serialNumber
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun processBgReceived_WithMmol() {
|
||||||
|
|
||||||
|
val unitToTest = MedtronicHistoryData(
|
||||||
|
packetInjector, aapsLogger, sp, rh, rxBus, activePlugin,
|
||||||
|
medtronicUtil, decoder,
|
||||||
|
medtronicPumpStatus,
|
||||||
|
pumpSync,
|
||||||
|
pumpSyncStorage, uiInteraction, profileUtil
|
||||||
|
)
|
||||||
|
val glucoseMgdl = 180
|
||||||
|
val glucoseMmol = 10.0
|
||||||
|
|
||||||
|
`when`(sp.getString(app.aaps.core.utils.R.string.key_units, GlucoseUnit.MGDL.asText)).thenReturn(GlucoseUnit.MMOL.asText)
|
||||||
|
|
||||||
|
val bgRecord = PumpHistoryEntry()
|
||||||
|
bgRecord.setEntryType(medtronicUtil.medtronicPumpModel, PumpHistoryEntryType.BGReceived)
|
||||||
|
bgRecord.addDecodedData("GlucoseMgdl", glucoseMgdl)
|
||||||
|
bgRecord.addDecodedData("MeterSerial", "123456")
|
||||||
|
|
||||||
|
unitToTest.processBgReceived(listOf(bgRecord))
|
||||||
|
|
||||||
|
verify(pumpSync).insertFingerBgIfNewWithTimestamp(
|
||||||
|
DateTimeUtil.toMillisFromATD(bgRecord.atechDateTime),
|
||||||
|
glucoseMmol,
|
||||||
|
GlucoseUnit.MMOL, null,
|
||||||
|
bgRecord.pumpId,
|
||||||
|
medtronicPumpStatus.pumpType,
|
||||||
|
medtronicPumpStatus.serialNumber
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,7 @@
|
||||||
<string name="omnipod_common_pod_status_activation_time_exceeded">Activation time exceeded</string>
|
<string name="omnipod_common_pod_status_activation_time_exceeded">Activation time exceeded</string>
|
||||||
<string name="omnipod_common_pod_status_inactive">Inactive</string>
|
<string name="omnipod_common_pod_status_inactive">Inactive</string>
|
||||||
<string name="omnipod_common_pod_status_pod_fault_description">Pod Fault: %1$03d %2$s</string>
|
<string name="omnipod_common_pod_status_pod_fault_description">Pod Fault: %1$03d %2$s</string>
|
||||||
|
<string name="omnipod_common_pod_status_normal">Normal</string>
|
||||||
|
|
||||||
<!-- Omnipod - Commands -->
|
<!-- Omnipod - Commands -->
|
||||||
<string name="omnipod_common_cmd_deactivate_pod">Deactivate Pod</string>
|
<string name="omnipod_common_cmd_deactivate_pod">Deactivate Pod</string>
|
||||||
|
|
|
@ -991,9 +991,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
val extended = JSONObject()
|
val extended = JSONObject()
|
||||||
try {
|
try {
|
||||||
val podStatus = when {
|
val podStatus = when {
|
||||||
podStateManager.isPodRunning && podStateManager.isSuspended -> "suspended"
|
podStateManager.isPodRunning && podStateManager.isSuspended -> rh.gs(info.nightscout.androidaps.plugins.pump.omnipod.common.R.string.omnipod_common_pod_status_suspended).lowercase()
|
||||||
podStateManager.isPodRunning -> "normal"
|
podStateManager.isPodRunning -> rh.gs(info.nightscout.androidaps.plugins.pump.omnipod.common.R.string.omnipod_common_pod_status_normal).lowercase()
|
||||||
else -> "no active Pod"
|
else -> rh.gs(info.nightscout.androidaps.plugins.pump.omnipod.common.R.string.omnipod_common_pod_status_no_active_pod).lowercase()
|
||||||
}
|
}
|
||||||
status.put("status", podStatus)
|
status.put("status", podStatus)
|
||||||
status.put("timestamp", dateUtil.toISOString(podStateManager.lastUpdatedSystem))
|
status.put("timestamp", dateUtil.toISOString(podStateManager.lastUpdatedSystem))
|
||||||
|
|
|
@ -36,7 +36,7 @@ import org.mockito.invocation.InvocationOnMock
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
open class TestBaseWithProfile : TestBase() {
|
open class TestBaseWithProfile : TestBase() {
|
||||||
|
|
||||||
@Mock lateinit var activePlugin: ActivePlugin
|
@Mock open lateinit var activePlugin: ActivePlugin
|
||||||
@Mock lateinit var rh: ResourceHelper
|
@Mock lateinit var rh: ResourceHelper
|
||||||
@Mock lateinit var iobCobCalculator: IobCobCalculator
|
@Mock lateinit var iobCobCalculator: IobCobCalculator
|
||||||
@Mock lateinit var fabricPrivacy: FabricPrivacy
|
@Mock lateinit var fabricPrivacy: FabricPrivacy
|
||||||
|
|
|
@ -86,6 +86,9 @@ android {
|
||||||
versionName = Versions.appVersion + "-aapsclient2"
|
versionName = Versions.appVersion + "-aapsclient2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
|
Loading…
Reference in a new issue