Recovery option when patch activation process is interrupted

This commit is contained in:
jbr7rr 2023-07-01 20:46:29 +02:00
parent af7e38a6db
commit 09aa78f1d1
13 changed files with 346 additions and 54 deletions

View file

@ -13,6 +13,8 @@ enum class PatchStep {
ATTACH_PATCH,
ACTIVATE,
ACTIVATE_COMPLETE,
RETRY_ACTIVATION,
RETRY_ACTIVATION_CONNECT,
ERROR,
CANCEL,
COMPLETE;

View file

@ -21,6 +21,8 @@ import info.nightscout.pump.medtrum.ui.MedtrumPrimingFragment
import info.nightscout.pump.medtrum.ui.MedtrumStartDeactivationFragment
import info.nightscout.pump.medtrum.ui.MedtrumActivity
import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment
import info.nightscout.pump.medtrum.ui.MedtrumRetryActivationConnectFragment
import info.nightscout.pump.medtrum.ui.MedtrumRetryActivationFragment
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumOverviewViewModel
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel
import info.nightscout.pump.medtrum.ui.viewmodel.ViewModelFactory
@ -77,6 +79,13 @@ abstract class MedtrumModule {
@ContributesAndroidInjector
internal abstract fun contributesPreparePatchConnectFragment(): MedtrumPreparePatchConnectFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesRetryActivationFragment(): MedtrumRetryActivationFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesRetryActivationConnectFragment(): MedtrumRetryActivationConnectFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPrimeFragment(): MedtrumPrimeFragment

View file

@ -157,6 +157,9 @@ class BLEComm @Inject internal constructor(
isConnected = false
isConnecting = true
mWritePackets = null
mReadPacket = null
if (mDevice != null && mDeviceSN == deviceSN) {
// Skip scanning and directly connect to gatt
aapsLogger.debug(LTag.PUMPBTCOMM, "Skipping scan and directly connecting to gatt")

View file

@ -43,16 +43,11 @@ class MedtrumActivity : MedtrumBaseActivity<ActivityMedtrumBinding>() {
PatchStep.ATTACH_PATCH -> setupViewFragment(MedtrumAttachPatchFragment.newInstance())
PatchStep.ACTIVATE -> setupViewFragment(MedtrumActivateFragment.newInstance())
PatchStep.ACTIVATE_COMPLETE -> setupViewFragment(MedtrumActivateCompleteFragment.newInstance())
PatchStep.CANCEL,
PatchStep.COMPLETE -> this@MedtrumActivity.finish()
PatchStep.ERROR -> Unit // Do nothing, let activity handle this
PatchStep.CANCEL -> {
if (setupStep.value !in listOf(MedtrumViewModel.SetupStep.ACTIVATED, MedtrumViewModel.SetupStep.START_DEACTIVATION, MedtrumViewModel.SetupStep.STOPPED)) {
resetPumpState()
}
this@MedtrumActivity.finish()
}
PatchStep.RETRY_ACTIVATION -> setupViewFragment(MedtrumRetryActivationFragment.newInstance())
PatchStep.RETRY_ACTIVATION_CONNECT -> setupViewFragment(MedtrumRetryActivationConnectFragment.newInstance())
PatchStep.START_DEACTIVATION -> setupViewFragment(MedtrumStartDeactivationFragment.newInstance())
PatchStep.DEACTIVATE -> setupViewFragment(MedtrumDeactivatePatchFragment.newInstance())

View file

@ -41,9 +41,10 @@ class MedtrumOverviewFragment : MedtrumBaseFragment<FragmentMedtrumOverviewBindi
EventType.CHANGE_PATCH_CLICKED -> requireContext().apply {
if (medtrumPump.pumpState > MedtrumPumpState.EJECTED && medtrumPump.pumpState < MedtrumPumpState.STOPPED) {
startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.START_DEACTIVATION))
} else {
} else if (medtrumPump.pumpState in listOf(MedtrumPumpState.STOPPED, MedtrumPumpState.NONE)) {
startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.PREPARE_PATCH))
medtrumPump.pumpState = MedtrumPumpState.NONE // Reset pumpstate here, fetch on next connection
} else {
startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.RETRY_ACTIVATION))
}
}
else -> Unit

View file

