This commit is contained in:
miyeongkim 2021-11-15 17:16:05 +09:00
commit 2c89d8f240
47 changed files with 2637 additions and 256 deletions

View file

@ -48,6 +48,7 @@ import info.nightscout.androidaps.plugins.source.EversensePlugin
import info.nightscout.androidaps.plugins.source.GlimpPlugin
import info.nightscout.androidaps.plugins.source.PoctechPlugin
import info.nightscout.androidaps.plugins.source.TomatoPlugin
import info.nightscout.androidaps.plugins.source.GlunovoPlugin
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.protection.PasswordCheck
@ -90,6 +91,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
@Inject lateinit var glimpPlugin: GlimpPlugin
@Inject lateinit var poctechPlugin: PoctechPlugin
@Inject lateinit var tomatoPlugin: TomatoPlugin
@Inject lateinit var glunovoPlugin: GlunovoPlugin
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var statusLinePlugin: StatusLinePlugin
@Inject lateinit var tidepoolPlugin: TidepoolPlugin
@ -160,6 +162,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
addPreferencesFromResourceIfEnabled(eversensePlugin, rootKey)
addPreferencesFromResourceIfEnabled(dexcomPlugin, rootKey)
addPreferencesFromResourceIfEnabled(tomatoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glunovoPlugin, rootKey)
addPreferencesFromResourceIfEnabled(poctechPlugin, rootKey)
addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey)
addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, config.APS)

View file

@ -353,6 +353,12 @@ abstract class PluginsModule {
@AllConfigs
@IntoMap
@IntKey(470)
abstract fun bindGlunovoPlugin(plugin: GlunovoPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(475)
abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase
// @Binds

View file

@ -28,7 +28,8 @@ class StorageConstraintPlugin @Inject constructor(
aapsLogger: AAPSLogger,
rh: ResourceHelper,
private val rxBus: RxBus
) : PluginBase(PluginDescription()
) : PluginBase(
PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
@ -37,11 +38,12 @@ class StorageConstraintPlugin @Inject constructor(
aapsLogger, rh, injector
), Constraints {
@Suppress("ReplaceGetOrSet")
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
val diskFree = availableInternalMemorySize()
aapsLogger.debug(LTag.CONSTRAINTS, "Internal storage free (Mb):$diskFree")
if (diskFree < Constants.MINIMUM_FREE_SPACE) {
value[aapsLogger, false, rh.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE)] = this
aapsLogger.debug(LTag.CONSTRAINTS, "Internal storage free (Mb):$diskFree")
value.set(aapsLogger, false, rh.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE), this)
val notification = Notification(Notification.DISK_FULL, rh.gs(R.string.diskfull, Constants.MINIMUM_FREE_SPACE), Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
} else {

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.general.nsclient.services
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.*
import androidx.work.OneTimeWorkRequest
import com.google.common.base.Charsets
@ -17,7 +16,6 @@ import info.nightscout.androidaps.events.EventConfigBuilderChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
@ -41,12 +39,12 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.JsonHelper.safeGetStringAllowNull
import info.nightscout.androidaps.utils.T.Companion.mins
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -80,6 +78,7 @@ class NSClientService : DaggerService() {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var repository: AppRepository
@Inject lateinit var xDripBroadcast: XDripBroadcast
companion object {
@ -476,15 +475,7 @@ class NSClientService : DaggerService() {
OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java)
.setInputData(dataWorker.storeInputData(profileStoreJson, null))
.build())
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
val bundle = Bundle()
bundle.putString("profile", profileStoreJson.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_NEW_PROFILE)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
broadcast(intent)
}
xDripBroadcast.sendProfile(profileStoreJson)
}
}
if (data.has("treatments")) {
@ -502,18 +493,7 @@ class NSClientService : DaggerService() {
OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java)
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
.build())
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
val splitted = splitArray(addedOrUpdatedTreatments)
for (part in splitted) {
val bundle = Bundle()
bundle.putString("treatments", part.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_CHANGED_TREATMENT)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
broadcast(intent)
}
}
xDripBroadcast.sendTreatments(addedOrUpdatedTreatments)
}
}
if (data.has("devicestatus")) {
@ -550,18 +530,7 @@ class NSClientService : DaggerService() {
dataWorker.enqueue(OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java)
.setInputData(dataWorker.storeInputData(sgvs, null))
.build())
val splitted = splitArray(sgvs)
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
for (part in splitted) {
val bundle = Bundle()
bundle.putString("sgvs", part.toString())
bundle.putBoolean("delta", isDelta)
val intent = Intent(Intents.ACTION_NEW_SGV)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
broadcast(intent)
}
}
xDripBroadcast.sendSgvs(sgvs)
}
rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
} catch (e: JSONException) {
@ -675,40 +644,6 @@ class NSClientService : DaggerService() {
}
}
private fun splitArray(array: JSONArray): List<JSONArray> {
var ret: MutableList<JSONArray> = ArrayList()
try {
val size = array.length()
var count = 0
var newarr: JSONArray? = null
for (i in 0 until size) {
if (count == 0) {
if (newarr != null) ret.add(newarr)
newarr = JSONArray()
count = 20
}
newarr?.put(array[i])
--count
}
if (newarr != null && newarr.length() > 0) ret.add(newarr)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
ret = ArrayList()
ret.add(array)
}
return ret
}
private fun broadcast(intent: Intent) {
val receivers: List<ResolveInfo> = packageManager.queryBroadcastReceivers(intent, 0)
for (resolveInfo in receivers)
resolveInfo.activityInfo.packageName?.let {
intent.setPackage(it)
sendBroadcast(intent)
aapsLogger.debug(LTag.CORE, "Sending broadcast " + intent.action + " to: " + it)
}
}
init {
if (handler == null) {
val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler")

View file

@ -42,7 +42,6 @@ import info.nightscout.androidaps.interfaces.ActivePlugin;
import info.nightscout.androidaps.interfaces.Config;
import info.nightscout.androidaps.interfaces.GlucoseUnit;
import info.nightscout.androidaps.interfaces.IobCobCalculator;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.Profile;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.logging.AAPSLogger;

View file

@ -537,7 +537,11 @@ class IobCobCalculatorPlugin @Inject constructor(
for (pos in extendedBoluses.indices) {
val e = extendedBoluses[pos]
if (e.timestamp > toTime) continue
if (e.end > now) e.duration = now - e.timestamp
if (e.end > now) {
val newDuration = now - e.timestamp
e.amount *= newDuration.toDouble() / e.duration
e.duration = newDuration
}
val profile = profileFunction.getProfile(e.timestamp) ?: return total
val calc = e.iobCalc(toTime, profile, activePlugin.activeInsulin)
total.plus(calc)
@ -632,7 +636,11 @@ class IobCobCalculatorPlugin @Inject constructor(
val e = extendedBoluses[pos]
if (e.timestamp > toTime) continue
val profile = profileFunction.getProfile(e.timestamp) ?: continue
if (e.end > now) e.duration = now - e.timestamp
if (e.end > now) {
val newDuration = now - e.timestamp
e.amount *= newDuration.toDouble() / e.duration
e.duration = newDuration
}
val calc = e.iobCalc(toTime, profile, activePlugin.activeInsulin)
totalExt.plus(calc)
}
@ -667,7 +675,11 @@ class IobCobCalculatorPlugin @Inject constructor(
val e = extendedBoluses[pos]
if (e.timestamp > toTime) continue
val profile = profileFunction.getProfile(e.timestamp) ?: continue
if (e.end > now) e.duration = now - e.timestamp
if (e.end > now) {
val newDuration = now - e.timestamp
e.amount *= newDuration.toDouble() / e.duration
e.duration = newDuration
}
val calc = e.iobCalc(toTime, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget, activePlugin.activeInsulin)
totalExt.plus(calc)
}

View file

@ -140,6 +140,7 @@ class BGSourceFragment : DaggerFragment() {
R.string.nsclientbg -> Sources.NSClientSource
R.string.poctech -> Sources.PocTech
R.string.tomato -> Sources.Tomato
R.string.glunovo -> Sources.Glunovo
R.string.xdrip -> Sources.Xdrip
else -> Sources.Unknown
}

View file

@ -0,0 +1,163 @@
package info.nightscout.androidaps.plugins.source
import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.HandlerThread
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.interfaces.BgSource
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GlunovoPlugin @Inject constructor(
injector: HasAndroidInjector,
resourceHelper: ResourceHelper,
aapsLogger: AAPSLogger,
private val sp: SP,
private val context: Context,
private val repository: AppRepository,
private val xDripBroadcast: XDripBroadcast,
private val dateUtil: DateUtil,
private val fabricPrivacy: FabricPrivacy
) : PluginBase(
PluginDescription()
.mainType(PluginType.BGSOURCE)
.fragmentClass(BGSourceFragment::class.java.name)
.pluginIcon(R.drawable.ic_glunovo)
.pluginName(R.string.glunovo)
.preferencesId(R.xml.pref_bgsource)
.shortName(R.string.glunovo)
.description(R.string.description_source_glunovo),
aapsLogger, resourceHelper, injector
), BgSource {
private val loopHandler: Handler = Handler(HandlerThread(this::class.java.simpleName + "Handler").also { it.start() }.looper)
private lateinit var refreshLoop: Runnable
private val contentUri: Uri = Uri.parse("content://$AUTHORITY/$TABLE_NAME")
init {
refreshLoop = Runnable {
try {
handleNewData()
} catch (e: Exception) {
fabricPrivacy.logException(e)
aapsLogger.error("Error while processing data", e)
}
val lastReadTimestamp = sp.getLong(R.string.key_last_processed_glunovo_timestamp, 0L)
val differenceToNow = INTERVAL - (dateUtil.now() - lastReadTimestamp) % INTERVAL + T.secs(10).msecs()
loopHandler.postDelayed(refreshLoop, differenceToNow)
}
}
private val disposable = CompositeDisposable()
override fun onStart() {
super.onStart()
loopHandler.postDelayed(refreshLoop, T.secs(30).msecs()) // do not start immediately, app may be still starting
}
override fun onStop() {
super.onStop()
loopHandler.removeCallbacks(refreshLoop)
disposable.clear()
}
private fun handleNewData() {
if (!isEnabled()) return
context.contentResolver.query(contentUri, null, null, null, null)?.let { cr ->
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
val calibrations = mutableListOf<CgmSourceTransaction.Calibration>()
cr.moveToFirst()
while (!cr.isAfterLast) {
val timestamp = cr.getLong(0)
val value = cr.getDouble(1) //value in mmol/l...
val curr = cr.getDouble(2)
// bypass already processed
if (timestamp < sp.getLong(R.string.key_last_processed_glunovo_timestamp, 0L)) {
cr.moveToNext()
continue
}
if (timestamp > dateUtil.now() || timestamp == 0L) {
aapsLogger.error(LTag.BGSOURCE, "Error in received data date/time $timestamp")
cr.moveToNext()
continue
}
if (value < 2 || value > 25) {
aapsLogger.error(LTag.BGSOURCE, "Error in received data value (value out of bounds) $value")
cr.moveToNext()
continue
}
if (curr != 0.0)
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = timestamp,
value = value * Constants.MMOLL_TO_MGDL,
raw = 0.0,
noise = null,
trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.GLUNOVO_NATIVE
)
else
calibrations.add(
CgmSourceTransaction.Calibration(
timestamp = timestamp,
value = value,
glucoseUnit = TherapyEvent.GlucoseUnit.MMOL
)
)
sp.putLong(R.string.key_last_processed_glunovo_timestamp, timestamp)
cr.moveToNext()
}
cr.close()
if (glucoseValues.isNotEmpty() || calibrations.isNotEmpty())
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, null))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving values from Glunovo App", it)
}
.blockingGet()
.also { savedValues ->
savedValues.inserted.forEach {
xDripBroadcast.send(it)
aapsLogger.debug(LTag.DATABASE, "Inserted bg $it")
}
}
}
}
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean =
glucoseValue.sourceSensor == GlucoseValue.SourceSensor.GLUNOVO_NATIVE && sp.getBoolean(R.string.key_dexcomg5_nsupload, false)
companion object {
@Suppress("SpellCheckingInspection")
const val AUTHORITY = "alexpr.co.uk.infinivocgm.cgm_db.CgmExternalProvider/"
const val TABLE_NAME = "CgmReading"
const val INTERVAL = 180000L // 3 min
}
}

