commit
5cc36fc821
15 changed files with 729 additions and 216 deletions
|
@ -344,6 +344,22 @@ interface PumpSync {
|
||||||
**/
|
**/
|
||||||
fun invalidateTemporaryBasal(id: Long): Boolean
|
fun invalidateTemporaryBasal(id: Long): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate of temporary basals that failed to start
|
||||||
|
* Dash specific, replace by setting duration to zero ????
|
||||||
|
*
|
||||||
|
* If exists, isValid is set false
|
||||||
|
* If db record doesn't exist data is ignored and false returned
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param pumpId pumpId of temporary basal
|
||||||
|
* @param pumpType pump type like PumpType.ACCU_CHEK_COMBO
|
||||||
|
* @param pumpSerial pump serial number
|
||||||
|
* @return true if running record is found and invalidated
|
||||||
|
**/
|
||||||
|
|
||||||
|
fun invalidateTemporaryBasalWithPumpId(pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate of temporary basals that failed to start
|
* Invalidate of temporary basals that failed to start
|
||||||
* MDT specific
|
* MDT specific
|
||||||
|
|
|
@ -330,6 +330,19 @@ class PumpSyncImplementation @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidateTemporaryBasalWithPumpId(pumpId: Long, pumpType: PumpType, pumpSerial: String): Boolean {
|
||||||
|
repository.runTransactionForResult(InvalidateTemporaryBasalTransactionWithPumpId(pumpId, pumpType.toDbPumpType(),
|
||||||
|
pumpSerial))
|
||||||
|
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryBasal", it) }
|
||||||
|
.blockingGet()
|
||||||
|
.also { result ->
|
||||||
|
result.invalidated.forEach {
|
||||||
|
aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it")
|
||||||
|
}
|
||||||
|
return result.invalidated.size > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun invalidateTemporaryBasalWithTempId(temporaryId: Long): Boolean {
|
override fun invalidateTemporaryBasalWithTempId(temporaryId: Long): Boolean {
|
||||||
repository.runTransactionForResult(InvalidateTemporaryBasalWithTempIdTransaction(temporaryId))
|
repository.runTransactionForResult(InvalidateTemporaryBasalWithTempIdTransaction(temporaryId))
|
||||||
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryBasal", it) }
|
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryBasal", it) }
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package info.nightscout.androidaps.database.transactions
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.database.embedments.InterfaceIDs
|
||||||
|
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||||
|
|
||||||
|
class InvalidateTemporaryBasalTransactionWithPumpId(val pumpId: Long, val pumpType: InterfaceIDs.PumpType, val
|
||||||
|
pumpSerial:
|
||||||
|
String) :
|
||||||
|
Transaction<InvalidateTemporaryBasalTransactionWithPumpId.TransactionResult>() {
|
||||||
|
|
||||||
|
override fun run(): TransactionResult {
|
||||||
|
val result = TransactionResult()
|
||||||
|
val temporaryBasal = database.temporaryBasalDao.findByPumpIds(pumpId, pumpType, pumpSerial)
|
||||||
|
?: throw IllegalArgumentException("There is no such Temporary Basal with the specified temp ID.")
|
||||||
|
temporaryBasal.isValid = false
|
||||||
|
database.temporaryBasalDao.updateExistingEntry(temporaryBasal)
|
||||||
|
result.invalidated.add(temporaryBasal)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransactionResult {
|
||||||
|
|
||||||
|
val invalidated = mutableListOf<TemporaryBasal>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,11 +12,12 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
import info.nightscout.androidaps.plugins.common.ManufacturerType
|
import info.nightscout.androidaps.plugins.common.ManufacturerType
|
||||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
|
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
|
||||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType
|
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.*
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.*
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
|
||||||
|
@ -35,16 +36,15 @@ import info.nightscout.androidaps.queue.commands.CustomCommand
|
||||||
import info.nightscout.androidaps.utils.T
|
import info.nightscout.androidaps.utils.T
|
||||||
import info.nightscout.androidaps.utils.TimeChangeType
|
import info.nightscout.androidaps.utils.TimeChangeType
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Observable
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.ceil
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -56,6 +56,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
private val history: DashHistory,
|
private val history: DashHistory,
|
||||||
private val pumpSync: PumpSync,
|
private val pumpSync: PumpSync,
|
||||||
private val rxBus: RxBusWrapper,
|
private val rxBus: RxBusWrapper,
|
||||||
|
private val aapsSchedulers: AapsSchedulers,
|
||||||
|
private val bleManager: OmnipodDashBleManager,
|
||||||
|
|
||||||
// private val disposable: CompositeDisposable = CompositeDisposable(),
|
// private val disposable: CompositeDisposable = CompositeDisposable(),
|
||||||
// private val aapsSchedulers: AapsSchedulers,
|
// private val aapsSchedulers: AapsSchedulers,
|
||||||
|
|
||||||
|
@ -64,8 +67,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
resourceHelper: ResourceHelper,
|
resourceHelper: ResourceHelper,
|
||||||
commandQueue: CommandQueueProvider
|
commandQueue: CommandQueueProvider
|
||||||
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump {
|
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump {
|
||||||
|
@Volatile var bolusCanceled = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong()
|
||||||
|
private const val BOLUS_RETRIES = 5 // numer of retries for cancel/get bolus status
|
||||||
|
|
||||||
private val pluginDescription = PluginDescription()
|
private val pluginDescription = PluginDescription()
|
||||||
.mainType(PluginType.PUMP)
|
.mainType(PluginType.PUMP)
|
||||||
|
@ -94,6 +100,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isConnected(): Boolean {
|
override fun isConnected(): Boolean {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,18 +131,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPumpStatus(reason: String) {
|
override fun getPumpStatus(reason: String) {
|
||||||
val throwable = Completable.concat(
|
val throwable = getPodStatus().blockingGet()
|
||||||
listOf(
|
|
||||||
omnipodManager
|
|
||||||
.getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE)
|
|
||||||
.ignoreElements(),
|
|
||||||
history.updateFromState(podStateManager),
|
|
||||||
podStateManager.updateActiveCommand()
|
|
||||||
.map { handleCommandConfirmation(it) }
|
|
||||||
.ignoreElement(),
|
|
||||||
checkPodKaput()
|
|
||||||
)
|
|
||||||
).blockingGet()
|
|
||||||
if (throwable != null) {
|
if (throwable != null) {
|
||||||
aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable)
|
aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable)
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,26 +139,58 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPodStatus(): Completable = Completable.concat(
|
||||||
|
listOf(
|
||||||
|
omnipodManager
|
||||||
|
.getStatus(ResponseType.StatusResponseType.DEFAULT_STATUS_RESPONSE)
|
||||||
|
.ignoreElements(),
|
||||||
|
history.updateFromState(podStateManager),
|
||||||
|
podStateManager.updateActiveCommand()
|
||||||
|
.map { handleCommandConfirmation(it) }
|
||||||
|
.ignoreElement(),
|
||||||
|
checkPodKaput(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private fun checkPodKaput(): Completable = Completable.defer {
|
private fun checkPodKaput(): Completable = Completable.defer {
|
||||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||||
if (podStateManager.isPodKaput &&
|
if (podStateManager.isPodKaput) {
|
||||||
(tbr == null || tbr.rate != 0.0)
|
if (tbr == null || tbr.rate != 0.0) {
|
||||||
) {
|
pumpSync.syncTemporaryBasalWithPumpId(
|
||||||
pumpSync.syncTemporaryBasalWithPumpId(
|
timestamp = System.currentTimeMillis(),
|
||||||
timestamp = System.currentTimeMillis(),
|
rate = 0.0,
|
||||||
rate = 0.0,
|
duration = T.mins(PodConstants.MAX_POD_LIFETIME.standardMinutes).msecs(),
|
||||||
duration = T.mins(PodConstants.MAX_POD_LIFETIME.standardMinutes).msecs(),
|
isAbsolute = true,
|
||||||
isAbsolute = true,
|
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
||||||
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
|
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
||||||
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
pumpSerial = serialNumber()
|
||||||
pumpSerial = serialNumber()
|
)
|
||||||
)
|
}
|
||||||
|
podStateManager.lastBolus?.run {
|
||||||
|
if (!deliveryComplete) {
|
||||||
|
val deliveredUnits = markComplete()
|
||||||
|
deliveryComplete = true
|
||||||
|
val bolusHistoryEntry = history.getById(historyId)
|
||||||
|
val sync = pumpSync.syncBolusWithPumpId(
|
||||||
|
timestamp = bolusHistoryEntry.createdAt,
|
||||||
|
amount = deliveredUnits,
|
||||||
|
pumpId = bolusHistoryEntry.pumpId(),
|
||||||
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
|
pumpSerial = serialNumber(),
|
||||||
|
type = bolusType
|
||||||
|
)
|
||||||
|
aapsLogger.info(LTag.PUMP, "syncBolusWithPumpId on CANCEL_BOLUS returned: $sync")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Completable.complete()
|
Completable.complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
||||||
|
if (!podStateManager.isActivationCompleted) {
|
||||||
|
return PumpEnactResult(injector).success(true).enacted(true)
|
||||||
|
}
|
||||||
val basalProgram = mapProfileToBasalProgram(profile)
|
val basalProgram = mapProfileToBasalProgram(profile)
|
||||||
return executeProgrammingCommand(
|
return executeProgrammingCommand(
|
||||||
pre = suspendDeliveryIfActive(),
|
pre = suspendDeliveryIfActive(),
|
||||||
|
@ -170,7 +198,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
activeCommandEntry = { historyId ->
|
activeCommandEntry = { historyId ->
|
||||||
podStateManager.createActiveCommand(historyId, basalProgram = basalProgram)
|
podStateManager.createActiveCommand(historyId, basalProgram = basalProgram)
|
||||||
},
|
},
|
||||||
command = omnipodManager.setBasalProgram(basalProgram).ignoreElements(),
|
command = omnipodManager.setBasalProgram(basalProgram, hasBasalBeepEnabled()).ignoreElements(),
|
||||||
post = failWhenUnconfirmed(),
|
post = failWhenUnconfirmed(),
|
||||||
).toPumpEnactResult()
|
).toPumpEnactResult()
|
||||||
}
|
}
|
||||||
|
@ -190,11 +218,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
else
|
else
|
||||||
executeProgrammingCommand(
|
executeProgrammingCommand(
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
|
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
|
||||||
command = omnipodManager.suspendDelivery()
|
command = omnipodManager.suspendDelivery(hasBasalBeepEnabled())
|
||||||
.filter { podEvent -> podEvent is PodEvent.CommandSent }
|
.filter { podEvent -> podEvent.isCommandSent() }
|
||||||
.map {
|
.map {
|
||||||
pumpSyncTempBasal(
|
pumpSyncTempBasal(
|
||||||
it,
|
|
||||||
0.0,
|
0.0,
|
||||||
PodConstants.MAX_POD_LIFETIME.standardMinutes,
|
PodConstants.MAX_POD_LIFETIME.standardMinutes,
|
||||||
PumpSync.TemporaryBasalType.PUMP_SUSPEND
|
PumpSync.TemporaryBasalType.PUMP_SUSPEND
|
||||||
|
@ -205,12 +232,12 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* override fun onStop() {
|
/* override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private fun observeDeliverySuspended(): Completable = Completable.defer {
|
private fun observeDeliverySuspended(): Completable = Completable.defer {
|
||||||
if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED)
|
if (podStateManager.deliveryStatus == DeliveryStatus.SUSPENDED)
|
||||||
|
@ -240,7 +267,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
get() {
|
get() {
|
||||||
val date = Date()
|
val date = Date()
|
||||||
val ret = podStateManager.basalProgram?.rateAt(date) ?: 0.0
|
val ret = podStateManager.basalProgram?.rateAt(date) ?: 0.0
|
||||||
aapsLogger.info(LTag.PUMP, "baseBasalRate: %ret at $date}")
|
aapsLogger.info(LTag.PUMP, "baseBasalRate: $ret at $date}")
|
||||||
return if (podStateManager.alarmType != null) {
|
return if (podStateManager.alarmType != null) {
|
||||||
0.0
|
0.0
|
||||||
} else
|
} else
|
||||||
|
@ -264,65 +291,211 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
get() = 0
|
get() = 0
|
||||||
|
|
||||||
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
|
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
|
||||||
// TODO update Treatments (?)
|
try {
|
||||||
// TODO bolus progress
|
aapsLogger.info(LTag.PUMP, "Delivering treatment: $detailedBolusInfo")
|
||||||
// TODO report actual delivered amount after Pod Alarm and bolus cancellation
|
val beepsConfigurationKey = if (detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB)
|
||||||
|
R.string.key_omnipod_common_smb_beeps_enabled
|
||||||
|
else
|
||||||
|
R.string.key_omnipod_common_bolus_beeps_enabled
|
||||||
|
val bolusBeeps = sp.getBoolean(beepsConfigurationKey, false)
|
||||||
|
R.string.key_omnipod_common_smb_beeps_enabled
|
||||||
|
if (detailedBolusInfo.carbs > 0 ||
|
||||||
|
detailedBolusInfo.insulin == 0.0
|
||||||
|
) {
|
||||||
|
// Accept only valid insulin requests
|
||||||
|
return PumpEnactResult(injector)
|
||||||
|
.success(false)
|
||||||
|
.enacted(false)
|
||||||
|
.bolusDelivered(0.0)
|
||||||
|
.comment("Invalid input")
|
||||||
|
}
|
||||||
|
val requestedBolusAmount = detailedBolusInfo.insulin
|
||||||
|
if (requestedBolusAmount > reservoirLevel) {
|
||||||
|
return PumpEnactResult(injector)
|
||||||
|
.success(false)
|
||||||
|
.enacted(false)
|
||||||
|
.bolusDelivered(0.0)
|
||||||
|
.comment("Not enough insulin in the reservoir")
|
||||||
|
}
|
||||||
|
var deliveredBolusAmount = 0.0
|
||||||
|
|
||||||
return Single.create<PumpEnactResult> { source ->
|
aapsLogger.info(
|
||||||
val bolusBeeps = sp.getBoolean(R.string.key_omnipod_common_bolus_beeps_enabled, false)
|
LTag.PUMP,
|
||||||
|
"deliverTreatment: requestedBolusAmount=$requestedBolusAmount"
|
||||||
Observable.concat(
|
)
|
||||||
history.createRecord(
|
val ret = executeProgrammingCommand(
|
||||||
|
pre = observeDeliveryNotCanceled(),
|
||||||
|
historyEntry = history.createRecord(
|
||||||
commandType = OmnipodCommandType.SET_BOLUS,
|
commandType = OmnipodCommandType.SET_BOLUS,
|
||||||
bolusRecord = BolusRecord(
|
bolusRecord = BolusRecord(
|
||||||
detailedBolusInfo.insulin,
|
requestedBolusAmount,
|
||||||
BolusType.fromBolusInfoBolusType(detailedBolusInfo.bolusType),
|
BolusType.fromBolusInfoBolusType(detailedBolusInfo.bolusType)
|
||||||
),
|
)
|
||||||
).flatMapObservable { recordId ->
|
),
|
||||||
podStateManager.createActiveCommand(recordId).toObservable()
|
activeCommandEntry = { historyId ->
|
||||||
|
podStateManager.createActiveCommand(
|
||||||
|
historyId,
|
||||||
|
requestedBolus = requestedBolusAmount
|
||||||
|
)
|
||||||
},
|
},
|
||||||
omnipodManager.bolus(
|
command = omnipodManager.bolus(
|
||||||
detailedBolusInfo.insulin,
|
detailedBolusInfo.insulin,
|
||||||
bolusBeeps,
|
bolusBeeps,
|
||||||
bolusBeeps
|
bolusBeeps
|
||||||
),
|
).filter { podEvent -> podEvent.isCommandSent() }
|
||||||
history.updateFromState(podStateManager).toObservable(),
|
.map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) }
|
||||||
podStateManager.updateActiveCommand().toObservable(),
|
.ignoreElements(),
|
||||||
).subscribeBy(
|
post = waitForBolusDeliveryToComplete(BOLUS_RETRIES, requestedBolusAmount, detailedBolusInfo.bolusType)
|
||||||
onNext = { podEvent ->
|
.map {
|
||||||
aapsLogger.debug(
|
deliveredBolusAmount = it
|
||||||
LTag.PUMP,
|
aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount")
|
||||||
"Received PodEvent in deliverTreatment: $podEvent"
|
}
|
||||||
)
|
.ignoreElement()
|
||||||
},
|
).toSingleDefault(
|
||||||
onError = { throwable ->
|
PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(deliveredBolusAmount)
|
||||||
aapsLogger.error(LTag.PUMP, "Error in deliverTreatment", throwable)
|
|
||||||
source.onSuccess(
|
|
||||||
PumpEnactResult(injector).success(false).enacted(false).comment(
|
|
||||||
throwable.toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onComplete = {
|
|
||||||
aapsLogger.debug("deliverTreatment completed")
|
|
||||||
source.onSuccess(
|
|
||||||
PumpEnactResult(injector).success(true).enacted(true)
|
|
||||||
.bolusDelivered(detailedBolusInfo.insulin)
|
|
||||||
.carbsDelivered(detailedBolusInfo.carbs)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}.blockingGet()
|
.onErrorReturnItem(
|
||||||
|
// success if canceled
|
||||||
|
PumpEnactResult(injector).success(bolusCanceled).enacted(false)
|
||||||
|
)
|
||||||
|
.blockingGet()
|
||||||
|
aapsLogger.info(LTag.PUMP, "deliverTreatment result: $ret")
|
||||||
|
return ret
|
||||||
|
} finally {
|
||||||
|
bolusCanceled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeDeliveryNotCanceled(): Completable = Completable.defer {
|
||||||
|
if (bolusCanceled) {
|
||||||
|
Completable.error(java.lang.IllegalStateException("Bolus canceled"))
|
||||||
|
} else {
|
||||||
|
Completable.complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateBolusProgressDialog(msg: String, percent: Int) {
|
||||||
|
val progressUpdateEvent = EventOverviewBolusProgress
|
||||||
|
val percent = percent
|
||||||
|
progressUpdateEvent.status = msg
|
||||||
|
progressUpdateEvent.percent = percent
|
||||||
|
rxBus.send(progressUpdateEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun waitForBolusDeliveryToComplete(
|
||||||
|
maxTries: Int,
|
||||||
|
requestedBolusAmount: Double,
|
||||||
|
bolusType: DetailedBolusInfo.BolusType
|
||||||
|
): Single<Double> = Single.defer {
|
||||||
|
|
||||||
|
if (bolusCanceled && podStateManager.activeCommand != null) {
|
||||||
|
var errorGettingStatus: Throwable? = null
|
||||||
|
for (tries in 1..maxTries) {
|
||||||
|
errorGettingStatus = getPodStatus().blockingGet()
|
||||||
|
if (errorGettingStatus != null) {
|
||||||
|
aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus")
|
||||||
|
Thread.sleep(BOLUS_RETRY_INTERVAL_MS) // retry every 2 sec
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorGettingStatus != null) {
|
||||||
|
// requestedBolusAmount will be updated later, via pumpSync
|
||||||
|
// This state is: cancellation requested and getPodStatus failed 5 times.
|
||||||
|
return@defer Single.just(requestedBolusAmount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val estimatedDeliveryTimeSeconds = estimateBolusDeliverySeconds(requestedBolusAmount)
|
||||||
|
aapsLogger.info(LTag.PUMP, "estimatedDeliveryTimeSeconds: $estimatedDeliveryTimeSeconds")
|
||||||
|
var waited = 0
|
||||||
|
while (waited < estimatedDeliveryTimeSeconds && !bolusCanceled) {
|
||||||
|
waited += 1
|
||||||
|
Thread.sleep(1000)
|
||||||
|
if (bolusType == DetailedBolusInfo.BolusType.SMB) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val percent = (waited.toFloat() / estimatedDeliveryTimeSeconds) * 100
|
||||||
|
updateBolusProgressDialog(resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount), percent.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tryNumber in 1..maxTries) {
|
||||||
|
updateBolusProgressDialog("Checking delivery status", 100)
|
||||||
|
|
||||||
|
val cmd = if (bolusCanceled)
|
||||||
|
cancelBolus()
|
||||||
|
else
|
||||||
|
getPodStatus()
|
||||||
|
|
||||||
|
val errorGettingStatus = cmd.blockingGet()
|
||||||
|
if (errorGettingStatus != null) {
|
||||||
|
aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus")
|
||||||
|
Thread.sleep(BOLUS_RETRY_INTERVAL_MS) // retry every 3 sec
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val bolusDeliveringActive = podStateManager.deliveryStatus?.bolusDeliveringActive() ?: false
|
||||||
|
|
||||||
|
if (bolusDeliveringActive) {
|
||||||
|
// delivery not complete yet
|
||||||
|
val remainingUnits = podStateManager.lastBolus!!.bolusUnitsRemaining
|
||||||
|
val percent = ((requestedBolusAmount - remainingUnits) / requestedBolusAmount) * 100
|
||||||
|
updateBolusProgressDialog(
|
||||||
|
resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount),
|
||||||
|
percent.toInt()
|
||||||
|
)
|
||||||
|
|
||||||
|
val sleepSeconds = if (bolusCanceled)
|
||||||
|
BOLUS_RETRY_INTERVAL_MS
|
||||||
|
else
|
||||||
|
estimateBolusDeliverySeconds(remainingUnits)
|
||||||
|
Thread.sleep(sleepSeconds * 1000.toLong())
|
||||||
|
} else {
|
||||||
|
// delivery is complete. If pod is Kaput, we are handling this in getPodStatus
|
||||||
|
return@defer Single.just(podStateManager.lastBolus!!.deliveredUnits()!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Single.just(requestedBolusAmount) // will be updated later!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelBolus(): Completable {
|
||||||
|
val bolusBeeps = sp.getBoolean(R.string.key_omnipod_common_bolus_beeps_enabled, false)
|
||||||
|
return executeProgrammingCommand(
|
||||||
|
historyEntry = history.createRecord(commandType = OmnipodCommandType.CANCEL_BOLUS),
|
||||||
|
command = omnipodManager.stopBolus(bolusBeeps).ignoreElements(),
|
||||||
|
checkNoActiveCommand = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long {
|
||||||
|
return ceil(requestedBolusAmount / 0.05).toLong() * 2 + 3
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pumpSyncBolusStart(
|
||||||
|
requestedBolusAmount: Double,
|
||||||
|
bolusType: DetailedBolusInfo.BolusType
|
||||||
|
): Boolean {
|
||||||
|
val activeCommand = podStateManager.activeCommand
|
||||||
|
if (activeCommand == null) {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"No active command or illegal podEvent: " +
|
||||||
|
"activeCommand=$activeCommand"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val historyEntry = history.getById(activeCommand.historyId)
|
||||||
|
val ret = pumpSync.syncBolusWithPumpId(
|
||||||
|
timestamp = historyEntry.createdAt,
|
||||||
|
amount = requestedBolusAmount,
|
||||||
|
type = bolusType,
|
||||||
|
pumpId = historyEntry.pumpId(),
|
||||||
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
|
pumpSerial = serialNumber()
|
||||||
|
)
|
||||||
|
aapsLogger.debug(LTag.PUMP, "pumpSyncBolusStart: $ret")
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopBolusDelivering() {
|
override fun stopBolusDelivering() {
|
||||||
// TODO update Treatments (?)
|
|
||||||
aapsLogger.info(LTag.PUMP, "stopBolusDelivering called")
|
aapsLogger.info(LTag.PUMP, "stopBolusDelivering called")
|
||||||
val ret = executeProgrammingCommand(
|
bolusCanceled = true
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_BOLUS),
|
|
||||||
command = omnipodManager.stopBolus().ignoreElements()
|
|
||||||
).toPumpEnactResult()
|
|
||||||
aapsLogger.info(LTag.PUMP, "stopBolusDelivering finished with result: $ret")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setTempBasalAbsolute(
|
override fun setTempBasalAbsolute(
|
||||||
|
@ -332,13 +505,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
enforceNew: Boolean,
|
enforceNew: Boolean,
|
||||||
tbrType: PumpSync.TemporaryBasalType
|
tbrType: PumpSync.TemporaryBasalType
|
||||||
): PumpEnactResult {
|
): PumpEnactResult {
|
||||||
val tempBasalBeeps = sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false)
|
val tempBasalBeeps = hasTempBasalBeepEnabled()
|
||||||
aapsLogger.info(
|
aapsLogger.info(
|
||||||
LTag.PUMP,
|
LTag.PUMP,
|
||||||
"setTempBasalAbsolute: $durationInMinutes min :: $absoluteRate U/h :: " +
|
"setTempBasalAbsolute: duration=$durationInMinutes min, rate=$absoluteRate U/h :: " +
|
||||||
"enforce: $enforceNew :: tbrType: $tbrType"
|
"enforce=$enforceNew, tbrType=$tbrType"
|
||||||
)
|
)
|
||||||
return executeProgrammingCommand(
|
|
||||||
|
val ret = executeProgrammingCommand(
|
||||||
|
pre = observeNoActiveTempBasal(true),
|
||||||
historyEntry = history.createRecord(
|
historyEntry = history.createRecord(
|
||||||
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
|
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
|
||||||
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
|
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
|
||||||
|
@ -358,29 +533,31 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
durationInMinutes.toShort(),
|
durationInMinutes.toShort(),
|
||||||
tempBasalBeeps
|
tempBasalBeeps
|
||||||
)
|
)
|
||||||
.filter { podEvent -> podEvent is PodEvent.CommandSent }
|
.filter { podEvent -> podEvent.isCommandSent() }
|
||||||
.map { pumpSyncTempBasal(it, absoluteRate, durationInMinutes.toLong(), tbrType) }
|
.map { pumpSyncTempBasal(absoluteRate, durationInMinutes.toLong(), tbrType) }
|
||||||
.ignoreElements(),
|
.ignoreElements(),
|
||||||
pre = observeNoActiveTempBasal(true),
|
|
||||||
).toPumpEnactResult()
|
).toPumpEnactResult()
|
||||||
|
if (ret.success && ret.enacted) {
|
||||||
|
ret.isPercent(false).absolute(absoluteRate).duration(durationInMinutes)
|
||||||
|
}
|
||||||
|
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute: result=$ret")
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pumpSyncTempBasal(
|
private fun pumpSyncTempBasal(
|
||||||
podEvent: PodEvent,
|
|
||||||
absoluteRate: Double,
|
absoluteRate: Double,
|
||||||
durationInMinutes: Long,
|
durationInMinutes: Long,
|
||||||
tbrType: PumpSync.TemporaryBasalType
|
tbrType: PumpSync.TemporaryBasalType
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val activeCommand = podStateManager.activeCommand
|
val activeCommand = podStateManager.activeCommand
|
||||||
if (activeCommand == null || podEvent !is PodEvent.CommandSent) {
|
if (activeCommand == null) {
|
||||||
throw IllegalArgumentException(
|
throw IllegalArgumentException(
|
||||||
"No active command or illegal podEvent: " +
|
"No active command: " +
|
||||||
"activeCommand=$activeCommand" +
|
"activeCommand=$activeCommand"
|
||||||
"podEvent=$podEvent"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val historyEntry = history.getById(activeCommand.historyId)
|
val historyEntry = history.getById(activeCommand.historyId)
|
||||||
|
aapsLogger.debug(LTag.PUMP, "pumpSyncTempBasal: absoluteRate=$absoluteRate, durationInMinutes=$durationInMinutes")
|
||||||
val ret = pumpSync.syncTemporaryBasalWithPumpId(
|
val ret = pumpSync.syncTemporaryBasalWithPumpId(
|
||||||
timestamp = historyEntry.createdAt,
|
timestamp = historyEntry.createdAt,
|
||||||
rate = absoluteRate,
|
rate = absoluteRate,
|
||||||
|
@ -391,11 +568,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
pumpType = PumpType.OMNIPOD_DASH,
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
pumpSerial = serialNumber()
|
pumpSerial = serialNumber()
|
||||||
)
|
)
|
||||||
aapsLogger.debug(LTag.PUMP, "Pump sync temp basal: $ret")
|
aapsLogger.debug(LTag.PUMP, "pumpSyncTempBasal: $ret")
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeNoActiveTempBasal(): Completable {
|
private fun observeNoActiveTempBasal(enforceNew: Boolean): Completable {
|
||||||
return Completable.defer {
|
return Completable.defer {
|
||||||
when {
|
when {
|
||||||
podStateManager.deliveryStatus !in
|
podStateManager.deliveryStatus !in
|
||||||
|
@ -417,27 +594,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
|
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
|
||||||
executeProgrammingCommand(
|
executeProgrammingCommand(
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
||||||
command = omnipodManager.stopTempBasal().ignoreElements()
|
command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeActiveTempBasal(): Completable {
|
|
||||||
|
|
||||||
return Completable.defer {
|
|
||||||
if (podStateManager.tempBasalActive || pumpSync.expectedPumpState().temporaryBasal != null)
|
|
||||||
Completable.complete()
|
|
||||||
else
|
|
||||||
Completable.error(
|
|
||||||
java.lang.IllegalStateException(
|
|
||||||
"There is no active basal to cancel"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setTempBasalPercent(
|
override fun setTempBasalPercent(
|
||||||
percent: Int,
|
percent: Int,
|
||||||
durationInMinutes: Int,
|
durationInMinutes: Int,
|
||||||
|
@ -456,15 +619,29 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
.comment("Omnipod Dash driver does not support extended boluses")
|
.comment("Omnipod Dash driver does not support extended boluses")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hasTempBasalBeepEnabled(): Boolean {
|
||||||
|
return sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasBasalBeepEnabled(): Boolean {
|
||||||
|
return sp.getBoolean(R.string.key_omnipod_common_basal_beeps_enabled, false)
|
||||||
|
}
|
||||||
|
|
||||||
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
||||||
|
if (!podStateManager.tempBasalActive &&
|
||||||
|
pumpSync.expectedPumpState().temporaryBasal == null
|
||||||
|
) {
|
||||||
|
// nothing to cancel
|
||||||
|
return PumpEnactResult(injector).success(true).enacted(false)
|
||||||
|
}
|
||||||
|
|
||||||
return executeProgrammingCommand(
|
return executeProgrammingCommand(
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
|
||||||
command = omnipodManager.stopTempBasal().ignoreElements(),
|
command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements(),
|
||||||
pre = observeActiveTempBasal(),
|
|
||||||
).toPumpEnactResult()
|
).toPumpEnactResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Completable.toPumpEnactResult(): PumpEnactResult {
|
private fun Completable.toPumpEnactResult(): PumpEnactResult {
|
||||||
return this.toSingleDefault(PumpEnactResult(injector).success(true).enacted(true))
|
return this.toSingleDefault(PumpEnactResult(injector).success(true).enacted(true))
|
||||||
.doOnError { throwable ->
|
.doOnError { throwable ->
|
||||||
aapsLogger.error(LTag.PUMP, "toPumpEnactResult, error executing command: $throwable")
|
aapsLogger.error(LTag.PUMP, "toPumpEnactResult, error executing command: $throwable")
|
||||||
|
@ -567,11 +744,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
private fun suspendDelivery(): PumpEnactResult {
|
private fun suspendDelivery(): PumpEnactResult {
|
||||||
return executeProgrammingCommand(
|
return executeProgrammingCommand(
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
|
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
|
||||||
command = omnipodManager.suspendDelivery()
|
command = omnipodManager.suspendDelivery(hasBasalBeepEnabled())
|
||||||
.filter { podEvent -> podEvent is PodEvent.CommandSent }
|
.filter { podEvent -> podEvent.isCommandSent() }
|
||||||
.map {
|
.map {
|
||||||
pumpSyncTempBasal(
|
pumpSyncTempBasal(
|
||||||
it,
|
|
||||||
0.0,
|
0.0,
|
||||||
PodConstants.MAX_POD_LIFETIME.standardMinutes,
|
PodConstants.MAX_POD_LIFETIME.standardMinutes,
|
||||||
PumpSync.TemporaryBasalType.PUMP_SUSPEND
|
PumpSync.TemporaryBasalType.PUMP_SUSPEND
|
||||||
|
@ -594,16 +770,20 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
executeProgrammingCommand(
|
executeProgrammingCommand(
|
||||||
pre = observeDeliverySuspended(),
|
pre = observeDeliverySuspended(),
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
|
historyEntry = history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
|
||||||
command = omnipodManager.setBasalProgram(mapProfileToBasalProgram(it)).ignoreElements()
|
command = omnipodManager.setBasalProgram(mapProfileToBasalProgram(it), hasBasalBeepEnabled()).ignoreElements()
|
||||||
).toPumpEnactResult()
|
).toPumpEnactResult()
|
||||||
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n
|
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deactivatePod(): PumpEnactResult {
|
private fun deactivatePod(): PumpEnactResult {
|
||||||
return executeProgrammingCommand(
|
val ret = executeProgrammingCommand(
|
||||||
historyEntry = history.createRecord(OmnipodCommandType.DEACTIVATE_POD),
|
historyEntry = history.createRecord(OmnipodCommandType.DEACTIVATE_POD),
|
||||||
command = omnipodManager.deactivatePod().ignoreElements()
|
command = omnipodManager.deactivatePod().ignoreElements()
|
||||||
).toPumpEnactResult()
|
).toPumpEnactResult()
|
||||||
|
if (podStateManager.activeCommand != null) {
|
||||||
|
ret.success(false)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleTimeChange(): PumpEnactResult {
|
private fun handleTimeChange(): PumpEnactResult {
|
||||||
|
@ -651,11 +831,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
{ historyId -> podStateManager.createActiveCommand(historyId) },
|
{ historyId -> podStateManager.createActiveCommand(historyId) },
|
||||||
command: Completable,
|
command: Completable,
|
||||||
post: Completable = Completable.complete(),
|
post: Completable = Completable.complete(),
|
||||||
|
checkNoActiveCommand: Boolean = true
|
||||||
): Completable {
|
): Completable {
|
||||||
return Completable.concat(
|
return Completable.concat(
|
||||||
listOf(
|
listOf(
|
||||||
pre,
|
pre,
|
||||||
podStateManager.observeNoActiveCommand().ignoreElements(),
|
if (checkNoActiveCommand)
|
||||||
|
podStateManager.observeNoActiveCommand()
|
||||||
|
else
|
||||||
|
Completable.complete(),
|
||||||
historyEntry
|
historyEntry
|
||||||
.flatMap { activeCommandEntry(it) }
|
.flatMap { activeCommandEntry(it) }
|
||||||
.ignoreElement(),
|
.ignoreElement(),
|
||||||
|
@ -667,6 +851,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
podStateManager.updateActiveCommand()
|
podStateManager.updateActiveCommand()
|
||||||
.map { handleCommandConfirmation(it) }
|
.map { handleCommandConfirmation(it) }
|
||||||
.ignoreElement(),
|
.ignoreElement(),
|
||||||
|
checkPodKaput(),
|
||||||
post,
|
post,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -712,9 +897,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
OmnipodCommandType.SET_TEMPORARY_BASAL -> {
|
OmnipodCommandType.SET_TEMPORARY_BASAL -> {
|
||||||
// This treatment was synced before sending the command
|
// This treatment was synced before sending the command
|
||||||
aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}")
|
|
||||||
if (!confirmation.success) {
|
if (!confirmation.success) {
|
||||||
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
|
aapsLogger.info(LTag.PUMPCOMM, "temporary basal denied. PumpId: ${historyEntry.pumpId()}")
|
||||||
|
pumpSync.invalidateTemporaryBasalWithPumpId(
|
||||||
|
historyEntry.pumpId(),
|
||||||
|
PumpType.OMNIPOD_DASH,
|
||||||
|
serialNumber()
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
podStateManager.tempBasal = command.tempBasal
|
podStateManager.tempBasal = command.tempBasal
|
||||||
}
|
}
|
||||||
|
@ -722,17 +911,68 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
||||||
|
|
||||||
OmnipodCommandType.SUSPEND_DELIVERY -> {
|
OmnipodCommandType.SUSPEND_DELIVERY -> {
|
||||||
if (!confirmation.success) {
|
if (!confirmation.success) {
|
||||||
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
|
pumpSync.invalidateTemporaryBasalWithPumpId(historyEntry.pumpId(), PumpType.OMNIPOD_DASH, serialNumber())
|
||||||
} else {
|
} else {
|
||||||
podStateManager.tempBasal = null
|
podStateManager.tempBasal = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmnipodCommandType.SET_BOLUS -> {
|
||||||
|
if (confirmation.success) {
|
||||||
|
if (command.requestedBolus == null) {
|
||||||
|
aapsLogger.error(LTag.PUMP, "Requested bolus not found: $command")
|
||||||
|
}
|
||||||
|
val record = historyEntry.record
|
||||||
|
if (record !is BolusRecord) {
|
||||||
|
aapsLogger.error(
|
||||||
|
LTag.PUMP,
|
||||||
|
"Expected SET_BOLUS history record to be a BolusRecord, found " +
|
||||||
|
"$record"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
record as BolusRecord
|
||||||
|
|
||||||
|
podStateManager.createLastBolus(
|
||||||
|
record.amout,
|
||||||
|
command.historyId,
|
||||||
|
record.bolusType.toBolusInfoBolusType()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pumpSync.syncBolusWithPumpId(
|
||||||
|
timestamp = historyEntry.createdAt,
|
||||||
|
amount = 0.0,
|
||||||
|
pumpId = historyEntry.pumpId(),
|
||||||
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
|
pumpSerial = serialNumber(),
|
||||||
|
type = null // TODO: set the correct bolus type here!!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OmnipodCommandType.CANCEL_BOLUS -> {
|
||||||
|
if (confirmation.success) {
|
||||||
|
|
||||||
|
podStateManager.lastBolus?.run {
|
||||||
|
val deliveredUnits = markComplete()
|
||||||
|
val bolusHistoryEntry = history.getById(historyId)
|
||||||
|
val sync = pumpSync.syncBolusWithPumpId(
|
||||||
|
timestamp = bolusHistoryEntry.createdAt,
|
||||||
|
amount = deliveredUnits, // we just marked this bolus as complete
|
||||||
|
pumpId = bolusHistoryEntry.pumpId(),
|
||||||
|
pumpType = PumpType.OMNIPOD_DASH,
|
||||||
|
pumpSerial = serialNumber(),
|
||||||
|
type = bolusType
|
||||||
|
)
|
||||||
|
aapsLogger.info(LTag.PUMP, "syncBolusWithPumpId on CANCEL_BOLUS returned: $sync")
|
||||||
|
} ?: aapsLogger.error(LTag.PUMP, "Cancelled bolus that does not exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
aapsLogger.warn(
|
aapsLogger.warn(
|
||||||
LTag.PUMP,
|
LTag.PUMP,
|
||||||
"Will not sync confirmed command of type: $historyEntry and " +
|
"Will not sync confirmed command of type: $historyEntry and " +
|
||||||
"succes: ${confirmation.success}"
|
"success: ${confirmation.success}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,19 @@ interface OmnipodDashManager {
|
||||||
|
|
||||||
fun getStatus(type: ResponseType.StatusResponseType): Observable<PodEvent>
|
fun getStatus(type: ResponseType.StatusResponseType): Observable<PodEvent>
|
||||||
|
|
||||||
fun setBasalProgram(basalProgram: BasalProgram): Observable<PodEvent>
|
fun setBasalProgram(basalProgram: BasalProgram, hasBasalBeepEnabled: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun suspendDelivery(): Observable<PodEvent>
|
fun suspendDelivery(hasBasalBeepEnabled: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun setTime(): Observable<PodEvent>
|
fun setTime(): Observable<PodEvent>
|
||||||
|
|
||||||
fun setTempBasal(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent>
|
fun setTempBasal(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun stopTempBasal(): Observable<PodEvent>
|
fun stopTempBasal(hasTempBasalBeepEnabled: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun bolus(units: Double, confirmationBeeps: Boolean, completionBeeps: Boolean): Observable<PodEvent>
|
fun bolus(units: Double, confirmationBeeps: Boolean, completionBeeps: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun stopBolus(): Observable<PodEvent>
|
fun stopBolus(beep: Boolean): Observable<PodEvent>
|
||||||
|
|
||||||
fun playBeep(beepType: BeepType): Observable<PodEvent>
|
fun playBeep(beepType: BeepType): Observable<PodEvent>
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,8 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
private val observeConnectToPod: Observable<PodEvent>
|
private val observeConnectToPod: Observable<PodEvent>
|
||||||
get() = Observable.defer {
|
get() = Observable.defer {
|
||||||
bleManager.connect()
|
bleManager.connect()
|
||||||
} // TODO add retry
|
.doOnError { throwable -> logger.warn(LTag.PUMPBTCOMM, "observeConnectToPod error=$throwable") }
|
||||||
|
}
|
||||||
|
|
||||||
private val observePairNewPod: Observable<PodEvent>
|
private val observePairNewPod: Observable<PodEvent>
|
||||||
get() = Observable.defer {
|
get() = Observable.defer {
|
||||||
|
@ -156,7 +157,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeSendProgramBasalCommand(basalProgram: BasalProgram): Observable<PodEvent> {
|
private fun observeSendProgramBasalCommand(basalProgram: BasalProgram, hasBasalBeepEnabled: Boolean): Observable<PodEvent> {
|
||||||
return Observable.defer {
|
return Observable.defer {
|
||||||
val currentTime = Date()
|
val currentTime = Date()
|
||||||
logger.debug(LTag.PUMPCOMM, "Programming basal. currentTime={}, basalProgram={}", currentTime, basalProgram)
|
logger.debug(LTag.PUMPCOMM, "Programming basal. currentTime={}, basalProgram={}", currentTime, basalProgram)
|
||||||
|
@ -165,7 +166,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
.setNonce(NONCE)
|
.setNonce(NONCE)
|
||||||
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
|
.setProgramReminder(ProgramReminder(atStart = hasBasalBeepEnabled, atEnd = false, atInterval = 0))
|
||||||
.setBasalProgram(basalProgram)
|
.setBasalProgram(basalProgram)
|
||||||
.setCurrentTime(currentTime)
|
.setCurrentTime(currentTime)
|
||||||
.build(),
|
.build(),
|
||||||
|
@ -218,7 +219,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodReadyForActivationPart1,
|
observePodReadyForActivationPart1,
|
||||||
observePairNewPod,
|
observePairNewPod,
|
||||||
observeConnectToPod, // FIXME needed after disconnect; observePairNewPod does not connect in that case.
|
observeConnectToPod,
|
||||||
observeActivationPart1Commands(lowReservoirAlertTrigger)
|
observeActivationPart1Commands(lowReservoirAlertTrigger)
|
||||||
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
|
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PHASE_1_COMPLETED))
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
|
@ -246,6 +247,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) {
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) {
|
||||||
|
observables.add(observeConnectToPod) // connection can time out while waiting
|
||||||
observables.add(
|
observables.add(
|
||||||
Observable.defer {
|
Observable.defer {
|
||||||
Observable.timer(podStateManager.firstPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS)
|
Observable.timer(podStateManager.firstPrimeBolusVolume!!.toLong(), TimeUnit.SECONDS)
|
||||||
|
@ -397,7 +399,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
if (podStateManager.activationProgress.isBefore(ActivationProgress.PROGRAMMED_BASAL)) {
|
if (podStateManager.activationProgress.isBefore(ActivationProgress.PROGRAMMED_BASAL)) {
|
||||||
observables.add(
|
observables.add(
|
||||||
observeSendProgramBasalCommand(basalProgram)
|
observeSendProgramBasalCommand(basalProgram, false)
|
||||||
.doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_BASAL))
|
.doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_BASAL))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -417,11 +419,11 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.subscribeOn(aapsSchedulers.io)
|
.subscribeOn(aapsSchedulers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setBasalProgram(basalProgram: BasalProgram): Observable<PodEvent> {
|
override fun setBasalProgram(basalProgram: BasalProgram, hasBasalBeepEnabled: Boolean): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodRunning,
|
observePodRunning,
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendProgramBasalCommand(basalProgram)
|
observeSendProgramBasalCommand(basalProgram, hasBasalBeepEnabled)
|
||||||
)
|
)
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
.doOnNext(PodEventInterceptor())
|
.doOnNext(PodEventInterceptor())
|
||||||
|
@ -429,25 +431,34 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.subscribeOn(aapsSchedulers.io)
|
.subscribeOn(aapsSchedulers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeSendStopDeliveryCommand(deliveryType: StopDeliveryCommand.DeliveryType): Observable<PodEvent> {
|
private fun observeSendStopDeliveryCommand(
|
||||||
|
deliveryType: StopDeliveryCommand.DeliveryType,
|
||||||
|
beepEnabled: Boolean
|
||||||
|
): Observable<PodEvent> {
|
||||||
return Observable.defer {
|
return Observable.defer {
|
||||||
|
val beepType = if (!beepEnabled)
|
||||||
|
BeepType.SILENT
|
||||||
|
else
|
||||||
|
BeepType.LONG_SINGLE_BEEP
|
||||||
|
|
||||||
bleManager.sendCommand(
|
bleManager.sendCommand(
|
||||||
StopDeliveryCommand.Builder()
|
StopDeliveryCommand.Builder()
|
||||||
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||||
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||||
.setNonce(NONCE)
|
.setNonce(NONCE)
|
||||||
.setDeliveryType(deliveryType)
|
.setDeliveryType(deliveryType)
|
||||||
|
.setBeepType(beepType)
|
||||||
.build(),
|
.build(),
|
||||||
DefaultStatusResponse::class
|
DefaultStatusResponse::class
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun suspendDelivery(): Observable<PodEvent> {
|
override fun suspendDelivery(hasBasalBeepEnabled: Boolean): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodRunning,
|
observePodRunning,
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL)
|
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled)
|
||||||
)
|
)
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
.doOnNext(PodEventInterceptor())
|
.doOnNext(PodEventInterceptor())
|
||||||
|
@ -489,11 +500,11 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.subscribeOn(aapsSchedulers.io)
|
.subscribeOn(aapsSchedulers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopTempBasal(): Observable<PodEvent> {
|
override fun stopTempBasal(hasTempBasalBeepEnabled: Boolean): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodRunning,
|
observePodRunning,
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.TEMP_BASAL)
|
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.TEMP_BASAL, hasTempBasalBeepEnabled)
|
||||||
)
|
)
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
.doOnNext(PodEventInterceptor())
|
.doOnNext(PodEventInterceptor())
|
||||||
|
@ -518,11 +529,11 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
.subscribeOn(aapsSchedulers.io)
|
.subscribeOn(aapsSchedulers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stopBolus(): Observable<PodEvent> {
|
override fun stopBolus(beep: Boolean): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodRunning,
|
observePodRunning,
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.BOLUS)
|
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.BOLUS, beep)
|
||||||
)
|
)
|
||||||
// TODO these would be common for any observable returned in a public function in this class
|
// TODO these would be common for any observable returned in a public function in this class
|
||||||
.doOnNext(PodEventInterceptor())
|
.doOnNext(PodEventInterceptor())
|
||||||
|
@ -615,7 +626,6 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
|
|
||||||
override fun deactivatePod(): Observable<PodEvent> {
|
override fun deactivatePod(): Observable<PodEvent> {
|
||||||
return Observable.concat(
|
return Observable.concat(
|
||||||
observePodRunning,
|
|
||||||
observeConnectToPod,
|
observeConnectToPod,
|
||||||
observeSendDeactivateCommand
|
observeSendDeactivateCommand
|
||||||
)
|
)
|
||||||
|
@ -645,6 +655,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
is PodEvent.CommandSent -> {
|
is PodEvent.CommandSent -> {
|
||||||
|
logger.debug(LTag.PUMP, "Command sent: ${event.command.commandType}")
|
||||||
podStateManager.activeCommand?.let {
|
podStateManager.activeCommand?.let {
|
||||||
if (it.sequence == event.command.sequenceNumber) {
|
if (it.sequence == event.command.sequenceNumber) {
|
||||||
it.sentRealtime = SystemClock.elapsedRealtime()
|
it.sentRealtime = SystemClock.elapsedRealtime()
|
||||||
|
|
|
@ -44,6 +44,14 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
val session = assertSessionEstablished()
|
val session = assertSessionEstablished()
|
||||||
|
|
||||||
emitter.onNext(PodEvent.CommandSending(cmd))
|
emitter.onNext(PodEvent.CommandSending(cmd))
|
||||||
|
/*
|
||||||
|
if (Random.nextBoolean()) {
|
||||||
|
// XXX use this to test "failed to confirm" commands
|
||||||
|
emitter.onNext(PodEvent.CommandSendNotConfirmed(cmd))
|
||||||
|
emitter.tryOnError(MessageIOException("XXX random failure to test unconfirmed commands"))
|
||||||
|
return@create
|
||||||
|
}
|
||||||
|
*/
|
||||||
when (session.sendCommand(cmd)) {
|
when (session.sendCommand(cmd)) {
|
||||||
is CommandSendErrorSending -> {
|
is CommandSendErrorSending -> {
|
||||||
emitter.tryOnError(CouldNotSendCommandException())
|
emitter.tryOnError(CouldNotSendCommandException())
|
||||||
|
@ -55,7 +63,12 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
is CommandSendErrorConfirming ->
|
is CommandSendErrorConfirming ->
|
||||||
emitter.onNext(PodEvent.CommandSendNotConfirmed(cmd))
|
emitter.onNext(PodEvent.CommandSendNotConfirmed(cmd))
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
if (Random.nextBoolean()) {
|
||||||
|
// XXX use this commands confirmed with success
|
||||||
|
emitter.tryOnError(MessageIOException("XXX random failure to test unconfirmed commands"))
|
||||||
|
return@create
|
||||||
|
}*/
|
||||||
when (val readResult = session.readAndAckResponse(responseType)) {
|
when (val readResult = session.readAndAckResponse(responseType)) {
|
||||||
is CommandReceiveSuccess ->
|
is CommandReceiveSuccess ->
|
||||||
emitter.onNext(PodEvent.ResponseReceived(cmd, readResult.result))
|
emitter.onNext(PodEvent.ResponseReceived(cmd, readResult.result))
|
||||||
|
@ -84,7 +97,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStatus(): ConnectionState {
|
override fun getStatus(): ConnectionState {
|
||||||
return connection?.let { getStatus() }
|
return connection?.let { it.connectionState() }
|
||||||
?: NotConnected
|
?: NotConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,18 +115,27 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
val conn = connection
|
val conn = connection
|
||||||
?: Connection(podDevice, aapsLogger, context, podState)
|
?: Connection(podDevice, aapsLogger, context, podState)
|
||||||
connection = conn
|
connection = conn
|
||||||
if (conn.connectionState() is Connected) {
|
if (conn.connectionState() is Connected && conn.session != null) {
|
||||||
if (conn.session == null) {
|
emitter.onNext(PodEvent.AlreadyConnected(podAddress))
|
||||||
emitter.onNext(PodEvent.EstablishingSession)
|
|
||||||
establishSession(1.toByte())
|
|
||||||
emitter.onNext(PodEvent.Connected)
|
|
||||||
} else {
|
|
||||||
emitter.onNext(PodEvent.AlreadyConnected(podAddress))
|
|
||||||
}
|
|
||||||
emitter.onComplete()
|
emitter.onComplete()
|
||||||
return@create
|
return@create
|
||||||
}
|
}
|
||||||
conn.connect()
|
|
||||||
|
// two retries
|
||||||
|
for (i in 1..MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
|
||||||
|
try {
|
||||||
|
// wait i * CONNECTION_TIMEOUT
|
||||||
|
conn.connect(i)
|
||||||
|
break
|
||||||
|
} catch (e: Exception) {
|
||||||
|
aapsLogger.warn(LTag.PUMPBTCOMM, "connect error=$e")
|
||||||
|
if (i == MAX_NUMBER_OF_CONNECTION_ATTEMPTS) {
|
||||||
|
emitter.onError(e)
|
||||||
|
return@create
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emitter.onNext(PodEvent.BluetoothConnected(podAddress))
|
emitter.onNext(PodEvent.BluetoothConnected(podAddress))
|
||||||
emitter.onNext(PodEvent.EstablishingSession)
|
emitter.onNext(PodEvent.EstablishingSession)
|
||||||
establishSession(1.toByte())
|
establishSession(1.toByte())
|
||||||
|
@ -218,7 +240,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val MAX_NUMBER_OF_CONNECTION_ATTEMPTS = 3
|
||||||
const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
|
const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Connection(
|
||||||
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
|
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
|
||||||
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
|
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
|
||||||
// OnDisconnect can be called after this point!!!
|
// OnDisconnect can be called after this point!!!
|
||||||
val state = waitForConnection()
|
val state = waitForConnection(2)
|
||||||
if (state !is Connected) {
|
if (state !is Connected) {
|
||||||
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
|
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
|
||||||
throw FailedToConnectException(podDevice.address)
|
throw FailedToConnectException(podDevice.address)
|
||||||
|
@ -79,6 +79,7 @@ class Connection(
|
||||||
gattConnection,
|
gattConnection,
|
||||||
bleCommCallbacks
|
bleCommCallbacks
|
||||||
)
|
)
|
||||||
|
|
||||||
val sendResult = cmdBleIO.hello()
|
val sendResult = cmdBleIO.hello()
|
||||||
if (sendResult !is BleSendSuccess) {
|
if (sendResult !is BleSendSuccess) {
|
||||||
throw FailedToConnectException("Could not send HELLO command to ${podDevice.address}")
|
throw FailedToConnectException("Could not send HELLO command to ${podDevice.address}")
|
||||||
|
@ -89,7 +90,7 @@ class Connection(
|
||||||
|
|
||||||
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
|
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
|
||||||
|
|
||||||
fun connect() {
|
fun connect(timeoutMultiplier: Int) {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,7 @@ class Connection(
|
||||||
throw FailedToConnectException("connect() returned false")
|
throw FailedToConnectException("connect() returned false")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitForConnection() !is Connected) {
|
if (waitForConnection(timeoutMultiplier) !is Connected) {
|
||||||
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
|
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
|
||||||
throw FailedToConnectException(podDevice.address)
|
throw FailedToConnectException(podDevice.address)
|
||||||
}
|
}
|
||||||
|
@ -111,6 +112,8 @@ class Connection(
|
||||||
dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
|
dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
|
||||||
cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
|
cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
|
||||||
|
|
||||||
|
// val ret = gattConnection.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
|
||||||
|
// aapsLogger.info(LTag.PUMPBTCOMM, "requestConnectionPriority: $ret")
|
||||||
cmdBleIO.hello()
|
cmdBleIO.hello()
|
||||||
cmdBleIO.readyToRead()
|
cmdBleIO.readyToRead()
|
||||||
dataBleIO.readyToRead()
|
dataBleIO.readyToRead()
|
||||||
|
@ -125,9 +128,9 @@ class Connection(
|
||||||
session = null
|
session = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun waitForConnection(): ConnectionState {
|
private fun waitForConnection(timeoutMultiplier: Int): ConnectionState {
|
||||||
try {
|
try {
|
||||||
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
|
bleCommCallbacks.waitForConnection(BASE_CONNECT_TIMEOUT_MS * timeoutMultiplier)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
// We are still going to check if connection was successful
|
// We are still going to check if connection was successful
|
||||||
aapsLogger.info(LTag.PUMPBTCOMM, "Interrupted while waiting for connection")
|
aapsLogger.info(LTag.PUMPBTCOMM, "Interrupted while waiting for connection")
|
||||||
|
@ -178,7 +181,6 @@ class Connection(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val BASE_CONNECT_TIMEOUT_MS = 10000
|
||||||
private const val CONNECT_TIMEOUT_MS = 12000
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,4 +65,8 @@ sealed class PodEvent {
|
||||||
return "ResponseReceived(command=$command, response=$response)"
|
return "ResponseReceived(command=$command, response=$response)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isCommandSent(): Boolean {
|
||||||
|
return this is CommandSent || this is CommandSendNotConfirmed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,16 @@ enum class DeliveryStatus(override val value: Byte) : HasValue {
|
||||||
BOLUS_AND_BASAL_ACTIVE(0x05.toByte()),
|
BOLUS_AND_BASAL_ACTIVE(0x05.toByte()),
|
||||||
BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()),
|
BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()),
|
||||||
UNKNOWN(0xff.toByte());
|
UNKNOWN(0xff.toByte());
|
||||||
|
|
||||||
|
fun bolusDeliveringActive(): Boolean {
|
||||||
|
return value in arrayOf(BOLUS_AND_BASAL_ACTIVE.value, BOLUS_AND_TEMP_BASAL_ACTIVE.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tempBasalActive(): Boolean {
|
||||||
|
return value in arrayOf(BOLUS_AND_TEMP_BASAL_ACTIVE.value, TEMP_BASAL_ACTIVE.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun suspended(): Boolean {
|
||||||
|
return value == SUSPENDED.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
|
||||||
|
@ -8,6 +9,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
||||||
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
@ -64,6 +66,7 @@ interface OmnipodDashPodStateManager {
|
||||||
val tempBasalActive: Boolean
|
val tempBasalActive: Boolean
|
||||||
var basalProgram: BasalProgram?
|
var basalProgram: BasalProgram?
|
||||||
val activeCommand: ActiveCommand?
|
val activeCommand: ActiveCommand?
|
||||||
|
val lastBolus: LastBolus?
|
||||||
|
|
||||||
fun increaseMessageSequenceNumber()
|
fun increaseMessageSequenceNumber()
|
||||||
fun increaseEapAkaSequenceNumber(): ByteArray
|
fun increaseEapAkaSequenceNumber(): ByteArray
|
||||||
|
@ -75,12 +78,19 @@ interface OmnipodDashPodStateManager {
|
||||||
fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
|
fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
|
||||||
fun reset()
|
fun reset()
|
||||||
|
|
||||||
fun createActiveCommand(historyId: String, basalProgram: BasalProgram? = null, tempBasal: TempBasal? = null):
|
fun createActiveCommand(
|
||||||
Single<ActiveCommand>
|
historyId: String,
|
||||||
|
basalProgram: BasalProgram? = null,
|
||||||
|
tempBasal: TempBasal? = null,
|
||||||
|
requestedBolus: Double? = null
|
||||||
|
): Single<ActiveCommand>
|
||||||
fun updateActiveCommand(): Maybe<CommandConfirmed>
|
fun updateActiveCommand(): Maybe<CommandConfirmed>
|
||||||
fun observeNoActiveCommand(): Observable<PodEvent>
|
fun observeNoActiveCommand(): Completable
|
||||||
fun getCommandConfirmationFromState(): CommandConfirmationFromState
|
fun getCommandConfirmationFromState(): CommandConfirmationFromState
|
||||||
|
|
||||||
|
fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType)
|
||||||
|
fun markLastBolusComplete(): LastBolus?
|
||||||
|
|
||||||
data class ActiveCommand(
|
data class ActiveCommand(
|
||||||
val sequence: Short,
|
val sequence: Short,
|
||||||
val createdRealtime: Long,
|
val createdRealtime: Long,
|
||||||
|
@ -88,11 +98,36 @@ interface OmnipodDashPodStateManager {
|
||||||
val historyId: String,
|
val historyId: String,
|
||||||
var sendError: Throwable?,
|
var sendError: Throwable?,
|
||||||
var basalProgram: BasalProgram?,
|
var basalProgram: BasalProgram?,
|
||||||
val tempBasal: TempBasal?
|
val tempBasal: TempBasal?,
|
||||||
|
val requestedBolus: Double?
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: set created to "now" on boot
|
// TODO: set created to "now" on boot
|
||||||
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable
|
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable
|
||||||
|
|
||||||
|
data class LastBolus(
|
||||||
|
val startTime: Long,
|
||||||
|
val requestedUnits: Double,
|
||||||
|
var bolusUnitsRemaining: Double,
|
||||||
|
var deliveryComplete: Boolean,
|
||||||
|
val historyId: String,
|
||||||
|
val bolusType: DetailedBolusInfo.BolusType
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun deliveredUnits(): Double? {
|
||||||
|
return if (deliveryComplete) {
|
||||||
|
requestedUnits - bolusUnitsRemaining
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markComplete(): Double {
|
||||||
|
this.deliveryComplete = true
|
||||||
|
return requestedUnits - bolusUnitsRemaining
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class BluetoothConnectionState {
|
enum class BluetoothConnectionState {
|
||||||
CONNECTING, CONNECTED, DISCONNECTED
|
CONNECTING, CONNECTED, DISCONNECTED
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
|
@ -17,6 +18,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
@ -151,6 +153,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
store()
|
store()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val lastBolus: OmnipodDashPodStateManager.LastBolus?
|
||||||
|
@Synchronized
|
||||||
|
get() = podState.lastBolus
|
||||||
|
|
||||||
override val tempBasalActive: Boolean
|
override val tempBasalActive: Boolean
|
||||||
get() = !isSuspended && tempBasal?.let {
|
get() = !isSuspended && tempBasal?.let {
|
||||||
it.startTime + it.durationInMinutes * 60 * 1000 > System.currentTimeMillis()
|
it.startTime + it.durationInMinutes * 60 * 1000 > System.currentTimeMillis()
|
||||||
|
@ -197,11 +203,46 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
override val activeCommand: OmnipodDashPodStateManager.ActiveCommand?
|
override val activeCommand: OmnipodDashPodStateManager.ActiveCommand?
|
||||||
get() = podState.activeCommand
|
get() = podState.activeCommand
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType) {
|
||||||
|
podState.lastBolus = OmnipodDashPodStateManager.LastBolus(
|
||||||
|
startTime = System.currentTimeMillis(),
|
||||||
|
requestedUnits = requestedUnits,
|
||||||
|
bolusUnitsRemaining = requestedUnits,
|
||||||
|
deliveryComplete = false, // cancelled, delivered 100% or pod failure
|
||||||
|
historyId = historyId,
|
||||||
|
bolusType = bolusType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun markLastBolusComplete(): OmnipodDashPodStateManager.LastBolus? {
|
||||||
|
val lastBolus = podState.lastBolus
|
||||||
|
|
||||||
|
lastBolus?.run {
|
||||||
|
this.deliveryComplete = true
|
||||||
|
}
|
||||||
|
?: logger.error(LTag.PUMP, "Trying to mark null bolus as complete")
|
||||||
|
|
||||||
|
return lastBolus
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
|
||||||
|
podState.lastBolus?.run {
|
||||||
|
val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05
|
||||||
|
this.bolusUnitsRemaining = remainingUnits
|
||||||
|
if (remainingUnits == 0.0) {
|
||||||
|
this.deliveryComplete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun createActiveCommand(
|
override fun createActiveCommand(
|
||||||
historyId: String,
|
historyId: String,
|
||||||
basalProgram: BasalProgram?,
|
basalProgram: BasalProgram?,
|
||||||
tempBasal: OmnipodDashPodStateManager.TempBasal?
|
tempBasal: OmnipodDashPodStateManager.TempBasal?,
|
||||||
|
requestedBolus: Double?
|
||||||
):
|
):
|
||||||
Single<OmnipodDashPodStateManager.ActiveCommand> {
|
Single<OmnipodDashPodStateManager.ActiveCommand> {
|
||||||
return Single.create { source ->
|
return Single.create { source ->
|
||||||
|
@ -213,6 +254,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
sendError = null,
|
sendError = null,
|
||||||
basalProgram = basalProgram,
|
basalProgram = basalProgram,
|
||||||
tempBasal = tempBasal,
|
tempBasal = tempBasal,
|
||||||
|
requestedBolus = requestedBolus
|
||||||
)
|
)
|
||||||
podState.activeCommand = command
|
podState.activeCommand = command
|
||||||
source.onSuccess(command)
|
source.onSuccess(command)
|
||||||
|
@ -228,12 +270,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun observeNoActiveCommand(): Observable<PodEvent> {
|
override fun observeNoActiveCommand(): Completable {
|
||||||
return Observable.defer {
|
return Completable.defer {
|
||||||
if (activeCommand == null) {
|
if (activeCommand == null) {
|
||||||
Observable.empty()
|
Completable.complete()
|
||||||
} else {
|
} else {
|
||||||
Observable.error(
|
logger.warn(LTag.PUMP, "Active command already existing: $activeCommand")
|
||||||
|
Completable.error(
|
||||||
java.lang.IllegalStateException(
|
java.lang.IllegalStateException(
|
||||||
"Trying to send a command " +
|
"Trying to send a command " +
|
||||||
"and the last command was not confirmed"
|
"and the last command was not confirmed"
|
||||||
|
@ -257,7 +300,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
CommandSendingFailure -> {
|
CommandSendingFailure -> {
|
||||||
podState.activeCommand = null
|
podState.activeCommand = null
|
||||||
source.onError(
|
source.onError(
|
||||||
activeCommand?.sendError
|
activeCommand.sendError
|
||||||
?: java.lang.IllegalStateException(
|
?: java.lang.IllegalStateException(
|
||||||
"Could not send command and sendError is " +
|
"Could not send command and sendError is " +
|
||||||
"missing"
|
"missing"
|
||||||
|
@ -323,7 +366,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
|
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
|
||||||
logger.debug(LTag.PUMPBTCOMM, "Default status reponse :$response")
|
logger.debug(LTag.PUMPCOMM, "Default status response :$response")
|
||||||
podState.deliveryStatus = response.deliveryStatus
|
podState.deliveryStatus = response.deliveryStatus
|
||||||
podState.podStatus = response.podStatus
|
podState.podStatus = response.podStatus
|
||||||
podState.pulsesDelivered = response.totalPulsesDelivered
|
podState.pulsesDelivered = response.totalPulsesDelivered
|
||||||
|
@ -336,6 +379,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
|
|
||||||
podState.lastUpdatedSystem = System.currentTimeMillis()
|
podState.lastUpdatedSystem = System.currentTimeMillis()
|
||||||
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
||||||
|
updateLastBolusFromResponse(response.bolusPulsesRemaining)
|
||||||
|
|
||||||
store()
|
store()
|
||||||
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
|
@ -384,6 +428,11 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
podState.uniqueId = response.uniqueIdReceivedInCommand
|
podState.uniqueId = response.uniqueIdReceivedInCommand
|
||||||
|
|
||||||
podState.lastUpdatedSystem = System.currentTimeMillis()
|
podState.lastUpdatedSystem = System.currentTimeMillis()
|
||||||
|
// TODO: what is considered to be the pod activation time?
|
||||||
|
// LTK negotiation ?
|
||||||
|
// setUniqueId?
|
||||||
|
// compute it from the number of "minutesOnPod"?
|
||||||
|
podState.activationTime = System.currentTimeMillis()
|
||||||
|
|
||||||
store()
|
store()
|
||||||
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
|
@ -397,6 +446,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
podState.deliveryStatus = response.deliveryStatus
|
podState.deliveryStatus = response.deliveryStatus
|
||||||
podState.podStatus = response.podStatus
|
podState.podStatus = response.podStatus
|
||||||
podState.pulsesDelivered = response.totalPulsesDelivered
|
podState.pulsesDelivered = response.totalPulsesDelivered
|
||||||
|
|
||||||
if (response.reservoirPulsesRemaining < 1023) {
|
if (response.reservoirPulsesRemaining < 1023) {
|
||||||
podState.pulsesRemaining = response.reservoirPulsesRemaining
|
podState.pulsesRemaining = response.reservoirPulsesRemaining
|
||||||
}
|
}
|
||||||
|
@ -407,6 +457,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
|
|
||||||
podState.lastUpdatedSystem = System.currentTimeMillis()
|
podState.lastUpdatedSystem = System.currentTimeMillis()
|
||||||
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
|
||||||
|
updateLastBolusFromResponse(response.bolusPulsesRemaining)
|
||||||
|
|
||||||
store()
|
store()
|
||||||
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
rxBus.send(EventOmnipodDashPumpValuesChanged())
|
||||||
|
@ -462,6 +513,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
var bluetoothAddress: String? = null
|
var bluetoothAddress: String? = null
|
||||||
var ltk: ByteArray? = null
|
var ltk: ByteArray? = null
|
||||||
var eapAkaSequenceNumber: Long = 1
|
var eapAkaSequenceNumber: Long = 1
|
||||||
|
var bolusPulsesRemaining: Short = 0
|
||||||
|
|
||||||
var bleVersion: SoftwareVersion? = null
|
var bleVersion: SoftwareVersion? = null
|
||||||
var firmwareVersion: SoftwareVersion? = null
|
var firmwareVersion: SoftwareVersion? = null
|
||||||
|
@ -484,5 +536,6 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
||||||
var basalProgram: BasalProgram? = null
|
var basalProgram: BasalProgram? = null
|
||||||
var tempBasal: OmnipodDashPodStateManager.TempBasal? = null
|
var tempBasal: OmnipodDashPodStateManager.TempBasal? = null
|
||||||
var activeCommand: OmnipodDashPodStateManager.ActiveCommand? = null
|
var activeCommand: OmnipodDashPodStateManager.ActiveCommand? = null
|
||||||
|
var lastBolus: OmnipodDashPodStateManager.LastBolus? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,13 @@ data class TempBasalRecord(val duration: Int, val rate: Double) : Record()
|
||||||
enum class BolusType {
|
enum class BolusType {
|
||||||
DEFAULT, SMB;
|
DEFAULT, SMB;
|
||||||
|
|
||||||
|
fun toBolusInfoBolusType(): DetailedBolusInfo.BolusType {
|
||||||
|
return when (this) {
|
||||||
|
DEFAULT -> DetailedBolusInfo.BolusType.NORMAL
|
||||||
|
SMB -> DetailedBolusInfo.BolusType.SMB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromBolusInfoBolusType(type: DetailedBolusInfo.BolusType): BolusType {
|
fun fromBolusInfoBolusType(type: DetailedBolusInfo.BolusType): BolusType {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activati
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodDashPodManagementBinding
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodDashPodManagementBinding
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.deactivation.DashPodDeactivationWizardActivity
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.deactivation.DashPodDeactivationWizardActivity
|
||||||
|
@ -124,7 +125,8 @@ class DashPodManagementActivity : NoSplashAppCompatActivity() {
|
||||||
|
|
||||||
binding.buttonActivatePod.isEnabled = podStateManager.activationProgress.isBefore(ActivationProgress.COMPLETED)
|
binding.buttonActivatePod.isEnabled = podStateManager.activationProgress.isBefore(ActivationProgress.COMPLETED)
|
||||||
binding.buttonDeactivatePod.isEnabled =
|
binding.buttonDeactivatePod.isEnabled =
|
||||||
podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)
|
podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) ||
|
||||||
|
podStateManager.podStatus == PodStatus.ALARM
|
||||||
|
|
||||||
if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)) {
|
if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)) {
|
||||||
if (commandQueue.isCustomCommandInQueue(CommandPlayTestBeep::class.java)) {
|
if (commandQueue.isCustomCommandInQueue(CommandPlayTestBeep::class.java)) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -23,6 +24,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.Comm
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandResumeDelivery
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandResumeDelivery
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandSilenceAlerts
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandSilenceAlerts
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandSuspendDelivery
|
import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.CommandSuspendDelivery
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.EventOmnipodDashPumpValuesChanged
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.EventOmnipodDashPumpValuesChanged
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.R
|
||||||
|
@ -42,6 +44,8 @@ import info.nightscout.androidaps.utils.ui.UIRunnable
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import org.joda.time.DateTimeZone
|
||||||
import org.joda.time.Duration
|
import org.joda.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -232,6 +236,29 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get time on pos from activation time and minutes since activation
|
||||||
|
private fun getTimeOnPod(): DateTime? {
|
||||||
|
var timeOnPod: DateTime? = null
|
||||||
|
val minutesSinceActivation = podStateManager.minutesSinceActivation
|
||||||
|
val activationTime = podStateManager.activationTime
|
||||||
|
if ((activationTime != null) and (minutesSinceActivation != null)) {
|
||||||
|
timeOnPod = DateTime(activationTime!!).plusMinutes(minutesSinceActivation!!.toInt())
|
||||||
|
}
|
||||||
|
return timeOnPod
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider storing expiry datetime in pod state saving continuesly recalculating to the same value
|
||||||
|
private fun getExpiryAt(): DateTime? {
|
||||||
|
var expiresAt: DateTime? = null
|
||||||
|
val podLifeInHours = podStateManager.podLifeInHours
|
||||||
|
val minutesSinceActivation = podStateManager.minutesSinceActivation
|
||||||
|
if ((podLifeInHours != null) and (minutesSinceActivation != null)) {
|
||||||
|
val expiresInMinutes = (podLifeInHours!! * 60) - minutesSinceActivation!!
|
||||||
|
expiresAt = DateTime().plusMinutes(expiresInMinutes)
|
||||||
|
}
|
||||||
|
return expiresAt
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateOmnipodStatus() {
|
private fun updateOmnipodStatus() {
|
||||||
updateLastConnection()
|
updateLastConnection()
|
||||||
updateLastBolus()
|
updateLastBolus()
|
||||||
|
@ -262,7 +289,16 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
podStateManager.firmwareVersion.toString(),
|
podStateManager.firmwareVersion.toString(),
|
||||||
podStateManager.bluetoothVersion.toString()
|
podStateManager.bluetoothVersion.toString()
|
||||||
)
|
)
|
||||||
podInfoBinding.timeOnPod.text = podStateManager.minutesSinceActivation.toString() + " minutes"
|
|
||||||
|
// Update time on Pod
|
||||||
|
// TODO: For now: derive from podStateManager.minutesSinceActivation
|
||||||
|
val timeOnPod = getTimeOnPod()
|
||||||
|
if (timeOnPod == null) {
|
||||||
|
podInfoBinding.timeOnPod.text = "???"
|
||||||
|
} else {
|
||||||
|
podInfoBinding.timeOnPod.text = readableZonedTime(timeOnPod)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
/*
|
/*
|
||||||
podInfoBinding.timeOnPod.setTextColor(if (podStateManager.timeDeviatesMoreThan(OmnipodConstants.TIME_DEVIATION_THRESHOLD)) {
|
podInfoBinding.timeOnPod.setTextColor(if (podStateManager.timeDeviatesMoreThan(OmnipodConstants.TIME_DEVIATION_THRESHOLD)) {
|
||||||
|
@ -272,22 +308,28 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO
|
// TODO: Active command
|
||||||
/*
|
if (podStateManager.activeCommand != null) {
|
||||||
val expiresAt = podStateManager.expiresAt
|
podInfoBinding.podExpiryDate.setTextColor(Color.RED)
|
||||||
|
podInfoBinding.podExpiryDate.text = "Active command"
|
||||||
|
} else {
|
||||||
|
podInfoBinding.podExpiryDate.text = PLACEHOLDER
|
||||||
|
podInfoBinding.podExpiryDate.setTextColor(Color.WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
if (expiresAt == null) {
|
// Update Pod expiry time
|
||||||
|
val expiresAt = getExpiryAt()
|
||||||
|
if (expiresAt is Nothing) {
|
||||||
podInfoBinding.podExpiryDate.text = PLACEHOLDER
|
podInfoBinding.podExpiryDate.text = PLACEHOLDER
|
||||||
podInfoBinding.podExpiryDate.setTextColor(Color.WHITE)
|
podInfoBinding.podExpiryDate.setTextColor(Color.WHITE)
|
||||||
} else {
|
} else {
|
||||||
podInfoBinding.podExpiryDate.text = readableZonedTime(expiresAt)
|
podInfoBinding.podExpiryDate.text = readableZonedTime(expiresAt!!)
|
||||||
podInfoBinding.podExpiryDate.setTextColor(if (DateTime.now().isAfter(expiresAt)) {
|
podInfoBinding.podExpiryDate.setTextColor(if (DateTime.now().isAfter(expiresAt)) {
|
||||||
Color.RED
|
Color.RED
|
||||||
} else {
|
} else {
|
||||||
Color.WHITE
|
Color.WHITE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/* TODO
|
/* TODO
|
||||||
if (podStateManager.isPodFaulted) {
|
if (podStateManager.isPodFaulted) {
|
||||||
|
@ -367,7 +409,13 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
private fun updateLastConnection() {
|
private fun updateLastConnection() {
|
||||||
if (podStateManager.isUniqueIdSet) {
|
if (podStateManager.isUniqueIdSet) {
|
||||||
podInfoBinding.lastConnection.text = readableDuration(podStateManager.lastUpdatedSystem)
|
podInfoBinding.lastConnection.text = readableDuration(
|
||||||
|
Duration(
|
||||||
|
podStateManager.lastUpdatedSystem,
|
||||||
|
System
|
||||||
|
.currentTimeMillis()
|
||||||
|
)
|
||||||
|
)
|
||||||
val lastConnectionColor =
|
val lastConnectionColor =
|
||||||
if (omnipodDashPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) {
|
if (omnipodDashPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) {
|
||||||
Color.RED
|
Color.RED
|
||||||
|
@ -377,7 +425,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
podInfoBinding.lastConnection.setTextColor(lastConnectionColor)
|
podInfoBinding.lastConnection.setTextColor(lastConnectionColor)
|
||||||
} else {
|
} else {
|
||||||
podInfoBinding.lastConnection.setTextColor(Color.WHITE)
|
podInfoBinding.lastConnection.setTextColor(Color.WHITE)
|
||||||
podInfoBinding.lastConnection.text = readableDuration(podStateManager.lastUpdatedSystem)
|
podInfoBinding.lastConnection.text = readableDuration(
|
||||||
|
Duration(podStateManager.lastUpdatedSystem, System.currentTimeMillis())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,8 +450,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
resourceHelper.gs(R.string.omnipod_common_pod_status_suspended)
|
resourceHelper.gs(R.string.omnipod_common_pod_status_suspended)
|
||||||
} else {
|
} else {
|
||||||
resourceHelper.gs(R.string.omnipod_common_pod_status_running) +
|
resourceHelper.gs(R.string.omnipod_common_pod_status_running) +
|
||||||
podStateManager.deliveryStatus?.let { " " + podStateManager.deliveryStatus.toString() }
|
if (BuildConfig.DEBUG)
|
||||||
// TODO Display deliveryStatus in a nice way
|
podStateManager.deliveryStatus?.let { " " + podStateManager.deliveryStatus.toString() }
|
||||||
|
else ""
|
||||||
}
|
}
|
||||||
// TODO
|
// TODO
|
||||||
/*
|
/*
|
||||||
|
@ -416,7 +467,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val podStatusColor =
|
val podStatusColor =
|
||||||
if (!podStateManager.isActivationCompleted || /* TODO podStateManager.isPodDead || */ podStateManager.isSuspended) {
|
if (!podStateManager.isActivationCompleted || podStateManager.isPodKaput || podStateManager.isSuspended) {
|
||||||
Color.RED
|
Color.RED
|
||||||
} else {
|
} else {
|
||||||
Color.WHITE
|
Color.WHITE
|
||||||
|
@ -425,27 +476,43 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLastBolus() {
|
private fun updateLastBolus() {
|
||||||
// TODO
|
|
||||||
/*
|
|
||||||
if (podStateManager.isActivationCompleted && podStateManager.hasLastBolus()) {
|
|
||||||
var text = resourceHelper.gs(R.string.omnipod_common_overview_last_bolus_value, omnipodDashPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime))
|
|
||||||
val textColor: Int
|
|
||||||
|
|
||||||
if (podStateManager.isLastBolusCertain) {
|
var textColor = Color.WHITE
|
||||||
textColor = Color.WHITE
|
podStateManager.activeCommand?.let {
|
||||||
} else {
|
val requestedBolus = it.requestedBolus
|
||||||
|
if (requestedBolus != null) {
|
||||||
|
var text = resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_overview_last_bolus_value,
|
||||||
|
omnipodDashPumpPlugin.model().determineCorrectBolusSize(requestedBolus),
|
||||||
|
resourceHelper.gs(R.string.insulin_unit_shortname),
|
||||||
|
readableDuration(Duration(it.createdRealtime, SystemClock.elapsedRealtime()))
|
||||||
|
)
|
||||||
|
text += " (uncertain) "
|
||||||
textColor = Color.RED
|
textColor = Color.RED
|
||||||
text += " (" + resourceHelper.gs(R.string.omnipod_eros_uncertain) + ")"
|
podInfoBinding.lastBolus.text = text
|
||||||
|
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||||
|
podStateManager.lastBolus?.let {
|
||||||
|
// display requested units if delivery is in progress
|
||||||
|
var bolusSize = it.deliveredUnits()
|
||||||
|
?: it.requestedUnits
|
||||||
|
|
||||||
|
var text = resourceHelper.gs(
|
||||||
|
R.string.omnipod_common_overview_last_bolus_value,
|
||||||
|
omnipodDashPumpPlugin.model().determineCorrectBolusSize(bolusSize),
|
||||||
|
resourceHelper.gs(R.string.insulin_unit_shortname),
|
||||||
|
readableDuration(Duration(it.startTime, System.currentTimeMillis()))
|
||||||
|
)
|
||||||
|
if (!it.deliveryComplete) {
|
||||||
|
textColor = Color.YELLOW
|
||||||
|
}
|
||||||
podInfoBinding.lastBolus.text = text
|
podInfoBinding.lastBolus.text = text
|
||||||
podInfoBinding.lastBolus.setTextColor(textColor)
|
podInfoBinding.lastBolus.setTextColor(textColor)
|
||||||
|
|
||||||
} else {
|
|
||||||
podInfoBinding.lastBolus.text = PLACEHOLDER
|
|
||||||
podInfoBinding.lastBolus.setTextColor(Color.WHITE)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTempBasal() {
|
private fun updateTempBasal() {
|
||||||
|
@ -529,9 +596,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
|
|
||||||
private fun updateSuspendDeliveryButton() {
|
private fun updateSuspendDeliveryButton() {
|
||||||
// If the Pod is currently suspended, we show the Resume delivery button instead.
|
// If the Pod is currently suspended, we show the Resume delivery button instead.
|
||||||
// TODO: isSuspendDeliveryButtonEnabled doesn't work
|
if (isSuspendDeliveryButtonEnabled() &&
|
||||||
val isSuspendDeliveryButtonEnabled = true
|
|
||||||
if (isSuspendDeliveryButtonEnabled &&
|
|
||||||
podStateManager.isPodRunning &&
|
podStateManager.isPodRunning &&
|
||||||
(!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))
|
(!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))
|
||||||
) {
|
) {
|
||||||
|
@ -560,7 +625,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSuspendDeliveryButtonEnabled(): Boolean {
|
private fun isSuspendDeliveryButtonEnabled(): Boolean {
|
||||||
return sp.getBoolean(R.string.omnipod_common_preferences_suspend_delivery_button_enabled, false)
|
R.string.key_omnipod_common_basal_beeps_enabled
|
||||||
|
return sp.getBoolean(R.string.key_omnipod_common_suspend_delivery_button_enabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
|
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
|
||||||
|
@ -577,23 +643,28 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private fun getTimeZone(): DateTimeZone {
|
||||||
|
// TODO: Get timezone as configured/podState
|
||||||
|
// return getSafe(() -> podState.getTimeZone());
|
||||||
|
return DateTimeZone.getDefault()
|
||||||
|
}
|
||||||
|
|
||||||
private fun readableZonedTime(time: DateTime): String {
|
private fun readableZonedTime(time: DateTime): String {
|
||||||
val timeAsJavaData = time.toLocalDateTime().toDate()
|
val timeAsJavaData = time.toLocalDateTime().toDate()
|
||||||
val timeZone = podStateManager.timeZone.toTimeZone()
|
|
||||||
|
val timeZone = getTimeZone().toTimeZone()
|
||||||
if (timeZone == TimeZone.getDefault()) {
|
if (timeZone == TimeZone.getDefault()) {
|
||||||
return dateUtil.dateAndTimeString(timeAsJavaData)
|
return dateUtil.dateAndTimeString(timeAsJavaData.time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get full timezoned time
|
||||||
val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
|
val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
|
||||||
val locale = resources.configuration.locales.get(0)
|
val locale = resources.configuration.locales.get(0)
|
||||||
val timeZoneDisplayName = timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(isDaylightTime, TimeZone.LONG, locale)
|
val timeZoneDisplayName = timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(isDaylightTime, TimeZone.LONG, locale)
|
||||||
return resourceHelper.gs(R.string.omnipod_common_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData), timeZoneDisplayName)
|
return resourceHelper.gs(R.string.omnipod_common_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData.time), timeZoneDisplayName)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
private fun readableDuration(dateTime: Long): String {
|
private fun readableDuration(duration: Duration): String {
|
||||||
val duration = Duration(dateTime, System.currentTimeMillis())
|
|
||||||
val hours = duration.standardHours.toInt()
|
val hours = duration.standardHours.toInt()
|
||||||
val minutes = duration.standardMinutes.toInt()
|
val minutes = duration.standardMinutes.toInt()
|
||||||
val seconds = duration.standardSeconds.toInt()
|
val seconds = duration.standardSeconds.toInt()
|
||||||
|
|
Loading…
Reference in a new issue