@ -34,7 +34,8 @@ class MedtrumPrimingFragment : MedtrumBaseFragment<FragmentMedtrumPrimingBinding
setupStep.observe(viewLifecycleOwner) {
when (it) {
MedtrumViewModel.SetupStep.INITIAL,
MedtrumViewModel.SetupStep.FILLED -> Unit // Nothing to do here, previous state
MedtrumViewModel.SetupStep.FILLED,
MedtrumViewModel.SetupStep.PRIMING -> Unit // Nothing to do here
MedtrumViewModel.SetupStep.PRIMED -> moveStep(PatchStep.PRIME_COMPLETE)
MedtrumViewModel.SetupStep.ERROR -> {

View file

@ -0,0 +1,50 @@
package info.nightscout.pump.medtrum.ui
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import info.nightscout.pump.medtrum.R
import info.nightscout.pump.medtrum.code.PatchStep
import info.nightscout.pump.medtrum.databinding.FragmentMedtrumRetryActivationConnectBinding
import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import javax.inject.Inject
class MedtrumRetryActivationConnectFragment : MedtrumBaseFragment<FragmentMedtrumRetryActivationConnectBinding>() {
@Inject lateinit var aapsLogger: AAPSLogger
companion object {
fun newInstance(): MedtrumRetryActivationConnectFragment = MedtrumRetryActivationConnectFragment()
}
override fun getLayoutId(): Int = R.layout.fragment_medtrum_retry_activation_connect
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
aapsLogger.debug(LTag.PUMP, "MedtrumRetryActivationConnectFragment onViewCreated")
binding.apply {
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java)
viewModel?.apply {
setupStep.observe(viewLifecycleOwner) {
when (it) {
MedtrumViewModel.SetupStep.INITIAL -> Unit // Nothing to do here
MedtrumViewModel.SetupStep.FILLED -> forceMoveStep(PatchStep.PRIME)
MedtrumViewModel.SetupStep.PRIMING -> forceMoveStep(PatchStep.PRIMING)
MedtrumViewModel.SetupStep.PRIMED -> forceMoveStep(PatchStep.PRIME_COMPLETE)
MedtrumViewModel.SetupStep.ACTIVATED -> forceMoveStep(PatchStep.ACTIVATE_COMPLETE)
else -> {
aapsLogger.error(LTag.PUMP, "Unexpected state: $it")
}
}
}
retryActivationConnect()
}
}
}
}

View file

@ -0,0 +1,46 @@
package info.nightscout.pump.medtrum.ui
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.pump.medtrum.R
import info.nightscout.pump.medtrum.code.PatchStep
import info.nightscout.pump.medtrum.databinding.FragmentMedtrumRetryActivationBinding
import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import javax.inject.Inject
class MedtrumRetryActivationFragment : MedtrumBaseFragment<FragmentMedtrumRetryActivationBinding>() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rh: ResourceHelper
companion object {
fun newInstance(): MedtrumRetryActivationFragment = MedtrumRetryActivationFragment()
}
override fun getLayoutId(): Int = R.layout.fragment_medtrum_retry_activation
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
aapsLogger.debug(LTag.PUMP, "MedtrumRetryActivationFragment onViewCreated")
binding.apply {
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java)
viewModel?.apply {
preparePatch() // Use this to make sure we are disconnceted at this stage
}
btnNegative.setOnClickListener {
OKDialog.showConfirmation(requireActivity(), rh.gs(R.string.medtrum_deactivate_pump_confirm)) {
viewModel?.apply {
moveStep(PatchStep.FORCE_DEACTIVATION)
}
}
}
}
}
}

View file

