combov2: Use dedicated variable for keeping track of suspended state
This is necessary to prevent race conditions during connection setup. isSuspended() was relying on the driverStateUIFlow to determine if the pump is currently suspended. However, because driverStateUIFlow is updated in a separate coroutine, there is a chance that the Suspended state is set shortly _after_ connect() called isSuspended() to determine if the pump is suspended or not, causing connect() to do the wrong thing and assume that the pump is currently connected. Since the main screen does not show the current profile number, this leads to the error message "profile 1 must be the active profile, not profile 0".
This commit is contained in:
parent
05c2a42ba4
commit
8537c42d0a
1 changed files with 40 additions and 15 deletions
|
@ -189,6 +189,18 @@ class ComboV2Plugin @Inject constructor (
|
||||||
// allow other components to react to state changes.
|
// allow other components to react to state changes.
|
||||||
private val _driverStateFlow = MutableStateFlow<DriverState>(DriverState.NotInitialized)
|
private val _driverStateFlow = MutableStateFlow<DriverState>(DriverState.NotInitialized)
|
||||||
|
|
||||||
|
// If true, the pump was found to be suspended during the connect()
|
||||||
|
// call. This is separate from driverStateFlow and driverStateUIFlow.
|
||||||
|
// It is set immediately after connect() (while the other two may be
|
||||||
|
// set in a separate coroutine), which is important for check
|
||||||
|
// inside connect() and checks before commands like deliverTreatment()
|
||||||
|
// are run. This is what drives the isSuspended() call, and is _not_
|
||||||
|
// to be used for UI update purposes (use driverStateUIFlow for that).
|
||||||
|
// Like driverStateUIFlow, this state persists even after disconnecting
|
||||||
|
// from the pump. This is necessary, because AAPS may call isSuspended()
|
||||||
|
// even when the pump is not connected.
|
||||||
|
private var pumpIsSuspended = false
|
||||||
|
|
||||||
// The basal profile that is set to be the pump's current profile.
|
// The basal profile that is set to be the pump's current profile.
|
||||||
// If the pump's actual basal profile deviates from this, it is
|
// If the pump's actual basal profile deviates from this, it is
|
||||||
// overwritten. This check is performed in checkBasalProfile().
|
// overwritten. This check is performed in checkBasalProfile().
|
||||||
|
@ -225,15 +237,19 @@ class ComboV2Plugin @Inject constructor (
|
||||||
// Driver is connected, but pump is suspended and
|
// Driver is connected, but pump is suspended and
|
||||||
// cannot currently execute commands. This state is
|
// cannot currently execute commands. This state is
|
||||||
// special in that it technically persists even after
|
// special in that it technically persists even after
|
||||||
// disconnecting. However, it is still important to
|
// disconnecting (because the pump remains suspended
|
||||||
// model it as a driver state to prevent commands
|
// until the user resumes it, not until the connection
|
||||||
// that deliver insulin from being executed (and,
|
// is terminated), but it does not persists the same
|
||||||
// it is needed for the isSuspended() implementation).
|
// way here (it is replaced by Disconnected after
|
||||||
// NOTE: Instead of comparing the driverStateFlow
|
// the connection is terminated). This state is used
|
||||||
// value with this state directly, consider using
|
// for UI updates (see driverStateUIFlow) and for
|
||||||
// isSuspended() instead, since it is based on the
|
// checks during driver state updates and connection
|
||||||
// driverStateUIFlow, and thus retains the Suspended
|
// attempts.
|
||||||
// and Error states even after disconnecting.
|
// NOTE: Do not compare against this state to check
|
||||||
|
// prior to commands like deliverTreatment() if
|
||||||
|
// the pump is currently suspended or not. Use
|
||||||
|
// isSuspended() instead. See the pumpIsSuspended
|
||||||
|
// documentation for details.
|
||||||
object Suspended : DriverState("suspended")
|
object Suspended : DriverState("suspended")
|
||||||
// Driver is currently executing a command.
|
// Driver is currently executing a command.
|
||||||
// isBusy() will return true in this state.
|
// isBusy() will return true in this state.
|
||||||
|
@ -453,12 +469,7 @@ class ComboV2Plugin @Inject constructor (
|
||||||
override fun isInitialized(): Boolean =
|
override fun isInitialized(): Boolean =
|
||||||
isPaired() && (driverStateFlow.value != DriverState.NotInitialized) && !pumpErrorObserved
|
isPaired() && (driverStateFlow.value != DriverState.NotInitialized) && !pumpErrorObserved
|
||||||
|
|
||||||
override fun isSuspended(): Boolean =
|
override fun isSuspended(): Boolean = pumpIsSuspended
|
||||||
when (driverStateUIFlow.value) {
|
|
||||||
DriverState.Suspended,
|
|
||||||
DriverState.Error -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isBusy(): Boolean =
|
override fun isBusy(): Boolean =
|
||||||
when (driverStateFlow.value) {
|
when (driverStateFlow.value) {
|
||||||
|
@ -656,6 +667,14 @@ class ComboV2Plugin @Inject constructor (
|
||||||
// No need to set the driver state here, since the pump's stateFlow will announce that.
|
// No need to set the driver state here, since the pump's stateFlow will announce that.
|
||||||
|
|
||||||
pump?.let {
|
pump?.let {
|
||||||
|
pumpIsSuspended = when (it.stateFlow.value) {
|
||||||
|
ComboCtlPump.State.Suspended,
|
||||||
|
is ComboCtlPump.State.Error -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
aapsLogger.debug(LTag.PUMP, "Pump is suspended: $pumpIsSuspended")
|
||||||
|
|
||||||
// We can't read the active profile number in the suspended state, since
|
// We can't read the active profile number in the suspended state, since
|
||||||
// the Combo's screen does not show any profile number then.
|
// the Combo's screen does not show any profile number then.
|
||||||
if (!isSuspended()) {
|
if (!isSuspended()) {
|
||||||
|
@ -1729,6 +1748,12 @@ class ComboV2Plugin @Inject constructor (
|
||||||
// This is a variant of driverStateFlow that retains the Error
|
// This is a variant of driverStateFlow that retains the Error
|
||||||
// and Suspended state even after disconnecting to make sure these
|
// and Suspended state even after disconnecting to make sure these
|
||||||
// states kept being showed to the user post-disconnect.
|
// states kept being showed to the user post-disconnect.
|
||||||
|
// NOTE: Do not rely on this to check prior to a command if the
|
||||||
|
// pump is suspended or not, since the driver state UI flow is
|
||||||
|
// updated in a separate coroutine, and is _only_ meant for UI
|
||||||
|
// updates. Using this for other purposes can cause race conditions
|
||||||
|
// to appear, such as when immediately after the Pump.connect() call
|
||||||
|
// finishes, the state is checked. Use isSuspended() instead.
|
||||||
private val _driverStateUIFlow = MutableStateFlow<DriverState>(DriverState.NotInitialized)
|
private val _driverStateUIFlow = MutableStateFlow<DriverState>(DriverState.NotInitialized)
|
||||||
val driverStateUIFlow = _driverStateUIFlow.asStateFlow()
|
val driverStateUIFlow = _driverStateUIFlow.asStateFlow()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue