Merge pull request #53 from 0pen-dash/avereha/fixes-v2

Avereha/fixes v2
This commit is contained in:
Andrei Vereha 2021-06-26 15:11:26 +02:00 committed by GitHub
commit 4755d749d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 220 additions and 90 deletions

View file

@ -1,6 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash package info.nightscout.androidaps.plugins.pump.omnipod.dash
import android.content.Context import android.content.Context
import android.os.Handler
import android.os.Looper
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
@ -19,6 +21,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
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
@ -67,10 +70,14 @@ class OmnipodDashPumpPlugin @Inject constructor(
commandQueue: CommandQueueProvider commandQueue: CommandQueueProvider
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump { ) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), Pump {
@Volatile var bolusCanceled = false @Volatile var bolusCanceled = false
private val handler: Handler = Handler(Looper.getMainLooper())
lateinit private var statusChecker: Runnable
var nextPodWarningCheck : Long = 0
companion object { companion object {
private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong() private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong()
private const val BOLUS_RETRIES = 5 // numer of retries for cancel/get bolus status private const val BOLUS_RETRIES = 5 // number of retries for cancel/get bolus status
private const val STATUS_CHECK_INTERVAL_MS = (60L * 1000)
private val pluginDescription = PluginDescription() private val pluginDescription = PluginDescription()
.mainType(PluginType.PUMP) .mainType(PluginType.PUMP)
@ -84,6 +91,52 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val pumpDescription = PumpDescription(PumpType.OMNIPOD_DASH) private val pumpDescription = PumpDescription(PumpType.OMNIPOD_DASH)
} }
init {
statusChecker = Runnable {
refreshStatusOnUnacknowledgedCommands()
updatePodWarnings()
handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS)
}
}
private fun updatePodWarnings() {
if (System.currentTimeMillis() > nextPodWarningCheck) {
if (!podStateManager.isPodRunning) {
val notification =
Notification(
Notification.OMNIPOD_POD_NOT_ATTACHED,
"Pod not activated",
Notification.NORMAL
)
rxBus.send(EventNewNotification(notification))
} else {
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED))
if (podStateManager.isSuspended) {
val notification =
Notification(
Notification.OMNIPOD_POD_SUSPENDED,
"Insulin delivery suspended",
Notification.NORMAL
)
rxBus.send(EventNewNotification(notification))
} else {
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
// TODO: time out of sync notification?
}
}
nextPodWarningCheck = DateTimeUtil.getTimeInFutureFromMinutes(15)
}
}
private fun refreshStatusOnUnacknowledgedCommands() {
if (podStateManager.isPodRunning &&
podStateManager.activeCommand != null &&
commandQueue.size() == 0 &&
commandQueue.performing() == null) {
commandQueue.readStatus("Unconfirmed command", null)
}
}
override fun isInitialized(): Boolean { override fun isInitialized(): Boolean {
// TODO // TODO
return true return true
@ -129,8 +182,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
// TODO // TODO
} }
override fun getPumpStatus(reason: String) { override fun getPumpStatus(reason: String) {
if (reason != "REQUESTED BY USER" && !podStateManager.isActivationCompleted) { aapsLogger.debug(LTag.PUMP, "getPumpStatus reason=$reason")
if (reason != "REQUESTED BY USER" && !podStateManager.isActivationCompleted) {
// prevent races on BLE when the pod is not activated // prevent races on BLE when the pod is not activated
return return
} }
@ -140,6 +196,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable) aapsLogger.error(LTag.PUMP, "Error in getPumpStatus", throwable)
} else { } else {
aapsLogger.info(LTag.PUMP, "getPumpStatus executed with success") aapsLogger.info(LTag.PUMP, "getPumpStatus executed with success")
if (!podStateManager.isActivationCompleted) {
val msg = podStateManager.recoverActivationFromPodStatus()
msg?.let {
// TODO: show dialog with "try again, the pod is busy now"
aapsLogger.info(LTag.PUMP, "recoverActivationFromPodStatus msg=$msg")
}
}
} }
} }
@ -201,6 +264,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (!podStateManager.isActivationCompleted) { if (!podStateManager.isActivationCompleted) {
return PumpEnactResult(injector).success(true).enacted(true) return PumpEnactResult(injector).success(true).enacted(true)
} }
aapsLogger.debug(LTag.PUMP, "setNewBasalProfile profile=$profile")
val basalProgram = mapProfileToBasalProgram(profile) val basalProgram = mapProfileToBasalProgram(profile)
var deliverySuspended = false var deliverySuspended = false
return executeProgrammingCommand( return executeProgrammingCommand(
@ -262,7 +326,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
rxBus.send(EventTempBasalChange()) rxBus.send(EventTempBasalChange())
} }
.ignoreElements() .ignoreElements()
).doFinally { ).doOnComplete {
notifyOnUnconfirmed( notifyOnUnconfirmed(
Notification.FAILED_UPDATE_PROFILE, Notification.FAILED_UPDATE_PROFILE,
"Suspend delivery is unconfirmed! " + "Suspend delivery is unconfirmed! " +
@ -272,16 +336,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
} }
/* override fun onStop() {
super.onStop()
disposable.clear()
}
*/
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
podStateManager.onStart() podStateManager.onStart()
handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MS)
}
override fun onStop() {
super.onStop()
handler.removeCallbacks(statusChecker)
} }
private fun observeDeliverySuspended(): Completable = Completable.defer { private fun observeDeliverySuspended(): Completable = Completable.defer {
@ -673,10 +736,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
executeProgrammingCommand( executeProgrammingCommand(
historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements() command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements()
).doFinally { ).doOnComplete {
notifyOnUnconfirmed( notifyOnUnconfirmed(
Notification.OMNIPOD_TBR_ALERTS, Notification.OMNIPOD_TBR_ALERTS,
"Setting temp basal failed. If a temp basal was previously running, it might have been cancelled. " + "Cancelling temp basal might have failed." +
"If a temp basal was previously running, it might have been cancelled." +
"Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n "Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n
R.raw.boluserror, R.raw.boluserror,
) )
@ -734,6 +798,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
private fun notifyOnUnconfirmed(notificationId: Int, msg: String, sound: Int?) { private fun notifyOnUnconfirmed(notificationId: Int, msg: String, sound: Int?) {
if (podStateManager.activeCommand != null) { if (podStateManager.activeCommand != null) {
aapsLogger.debug(LTag.PUMP, "Notification for active command: ${podStateManager.activeCommand}")
showNotification(notificationId, msg, Notification.URGENT, sound) showNotification(notificationId, msg, Notification.URGENT, sound)
} }
} }
@ -877,7 +942,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
.ignoreElements() .ignoreElements()
).doFinally { ).doFinally {
notifyOnUnconfirmed( notifyOnUnconfirmed(
Notification.PUMP_ERROR, Notification.FAILED_UPDATE_PROFILE,
"Unconfirmed resumeDelivery command. Please refresh pod status", "Unconfirmed resumeDelivery command. Please refresh pod status",
R.raw.boluserror R.raw.boluserror
) )
@ -972,7 +1037,18 @@ class OmnipodDashPumpPlugin @Inject constructor(
val historyEntry = history.getById(command.historyId) val historyEntry = history.getById(command.historyId)
aapsLogger.debug(LTag.PUMPCOMM, "handling command confirmation: $confirmation") aapsLogger.debug(LTag.PUMPCOMM, "handling command confirmation: $confirmation")
when (historyEntry.commandType) { when (historyEntry.commandType) {
OmnipodCommandType.CANCEL_TEMPORARY_BASAL, OmnipodCommandType.CANCEL_TEMPORARY_BASAL -> {
if (confirmation.success) {
pumpSync.syncStopTemporaryBasalWithPumpId(
historyEntry.createdAt,
historyEntry.pumpId(),
PumpType.OMNIPOD_DASH,
serialNumber()
)
podStateManager.tempBasal = null
}
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
}
OmnipodCommandType.RESUME_DELIVERY -> { OmnipodCommandType.RESUME_DELIVERY -> {
// We can't invalidate this command, // We can't invalidate this command,
// and this is why it is pumpSync-ed at this point // and this is why it is pumpSync-ed at this point
@ -984,6 +1060,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
serialNumber() serialNumber()
) )
podStateManager.tempBasal = null podStateManager.tempBasal = null
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
} }
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS)) rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
} }
@ -1004,8 +1081,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
PumpType.OMNIPOD_DASH, PumpType.OMNIPOD_DASH,
serialNumber() serialNumber()
) )
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
} }
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
} }
OmnipodCommandType.SET_TEMPORARY_BASAL -> { OmnipodCommandType.SET_TEMPORARY_BASAL -> {

View file

@ -173,8 +173,7 @@ class OmnipodDashManagerImpl @Inject constructor(
DefaultStatusResponse::class DefaultStatusResponse::class
) )
}.doOnComplete { }.doOnComplete {
// TODO: remove podStateManager.basalProgram? podStateManager.timeZone = TimeZone.getDefault()
podStateManager.basalProgram = basalProgram
} }
} }
@ -246,6 +245,7 @@ class OmnipodDashManagerImpl @Inject constructor(
observeVerifyPrime.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIME_COMPLETED)) observeVerifyPrime.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIME_COMPLETED))
) )
} }
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) { if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING)) {
observables.add(observeConnectToPod) // connection can time out while waiting observables.add(observeConnectToPod) // connection can time out while waiting
observables.add( observables.add(
@ -270,6 +270,7 @@ class OmnipodDashManagerImpl @Inject constructor(
}.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING)) }.doOnComplete(ActivationProgressUpdater(ActivationProgress.PRIMING))
) )
} }
if (podStateManager.activationProgress.isBefore(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT)) { if (podStateManager.activationProgress.isBefore(ActivationProgress.REPROGRAMMED_LUMP_OF_COAL_ALERT)) {
observables.add( observables.add(
observeSendProgramAlertsCommand( observeSendProgramAlertsCommand(
@ -304,6 +305,7 @@ class OmnipodDashManagerImpl @Inject constructor(
).doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_LOW_RESERVOIR_ALERTS)) ).doOnComplete(ActivationProgressUpdater(ActivationProgress.PROGRAMMED_LOW_RESERVOIR_ALERTS))
) )
} }
if (podStateManager.activationProgress.isBefore(ActivationProgress.SET_UNIQUE_ID)) { if (podStateManager.activationProgress.isBefore(ActivationProgress.SET_UNIQUE_ID)) {
observables.add( observables.add(
observeSendSetUniqueIdCommand.doOnComplete(ActivationProgressUpdater(ActivationProgress.SET_UNIQUE_ID)) observeSendSetUniqueIdCommand.doOnComplete(ActivationProgressUpdater(ActivationProgress.SET_UNIQUE_ID))

View file

@ -21,6 +21,7 @@ class ServiceDiscoverer(
*/ */
fun discoverServices(): Map<CharacteristicType, BluetoothGattCharacteristic> { fun discoverServices(): Map<CharacteristicType, BluetoothGattCharacteristic> {
logger.debug(LTag.PUMPBTCOMM, "Discovering services") logger.debug(LTag.PUMPBTCOMM, "Discovering services")
bleCallbacks.startServiceDiscovery()
val discover = gatt.discoverServices() val discover = gatt.discoverServices()
if (!discover) { if (!discover) {
throw ConnectException("Could not start discovering services`") throw ConnectException("Could not start discovering services`")

View file

@ -61,6 +61,10 @@ class BleCommCallbacks(
} }
} }
fun startServiceDiscovery() {
serviceDiscoveryComplete = CountDownLatch(1)
}
fun waitForServiceDiscovery(timeoutMs: Int) { fun waitForServiceDiscovery(timeoutMs: Int) {
try { try {
serviceDiscoveryComplete.await(timeoutMs.toLong(), TimeUnit.MILLISECONDS) serviceDiscoveryComplete.await(timeoutMs.toLong(), TimeUnit.MILLISECONDS)

View file

@ -35,11 +35,12 @@ interface OmnipodDashPodStateManager {
val isPodKaput: Boolean val isPodKaput: Boolean
var bluetoothConnectionState: BluetoothConnectionState var bluetoothConnectionState: BluetoothConnectionState
var timeZone: DateTimeZone var timeZone: TimeZone
val lastUpdatedSystem: Long // System.currentTimeMillis() val lastUpdatedSystem: Long // System.currentTimeMillis()
val lastStatusResponseReceived: Long val lastStatusResponseReceived: Long
val time: DateTime? val time: DateTime?
val timeBehind: Duration? val timeDrift: Duration?
val expiry: DateTime?
val messageSequenceNumber: Short val messageSequenceNumber: Short
val sequenceNumberOfLastProgrammingCommand: Short? val sequenceNumberOfLastProgrammingCommand: Short?
@ -96,6 +97,12 @@ interface OmnipodDashPodStateManager {
fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType) fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType)
fun markLastBolusComplete(): LastBolus? fun markLastBolusComplete(): LastBolus?
fun onStart() fun onStart()
/*
This is called only:. It overwrites activationStatus
- when activation was interrupted(application crash, killed, etc)
- after getPodStatus was successful(we have an up-to-date podStatus)
*/
fun recoverActivationFromPodStatus(): String?
data class ActiveCommand( data class ActiveCommand(
val sequence: Short, val sequence: Short,

View file

@ -99,10 +99,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
} }
override var timeZone: DateTimeZone override var timeZone: TimeZone
get() = podState.timeZone get() = TimeZone.getTimeZone(podState.timeZone)
set(tz) { set(tz) {
podState.timeZone = tz podState.timeZone = tz.getDisplayName(true, TimeZone.SHORT)
store() store()
} }
@ -189,16 +189,27 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
if ((activationTime != null) && (minutesSinceActivation != null)) { if ((activationTime != null) && (minutesSinceActivation != null)) {
return DateTime(activationTime) return DateTime(activationTime)
.plusMinutes(minutesSinceActivation.toInt()) .plusMinutes(minutesSinceActivation.toInt())
.plus(Duration(podState.lastStatusResponseReceived, System.currentTimeMillis())) .plus(Duration(podState.lastUpdatedSystem, System.currentTimeMillis()))
} }
return null return null
} }
override val timeBehind: Duration? override val timeDrift: Duration?
get() { get() {
return Duration(DateTime.now(), time) return Duration(DateTime.now(), time)
} }
override val expiry: DateTime?
// TODO: Consider storing expiry datetime in pod state saving continuously recalculating to the same value
get() {
val podLifeInHours = podLifeInHours
val activationTime = podState.activationTime
if (podLifeInHours != null && activationTime != null) {
return DateTime(podState.activationTime).plusHours(podLifeInHours.toInt())
}
return null
}
override var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState override var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState
get() = podState.bluetoothConnectionState get() = podState.bluetoothConnectionState
set(bluetoothConnectionState) { set(bluetoothConnectionState) {
@ -312,6 +323,29 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
} }
override fun recoverActivationFromPodStatus(): String? {
val newActivationProgress = when (podState.podStatus) {
PodStatus.FILLED ->
ActivationProgress.NOT_STARTED
PodStatus.UID_SET ->
ActivationProgress.SET_UNIQUE_ID
PodStatus.ENGAGING_CLUTCH_DRIVE, PodStatus.PRIMING ->
return "Busy"
PodStatus.CLUTCH_DRIVE_ENGAGED ->
ActivationProgress.PRIME_COMPLETED
PodStatus.BASAL_PROGRAM_SET ->
ActivationProgress.PROGRAMMED_BASAL
PodStatus.RUNNING_ABOVE_MIN_VOLUME, PodStatus.RUNNING_BELOW_MIN_VOLUME ->
ActivationProgress.CANNULA_INSERTED
else ->
null
}
newActivationProgress?.let {
podState.activationProgress = it
}
return null
}
@Synchronized @Synchronized
override fun updateActiveCommand() = Maybe.create<CommandConfirmed> { source -> override fun updateActiveCommand() = Maybe.create<CommandConfirmed> { source ->
val activeCommand = podState.activeCommand val activeCommand = podState.activeCommand
@ -402,6 +436,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
podState.lastStatusResponseReceived = now + 2 podState.lastStatusResponseReceived = now + 2
podState.activeCommand = newCommand podState.activeCommand = newCommand
} }
CommandSendingNotConfirmed -> { CommandSendingNotConfirmed -> {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val newCommand = podState.activeCommand?.copy( val newCommand = podState.activeCommand?.copy(
@ -410,10 +445,10 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
) )
podState.lastStatusResponseReceived = 0 podState.lastStatusResponseReceived = 0
} }
CommandSendingFailure, NoActiveCommand -> CommandSendingFailure, NoActiveCommand ->
podState.activeCommand = null podState.activeCommand = null
} }
} }
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) { override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
@ -431,6 +466,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
podState.lastUpdatedSystem = System.currentTimeMillis() podState.lastUpdatedSystem = System.currentTimeMillis()
podState.lastStatusResponseReceived = SystemClock.elapsedRealtime() podState.lastStatusResponseReceived = SystemClock.elapsedRealtime()
updateLastBolusFromResponse(response.bolusPulsesRemaining) updateLastBolusFromResponse(response.bolusPulsesRemaining)
if (podState.activationTime == null) {
podState.activationTime = System.currentTimeMillis() - (response.minutesSinceActivation * 60000)
}
store() store()
rxBus.send(EventOmnipodDashPumpValuesChanged()) rxBus.send(EventOmnipodDashPumpValuesChanged())
@ -479,12 +517,6 @@ 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())
} }
@ -565,7 +597,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var ltk: ByteArray? = null var ltk: ByteArray? = null
var eapAkaSequenceNumber: Long = 1 var eapAkaSequenceNumber: Long = 1
var bolusPulsesRemaining: Short = 0 var bolusPulsesRemaining: Short = 0
var timeZone = DateTimeZone.getDefault() var timeZone: String = "" // TimeZone ID (e.g. "Europe/Amsterdam")
var bleVersion: SoftwareVersion? = null var bleVersion: SoftwareVersion? = null
var firmwareVersion: SoftwareVersion? = null var firmwareVersion: SoftwareVersion? = null

View file

@ -125,7 +125,7 @@ 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.SET_UNIQUE_ID) || podStateManager.ltk != null ||
podStateManager.podStatus == PodStatus.ALARM podStateManager.podStatus == PodStatus.ALARM
if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)) { if (podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED)) {

View file

@ -45,7 +45,6 @@ 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.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
@ -69,7 +68,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds
private const val PLACEHOLDER = "-" private const val PLACEHOLDER = "-"
private const val MAX_TIME_DEVIATION_MINUTES = 15L private const val MAX_TIME_DEVIATION_MINUTES = 10L
} }
private var disposables: CompositeDisposable = CompositeDisposable() private var disposables: CompositeDisposable = CompositeDisposable()
@ -237,31 +236,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
} }
// Get time on pod 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) && (minutesSinceActivation != null)) {
timeOnPod = DateTime(activationTime)
.plusMinutes(minutesSinceActivation.toInt())
.plus(Duration(podStateManager.lastStatusResponseReceived, System.currentTimeMillis()))
}
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 && minutesSinceActivation != null) {
val expiresInMinutes = podLifeInHours * 60 - minutesSinceActivation
expiresAt = DateTime().plusMinutes(expiresInMinutes)
}
return expiresAt
}
private fun updateOmnipodStatus() { private fun updateOmnipodStatus() {
updateLastConnection() updateLastConnection()
updateLastBolus() updateLastBolus()
@ -294,35 +268,34 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
) )
// Update time on Pod // Update time on Pod
// TODO: For now: derive from podStateManager.minutesSinceActivation podInfoBinding.timeOnPod.text = podStateManager.time?.let {
val timeOnPod = getTimeOnPod()
podInfoBinding.timeOnPod.text = podStateManager.time?.let{
readableZonedTime(it) readableZonedTime(it)
} ?: PLACEHOLDER } ?: PLACEHOLDER
podInfoBinding.timeOnPod.setTextColor( podInfoBinding.timeOnPod.setTextColor(
podStateManager.timeBehind?.let { podStateManager.timeDrift?.let {
if (it.abs().isLongerThan(Duration.standardMinutes(MAX_TIME_DEVIATION_MINUTES))) { if (it.abs().isLongerThan(Duration.standardMinutes(MAX_TIME_DEVIATION_MINUTES))) {
Color.RED Color.RED
}else { } else {
Color.WHITE Color.WHITE
} }
} ?: Color.WHITE } ?: Color.WHITE
) )
// Update Pod expiry time // Update Pod expiry time
val expiresAt = getExpiryAt() val expiresAt = podStateManager.expiry
if (expiresAt == null) { if (expiresAt == null) {
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(
Color.RED if (DateTime.now().isAfter(expiresAt)) {
} else { Color.RED
Color.WHITE } else {
}) Color.WHITE
}
)
} }
podStateManager.alarmType?.let { podStateManager.alarmType?.let {
@ -413,9 +386,7 @@ 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( podInfoBinding.lastConnection.text = PLACEHOLDER
Duration(podStateManager.lastUpdatedSystem, System.currentTimeMillis())
)
} }
} }
@ -552,7 +523,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateRefreshStatusButton() { private fun updateRefreshStatusButton() {
buttonBinding.buttonRefreshStatus.isEnabled = buttonBinding.buttonRefreshStatus.isEnabled =
podStateManager.isUniqueIdSet && podStateManager.isUniqueIdSet &&
podStateManager.activationProgress.isAtLeast(ActivationProgress.PHASE_1_COMPLETED) &&
isQueueEmpty() isQueueEmpty()
} }
@ -630,25 +600,41 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
} }
private fun getTimeZone(): DateTimeZone { // private fun getTimeZone(): DateTimeZone {
// TODO: Get timezone as configured/podState // // return getSafe(() -> podState.getTimeZone());
// return getSafe(() -> podState.getTimeZone()); // return podStateManager.timeZone
return DateTimeZone.getDefault() // }
private fun getTimeZone(): TimeZone {
// Return timezone ID (e.g "Europe/Amsterdam")
return podStateManager.timeZone
} }
private fun readableZonedTime(time: DateTime): String { private fun readableZonedTime(time: DateTime): String {
val timeAsJavaData = time.toLocalDateTime().toDate() val timeAsJavaData = time.toLocalDateTime().toDate()
return dateUtil.dateAndTimeString(timeAsJavaData.time)
val timeZone = getTimeZone().toTimeZone() // // TODO: Handle timeZone ID
if (timeZone == TimeZone.getDefault()) { // val timeZone = getTimeZone()
return dateUtil.dateAndTimeString(timeAsJavaData.time) // if (timeZone == "") {
} // // No timezone defined, use local time (default)
// return dateUtil.dateAndTimeString(timeAsJavaData.time)
// Get full timezoned time // }
val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData) // else {
val locale = resources.configuration.locales.get(0) // // Get full timezoned time
val timeZoneDisplayName = timeZone.getDisplayName(isDaylightTime, TimeZone.SHORT, locale) + " " + timeZone.getDisplayName(isDaylightTime, TimeZone.LONG, locale) // val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
return resourceHelper.gs(R.string.omnipod_common_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData.time), timeZoneDisplayName) // val locale = resources.configuration.locales.get(0)
// 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.time),
// timeZoneDisplayName
// )
// }
} }
private fun readableDuration(duration: Duration): String { private fun readableDuration(duration: Duration): String {

View file

@ -2,13 +2,17 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activatio
import androidx.annotation.StringRes import androidx.annotation.StringRes
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpSync
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.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activation.viewmodel.action.InsertCannulaViewModel
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.driver.OmnipodDashManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
@ -17,6 +21,8 @@ import javax.inject.Inject
class DashInsertCannulaViewModel @Inject constructor( class DashInsertCannulaViewModel @Inject constructor(
private val omnipodManager: OmnipodDashManager, private val omnipodManager: OmnipodDashManager,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val pumpSync: PumpSync,
private val podStateManager: OmnipodDashPodStateManager,
injector: HasAndroidInjector, injector: HasAndroidInjector,
logger: AAPSLogger logger: AAPSLogger
) : InsertCannulaViewModel(injector, logger) { ) : InsertCannulaViewModel(injector, logger) {
@ -52,6 +58,19 @@ class DashInsertCannulaViewModel @Inject constructor(
}, },
onComplete = { onComplete = {
logger.debug("Pod activation part 2 completed") logger.debug("Pod activation part 2 completed")
pumpSync.connectNewPump()
pumpSync.insertTherapyEventIfNewWithTimestamp(
timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.CANNULA_CHANGE,
pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
)
pumpSync.insertTherapyEventIfNewWithTimestamp(
timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.INSULIN_CHANGE,
pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
)
source.onSuccess(PumpEnactResult(injector).success(true)) source.onSuccess(PumpEnactResult(injector).success(true))
} }
) )