@ -70,7 +70,7 @@ class MedtrumViewModel @Inject constructor(
) {
medtrumService?.connect("Try reconnect from viewModel")
}
if (patchStep.value == PatchStep.PREPARE_PATCH_CONNECT) {
if (patchStep.value in listOf(PatchStep.PREPARE_PATCH_CONNECT, PatchStep.RETRY_ACTIVATION_CONNECT)) {
// We are disconnected during prepare patch connect, this means we failed to connect (wrong session token?)
// Retry 3 times, then give up
if (connectRetryCounter < 3) {
@ -104,7 +104,7 @@ class MedtrumViewModel @Inject constructor(
}
MedtrumPumpState.PRIMING -> {
// Do Nothing, wait for priming to complete
updateSetupStep(SetupStep.PRIMING)
}
MedtrumPumpState.PRIMED, MedtrumPumpState.EJECTED -> {
@ -159,7 +159,9 @@ class MedtrumViewModel @Inject constructor(
PatchStep.DEACTIVATE,
PatchStep.FORCE_DEACTIVATION,
PatchStep.DEACTIVATION_COMPLETE,
PatchStep.PREPARE_PATCH -> {
PatchStep.PREPARE_PATCH,
PatchStep.RETRY_ACTIVATION,
PatchStep.RETRY_ACTIVATION_CONNECT -> {
// Do nothing, deactivation uses commandQueue to control connection
}
@ -188,6 +190,13 @@ class MedtrumViewModel @Inject constructor(
aapsLogger.info(LTag.PUMP, "moveStep: $oldPatchStep -> $newPatchStep")
}
fun forceMoveStep(newPatchStep: PatchStep) {
val oldPatchStep = patchStep.value
prepareStep(newPatchStep)
aapsLogger.info(LTag.PUMP, "forceMoveStep: $oldPatchStep -> $newPatchStep")
}
fun initializePatchStep(step: PatchStep) {
mInitPatchStep = prepareStep(step)
}
@ -200,7 +209,7 @@ class MedtrumViewModel @Inject constructor(
scope.launch {
if (medtrumService?.isConnected == false) {
aapsLogger.info(LTag.PUMP, "preparePatch: new session")
// New session, generate new session token, only do this when not connected
// New session, generate new session token
medtrumPump.patchSessionToken = Crypt().generateRandomToken()
// Connect to pump
medtrumService?.connect("PreparePatch")
@ -256,8 +265,17 @@ class MedtrumViewModel @Inject constructor(
})
}
fun resetPumpState() {
fun retryActivationConnect() {
scope.launch {
if (medtrumService?.isConnected == false) {
// Reset medtrum pump state, we will pickup pomp state on connect
medtrumPump.pumpState = MedtrumPumpState.NONE
medtrumService?.connect("RetryActivationConnect")
} else {
aapsLogger.error(LTag.PUMP, "retryActivationConnect: Already connected when trying to prepare patch")
updateSetupStep(SetupStep.ERROR)
}
}
}
private fun prepareStep(newStep: PatchStep): PatchStep {
@ -273,6 +291,9 @@ class MedtrumViewModel @Inject constructor(
PatchStep.START_DEACTIVATION -> R.string.step_deactivate
PatchStep.DEACTIVATE -> R.string.step_deactivating
PatchStep.DEACTIVATION_COMPLETE -> R.string.step_deactivate_complete
PatchStep.RETRY_ACTIVATION,
PatchStep.RETRY_ACTIVATION_CONNECT -> R.string.step_retry_activation
PatchStep.COMPLETE,
PatchStep.FORCE_DEACTIVATION,
PatchStep.ERROR,
@ -291,7 +312,7 @@ class MedtrumViewModel @Inject constructor(
return newStep
}
enum class SetupStep { INITIAL, FILLED, PRIMED, ACTIVATED, ERROR, START_DEACTIVATION, STOPPED
enum class SetupStep { INITIAL, FILLED, PRIMING, PRIMED, ACTIVATED, ERROR, START_DEACTIVATION, STOPPED
}
val setupStep = MutableLiveData<SetupStep>()

View file

@ -63,12 +63,11 @@
<Button
android:id="@+id/btn_positive"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/ok"
android:text="@string/ok"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/parent"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.COMPLETE)}" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="info.nightscout.pump.medtrum.code.PatchStep" />
<variable
name="viewModel"
type="info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:fillViewport="true"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.MedtrumActivity">
<TextView
android:id="@+id/text_activation_in_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="5dp"
android:gravity="center"
android:text="@string/activation_in_progress"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_press_retry_or_discard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:gravity="center"
android:text="@string/press_retry_or_discard"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_activation_in_progress" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/btn_negative"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/discard"
android:text="@string/discard"
app:layout_constraintEnd_toStartOf="@id/btn_positive"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_positive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:contentDescription="@string/next"
android:text="@string/next"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_negative"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.RETRY_ACTIVATION_CONNECT)}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="info.nightscout.pump.medtrum.code.PatchStep" />
<variable
name="viewModel"
type="info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:fillViewport="true"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.MedtrumActivity">
<TextView
android:id="@+id/text_reading_activation_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="5dp"
android:gravity="center"
android:text="@string/reading_activation_status"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/btn_negative"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/cancel"
android:text="@string/cancel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.CANCEL)}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

View file

@ -93,6 +93,7 @@
<string name ="step_deactivate">Deactivate Patch</string>
<string name ="step_deactivating">Deactivating...</string>
<string name ="step_deactivate_complete">Patch deactivated</string>
<string name ="step_retry_activation">Activation in progress</string>
<string name ="unexpected_state">Unexpected state: %1$s</string>
<string name="base_serial">Pump Base Serial: %1$X</string>
@ -122,6 +123,10 @@
<string name="remove_base_discard_patch">Remove pump base and dispose of used patch appropriately.</string>
<string name="press_next_or_OK">Press <b>OK</b> to return to main screen. Press <b>Next</b> to start activation of new patch.</string>
<string name="activation_in_progress">Oops! Something went wrong, it seems there is already an activation in progress.</string>
<string name="press_retry_or_discard">Press <b>Next</b> to resume the activation or <b>Discard</b> to reset the activation status.</string>
<string name="reading_activation_status">Please wait, reading activation status from pump.</string>
<!-- settings-->
<string name="sn_input_title">Serial Number</string>
<string name="sn_input_summary">Enter the serial number of your pump base.</string>