View file

@ -60,7 +60,6 @@ class QueueThread internal constructor(
//write time
sp.putLong(R.string.key_btwatchdog_lastbark, System.currentTimeMillis())
//toggle BT
pump.stopConnecting()
pump.disconnect("watchdog")
SystemClock.sleep(1000)
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
@ -112,14 +111,18 @@ class QueueThread internal constructor(
// Pickup 1st command and set performing variable
if (queue.size() > 0) {
queue.pickup()
if (queue.performing() != null) {
aapsLogger.debug(LTag.PUMPQUEUE, "performing " + queue.performing()?.status())
val cont = queue.performing()?.let {
aapsLogger.debug(LTag.PUMPQUEUE, "performing " + it.status())
rxBus.send(EventQueueChanged())
queue.performing()?.execute()
rxBus.send(EventPumpStatusChanged(it.status()))
it.execute()
queue.resetPerforming()
rxBus.send(EventQueueChanged())
lastCommandTime = System.currentTimeMillis()
SystemClock.sleep(100)
true
} ?: false
if (cont) {
continue
}
}

View file

@ -7,12 +7,11 @@ interface Intents {
// AAPS -> Xdrip
const val ACTION_NEW_TREATMENT = "info.nightscout.client.NEW_TREATMENT"
const val ACTION_CHANGED_TREATMENT = "info.nightscout.client.CHANGED_TREATMENT"
const val ACTION_REMOVED_TREATMENT = "info.nightscout.client.REMOVED_TREATMENT"
const val ACTION_NEW_PROFILE = "info.nightscout.client.NEW_PROFILE"
const val ACTION_NEW_SGV = "info.nightscout.client.NEW_SGV"
const val ACTION_NEW_MBG = "info.nightscout.client.NEW_MBG"
const val ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL"
// AAPS -> xDrip 640G mode
const val XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR"
// xDrip -> AAPS
const val ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate"

View file

@ -7,6 +7,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
@ -24,6 +25,7 @@ class XDripBroadcast @Inject constructor(
private val sp: SP
) {
// sent in 640G mode
fun send(glucoseValue: GlucoseValue) {
if (sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
@ -41,7 +43,7 @@ class XDripBroadcast @Inject constructor(
bundle.putString("action", "add")
bundle.putString("collection", "entries")
bundle.putString("data", entriesBody.toString())
val intent = Intent(XDRIP_PLUS_NS_EMULATOR)
val intent = Intent(Intents.XDRIP_PLUS_NS_EMULATOR)
intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
context.sendBroadcast(intent)
val receivers = context.packageManager.queryBroadcastReceivers(intent, 0)
@ -57,7 +59,75 @@ class XDripBroadcast @Inject constructor(
}
}
companion object {
const val XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR"
// sent in NSClient dbaccess mode
fun sendProfile(profileStoreJson: JSONObject) {
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false))
broadcast(
Intent(Intents.ACTION_NEW_PROFILE).apply {
addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
putExtras(Bundle().apply { putString("profile", profileStoreJson.toString()) })
}
)
}
// sent in NSClient dbaccess mode
fun sendTreatments(addedOrUpdatedTreatments: JSONArray) {
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false))
splitArray(addedOrUpdatedTreatments).forEach { part ->
broadcast(
Intent(Intents.ACTION_NEW_TREATMENT).apply {
addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
putExtras(Bundle().apply { putString("treatments", part.toString()) })
}
)
}
}
// sent in NSClient dbaccess mode
fun sendSgvs(sgvs: JSONArray) {
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false))
splitArray(sgvs).forEach { part ->
broadcast(
Intent(Intents.ACTION_NEW_SGV).apply {
addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
putExtras(Bundle().apply { putString("sgvs", part.toString()) })
}
)
}
}
private fun splitArray(array: JSONArray): List<JSONArray> {
var ret: MutableList<JSONArray> = ArrayList()
try {
val size = array.length()
var count = 0
var newarr: JSONArray? = null
for (i in 0 until size) {
if (count == 0) {
if (newarr != null) ret.add(newarr)
newarr = JSONArray()
count = 20
}
newarr?.put(array[i])
--count
}
if (newarr != null && newarr.length() > 0) ret.add(newarr)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
ret = ArrayList()
ret.add(array)
}
return ret
}
private fun broadcast(intent: Intent) {
context.packageManager.queryBroadcastReceivers(intent, 0).forEach { resolveInfo ->
resolveInfo.activityInfo.packageName?.let {
intent.setPackage(it)
context.sendBroadcast(intent)
aapsLogger.debug(LTag.CORE, "Sending broadcast " + intent.action + " to: " + it)
}
}
}
}

View file

@ -807,6 +807,8 @@
<string name="time_elapsed">Time elapsed</string>
<string name="poctech">Poctech</string>
<string name="description_source_poctech">Receive BG values from Poctech app</string>
<string name="glunovo">Glunovo</string>
<string name="description_source_glunovo">Receive values from Glunovo app</string>
<string name="description_source_tomato">Receive BG values from Tomato app (MiaoMiao device)</string>
<string name="key_high_temptarget_raises_sensitivity" translatable="false">high_temptarget_raises_sensitivity</string>
<string name="key_low_temptarget_lowers_sensitivity" translatable="false">low_temptarget_lowers_sensitivity</string>
@ -1132,5 +1134,6 @@
<string name="data_status">BG data status</string>
<string name="recalculated_data_used">Recalculated data used</string>
<string name="bg_too_close">BG too close:\n%1$s\n%2$s</string>
<string name="key_last_processed_glunovo_timestamp" translatable="false">last_processed_glunovo_timestamp</string>
</resources>

View file

@ -46,4 +46,6 @@ class InputDuration(
this.value = value / 60
return this
}
override fun toString(): String = "InputDuration: $value $unit"
}

View file

