Use timeout in connect states, other minor changes

This commit is contained in:
jbr7rr 2023-05-29 14:40:08 +02:00
parent 37a6a4f271
commit 3c8faa6723
4 changed files with 110 additions and 86 deletions

View file

@ -236,8 +236,8 @@ import kotlin.math.round
result.success = connectionOK && abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep result.success = connectionOK && abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep
result.bolusDelivered = t.insulin result.bolusDelivered = t.insulin
if (!result.success) { if (!result.success) {
// Todo error code? // Note: There are no error codes
result.comment = "error" result.comment = "failed"
} else { } else {
result.comment = "ok" result.comment = "ok"
} }
@ -266,8 +266,8 @@ import kotlin.math.round
if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false) if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false)
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - absoluteRate: $absoluteRate, durationInMinutes: $durationInMinutes, enforceNew: $enforceNew") aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - absoluteRate: $absoluteRate, durationInMinutes: $durationInMinutes, enforceNew: $enforceNew")
// round rate to 0.05 // round rate to pump rate
val pumpRate = round(absoluteRate * 20) / 20 // TODO: Maybe replace by constraints thing val pumpRate = constraintChecker.applyBasalConstraints(Constraint(absoluteRate), profile).value()
temporaryBasalStorage.add(PumpSync.PumpState.TemporaryBasal(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), pumpRate, true, tbrType, 0L, 0L)) temporaryBasalStorage.add(PumpSync.PumpState.TemporaryBasal(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), pumpRate, true, tbrType, 0L, 0L))
val connectionOk = medtrumService?.setTempBasal(pumpRate, durationInMinutes) ?: false val connectionOk = medtrumService?.setTempBasal(pumpRate, durationInMinutes) ?: false
if (connectionOk if (connectionOk
@ -307,7 +307,7 @@ import kotlin.math.round
} }
override fun cancelExtendedBolus(): PumpEnactResult { override fun cancelExtendedBolus(): PumpEnactResult {
return PumpEnactResult(injector) // TODO return PumpEnactResult(injector)
} }
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
@ -333,10 +333,10 @@ import kotlin.math.round
return ""// TODO return ""// TODO
} }
override val isFakingTempsByExtendedBoluses: Boolean = false //TODO override val isFakingTempsByExtendedBoluses: Boolean = false
override fun loadTDDs(): PumpEnactResult { override fun loadTDDs(): PumpEnactResult {
return PumpEnactResult(injector) // TODO return PumpEnactResult(injector) // Note: Can implement this if we implement history fully (no priority)
} }
override fun canHandleDST(): Boolean { override fun canHandleDST(): Boolean {

View file

@ -66,7 +66,15 @@ class MedtrumPump @Inject constructor(
set(value) { set(value) {
_primeProgress.value = value _primeProgress.value = value
} }
private var _lastBasalType: MutableStateFlow<BasalType> = MutableStateFlow(BasalType.NONE)
val lastBasalTypeFlow: StateFlow<BasalType> = _lastBasalType
var lastBasalType: BasalType
get() = _lastBasalType.value
set(value) {
_lastBasalType.value = value
}
private val _lastBasalRate = MutableStateFlow(0.0) private val _lastBasalRate = MutableStateFlow(0.0)
val lastBasalRateFlow: StateFlow<Double> = _lastBasalRate val lastBasalRateFlow: StateFlow<Double> = _lastBasalRate
var lastBasalRate: Double var lastBasalRate: Double
@ -125,15 +133,6 @@ class MedtrumPump @Inject constructor(
sp.putString(R.string.key_actual_basal_profile, encodedString?: "") sp.putString(R.string.key_actual_basal_profile, encodedString?: "")
} }
private var _lastBasalType: MutableStateFlow<BasalType> = MutableStateFlow(BasalType.NONE)
val lastBasalTypeFlow: StateFlow<BasalType> = _lastBasalType
var lastBasalType: BasalType
get() = _lastBasalType.value
set(value) {
_lastBasalType.value = value
sp.putInt(R.string.key_last_basal_type, value.ordinal) // TODO is this still needed in SP?
}
private var _pumpSN = 0L private var _pumpSN = 0L
val pumpSN: Long val pumpSN: Long
get() = _pumpSN get() = _pumpSN
@ -161,8 +160,6 @@ class MedtrumPump @Inject constructor(
var bolusDone = false // success end var bolusDone = false // success end
// Last basal status update // Last basal status update
// TODO: Save this in SP?
var lastBasalSequence = 0 var lastBasalSequence = 0
var lastBasalPatchId = 0L var lastBasalPatchId = 0L
var lastBasalStartTime = 0L var lastBasalStartTime = 0L
@ -194,7 +191,6 @@ class MedtrumPump @Inject constructor(
_currentSequenceNumber = sp.getInt(R.string.key_current_sequence_number, 0) _currentSequenceNumber = sp.getInt(R.string.key_current_sequence_number, 0)
_patchId = sp.getLong(R.string.key_patch_id, 0L) _patchId = sp.getLong(R.string.key_patch_id, 0L)
_syncedSequenceNumber = sp.getInt(R.string.key_synced_sequence_number, 0) _syncedSequenceNumber = sp.getInt(R.string.key_synced_sequence_number, 0)
lastBasalType = enumValues<BasalType>()[sp.getInt(R.string.key_last_basal_type, 0)] // TODO: is this nice?
val encodedString = sp.getString(R.string.key_actual_basal_profile, "0") val encodedString = sp.getString(R.string.key_actual_basal_profile, "0")
try { try {

View file

@ -41,6 +41,11 @@ import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T import info.nightscout.shared.utils.T
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -74,6 +79,8 @@ class MedtrumService : DaggerService(), BLECommCallback {
private var currentState: State = IdleState() private var currentState: State = IdleState()
private var mPacket: MedtrumPacket? = null private var mPacket: MedtrumPacket? = null
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
val isConnected: Boolean val isConnected: Boolean
get() = medtrumPump.connectionState == ConnectionState.CONNECTED get() = medtrumPump.connectionState == ConnectionState.CONNECTED
@ -100,8 +107,9 @@ class MedtrumService : DaggerService(), BLECommCallback {
} }
override fun onDestroy() { override fun onDestroy() {
disposable.clear()
super.onDestroy() super.onDestroy()
disposable.clear()
scope.cancel()
} }
fun connect(from: String): Boolean { fun connect(from: String): Boolean {
@ -141,12 +149,9 @@ class MedtrumService : DaggerService(), BLECommCallback {
} }
fun readPumpStatus() { fun readPumpStatus() {
// TODO decide what we need to do here
var result = false
// Most of these things are already done when a connection is setup, but wo dont know how long the pump was connected for? // Most of these things are already done when a connection is setup, but wo dont know how long the pump was connected for?
// So just do a syncronize to make sure we have the latest data // So just do a syncronize to make sure we have the latest data
result = sendPacketAndGetResponse(SynchronizePacket(injector)) var result = sendPacketAndGetResponse(SynchronizePacket(injector))
// Sync records (based on the info we have from the sync) // Sync records (based on the info we have from the sync)
if (result) result = syncRecords() if (result) result = syncRecords()
@ -224,9 +229,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
} }
fun cancelTempBasal(): Boolean { fun cancelTempBasal(): Boolean {
var result = false var result = sendPacketAndGetResponse(CancelTempBasalPacket(injector))
result = sendPacketAndGetResponse(CancelTempBasalPacket(injector))
// Get history records, this will update the pump state // Get history records, this will update the pump state
if (result) result = syncRecords() if (result) result = syncRecords()
@ -330,6 +333,9 @@ class MedtrumService : DaggerService(), BLECommCallback {
// State class, Can we move this to different file? // State class, Can we move this to different file?
private abstract inner class State { private abstract inner class State {
protected var responseHandled = false
protected var responseSuccess = false
open fun onEnter() {} open fun onEnter() {}
open fun onIndication(data: ByteArray) { open fun onIndication(data: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "onIndication: " + this.toString() + "Should not be called here!") aapsLogger.debug(LTag.PUMPCOMM, "onIndication: " + this.toString() + "Should not be called here!")
@ -339,22 +345,39 @@ class MedtrumService : DaggerService(), BLECommCallback {
aapsLogger.debug(LTag.PUMPCOMM, "onConnected") aapsLogger.debug(LTag.PUMPCOMM, "onConnected")
} }
open fun onDisconnected() { fun onDisconnected() {
aapsLogger.debug(LTag.PUMPCOMM, "onDisconnected") aapsLogger.debug(LTag.PUMPCOMM, "onDisconnected")
medtrumPump.connectionState = ConnectionState.DISCONNECTED medtrumPump.connectionState = ConnectionState.DISCONNECTED
if (medtrumPump.patchActivated) { if (medtrumPump.patchActivated) {
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED))
} }
// TODO: Check flow for this responseHandled = true
responseSuccess = false
toState(IdleState()) toState(IdleState())
} }
open fun waitForResponse(): Boolean { fun waitForResponse(): Boolean {
return false val startTime = System.currentTimeMillis()
val timeoutMillis = T.secs(45).msecs()
while (!responseHandled) {
if (System.currentTimeMillis() - startTime > timeoutMillis) {
// If we haven't received a response in the specified time, assume the command failed
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service State timeout")
// Disconnect to cancel any outstanding commands and go back to ready state
bleComm.disconnect("Timeout")
toState(IdleState())
return false
}
SystemClock.sleep(100)
}
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service State responseHandled: $responseHandled responseSuccess: $responseSuccess")
return responseSuccess
} }
open fun onSendMessageError(reason: String) { fun onSendMessageError(reason: String) {
aapsLogger.debug(LTag.PUMPCOMM, "onSendMessageError: " + this.toString() + "reason: $reason") aapsLogger.debug(LTag.PUMPCOMM, "onSendMessageError: " + this.toString() + "reason: $reason")
responseHandled = true
responseSuccess = false
} }
} }
@ -368,13 +391,9 @@ class MedtrumService : DaggerService(), BLECommCallback {
super.onConnected() super.onConnected()
toState(AuthState()) toState(AuthState())
} }
override fun onDisconnected() {
super.onDisconnected()
}
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class AuthState : State() { private inner class AuthState : State() {
val retryCounter = 0 val retryCounter = 0
@ -382,11 +401,16 @@ class MedtrumService : DaggerService(), BLECommCallback {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached AuthState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached AuthState")
mPacket = AuthorizePacket(injector) mPacket = AuthorizePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
// TODO Get pump version info // TODO Get pump version info
val deviceType = (mPacket as AuthorizePacket).deviceType val deviceType = (mPacket as AuthorizePacket).deviceType
val swVersion = (mPacket as AuthorizePacket).swVersion val swVersion = (mPacket as AuthorizePacket).swVersion
@ -394,31 +418,31 @@ class MedtrumService : DaggerService(), BLECommCallback {
toState(GetDeviceTypeState()) toState(GetDeviceTypeState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
// retry twice responseHandled = true
// TODO: Test and see if this can be removed responseSuccess = false
if (retryCounter < 2) { bleComm.disconnect("Failure")
aapsLogger.error(LTag.PUMPCOMM, "AuthState failed!, retrying") toState(IdleState())
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
} else {
bleComm.disconnect("Failure")
toState(IdleState())
}
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class GetDeviceTypeState : State() { private inner class GetDeviceTypeState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetDeviceTypeState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetDeviceTypeState")
mPacket = GetDeviceTypePacket(injector) mPacket = GetDeviceTypePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
// TODO Get device type and SN // TODO Get device type and SN
val deviceType = (mPacket as GetDeviceTypePacket).deviceType val deviceType = (mPacket as GetDeviceTypePacket).deviceType
val deviceSN = (mPacket as GetDeviceTypePacket).deviceSN val deviceSN = (mPacket as GetDeviceTypePacket).deviceSN
@ -426,24 +450,31 @@ class MedtrumService : DaggerService(), BLECommCallback {
toState(GetTimeState()) toState(GetTimeState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class GetTimeState : State() { private inner class GetTimeState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetTimeState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached GetTimeState")
mPacket = GetTimePacket(injector) mPacket = GetTimePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
val currTime = dateUtil.nowWithoutMilliseconds() val currTime = dateUtil.nowWithoutMilliseconds()
if (abs(medtrumPump.lastTimeReceivedFromPump - currTime) <= T.secs(5).msecs()) { // Allow 5 sec deviation if (abs(medtrumPump.lastTimeReceivedFromPump - currTime) <= T.secs(5).msecs()) { // Allow 5 sec deviation
toState(SynchronizeState()) toState(SynchronizeState())
@ -460,90 +491,120 @@ class MedtrumService : DaggerService(), BLECommCallback {
} }
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class SetTimeState : State() { private inner class SetTimeState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeState")
mPacket = SetTimePacket(injector) mPacket = SetTimePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
toState(SetTimeZoneState()) toState(SetTimeZoneState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class SetTimeZoneState : State() { private inner class SetTimeZoneState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeZoneState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SetTimeZoneState")
mPacket = SetTimeZonePacket(injector) mPacket = SetTimeZonePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
toState(SynchronizeState()) toState(SynchronizeState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class SynchronizeState : State() { private inner class SynchronizeState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SynchronizeState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SynchronizeState")
mPacket = SynchronizePacket(injector) mPacket = SynchronizePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
toState(SubscribeState()) toState(SubscribeState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
} }
} }
// State for connect flow, could be replaced by commandState and steps in connect() // State for connect flow
private inner class SubscribeState : State() { private inner class SubscribeState : State() {
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SubscribeState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached SubscribeState")
mPacket = SubscribePacket(injector) mPacket = SubscribePacket(injector)
mPacket?.getRequest()?.let { bleComm.sendMessage(it) } mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
scope.launch {
waitForResponse()
}
} }
override fun onIndication(data: ByteArray) { override fun onIndication(data: ByteArray) {
if (mPacket?.handleResponse(data) == true) { if (mPacket?.handleResponse(data) == true) {
// Succes! // Succes!
responseHandled = true
responseSuccess = true
toState(ReadyState()) toState(ReadyState())
} else if (mPacket?.failed == true) { } else if (mPacket?.failed == true) {
// Failure // Failure
responseHandled = true
responseSuccess = false
bleComm.disconnect("Failure") bleComm.disconnect("Failure")
toState(IdleState()) toState(IdleState())
} }
@ -566,9 +627,6 @@ class MedtrumService : DaggerService(), BLECommCallback {
// This state is when a command is send and we wait for a response for that command // This state is when a command is send and we wait for a response for that command
private inner class CommandState : State() { private inner class CommandState : State() {
private var responseHandled = false
private var responseSuccess = false
override fun onEnter() { override fun onEnter() {
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached CommandState") aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached CommandState")
} }
@ -586,34 +644,5 @@ class MedtrumService : DaggerService(), BLECommCallback {
toState(ReadyState()) toState(ReadyState())
} }
} }
override fun onDisconnected() {
super.onDisconnected()
responseHandled = true
responseSuccess = false
}
override fun onSendMessageError(reason: String) {
super.onSendMessageError(reason)
responseHandled = true
responseSuccess = false
}
override fun waitForResponse(): Boolean {
val startTime = System.currentTimeMillis()
val timeoutMillis = T.secs(45).msecs()
while (!responseHandled) {
if (System.currentTimeMillis() - startTime > timeoutMillis) {
// If we haven't received a response in the specified time, assume the command failed
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service CommandState timeout")
// Disconnect to cancel any outstanding commands and go back to ready state
bleComm.disconnect("Timeout")
toState(ReadyState())
return false
}
Thread.sleep(100)
}
return responseSuccess
}
} }
} }

View file

@ -7,7 +7,6 @@
<string name="key_session_token" translatable="false">medtrum_session_token</string> <string name="key_session_token" translatable="false">medtrum_session_token</string>
<string name="key_patch_id" translatable="false">patch_id</string> <string name="key_patch_id" translatable="false">patch_id</string>
<string name="key_actual_basal_profile" translatable="false">actual_basal_profile</string> <string name="key_actual_basal_profile" translatable="false">actual_basal_profile</string>
<string name="key_last_basal_type" translatable="false">last_basal_type</string>
<string name="key_current_sequence_number" translatable="false">current_sequence_number</string> <string name="key_current_sequence_number" translatable="false">current_sequence_number</string>
<string name="key_synced_sequence_number" translatable="false">synced_sequence_number</string> <string name="key_synced_sequence_number" translatable="false">synced_sequence_number</string>