@ -9,7 +9,7 @@ buildscript {
rxkotlin_version = '2.4.0'
room_version = '2.3.0'
lifecycle_version = '2.3.1'
dagger_version = '2.40'
dagger_version = '2.40.1'
coroutinesVersion = '1.4.1'
activityVersion = '1.3.1'
fragmentktx_version = '1.3.6'

View file

@ -110,7 +110,7 @@ fun extendedBolusFromJson(jsonObject: JSONObject): ExtendedBolus? {
val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null)
if (timestamp == 0L) return null
if (duration == 0L) return null
if (duration == 0L && durationInMilliseconds == 0L) return null
if (amount == 0.0) return null
return ExtendedBolus(
@ -135,7 +135,7 @@ fun ExtendedBolus.iobCalc(time: Long, profile: Profile, insulinInterface: Insuli
val dia = profile.dia
val diaAgo = time - dia * 60 * 60 * 1000
val aboutFiveMinIntervals = ceil(realDuration / 5.0).toInt()
val spacing = realDuration / aboutFiveMinIntervals
val spacing = realDuration / aboutFiveMinIntervals.toDouble()
for (j in 0L until aboutFiveMinIntervals) {
// find middle of the interval
val calcDate = (timestamp + j * spacing * 60 * 1000 + 0.5 * spacing * 60 * 1000).toLong()

View file

@ -134,14 +134,14 @@ fun TemporaryBasal.toStringShort(): String =
fun TemporaryBasal.iobCalc(time: Long, profile: Profile, insulinInterface: Insulin): IobTotal {
val result = IobTotal(time)
val realDuration: Int = getPassedDurationToTimeInMinutes(time)
val realDuration = getPassedDurationToTimeInMinutes(time)
var netBasalAmount = 0.0
if (realDuration > 0) {
var netBasalRate: Double
val dia = profile.dia
val diaAgo = time - dia * 60 * 60 * 1000
val aboutFiveMinIntervals = ceil(realDuration / 5.0).toInt()
val tempBolusSpacing = (realDuration / aboutFiveMinIntervals).toDouble()
val tempBolusSpacing = realDuration / aboutFiveMinIntervals.toDouble()
for (j in 0L until aboutFiveMinIntervals) {
// find middle of the interval
val calcDate = (timestamp + j * tempBolusSpacing * 60 * 1000 + 0.5 * tempBolusSpacing * 60 * 1000).toLong()
@ -175,7 +175,7 @@ fun TemporaryBasal.iobCalc(time: Long, profile: Profile, insulinInterface: Insul
fun TemporaryBasal.iobCalc(time: Long, profile: Profile, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean, insulinInterface: Insulin): IobTotal {
val result = IobTotal(time)
val realDuration: Double = getPassedDurationToTimeInMinutes(time).toDouble()
val realDuration = getPassedDurationToTimeInMinutes(time)
var netBasalAmount = 0.0
var sensitivityRatio = lastAutosensResult.ratio
val normalTarget = 100.0
@ -190,7 +190,7 @@ fun TemporaryBasal.iobCalc(time: Long, profile: Profile, lastAutosensResult: Aut
val dia = profile.dia
val diaAgo = time - dia * 60 * 60 * 1000
val aboutFiveMinIntervals = ceil(realDuration / 5.0).toInt()
val tempBolusSpacing = realDuration / aboutFiveMinIntervals
val tempBolusSpacing = realDuration / aboutFiveMinIntervals.toDouble()
for (j in 0L until aboutFiveMinIntervals) {
// find middle of the interval
val calcDate = (timestamp + j * tempBolusSpacing * 60 * 1000 + 0.5 * tempBolusSpacing * 60 * 1000).toLong()

View file

@ -27,8 +27,13 @@ interface PumpSync {
*
* Call this function when new pump is paired to accept data from new pump
* to prevent overlapping pump histories
* @param endRunning if true end previous running TBR and EB
*/
fun connectNewPump()
// @JvmOverloads and default value impossible on interface
// replace by `fun connectNewPump(endRunning: Boolean = true)` after full conversion to kotlin
fun connectNewPump(endRunning: Boolean)
fun connectNewPump() = connectNewPump(true)
/*
* GENERAL STATUS
@ -55,8 +60,29 @@ interface PumpSync {
*/
data class PumpState(val temporaryBasal: TemporaryBasal?, val extendedBolus: ExtendedBolus?, val bolus: Bolus?, val profile: Profile?) {
data class TemporaryBasal(val timestamp: Long, val duration: Long, val rate: Double, val isAbsolute: Boolean, val type: TemporaryBasalType, val id: Long, val pumpId: Long?)
data class ExtendedBolus(val timestamp: Long, val duration: Long, val amount: Double, val rate: Double)
data class TemporaryBasal @JvmOverloads constructor(
val timestamp: Long,
val duration: Long,
val rate: Double,
val isAbsolute: Boolean,
val type: TemporaryBasalType,
val id: Long,
val pumpId: Long?,
// used only to cancel TBR on pump change
val pumpType: PumpType = PumpType.USER,
val pumpSerial: String = ""
)
data class ExtendedBolus @JvmOverloads constructor(
val timestamp: Long,
val duration: Long,
val amount: Double,
val rate: Double,
// used only to cancel EB on pump change
val pumpType: PumpType = PumpType.USER,
val pumpSerial: String = ""
)
data class Bolus(val timestamp: Long, val amount: Double)
}

View file

@ -61,6 +61,8 @@ class RunningConfiguration @Inject constructor(
// called in NSClient mode only
fun apply(configuration: JSONObject) {
assert(config.NSCLIENT)
if (configuration.has("version")) {
rxBus.send(EventNSClientNewLog("VERSION", "Received AndroidAPS version ${configuration.getString("version")}"))
if (config.VERSION_NAME.startsWith(configuration.getString("version")).not()) {
@ -100,7 +102,7 @@ class RunningConfiguration @Inject constructor(
if (sp.getString(R.string.key_virtualpump_type, "fake") != pumpType) {
sp.putString(R.string.key_virtualpump_type, pumpType)
activePlugin.activePump.pumpDescription.fillFor(PumpType.getByDescription(pumpType))
pumpSync.connectNewPump()
pumpSync.connectNewPump(endRunning = false) // do not end running TBRs, we call this only to accept data properly
aapsLogger.debug(LTag.CORE, "Changing pump type to $pumpType")
}
}

View file

@ -37,7 +37,15 @@ class PumpSyncImplementation @Inject constructor(
private val disposable = CompositeDisposable()
override fun connectNewPump() {
override fun connectNewPump(endRunning: Boolean) {
if (endRunning) {
expectedPumpState().temporaryBasal?.let {
syncStopTemporaryBasalWithPumpId(dateUtil.now(), dateUtil.now(), it.pumpType, it.pumpSerial)
}
expectedPumpState().extendedBolus?.let {
syncStopExtendedBolusWithPumpId(dateUtil.now(), dateUtil.now(), it.pumpType, it.pumpSerial)
}
}
sp.remove(R.string.key_active_pump_type)
sp.remove(R.string.key_active_pump_serial_number)
sp.remove(R.string.key_active_pump_change_timestamp)
@ -77,7 +85,7 @@ class PumpSyncImplementation @Inject constructor(
}
override fun expectedPumpState(): PumpSync.PumpState {
val bolus = repository.getLastBolusRecordWrapped().blockingGet();
val bolus = repository.getLastBolusRecordWrapped().blockingGet()
val temporaryBasal = repository.getTemporaryBasalActiveAt(dateUtil.now()).blockingGet()
val extendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet()
@ -91,7 +99,9 @@ class PumpSyncImplementation @Inject constructor(
rate = temporaryBasal.value.rate,
isAbsolute = temporaryBasal.value.isAbsolute,
type = PumpSync.TemporaryBasalType.fromDbType(temporaryBasal.value.type),
pumpId = temporaryBasal.value.interfaceIDs.pumpId
pumpId = temporaryBasal.value.interfaceIDs.pumpId,
pumpType = temporaryBasal.value.interfaceIDs.pumpType?.let { PumpType.fromDbPumpType(it)} ?: PumpType.USER,
pumpSerial = temporaryBasal.value.interfaceIDs.pumpSerial ?: "",
)
else null,
extendedBolus =
@ -100,7 +110,9 @@ class PumpSyncImplementation @Inject constructor(
timestamp = extendedBolus.value.timestamp,
duration = extendedBolus.value.duration,
amount = extendedBolus.value.amount,
rate = extendedBolus.value.rate
rate = extendedBolus.value.rate,
pumpType = extendedBolus.value.interfaceIDs.pumpType?.let { PumpType.fromDbPumpType(it)} ?: PumpType.USER,
pumpSerial = extendedBolus.value.interfaceIDs.pumpSerial ?: ""
)
else null,
bolus =

View file

@ -359,6 +359,41 @@ enum class PumpType {
fun getByDescription(desc: String): PumpType =
values().firstOrNull { it.description == desc } ?: GENERIC_AAPS
fun fromDbPumpType(pt: InterfaceIDs.PumpType): PumpType =
when (pt) {
InterfaceIDs.PumpType.GENERIC_AAPS -> GENERIC_AAPS
InterfaceIDs.PumpType.CELLNOVO -> CELLNOVO
InterfaceIDs.PumpType.ACCU_CHEK_COMBO -> ACCU_CHEK_COMBO
InterfaceIDs.PumpType.ACCU_CHEK_SPIRIT -> ACCU_CHEK_SPIRIT
InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT -> ACCU_CHEK_INSIGHT_VIRTUAL
InterfaceIDs.PumpType.ACCU_CHEK_INSIGHT_BLUETOOTH -> ACCU_CHEK_INSIGHT
InterfaceIDs.PumpType.ACCU_CHEK_SOLO -> ACCU_CHEK_SOLO
InterfaceIDs.PumpType.ANIMAS_VIBE -> ANIMAS_VIBE
InterfaceIDs.PumpType.ANIMAS_PING -> ANIMAS_PING
InterfaceIDs.PumpType.DANA_R -> DANA_R
InterfaceIDs.PumpType.DANA_R_KOREAN -> DANA_R_KOREAN
InterfaceIDs.PumpType.DANA_RS -> DANA_RS
InterfaceIDs.PumpType.DANA_RS_KOREAN -> DANA_RS_KOREAN
InterfaceIDs.PumpType.DANA_RV2 -> DANA_RV2
InterfaceIDs.PumpType.DANA_I -> DANA_I
InterfaceIDs.PumpType.OMNIPOD_EROS -> OMNIPOD_EROS
InterfaceIDs.PumpType.OMNIPOD_DASH -> OMNIPOD_DASH
InterfaceIDs.PumpType.MEDTRONIC_512_517 -> MEDTRONIC_512_712
InterfaceIDs.PumpType.MEDTRONIC_515_715 -> MEDTRONIC_515_715
InterfaceIDs.PumpType.MEDTRONIC_522_722 -> MEDTRONIC_522_722
InterfaceIDs.PumpType.MEDTRONIC_523_723_REVEL -> MEDTRONIC_523_723_REVEL
InterfaceIDs.PumpType.MEDTRONIC_554_754_VEO -> MEDTRONIC_554_754_VEO
InterfaceIDs.PumpType.MEDTRONIC_640G -> MEDTRONIC_640G
InterfaceIDs.PumpType.TANDEM_T_SLIM -> TANDEM_T_SLIM
InterfaceIDs.PumpType.TANDEM_T_SLIM_G4 -> TANDEM_T_SLIM_G4
InterfaceIDs.PumpType.TANDEM_T_FLEX -> TANDEM_T_FLEX
InterfaceIDs.PumpType.TANDEM_T_SLIM_X2 -> TANDEM_T_SLIM_X2
InterfaceIDs.PumpType.YPSOPUMP -> YPSOPUMP
InterfaceIDs.PumpType.MDI -> MDI
InterfaceIDs.PumpType.USER -> USER
InterfaceIDs.PumpType.DIACONN_G8 -> DIACONN_G8
}
}
constructor(description: String, model: String, parent: PumpType, pumpCapability: PumpCapability? = null, source: Sources? = null) {

View file

@ -265,11 +265,11 @@ public class DateTimeUtil {
}
public static long getATDWithAddedMinutes(long atd, int minutesDiff) {
public static long getATDWithAddedSeconds(Long atd, int addedSeconds) {
GregorianCalendar oldestEntryTime = DateTimeUtil.toGregorianCalendar(atd);
oldestEntryTime.add(Calendar.MINUTE, minutesDiff);
oldestEntryTime.add(Calendar.SECOND, addedSeconds);
return oldestEntryTime.getTimeInMillis();
return toATechDate(oldestEntryTime.getTimeInMillis());
}
@ -279,7 +279,6 @@ public class DateTimeUtil {
return toATechDate(oldestEntryTime);
}
public static long getTimeInFutureFromMinutes(long startTime, int minutes) {
return startTime + getTimeInMs(minutes);
}

View file

@ -70,6 +70,7 @@ class UserEntryPresentationHelper @Inject constructor(
Sources.NSClientSource -> R.drawable.ic_nsclient_bg
Sources.PocTech -> R.drawable.ic_poctech
Sources.Tomato -> R.drawable.ic_sensor
Sources.Glunovo -> R.drawable.ic_glunovo
Sources.Xdrip -> R.drawable.ic_blooddrop_48
Sources.LocalProfile -> R.drawable.ic_local_profile
Sources.Loop -> R.drawable.ic_loop_closed_white

View file

@ -0,0 +1,85 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M51.787,23.533c-0.01,-10.925 -8.888,-19.787 -19.813,-19.777c-10.925,0.01 -19.787,8.889 -19.777,19.814l0.016,16.897c0.01,10.925 8.888,19.787 19.813,19.777c10.925,-0.01 19.787,-8.889 19.777,-19.814l-0.016,-16.897Z"
android:fillColor="#ebeae3"/>
<path
android:pathData="M46.353,25.52c-0.007,-7.925 -6.447,-14.353 -14.372,-14.346c-7.925,0.008 -14.353,6.448 -14.346,14.373l0.015,16.092c0.006,6.181 5.029,11.195 11.21,11.19l6.318,-0.006c6.182,-0.006 11.196,-5.029 11.19,-11.211l-0.015,-16.092Z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="31.4229"
android:startX="17.6405"
android:endY="30.819769"
android:endX="58.322098"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#FFB3B3B3"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M22.928,44.362l-0.021,-22.414c-0.004,-4.01 3.249,-7.27 7.26,-7.273l3.634,-0.004c4.011,-0.003 7.27,3.25 7.274,7.261l0.02,22.413c-0.638,2.068 -1.683,3.934 -3.208,5.56l-11.85,0.011c-1.578,-1.46 -2.517,-3.384 -3.119,-5.554l0.01,-0Z"
android:strokeWidth="0.56"
android:fillColor="#00000000"
android:strokeColor="#000"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.826,46.02l-2.759,0.002l-0.001,-0.899c-0,-0.158 0.008,-0.279 0.026,-0.362c0.022,-0.117 0.065,-0.216 0.129,-0.295c0.063,-0.079 0.152,-0.143 0.266,-0.191c0.114,-0.048 0.239,-0.073 0.376,-0.073c0.235,0 0.434,0.064 0.596,0.193c0.163,0.129 0.244,0.362 0.245,0.699l0,0.611l1.122,-0.001l0,0.316ZM27.378,45.706l-0,-0.616c-0,-0.204 -0.044,-0.349 -0.132,-0.434c-0.088,-0.086 -0.212,-0.129 -0.371,-0.129c-0.116,0.001 -0.214,0.026 -0.297,0.076c-0.082,0.051 -0.136,0.117 -0.162,0.2c-0.017,0.053 -0.025,0.151 -0.025,0.294l0.001,0.61l0.986,-0.001Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.48,43.896c-0.458,0.001 -0.816,-0.105 -1.076,-0.317c-0.259,-0.213 -0.389,-0.487 -0.389,-0.823c-0,-0.22 0.06,-0.418 0.182,-0.595c0.122,-0.177 0.291,-0.311 0.509,-0.404c0.217,-0.093 0.464,-0.14 0.74,-0.14c0.28,-0 0.531,0.048 0.752,0.146c0.221,0.097 0.388,0.235 0.502,0.414c0.114,0.179 0.171,0.371 0.171,0.578c-0,0.225 -0.063,0.425 -0.188,0.602c-0.125,0.177 -0.297,0.311 -0.514,0.402c-0.217,0.091 -0.446,0.137 -0.689,0.137ZM27.486,43.571c0.332,-0 0.594,-0.078 0.786,-0.232c0.191,-0.155 0.286,-0.349 0.286,-0.582c-0,-0.237 -0.097,-0.432 -0.29,-0.585c-0.194,-0.154 -0.468,-0.23 -0.823,-0.23c-0.225,0 -0.421,0.033 -0.588,0.099c-0.168,0.066 -0.298,0.162 -0.39,0.288c-0.092,0.127 -0.138,0.268 -0.138,0.425c0,0.224 0.089,0.416 0.267,0.576c0.178,0.161 0.474,0.241 0.89,0.241Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.853,39.51l0.092,-0.316c0.3,0.066 0.529,0.185 0.687,0.357c0.157,0.171 0.236,0.381 0.236,0.629c0.001,0.257 -0.06,0.466 -0.181,0.627c-0.121,0.161 -0.296,0.284 -0.526,0.368c-0.229,0.084 -0.476,0.127 -0.739,0.127c-0.288,0 -0.538,-0.047 -0.752,-0.142c-0.215,-0.094 -0.377,-0.229 -0.488,-0.404c-0.112,-0.175 -0.167,-0.368 -0.168,-0.578c0,-0.238 0.07,-0.439 0.211,-0.602c0.14,-0.162 0.338,-0.276 0.593,-0.34l0.085,0.311c-0.201,0.055 -0.347,0.135 -0.439,0.24c-0.091,0.106 -0.137,0.238 -0.137,0.397c0,0.183 0.051,0.336 0.153,0.459c0.102,0.123 0.238,0.21 0.41,0.259c0.171,0.05 0.348,0.075 0.53,0.075c0.234,-0.001 0.439,-0.03 0.614,-0.089c0.175,-0.06 0.306,-0.152 0.392,-0.276c0.087,-0.125 0.13,-0.26 0.13,-0.405c-0,-0.177 -0.059,-0.326 -0.177,-0.449c-0.118,-0.122 -0.294,-0.205 -0.526,-0.248Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.82,38.199l-2.434,0.002l0,0.785l-0.325,0l-0.002,-1.889l0.326,0l0,0.789l2.434,-0.003l0.001,0.316Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.174,36.055l0.043,-0.302c0.205,0.047 0.363,0.135 0.477,0.264c0.113,0.129 0.169,0.294 0.169,0.494c0.001,0.253 -0.089,0.453 -0.269,0.601c-0.18,0.148 -0.433,0.222 -0.758,0.223c-0.336,-0 -0.597,-0.075 -0.783,-0.224c-0.186,-0.149 -0.279,-0.343 -0.279,-0.582c-0,-0.231 0.09,-0.419 0.272,-0.566c0.182,-0.146 0.438,-0.22 0.768,-0.22c0.02,0 0.05,0.001 0.091,0.002l0.001,1.287c0.219,-0.011 0.387,-0.065 0.504,-0.161c0.117,-0.097 0.175,-0.217 0.175,-0.361c-0,-0.107 -0.033,-0.199 -0.098,-0.275c-0.066,-0.076 -0.17,-0.136 -0.313,-0.18ZM27.627,37.016l-0.001,-0.964c-0.168,0.013 -0.294,0.05 -0.378,0.111c-0.131,0.093 -0.196,0.214 -0.196,0.363c0,0.134 0.053,0.247 0.157,0.339c0.104,0.091 0.244,0.142 0.418,0.151Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.084,34.259l0.043,-0.288c0.23,0.031 0.41,0.112 0.54,0.241c0.13,0.129 0.195,0.288 0.195,0.477c0,0.236 -0.089,0.426 -0.268,0.57c-0.179,0.144 -0.435,0.216 -0.769,0.216c-0.216,0 -0.405,-0.031 -0.567,-0.092c-0.161,-0.062 -0.283,-0.156 -0.364,-0.282c-0.081,-0.126 -0.122,-0.263 -0.122,-0.412c0,-0.187 0.055,-0.341 0.164,-0.46c0.11,-0.119 0.266,-0.196 0.468,-0.23l0.051,0.285c-0.134,0.027 -0.235,0.075 -0.303,0.144c-0.067,0.069 -0.101,0.152 -0.101,0.249c-0,0.148 0.061,0.268 0.184,0.36c0.122,0.092 0.316,0.137 0.581,0.137c0.268,-0 0.463,-0.045 0.585,-0.134c0.122,-0.089 0.182,-0.205 0.182,-0.348c0,-0.115 -0.041,-0.21 -0.122,-0.287c-0.082,-0.077 -0.208,-0.126 -0.377,-0.146Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.816,33.72l-2.76,0.002l-0,-0.292l0.99,-0.001c-0.183,-0.136 -0.275,-0.309 -0.275,-0.517c-0,-0.128 0.029,-0.239 0.087,-0.333c0.058,-0.094 0.139,-0.162 0.242,-0.203c0.102,-0.04 0.252,-0.061 0.448,-0.061l1.267,-0.001l-0,0.292l-1.267,0.002c-0.17,-0 -0.293,0.032 -0.37,0.095c-0.077,0.063 -0.116,0.153 -0.116,0.269c0.001,0.087 0.027,0.168 0.079,0.245c0.052,0.076 0.123,0.13 0.212,0.163c0.089,0.032 0.212,0.048 0.369,0.048l1.094,-0.001l-0,0.293Z"
android:fillType="nonZero"/>
<path
android:fillColor="#FF000000"
android:pathData="M27.844,29.206l0.092,-0.315c0.3,0.066 0.529,0.185 0.687,0.356c0.157,0.172 0.236,0.382 0.237,0.63c-0,0.257 -0.061,0.466 -0.182,0.627c-0.121,0.161 -0.296,0.283 -0.525,0.368c-0.23,0.084 -0.477,0.126 -0.74,0.126c-0.288,0.001 -0.538,-0.047 -0.752,-0.141c-0.214,-0.095 -0.377,-0.23 -0.488,-0.405c-0.112,-0.175 -0.167,-0.367 -0.167,-0.577c-0.001,-0.239 0.069,-0.439 0.21,-0.602c0.14,-0.163 0.338,-0.276 0.593,-0.34l0.085,0.31c-0.201,0.056 -0.347,0.136 -0.439,0.241c-0.091,0.105 -0.137,0.237 -0.137,0.397c0,0.183 0.051,0.336 0.153,0.459c0.102,0.123 0.238,0.209 0.41,0.259c0.171,0.05 0.348,0.074 0.53,0.074c0.234,-0 0.439,-0.03 0.614,-0.089c0.175,-0.059 0.306,-0.151 0.393,-0.276c0.086,-0.125 0.129,-0.26 0.129,-0.405c-0,-0.176 -0.059,-0.326 -0.177,-0.448c-0.118,-0.123 -0.294,-0.205 -0.526,-0.249Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M28.811,27.895l-2.434,0.002l0,0.786l-0.325,-0l-0.002,-1.889l0.326,-0l0,0.788l2.434,-0.002l0.001,0.315Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.982,26.833l-0.341,-0l-0.001,-0.899l0.341,-0l0.001,0.899Z"
android:fillType="nonZero"/>
<path
android:fillColor="#FF000000"
android:pathData="M28.808,24.589l0,0.292l-2.159,0.002c0.078,0.07 0.156,0.163 0.234,0.277c0.077,0.114 0.136,0.217 0.175,0.308l-0.328,0c-0.089,-0.163 -0.197,-0.306 -0.324,-0.429c-0.127,-0.122 -0.25,-0.209 -0.369,-0.259l-0,-0.189l2.771,-0.002Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.446,23.84c-0.326,0.001 -0.589,-0.028 -0.788,-0.086c-0.199,-0.058 -0.352,-0.144 -0.46,-0.258c-0.108,-0.114 -0.162,-0.258 -0.163,-0.431c0,-0.128 0.03,-0.241 0.09,-0.337c0.059,-0.097 0.145,-0.176 0.257,-0.239c0.113,-0.063 0.249,-0.113 0.411,-0.149c0.161,-0.035 0.378,-0.054 0.652,-0.054c0.324,-0 0.585,0.028 0.784,0.086c0.199,0.057 0.353,0.143 0.461,0.257c0.109,0.114 0.163,0.259 0.164,0.433c-0,0.23 -0.095,0.41 -0.286,0.542c-0.23,0.157 -0.604,0.236 -1.122,0.236ZM27.446,23.54c0.453,-0.001 0.754,-0.047 0.904,-0.139c0.15,-0.091 0.225,-0.204 0.225,-0.339c-0,-0.134 -0.075,-0.247 -0.226,-0.338c-0.151,-0.092 -0.452,-0.137 -0.904,-0.137c-0.454,0 -0.756,0.046 -0.905,0.138c-0.15,0.092 -0.224,0.206 -0.224,0.343c0,0.134 0.066,0.241 0.198,0.321c0.168,0.101 0.479,0.151 0.932,0.151Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.445,21.989c-0.327,0 -0.589,-0.029 -0.788,-0.087c-0.199,-0.057 -0.353,-0.143 -0.461,-0.258c-0.108,-0.114 -0.162,-0.258 -0.162,-0.431c-0,-0.128 0.029,-0.24 0.089,-0.337c0.059,-0.096 0.145,-0.176 0.258,-0.239c0.112,-0.063 0.249,-0.112 0.41,-0.148c0.161,-0.036 0.379,-0.054 0.652,-0.054c0.324,-0.001 0.585,0.028 0.784,0.085c0.199,0.057 0.353,0.143 0.462,0.257c0.108,0.115 0.163,0.259 0.163,0.433c0,0.23 -0.095,0.411 -0.286,0.542c-0.229,0.157 -0.603,0.236 -1.121,0.237ZM27.444,21.688c0.453,-0 0.755,-0.046 0.905,-0.138c0.15,-0.092 0.225,-0.205 0.224,-0.339c0,-0.135 -0.075,-0.248 -0.226,-0.339c-0.15,-0.091 -0.452,-0.137 -0.904,-0.137c-0.454,0.001 -0.756,0.047 -0.905,0.139c-0.149,0.091 -0.224,0.205 -0.224,0.342c0,0.134 0.066,0.242 0.198,0.322c0.169,0.1 0.479,0.15 0.932,0.15Z"
android:fillType="nonZero"/>
<path
android:fillColor="@android:color/black"
android:pathData="M27.307,19.687c-0.051,0.121 -0.125,0.211 -0.22,0.27c-0.095,0.059 -0.21,0.088 -0.343,0.088c-0.2,0 -0.369,-0.062 -0.506,-0.186c-0.137,-0.125 -0.206,-0.291 -0.206,-0.498c0,-0.208 0.07,-0.375 0.21,-0.502c0.14,-0.127 0.31,-0.191 0.511,-0.191c0.128,0 0.239,0.029 0.334,0.087c0.095,0.058 0.168,0.146 0.219,0.264c0.056,-0.147 0.145,-0.258 0.267,-0.334c0.123,-0.077 0.27,-0.115 0.441,-0.115c0.236,-0.001 0.434,0.071 0.595,0.215c0.161,0.144 0.241,0.334 0.241,0.569c0.001,0.235 -0.08,0.425 -0.241,0.569c-0.161,0.144 -0.362,0.217 -0.603,0.217c-0.18,0 -0.33,-0.039 -0.451,-0.118c-0.121,-0.078 -0.204,-0.19 -0.248,-0.335ZM26.733,19.746c0.13,-0 0.237,-0.037 0.32,-0.109c0.083,-0.073 0.124,-0.167 0.124,-0.283c-0,-0.113 -0.041,-0.205 -0.124,-0.277c-0.082,-0.072 -0.183,-0.108 -0.302,-0.108c-0.124,0 -0.229,0.037 -0.313,0.112c-0.085,0.074 -0.127,0.166 -0.127,0.277c-0,0.111 0.041,0.204 0.124,0.278c0.083,0.073 0.182,0.11 0.298,0.11ZM28.007,19.839c0.097,0 0.191,-0.02 0.281,-0.059c0.09,-0.04 0.16,-0.099 0.21,-0.177c0.049,-0.078 0.074,-0.162 0.074,-0.252c-0,-0.14 -0.052,-0.255 -0.157,-0.346c-0.104,-0.091 -0.236,-0.136 -0.397,-0.136c-0.163,-0 -0.298,0.047 -0.405,0.141c-0.106,0.093 -0.16,0.211 -0.159,0.352c-0,0.137 0.053,0.251 0.158,0.342c0.106,0.09 0.237,0.135 0.395,0.135Z"
android:fillType="nonZero"/>
</vector>

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps.database.embedments
import info.nightscout.androidaps.database.entities.TherapyEvent
data class InterfaceIDs(
var nightscoutSystemId: String? = null,
var nightscoutId: String? = null,

View file

@ -108,6 +108,7 @@ data class GlucoseValue(
GLIMP("Glimp"),
LIBRE_2_NATIVE("Libre2"),
POCTECH_NATIVE("Poctech"),
GLUNOVO_NATIVE("Glunovo"),
MM_600_SERIES("MM600Series"),
EVERSENSE("Eversense"),
RANDOM("Random"),

View file

@ -146,6 +146,7 @@ data class UserEntry(
NSClientSource,
PocTech,
Tomato,
Glunovo,
Xdrip,
LocalProfile, //From LocalProfile plugin
Loop, //From Loop plugin

11
medtronic/Changelog.txt Normal file
View file

@ -0,0 +1,11 @@
V1 - Medtronic initial implementation
... lots of changes
V2 - Rewrite into kotlin, new database (for v3.0)
0001 - initial version
0002 - some fixes
0003 - SMB fix (798)
0004 - Zero TBR Duration fix (798), refactoring of TempBasalProcessDTO
0005 - fixes to MedtronicHistoryEntry lateinit problem

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.Riley
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask
import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
import info.nightscout.androidaps.plugins.pump.common.utils.ProfileUtil
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry
@ -111,7 +112,7 @@ class MedtronicPumpPlugin @Inject constructor(
private var isBusy = false
override fun onStart() {
aapsLogger.debug(LTag.PUMP, deviceID() + " started.")
aapsLogger.debug(LTag.PUMP, deviceID() + " started. (V2.0005)")
serviceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is disconnected")
@ -468,6 +469,7 @@ class MedtronicPumpPlugin @Inject constructor(
}
}
@Synchronized
override fun isThisProfileSet(profile: Profile): Boolean {
aapsLogger.debug(LTag.PUMP, "isThisProfileSet: basalInitalized=" + medtronicPumpStatus.basalProfileStatus)
if (!isInitialized) return true
@ -580,6 +582,7 @@ class MedtronicPumpPlugin @Inject constructor(
scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0)
}
@Synchronized
override fun deliverBolus(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - " + BolusDeliveryType.DeliveryPrepared)
setRefreshButtonEnabled(false)
@ -691,6 +694,7 @@ class MedtronicPumpPlugin @Inject constructor(
// if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged),
// if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed
@Synchronized
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult {
setRefreshButtonEnabled(false)
if (isPumpNotReachable) {
@ -742,6 +746,7 @@ class MedtronicPumpPlugin @Inject constructor(
return PumpEnactResult(injector).success(false).enacted(false)
.comment(R.string.medtronic_cmd_cant_cancel_tbr_stop_op)
} else {
//cancelTBRWithTemporaryId()
aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - Current TBR cancelled.")
}
}
@ -760,8 +765,9 @@ class MedtronicPumpPlugin @Inject constructor(
medtronicPumpStatus.tempBasalAmount = absoluteRate
medtronicPumpStatus.tempBasalLength = durationInMinutes
val tempData = info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR(absoluteRate, true, durationInMinutes, tbrType)
val tempData = PumpDbEntryTBR(absoluteRate, true, durationInMinutes, tbrType)
medtronicPumpStatus.runningTBRWithTemp = tempData
pumpSyncStorage.addTemporaryBasalRateWithTempId(tempData, true, this)
incrementStatistics(MedtronicConst.Statistics.TBRsSet)
@ -771,6 +777,63 @@ class MedtronicPumpPlugin @Inject constructor(
}
}
@Deprecated("Not used, TBRs fixed in history, should be removed.")
private fun cancelTBRWithTemporaryId() {
val tbrs : MutableList<PumpDbEntryTBR> = pumpSyncStorage.getTBRs()
if (tbrs.size > 0 && medtronicPumpStatus.runningTBRWithTemp!=null) {
aapsLogger.info(LTag.PUMP, logPrefix + "cancelTBRWithTemporaryId - TBR items: ${tbrs.size}")
var item : PumpDbEntryTBR? = null
if (tbrs.size==1) {
item = tbrs.get(0);
} else {
for (tbr in tbrs) {
if (tbr.date == medtronicPumpStatus.runningTBRWithTemp!!.date) {
item = tbr
break;
}
}
}
if (item!=null) {
aapsLogger.debug(LTag.PUMP, "DD: cancelTBRWithTemporaryId: tempIdEntry=${item}")
val differenceS = (System.currentTimeMillis() - item.date) / 1000
aapsLogger.debug(LTag.PUMP, "syncTemporaryBasalWithTempId " +
"[date=${item.date}, " +
"rate=${item.rate}, " +
"duration=${differenceS} s, " +
"isAbsolute=${!item.isAbsolute}, temporaryId=${item.temporaryId}, " +
"pumpId=NO, pumpType=${medtronicPumpStatus.pumpType}, " +
"pumpSerial=${medtronicPumpStatus.serialNumber}]")
val result = pumpSync.syncTemporaryBasalWithTempId(
timestamp = item.date,
rate = item.rate,
duration= differenceS * 1000L,
isAbsolute = item.isAbsolute,
temporaryId = item.temporaryId,
type = item.tbrType,
pumpId = null,
pumpType = medtronicPumpStatus.pumpType,
pumpSerial = medtronicPumpStatus.serialNumber)
aapsLogger.debug(LTag.PUMP, "syncTemporaryBasalWithTempId - Result: $result")
}
} else {
aapsLogger.info(LTag.PUMP, logPrefix + "cancelTBRWithTemporaryId - TBR items: ${tbrs.size}, runningTBRWithTemp=${medtronicPumpStatus.runningTBRWithTemp}")
}
if (medtronicPumpStatus.runningTBRWithTemp!=null) {
medtronicPumpStatus.runningTBRWithTemp = null
}
}
@Synchronized
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult {
return if (percent == 0) {
setTempBasalAbsolute(0.0, durationInMinutes, profile, enforceNew, tbrType)
@ -966,6 +1029,7 @@ class MedtronicPumpPlugin @Inject constructor(
}
}
@Synchronized
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - started")
if (isPumpNotReachable) {
@ -1001,7 +1065,7 @@ class MedtronicPumpPlugin @Inject constructor(
aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - Cancel TBR successful.")
val runningTBR = medtronicPumpStatus.runningTBR
// TODO
if (runningTBR != null) {
if (medtronicHistoryData.isTBRActive(runningTBR)) {
@ -1026,6 +1090,8 @@ class MedtronicPumpPlugin @Inject constructor(
}
}
//cancelTBRWithTemporaryId()
PumpEnactResult(injector).success(true).enacted(true) //
.isTempCancel(true)
}
@ -1043,6 +1109,7 @@ class MedtronicPumpPlugin @Inject constructor(
return medtronicPumpStatus.serialNumber
}
@Synchronized
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
aapsLogger.info(LTag.PUMP, logPrefix + "setNewBasalProfile")

View file

@ -14,13 +14,13 @@ import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil
*/
abstract class MedtronicHistoryEntry : MedtronicHistoryEntryInterface {
lateinit var rawData: List<Byte>
var rawData: List<Byte> = listOf()
protected var sizes = IntArray(3)
lateinit var head: ByteArray
lateinit var datetime: ByteArray
lateinit var body: ByteArray
var head: ByteArray = byteArrayOf()
var datetime: ByteArray = byteArrayOf()
var body: ByteArray = byteArrayOf()
var id: Long = 0
@ -41,7 +41,13 @@ abstract class MedtronicHistoryEntry : MedtronicHistoryEntryInterface {
/**
* Pump id that will be used with AAPS object (time * 1000 + historyType (max is FF = 255)
*/
open var pumpId: Long = 0L
var pumpId: Long = 0L
get() {
if (field == 0L) {
field = generatePumpId()
}
return field
}
/**
* if history object is already linked to AAPS object (either Treatment, TempBasal or TDD (tdd's
@ -158,7 +164,7 @@ abstract class MedtronicHistoryEntry : MedtronicHistoryEntryInterface {
sb.append("]")
return sb.toString()
}
if (head.size != 0) {
if (head!=null && head.size != 0) {
sb.append(", head=")
sb.append(ByteUtil.shortHexString(head))
}

View file

@ -92,14 +92,6 @@ class PumpHistoryEntry : MedtronicHistoryEntry() {
}
}
override var pumpId: Long = 0L
get() {
if (field == 0L) {
field = generatePumpId()
}
return field
}
fun hasBolusChanged(entry: PumpHistoryEntry): Boolean {
if (entryType == PumpHistoryEntryType.Bolus) {
val thisOne: BolusDTO = this.decodedData["Object"] as BolusDTO

View file

@ -508,25 +508,25 @@ class MedtronicHistoryData @Inject constructor(
if (temporaryId != null) {
val result = pumpSync.syncBolusWithTempId(
tryToGetByLocalTime(bolus.atechDateTime),
deliveredAmount,
temporaryId,
type,
bolus.pumpId,
medtronicPumpStatus.pumpType,
medtronicPumpStatus.serialNumber)
timestamp = tryToGetByLocalTime(bolus.atechDateTime),
amount = deliveredAmount,
temporaryId = temporaryId,
type = null,
pumpId = bolus.pumpId,
pumpType = medtronicPumpStatus.pumpType,
pumpSerial = medtronicPumpStatus.serialNumber)
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncBolusWithTempId [date=%d, temporaryId=%d, pumpId=%d, insulin=%.2f, pumpSerial=%s] - Result: %b",
bolus.atechDateTime, temporaryId, bolus.pumpId, deliveredAmount,
medtronicPumpStatus.serialNumber, result))
} else {
val result = pumpSync.syncBolusWithPumpId(
tryToGetByLocalTime(bolus.atechDateTime),
deliveredAmount,
type,
bolus.pumpId,
medtronicPumpStatus.pumpType,
medtronicPumpStatus.serialNumber)
timestamp = tryToGetByLocalTime(bolus.atechDateTime),
amount = deliveredAmount,
type = null,
pumpId = bolus.pumpId,
pumpType = medtronicPumpStatus.pumpType,
pumpSerial = medtronicPumpStatus.serialNumber)
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncBolusWithPumpId [date=%d, pumpId=%d, insulin=%.2f, pumpSerial=%s] - Result: %b",
bolus.atechDateTime, bolus.pumpId, deliveredAmount,
@ -571,62 +571,42 @@ class MedtronicHistoryData @Inject constructor(
private fun processTBREntries(entryList: MutableList<PumpHistoryEntry>) {
entryList.reverse()
val tbr = entryList[0].getDecodedDataEntry("Object") as TempBasalPair
var readOldItem = false
if (tbr.isCancelTBR) {
// var readOldItem = false
val oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.TempBasalCombined)
if (tbr.isCancelTBR) { // if we have cancel we need to limit previous TBR with this cancel
if (oneMoreEntryFromHistory != null) {
entryList.add(0, oneMoreEntryFromHistory)
readOldItem = true
} else {
entryList.removeAt(0)
}
} else {
if (oneMoreEntryFromHistory != null) {
val tbrPrev = oneMoreEntryFromHistory.getDecodedDataEntry("Object") as TempBasalPair
if (tbrPrev.isZeroTBR) { // if we had Zere TBR in last previous TBR, then we need to limit it, so we need to process it too
entryList.add(0, oneMoreEntryFromHistory)
}
}
}
val tbrRecords = pumpSyncStorage.getTBRs()
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, ProcessHistoryRecord.TBR.description + " List (before filter): %s, FromDb=%s", gson.toJson(entryList),
tbrRecords))
var processDTO: TempBasalProcessDTO? = null
val processList: MutableList<TempBasalProcessDTO> = mutableListOf()
for (treatment in entryList) {
val tbr2 = treatment.getDecodedDataEntry("Object") as TempBasalPair
if (tbr2.isCancelTBR) {
if (processDTO != null) {
processDTO.itemTwo = treatment
processDTO.cancelPresent = true
if (readOldItem) {
processDTO.processOperation = TempBasalProcessDTO.Operation.Edit
readOldItem = false
}
} else {
aapsLogger.warn(LTag.PUMP, "processDTO was null - shouldn't happen, ignoring item. ItemTwo=$treatment")
}
} else {
if (processDTO != null) {
processList.add(processDTO)
}
processDTO = TempBasalProcessDTO(
itemOne = treatment,
processOperation = TempBasalProcessDTO.Operation.Add,
aapsLogger = aapsLogger,
objectType = TempBasalProcessDTO.ObjectType.TemporaryBasal
)
}
}
if (processDTO != null) {
processList.add(processDTO)
}
val processList: MutableList<TempBasalProcessDTO> = createTBRProcessList(entryList);
if (processList.isNotEmpty()) {
for (tempBasalProcessDTO in processList) {
aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemOne: " + gson.toJson(tempBasalProcessDTO.itemOne))
aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemTwo: " + (if (tempBasalProcessDTO.itemTwo == null) "null" else gson.toJson(tempBasalProcessDTO.itemTwo!!)))
aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO: " + tempBasalProcessDTO.toTreatmentString())
//aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemOne: " + gson.toJson(tempBasalProcessDTO.itemOne))
//aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemTwo: " + (if (tempBasalProcessDTO.itemTwo == null) "null" else gson.toJson(tempBasalProcessDTO.itemTwo!!)))
@Suppress("Unchecked_Cast")
val entryWithTempId = findDbEntry(tempBasalProcessDTO.itemOne, tbrRecords as MutableList<PumpDbEntry>) as PumpDbEntryTBR?
aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId: " + (entryWithTempId?.toString() ?: "null"))
val tbrEntry = tempBasalProcessDTO.itemOneTbr //.getDecodedDataEntry("Object") as TempBasalPair
val tbrEntry = tempBasalProcessDTO.itemOneTbr
aapsLogger.debug(LTag.PUMP, String.format("DD: tbrEntry=%s, tempBasalProcessDTO=%s", gson.toJson(tbrEntry), gson.toJson(tempBasalProcessDTO)))
@ -649,7 +629,7 @@ class MedtronicHistoryData @Inject constructor(
tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime),
tbrEntry.insulinRate,
tempBasalProcessDTO.durationAsSeconds * 1000L,
!tbrEntry.isPercent,
isAbsolute = !tbrEntry.isPercent,
entryWithTempId.temporaryId,
PumpSync.TemporaryBasalType.NORMAL,
tempBasalProcessDTO.pumpId,
@ -705,10 +685,10 @@ class MedtronicHistoryData @Inject constructor(
date = tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime),
pumpType = medtronicPumpStatus.pumpType,
serialNumber = medtronicPumpStatus.serialNumber,
entry = PumpDbEntryTBR(rate = tbrEntry.insulinRate,
rate = tbrEntry.insulinRate,
isAbsolute = !tbrEntry.isPercent,
durationInSeconds = tempBasalProcessDTO.durationAsSeconds,
tbrType = PumpSync.TemporaryBasalType.NORMAL),
tbrType = PumpSync.TemporaryBasalType.NORMAL,
pumpId = tempBasalProcessDTO.pumpId)
}
}
@ -720,6 +700,56 @@ class MedtronicHistoryData @Inject constructor(
} // collection
}
fun createTBRProcessList(entryList: MutableList<PumpHistoryEntry>) : MutableList<TempBasalProcessDTO> {
aapsLogger.debug(LTag.PUMP, "${ProcessHistoryRecord.TBR.description} List (before filter): ${gson.toJson(entryList)}")
var processDTO: TempBasalProcessDTO? = null
val processList: MutableList<TempBasalProcessDTO> = mutableListOf()
for (treatment in entryList) {
val tbr2 = treatment.getDecodedDataEntry("Object") as TempBasalPair
if (tbr2.isCancelTBR) {
if (processDTO != null) {
processDTO.itemTwo = treatment
} else {
aapsLogger.warn(LTag.PUMP, "processDTO was null - shouldn't happen, ignoring item. ItemTwo=$treatment")
}
} else {
if (processDTO != null) {
processList.add(processDTO)
}
processDTO = TempBasalProcessDTO(
itemOne = treatment,
aapsLogger = aapsLogger,
objectType = TempBasalProcessDTO.ObjectType.TemporaryBasal
)
}
}
if (processDTO != null) {
processList.add(processDTO)
}
var previousItem: TempBasalProcessDTO? = null
// fix for Zero TBRs
for (tempBasalProcessDTO in processList) {
if (previousItem!=null) {
var pheEnd = PumpHistoryEntry()
pheEnd.atechDateTime = DateTimeUtil.getATDWithAddedSeconds(tempBasalProcessDTO.itemOne.atechDateTime, -2)
pheEnd.addDecodedData("Object", TempBasalPair(0.0, false, 0))
previousItem.itemTwo = pheEnd
previousItem = null
}
if (tempBasalProcessDTO.itemOneTbr!!.isZeroTBR) {
previousItem = tempBasalProcessDTO
}
}
return processList
}
fun isTBRActive(dbEntry: PumpDbEntryTBR): Boolean {
@ -881,7 +911,6 @@ class MedtronicHistoryData @Inject constructor(
while (i < filtered2Items.size) {
val tbrProcess = TempBasalProcessDTO(
itemOne = filtered2Items[i],
processOperation = TempBasalProcessDTO.Operation.Add,
aapsLogger = aapsLogger,
objectType = TempBasalProcessDTO.ObjectType.Suspend)
@ -959,7 +988,6 @@ class MedtronicHistoryData @Inject constructor(
if (items.size > 0) {
val tbrProcess = TempBasalProcessDTO(
itemOne = items[items.size - 1],
processOperation = TempBasalProcessDTO.Operation.Add,
aapsLogger = aapsLogger,
objectType = TempBasalProcessDTO.ObjectType.Suspend)
@ -975,7 +1003,6 @@ class MedtronicHistoryData @Inject constructor(
if (items.size > 0) {
val tbrProcess = TempBasalProcessDTO(
itemOne = items[0],
processOperation = TempBasalProcessDTO.Operation.Add,
aapsLogger = aapsLogger,
objectType = TempBasalProcessDTO.ObjectType.Suspend)

View file

@ -91,6 +91,9 @@ class TempBasalPair : TempBasalPair {
val isCancelTBR: Boolean
get() = MedtronicUtil.isSame(insulinRate, 0.0) && durationMinutes == 0
val isZeroTBR: Boolean
get() = MedtronicUtil.isSame(insulinRate, 0.0) && durationMinutes != 0
val description: String
get() {
if (isCancelTBR) {

View file

@ -4,9 +4,9 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry
import java.lang.StringBuilder
class TempBasalProcessDTO constructor(var itemOne: PumpHistoryEntry,
var processOperation: Operation = Operation.None,
var aapsLogger: AAPSLogger,
var objectType: ObjectType = ObjectType.TemporaryBasal) {
@ -21,8 +21,6 @@ class TempBasalProcessDTO constructor(var itemOne: PumpHistoryEntry,
var itemOneTbr: TempBasalPair? = null
var itemTwoTbr: TempBasalPair? = null
var cancelPresent: Boolean = false
val atechDateTime: Long
get() = itemOne.atechDateTime
@ -31,26 +29,26 @@ class TempBasalProcessDTO constructor(var itemOne: PumpHistoryEntry,
val durationAsSeconds: Int
get() {
aapsLogger.debug(LTag.PUMP, "durationAsSeconds: [objectType=$objectType]")
//aapsLogger.debug(LTag.PUMP, "durationAsSeconds: [objectType=$objectType]")
if (objectType == ObjectType.TemporaryBasal) {
if (itemTwo == null) {
if (itemOneTbr != null) {
aapsLogger.debug("TemporaryBasalPair - itemOneSingle: $itemOneTbr")
//aapsLogger.debug("TemporaryBasalPair - itemOneSingle: $itemOneTbr")
return itemOneTbr!!.durationMinutes * 60
} else {
aapsLogger.error("Couldn't find TempBasalPair in entry: $itemOne")
//aapsLogger.error("Couldn't find TempBasalPair in entry: $itemOne")
return 0
}
} else {
aapsLogger.debug(LTag.PUMP, "Found 2 items for duration: itemOne=$itemOne, itemTwo=$itemTwo")
//aapsLogger.debug(LTag.PUMP, "Found 2 items for duration: itemOne=$itemOne, itemTwo=$itemTwo")
val secondsDiff = DateTimeUtil.getATechDateDiferenceAsSeconds(itemOne.atechDateTime, itemTwo!!.atechDateTime)
aapsLogger.debug(LTag.PUMP, "Difference in seconds: $secondsDiff")
//aapsLogger.debug(LTag.PUMP, "Difference in seconds: $secondsDiff")
return secondsDiff
}
} else {
aapsLogger.debug(LTag.PUMP, "Found 2 items for duration (in SuspendMode): itemOne=$itemOne, itemTwo=$itemTwo")
//aapsLogger.debug(LTag.PUMP, "Found 2 items for duration (in SuspendMode): itemOne=$itemOne, itemTwo=$itemTwo")
val secondsDiff = DateTimeUtil.getATechDateDiferenceAsSeconds(itemOne.atechDateTime, itemTwo!!.atechDateTime)
aapsLogger.debug(LTag.PUMP, "Difference in seconds: $secondsDiff")
//aapsLogger.debug(LTag.PUMP, "Difference in seconds: $secondsDiff")
return secondsDiff
}
}
@ -61,8 +59,31 @@ class TempBasalProcessDTO constructor(var itemOne: PumpHistoryEntry,
}
}
fun toTreatmentString(): String {
val stringBuilder = StringBuilder()
stringBuilder.append(itemOne.DT)
if (itemTwo!=null) {
stringBuilder.append(" - ")
stringBuilder.append(itemTwo!!.DT)
}
var dur = durationAsSeconds
stringBuilder.append(" " + durationAsSeconds + " s (" + durationAsSeconds/60 + ")")
if (itemTwoTbr!=null) {
stringBuilder.append(" " + itemOneTbr!!.insulinRate + " / " + itemTwoTbr!!.insulinRate)
} else {
stringBuilder.append(" " + itemOneTbr!!.insulinRate)
}
return stringBuilder.toString()
}
override fun toString(): String {
return "ItemOne: $itemOne, ItemTwo: $itemTwo, Duration: $durationAsSeconds, Operation: $processOperation, ObjectType: $objectType"
return "ItemOne: $itemOne, ItemTwo: $itemTwo, Duration: $durationAsSeconds, ObjectType: $objectType"
}
enum class Operation {

View file

@ -34,6 +34,7 @@ class MedtronicPumpStatus @Inject constructor(private val rh: ResourceHelper,
var maxBolus: Double? = null
var maxBasal: Double? = null
var runningTBR: PumpDbEntryTBR? = null
var runningTBRWithTemp: PumpDbEntryTBR? = null
// statuses
var pumpDeviceState = PumpDeviceState.NeverContacted

View file

@ -0,0 +1,91 @@
package info.nightscout.androidaps.plugins.pump.medtronic.data
import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.internal.LinkedTreeMap
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage
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.data.dto.TempBasalPair
import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.hamcrest.Matchers.notNullValue
import org.junit.Assert.*
import org.junit.Test
import org.mockito.Mock
import java.io.File
import java.net.URL
class MedtronicHistoryDataUTest : TestBase() {
@Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var medtronicUtil: MedtronicUtil
@Mock lateinit var medtronicPumpHistoryDecoder: MedtronicPumpHistoryDecoder
@Mock lateinit var medtronicPumpStatus: MedtronicPumpStatus
@Mock lateinit var pumpSync: PumpSync
@Mock lateinit var pumpSyncStorage: PumpSyncStorage
@Mock lateinit var sp: SP
private val packetInjector = HasAndroidInjector {
AndroidInjector {
}
}
@Test
fun createTBRProcessList() {
var unitToTest = MedtronicHistoryData(packetInjector, aapsLogger, sp, activePlugin,
medtronicUtil, medtronicPumpHistoryDecoder,
medtronicPumpStatus,
pumpSync,
pumpSyncStorage)
val gson = Gson()
val fileText = ClassLoader.getSystemResource("tbr_data.json").readText()
val listType: Type = object : TypeToken<MutableList<PumpHistoryEntry?>?>() {}.getType()
val yourClassList: MutableList<PumpHistoryEntry> = gson.fromJson(fileText, listType)
for (pumpHistoryEntry in yourClassList) {
val stringObject = pumpHistoryEntry.decodedData["Object"] as LinkedTreeMap<String,Object>
val rate : Double = stringObject.get("insulinRate") as Double
val durationMinutes: Double = stringObject.get("durationMinutes") as Double
val durationMinutesInt : Int = durationMinutes.toInt()
var tmbPair = TempBasalPair(rate, false, durationMinutesInt)
pumpHistoryEntry.decodedData.remove("Object")
pumpHistoryEntry.addDecodedData("Object", tmbPair)
}
System.out.println("TBR Pre-Process List: " + gson.toJson(yourClassList))
val createTBRProcessList = unitToTest.createTBRProcessList(yourClassList)
System.out.println("TBR Process List: " + createTBRProcessList.size)
for (tempBasalProcessDTO in createTBRProcessList) {
System.out.println(tempBasalProcessDTO.toTreatmentString())
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -235,7 +235,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun connect(reason: String) {
aapsLogger.info(LTag.PUMP, "connect reason=$reason")
podStateManager.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
synchronized(this) {
stopConnecting?.let {
aapsLogger.warn(LTag.PUMP, "Already connecting: $stopConnecting")
@ -244,7 +243,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
val stop = CountDownLatch(1)
stopConnecting = stop
}
thread(
start = true,
name = "ConnectionThread",
@ -253,6 +251,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
stopConnecting?.let {
val error = omnipodManager.connect(it).ignoreElements().blockingGet()
aapsLogger.info(LTag.PUMPCOMM, "connect error=$error")
if (error == null) {
podStateManager.incrementSuccessfulConnectionAttemptsAfterRetries()
}
}
} finally {
synchronized(this) {
@ -270,6 +271,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun stopConnecting() {
aapsLogger.info(LTag.PUMP, "stopConnecting")
podStateManager.incrementFailedConnectionsAfterRetries()
stopConnecting?.countDown()
omnipodManager.disconnect(true)
}
@ -340,15 +342,16 @@ class OmnipodDashPumpPlugin @Inject constructor(
aapsLogger.info(LTag.PUMP, "syncBolusWithPumpId on CANCEL_BOLUS returned: $sync")
}
}
if (!podStateManager.alarmSynced) {
podStateManager.alarmType?.let {
if (!commandQueue.isCustomCommandInQueue(CommandDeactivatePod::class.java)) {
showNotification(
Notification.OMNIPOD_POD_FAULT,
it.toString(),
Notification.URGENT,
R.raw.boluserror
)
if (!podStateManager.alarmSynced) {
}
pumpSync.insertAnnouncement(
error = it.toString(),
pumpId = Random.Default.nextLong(),
@ -694,7 +697,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
}
val percent = (waited.toFloat() / estimatedDeliveryTimeSeconds) * 100
updateBolusProgressDialog(
rh.gs(R.string.bolusdelivering, requestedBolusAmount),
rh.gs(R.string.dash_bolusdelivering, requestedBolusAmount),
percent.toInt()
)
}
@ -1191,7 +1194,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
private fun handleTimeChange(): PumpEnactResult {
return profileFunction.getProfile()?.let {
setNewBasalProfile(it, OmnipodCommandType.SET_TIME)
} ?: PumpEnactResult(injector).success(true).enacted(false).comment("No profile active")
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active")
}
private fun updateAlertConfiguration(): PumpEnactResult {

View file

@ -23,8 +23,6 @@ interface OmnipodDashManager {
fun suspendDelivery(hasBasalBeepEnabled: Boolean): Observable<PodEvent>
fun setTime(): Observable<PodEvent>
fun setTempBasal(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent>
fun stopTempBasal(hasTempBasalBeepEnabled: Boolean): Observable<PodEvent>

View file

@ -196,7 +196,7 @@ class OmnipodDashManagerImpl @Inject constructor(
DefaultStatusResponse::class
)
}.doOnComplete {
podStateManager.timeZone = TimeZone.getDefault()
podStateManager.updateTimeZone()
}
}
@ -506,12 +506,6 @@ class OmnipodDashManagerImpl @Inject constructor(
).interceptPodEvents()
}
override fun setTime(): Observable<PodEvent> {
// TODO
logger.error(LTag.PUMPCOMM, "NOT IMPLEMENTED: setTime()")
return Observable.empty()
}
private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent> {
return Observable.defer {
bleManager.sendCommand(

View file

@ -33,8 +33,11 @@ interface OmnipodDashPodStateManager {
var bluetoothConnectionState: BluetoothConnectionState
var connectionAttempts: Int
var successfulConnections: Int
val successfulConnectionAttemptsAfterRetries: Int
val failedConnectionsAfterRetries: Int
var timeZone: TimeZone
val timeZoneId: String?
val timeZoneUpdated: Long?
val sameTimeZone: Boolean // The TimeZone is the same on the phone and on the pod
val lastUpdatedSystem: Long // System.currentTimeMillis()
val lastStatusResponseReceived: Long
@ -85,6 +88,9 @@ interface OmnipodDashPodStateManager {
fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
fun reset()
fun connectionSuccessRatio(): Float
fun incrementSuccessfulConnectionAttemptsAfterRetries()
fun incrementFailedConnectionsAfterRetries()
fun updateTimeZone()
fun createActiveCommand(
historyId: String,

View file

@ -23,6 +23,8 @@ import io.reactivex.Single
import java.io.Serializable
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.util.*
import javax.inject.Inject
@ -114,25 +116,35 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
podState.successfulConnections = value
}
override var timeZone: TimeZone
get() = TimeZone.getTimeZone(podState.timeZone)
set(tz) {
podState.timeZone = tz.toZoneId().normalized().id
store()
override val successfulConnectionAttemptsAfterRetries: Int
@Synchronized
get() = podState.successfulConnectionAttemptsAfterRetries
@Synchronized
override fun incrementSuccessfulConnectionAttemptsAfterRetries() {
podState.successfulConnectionAttemptsAfterRetries++
}
override val failedConnectionsAfterRetries: Int
@Synchronized
get() = podState.failedConnectionsAfterRetries
override fun incrementFailedConnectionsAfterRetries() {
podState.failedConnectionsAfterRetries++
}
override val timeZoneId: String?
get() = podState.timeZone
override val sameTimeZone: Boolean
get() {
val now = System.currentTimeMillis()
val currentTimezone = TimeZone.getDefault()
val currentOffset = currentTimezone.getOffset(now)
val podOffset = timeZone.getOffset(now)
val podOffset = podState.timeZoneOffset
logger.debug(
LTag.PUMPCOMM,
"sameTimeZone currentTimezone=${currentTimezone.getDisplayName(
true,
TimeZone.SHORT
)} " +
"sameTimeZone " +
"currentOffset=$currentOffset " +
"podOffset=$podOffset"
)
@ -219,8 +231,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
get() {
val minutesSinceActivation = podState.minutesSinceActivation
val activationTime = podState.activationTime
if ((activationTime != null) && (minutesSinceActivation != null)) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(activationTime), timeZone.toZoneId())
val timeZoneOffset = podState.timeZoneOffset
if ((activationTime != null) && (minutesSinceActivation != null) && (timeZoneOffset != null)) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(activationTime), ZoneId.ofOffset("", ZoneOffset.ofTotalSeconds(timeZoneOffset / 1000)))
.plusMinutes(minutesSinceActivation.toLong())
.plus(Duration.ofMillis(System.currentTimeMillis() - lastUpdatedSystem))
}
@ -229,7 +242,23 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
override val timeDrift: Duration?
get() {
return Duration.between(ZonedDateTime.now(), time)
return time?.let {
return Duration.between(ZonedDateTime.now(), it)
} ?: null
}
override val timeZoneUpdated: Long?
get() {
return podState.timeZoneUpdated
}
override fun updateTimeZone() {
val timeZone = TimeZone.getDefault()
val now = System.currentTimeMillis()
podState.timeZoneOffset = timeZone.getOffset(now)
podState.timeZone = timeZone.id
podState.timeZoneUpdated = now
}
override val expiry: ZonedDateTime?
@ -627,13 +656,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
}
override fun connectionSuccessRatio(): Float {
if (connectionAttempts == 0) {
if (failedConnectionsAfterRetries + successfulConnectionAttemptsAfterRetries == 0) {
return 0.0F
} else if (connectionAttempts <= successfulConnections) {
// Prevent bogus quality > 1 during initialisation
return 1.0F
}
return successfulConnections.toFloat() / connectionAttempts.toFloat()
return successfulConnectionAttemptsAfterRetries.toFloat() / (successfulConnectionAttemptsAfterRetries + failedConnectionsAfterRetries)
}
override fun reset() {
@ -675,6 +701,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
var connectionAttempts = 0
var successfulConnections = 0
var successfulConnectionAttemptsAfterRetries = 0
var failedConnectionsAfterRetries = 0
var messageSequenceNumber: Short = 0
var sequenceNumberOfLastProgrammingCommand: Short? = null
var activationTime: Long? = null
@ -682,8 +710,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var bluetoothAddress: String? = null
var ltk: ByteArray? = null
var eapAkaSequenceNumber: Long = 1
var bolusPulsesRemaining: Short = 0
var timeZone: String = "" // TimeZone ID (e.g. "Europe/Amsterdam")
var timeZone: String? = null // TimeZone ID (e.g. "Europe/Amsterdam")
var timeZoneOffset: Int? = null
var timeZoneUpdated: Long? = null
var alarmSynced: Boolean = false
var bleVersion: SoftwareVersion? = null

View file

@ -50,6 +50,7 @@ import org.apache.commons.lang3.StringUtils
import java.time.Duration
import java.time.ZonedDateTime
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.collections.ArrayList
@ -213,6 +214,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
disposables += rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main)
.delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe(
{
updateBluetoothConnectionStatus(it)
@ -254,17 +256,18 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
?: PLACEHOLDER
val connectionSuccessPercentage = podStateManager.connectionSuccessRatio() * 100
val connectionAttempts = podStateManager.failedConnectionsAfterRetries + podStateManager.successfulConnectionAttemptsAfterRetries
val successPercentageString = String.format("%.2f %%", connectionSuccessPercentage)
val quality =
"${podStateManager.successfulConnections}/${podStateManager.connectionAttempts} :: $successPercentageString"
"${podStateManager.successfulConnectionAttemptsAfterRetries}/$connectionAttempts :: $successPercentageString"
bluetoothStatusBinding.omnipodDashBluetoothConnectionQuality.text = quality
val connectionStatsColor = when {
connectionSuccessPercentage > 90 ->
Color.WHITE
connectionSuccessPercentage > 60 ->
connectionSuccessPercentage < 70 && podStateManager.successfulConnectionAttemptsAfterRetries > 50 ->
Color.RED
connectionSuccessPercentage < 90 && podStateManager.successfulConnectionAttemptsAfterRetries > 50 ->
Color.YELLOW
else ->
Color.RED
Color.WHITE
}
bluetoothStatusBinding.omnipodDashBluetoothConnectionQuality.setTextColor(connectionStatsColor)
bluetoothStatusBinding.omnipodDashDeliveryStatus.text = podStateManager.deliveryStatus?.let {
@ -303,12 +306,20 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
podStateManager.bluetoothVersion.toString()
)
// Update time on Pod
val timeZone = podStateManager.timeZoneId?.let { timeZoneId ->
podStateManager.timeZoneUpdated?.let { timeZoneUpdated ->
val tz = TimeZone.getTimeZone(timeZoneId)
val inDST = tz.inDaylightTime(Date(timeZoneUpdated))
val locale = resources.configuration.locales.get(0)
tz.getDisplayName(inDST, TimeZone.SHORT, locale)
} ?: PLACEHOLDER
} ?: PLACEHOLDER
podInfoBinding.timeOnPod.text = podStateManager.time?.let {
rh.gs(
R.string.omnipod_common_time_with_timezone,
dateUtil.dateAndTimeString(it.toEpochSecond() * 1000),
podStateManager.timeZone.getDisplayName(true, TimeZone.SHORT)
timeZone
)
} ?: PLACEHOLDER

View file

@ -46,4 +46,5 @@
<string name="omnipod_dash_unknown">Unknown state for the command</string>
<string name="omnipod_common_history_tbr_value">Rate: %1$.2f U, duration: %2$d minutes</string>
<string name="omnipod_common_history_bolus_value">%1$.2f U</string>
<string name="dash_bolusdelivering">Delivering %1$.2f U</string>
</resources>

View file

@ -50,7 +50,7 @@ public abstract class ErosPodStateManager {
}
public final void discardState() {
this.podState = null;
this.podState = new PodState(this.podState.address);
storePodState();
}

View file

@ -27,7 +27,7 @@ class ErosInitializePodViewModel @Inject constructor(
override fun isPodDeactivatable(): Boolean = podStateManager.activationProgress.isAtLeast(ActivationProgress.PAIRING_COMPLETED)
override fun doExecuteAction(): Single<PumpEnactResult> = Single.just(aapsOmnipodManager.initializePod())
override fun doExecuteAction(): Single<PumpEnactResult> = Single.fromCallable { aapsOmnipodManager.initializePod() }
@StringRes
override fun getTitleId(): Int = R.string.omnipod_common_pod_activation_wizard_initialize_pod_title

View file

@ -29,7 +29,7 @@ class ErosInsertCannulaViewModel @Inject constructor(
override fun isPodDeactivatable(): Boolean = podStateManager.activationProgress.isAtLeast(ActivationProgress.PAIRING_COMPLETED)
override fun doExecuteAction(): Single<PumpEnactResult> = Single.just(aapsOmnipodManager.insertCannula(profileFunction.getProfile()))
override fun doExecuteAction(): Single<PumpEnactResult> = Single.fromCallable { aapsOmnipodManager.insertCannula(profileFunction.getProfile()) }
@StringRes
override fun getTitleId(): Int = R.string.omnipod_common_pod_activation_wizard_insert_cannula_title

View file

@ -323,6 +323,7 @@ abstract class PumpPluginAbstract protected constructor(
return ret
}
@Synchronized
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
return try {
if (detailedBolusInfo.insulin == 0.0 && detailedBolusInfo.carbs == 0.0) {