Merge pull request #7 from AAPS-Omnipod/omnipod_eros_dev_upstream_merge

Latest Omnipod updates + custom queue commands
This commit is contained in:
Milos Kozak 2020-10-02 09:40:45 +02:00 committed by GitHub
commit cbdee2b81b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
156 changed files with 2996 additions and 2614 deletions

View file

@ -247,6 +247,7 @@ dependencies {
implementation project(':danar') implementation project(':danar')
implementation project(':rileylink') implementation project(':rileylink')
implementation project(':medtronic') implementation project(':medtronic')
implementation project(':omnipod')
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.android.gms:play-services-wearable:17.0.0' implementation 'com.google.android.gms:play-services-wearable:17.0.0'
@ -267,6 +268,8 @@ dependencies {
implementation "androidx.activity:activity-ktx:${activityVersion}" implementation "androidx.activity:activity-ktx:${activityVersion}"
implementation "androidx.fragment:fragment:${fragmentVersion}" implementation "androidx.fragment:fragment:${fragmentVersion}"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation "io.reactivex.rxjava2:rxandroid:${rxandroid_version}" implementation "io.reactivex.rxjava2:rxandroid:${rxandroid_version}"
@ -282,8 +285,6 @@ dependencies {
implementation "com.jjoe64:graphview:4.0.1" implementation "com.jjoe64:graphview:4.0.1"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2" implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2"
implementation 'com.madgag.spongycastle:core:1.58.0.0' implementation 'com.madgag.spongycastle:core:1.58.0.0'
// Omnipod wizard
implementation(name: "com.atech-software.android.library.wizardpager-1.1.4", ext: "aar")
implementation("com.google.android:flexbox:0.3.0") { implementation("com.google.android:flexbox:0.3.0") {
exclude group: "com.android.support" exclude group: "com.android.support"
} }

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodModule
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -29,6 +30,7 @@ import javax.inject.Singleton
WizardModule::class, WizardModule::class,
RileyLinkModule::class, RileyLinkModule::class,
MedtronicModule::class, MedtronicModule::class,
OmnipodModule::class,
APSModule::class, APSModule::class,
PreferencesModule::class, PreferencesModule::class,
OverviewModule::class, OverviewModule::class,

View file

@ -26,4 +26,5 @@ abstract class CommandQueueModule {
@ContributesAndroidInjector abstract fun commandTempBasalAbsoluteInjector(): CommandTempBasalAbsolute @ContributesAndroidInjector abstract fun commandTempBasalAbsoluteInjector(): CommandTempBasalAbsolute
@ContributesAndroidInjector abstract fun commandTempBasalPercentInjector(): CommandTempBasalPercent @ContributesAndroidInjector abstract fun commandTempBasalPercentInjector(): CommandTempBasalPercent
@ContributesAndroidInjector abstract fun commandSetUserSettingsInjector(): CommandSetUserSettings @ContributesAndroidInjector abstract fun commandSetUserSettingsInjector(): CommandSetUserSettings
@ContributesAndroidInjector abstract fun commandCustomCommandInjector(): CommandCustomCommand
} }

View file

@ -36,6 +36,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightFragment import info.nightscout.androidaps.plugins.pump.insight.LocalInsightFragment
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment
import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic
import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodOverviewFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment
@ -67,6 +68,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment @ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment
@ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment @ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment
@ContributesAndroidInjector abstract fun contributesMedtronicFragment(): MedtronicFragment @ContributesAndroidInjector abstract fun contributesMedtronicFragment(): MedtronicFragment
@ContributesAndroidInjector abstract fun contributesOmnipodFragment(): OmnipodOverviewFragment
@ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment @ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment
@ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment @ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment
@ContributesAndroidInjector @ContributesAndroidInjector

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.Riley
import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService
import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService
import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService
import info.nightscout.androidaps.services.AlarmSoundService import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.DataService import info.nightscout.androidaps.services.DataService
import info.nightscout.androidaps.services.LocationService import info.nightscout.androidaps.services.LocationService
@ -29,4 +30,5 @@ abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesInsightConnectionService(): InsightConnectionService @ContributesAndroidInjector abstract fun contributesInsightConnectionService(): InsightConnectionService
@ContributesAndroidInjector abstract fun contributesRileyLinkService(): RileyLinkService @ContributesAndroidInjector abstract fun contributesRileyLinkService(): RileyLinkService
@ContributesAndroidInjector abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService @ContributesAndroidInjector abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService
@ContributesAndroidInjector abstract fun contributesRileyLinkOmnipodService(): RileyLinkOmnipodService
} }

View file

@ -29,6 +29,7 @@ import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TDD; import info.nightscout.androidaps.db.TDD;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventInitializationChanged;
import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.CommandQueueProvider;
@ -36,6 +37,7 @@ import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface;
import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.PumpPluginBase; import info.nightscout.androidaps.interfaces.PumpPluginBase;
@ -43,9 +45,9 @@ 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;
import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
@ -64,7 +66,6 @@ import info.nightscout.androidaps.plugins.pump.combo.ruffyscripter.history.PumpH
import info.nightscout.androidaps.plugins.pump.combo.ruffyscripter.history.PumpHistoryRequest; import info.nightscout.androidaps.plugins.pump.combo.ruffyscripter.history.PumpHistoryRequest;
import info.nightscout.androidaps.plugins.pump.combo.ruffyscripter.history.Tdd; import info.nightscout.androidaps.plugins.pump.combo.ruffyscripter.history.Tdd;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.InstanceId; import info.nightscout.androidaps.utils.InstanceId;
@ -1395,7 +1396,10 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
@Override @Override
public void executeCustomAction(CustomActionType customActionType) { public void executeCustomAction(CustomActionType customActionType) {
}
@Nullable @Override public PumpEnactResult executeCustomCommand(CustomCommand customCommand) {
return null;
} }
@Override @Override

View file

@ -12,6 +12,7 @@ import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -56,6 +57,7 @@ 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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
@ -1171,7 +1173,10 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface,
@Override @Override
public void executeCustomAction(CustomActionType customActionType) { public void executeCustomAction(CustomActionType customActionType) {
}
@Nullable @Override public PumpEnactResult executeCustomCommand(CustomCommand customCommand) {
return null;
} }
private void readHistory() { private void readHistory() {

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.plugins.pump.mdi; package info.nightscout.androidaps.plugins.pump.mdi;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -27,6 +28,7 @@ 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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
@ -281,7 +283,10 @@ public class MDIPlugin extends PumpPluginBase implements PumpInterface {
@Override @Override
public void executeCustomAction(CustomActionType customActionType) { public void executeCustomAction(CustomActionType customActionType) {
}
@Nullable @Override public PumpEnactResult executeCustomCommand(CustomCommand customCommand) {
return null;
} }
@Override @Override

View file

@ -16,9 +16,9 @@ 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
import info.nightscout.androidaps.plugins.common.ManufacturerType import info.nightscout.androidaps.plugins.common.ManufacturerType
import info.nightscout.androidaps.interfaces.ProfileFunction
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.queue.commands.CustomCommand
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
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
@ -133,6 +133,11 @@ class VirtualPumpPlugin @Inject constructor(
} }
override fun executeCustomAction(customActionType: CustomActionType) {} override fun executeCustomAction(customActionType: CustomActionType) {}
override fun executeCustomCommand(customCommand: CustomCommand?): PumpEnactResult? {
return null
}
override fun isInitialized(): Boolean { override fun isInitialized(): Boolean {
return true return true
} }

View file

@ -20,16 +20,16 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
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
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.queue.commands.CustomCommand
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.queue.commands.* import info.nightscout.androidaps.queue.commands.*
import info.nightscout.androidaps.queue.commands.Command.CommandType import info.nightscout.androidaps.queue.commands.Command.CommandType
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -485,6 +485,55 @@ class CommandQueue @Inject constructor(
return true return true
} }
override fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean {
if (isCustomCommandInQueue(customCommand.javaClass)) {
callback?.result(executingNowError())?.run()
return false
}
// remove all unfinished
removeAllCustomCommands(customCommand.javaClass)
// add new command to queue
add(CommandCustomCommand(injector, customCommand, callback))
notifyAboutNewCommand()
return true
}
@Synchronized
override fun isCustomCommandInQueue(customCommandType: Class<out CustomCommand>): Boolean {
if(isCustomCommandRunning(customCommandType)) {
return true
}
synchronized(queue) {
for (i in queue.indices) {
val command = queue[i]
if (command is CommandCustomCommand && customCommandType.isInstance(command.customCommand)) {
return true
}
}
}
return false
}
override fun isCustomCommandRunning(customCommandType: Class<out CustomCommand>): Boolean {
val performing = this.performing
if (performing is CommandCustomCommand && customCommandType.isInstance(performing.customCommand)) {
return true
}
return false
}
@Synchronized
private fun removeAllCustomCommands(targetType: Class<out CustomCommand>) {
synchronized(queue) {
for (i in queue.indices.reversed()) {
val command = queue[i]
if (command is CustomCommand && targetType.isInstance(command.commandType)) {
queue.removeAt(i)
}
}
}
}
override fun spannedStatus(): Spanned { override fun spannedStatus(): Spanned {
var s = "" var s = ""
var line = 0 var line = 0

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.queue.Callback
import javax.inject.Inject
class CommandCustomCommand(
injector: HasAndroidInjector,
val customCommand: CustomCommand,
callback: Callback?
) : Command(injector, CommandType.CUSTOM_COMMAND, callback) {
@Inject lateinit var activePlugin: ActivePluginProvider
override fun execute() {
val result = activePlugin.activePump.executeCustomCommand(customCommand)
aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${result?.success} enacted: ${result?.enacted}")
callback?.result(result)?.run()
}
override fun status(): String {
return customCommand.statusDescription
}
}

View file

@ -2,8 +2,6 @@ package info.nightscout.androidaps.queue.commands
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import javax.inject.Inject import javax.inject.Inject
@ -14,17 +12,12 @@ class CommandStartPump(
) : Command(injector, CommandType.START_PUMP, callback) { ) : Command(injector, CommandType.START_PUMP, callback) {
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var profileFunction: ProfileFunction
override fun execute() { override fun execute() {
val pump = activePlugin.activePump val pump = activePlugin.activePump
if (pump is LocalInsightPlugin) { if (pump is LocalInsightPlugin) {
val result = pump.startPump() val result = pump.startPump()
callback?.result(result)?.run() callback?.result(result)?.run()
} else if (pump.pumpDescription.pumpType == PumpType.Insulet_Omnipod) {
// When using CommandQueue.setProfile, it refuses to set the profile is the same as the current profile
// However we need to set the current profile to resume delivery in case the Pod is suspended
pump.setNewBasalProfile(profileFunction.getProfile())
} }
} }

View file

@ -16,10 +16,10 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
@ -30,12 +30,13 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin
import info.nightscout.androidaps.setupwizard.elements.* import info.nightscout.androidaps.setupwizard.elements.*
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.AndroidPermission import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.CryptoUtil import info.nightscout.androidaps.utils.CryptoUtil
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.extensions.isRunningTest import info.nightscout.androidaps.utils.extensions.isRunningTest
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -313,11 +314,37 @@ class SWDefinition @Inject constructor(
}, null) }, null)
}) })
.visibility(SWValidator { (activePlugin.activePump as PluginBase).preferencesId > 0 })) .visibility(SWValidator { (activePlugin.activePump as PluginBase).preferencesId > 0 }))
.add(SWInfotext(injector)
.label(R.string.setupwizard_pump_pump_not_initialized)
.visibility(SWValidator { !isPumpInitialized() }))
.add( // Omnipod only
SWInfotext(injector)
.label(R.string.setupwizard_pump_waiting_for_riley_link_connection)
.visibility(SWValidator {
val activePump = activePlugin.activePump
activePump is OmnipodPumpPlugin && !activePump.isRileyLinkReady
}))
.add(SWButton(injector) .add(SWButton(injector)
.text(R.string.readstatus) .text(R.string.readstatus)
.action(Runnable { commandQueue.readStatus("Clicked connect to pump", null) })) .action(Runnable { commandQueue.readStatus("Clicked connect to pump", null) })
.visibility(SWValidator {
// Hide for Omnipod, because as we don't require a Pod to be paired in the setup wizard,
// Getting the status might not be possible
activePlugin.activePump !is OmnipodPumpPlugin
}))
.add(SWEventListener(injector, EventPumpStatusChanged::class.java)) .add(SWEventListener(injector, EventPumpStatusChanged::class.java))
.validator(SWValidator { activePlugin.activePump.isInitialized }) .validator(SWValidator {
isPumpInitialized()
})
private fun isPumpInitialized(): Boolean {
val activePump = activePlugin.activePump
// For Omnipod, consider the pump initialized when a RL has been configured successfully
// Users will be prompted to activate a Pod after completing the setup wizard.
return activePump.isInitialized || (activePump is OmnipodPumpPlugin && activePump.isRileyLinkReady)
}
private val screenAps = SWScreen(injector, R.string.configbuilder_aps) private val screenAps = SWScreen(injector, R.string.configbuilder_aps)
.skippable(false) .skippable(false)
.add(SWInfotext(injector) .add(SWInfotext(injector)

View file

@ -16,13 +16,14 @@ import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.setupwizard.elements.SWItem import info.nightscout.androidaps.setupwizard.elements.SWItem
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.AndroidPermission import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.locale.LocaleHelper.update
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -81,6 +82,11 @@ class SetupWizardActivity : NoSplashAppCompatActivity() {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateButtons() }) { fabricPrivacy.logException(it) } .subscribe({ updateButtons() }) { fabricPrivacy.logException(it) }
) )
disposable.add(rxBus
.toObservable(EventRileyLinkDeviceStatusChange::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateButtons() }) { fabricPrivacy.logException(it) }
)
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventNSClientStatus::class.java) .toObservable(EventNSClientStatus::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -104,6 +110,7 @@ class SetupWizardActivity : NoSplashAppCompatActivity() {
updateButtons() updateButtons()
}) { fabricPrivacy.logException(it) } }) { fabricPrivacy.logException(it) }
) )
updateButtons()
} }
private fun generateLayout() { private fun generateLayout() {

View file

@ -951,6 +951,8 @@
<string name="bgsourcesetup">Configure BG source</string> <string name="bgsourcesetup">Configure BG source</string>
<string name="setupwizard_profile_description">Please select source of profile. If patient is a child you should use NS profile. If there is nobody following you on Nightscout you will probably prefer Local profile. Please remember that you are only selecting the profile source. To use it you must activate it by executing \"Profile switch\"</string> <string name="setupwizard_profile_description">Please select source of profile. If patient is a child you should use NS profile. If there is nobody following you on Nightscout you will probably prefer Local profile. Please remember that you are only selecting the profile source. To use it you must activate it by executing \"Profile switch\"</string>
<string name="setupwizard_aps_description">Select one from availables algorithms. They are sorted from oldest to newest. Newer algorithm is usually more powerful and more aggressive. Thus if you are new looper you may probably start with AMA and not with latest one. Do not forget to read the OpenAPS documentation and configure it before use.</string> <string name="setupwizard_aps_description">Select one from availables algorithms. They are sorted from oldest to newest. Newer algorithm is usually more powerful and more aggressive. Thus if you are new looper you may probably start with AMA and not with latest one. Do not forget to read the OpenAPS documentation and configure it before use.</string>
<string name="setupwizard_pump_waiting_for_riley_link_connection">Waiting for RileyLink connection…\n</string>
<string name="setupwizard_pump_pump_not_initialized"><b>Note:</b> You can continue setup once the pump has been initialized.\n</string>
<string name="startobjective">Start your first objective</string> <string name="startobjective">Start your first objective</string>
<string name="permission">Permission</string> <string name="permission">Permission</string>
<string name="askforpermission">Ask for permission</string> <string name="askforpermission">Ask for permission</string>

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PumpDescription import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.commands.CustomCommand
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.Command
@ -192,4 +193,92 @@ class CommandQueueTest : TestBaseWithProfile() {
Assert.assertFalse(queued) Assert.assertFalse(queued)
Assert.assertEquals(commandQueue.size(), 0) Assert.assertEquals(commandQueue.size(), 0)
} }
@Test
fun isCustomCommandRunning() {
// given
Assert.assertEquals(0, commandQueue.size())
// when
val queued1 = commandQueue.customCommand(CustomCommand1(), null)
val queued2 = commandQueue.customCommand(CustomCommand2(), null)
commandQueue.pickup()
// then
Assert.assertTrue(queued1)
Assert.assertTrue(queued2)
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand1::class.java))
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand2::class.java))
Assert.assertFalse(commandQueue.isCustomCommandInQueue(CustomCommand3::class.java))
Assert.assertTrue(commandQueue.isCustomCommandRunning(CustomCommand1::class.java))
Assert.assertFalse(commandQueue.isCustomCommandRunning(CustomCommand2::class.java))
Assert.assertFalse(commandQueue.isCustomCommandRunning(CustomCommand3::class.java))
Assert.assertEquals(1, commandQueue.size())
}
@Test
fun isCustomCommandInQueue() {
// given
Assert.assertEquals(0, commandQueue.size())
// when
val queued1 = commandQueue.customCommand(CustomCommand1(), null)
val queued2 = commandQueue.customCommand(CustomCommand2(), null)
// then
Assert.assertTrue(queued1)
Assert.assertTrue(queued2)
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand1::class.java))
Assert.assertTrue(commandQueue.isCustomCommandInQueue(CustomCommand2::class.java))
Assert.assertFalse(commandQueue.isCustomCommandInQueue(CustomCommand3::class.java))
Assert.assertEquals(2, commandQueue.size())
}
@Test
fun differentCustomCommandsAllowed() {
// given
Assert.assertEquals(0, commandQueue.size())
// when
val queued1 = commandQueue.customCommand(CustomCommand1(), null)
val queued2 = commandQueue.customCommand(CustomCommand2(), null)
// then
Assert.assertTrue(queued1)
Assert.assertTrue(queued2)
Assert.assertEquals(2, commandQueue.size())
}
@Test
fun sameCustomCommandNotAllowed() {
// given
Assert.assertEquals(0, commandQueue.size())
// when
val queued1 = commandQueue.customCommand(CustomCommand1(), null)
val queued2 = commandQueue.customCommand(CustomCommand1(), null)
// then
Assert.assertTrue(queued1)
Assert.assertFalse(queued2)
Assert.assertEquals(1, commandQueue.size())
}
private class CustomCommand1 : CustomCommand {
override val statusDescription: String
get() = "CUSTOM COMMAND 1"
}
private class CustomCommand2 : CustomCommand {
override val statusDescription: String
get() = "CUSTOM COMMAND 2"
}
private class CustomCommand3 : CustomCommand {
override val statusDescription: String
get() = "CUSTOM COMMAND 3"
}
} }

View file

@ -13,7 +13,8 @@ buildscript {
coroutinesVersion = '1.3.7' coroutinesVersion = '1.3.7'
activityVersion = '1.2.0-alpha06' activityVersion = '1.2.0-alpha06'
fragmentVersion = '1.3.0-alpha07' fragmentVersion = '1.3.0-alpha07'
ormLiteVersion = "4.46" ormLiteVersion = '4.46'
nav_version = '2.3.0'
} }
repositories { repositories {
google() google()

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.interfaces
import android.text.Spanned import android.text.Spanned
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.queue.commands.CustomCommand
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.Command
@ -33,6 +34,9 @@ interface CommandQueueProvider {
fun setUserOptions(callback: Callback?): Boolean fun setUserOptions(callback: Callback?): Boolean
fun loadTDDs(callback: Callback?): Boolean fun loadTDDs(callback: Callback?): Boolean
fun loadEvents(callback: Callback?): Boolean fun loadEvents(callback: Callback?): Boolean
fun customCommand(customCommand: CustomCommand, callback: Callback?): Boolean
fun isCustomCommandRunning(customCommandType: Class<out CustomCommand>): Boolean
fun isCustomCommandInQueue(customCommandType: Class<out CustomCommand>): Boolean
fun spannedStatus(): Spanned fun spannedStatus(): Spanned
fun isThisProfileSet(profile: Profile): Boolean fun isThisProfileSet(profile: Profile): Boolean
} }

View file

@ -1,5 +1,7 @@
package info.nightscout.androidaps.interfaces; package info.nightscout.androidaps.interfaces;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.json.JSONObject; import org.json.JSONObject;
@ -11,7 +13,9 @@ import info.nightscout.androidaps.data.PumpEnactResult;
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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.TimeChangeType; import info.nightscout.androidaps.utils.TimeChangeType;
/** /**
@ -105,10 +109,32 @@ public interface PumpInterface {
boolean canHandleDST(); boolean canHandleDST();
/**
* Provides a list of custom actions to be displayed in the Actions tab.
* Plese note that these actions will not be queued upon execution
*
* @return list of custom actions
*/
@Nullable
List<CustomAction> getCustomActions(); List<CustomAction> getCustomActions();
/**
* Executes a custom action. Please note that these actions will not be queued
*
* @param customActionType action to be executed
*/
void executeCustomAction(CustomActionType customActionType); void executeCustomAction(CustomActionType customActionType);
/**
* Executes a custom queued command
* See {@link CommandQueueProvider#customCommand(CustomCommand, Callback)} for queuing a custom command.
*
* @param customCommand the custom command to be executed
* @return PumpEnactResult that represents the command execution result
*/
@Nullable
PumpEnactResult executeCustomCommand(CustomCommand customCommand);
/** /**
* This method will be called when time or Timezone changes, and pump driver can then do a specific action (for * This method will be called when time or Timezone changes, and pump driver can then do a specific action (for
* example update clock on pump). * example update clock on pump).

View file

@ -31,7 +31,8 @@ abstract class Command(
SET_USER_SETTINGS, // so far only Dana specific, SET_USER_SETTINGS, // so far only Dana specific,
START_PUMP, START_PUMP,
STOP_PUMP, STOP_PUMP,
INSIGHT_SET_TBR_OVER_ALARM // insight only INSIGHT_SET_TBR_OVER_ALARM, // insight only
CUSTOM_COMMAND
} }
init { init {

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.queue.commands
import java.io.Serializable
/**
* Implement this interface for every custom pump command that you want to be able to queue
* See [info.nightscout.androidaps.interfaces.CommandQueueProvider.customCommand] for queuing a custom command.
*/
interface CustomCommand : Serializable {
/**
* @return short description of this command to be used in [info.nightscout.androidaps.queue.commands.Command.status]
* The description is typically all caps.
*/
val statusDescription: String
}

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.utils package info.nightscout.androidaps.utils
import android.os.Build
import android.text.Html import android.text.Html
import android.text.Spanned import android.text.Spanned

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.danar; package info.nightscout.androidaps.danar;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -9,9 +10,9 @@ import java.util.Date;
import java.util.List; import java.util.List;
import dagger.android.HasAndroidInjector; import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.dana.DanaPumpInterface;
import info.nightscout.androidaps.dana.DanaFragment; import info.nightscout.androidaps.dana.DanaFragment;
import info.nightscout.androidaps.dana.DanaPump; import info.nightscout.androidaps.dana.DanaPump;
import info.nightscout.androidaps.dana.DanaPumpInterface;
import info.nightscout.androidaps.dana.comm.RecordTypes; import info.nightscout.androidaps.dana.comm.RecordTypes;
import info.nightscout.androidaps.danar.services.AbstractDanaRExecutionService; import info.nightscout.androidaps.danar.services.AbstractDanaRExecutionService;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
@ -37,6 +38,7 @@ import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
@ -511,7 +513,10 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
@Override @Override
public void executeCustomAction(CustomActionType customActionType) { public void executeCustomAction(CustomActionType customActionType) {
}
@Nullable @Override public PumpEnactResult executeCustomCommand(CustomCommand customCommand) {
return null;
} }
@Override @Override

View file

@ -26,6 +26,7 @@ import info.nightscout.androidaps.plugins.common.ManufacturerType
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
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.queue.commands.CustomCommand
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
@ -658,6 +659,7 @@ class DanaRSPlugin @Inject constructor(
override fun loadTDDs(): PumpEnactResult = loadHistory(info.nightscout.androidaps.dana.comm.RecordTypes.RECORD_TYPE_DAILY) override fun loadTDDs(): PumpEnactResult = loadHistory(info.nightscout.androidaps.dana.comm.RecordTypes.RECORD_TYPE_DAILY)
override fun getCustomActions(): List<CustomAction>? = null override fun getCustomActions(): List<CustomAction>? = null
override fun executeCustomAction(customActionType: CustomActionType) {} override fun executeCustomAction(customActionType: CustomActionType) {}
override fun executeCustomCommand(customCommand: CustomCommand?): PumpEnactResult? = null
override fun canHandleDST(): Boolean = false override fun canHandleDST(): Boolean = false
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType?) {} override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType?) {}
override fun clearPairing() { override fun clearPairing() {

View file

@ -48,6 +48,7 @@ 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.queue.commands.CustomCommand;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
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.PumpPluginAbstract; import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract;
@ -1583,6 +1584,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
} }
@Nullable @Override public PumpEnactResult executeCustomCommand(CustomCommand customCommand) {
return null;
}
@Override @Override
public void timezoneOrDSTChanged(TimeChangeType changeType) { public void timezoneOrDSTChanged(TimeChangeType changeType) {

View file

@ -13,13 +13,11 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType;
@ -56,7 +54,7 @@ import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
* functionality added. * functionality added.
*/ */
@Singleton @Singleton
public class MedtronicCommunicationManager extends RileyLinkCommunicationManager { public class MedtronicCommunicationManager extends RileyLinkCommunicationManager<PumpMessage> {
@Inject MedtronicPumpStatus medtronicPumpStatus; @Inject MedtronicPumpStatus medtronicPumpStatus;
@Inject MedtronicPumpPlugin medtronicPumpPlugin; @Inject MedtronicPumpPlugin medtronicPumpPlugin;
@ -75,7 +73,8 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
// This empty constructor must be kept, otherwise dagger injection might break! // This empty constructor must be kept, otherwise dagger injection might break!
@Inject @Inject
public MedtronicCommunicationManager() {} public MedtronicCommunicationManager() {
}
@Inject @Inject
public void onInit() { public void onInit() {
@ -85,9 +84,8 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
} }
@Override @Override
public RLMessage createResponseMessage(byte[] payload) { public PumpMessage createResponseMessage(byte[] payload) {
PumpMessage pumpMessage = new PumpMessage(aapsLogger, payload); return new PumpMessage(aapsLogger, payload);
return pumpMessage;
} }
@Override @Override
@ -170,7 +168,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
if (radioResponse.isValid()) { if (radioResponse.isValid()) {
PumpMessage pumpResponse = (PumpMessage) createResponseMessage(radioResponse.getPayload()); PumpMessage pumpResponse = createResponseMessage(radioResponse.getPayload());
if (!pumpResponse.isValid()) { if (!pumpResponse.isValid()) {
aapsLogger.warn(LTag.PUMPCOMM, "Response is invalid ! [interrupted={}, timeout={}]", rfSpyResponse.wasInterrupted(), aapsLogger.warn(LTag.PUMPCOMM, "Response is invalid ! [interrupted={}, timeout={}]", rfSpyResponse.wasInterrupted(),
@ -545,14 +543,15 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
} }
private PumpMessage sendAndListen(RLMessage msg) throws RileyLinkCommunicationException { private PumpMessage sendAndListen(PumpMessage msg) throws RileyLinkCommunicationException {
return sendAndListen(msg, 4000); // 2000 return sendAndListen(msg, 4000); // 2000
} }
// All pump communications go through this function. // All pump communications go through this function.
protected PumpMessage sendAndListen(RLMessage msg, int timeout_ms) throws RileyLinkCommunicationException { @Override
return (PumpMessage) super.sendAndListen(msg, timeout_ms); protected PumpMessage sendAndListen(PumpMessage msg, int timeout_ms) throws RileyLinkCommunicationException {
return super.sendAndListen(msg, timeout_ms);
} }

View file

@ -75,6 +75,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.core:core-ktx:1.2.0'
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.activity:activity-ktx:${activityVersion}" implementation "androidx.activity:activity-ktx:${activityVersion}"
implementation "androidx.fragment:fragment:${fragmentVersion}"
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation 'com.google.firebase:firebase-analytics-ktx:17.4.3' implementation 'com.google.firebase:firebase-analytics-ktx:17.4.3'
@ -86,12 +88,9 @@ dependencies {
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2" implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2"
// Omnipod - start
implementation 'org.apache.commons:commons-lang3:3.10' implementation 'org.apache.commons:commons-lang3:3.10'
implementation 'net.danlew:android.joda:2.10.6' implementation 'net.danlew:android.joda:2.10.6'
implementation "com.google.code.gson:gson:2.8.6" implementation "com.google.code.gson:gson:2.8.6"
implementation(name: "com.atech-software.android.library.wizardpager-1.1.4", ext: "aar")
// Omnipod - end
implementation "com.google.dagger:dagger-android:$dagger_version" implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version" implementation "com.google.dagger:dagger-android-support:$dagger_version"
@ -100,6 +99,12 @@ dependencies {
kapt "com.google.dagger:dagger-android-processor:$dagger_version" kapt "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version" kapt "com.google.dagger:dagger-compiler:$dagger_version"
// Navigation
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation 'com.google.android.material:material:1.1.0'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
testImplementation "org.mockito:mockito-core:2.8.47" testImplementation "org.mockito:mockito-core:2.8.47"
testImplementation "org.powermock:powermock-api-mockito2:$powermockVersion" testImplementation "org.powermock:powermock-api-mockito2:$powermockVersion"

View file

@ -9,8 +9,7 @@
<activity android:name=".ui.PodManagementActivity" /> <activity android:name=".ui.PodManagementActivity" />
<activity android:name=".ui.PodHistoryActivity" /> <activity android:name=".ui.PodHistoryActivity" />
<activity <activity android:name=".ui.wizard.activation.PodActivationWizardActivity" />
android:name="com.atech.android.library.wizardpager.WizardPagerActivity" <activity android:name=".ui.wizard.deactivation.PodDeactivationWizardActivity" />
android:theme="@style/AppTheme.NoActionBar" />
</application> </application>
</manifest> </manifest>

View file

@ -12,15 +12,13 @@ import android.os.SystemClock;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Duration; import org.joda.time.Duration;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -44,6 +42,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.PumpPluginBase; import info.nightscout.androidaps.interfaces.PumpPluginBase;
@ -53,7 +52,6 @@ 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.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
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.data.TempBasalPair; import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
@ -71,7 +69,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.data.ActiveBolus;
import info.nightscout.androidaps.plugins.pump.omnipod.data.RLHistoryItemOmnipod; import info.nightscout.androidaps.plugins.pump.omnipod.data.RLHistoryItemOmnipod;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCommandType; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCustomActionType; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCustomActionType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStatusRequestType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog;
@ -81,10 +78,15 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateMa
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandHandleTimeChange;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandUpdateAlertConfiguration;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.OmnipodCustomCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.OmnipodCustomCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService;
import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodFragment; import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodOverviewFragment;
import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil; import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil; import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil;
import info.nightscout.androidaps.queue.commands.CustomCommand;
import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.FabricPrivacy;
@ -104,6 +106,7 @@ import io.reactivex.schedulers.Schedulers;
public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, RileyLinkPumpDevice { public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface, RileyLinkPumpDevice {
private static final long RILEY_LINK_CONNECT_TIMEOUT_MILLIS = 3 * 60 * 1000L; // 3 minutes private static final long RILEY_LINK_CONNECT_TIMEOUT_MILLIS = 3 * 60 * 1000L; // 3 minutes
private static final long STATUS_CHECK_INTERVAL_MILLIS = 60 * 1000L; // 1 minute private static final long STATUS_CHECK_INTERVAL_MILLIS = 60 * 1000L; // 1 minute
public static final int STARTUP_STATUS_REQUEST_TRIES = 2;
private final PodStateManager podStateManager; private final PodStateManager podStateManager;
private final RileyLinkServiceData rileyLinkServiceData; private final RileyLinkServiceData rileyLinkServiceData;
@ -112,6 +115,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private final AapsOmnipodUtil aapsOmnipodUtil; private final AapsOmnipodUtil aapsOmnipodUtil;
private final RileyLinkUtil rileyLinkUtil; private final RileyLinkUtil rileyLinkUtil;
private final OmnipodAlertUtil omnipodAlertUtil; private final OmnipodAlertUtil omnipodAlertUtil;
private final ProfileFunction profileFunction;
private final AAPSLogger aapsLogger; private final AAPSLogger aapsLogger;
private final RxBusWrapper rxBus; private final RxBusWrapper rxBus;
private final ActivePluginProvider activePlugin; private final ActivePluginProvider activePlugin;
@ -124,8 +128,8 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private final ServiceConnection serviceConnection; private final ServiceConnection serviceConnection;
private final PumpType pumpType = PumpType.Insulet_Omnipod; private final PumpType pumpType = PumpType.Insulet_Omnipod;
private final List<CustomAction> customActions = new ArrayList<>(); private final List<CustomAction> customActions = Collections.singletonList(new CustomAction(
private final List<OmnipodStatusRequestType> statusRequestList = new ArrayList<>(); R.string.omnipod_custom_action_reset_rileylink, OmnipodCustomActionType.RESET_RILEY_LINK_CONFIGURATION, true));
private final CompositeDisposable disposables = new CompositeDisposable(); private final CompositeDisposable disposables = new CompositeDisposable();
// variables for handling statuses and history // variables for handling statuses and history
@ -136,12 +140,11 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private boolean busy = false; private boolean busy = false;
private int timeChangeRetries; private int timeChangeRetries;
private long nextPodCheck; private long nextPodCheck;
private boolean sentIdToFirebase;
private long lastConnectionTimeMillis; private long lastConnectionTimeMillis;
private final Handler loopHandler = new Handler(Looper.getMainLooper()); private final Handler loopHandler = new Handler(Looper.getMainLooper());
private final Runnable statusChecker; private final Runnable statusChecker;
private OmnipodCommandType currentCommand; private boolean isCancelTempBasalRunning;
@Inject @Inject
public OmnipodPumpPlugin( public OmnipodPumpPlugin(
@ -161,15 +164,16 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
DateUtil dateUtil, DateUtil dateUtil,
AapsOmnipodUtil aapsOmnipodUtil, AapsOmnipodUtil aapsOmnipodUtil,
RileyLinkUtil rileyLinkUtil, RileyLinkUtil rileyLinkUtil,
OmnipodAlertUtil omnipodAlertUtil OmnipodAlertUtil omnipodAlertUtil,
ProfileFunction profileFunction
) { ) {
super(new PluginDescription() // super(new PluginDescription() //
.mainType(PluginType.PUMP) // .mainType(PluginType.PUMP) //
.fragmentClass(OmnipodFragment.class.getName()) // .fragmentClass(OmnipodOverviewFragment.class.getName()) //
.pluginName(R.string.omnipod_name) // .pluginName(R.string.omnipod_name) //
.shortName(R.string.omnipod_name_short) // .shortName(R.string.omnipod_name_short) //
.preferencesId(R.xml.pref_omnipod) // .preferencesId(R.xml.pref_omnipod) //
.description(R.string.description_pump_omnipod), // .description(R.string.omnipod_pump_description), //
injector, aapsLogger, resourceHelper, commandQueue); injector, aapsLogger, resourceHelper, commandQueue);
this.aapsLogger = aapsLogger; this.aapsLogger = aapsLogger;
this.rxBus = rxBus; this.rxBus = rxBus;
@ -186,12 +190,10 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
this.aapsOmnipodUtil = aapsOmnipodUtil; this.aapsOmnipodUtil = aapsOmnipodUtil;
this.rileyLinkUtil = rileyLinkUtil; this.rileyLinkUtil = rileyLinkUtil;
this.omnipodAlertUtil = omnipodAlertUtil; this.omnipodAlertUtil = omnipodAlertUtil;
this.profileFunction = profileFunction;
pumpDescription = new PumpDescription(pumpType); pumpDescription = new PumpDescription(pumpType);
customActions.add(new CustomAction(
R.string.omnipod_custom_action_reset_rileylink, OmnipodCustomActionType.RESET_RILEY_LINK_CONFIGURATION, true));
this.serviceConnection = new ServiceConnection() { this.serviceConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
@ -228,14 +230,11 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
aapsOmnipodManager.createSuspendedFakeTbrIfNotExists(); aapsOmnipodManager.createSuspendedFakeTbrIfNotExists();
} }
if (!getCommandQueue().statusInQueue()) { if (OmnipodPumpPlugin.this.hasTimeDateOrTimeZoneChanged) {
if (!OmnipodPumpPlugin.this.statusRequestList.isEmpty()) { getCommandQueue().customCommand(new CommandHandleTimeChange(false), null);
getCommandQueue().readStatus("Status Refresh Requested", null);
} else if (OmnipodPumpPlugin.this.hasTimeDateOrTimeZoneChanged) {
getCommandQueue().readStatus("Date or Time Zone Changed", null);
} else if (!OmnipodPumpPlugin.this.verifyPodAlertConfiguration()) {
getCommandQueue().readStatus("Expiration Alerts Changed", null);
} }
if (!OmnipodPumpPlugin.this.verifyPodAlertConfiguration()) {
getCommandQueue().customCommand(new CommandUpdateAlertConfiguration(), null);
} }
doPodCheck(); doPodCheck();
@ -288,7 +287,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_enabled) || event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_enabled) ||
event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_units)) { event.isChanged(getResourceHelper(), R.string.key_omnipod_low_reservoir_alert_units)) {
if (!verifyPodAlertConfiguration() && !getCommandQueue().statusInQueue()) { if (!verifyPodAlertConfiguration() && !getCommandQueue().statusInQueue()) {
getCommandQueue().readStatus("Expiration Alerts Changed", null); getCommandQueue().customCommand(new CommandUpdateAlertConfiguration(), null);
} }
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
@ -316,13 +315,17 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
); );
} }
public boolean isRileyLinkReady() {
return rileyLinkServiceData.rileyLinkServiceState.isReady();
}
private void updateAapsTbr() { private void updateAapsTbr() {
// As per the characteristics of the Omnipod, we only know whether or not a TBR is currently active // As per the characteristics of the Omnipod, we only know whether or not a TBR is currently active
// But it doesn't tell us the duration or amount, so we can only update TBR status in AAPS if // But it doesn't tell us the duration or amount, so we can only update TBR status in AAPS if
// The pod is not running a TBR, while AAPS thinks it is // The pod is not running a TBR, while AAPS thinks it is
if (!podStateManager.isTempBasalRunning()) { if (!podStateManager.isTempBasalRunning()) {
// Only report TBR cancellations if they haven't been explicitly requested // Only report TBR cancellations if they haven't been explicitly requested
if (currentCommand != OmnipodCommandType.CANCEL_TEMPORARY_BASAL) { if (!isCancelTempBasalRunning) {
if (activePlugin.getActiveTreatments().isTempBasalInProgress() && !aapsOmnipodManager.hasSuspendedFakeTbr()) { if (activePlugin.getActiveTreatments().isTempBasalInProgress() && !aapsOmnipodManager.hasSuspendedFakeTbr()) {
aapsOmnipodManager.reportCancelledTbr(); aapsOmnipodManager.reportCancelledTbr();
} }
@ -348,13 +351,9 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
Notification notification = new Notification(Notification.OMNIPOD_POD_NOT_ATTACHED, resourceHelper.gs(R.string.omnipod_error_pod_not_attached), Notification.NORMAL); Notification notification = new Notification(Notification.OMNIPOD_POD_NOT_ATTACHED, resourceHelper.gs(R.string.omnipod_error_pod_not_attached), Notification.NORMAL);
rxBus.send(new EventNewNotification(notification)); rxBus.send(new EventNewNotification(notification));
} else { } else {
rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED));
if (podStateManager.isSuspended()) { if (podStateManager.isSuspended()) {
Notification notification = new Notification(Notification.OMNIPOD_POD_SUSPENDED, resourceHelper.gs(R.string.omnipod_error_pod_suspended), Notification.NORMAL); Notification notification = new Notification(Notification.OMNIPOD_POD_SUSPENDED, resourceHelper.gs(R.string.omnipod_confirmation_pod_suspended), Notification.NORMAL);
rxBus.send(new EventNewNotification(notification)); rxBus.send(new EventNewNotification(notification));
} else {
rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED));
} }
} }
@ -362,7 +361,6 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
} }
} }
// TODO is this correct?
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
return isConnected() && podStateManager.isPodActivationCompleted(); return isConnected() && podStateManager.isPodActivationCompleted();
@ -442,109 +440,23 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
lastConnectionTimeMillis = System.currentTimeMillis(); lastConnectionTimeMillis = System.currentTimeMillis();
} }
// We abuse getPumpStatus to squeeze commands in the queue /**
// The only actual status requests we send to the Pod are on startup (in initializeAfterRileyLinkConnection) * The only actual status requests we send to the Pod here are on startup (in {@link #initializeAfterRileyLinkConnection() initializeAfterRileyLinkConnection()})
// And when the user explicitly requested it by clicking the Refresh button on the Omnipod tab * And when the user explicitly requested it by clicking the Refresh button on the Omnipod tab (which is executed through {@link #executeCustomCommand(CustomCommand)})
// We don't do periodical status requests because that can drain the Pod's battery * We don't do periodical status requests because that could drain the Pod's battery
// However that should be fine because we get a StatusResponse from all insulin commands sent to the Pod */
@Override @Override
public void getPumpStatus() { public void getPumpStatus() {
if (firstRun) { if (firstRun) {
initializeAfterRileyLinkConnection(); initializeAfterRileyLinkConnection();
} else if (!statusRequestList.isEmpty()) { firstRun = false;
Iterator<OmnipodStatusRequestType> iterator = statusRequestList.iterator();
while (iterator.hasNext()) {
OmnipodStatusRequestType statusRequest = iterator.next();
switch (statusRequest) {
case GET_PULSE_LOG:
try {
PodInfoRecentPulseLog result = executeCommand(OmnipodCommandType.GET_POD_PULSE_LOG, aapsOmnipodManager::readPulseLog);
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", 0);
i.putExtra("status", "Pulse Log (copied to clipboard):\n" + result.toString());
i.putExtra("title", resourceHelper.gs(R.string.omnipod_warning));
i.putExtra("clipboardContent", result.toString());
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
} catch (Exception ex) {
aapsLogger.warn(LTag.PUMP, "Failed to retrieve pulse log", ex);
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", 0);
i.putExtra("status", "Failed to retrieve pulse log");
i.putExtra("title", resourceHelper.gs(R.string.omnipod_warning));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
break;
case ACKNOWLEDGE_ALERTS:
executeCommand(OmnipodCommandType.ACKNOWLEDGE_ALERTS, aapsOmnipodManager::acknowledgeAlerts);
break;
case GET_POD_STATE:
executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
break;
case SUSPEND_DELIVERY:
executeCommand(OmnipodCommandType.SUSPEND_DELIVERY, aapsOmnipodManager::suspendDelivery);
break;
default:
aapsLogger.error(LTag.PUMP, "Unknown status request: " + statusRequest.name());
}
iterator.remove();
}
} else if (this.hasTimeDateOrTimeZoneChanged) {
PumpEnactResult result = executeCommand(OmnipodCommandType.SET_TIME, aapsOmnipodManager::setTime);
if (result.success) {
this.hasTimeDateOrTimeZoneChanged = false;
timeChangeRetries = 0;
Notification notification = new Notification(
Notification.TIME_OR_TIMEZONE_CHANGE,
resourceHelper.gs(R.string.omnipod_time_or_timezone_change),
Notification.INFO, 60);
rxBus.send(new EventNewNotification(notification));
} else {
timeChangeRetries++;
if (timeChangeRetries > 3) {
this.hasTimeDateOrTimeZoneChanged = false;
timeChangeRetries = 0;
} }
} }
} else if (!verifyPodAlertConfiguration()) {
Duration expirationReminderTimeBeforeShutdown = omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown();
Integer lowReservoirAlertUnits = omnipodAlertUtil.getLowReservoirAlertUnits();
List<AlertConfiguration> alertConfigurations = new ExpirationReminderBuilder(podStateManager) // @NonNull
.expirationAdvisory(expirationReminderTimeBeforeShutdown != null,
Optional.ofNullable(expirationReminderTimeBeforeShutdown).orElse(Duration.ZERO)) //
.lowReservoir(lowReservoirAlertUnits != null, Optional.ofNullable(lowReservoirAlertUnits).orElse(0)) //
.build();
PumpEnactResult result = executeCommand(OmnipodCommandType.CONFIGURE_ALERTS, () -> aapsOmnipodManager.configureAlerts(alertConfigurations));
if (result.success) {
aapsLogger.info(LTag.PUMP, "Successfully configured alerts in Pod");
podStateManager.setExpirationAlertTimeBeforeShutdown(expirationReminderTimeBeforeShutdown);
podStateManager.setLowReservoirAlertUnits(lowReservoirAlertUnits);
Notification notification = new Notification(
Notification.OMNIPOD_POD_ALERTS_UPDATED,
resourceHelper.gs(R.string.omnipod_expiration_alerts_updated),
Notification.INFO, 60);
rxBus.send(new EventNewNotification(notification));
} else {
aapsLogger.warn(LTag.PUMP, "Failed to configure alerts in Pod");
}
}
}
@NotNull
@Override @Override
public PumpEnactResult setNewBasalProfile(Profile profile) { public PumpEnactResult setNewBasalProfile(Profile profile) {
PumpEnactResult result = executeCommand(OmnipodCommandType.SET_BASAL_PROFILE, () -> aapsOmnipodManager.setBasalProfile(profile)); PumpEnactResult result = executeCommand(OmnipodCommandType.SET_BASAL_PROFILE, () -> aapsOmnipodManager.setBasalProfile(profile, true));
aapsLogger.info(LTag.PUMP, "Basal Profile was set: " + result.success); aapsLogger.info(LTag.PUMP, "Basal Profile was set: " + result.success);
@ -563,7 +475,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
@Override @Override
public long lastDataTime() { public long lastDataTime() {
return podStateManager.isPodActivationCompleted() ? podStateManager.getLastSuccessfulCommunication().getMillis() : 0; return podStateManager.isPodInitialized() ? podStateManager.getLastSuccessfulCommunication().getMillis() : 0;
} }
@Override @Override
@ -618,7 +530,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
executeCommand(OmnipodCommandType.CANCEL_BOLUS, aapsOmnipodManager::cancelBolus); executeCommand(OmnipodCommandType.CANCEL_BOLUS, aapsOmnipodManager::cancelBolus);
} }
// if enforceNew===true current temp basal is cancelled and new TBR set (duration is prolonged), // if enforceNew is true, current temp basal is cancelled and new TBR set (duration is prolonged),
// if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed
@Override @Override
public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer
@ -660,7 +572,12 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
return new PumpEnactResult(getInjector()).success(true).enacted(false); return new PumpEnactResult(getInjector()).success(true).enacted(false);
} }
isCancelTempBasalRunning = true;
try {
return executeCommand(OmnipodCommandType.CANCEL_TEMPORARY_BASAL, aapsOmnipodManager::cancelTemporaryBasal); return executeCommand(OmnipodCommandType.CANCEL_TEMPORARY_BASAL, aapsOmnipodManager::cancelTemporaryBasal);
} finally {
isCancelTempBasalRunning = false;
}
} }
// TODO improve (i8n and more) // TODO improve (i8n and more)
@ -719,12 +636,12 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
return pumpType.getManufacturer(); return pumpType.getManufacturer();
} }
@Override @NotNull @Override @NonNull
public PumpType model() { public PumpType model() {
return pumpType; return pumpType;
} }
@NotNull @NonNull
@Override @Override
public String serialNumber() { public String serialNumber() {
return podStateManager.isPodInitialized() ? String.valueOf(podStateManager.getAddress()) : "-"; return podStateManager.isPodInitialized() ? String.valueOf(podStateManager.getAddress()) : "-";
@ -779,18 +696,145 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
break; break;
default: default:
aapsLogger.warn(LTag.PUMP, "Unknown custom action: {}" + mcat); aapsLogger.warn(LTag.PUMP, "Unknown custom action: " + mcat);
break; break;
} }
} }
@Override
public PumpEnactResult executeCustomCommand(CustomCommand command) {
if (!(command instanceof OmnipodCustomCommand)) {
aapsLogger.warn(LTag.PUMP, "Unknown custom command: " + command.getClass().getName());
return new PumpEnactResult(getInjector()).success(false).enacted(false).comment(resourceHelper.gs(R.string.omnipod_error_unknown_custom_command, command.getClass().getName()));
}
OmnipodCustomCommandType commandType = ((OmnipodCustomCommand) command).getType();
aapsLogger.debug(LTag.PUMP, "Executing custom command: " + commandType);
switch (commandType) {
case ACKNOWLEDGE_ALERTS:
return executeCommand(OmnipodCommandType.ACKNOWLEDGE_ALERTS, aapsOmnipodManager::acknowledgeAlerts);
case GET_POD_STATUS:
return executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
case READ_PULSE_LOG:
return retrievePulseLog();
case SUSPEND_DELIVERY:
return executeCommand(OmnipodCommandType.SUSPEND_DELIVERY, aapsOmnipodManager::suspendDelivery);
case RESUME_DELIVERY:
return executeCommand(OmnipodCommandType.RESUME_DELIVERY, () -> aapsOmnipodManager.setBasalProfile(profileFunction.getProfile(), false));
case DEACTIVATE_POD:
return executeCommand(OmnipodCommandType.DEACTIVATE_POD, aapsOmnipodManager::deactivatePod);
case HANDLE_TIME_CHANGE:
return handleTimeChange(((CommandHandleTimeChange) command).isRequestedByUser());
case UPDATE_ALERT_CONFIGURATION:
return updateAlertConfiguration();
default:
aapsLogger.warn(LTag.PUMP, "Unknown custom command: " + commandType);
return new PumpEnactResult(getInjector()).success(false).enacted(false).comment(resourceHelper.gs(R.string.omnipod_error_unknown_custom_command, commandType));
}
}
private PumpEnactResult retrievePulseLog() {
PodInfoRecentPulseLog result;
try {
result = executeCommand(OmnipodCommandType.READ_POD_PULSE_LOG, aapsOmnipodManager::readPulseLog);
} catch (Exception ex) {
return new PumpEnactResult(getInjector()).success(false).enacted(false).comment(aapsOmnipodManager.translateException(ex));
}
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", 0);
i.putExtra("status", resourceHelper.gs(R.string.omnipod_pulse_log_value) + ":\n" + result.toString());
i.putExtra("title", resourceHelper.gs(R.string.omnipod_pulse_log));
i.putExtra("clipboardContent", result.toString());
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
return new PumpEnactResult(getInjector()).success(true).enacted(false);
}
@NonNull private PumpEnactResult updateAlertConfiguration() {
Duration expirationReminderTimeBeforeShutdown = omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown();
Integer lowReservoirAlertUnits = omnipodAlertUtil.getLowReservoirAlertUnits();
List<AlertConfiguration> alertConfigurations = new ExpirationReminderBuilder(podStateManager) //
.expirationAdvisory(expirationReminderTimeBeforeShutdown != null,
Optional.ofNullable(expirationReminderTimeBeforeShutdown).orElse(Duration.ZERO)) //
.lowReservoir(lowReservoirAlertUnits != null, Optional.ofNullable(lowReservoirAlertUnits).orElse(0)) //
.build();
PumpEnactResult result = executeCommand(OmnipodCommandType.CONFIGURE_ALERTS, () -> aapsOmnipodManager.configureAlerts(alertConfigurations));
if (result.success) {
aapsLogger.info(LTag.PUMP, "Successfully configured alerts in Pod");
podStateManager.setExpirationAlertTimeBeforeShutdown(expirationReminderTimeBeforeShutdown);
podStateManager.setLowReservoirAlertUnits(lowReservoirAlertUnits);
Notification notification = new Notification(
Notification.OMNIPOD_POD_ALERTS_UPDATED,
resourceHelper.gs(R.string.omnipod_confirmation_expiration_alerts_updated),
Notification.INFO, 60);
rxBus.send(new EventNewNotification(notification));
} else {
aapsLogger.warn(LTag.PUMP, "Failed to configure alerts in Pod");
}
return result;
}
@NonNull private PumpEnactResult handleTimeChange(boolean requestedByUser) {
aapsLogger.debug(LTag.PUMP, "Setting time, requestedByUser={}", requestedByUser);
PumpEnactResult result;
if (requestedByUser || aapsOmnipodManager.isTimeChangeEventEnabled()) {
result = executeCommand(OmnipodCommandType.SET_TIME, () -> aapsOmnipodManager.setTime(!requestedByUser));
} else {
// Even if automatically changing the time is disabled, we still want to at least do a GetStatus request,
// in order to update the Pod's activation time, which we need for calculating the time on the Pod
result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
}
if (result.success) {
this.hasTimeDateOrTimeZoneChanged = false;
timeChangeRetries = 0;
if (!requestedByUser && aapsOmnipodManager.isTimeChangeEventEnabled()) {
Notification notification = new Notification(
Notification.TIME_OR_TIMEZONE_CHANGE,
resourceHelper.gs(R.string.omnipod_confirmation_time_on_pod_updated),
Notification.INFO, 60);
rxBus.send(new EventNewNotification(notification));
}
} else {
if (!requestedByUser) {
timeChangeRetries++;
if (timeChangeRetries > 3) {
if (aapsOmnipodManager.isTimeChangeEventEnabled()) {
Notification notification = new Notification(
Notification.TIME_OR_TIMEZONE_CHANGE,
resourceHelper.gs(R.string.omnipod_error_automatic_time_or_timezone_change_failed),
Notification.INFO, 60);
rxBus.send(new EventNewNotification(notification));
}
this.hasTimeDateOrTimeZoneChanged = false;
timeChangeRetries = 0;
}
}
}
return result;
}
@Override @Override
public void timezoneOrDSTChanged(TimeChangeType timeChangeType) { public void timezoneOrDSTChanged(TimeChangeType timeChangeType) {
aapsLogger.warn(LTag.PUMP, "Time, Date and/or TimeZone changed. [changeType=" + timeChangeType.name() + ", eventHandlingEnabled=" + aapsOmnipodManager.isTimeChangeEventEnabled() + "]"); aapsLogger.warn(LTag.PUMP, "Time, Date and/or TimeZone changed. [changeType=" + timeChangeType.name() + ", eventHandlingEnabled=" + aapsOmnipodManager.isTimeChangeEventEnabled() + "]");
if (aapsOmnipodManager.isTimeChangeEventEnabled() && podStateManager.isPodRunning()) { if (podStateManager.isPodRunning()) {
aapsLogger.info(LTag.PUMP, "Time, Date and/or TimeZone changed event received and will be consumed by driver."); aapsLogger.info(LTag.PUMP, "Time, Date and/or TimeZone changed event received and will be consumed by driver.");
this.hasTimeDateOrTimeZoneChanged = true; hasTimeDateOrTimeZoneChanged = true;
} }
} }
@ -819,10 +863,6 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
return false; return false;
} }
public void addPodStatusRequest(OmnipodStatusRequestType pumpStatusRequest) {
statusRequestList.add(pumpStatusRequest);
}
@Override @Override
public boolean isFakingTempsByExtendedBoluses() { public boolean isFakingTempsByExtendedBoluses() {
return false; return false;
@ -853,50 +893,47 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation."); aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation.");
} }
@NotNull @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer @NonNull @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer
durationInMinutes, Profile profile, boolean enforceNew) { durationInMinutes, Profile profile, boolean enforceNew) {
aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [OmnipodPumpPlugin] - Not implemented."); aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [OmnipodPumpPlugin] - Not implemented.");
return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver); return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver);
} }
@NotNull @Override public PumpEnactResult setExtendedBolus(Double insulin, Integer @NonNull @Override public PumpEnactResult setExtendedBolus(Double insulin, Integer
durationInMinutes) { durationInMinutes) {
aapsLogger.debug(LTag.PUMP, "setExtendedBolus [OmnipodPumpPlugin] - Not implemented."); aapsLogger.debug(LTag.PUMP, "setExtendedBolus [OmnipodPumpPlugin] - Not implemented.");
return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver); return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver);
} }
@NotNull @Override public PumpEnactResult cancelExtendedBolus() { @NonNull @Override public PumpEnactResult cancelExtendedBolus() {
aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [OmnipodPumpPlugin] - Not implemented."); aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [OmnipodPumpPlugin] - Not implemented.");
return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver); return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver);
} }
@NotNull @Override public PumpEnactResult loadTDDs() { @NonNull @Override public PumpEnactResult loadTDDs() {
aapsLogger.debug(LTag.PUMP, "loadTDDs [OmnipodPumpPlugin] - Not implemented."); aapsLogger.debug(LTag.PUMP, "loadTDDs [OmnipodPumpPlugin] - Not implemented.");
return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver); return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver);
} }
private void initializeAfterRileyLinkConnection() { private void initializeAfterRileyLinkConnection() {
if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.PAIRING_COMPLETED)) { if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.PAIRING_COMPLETED)) {
for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) {
PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus); PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
if (result.success) { if (result.success) {
aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup"); aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup");
break;
} else { } else {
aapsLogger.warn(LTag.PUMP, "Failed to retrieve Pod status on startup"); aapsLogger.warn(LTag.PUMP, "Failed to retrieve Pod status on startup");
} }
}
} else { } else {
aapsLogger.debug(LTag.PUMP, "Not retrieving Pod status on startup: no Pod running"); aapsLogger.debug(LTag.PUMP, "Not retrieving Pod status on startup: no Pod running");
} }
if (!sentIdToFirebase) {
Bundle params = new Bundle(); Bundle params = new Bundle();
params.putString("version", BuildConfig.VERSION); params.putString("version", BuildConfig.VERSION);
fabricPrivacy.getFirebaseAnalytics().logEvent("OmnipodPumpInit", params); fabricPrivacy.getFirebaseAnalytics().logEvent("OmnipodPumpInit", params);
sentIdToFirebase = true;
}
this.firstRun = false;
} }
@NonNull private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) { @NonNull private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
@ -914,19 +951,14 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private <T> T executeCommand(OmnipodCommandType commandType, Supplier<T> supplier) { private <T> T executeCommand(OmnipodCommandType commandType, Supplier<T> supplier) {
try { try {
currentCommand = commandType;
aapsLogger.debug(LTag.PUMP, "Executing command: {}", commandType); aapsLogger.debug(LTag.PUMP, "Executing command: {}", commandType);
rileyLinkUtil.getRileyLinkHistory().add(new RLHistoryItemOmnipod(getInjector(), commandType)); rileyLinkUtil.getRileyLinkHistory().add(new RLHistoryItemOmnipod(getInjector(), commandType));
T pumpEnactResult = supplier.get(); return supplier.get();
} finally {
rxBus.send(new EventRefreshOverview("Omnipod command: " + commandType.name(), false)); rxBus.send(new EventRefreshOverview("Omnipod command: " + commandType.name(), false));
rxBus.send(new EventOmnipodPumpValuesChanged()); rxBus.send(new EventOmnipodPumpValuesChanged());
return pumpEnactResult;
} finally {
currentCommand = null;
} }
} }
@ -946,7 +978,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
return true; return true;
} }
private void incrementStatistics(String statsKey) { private void incrementStatistics(int statsKey) {
long currentCount = sp.getLong(statsKey, 0L); long currentCount = sp.getLong(statsKey, 0L);
currentCount++; currentCount++;
sp.putLong(statsKey, currentCount); sp.putLong(statsKey, currentCount);

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dagger
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import dagger.MapKey
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Qualifier
import javax.inject.Scope
import kotlin.reflect.KClass
@Qualifier
annotation class OmnipodPluginQualifier
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
// TODO: These annotations and Factories could be used globally -> move to core or app
@Suppress("UNCHECKED_CAST")
class ViewModelFactory @Inject constructor(
private val viewModels: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}
@MustBeDocumented
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope
@MustBeDocumented
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FragmentScope

View file

@ -9,11 +9,8 @@ import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsPodStateManag
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodHistoryActivity import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodHistoryActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodManagementActivity import info.nightscout.androidaps.plugins.pump.omnipod.ui.PodManagementActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.initpod.InitActionFragment import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.PodActivationWizardActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.initpod.InitPodTask import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.PodDeactivationWizardActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.pages.InitPodRefreshAction
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.pages.PodInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.removepod.RemoveActionFragment
@Module @Module
@Suppress("unused") @Suppress("unused")
@ -24,21 +21,23 @@ abstract class OmnipodModule {
abstract fun contributesPodManagementActivity(): PodManagementActivity abstract fun contributesPodManagementActivity(): PodManagementActivity
@ContributesAndroidInjector abstract fun contributesPodHistoryActivity(): PodHistoryActivity @ContributesAndroidInjector abstract fun contributesPodHistoryActivity(): PodHistoryActivity
// Fragments @ActivityScope
@ContributesAndroidInjector abstract fun initActionFragment(): InitActionFragment @ContributesAndroidInjector(modules = [OmnipodWizardModule::class])
@ContributesAndroidInjector abstract fun removeActionFragment(): RemoveActionFragment abstract fun contributesActivationWizardActivity(): PodActivationWizardActivity
@ContributesAndroidInjector abstract fun podInfoFragment(): PodInfoFragment
@ActivityScope
@ContributesAndroidInjector(modules = [OmnipodWizardModule::class])
abstract fun contributesDeactivationWizardActivity(): PodDeactivationWizardActivity
// Service // Service
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun omnipodCommunicationManagerProvider(): OmnipodRileyLinkCommunicationManager abstract fun omnipodCommunicationManagerProvider(): OmnipodRileyLinkCommunicationManager
// Data // Data
@ContributesAndroidInjector abstract fun initPodRefreshAction(): InitPodRefreshAction
@ContributesAndroidInjector abstract fun initPodTask(): InitPodTask
@ContributesAndroidInjector abstract fun rlHistoryItemOmnipod(): RLHistoryItemOmnipod @ContributesAndroidInjector abstract fun rlHistoryItemOmnipod(): RLHistoryItemOmnipod
companion object { companion object {
@Provides @Provides
fun podStateManagerProvider(aapsPodStateManager: AapsPodStateManager): PodStateManager = aapsPodStateManager fun podStateManagerProvider(aapsPodStateManager: AapsPodStateManager): PodStateManager = aapsPodStateManager
} }

View file

@ -0,0 +1,92 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dagger
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import dagger.multibindings.IntoMap
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment.AttachPodInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment.FillPodInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment.InitializePodActionFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment.InsertCannulaActionFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment.PodActivatedInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel.InitializePodActionViewModel
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel.InsertCannulaActionViewModel
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.fragment.DeactivatePodActionFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.fragment.DeactivatePodInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.fragment.PodDeactivatedInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.fragment.PodDiscardedInfoFragment
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.viewmodel.DeactivatePodActionViewModel
import javax.inject.Provider
@Module
abstract class OmnipodWizardModule {
companion object {
@Provides
@OmnipodPluginQualifier
fun providesViewModelFactory(@OmnipodPluginQualifier viewModels: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>): ViewModelProvider.Factory {
return ViewModelFactory(viewModels)
}
}
// #### VIEW MODELS ############################################################################
@Binds
@IntoMap
@OmnipodPluginQualifier
@ViewModelKey(InitializePodActionViewModel::class)
internal abstract fun initializePodActionViewModel(viewModel: InitializePodActionViewModel): ViewModel
@Binds
@IntoMap
@OmnipodPluginQualifier
@ViewModelKey(InsertCannulaActionViewModel::class)
internal abstract fun insertCannulaActionViewModel(viewModel: InsertCannulaActionViewModel): ViewModel
@Binds
@IntoMap
@OmnipodPluginQualifier
@ViewModelKey(DeactivatePodActionViewModel::class)
internal abstract fun deactivatePodActionViewModel(viewModel: DeactivatePodActionViewModel): ViewModel
// #### FRAGMENTS ##############################################################################
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesDeactivatePodActionFragment(): DeactivatePodActionFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesInsertCannulaActionFragment(): InsertCannulaActionFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesInitializePodActionFragment(): InitializePodActionFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesAttachPodInfoFragment(): AttachPodInfoFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesDeactivatePodInfoFragment(): DeactivatePodInfoFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesFillPodInfoFragment(): FillPodInfoFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodDeactivatedInfoFragment(): PodDeactivatedInfoFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodDiscardedInfoFragment(): PodDiscardedInfoFragment
@FragmentScope
@ContributesAndroidInjector
internal abstract fun contributesPodActivatedInfoFragment(): PodActivatedInfoFragment
}

View file

@ -6,8 +6,8 @@ import info.nightscout.androidaps.plugins.pump.omnipod.R;
* Created by andy on 4.8.2019 * Created by andy on 4.8.2019
*/ */
public enum OmnipodCommandType { public enum OmnipodCommandType {
PAIR_AND_PRIME_POD(R.string.omnipod_cmd_pair_and_prime), // First step of Pod activation INITIALIZE_POD(R.string.omnipod_cmd_initialize_pod), // First step of Pod activation
FILL_CANNULA_AND_SET_BASAL_PROFILE(R.string.omnipod_cmd_fill_cannula_set_basal_profile), // Second step of Pod activation INSERT_CANNULA(R.string.omnipod_cmd_insert_cannula), // Second step of Pod activation
DEACTIVATE_POD(R.string.omnipod_cmd_deactivate_pod), // DEACTIVATE_POD(R.string.omnipod_cmd_deactivate_pod), //
SET_BASAL_PROFILE(R.string.omnipod_cmd_set_basal_schedule), // SET_BASAL_PROFILE(R.string.omnipod_cmd_set_basal_schedule), //
SET_BOLUS(R.string.omnipod_cmd_set_bolus), // SET_BOLUS(R.string.omnipod_cmd_set_bolus), //
@ -19,8 +19,9 @@ public enum OmnipodCommandType {
SET_TIME(R.string.omnipod_cmd_set_time), // SET_TIME(R.string.omnipod_cmd_set_time), //
CONFIGURE_ALERTS(R.string.omnipod_cmd_configure_alerts), // CONFIGURE_ALERTS(R.string.omnipod_cmd_configure_alerts), //
ACKNOWLEDGE_ALERTS(R.string.omnipod_cmd_acknowledge_alerts), // ACKNOWLEDGE_ALERTS(R.string.omnipod_cmd_acknowledge_alerts), //
GET_POD_PULSE_LOG(R.string.omnipod_cmd_get_pulse_log), // READ_POD_PULSE_LOG(R.string.omnipod_cmd_read_pulse_log), //
SUSPEND_DELIVERY(R.string.omnipod_cmd_suspend_delivery); SUSPEND_DELIVERY(R.string.omnipod_cmd_suspend_delivery),
RESUME_DELIVERY(R.string.omnipod_cmd_resume_delivery);
private int resourceId; private int resourceId;

View file

@ -1,8 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.definition;
public enum OmnipodStatusRequestType {
ACKNOWLEDGE_ALERTS,
GET_POD_STATE,
GET_PULSE_LOG,
SUSPEND_DELIVERY
}

View file

@ -3,11 +3,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.definition;
import info.nightscout.androidaps.plugins.pump.omnipod.R; import info.nightscout.androidaps.plugins.pump.omnipod.R;
public class OmnipodStorageKeys { public class OmnipodStorageKeys {
private static final String PREFIX = "AAPS.Omnipod.";
public static class Preferences { public static class Preferences {
public static final String POD_STATE = PREFIX + "pod_state"; public static final int POD_STATE = R.string.key_omnipod_pod_state;
public static final String ACTIVE_BOLUS = PREFIX + "current_bolus"; public static final int ACTIVE_BOLUS = R.string.key_omnipod_current_bolus;
public static final int BASAL_BEEPS_ENABLED = R.string.key_omnipod_basal_beeps_enabled; public static final int BASAL_BEEPS_ENABLED = R.string.key_omnipod_basal_beeps_enabled;
public static final int BOLUS_BEEPS_ENABLED = R.string.key_omnipod_bolus_beeps_enabled; public static final int BOLUS_BEEPS_ENABLED = R.string.key_omnipod_bolus_beeps_enabled;
public static final int SMB_BEEPS_ENABLED = R.string.key_omnipod_smb_beeps_enabled; public static final int SMB_BEEPS_ENABLED = R.string.key_omnipod_smb_beeps_enabled;
@ -22,8 +20,8 @@ public class OmnipodStorageKeys {
} }
public static class Statistics { public static class Statistics {
public static final String TBRS_SET = PREFIX + "tbrs_set"; public static final int TBRS_SET = R.string.key_omnipod_tbrs_set;
public static final String STANDARD_BOLUSES_DELIVERED = PREFIX + "std_boluses_delivered"; public static final int STANDARD_BOLUSES_DELIVERED = R.string.key_omnipod_std_boluses_delivered;
public static final String SMB_BOLUSES_DELIVERED = PREFIX + "smb_boluses_delivered"; public static final int SMB_BOLUSES_DELIVERED = R.string.key_omnipod_smb_boluses_delivered;
} }
} }

View file

@ -13,10 +13,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.R;
*/ */
public enum PodHistoryEntryType { public enum PodHistoryEntryType {
PAIR_AND_PRIME(1, R.string.omnipod_init_pod_wizard_step2_title, PumpHistoryEntryGroup.Prime), INITIALIZE_POD(1, R.string.omnipod_cmd_initialize_pod, PumpHistoryEntryGroup.Prime),
FILL_CANNULA_SET_BASAL_PROFILE(2, R.string.omnipod_init_pod_wizard_step4_title, PumpHistoryEntryGroup.Prime), INSERT_CANNULA(2, R.string.omnipod_cmd_insert_cannula, PumpHistoryEntryGroup.Prime),
DEACTIVATE_POD(3, R.string.omnipod_cmd_deactivate_pod, PumpHistoryEntryGroup.Prime), DEACTIVATE_POD(3, R.string.omnipod_cmd_deactivate_pod, PumpHistoryEntryGroup.Prime),
RESET_POD_STATE(4, R.string.omnipod_cmd_discard_pod, PumpHistoryEntryGroup.Prime), DISCARD_POD(4, R.string.omnipod_cmd_discard_pod, PumpHistoryEntryGroup.Prime),
SET_TEMPORARY_BASAL(10, R.string.omnipod_cmd_set_tbr, PumpHistoryEntryGroup.Basal), SET_TEMPORARY_BASAL(10, R.string.omnipod_cmd_set_tbr, PumpHistoryEntryGroup.Basal),
CANCEL_TEMPORARY_BASAL_BY_DRIVER(11, R.string.omnipod_cmd_cancel_tbr_by_driver, PumpHistoryEntryGroup.Basal), CANCEL_TEMPORARY_BASAL_BY_DRIVER(11, R.string.omnipod_cmd_cancel_tbr_by_driver, PumpHistoryEntryGroup.Basal),

View file

@ -1,54 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.definition;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.R;
public enum PodInitActionType {
PAIR_AND_PRIME_WIZARD_STEP(), //
PAIR_POD(R.string.omnipod_init_pod_pair_pod, PAIR_AND_PRIME_WIZARD_STEP), //
PRIME_POD(R.string.omnipod_init_pod_prime_pod, PAIR_AND_PRIME_WIZARD_STEP), //
FILL_CANNULA_SET_BASAL_PROFILE_WIZARD_STEP(), //
FILL_CANNULA(R.string.omnipod_init_pod_fill_cannula, FILL_CANNULA_SET_BASAL_PROFILE_WIZARD_STEP), //
SET_BASAL_PROFILE(R.string.omnipod_init_pod_set_basal_profile, FILL_CANNULA_SET_BASAL_PROFILE_WIZARD_STEP), //
DEACTIVATE_POD_WIZARD_STEP(), //
CANCEL_DELIVERY(R.string.omnipod_deactivate_pod_cancel_delivery, DEACTIVATE_POD_WIZARD_STEP), //
DEACTIVATE_POD(R.string.omnipod_deactivate_pod_deactivate_pod, DEACTIVATE_POD_WIZARD_STEP);
private int resourceId;
private PodInitActionType parent;
PodInitActionType(int resourceId, PodInitActionType parent) {
this.resourceId = resourceId;
this.parent = parent;
}
PodInitActionType() {
}
public boolean isParent() {
return this.parent == null;
}
public List<PodInitActionType> getChildren() {
List<PodInitActionType> outList = new ArrayList<>();
for (PodInitActionType value : values()) {
if (value.parent == this) {
outList.add(value);
}
}
return outList;
}
public int getResourceId() {
return resourceId;
}
}

View file

@ -1,7 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.definition;
public interface PodInitReceiver {
void returnInitTaskStatus(PodInitActionType podInitActionType, boolean isSuccess, String errorMessage);
}

View file

@ -6,7 +6,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSet; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSet;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSlot; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSlot;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -16,12 +15,12 @@ public class AcknowledgeAlertsAction implements OmnipodAction<StatusResponse> {
public AcknowledgeAlertsAction(PodStateManager podStateManager, AlertSet alerts) { public AcknowledgeAlertsAction(PodStateManager podStateManager, AlertSet alerts) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (alerts == null) { if (alerts == null) {
throw new ActionInitializationException("Alert set can not be null"); throw new IllegalArgumentException("Alert set can not be null");
} else if (alerts.size() == 0) { } else if (alerts.size() == 0) {
throw new ActionInitializationException("Alert set can not be empty"); throw new IllegalArgumentException("Alert set can not be empty");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.alerts = alerts; this.alerts = alerts;

View file

@ -9,7 +9,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BolusDeliverySchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BolusDeliverySchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -23,10 +22,10 @@ public class BolusAction implements OmnipodAction<StatusResponse> {
public BolusAction(PodStateManager podStateManager, double units, Duration timeBetweenPulses, public BolusAction(PodStateManager podStateManager, double units, Duration timeBetweenPulses,
boolean acknowledgementBeep, boolean completionBeep) { boolean acknowledgementBeep, boolean completionBeep) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (timeBetweenPulses == null) { if (timeBetweenPulses == null) {
throw new ActionInitializationException("Time between pulses cannot be null"); throw new IllegalArgumentException("Time between pulses cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.units = units; this.units = units;

View file

@ -10,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.BeepType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.BeepType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -22,10 +21,10 @@ public class CancelDeliveryAction implements OmnipodAction<StatusResponse> {
public CancelDeliveryAction(PodStateManager podStateManager, EnumSet<DeliveryType> deliveryTypes, public CancelDeliveryAction(PodStateManager podStateManager, EnumSet<DeliveryType> deliveryTypes,
boolean acknowledgementBeep) { boolean acknowledgementBeep) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (deliveryTypes == null) { if (deliveryTypes == null) {
throw new ActionInitializationException("Delivery types cannot be null"); throw new IllegalArgumentException("Delivery types cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.deliveryTypes = deliveryTypes; this.deliveryTypes = deliveryTypes;

View file

@ -5,7 +5,6 @@ import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.ConfigureAlertsCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.ConfigureAlertsCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -15,10 +14,10 @@ public class ConfigureAlertsAction implements OmnipodAction<StatusResponse> {
public ConfigureAlertsAction(PodStateManager podStateManager, List<AlertConfiguration> alertConfigurations) { public ConfigureAlertsAction(PodStateManager podStateManager, List<AlertConfiguration> alertConfigurations) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (alertConfigurations == null) { if (alertConfigurations == null) {
throw new ActionInitializationException("Alert configurations cannot be null"); throw new IllegalArgumentException("Alert configurations cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.alertConfigurations = alertConfigurations; this.alertConfigurations = alertConfigurations;

View file

@ -5,7 +5,6 @@ import java.util.EnumSet;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.DeactivatePodCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.DeactivatePodCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -16,7 +15,7 @@ public class DeactivatePodAction implements OmnipodAction<StatusResponse> {
public DeactivatePodAction(PodStateManager podStateManager, boolean acknowledgementBeep) { public DeactivatePodAction(PodStateManager podStateManager, boolean acknowledgementBeep) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.acknowledgementBeep = acknowledgementBeep; this.acknowledgementBeep = acknowledgementBeep;

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.act
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -13,10 +12,10 @@ public class GetPodInfoAction implements OmnipodAction<PodInfoResponse> {
public GetPodInfoAction(PodStateManager podStateManager, PodInfoType podInfoType) { public GetPodInfoAction(PodStateManager podStateManager, PodInfoType podInfoType) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (podInfoType == null) { if (podInfoType == null) {
throw new ActionInitializationException("Pod info type cannot be null"); throw new IllegalArgumentException("Pod info type cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.podInfoType = podInfoType; this.podInfoType = podInfoType;

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.act
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -12,7 +11,7 @@ public class GetStatusAction implements OmnipodAction<StatusResponse> {
public GetStatusAction(PodStateManager podState) { public GetStatusAction(PodStateManager podState) {
if (podState == null) { if (podState == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
this.podStateManager = podState; this.podStateManager = podState;
} }

View file

@ -11,7 +11,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertCo
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -26,10 +25,10 @@ public class InsertCannulaAction implements OmnipodAction<StatusResponse> {
public InsertCannulaAction(PodStateManager podStateManager, BasalSchedule initialBasalSchedule, public InsertCannulaAction(PodStateManager podStateManager, BasalSchedule initialBasalSchedule,
Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) { Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (initialBasalSchedule == null) { if (initialBasalSchedule == null) {
throw new ActionInitializationException("Initial basal schedule cannot be null"); throw new IllegalArgumentException("Initial basal schedule cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.initialBasalSchedule = initialBasalSchedule; this.initialBasalSchedule = initialBasalSchedule;

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.act
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.PrimeService; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.PrimeService;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -15,10 +14,10 @@ public class PrimeAction implements OmnipodAction<StatusResponse> {
public PrimeAction(PrimeService primeService, PodStateManager podStateManager) { public PrimeAction(PrimeService primeService, PodStateManager podStateManager) {
if (primeService == null) { if (primeService == null) {
throw new ActionInitializationException("Prime service cannot be null"); throw new IllegalArgumentException("Prime service cannot be null");
} }
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
this.service = primeService; this.service = primeService;
this.podStateManager = podStateManager; this.podStateManager = podStateManager;

View file

@ -9,7 +9,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -23,13 +22,13 @@ public class SetBasalScheduleAction implements OmnipodAction<StatusResponse> {
public SetBasalScheduleAction(PodStateManager podStateManager, BasalSchedule basalSchedule, public SetBasalScheduleAction(PodStateManager podStateManager, BasalSchedule basalSchedule,
boolean confidenceReminder, Duration scheduleOffset, boolean acknowledgementBeep) { boolean confidenceReminder, Duration scheduleOffset, boolean acknowledgementBeep) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (basalSchedule == null) { if (basalSchedule == null) {
throw new ActionInitializationException("Basal schedule cannot be null"); throw new IllegalArgumentException("Basal schedule cannot be null");
} }
if (scheduleOffset == null) { if (scheduleOffset == null) {
throw new ActionInitializationException("Schedule offset cannot be null"); throw new IllegalArgumentException("Schedule offset cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.basalSchedule = basalSchedule; this.basalSchedule = basalSchedule;

View file

@ -10,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.TempBasalExtraCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.TempBasalExtraCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
@ -24,10 +23,10 @@ public class SetTempBasalAction implements OmnipodAction<StatusResponse> {
public SetTempBasalAction(PodStateManager podStateManager, double rate, Duration duration, public SetTempBasalAction(PodStateManager podStateManager, double rate, Duration duration,
boolean acknowledgementBeep, boolean completionBeep) { boolean acknowledgementBeep, boolean completionBeep) {
if (podStateManager == null) { if (podStateManager == null) {
throw new ActionInitializationException("Pod state manager cannot be null"); throw new IllegalArgumentException("Pod state manager cannot be null");
} }
if (duration == null) { if (duration == null) {
throw new ActionInitializationException("Duration cannot be null"); throw new IllegalArgumentException("Duration cannot be null");
} }
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.rate = rate; this.rate = rate;

View file

@ -17,11 +17,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.util.AlertConfigur
public final class ExpirationReminderBuilder { public final class ExpirationReminderBuilder {
private final Map<AlertSlot, AlertConfiguration> alerts = new HashMap<>(); private final Map<AlertSlot, AlertConfiguration> alerts = new HashMap<>();
private final DateTime endOfServiceTime; private final DateTime endOfServiceTime;
private final PodStateManager podStateManager;
public ExpirationReminderBuilder(PodStateManager podStateManager) { public ExpirationReminderBuilder(PodStateManager podStateManager) {
this.endOfServiceTime = podStateManager.getActivatedAt().plus(OmnipodConstants.SERVICE_DURATION); this.endOfServiceTime = podStateManager.getActivatedAt().plus(OmnipodConstants.SERVICE_DURATION);
this.podStateManager = podStateManager;
} }
public ExpirationReminderBuilder defaults() { public ExpirationReminderBuilder defaults() {
@ -45,21 +43,17 @@ public final class ExpirationReminderBuilder {
public ExpirationReminderBuilder expirationAdvisory(boolean active, Duration timeBeforeShutdown) { public ExpirationReminderBuilder expirationAdvisory(boolean active, Duration timeBeforeShutdown) {
DateTime expirationAdvisoryAlarmTime = endOfServiceTime.minus(timeBeforeShutdown); DateTime expirationAdvisoryAlarmTime = endOfServiceTime.minus(timeBeforeShutdown);
if (DateTime.now().isBefore(expirationAdvisoryAlarmTime)) { Duration timeUntilExpirationAdvisoryAlarm = DateTime.now().isBefore(expirationAdvisoryAlarmTime) ? new Duration(DateTime.now(),
Duration timeUntilExpirationAdvisoryAlarm = new Duration(DateTime.now(), expirationAdvisoryAlarmTime) : Duration.ZERO;
expirationAdvisoryAlarmTime);
AlertConfiguration expirationAdvisoryAlertConfiguration = AlertConfigurationUtil.createExpirationAdvisoryAlertConfiguration(active, AlertConfiguration expirationAdvisoryAlertConfiguration = AlertConfigurationUtil.createExpirationAdvisoryAlertConfiguration(active,
timeUntilExpirationAdvisoryAlarm, timeBeforeShutdown); timeUntilExpirationAdvisoryAlarm, timeBeforeShutdown);
alerts.put(expirationAdvisoryAlertConfiguration.getAlertSlot(), expirationAdvisoryAlertConfiguration); alerts.put(expirationAdvisoryAlertConfiguration.getAlertSlot(), expirationAdvisoryAlertConfiguration);
}
return this; return this;
} }
public ExpirationReminderBuilder lowReservoir(boolean active, int units) { public ExpirationReminderBuilder lowReservoir(boolean active, int units) {
if (podStateManager.getReservoirLevel() == null || podStateManager.getReservoirLevel().intValue() > units) {
AlertConfiguration lowReservoirAlertConfiguration = AlertConfigurationUtil.createLowReservoirAlertConfiguration(active, (double) units); AlertConfiguration lowReservoirAlertConfiguration = AlertConfigurationUtil.createLowReservoirAlertConfiguration(active, (double) units);
alerts.put(lowReservoirAlertConfiguration.getAlertSlot(), lowReservoirAlertConfiguration); alerts.put(lowReservoirAlertConfiguration.getAlertSlot(), lowReservoirAlertConfiguration);
}
return this; return this;
} }

View file

@ -3,10 +3,13 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mes
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.GetStatusCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodCrc; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodCrc;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CrcMismatchException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CrcMismatchException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.MessageDecodingException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.MessageDecodingException;
@ -91,8 +94,11 @@ public class OmnipodMessage {
return encodedData; return encodedData;
} }
public void padWithGetStatusCommands(int packetSize) { public void padWithGetStatusCommands(int packetSize, AAPSLogger aapsLogger) {
while (getEncoded().length < packetSize) { while (getEncoded().length <= packetSize) {
if (getEncoded().length == PacketType.PDM.getMaxBodyLength()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Message length equals max body length: {}", this);
}
messageBlocks.add(new GetStatusCommand(PodInfoType.NORMAL)); messageBlocks.add(new GetStatusCommand(PodInfoType.NORMAL));
} }
} }

View file

@ -6,7 +6,6 @@ import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.MessageBlock; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.MessageBlock;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandInitializationException;
public class BolusExtraCommand extends MessageBlock { public class BolusExtraCommand extends MessageBlock {
private final boolean acknowledgementBeep; private final boolean acknowledgementBeep;
@ -29,9 +28,9 @@ public class BolusExtraCommand extends MessageBlock {
boolean acknowledgementBeep, boolean completionBeep, boolean acknowledgementBeep, boolean completionBeep,
Duration programReminderInterval, Duration timeBetweenPulses) { Duration programReminderInterval, Duration timeBetweenPulses) {
if (units <= 0D) { if (units <= 0D) {
throw new CommandInitializationException("Units should be > 0"); throw new IllegalArgumentException("Units should be > 0");
} else if (units > OmnipodConstants.MAX_BOLUS) { } else if (units > OmnipodConstants.MAX_BOLUS) {
throw new CommandInitializationException("Units exceeds max bolus"); throw new IllegalArgumentException("Units exceeds max bolus");
} }
this.units = units; this.units = units;
this.squareWaveUnits = squareWaveUnits; this.squareWaveUnits = squareWaveUnits;

View file

@ -12,7 +12,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedul
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BolusDeliverySchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BolusDeliverySchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.DeliverySchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.DeliverySchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.TempBasalDeliverySchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.TempBasalDeliverySchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandInitializationException;
public class SetInsulinScheduleCommand extends NonceResyncableMessageBlock { public class SetInsulinScheduleCommand extends NonceResyncableMessageBlock {
@ -51,12 +50,12 @@ public class SetInsulinScheduleCommand extends NonceResyncableMessageBlock {
// Temp basal // Temp basal
public SetInsulinScheduleCommand(int nonce, double tempBasalRate, Duration duration) { public SetInsulinScheduleCommand(int nonce, double tempBasalRate, Duration duration) {
if (tempBasalRate < 0D) { if (tempBasalRate < 0D) {
throw new CommandInitializationException("Rate should be >= 0"); throw new IllegalArgumentException("Rate should be >= 0");
} else if (tempBasalRate > OmnipodConstants.MAX_BASAL_RATE) { } else if (tempBasalRate > OmnipodConstants.MAX_BASAL_RATE) {
throw new CommandInitializationException("Rate exceeds max basal rate"); throw new IllegalArgumentException("Rate exceeds max basal rate");
} }
if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) { if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) {
throw new CommandInitializationException("Duration exceeds max temp basal duration"); throw new IllegalArgumentException("Duration exceeds max temp basal duration");
} }
int pulsesPerHour = (int) Math.round(tempBasalRate / OmnipodConstants.POD_PULSE_SIZE); int pulsesPerHour = (int) Math.round(tempBasalRate / OmnipodConstants.POD_PULSE_SIZE);
int pulsesPerSegment = pulsesPerHour / 2; int pulsesPerSegment = pulsesPerHour / 2;

View file

@ -10,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.RateEntry; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.RateEntry;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandInitializationException;
public class TempBasalExtraCommand extends MessageBlock { public class TempBasalExtraCommand extends MessageBlock {
private final boolean acknowledgementBeep; private final boolean acknowledgementBeep;
@ -24,12 +23,12 @@ public class TempBasalExtraCommand extends MessageBlock {
public TempBasalExtraCommand(double rate, Duration duration, boolean acknowledgementBeep, boolean completionBeep, public TempBasalExtraCommand(double rate, Duration duration, boolean acknowledgementBeep, boolean completionBeep,
Duration programReminderInterval) { Duration programReminderInterval) {
if (rate < 0D) { if (rate < 0D) {
throw new CommandInitializationException("Rate should be >= 0"); throw new IllegalArgumentException("Rate should be >= 0");
} else if (rate > OmnipodConstants.MAX_BASAL_RATE) { } else if (rate > OmnipodConstants.MAX_BASAL_RATE) {
throw new CommandInitializationException("Rate exceeds max basal rate"); throw new IllegalArgumentException("Rate exceeds max basal rate");
} }
if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) { if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) {
throw new CommandInitializationException("Duration exceeds max temp basal duration"); throw new IllegalArgumentException("Duration exceeds max temp basal duration");
} }
this.acknowledgementBeep = acknowledgementBeep; this.acknowledgementBeep = acknowledgementBeep;

View file

@ -69,7 +69,9 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
unacknowledgedAlerts = new AlertSet(encodedData[15]); unacknowledgedAlerts = new AlertSet(encodedData[15]);
faultAccessingTables = encodedData[16] == 0x02; faultAccessingTables = encodedData[16] == 0x02;
logEventErrorType = LogEventErrorCode.fromByte((byte) (encodedData[17] >>> 4)); int i = ByteUtil.convertUnsignedByteToInt(encodedData[17]);
byte value = (byte) (i >>> 4);
logEventErrorType = LogEventErrorCode.fromByte(value);
logEventErrorPodProgressStatus = PodProgressStatus.fromByte((byte) (encodedData[17] & 0x0f)); logEventErrorPodProgressStatus = PodProgressStatus.fromByte((byte) (encodedData[17] & 0x0f));
receiverLowGain = (byte) (ByteUtil.convertUnsignedByteToInt(encodedData[18]) >>> 6); receiverLowGain = (byte) (ByteUtil.convertUnsignedByteToInt(encodedData[18]) >>> 6);
radioRSSI = (byte) (encodedData[18] & 0x3f); radioRSSI = (byte) (encodedData[18] & 0x3f);

View file

@ -21,10 +21,8 @@ public class OmnipodConstants {
public static final Duration AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION = Duration.millis(1500); public static final Duration AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION = Duration.millis(1500);
public static final Duration SERVICE_DURATION = Duration.standardHours(80); public static final Duration SERVICE_DURATION = Duration.standardHours(80);
public static final Duration EXPIRATION_ADVISORY_WINDOW = Duration.standardHours(9);
public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1); public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1);
public static final Duration NOMINAL_POD_LIFE = Duration.standardHours(72); public static final Duration NOMINAL_POD_LIFE = Duration.standardHours(72);
public static final double LOW_RESERVOIR_ALERT = 20.0;
public static final double POD_PRIME_BOLUS_UNITS = 2.6; public static final double POD_PRIME_BOLUS_UNITS = 2.6;
public static final double POD_CANNULA_INSERTION_BOLUS_UNITS = 0.5; public static final double POD_CANNULA_INSERTION_BOLUS_UNITS = 0.5;

View file

@ -1,7 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class ActionInitializationException extends OmnipodException {
public ActionInitializationException(String message) {
super(message, true);
}
}

View file

@ -1,11 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class CommandInitializationException extends OmnipodException {
public CommandInitializationException(String message) {
super(message, true);
}
public CommandInitializationException(String message, Throwable cause) {
super(message, cause, true);
}
}

View file

@ -1,34 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class CommunicationException extends OmnipodException {
private final Type type;
public CommunicationException(Type type) {
super(type.getDescription(), false);
this.type = type;
}
public CommunicationException(Type type, Throwable cause) {
super(type.getDescription() + ": " + cause, cause, false);
this.type = type;
}
public Type getType() {
return type;
}
public enum Type {
TIMEOUT("Communication timeout"),
UNEXPECTED_EXCEPTION("Caught an unexpected Exception");
private final String description;
Type(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
public class PodProgressStatusVerificationFailedException extends OmnipodException {
private final PodProgressStatus expectedStatus;
public PodProgressStatusVerificationFailedException(PodProgressStatus expectedStatus, Throwable cause) {
super("Failed to verify Pod progress status (expected=" + expectedStatus + ")", cause, false);
this.expectedStatus = expectedStatus;
}
public PodProgressStatus getExpectedStatus() {
return expectedStatus;
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class RileyLinkInterruptedException extends OmnipodException {
public RileyLinkInterruptedException() {
super("RileyLink interrupted", false);
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
// Response indicating that there was a timeout in communication between the RileyLink and the Pod
public class RileyLinkTimeoutException extends OmnipodException {
public RileyLinkTimeoutException() {
super("Timeout in communication between RileyLink and Pod", false);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class RileyLinkUnexpectedException extends OmnipodException {
public RileyLinkUnexpectedException(Throwable cause) {
super("Unexpected Exception during RileyLink communication", cause, false);
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
// Indicates that we didn't get any response from the RL
public class RileyLinkUnreachableException extends OmnipodException {
public RileyLinkUnreachableException() {
super("Timeout in communication between phone and RileyLink", false);
}
}

View file

@ -41,7 +41,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfo
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommunicationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalDeliveryStatusException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
@ -49,6 +48,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalP
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NonceOutOfSyncException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NonceOutOfSyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodProgressStatusVerificationFailedException;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.Completable; import io.reactivex.Completable;
@ -59,7 +59,7 @@ import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.SingleSubject; import io.reactivex.subjects.SingleSubject;
public class OmnipodManager { public class OmnipodManager {
private static final int ACTION_VERIFICATION_TRIES = 3; private static final int ACTION_VERIFICATION_TRIES = 2;
private final OmnipodRileyLinkCommunicationManager communicationService; private final OmnipodRileyLinkCommunicationManager communicationService;
private PodStateManager podStateManager; private PodStateManager podStateManager;
@ -87,7 +87,7 @@ public class OmnipodManager {
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
} }
public synchronized Single<SetupActionResult> pairAndPrime() { public synchronized Single<Boolean> pairAndPrime() {
logStartingCommandExecution("pairAndPrime"); logStartingCommandExecution("pairAndPrime");
try { try {
@ -126,14 +126,14 @@ public class OmnipodManager {
logCommandExecutionFinished("pairAndPrime"); logCommandExecutionFinished("pairAndPrime");
} }
long delayInSeconds = calculateBolusDuration(OmnipodConstants.POD_PRIME_BOLUS_UNITS, OmnipodConstants.POD_PRIMING_DELIVERY_RATE).getStandardSeconds(); long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_PRIME_BOLUS_UNITS, OmnipodConstants.POD_PRIMING_DELIVERY_RATE).getMillis();
return Single.timer(delayInSeconds, TimeUnit.SECONDS) // return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) //
.map(o -> verifySetupAction(PodProgressStatus.PRIMING_COMPLETED)) // .map(o -> verifyPodProgressStatus(PodProgressStatus.PRIMING_COMPLETED)) //
.observeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
public synchronized Single<SetupActionResult> insertCannula( public synchronized Single<Boolean> insertCannula(
BasalSchedule basalSchedule, Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) { BasalSchedule basalSchedule, Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) {
if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING_COMPLETED)) { if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, !podStateManager.isPodInitialized() ? null : podStateManager.getPodProgressStatus()); throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, !podStateManager.isPodInitialized() ? null : podStateManager.getPodProgressStatus());
@ -154,11 +154,11 @@ public class OmnipodManager {
logCommandExecutionFinished("insertCannula"); logCommandExecutionFinished("insertCannula");
} }
long delayInSeconds = calculateBolusDuration(OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConstants.POD_CANNULA_INSERTION_DELIVERY_RATE).getStandardSeconds(); long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConstants.POD_CANNULA_INSERTION_DELIVERY_RATE).getMillis();
return Single.timer(delayInSeconds, TimeUnit.SECONDS) // return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) //
.map(o -> verifySetupAction(PodProgressStatus.ABOVE_FIFTY_UNITS)) // .map(o -> verifyPodProgressStatus(PodProgressStatus.ABOVE_FIFTY_UNITS)) //
.observeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
public synchronized StatusResponse getPodStatus() { public synchronized StatusResponse getPodStatus() {
@ -224,13 +224,10 @@ public class OmnipodManager {
suspendDelivery(acknowledgementBeep); suspendDelivery(acknowledgementBeep);
} }
// Store the new Basal schedule after successfully suspending delivery, so that if setting the Basal schedule fails,
// And we later try to resume delivery, the new schedule is used
podStateManager.setBasalSchedule(schedule);
try { try {
executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule, executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule,
false, podStateManager.getScheduleOffset(), acknowledgementBeep))); false, podStateManager.getScheduleOffset(), acknowledgementBeep)));
podStateManager.setBasalSchedule(schedule);
} catch (OmnipodException ex) { } catch (OmnipodException ex) {
if (ex.isCertainFailure()) { if (ex.isCertainFailure()) {
if (!wasSuspended) { if (!wasSuspended) {
@ -240,7 +237,9 @@ public class OmnipodManager {
} }
// verifyDeliveryStatus will throw an exception if verification fails // verifyDeliveryStatus will throw an exception if verification fails
if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { if (verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) {
podStateManager.setBasalSchedule(schedule);
} else {
if (!wasSuspended) { if (!wasSuspended) {
throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but setting the new basal schedule did not", ex); throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but setting the new basal schedule did not", ex);
} }
@ -248,6 +247,8 @@ public class OmnipodManager {
throw ex; throw ex;
} }
} }
} finally { } finally {
logCommandExecutionFinished("setBasalSchedule"); logCommandExecutionFinished("setBasalSchedule");
} }
@ -270,11 +271,15 @@ public class OmnipodManager {
throw ex; throw ex;
} }
// verifyDeliveryStatus will throw an exception if verification fails try {
if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) {
ex.setCertainFailure(true); ex.setCertainFailure(true);
throw ex; throw ex;
} }
} catch (DeliveryStatusVerificationFailedException ex2) {
podStateManager.setTempBasalCertain(false);
throw ex2;
}
} }
} }
@ -321,7 +326,23 @@ public class OmnipodManager {
} }
public synchronized void cancelTemporaryBasal(boolean acknowledgementBeep) { public synchronized void cancelTemporaryBasal(boolean acknowledgementBeep) {
try {
cancelDelivery(EnumSet.of(DeliveryType.TEMP_BASAL), acknowledgementBeep); cancelDelivery(EnumSet.of(DeliveryType.TEMP_BASAL), acknowledgementBeep);
} catch (OmnipodException ex) {
if (ex.isCertainFailure()) {
throw ex;
}
try {
if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) {
ex.setCertainFailure(true);
throw ex;
}
} catch (DeliveryStatusVerificationFailedException ex2) {
podStateManager.setTempBasalCertain(false);
throw ex2;
}
}
} }
private synchronized StatusResponse cancelDelivery(EnumSet<DeliveryType> deliveryTypes, boolean acknowledgementBeep) { private synchronized StatusResponse cancelDelivery(EnumSet<DeliveryType> deliveryTypes, boolean acknowledgementBeep) {
@ -367,7 +388,7 @@ public class OmnipodManager {
} }
DateTime estimatedBolusStartDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); DateTime estimatedBolusStartDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
Duration estimatedBolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); Duration estimatedBolusDuration = calculateEstimatedBolusDuration(estimatedBolusStartDate, units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE);
Duration estimatedRemainingBolusDuration = estimatedBolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); Duration estimatedRemainingBolusDuration = estimatedBolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
podStateManager.setLastBolus(estimatedBolusStartDate, units, estimatedBolusDuration, commandDeliveryStatus == CommandDeliveryStatus.SUCCESS); podStateManager.setLastBolus(estimatedBolusStartDate, units, estimatedBolusDuration, commandDeliveryStatus == CommandDeliveryStatus.SUCCESS);
@ -376,11 +397,11 @@ public class OmnipodManager {
if (progressIndicationConsumer != null) { if (progressIndicationConsumer != null) {
int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10)); long numberOfProgressReports = Math.max(10, Math.min(100, estimatedRemainingBolusDuration.getStandardSeconds()));
long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports; long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports;
disposables.add(Flowable.intervalRange(0, numberOfProgressReports + 1, 0, progressReportInterval, TimeUnit.MILLISECONDS) // disposables.add(Flowable.intervalRange(0, numberOfProgressReports + 1, 0, progressReportInterval, TimeUnit.MILLISECONDS) //
.observeOn(Schedulers.io()) // .subscribeOn(Schedulers.io()) //
.subscribe(count -> { .subscribe(count -> {
int percentage = (int) ((double) count / numberOfProgressReports * 100); int percentage = (int) ((double) count / numberOfProgressReports * 100);
double estimatedUnitsDelivered = activeBolusData == null ? 0 : activeBolusData.estimateUnitsDelivered(); double estimatedUnitsDelivered = activeBolusData == null ? 0 : activeBolusData.estimateUnitsDelivered();
@ -400,8 +421,8 @@ public class OmnipodManager {
bolusCommandExecutionSubject = null; bolusCommandExecutionSubject = null;
disposables.add(Completable.complete() // disposables.add(Completable.complete() //
.delay(estimatedRemainingBolusDuration.getMillis() + 250, TimeUnit.MILLISECONDS) // .delay(estimatedRemainingBolusDuration.getMillis(), TimeUnit.MILLISECONDS) //
.observeOn(Schedulers.io()) // .subscribeOn(Schedulers.io()) //
.doOnComplete(() -> { .doOnComplete(() -> {
synchronized (bolusDataMutex) { synchronized (bolusDataMutex) {
double bolusNotDelivered = 0.0d; double bolusNotDelivered = 0.0d;
@ -412,9 +433,8 @@ public class OmnipodManager {
StatusResponse statusResponse = getPodStatus(); StatusResponse statusResponse = getPodStatus();
if (statusResponse.getDeliveryStatus().isBolusing()) { if (statusResponse.getDeliveryStatus().isBolusing()) {
throw new IllegalDeliveryStatusException(DeliveryStatus.NORMAL, statusResponse.getDeliveryStatus()); throw new IllegalDeliveryStatusException(DeliveryStatus.NORMAL, statusResponse.getDeliveryStatus());
} else {
break;
} }
break;
} catch (PodFaultException ex) { } catch (PodFaultException ex) {
// Subtract units not delivered in case of a Pod failure // Subtract units not delivered in case of a Pod failure
bolusNotDelivered = ex.getFaultEvent().getBolusNotDelivered(); bolusNotDelivered = ex.getFaultEvent().getBolusNotDelivered();
@ -513,6 +533,8 @@ public class OmnipodManager {
podStateManager.setTimeZone(oldTimeZone); podStateManager.setTimeZone(oldTimeZone);
throw ex; throw ex;
} }
podStateManager.updateActivatedAt();
} finally { } finally {
logCommandExecutionFinished("setTime"); logCommandExecutionFinished("setTime");
} }
@ -529,9 +551,9 @@ public class OmnipodManager {
try { try {
PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG)); PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG));
PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo(); PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo();
aapsLogger.info(LTag.PUMPCOMM, "Retrieved pulse log from the pod: {}", pulseLogInfo.toString()); aapsLogger.info(LTag.PUMPCOMM, "Read pulse log from the pod: {}", pulseLogInfo.toString());
} catch (Exception ex) { } catch (Exception ex) {
aapsLogger.warn(LTag.PUMPCOMM, "Failed to retrieve pulse log from the pod", ex); aapsLogger.warn(LTag.PUMPCOMM, "Failed to read pulse log", ex);
} }
try { try {
@ -573,7 +595,7 @@ public class OmnipodManager {
logStartingCommandExecution("verifyCommand"); logStartingCommandExecution("verifyCommand");
try { try {
return supplier.get(); return supplier.get();
} catch (Exception originalException) { } catch (OmnipodException originalException) {
if (isCertainFailure(originalException)) { if (isCertainFailure(originalException)) {
throw originalException; throw originalException;
} else { } else {
@ -586,18 +608,11 @@ public class OmnipodManager {
return statusResponse; return statusResponse;
} catch (NonceOutOfSyncException verificationException) { } catch (NonceOutOfSyncException verificationException) {
aapsLogger.error(LTag.PUMPCOMM, "Command resolved to FAILURE (CERTAIN_FAILURE)", verificationException); aapsLogger.info(LTag.PUMPCOMM, "Command resolved to FAILURE (CERTAIN_FAILURE)", verificationException);
originalException.setCertainFailure(true);
if (originalException instanceof OmnipodException) {
((OmnipodException) originalException).setCertainFailure(true);
throw originalException; throw originalException;
} else {
OmnipodException newException = new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, originalException);
newException.setCertainFailure(true);
throw newException;
}
} catch (Exception verificationException) { } catch (Exception verificationException) {
aapsLogger.error(LTag.PUMPCOMM, "Command unresolved (UNCERTAIN_FAILURE)", verificationException); aapsLogger.warn(LTag.PUMPCOMM, "Command unresolved (UNCERTAIN_FAILURE)", verificationException);
throw originalException; throw originalException;
} }
} }
@ -612,28 +627,38 @@ public class OmnipodManager {
} }
} }
private SetupActionResult verifySetupAction(PodProgressStatus expectedPodProgressStatus) { /**
SetupActionResult result = null; * @param expectedPodProgressStatus expected Pod progress status
* @return true if the Pod's progress status matches the expected status, otherwise false
* @throws PodProgressStatusVerificationFailedException in case reading the Pod status fails
*/
private boolean verifyPodProgressStatus(PodProgressStatus expectedPodProgressStatus) {
Boolean result = null;
Throwable lastException = null;
for (int i = 0; ACTION_VERIFICATION_TRIES > i; i++) { for (int i = 0; ACTION_VERIFICATION_TRIES > i; i++) {
try { try {
StatusResponse statusResponse = getPodStatus(); StatusResponse statusResponse = getPodStatus();
if (statusResponse.getPodProgressStatus().equals(expectedPodProgressStatus)) { if (statusResponse.getPodProgressStatus().equals(expectedPodProgressStatus)) {
result = new SetupActionResult(SetupActionResult.ResultType.SUCCESS); return true;
break;
} else { } else {
result = new SetupActionResult(SetupActionResult.ResultType.FAILURE) // result = false;
.podProgressStatus(statusResponse.getPodProgressStatus());
break;
} }
} catch (Exception ex) { } catch (Exception ex) {
result = new SetupActionResult(SetupActionResult.ResultType.VERIFICATION_FAILURE) // lastException = ex;
.exception(ex);
} }
} }
if (result != null) {
return result; return result;
} }
final Throwable ex = lastException;
throw new PodProgressStatusVerificationFailedException(expectedPodProgressStatus, ex);
}
/** /**
* @param expectedStatus expected delivery status * @param expectedStatus expected delivery status
* @param verificationCause the Exception causing us to verify the delivery status * @param verificationCause the Exception causing us to verify the delivery status
@ -664,14 +689,27 @@ public class OmnipodManager {
aapsLogger.debug(LTag.PUMPCOMM, "Command execution finished for action: " + action); aapsLogger.debug(LTag.PUMPCOMM, "Command execution finished for action: " + action);
} }
private static Duration calculateBolusDuration(double units, double deliveryRate) { private Duration calculateEstimatedBolusDuration(DateTime startTime, double units, double deliveryRateInUnitsPerSecond) {
// TODO take current (temp) basal into account if (!podStateManager.isPodActivationCompleted()) {
// Be aware that the Pod possibly doesn't have a Basal Schedule yet // No basal or temp basal is active yet
return Duration.standardSeconds((long) Math.ceil(units / deliveryRate)); return Duration.standardSeconds((long) Math.ceil(units / deliveryRateInUnitsPerSecond));
} }
public static Duration calculateBolusDuration(double units) { double pulseIntervalInSeconds = OmnipodConstants.POD_PULSE_SIZE / deliveryRateInUnitsPerSecond;
return calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); long numberOfPulses = Math.round(units / OmnipodConstants.POD_PULSE_SIZE);
double totalEstimatedDurationInSeconds = 0D;
for (int i = 0; numberOfPulses > i; i++) {
DateTime estimatedTimeAtPulse = startTime.plusMillis((int) (totalEstimatedDurationInSeconds * 1000));
double effectiveBasalRateAtPulse = podStateManager.getEffectiveBasalRateAt(estimatedTimeAtPulse);
double effectivePulsesPerHourAtPulse = effectiveBasalRateAtPulse / OmnipodConstants.POD_PULSE_SIZE;
double effectiveBasalPulsesPerSecondAtPulse = effectivePulsesPerHourAtPulse / 3600;
double effectiveBasalPulsesPerBolusPulse = pulseIntervalInSeconds * effectiveBasalPulsesPerSecondAtPulse;
totalEstimatedDurationInSeconds += pulseIntervalInSeconds * (1 + effectiveBasalPulsesPerBolusPulse);
}
return Duration.millis(Math.round(totalEstimatedDurationInSeconds * 1000));
} }
public static boolean isCertainFailure(Exception ex) { public static boolean isCertainFailure(Exception ex) {

View file

@ -84,6 +84,7 @@ public abstract class PodStateManager {
/** /**
* @return true if we have a Pod state and the Pod is running, meaning the activation process has completed and the Pod is not deactivated or in a fault state * @return true if we have a Pod state and the Pod is running, meaning the activation process has completed and the Pod is not deactivated or in a fault state
* This does not mean the Pod is actually delivering insulin, combine with {@link #isSuspended() isSuspended()} for that
*/ */
public final boolean isPodRunning() { public final boolean isPodRunning() {
return isPodInitialized() && getPodProgressStatus().isRunning(); return isPodInitialized() && getPodProgressStatus().isRunning();
@ -295,8 +296,21 @@ public abstract class PodStateManager {
} }
public final DateTime getTime() { public final DateTime getTime() {
DateTime now = DateTime.now(); DateTimeZone timeZone = getSafe(() -> podState.getTimeZone());
return now.withZone(getSafe(() -> podState.getTimeZone())); if (timeZone == null) {
return DateTime.now();
}
Duration timeActive = getSafe(() -> podState.getTimeActive());
DateTime activatedAt = getSafe(() -> podState.getActivatedAt());
DateTime lastUpdatedFromResponse = getSafe(() -> podState.getLastUpdatedFromResponse());
if (timeActive == null || activatedAt == null) {
return DateTime.now().withZone(timeZone);
}
return activatedAt.plus(timeActive).plus(new Duration(lastUpdatedFromResponse, DateTime.now()));
}
public final boolean timeDeviatesMoreThan(Duration duration) {
return new Duration(getTime(), DateTime.now().withZoneRetainFields(getSafe(() -> podState.getTimeZone()))).abs().isLongerThan(duration);
} }
public final DateTime getActivatedAt() { public final DateTime getActivatedAt() {
@ -304,22 +318,23 @@ public abstract class PodStateManager {
return activatedAt == null ? null : activatedAt.withZone(getSafe(() -> podState.getTimeZone())); return activatedAt == null ? null : activatedAt.withZone(getSafe(() -> podState.getTimeZone()));
} }
public final void updateActivatedAt() {
setAndStore(() -> podState.setActivatedAt(DateTime.now().withZone(getSafe(() -> podState.getTimeZone())).minus(getSafe(this::getTimeActive))));
}
public final Duration getTimeActive() {
return getSafe(() -> podState.getTimeActive());
}
public final DateTime getExpiresAt() { public final DateTime getExpiresAt() {
DateTime expiresAt = getSafe(() -> podState.getExpiresAt()); DateTime activatedAt = getSafe(() -> podState.getActivatedAt());
return expiresAt == null ? null : expiresAt.withZone(getSafe(() -> podState.getTimeZone())); return activatedAt == null ? null : activatedAt.withZone(getSafe(() -> podState.getTimeZone())).plus(OmnipodConstants.NOMINAL_POD_LIFE);
} }
public final PodProgressStatus getPodProgressStatus() { public final PodProgressStatus getPodProgressStatus() {
return getSafe(() -> podState.getPodProgressStatus()); return getSafe(() -> podState.getPodProgressStatus());
} }
public final void setPodProgressStatus(PodProgressStatus podProgressStatus) {
if (podProgressStatus == null) {
throw new IllegalArgumentException("Pod progress status can not be null");
}
setAndStore(() -> podState.setPodProgressStatus(podProgressStatus));
}
public final boolean isSuspended() { public final boolean isSuspended() {
return getSafe(() -> podState.isSuspended()); return getSafe(() -> podState.isSuspended());
} }
@ -392,6 +407,10 @@ public abstract class PodStateManager {
return certain == null || certain; return certain == null || certain;
} }
public final void setTempBasalCertain(boolean certain) {
setSafe(() -> podState.setTempBasalCertain(certain));
}
public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean certain) { public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean certain) {
setTempBasal(startTime, amount, duration, certain, true); setTempBasal(startTime, amount, duration, certain, true);
} }
@ -427,16 +446,50 @@ public abstract class PodStateManager {
} }
/** /**
* @return true when a Temp Basal is stored in the Pod Stated and this temp basal is currently running (based on start time and duration) * @return true when a Temp Basal is stored in the Pod State and this temp basal is currently running (based on start time and duration)
*/ */
public final boolean isTempBasalRunning() { public final boolean isTempBasalRunning() {
return isTempBasalRunningAt(DateTime.now());
}
/**
* @return true when a Temp Basal is stored in the Pod State and this temp basal is running at the given time (based on start time and duration)
*/
public final boolean isTempBasalRunningAt(DateTime time) {
if (hasTempBasal()) { if (hasTempBasal()) {
DateTime tempBasalEndTime = getTempBasalStartTime().plus(getTempBasalDuration()); DateTime tempBasalStartTime = getTempBasalStartTime();
return DateTime.now().isBefore(tempBasalEndTime); DateTime tempBasalEndTime = tempBasalStartTime.plus(getTempBasalDuration());
return (time.isAfter(tempBasalStartTime) || time.isEqual(tempBasalStartTime)) && time.isBefore(tempBasalEndTime);
} }
return false; return false;
} }
/**
* @return the current effective basal rate (taking Pod suspension, TBR, and basal profile into account)
*/
public final double getEffectiveBasalRate() {
if (isSuspended()) {
return 0d;
}
return getEffectiveBasalRateAt(DateTime.now());
}
/**
* @return the effective basal rate at the given time (taking TBR, and basal profile into account)
* Suspension is not taken into account as we don't keep historic data of that
*/
public final double getEffectiveBasalRateAt(DateTime time) {
BasalSchedule basalSchedule = getSafe(() -> podState.getBasalSchedule());
if (basalSchedule == null) {
return 0d;
}
if (isTempBasalRunningAt(time)) {
return getTempBasalAmount();
}
Duration offset = new Duration(time.withTimeAtStartOfDay(), time);
return basalSchedule.rateAt(offset);
}
public final DeliveryStatus getLastDeliveryStatus() { public final DeliveryStatus getLastDeliveryStatus() {
return getSafe(() -> podState.getLastDeliveryStatus()); return getSafe(() -> podState.getLastDeliveryStatus());
} }
@ -463,21 +516,16 @@ public abstract class PodStateManager {
public final void updateFromResponse(StatusUpdatableResponse statusResponse) { public final void updateFromResponse(StatusUpdatableResponse statusResponse) {
setSafe(() -> { setSafe(() -> {
if (podState.getActivatedAt() == null) { if (podState.getActivatedAt() == null) {
DateTime activatedAtCalculated = getTime().minus(statusResponse.getTimeActive()); DateTime activatedAtCalculated = DateTime.now().withZone(podState.getTimeZone()).minus(statusResponse.getTimeActive());
podState.setActivatedAt(activatedAtCalculated); podState.setActivatedAt(activatedAtCalculated);
} }
DateTime expiresAt = podState.getExpiresAt();
DateTime expiresAtCalculated = podState.getActivatedAt().plus(OmnipodConstants.NOMINAL_POD_LIFE);
if (expiresAt == null || expiresAtCalculated.isBefore(expiresAt) || expiresAtCalculated.isAfter(expiresAt.plusMinutes(1))) {
podState.setExpiresAt(expiresAtCalculated);
}
podState.setSuspended(statusResponse.getDeliveryStatus() == DeliveryStatus.SUSPENDED); podState.setSuspended(statusResponse.getDeliveryStatus() == DeliveryStatus.SUSPENDED);
podState.setActiveAlerts(statusResponse.getUnacknowledgedAlerts()); podState.setActiveAlerts(statusResponse.getUnacknowledgedAlerts());
podState.setLastDeliveryStatus(statusResponse.getDeliveryStatus()); podState.setLastDeliveryStatus(statusResponse.getDeliveryStatus());
podState.setReservoirLevel(statusResponse.getReservoirLevel()); podState.setReservoirLevel(statusResponse.getReservoirLevel());
podState.setTotalTicksDelivered(statusResponse.getTicksDelivered()); podState.setTotalTicksDelivered(statusResponse.getTicksDelivered());
podState.setPodProgressStatus(statusResponse.getPodProgressStatus()); podState.setPodProgressStatus(statusResponse.getPodProgressStatus());
podState.setTimeActive(statusResponse.getTimeActive());
if (statusResponse.getDeliveryStatus().isTbrRunning()) { if (statusResponse.getDeliveryStatus().isTbrRunning()) {
if (!isTempBasalCertain() && isTempBasalRunning()) { if (!isTempBasalCertain() && isTempBasalRunning()) {
podState.setTempBasalCertain(true); podState.setTempBasalCertain(true);
@ -577,7 +625,7 @@ public abstract class PodStateManager {
private DateTime lastUpdatedFromResponse; private DateTime lastUpdatedFromResponse;
private DateTimeZone timeZone; private DateTimeZone timeZone;
private DateTime activatedAt; private DateTime activatedAt;
private DateTime expiresAt; private Duration timeActive;
private PodInfoFaultEvent faultEvent; private PodInfoFaultEvent faultEvent;
private Double reservoirLevel; private Double reservoirLevel;
private Integer totalTicksDelivered; private Integer totalTicksDelivered;
@ -695,12 +743,12 @@ public abstract class PodStateManager {
this.activatedAt = activatedAt; this.activatedAt = activatedAt;
} }
DateTime getExpiresAt() { public Duration getTimeActive() {
return expiresAt; return timeActive;
} }
void setExpiresAt(DateTime expiresAt) { public void setTimeActive(Duration timeActive) {
this.expiresAt = expiresAt; this.timeActive = timeActive;
} }
PodInfoFaultEvent getFaultEvent() { PodInfoFaultEvent getFaultEvent() {
@ -881,7 +929,7 @@ public abstract class PodStateManager {
", lastUpdatedFromResponse=" + lastUpdatedFromResponse + ", lastUpdatedFromResponse=" + lastUpdatedFromResponse +
", timeZone=" + timeZone + ", timeZone=" + timeZone +
", activatedAt=" + activatedAt + ", activatedAt=" + activatedAt +
", expiresAt=" + expiresAt + ", timeActive=" + timeActive +
", faultEvent=" + faultEvent + ", faultEvent=" + faultEvent +
", reservoirLevel=" + reservoirLevel + ", reservoirLevel=" + reservoirLevel +
", totalTicksDelivered=" + totalTicksDelivered + ", totalTicksDelivered=" + totalTicksDelivered +
@ -899,7 +947,7 @@ public abstract class PodStateManager {
", tempBasalStartTime=" + tempBasalStartTime + ", tempBasalStartTime=" + tempBasalStartTime +
", tempBasalDuration=" + tempBasalDuration + ", tempBasalDuration=" + tempBasalDuration +
", tempBasalCertain=" + tempBasalCertain + ", tempBasalCertain=" + tempBasalCertain +
", expirationAlertHoursBeforeShutdown=" + expirationAlertTimeBeforeShutdown + ", expirationAlertTimeBeforeShutdown=" + expirationAlertTimeBeforeShutdown +
", lowReservoirAlertUnits=" + lowReservoirAlertUnits + ", lowReservoirAlertUnits=" + lowReservoirAlertUnits +
", configuredAlerts=" + configuredAlerts + ", configuredAlerts=" + configuredAlerts +
'}'; '}';

View file

@ -1,61 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.manager;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
public class SetupActionResult {
private final ResultType resultType;
private String message;
private Exception exception;
private PodProgressStatus podProgressStatus;
public SetupActionResult(ResultType resultType) {
this.resultType = resultType;
}
public SetupActionResult message(String message) {
this.message = message;
return this;
}
public SetupActionResult exception(Exception ex) {
exception = ex;
return this;
}
public SetupActionResult podProgressStatus(PodProgressStatus podProgressStatus) {
this.podProgressStatus = podProgressStatus;
return this;
}
public ResultType getResultType() {
return resultType;
}
public String getMessage() {
return message;
}
public Exception getException() {
return exception;
}
public PodProgressStatus getPodProgressStatus() {
return podProgressStatus;
}
public enum ResultType {
SUCCESS(true),
VERIFICATION_FAILURE(false),
FAILURE(false);
private final boolean success;
ResultType(boolean success) {
this.success = success;
}
public boolean isSuccess() {
return success;
}
}
}

View file

@ -18,12 +18,12 @@ public class AlertConfigurationUtil {
public static AlertConfiguration createExpirationAdvisoryAlertConfiguration(boolean active, Duration timeUntilAlert, Duration duration) { public static AlertConfiguration createExpirationAdvisoryAlertConfiguration(boolean active, Duration timeUntilAlert, Duration duration) {
return new AlertConfiguration(AlertType.EXPIRATION_ADVISORY_ALERT, AlertSlot.SLOT7, active, false, duration, return new AlertConfiguration(AlertType.EXPIRATION_ADVISORY_ALERT, AlertSlot.SLOT7, active, false, duration,
new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_15_MINUTES); new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_MINUTE_FOR_3_MINUTES_REPEAT_EVERY_15_MINUTES);
} }
public static AlertConfiguration createShutdownImminentAlertConfiguration(Duration timeUntilAlert) { public static AlertConfiguration createShutdownImminentAlertConfiguration(Duration timeUntilAlert) {
return new AlertConfiguration(AlertType.SHUTDOWN_IMMINENT_ALARM, AlertSlot.SLOT2, true, false, Duration.ZERO, return new AlertConfiguration(AlertType.SHUTDOWN_IMMINENT_ALARM, AlertSlot.SLOT2, true, false, Duration.ZERO,
new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_MINUTE_FOR_3_MINUTES_REPEAT_EVERY_15_MINUTES); new TimerAlertTrigger(timeUntilAlert), BeepType.BIP_BEEP_BIP_BEEP_BIP_BEEP_BIP_BEEP, BeepRepeat.EVERY_15_MINUTES);
} }
public static AlertConfiguration createAutoOffAlertConfiguration(boolean active, Duration countdownDuration) { public static AlertConfiguration createAutoOffAlertConfiguration(boolean active, Duration countdownDuration) {

View file

@ -2,10 +2,13 @@ package info.nightscout.androidaps.plugins.pump.omnipod.manager;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Duration; import org.joda.time.Duration;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Supplier;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -14,15 +17,19 @@ import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.OmnipodHistoryRecord; import info.nightscout.androidaps.db.OmnipodHistoryRecord;
import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.Event; import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.ProfileFunction;
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;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
@ -32,10 +39,9 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.R; import info.nightscout.androidaps.plugins.pump.omnipod.R;
import info.nightscout.androidaps.plugins.pump.omnipod.data.ActiveBolus; import info.nightscout.androidaps.plugins.pump.omnipod.data.ActiveBolus;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys; import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodHistoryEntryType; import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodHistoryEntryType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodInitActionType;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodInitReceiver;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse;
@ -44,12 +50,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.Deliver
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalScheduleEntry; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalScheduleEntry;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandInitializationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommunicationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CrcMismatchException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CrcMismatchException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalDeliveryStatusException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalDeliveryStatusException;
@ -66,16 +70,19 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NotEnoug
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodReturnedErrorResponseException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodReturnedErrorResponseException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkInterruptedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkTimeoutException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkUnexpectedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkUnreachableException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.OmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.OmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.SetupActionResult;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil; import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil; import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodAlertUtil;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.SingleSubject; import io.reactivex.subjects.SingleSubject;
@Singleton @Singleton
@ -92,6 +99,8 @@ public class AapsOmnipodManager {
private final OmnipodManager delegate; private final OmnipodManager delegate;
private final DatabaseHelperInterface databaseHelper; private final DatabaseHelperInterface databaseHelper;
private final OmnipodAlertUtil omnipodAlertUtil; private final OmnipodAlertUtil omnipodAlertUtil;
private final NSUpload nsUpload;
private final ProfileFunction profileFunction;
private boolean basalBeepsEnabled; private boolean basalBeepsEnabled;
private boolean bolusBeepsEnabled; private boolean bolusBeepsEnabled;
@ -112,20 +121,22 @@ public class AapsOmnipodManager {
HasAndroidInjector injector, HasAndroidInjector injector,
ActivePluginProvider activePlugin, ActivePluginProvider activePlugin,
DatabaseHelperInterface databaseHelper, DatabaseHelperInterface databaseHelper,
OmnipodAlertUtil omnipodAlertUtil) { OmnipodAlertUtil omnipodAlertUtil,
if (podStateManager == null) { NSUpload nsUpload,
throw new IllegalArgumentException("Pod state manager can not be null"); ProfileFunction profileFunction
} ) {
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.aapsOmnipodUtil = aapsOmnipodUtil; this.aapsOmnipodUtil = aapsOmnipodUtil;
this.aapsLogger = aapsLogger; this.aapsLogger = aapsLogger;
this.rxBus = rxBus; this.rxBus = rxBus;
this.sp = sp;
this.resourceHelper = resourceHelper; this.resourceHelper = resourceHelper;
this.injector = injector; this.injector = injector;
this.activePlugin = activePlugin; this.activePlugin = activePlugin;
this.databaseHelper = databaseHelper; this.databaseHelper = databaseHelper;
this.sp = sp;
this.omnipodAlertUtil = omnipodAlertUtil; this.omnipodAlertUtil = omnipodAlertUtil;
this.nsUpload = nsUpload;
this.profileFunction = profileFunction;
delegate = new OmnipodManager(aapsLogger, sp, communicationService, podStateManager); delegate = new OmnipodManager(aapsLogger, sp, communicationService, podStateManager);
@ -142,141 +153,159 @@ public class AapsOmnipodManager {
timeChangeEventEnabled = sp.getBoolean(OmnipodStorageKeys.Preferences.TIME_CHANGE_EVENT_ENABLED, true); timeChangeEventEnabled = sp.getBoolean(OmnipodStorageKeys.Preferences.TIME_CHANGE_EVENT_ENABLED, true);
} }
public PumpEnactResult pairAndPrime(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver) { public PumpEnactResult initializePod() {
if (podInitActionType != PodInitActionType.PAIR_AND_PRIME_WIZARD_STEP) { PumpEnactResult result = new PumpEnactResult(injector);
return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name())); try {
Boolean res = executeCommand(delegate::pairAndPrime)
.blockingGet();
result.success(res).enacted(res);
if (!res) {
result.comment(R.string.omnipod_error_failed_to_initialize_pod);
}
} catch (Exception ex) {
result.success(false).enacted(false).comment(translateException(ex));
} }
try { addToHistory(System.currentTimeMillis(), PodHistoryEntryType.INITIALIZE_POD, result.comment, result.success);
Disposable disposable = delegate.pairAndPrime().subscribe(res -> //
handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), null));
return new PumpEnactResult(injector).success(true).enacted(true); return result;
} catch (Exception ex) { }
String comment = handleAndTranslateException(ex);
podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment); public PumpEnactResult insertCannula(Profile profile) {
addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.PAIR_AND_PRIME, comment); if (profile == null) {
String comment = getStringResource(R.string.omnipod_error_set_initial_basal_schedule_no_profile);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} }
}
public PumpEnactResult setInitialBasalScheduleAndInsertCannula(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver, Profile profile) { PumpEnactResult result = new PumpEnactResult(injector);
if (podInitActionType != PodInitActionType.FILL_CANNULA_SET_BASAL_PROFILE_WIZARD_STEP) {
return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name()));
}
try { try {
BasalSchedule basalSchedule; BasalSchedule basalSchedule = mapProfileToBasalSchedule(profile);
try {
basalSchedule = mapProfileToBasalSchedule(profile); Boolean res = executeCommand(() -> delegate.insertCannula(basalSchedule, omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown(), omnipodAlertUtil.getLowReservoirAlertUnits())) //
.blockingGet();
result.success(res).enacted(res);
if (!res) {
result.comment(R.string.omnipod_error_failed_to_insert_cannula);
}
} catch (Exception ex) { } catch (Exception ex) {
throw new CommandInitializationException("Basal profile mapping failed", ex); result.success(false).enacted(false).comment(translateException(ex));
} }
Disposable disposable = delegate.insertCannula(basalSchedule, omnipodAlertUtil.getExpirationReminderTimeBeforeShutdown(), omnipodAlertUtil.getLowReservoirAlertUnits()).subscribe(res -> // addToHistory(System.currentTimeMillis(), PodHistoryEntryType.INSERT_CANNULA, result.comment, result.success);
handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), profile));
rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)); if (result.success) {
uploadCareportalEvent(System.currentTimeMillis() - 2000, CareportalEvent.PUMPBATTERYCHANGE);
uploadCareportalEvent(System.currentTimeMillis() - 1000, CareportalEvent.INSULINCHANGE);
uploadCareportalEvent(System.currentTimeMillis(), CareportalEvent.SITECHANGE);
sendEvent(new EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED));
cancelSuspendedFakeTbrIfExists(); cancelSuspendedFakeTbrIfExists();
return new PumpEnactResult(injector).success(true).enacted(true);
} catch (Exception ex) {
String comment = handleAndTranslateException(ex);
podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment);
addFailureToHistory(PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} }
return result;
} }
public PumpEnactResult configureAlerts(List<AlertConfiguration> alertConfigurations) { public PumpEnactResult configureAlerts(List<AlertConfiguration> alertConfigurations) {
try { try {
StatusResponse statusResponse = delegate.configureAlerts(alertConfigurations); executeCommand(() -> delegate.configureAlerts(alertConfigurations));
} catch (Exception ex) {
String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
}
addSuccessToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, alertConfigurations); addSuccessToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, alertConfigurations);
return new PumpEnactResult(injector).success(true).enacted(false); return new PumpEnactResult(injector).success(true).enacted(false);
} catch (Exception ex) {
String comment = handleAndTranslateException(ex);
addFailureToHistory(PodHistoryEntryType.CONFIGURE_ALERTS, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
}
} }
public PumpEnactResult getPodStatus() { public PumpEnactResult getPodStatus() {
StatusResponse statusResponse;
try { try {
StatusResponse statusResponse = delegate.getPodStatus(); statusResponse = executeCommand(delegate::getPodStatus);
addSuccessToHistory(PodHistoryEntryType.GET_POD_STATUS, statusResponse);
return new PumpEnactResult(injector).success(true).enacted(false);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.GET_POD_STATUS, comment); addFailureToHistory(PodHistoryEntryType.GET_POD_STATUS, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
}
} }
public PumpEnactResult deactivatePod(PodInitReceiver podInitReceiver) { addSuccessToHistory(PodHistoryEntryType.GET_POD_STATUS, statusResponse);
return new PumpEnactResult(injector).success(true).enacted(false);
}
public PumpEnactResult deactivatePod() {
try { try {
delegate.deactivatePod(); executeCommand(delegate::deactivatePod);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
podInitReceiver.returnInitTaskStatus(PodInitActionType.DEACTIVATE_POD_WIZARD_STEP, false, comment); addFailureToHistory(PodHistoryEntryType.DEACTIVATE_POD, errorMessage);
addFailureToHistory(PodHistoryEntryType.DEACTIVATE_POD, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} }
addSuccessToHistory(PodHistoryEntryType.DEACTIVATE_POD, null); addSuccessToHistory(PodHistoryEntryType.DEACTIVATE_POD, null);
createSuspendedFakeTbrIfNotExists(); createSuspendedFakeTbrIfNotExists();
podInitReceiver.returnInitTaskStatus(PodInitActionType.DEACTIVATE_POD_WIZARD_STEP, true, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
public PumpEnactResult setBasalProfile(Profile profile) { public PumpEnactResult setBasalProfile(Profile profile, boolean showNotifications) {
if (profile == null) {
String note = getStringResource(R.string.omnipod_error_failed_to_set_profile_empty_profile);
showNotification(Notification.FAILED_UDPATE_PROFILE, note, Notification.URGENT, R.raw.boluserror);
return new PumpEnactResult(injector).success(false).enacted(false).comment(note);
}
PodHistoryEntryType historyEntryType = podStateManager.isSuspended() ? PodHistoryEntryType.RESUME_DELIVERY : PodHistoryEntryType.SET_BASAL_SCHEDULE; PodHistoryEntryType historyEntryType = podStateManager.isSuspended() ? PodHistoryEntryType.RESUME_DELIVERY : PodHistoryEntryType.SET_BASAL_SCHEDULE;
try { try {
BasalSchedule basalSchedule; BasalSchedule basalSchedule = mapProfileToBasalSchedule(profile);
try { executeCommand(() -> delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled()));
basalSchedule = mapProfileToBasalSchedule(profile); } catch (CommandFailedAfterChangingDeliveryStatusException ex) {
} catch (Exception ex) { createSuspendedFakeTbrIfNotExists();
throw new CommandInitializationException("Basal profile mapping failed", ex); if (showNotifications) {
showNotification(Notification.FAILED_UDPATE_PROFILE, getStringResource(R.string.omnipod_error_set_basal_failed_delivery_suspended), Notification.URGENT, R.raw.boluserror);
}
String errorMessage = translateException(ex.getCause());
addFailureToHistory(historyEntryType, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} catch (DeliveryStatusVerificationFailedException ex) {
if (showNotifications) {
String note;
if (ex.getExpectedStatus() == DeliveryStatus.SUSPENDED) {
// Happened when suspending delivery before setting the new profile
note = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_might_be_suspended);
} else {
// Happened when setting the new profile (after suspending delivery)
note = getStringResource(R.string.omnipod_error_set_basal_might_have_failed_delivery_might_be_suspended);
}
showNotification(Notification.FAILED_UDPATE_PROFILE, note, Notification.URGENT, R.raw.boluserror);
}
String errorMessage = translateException(ex.getCause());
addFailureToHistory(historyEntryType, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} catch (Exception ex) {
if (showNotifications) {
showNotification(Notification.FAILED_UDPATE_PROFILE, getStringResource(R.string.omnipod_error_set_basal_failed), Notification.URGENT, R.raw.boluserror);
}
String errorMessage = translateException(ex);
addFailureToHistory(historyEntryType, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled());
if (historyEntryType == PodHistoryEntryType.RESUME_DELIVERY) { if (historyEntryType == PodHistoryEntryType.RESUME_DELIVERY) {
cancelSuspendedFakeTbrIfExists(); cancelSuspendedFakeTbrIfExists();
sendEvent(new EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED));
} }
addSuccessToHistory(historyEntryType, profile.getBasalValues()); addSuccessToHistory(historyEntryType, profile.getBasalValues());
} catch (CommandFailedAfterChangingDeliveryStatusException ex) {
createSuspendedFakeTbrIfNotExists();
String comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_suspended);
showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror);
addFailureToHistory(historyEntryType, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} catch (DeliveryStatusVerificationFailedException ex) {
String comment;
if (ex.getExpectedStatus() == DeliveryStatus.SUSPENDED) {
// Happened when suspending delivery before setting the new profile
comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_might_be_suspended);
} else {
// Happened when setting the new profile (after suspending delivery)
comment = getStringResource(R.string.omnipod_error_set_basal_might_have_failed_delivery_might_be_suspended);
}
showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror);
addFailureToHistory(historyEntryType, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} catch (Exception ex) {
String comment = handleAndTranslateException(ex);
showNotification(Notification.FAILED_UDPATE_PROFILE, comment, Notification.URGENT, R.raw.boluserror);
addFailureToHistory(historyEntryType, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
}
rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED)); if (showNotifications) {
showNotification(Notification.PROFILE_SET_OK, showNotification(Notification.PROFILE_SET_OK, resourceHelper.gs(R.string.profile_set_ok), Notification.INFO, null);
resourceHelper.gs(R.string.profile_set_ok), }
Notification.INFO, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
@ -284,10 +313,13 @@ public class AapsOmnipodManager {
public PumpEnactResult discardPodState() { public PumpEnactResult discardPodState() {
podStateManager.discardState(); podStateManager.discardState();
addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.RESET_POD_STATE, null); addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.DISCARD_POD, null);
createSuspendedFakeTbrIfNotExists(); createSuspendedFakeTbrIfNotExists();
sendEvent(new EventOmnipodPumpValuesChanged());
rxBus.send(new EventRefreshOverview("Omnipod command: " + OmnipodCommandType.DISCARD_POD, false));
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
@ -298,27 +330,27 @@ public class AapsOmnipodManager {
Date bolusStarted; Date bolusStarted;
try { try {
bolusCommandResult = delegate.bolus(PumpType.Insulet_Omnipod.determineCorrectBolusSize(detailedBolusInfo.insulin), beepsEnabled, beepsEnabled, detailedBolusInfo.isSMB ? null : bolusCommandResult = executeCommand(() -> delegate.bolus(PumpType.Insulet_Omnipod.determineCorrectBolusSize(detailedBolusInfo.insulin), beepsEnabled, beepsEnabled, detailedBolusInfo.isSMB ? null :
(estimatedUnitsDelivered, percentage) -> { (estimatedUnitsDelivered, percentage) -> {
EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE; EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE;
progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, detailedBolusInfo.insulin)); progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, detailedBolusInfo.insulin));
progressUpdateEvent.setPercent(percentage); progressUpdateEvent.setPercent(percentage);
sendEvent(progressUpdateEvent); sendEvent(progressUpdateEvent);
}); }));
bolusStarted = new Date(); bolusStarted = new Date();
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.SET_BOLUS, comment); addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.SET_BOLUS, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
if (OmnipodManager.CommandDeliveryStatus.UNCERTAIN_FAILURE.equals(bolusCommandResult.getCommandDeliveryStatus())) { if (OmnipodManager.CommandDeliveryStatus.UNCERTAIN_FAILURE.equals(bolusCommandResult.getCommandDeliveryStatus())) {
// For safety reasons, we treat this as a bolus that has successfully been delivered, in order to prevent insulin overdose // For safety reasons, we treat this as a bolus that has successfully been delivered, in order to prevent insulin overdose
if (detailedBolusInfo.isSMB) { if (detailedBolusInfo.isSMB) {
showNotification(getStringResource(R.string.omnipod_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, R.raw.boluserror); showNotification(getStringResource(R.string.omnipod_error_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, R.raw.boluserror);
} else { } else {
showNotification(getStringResource(R.string.omnipod_bolus_failed_uncertain), Notification.URGENT, R.raw.boluserror); showNotification(getStringResource(R.string.omnipod_error_bolus_failed_uncertain), Notification.URGENT, R.raw.boluserror);
} }
} }
@ -378,7 +410,7 @@ public class AapsOmnipodManager {
aapsLogger.debug(LTag.PUMP, "Bolus command successfully executed. Proceeding bolus cancellation"); aapsLogger.debug(LTag.PUMP, "Bolus command successfully executed. Proceeding bolus cancellation");
} else { } else {
aapsLogger.debug(LTag.PUMP, "Not cancelling bolus: bolus command failed"); aapsLogger.debug(LTag.PUMP, "Not cancelling bolus: bolus command failed");
String comment = getStringResource(R.string.omnipod_bolus_did_not_succeed); String comment = getStringResource(R.string.omnipod_error_bolus_did_not_succeed);
addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, comment); addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, comment);
return new PumpEnactResult(injector).success(true).enacted(false).comment(comment); return new PumpEnactResult(injector).success(true).enacted(false).comment(comment);
} }
@ -387,19 +419,19 @@ public class AapsOmnipodManager {
String comment = null; String comment = null;
for (int i = 1; delegate.hasActiveBolus(); i++) { for (int i = 1; delegate.hasActiveBolus(); i++) {
aapsLogger.debug(LTag.PUMP, "Attempting to cancel bolus (#{})", i); aapsLogger.debug(LTag.PUMP, "Attempting to cancel bolus (#{})", i);
try { try {
delegate.cancelBolus(isBolusBeepsEnabled()); executeCommand(() -> delegate.cancelBolus(isBolusBeepsEnabled()));
aapsLogger.debug(LTag.PUMP, "Successfully cancelled bolus", i); aapsLogger.debug(LTag.PUMP, "Successfully cancelled bolus", i);
addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, null); addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} catch (PodFaultException ex) { } catch (PodFaultException ex) {
aapsLogger.debug(LTag.PUMP, "Successfully cancelled bolus (implicitly because of a Pod Fault)"); aapsLogger.debug(LTag.PUMP, "Successfully cancelled bolus (implicitly because of a Pod Fault)");
showPodFaultNotification(ex.getFaultEvent().getFaultEventCode());
addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, null); addSuccessToHistory(System.currentTimeMillis(), PodHistoryEntryType.CANCEL_BOLUS, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} catch (Exception ex) { } catch (Exception ex) {
aapsLogger.debug(LTag.PUMP, "Failed to cancel bolus", ex); aapsLogger.debug(LTag.PUMP, "Failed to cancel bolus", ex);
comment = handleAndTranslateException(ex); comment = translateException(ex);
} }
} }
@ -410,19 +442,18 @@ public class AapsOmnipodManager {
public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) { public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) {
boolean beepsEnabled = isTbrBeepsEnabled(); boolean beepsEnabled = isTbrBeepsEnabled();
try { try {
delegate.setTemporaryBasal(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), beepsEnabled, beepsEnabled); executeCommand(() -> delegate.setTemporaryBasal(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), beepsEnabled, beepsEnabled));
} catch (CommandFailedAfterChangingDeliveryStatusException ex) { } catch (CommandFailedAfterChangingDeliveryStatusException ex) {
String comment = getStringResource(R.string.omnipod_cancelled_old_tbr_failed_to_set_new); String errorMessage = translateException(ex.getCause());
addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, errorMessage);
showNotification(comment, Notification.NORMAL, null); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} catch (DeliveryStatusVerificationFailedException ex) { } catch (DeliveryStatusVerificationFailedException ex) {
String comment; String errorMessage = translateException(ex.getCause());
String note;
if (ex.getExpectedStatus() == DeliveryStatus.TEMP_BASAL_RUNNING) { if (ex.getExpectedStatus() == DeliveryStatus.TEMP_BASAL_RUNNING) {
// Happened after cancelling the old TBR, when attempting to set new TBR // Happened after cancelling the old TBR, when attempting to set new TBR
note = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_cancelled_new_might_have_failed);
comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_cancelled_new_might_have_failed);
long pumpId = addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment);
// Assume that setting the temp basal succeeded here, because in case it didn't succeed, // Assume that setting the temp basal succeeded here, because in case it didn't succeed,
// The next StatusResponse that we receive will allow us to recover from the wrong state // The next StatusResponse that we receive will allow us to recover from the wrong state
@ -430,19 +461,20 @@ public class AapsOmnipodManager {
// If we would assume that the TBR didn't succeed, we couldn't properly recover upon the next StatusResponse, // If we would assume that the TBR didn't succeed, we couldn't properly recover upon the next StatusResponse,
// as we could only see that the Pod is running a TBR, but we don't know the rate and duration as // as we could only see that the Pod is running a TBR, but we don't know the rate and duration as
// the Pod doesn't provide this information // the Pod doesn't provide this information
long pumpId = addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, errorMessage);
addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair); addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair);
} else { } else {
// Happened when attempting to cancel the old TBR // Happened when attempting to cancel the old TBR
comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled); note = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled);
addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, errorMessage);
} }
showNotification(note, Notification.URGENT, R.raw.boluserror);
showNotification(comment, Notification.URGENT, R.raw.boluserror); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
long pumpId = addSuccessToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair); long pumpId = addSuccessToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair);
@ -454,11 +486,14 @@ public class AapsOmnipodManager {
public PumpEnactResult cancelTemporaryBasal() { public PumpEnactResult cancelTemporaryBasal() {
try { try {
delegate.cancelTemporaryBasal(isTbrBeepsEnabled()); executeCommand(() -> delegate.cancelTemporaryBasal(isTbrBeepsEnabled()));
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); if (ex instanceof OmnipodException && !((OmnipodException) ex).isCertainFailure()) {
addFailureToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, comment); showNotification(getStringResource(R.string.omnipod_error_cancel_temp_basal_failed_uncertain), Notification.URGENT, R.raw.boluserror);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); }
String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
long pumpId = addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null); long pumpId = addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null);
@ -476,59 +511,63 @@ public class AapsOmnipodManager {
public PumpEnactResult acknowledgeAlerts() { public PumpEnactResult acknowledgeAlerts() {
try { try {
delegate.acknowledgeAlerts(); executeCommand(delegate::acknowledgeAlerts);
addSuccessToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, comment); addFailureToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
addSuccessToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
public PumpEnactResult suspendDelivery() { public PumpEnactResult suspendDelivery() {
try { try {
delegate.suspendDelivery(isBasalBeepsEnabled()); executeCommand(() -> delegate.suspendDelivery(isBasalBeepsEnabled()));
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, comment); addFailureToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
addSuccessToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, null); addSuccessToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, null);
createSuspendedFakeTbrIfNotExists(); createSuspendedFakeTbrIfNotExists();
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
// Updates the pods current time based on the device timezone and the pod's time zone // Updates the pods current time based on the device timezone and the pod's time zone
public PumpEnactResult setTime() { public PumpEnactResult setTime(boolean showNotifications) {
try { try {
delegate.setTime(isBasalBeepsEnabled()); executeCommand(() -> delegate.setTime(isBasalBeepsEnabled()));
addSuccessToHistory(PodHistoryEntryType.SET_TIME, null);
} catch (CommandFailedAfterChangingDeliveryStatusException ex) { } catch (CommandFailedAfterChangingDeliveryStatusException ex) {
createSuspendedFakeTbrIfNotExists(); createSuspendedFakeTbrIfNotExists();
String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_suspended); if (showNotifications) {
showNotification(comment, Notification.URGENT, R.raw.boluserror); showNotification(getStringResource(R.string.omnipod_error_set_time_failed_delivery_suspended), Notification.URGENT, R.raw.boluserror);
addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); }
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); String errorMessage = translateException(ex.getCause());
addFailureToHistory(PodHistoryEntryType.SET_TIME, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} catch (DeliveryStatusVerificationFailedException ex) { } catch (DeliveryStatusVerificationFailedException ex) {
String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_might_be_suspended); if (showNotifications) {
showNotification(comment, Notification.URGENT, R.raw.boluserror); showNotification(getStringResource(R.string.omnipod_error_set_time_failed_delivery_might_be_suspended), Notification.URGENT, R.raw.boluserror);
addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); }
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); String errorMessage = translateException(ex.getCause());
addFailureToHistory(PodHistoryEntryType.SET_TIME, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String errorMessage = translateException(ex);
addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); addFailureToHistory(PodHistoryEntryType.SET_TIME, errorMessage);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(errorMessage);
} }
addSuccessToHistory(PodHistoryEntryType.SET_TIME, null);
return new PumpEnactResult(injector).success(true).enacted(true); return new PumpEnactResult(injector).success(true).enacted(true);
} }
public PodInfoRecentPulseLog readPulseLog() { public PodInfoRecentPulseLog readPulseLog() {
PodInfoResponse response = delegate.getPodInfo(PodInfoType.RECENT_PULSE_LOG); PodInfoResponse response = executeCommand(() -> delegate.getPodInfo(PodInfoType.RECENT_PULSE_LOG));
return (PodInfoRecentPulseLog) response.getPodInfo(); return (PodInfoRecentPulseLog) response.getPodInfo();
} }
@ -660,7 +699,8 @@ public class AapsOmnipodManager {
return addSuccessToHistory(System.currentTimeMillis(), entryType, data); return addSuccessToHistory(System.currentTimeMillis(), entryType, data);
} }
private long addSuccessToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { private long addSuccessToHistory(long requestTime, PodHistoryEntryType entryType, Object
data) {
return addToHistory(requestTime, entryType, data, true); return addToHistory(requestTime, entryType, data, true);
} }
@ -668,11 +708,13 @@ public class AapsOmnipodManager {
return addFailureToHistory(System.currentTimeMillis(), entryType, data); return addFailureToHistory(System.currentTimeMillis(), entryType, data);
} }
private long addFailureToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { private long addFailureToHistory(long requestTime, PodHistoryEntryType entryType, Object
data) {
return addToHistory(requestTime, entryType, data, false); return addToHistory(requestTime, entryType, data, false);
} }
private long addToHistory(long requestTime, PodHistoryEntryType entryType, Object data, boolean success) { private long addToHistory(long requestTime, PodHistoryEntryType entryType, Object data,
boolean success) {
OmnipodHistoryRecord omnipodHistoryRecord = new OmnipodHistoryRecord(requestTime, entryType.getCode()); OmnipodHistoryRecord omnipodHistoryRecord = new OmnipodHistoryRecord(requestTime, entryType.getCode());
if (data != null) { if (data != null) {
@ -691,88 +733,91 @@ public class AapsOmnipodManager {
return omnipodHistoryRecord.getPumpId(); return omnipodHistoryRecord.getPumpId();
} }
private void handleSetupActionResult(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver, SetupActionResult res, long time, Profile profile) { private void executeCommand(Runnable runnable) {
String comment = null; try {
switch (res.getResultType()) { runnable.run();
case FAILURE: { } catch (Exception ex) {
aapsLogger.error(LTag.PUMP, "Setup action failed: illegal setup progress: {}", res.getPodProgressStatus()); handleException(ex);
comment = getStringResource(R.string.omnipod_driver_error_invalid_progress_state, res.getPodProgressStatus()); throw ex;
} }
break;
case VERIFICATION_FAILURE: {
aapsLogger.error(LTag.PUMP, "Setup action verification failed: caught exception", res.getException());
comment = getStringResource(R.string.omnipod_driver_error_setup_action_verification_failed);
}
break;
} }
if (podInitActionType == PodInitActionType.PAIR_AND_PRIME_WIZARD_STEP) { private <T> T executeCommand(Supplier<T> supplier) {
addToHistory(time, PodHistoryEntryType.PAIR_AND_PRIME, comment, res.getResultType().isSuccess()); try {
return supplier.get();
} catch (Exception ex) {
handleException(ex);
throw ex;
}
}
private void handleException(Exception ex) {
if (ex instanceof OmnipodException) {
aapsLogger.error(LTag.PUMP, String.format("Caught OmnipodException[certainFailure=%s] from OmnipodManager", ((OmnipodException) ex).isCertainFailure()), ex);
if (ex instanceof PodFaultException) {
FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode();
if (!(faultEventCode == FaultEventCode.NO_FAULTS && podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus() == PodProgressStatus.ACTIVATION_TIME_EXCEEDED)) {
showPodFaultNotification(faultEventCode);
}
}
} else { } else {
addToHistory(time, PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, res.getResultType().isSuccess() ? profile.getBasalValues() : comment, res.getResultType().isSuccess()); aapsLogger.error(LTag.PUMP, "Caught an unexpected non-OmnipodException from OmnipodManager", ex);
}
} }
podInitReceiver.returnInitTaskStatus(podInitActionType, res.getResultType().isSuccess(), comment); public String translateException(Throwable ex) {
}
private String handleAndTranslateException(Exception ex) {
String comment; String comment;
if (ex instanceof OmnipodException) { if (ex instanceof CrcMismatchException) {
if (ex instanceof ActionInitializationException || ex instanceof CommandInitializationException) { comment = getStringResource(R.string.omnipod_error_crc_mismatch);
comment = getStringResource(R.string.omnipod_driver_error_invalid_parameters);
} else if (ex instanceof CommunicationException) {
if (((CommunicationException) ex).getType() == CommunicationException.Type.TIMEOUT) {
comment = getStringResource(R.string.omnipod_driver_error_communication_failed_timeout);
} else {
comment = getStringResource(R.string.omnipod_driver_error_communication_failed_unexpected_exception);
}
} else if (ex instanceof CrcMismatchException) {
comment = getStringResource(R.string.omnipod_driver_error_crc_mismatch);
} else if (ex instanceof IllegalPacketTypeException) { } else if (ex instanceof IllegalPacketTypeException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_packet_type); comment = getStringResource(R.string.omnipod_error_invalid_packet_type);
} else if (ex instanceof IllegalPodProgressException || ex instanceof IllegalDeliveryStatusException) { } else if (ex instanceof IllegalPodProgressException || ex instanceof IllegalDeliveryStatusException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_progress_state); comment = getStringResource(R.string.omnipod_error_invalid_progress_state);
} else if (ex instanceof IllegalVersionResponseTypeException) { } else if (ex instanceof IllegalVersionResponseTypeException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_response); comment = getStringResource(R.string.omnipod_error_invalid_response);
} else if (ex instanceof IllegalResponseException) { } else if (ex instanceof IllegalResponseException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_response); comment = getStringResource(R.string.omnipod_error_invalid_response);
} else if (ex instanceof IllegalMessageSequenceNumberException) { } else if (ex instanceof IllegalMessageSequenceNumberException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_message_sequence_number); comment = getStringResource(R.string.omnipod_error_invalid_message_sequence_number);
} else if (ex instanceof IllegalMessageAddressException) { } else if (ex instanceof IllegalMessageAddressException) {
comment = getStringResource(R.string.omnipod_driver_error_invalid_message_address); comment = getStringResource(R.string.omnipod_error_invalid_message_address);
} else if (ex instanceof MessageDecodingException) { } else if (ex instanceof MessageDecodingException) {
comment = getStringResource(R.string.omnipod_driver_error_message_decoding_failed); comment = getStringResource(R.string.omnipod_error_message_decoding_failed);
} else if (ex instanceof NonceOutOfSyncException) { } else if (ex instanceof NonceOutOfSyncException) {
comment = getStringResource(R.string.omnipod_driver_error_nonce_out_of_sync); comment = getStringResource(R.string.omnipod_error_nonce_out_of_sync);
} else if (ex instanceof NonceResyncException) { } else if (ex instanceof NonceResyncException) {
comment = getStringResource(R.string.omnipod_driver_error_nonce_resync_failed); comment = getStringResource(R.string.omnipod_error_nonce_resync_failed);
} else if (ex instanceof NotEnoughDataException) { } else if (ex instanceof NotEnoughDataException) {
comment = getStringResource(R.string.omnipod_driver_error_not_enough_data); comment = getStringResource(R.string.omnipod_error_not_enough_data);
} else if (ex instanceof PodFaultException) { } else if (ex instanceof PodFaultException) {
FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode(); FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode();
showPodFaultNotification(faultEventCode);
comment = createPodFaultErrorMessage(faultEventCode); comment = createPodFaultErrorMessage(faultEventCode);
} else if (ex instanceof PodReturnedErrorResponseException) { } else if (ex instanceof PodReturnedErrorResponseException) {
comment = getStringResource(R.string.omnipod_driver_error_pod_returned_error_response); comment = getStringResource(R.string.omnipod_error_pod_returned_error_response);
} else if (ex instanceof RileyLinkUnreachableException) {
comment = getStringResource(R.string.omnipod_error_communication_failed_no_response_from_riley_link);
} else if (ex instanceof RileyLinkInterruptedException) {
comment = getStringResource(R.string.omnipod_error_communication_failed_riley_link_interrupted);
} else if (ex instanceof RileyLinkTimeoutException) {
comment = getStringResource(R.string.omnipod_error_communication_failed_no_response_from_pod);
} else if (ex instanceof RileyLinkUnexpectedException) {
Throwable cause = ex.getCause();
comment = getStringResource(R.string.omnipod_error_unexpected_exception, cause.getClass().getName(), cause.getMessage());
} else { } else {
// Shouldn't be reachable // Shouldn't be reachable
comment = getStringResource(R.string.omnipod_driver_error_unexpected_exception_type, ex.getClass().getName()); comment = getStringResource(R.string.omnipod_error_unexpected_exception, ex.getClass().getName(), ex.getMessage());
}
aapsLogger.error(LTag.PUMP, String.format("Caught OmnipodException[certainFailure=%s] from OmnipodManager (user-friendly error message: %s)", ((OmnipodException) ex).isCertainFailure(), comment), ex);
} else {
comment = getStringResource(R.string.omnipod_driver_error_unexpected_exception_type, ex.getClass().getName());
aapsLogger.error(LTag.PUMP, String.format("Caught unexpected exception type[certainFailure=false] from OmnipodManager (user-friendly error message: %s)", comment), ex);
} }
return comment; return comment;
} }
private String createPodFaultErrorMessage(FaultEventCode faultEventCode) { private String createPodFaultErrorMessage(FaultEventCode faultEventCode) {
String comment; if (faultEventCode == FaultEventCode.NO_FAULTS && podStateManager.getPodProgressStatus() == PodProgressStatus.ACTIVATION_TIME_EXCEEDED) {
comment = getStringResource(R.string.omnipod_driver_error_pod_fault, return getStringResource(R.string.omnipod_error_pod_fault_activation_time_exceeded);
}
return getStringResource(R.string.omnipod_error_pod_fault,
ByteUtil.convertUnsignedByteToInt(faultEventCode.getValue()), faultEventCode.name()); ByteUtil.convertUnsignedByteToInt(faultEventCode.getValue()), faultEventCode.name());
return comment;
} }
private void sendEvent(Event event) { private void sendEvent(Event event) {
@ -822,4 +867,29 @@ public class AapsOmnipodManager {
return new BasalSchedule(entries); return new BasalSchedule(entries);
} }
private void uploadCareportalEvent(long date, String event) {
if (databaseHelper.getCareportalEventFromTimestamp(date) != null)
return;
try {
JSONObject data = new JSONObject();
String enteredBy = sp.getString("careportal_enteredby", "");
if (enteredBy.isEmpty()) {
data.put("enteredBy", enteredBy);
}
data.put("created_at", DateUtil.toISOString(date));
data.put("mills", date);
data.put("eventType", event);
data.put("units", profileFunction.getUnits());
CareportalEvent careportalEvent = new CareportalEvent(injector);
careportalEvent.date = date;
careportalEvent.source = Source.USER;
careportalEvent.eventType = event;
careportalEvent.json = data.toString();
databaseHelper.createOrUpdate(careportalEvent);
nsUpload.uploadCareportalEntryToNS(data);
} catch (JSONException e) {
aapsLogger.error(LTag.PUMPCOMM, "Unhandled exception when uploading SiteChange event.", e);
}
}
} }

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandAcknowledgeAlerts extends OmnipodCustomCommand {
public CommandAcknowledgeAlerts() {
super(OmnipodCustomCommandType.ACKNOWLEDGE_ALERTS);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandDeactivatePod extends OmnipodCustomCommand {
public CommandDeactivatePod() {
super(OmnipodCustomCommandType.DEACTIVATE_POD);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandGetPodStatus extends OmnipodCustomCommand {
public CommandGetPodStatus() {
super(OmnipodCustomCommandType.GET_POD_STATUS);
}
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandHandleTimeChange extends OmnipodCustomCommand {
private final boolean requestedByUser;
public CommandHandleTimeChange(boolean requestedByUser) {
super(OmnipodCustomCommandType.HANDLE_TIME_CHANGE);
this.requestedByUser = requestedByUser;
}
public boolean isRequestedByUser() {
return requestedByUser;
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandReadPulseLog extends OmnipodCustomCommand {
public CommandReadPulseLog() {
super(OmnipodCustomCommandType.READ_PULSE_LOG);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandResumeDelivery extends OmnipodCustomCommand {
public CommandResumeDelivery() {
super(OmnipodCustomCommandType.RESUME_DELIVERY);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandSuspendDelivery extends OmnipodCustomCommand {
public CommandSuspendDelivery() {
super(OmnipodCustomCommandType.SUSPEND_DELIVERY);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public final class CommandUpdateAlertConfiguration extends OmnipodCustomCommand {
public CommandUpdateAlertConfiguration() {
super(OmnipodCustomCommandType.UPDATE_ALERT_CONFIGURATION);
}
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
import androidx.annotation.NonNull;
import info.nightscout.androidaps.queue.commands.CustomCommand;
public abstract class OmnipodCustomCommand implements CustomCommand {
private final OmnipodCustomCommandType type;
OmnipodCustomCommand(@NonNull OmnipodCustomCommandType type) {
this.type = type;
}
public final OmnipodCustomCommandType getType() {
return type;
}
@Override public final String getStatusDescription() {
return type.getDescription();
}
}

View file

@ -0,0 +1,22 @@
package info.nightscout.androidaps.plugins.pump.omnipod.queue.command;
public enum OmnipodCustomCommandType {
ACKNOWLEDGE_ALERTS("ACKNOWLEDGE ALERTS"),
GET_POD_STATUS("GET POD STATUS"),
READ_PULSE_LOG("READ PULSE LOG"),
SUSPEND_DELIVERY("SUSPEND DELIVERY"),
RESUME_DELIVERY("RESUME DELIVERY"),
DEACTIVATE_POD("DEACTIVATE POD"),
HANDLE_TIME_CHANGE("HANDLE TIME CHANGE"),
UPDATE_ALERT_CONFIGURATION("UPDATE ALERT CONFIGURATION");
private final String description;
OmnipodCustomCommandType(String description) {
this.description = description;
}
String getDescription() {
return description;
}
}

View file

@ -12,7 +12,6 @@ import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
@ -30,7 +29,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.Message
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommunicationException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageAddressException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageAddressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageSequenceNumberException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageSequenceNumberException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
@ -41,13 +39,16 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NotEnoug
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodFaultException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodReturnedErrorResponseException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.PodReturnedErrorResponseException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkTimeoutException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkUnexpectedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.RileyLinkUnreachableException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
/** /**
* Created by andy on 6/29/18. * Created by andy on 6/29/18.
*/ */
@Singleton @Singleton
public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunicationManager { public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunicationManager<OmnipodPacket> {
// This empty constructor must be kept, otherwise dagger injection might break! // This empty constructor must be kept, otherwise dagger injection might break!
@Inject @Inject
public OmnipodRileyLinkCommunicationManager() { public OmnipodRileyLinkCommunicationManager() {
@ -69,7 +70,7 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
} }
@Override @Override
public RLMessage createResponseMessage(byte[] payload) { public OmnipodPacket createResponseMessage(byte[] payload) {
return new OmnipodPacket(payload); return new OmnipodPacket(payload);
} }
@ -137,7 +138,7 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
if (responseClass.isInstance(responseMessageBlock)) { if (responseClass.isInstance(responseMessageBlock)) {
podStateManager.setLastSuccessfulCommunication(DateTime.now()); podStateManager.setLastSuccessfulCommunication(DateTime.now());
return (T) responseMessageBlock; return responseClass.cast(responseMessageBlock);
} else { } else {
if (responseMessageBlock.getType() == MessageBlockType.ERROR_RESPONSE) { if (responseMessageBlock.getType() == MessageBlockType.ERROR_RESPONSE) {
ErrorResponse error = (ErrorResponse) responseMessageBlock; ErrorResponse error = (ErrorResponse) responseMessageBlock;
@ -194,13 +195,13 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
// receive an ACK instead of a normal response, or a partial response and a communication timeout // receive an ACK instead of a normal response, or a partial response and a communication timeout
if (message.isNonceResyncable() && !message.containsBlock(DeactivatePodCommand.class)) { if (message.isNonceResyncable() && !message.containsBlock(DeactivatePodCommand.class)) {
OmnipodMessage paddedMessage = new OmnipodMessage(message); OmnipodMessage paddedMessage = new OmnipodMessage(message);
// If messages are nonce resyncable, we want do distinguish between certain and uncertain failures for verification purposes // If messages are nonce resyncable, we want to distinguish between certain and uncertain failures for verification purposes
// However, some commands (e.g. cancel delivery) are single packet command by nature. When we get a timeout with a single packet, // However, some commands (e.g. cancel delivery) are single packet command by nature. When we get a timeout with a single packet,
// we are unsure whether or not the command was received by the pod // we are unsure whether or not the command was received by the pod
// However, if we send > 1 packet, we know that the command wasn't received if we never send the subsequent packets, // However, if we send > 1 packet, we know that the command wasn't received if we never send the subsequent packets,
// because the last packet contains the CRC. // because the last packet contains the CRC.
// So we pad the message with get status commands to make it > packet // So we pad the message with get status commands to make it > packet
paddedMessage.padWithGetStatusCommands(PacketType.PDM.getMaxBodyLength()); // First packet is of type PDM paddedMessage.padWithGetStatusCommands(PacketType.PDM.getMaxBodyLength(), aapsLogger); // First packet is of type PDM
encodedMessage = paddedMessage.getEncoded(); encodedMessage = paddedMessage.getEncoded();
} else { } else {
encodedMessage = message.getEncoded(); encodedMessage = message.getEncoded();
@ -219,23 +220,16 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
try { try {
// We actually ignore previous (ack) responses if it was not last packet to send // We actually ignore previous (ack) responses if it was not last packet to send
response = exchangePackets(podStateManager, packet); response = exchangePackets(podStateManager, packet);
} catch (Exception ex) { } catch (OmnipodException ex) {
OmnipodException newException;
if (ex instanceof OmnipodException) {
newException = (OmnipodException) ex;
} else {
newException = new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
}
boolean lastPacket = encodedMessage.length == 0; boolean lastPacket = encodedMessage.length == 0;
// If this is not the last packet, the message wasn't fully sent, // If this is not the last packet, the message wasn't fully sent,
// so it's impossible for the pod to have received the message // so it's impossible for the pod to have received the message
newException.setCertainFailure(!lastPacket); ex.setCertainFailure(!lastPacket);
aapsLogger.debug(LTag.PUMPBTCOMM, "Caught exception in transportMessages. Set certainFailure to {} because encodedMessage.length={}", newException.isCertainFailure(), encodedMessage.length); aapsLogger.debug(LTag.PUMPBTCOMM, "Caught OmnipodException in transportMessages. Set certainFailure to {} because encodedMessage.length={}", ex.isCertainFailure(), encodedMessage.length);
throw newException; throw ex;
} }
} }
@ -247,6 +241,7 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
byte[] receivedMessageData = response.getEncodedMessage(); byte[] receivedMessageData = response.getEncodedMessage();
while (receivedMessage == null) { while (receivedMessage == null) {
try { try {
aapsLogger.debug(LTag.PUMPBTCOMM, "Attempting to decode message: {}", ByteUtil.shortHexStringWithoutSpaces(receivedMessageData));
receivedMessage = OmnipodMessage.decodeMessage(receivedMessageData); receivedMessage = OmnipodMessage.decodeMessage(receivedMessageData);
if (receivedMessage.getAddress() != message.getAddress()) { if (receivedMessage.getAddress() != message.getAddress()) {
throw new IllegalMessageAddressException(message.getAddress(), receivedMessage.getAddress()); throw new IllegalMessageAddressException(message.getAddress(), receivedMessage.getAddress());
@ -261,18 +256,11 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
OmnipodPacket ackForCon = createAckPacket(podStateManager, packetAddress, ackAddressOverride); OmnipodPacket ackForCon = createAckPacket(podStateManager, packetAddress, ackAddressOverride);
try {
OmnipodPacket conPacket = exchangePackets(podStateManager, ackForCon, 3, 40); OmnipodPacket conPacket = exchangePackets(podStateManager, ackForCon, 3, 40);
if (conPacket.getPacketType() != PacketType.CON) { if (conPacket.getPacketType() != PacketType.CON) {
throw new IllegalPacketTypeException(PacketType.CON, conPacket.getPacketType()); throw new IllegalPacketTypeException(PacketType.CON, conPacket.getPacketType());
} }
receivedMessageData = ByteUtil.concat(receivedMessageData, conPacket.getEncodedMessage()); receivedMessageData = ByteUtil.concat(receivedMessageData, conPacket.getEncodedMessage());
} catch (OmnipodException ex2) {
throw ex2;
} catch (Exception ex2) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex2);
}
} }
} }
@ -317,8 +305,6 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
} }
} catch (OmnipodException ex) { } catch (OmnipodException ex) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Ignoring exception in ackUntilQuiet", ex); aapsLogger.debug(LTag.PUMPBTCOMM, "Ignoring exception in ackUntilQuiet", ex);
} catch (Exception ex) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
} }
podStateManager.increasePacketNumber(); podStateManager.increasePacketNumber();
@ -337,24 +323,32 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
podStateManager.increasePacketNumber(); podStateManager.increasePacketNumber();
boolean gotResponseFromRileyLink = false;
while (System.currentTimeMillis() < timeoutTime) { while (System.currentTimeMillis() < timeoutTime) {
OmnipodPacket response; OmnipodPacket response;
try { try {
response = (OmnipodPacket) sendAndListen(packet, responseTimeoutMilliseconds, repeatCount, 9, preambleExtensionMilliseconds); response = sendAndListen(packet, responseTimeoutMilliseconds, repeatCount, 9, preambleExtensionMilliseconds);
} catch (RileyLinkCommunicationException | OmnipodException ex) { gotResponseFromRileyLink = true;
} catch (RileyLinkCommunicationException ex) {
if (ex.getErrorCode() != RileyLinkBLEError.NoResponse) {
gotResponseFromRileyLink = true;
}
aapsLogger.debug(LTag.PUMPBTCOMM, "Ignoring exception in exchangePackets: " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
continue;
} catch (OmnipodException ex) {
gotResponseFromRileyLink = true;
aapsLogger.debug(LTag.PUMPBTCOMM, "Ignoring exception in exchangePackets: " + ex.getClass().getSimpleName() + ": " + ex.getMessage()); aapsLogger.debug(LTag.PUMPBTCOMM, "Ignoring exception in exchangePackets: " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
continue; continue;
} catch (Exception ex) { } catch (Exception ex) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex); throw new RileyLinkUnexpectedException(ex);
} }
if (response == null) { if (!response.isValid()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "exchangePackets response is null");
continue;
} else if (!response.isValid()) {
aapsLogger.debug(LTag.PUMPBTCOMM, "exchangePackets response is invalid: " + response); aapsLogger.debug(LTag.PUMPBTCOMM, "exchangePackets response is invalid: " + response);
continue; continue;
} }
if (response.getAddress() != packet.getAddress() && if (response.getAddress() != packet.getAddress() &&
response.getAddress() != OmnipodConstants.DEFAULT_ADDRESS) { // In some (strange) cases, the Pod remains a packet address of 0xffffffff during it's lifetime response.getAddress() != OmnipodConstants.DEFAULT_ADDRESS) { // In some (strange) cases, the Pod remains a packet address of 0xffffffff during it's lifetime
aapsLogger.debug(LTag.PUMPBTCOMM, "Packet address " + response.getAddress() + " doesn't match " + packet.getAddress()); aapsLogger.debug(LTag.PUMPBTCOMM, "Packet address " + response.getAddress() + " doesn't match " + packet.getAddress());
@ -371,7 +365,12 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
return response; return response;
} }
throw new CommunicationException(CommunicationException.Type.TIMEOUT);
if (gotResponseFromRileyLink) {
throw new RileyLinkTimeoutException();
}
throw new RileyLinkUnreachableException();
} }
} }

View file

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
@ -22,14 +23,14 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.R import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStatusRequestType
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.*
import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil import info.nightscout.androidaps.plugins.pump.omnipod.util.AapsOmnipodUtil
import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.events.EventQueueChanged import info.nightscout.androidaps.queue.events.EventQueueChanged
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -42,17 +43,18 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.omnipod_overview.*
import kotlinx.android.synthetic.main.omnipod_fragment.*
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.Duration import org.joda.time.Duration
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
class OmnipodFragment : DaggerFragment() { class OmnipodOverviewFragment : DaggerFragment() {
companion object { companion object {
private val REFRESH_INTERVAL_MILLIS = 15 * 1000L; // 15 seconds private val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds
private val PLACEHOLDER = "-"; // 15 seconds private val PLACEHOLDER = "-" // 15 seconds
} }
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@ -83,18 +85,13 @@ class OmnipodFragment : DaggerFragment() {
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.omnipod_fragment, container, false) return inflater.inflate(R.layout.omnipod_overview, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
omnipod_button_resume_delivery.setOnClickListener { omnipod_overview_button_pod_management.setOnClickListener {
disablePodActionButtons()
commandQueue.startPump(null)
}
omnipod_button_pod_mgmt.setOnClickListener {
if (omnipodPumpPlugin.rileyLinkService?.verifyConfiguration() == true) { if (omnipodPumpPlugin.rileyLinkService?.verifyConfiguration() == true) {
activity?.let { activity -> activity?.let { activity ->
protectionCheck.queryProtection( protectionCheck.queryProtection(
@ -107,13 +104,19 @@ class OmnipodFragment : DaggerFragment() {
} }
} }
omnipod_button_refresh_status.setOnClickListener { omnipod_overview_button_resume_delivery.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
omnipodPumpPlugin.addPodStatusRequest(OmnipodStatusRequestType.GET_POD_STATE); commandQueue.customCommand(CommandResumeDelivery(),
commandQueue.readStatus("Clicked Refresh", null) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_resume_delivery), true).messageOnSuccess(resourceHelper.gs(R.string.omnipod_confirmation_delivery_resumed)))
} }
omnipod_button_rileylink_stats.setOnClickListener { omnipod_overview_button_refresh_status.setOnClickListener {
disablePodActionButtons()
commandQueue.customCommand(CommandGetPodStatus(),
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_refresh_status), false))
}
omnipod_overview_button_rileylink_stats.setOnClickListener {
if (omnipodPumpPlugin.rileyLinkService?.verifyConfiguration() == true) { if (omnipodPumpPlugin.rileyLinkService?.verifyConfiguration() == true) {
startActivity(Intent(context, RileyLinkStatusActivity::class.java)) startActivity(Intent(context, RileyLinkStatusActivity::class.java))
} else { } else {
@ -121,22 +124,31 @@ class OmnipodFragment : DaggerFragment() {
} }
} }
omnipod_button_acknowledge_active_alerts.setOnClickListener { omnipod_overview_button_acknowledge_active_alerts.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
omnipodPumpPlugin.addPodStatusRequest(OmnipodStatusRequestType.ACKNOWLEDGE_ALERTS); commandQueue.customCommand(CommandAcknowledgeAlerts(),
commandQueue.readStatus("Clicked Acknowledge Alert", null) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_acknowledge_alerts), false)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_confirmation_acknowledged_alerts)))
} }
omnipod_button_suspend_delivery.setOnClickListener { omnipod_overview_button_suspend_delivery.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
omnipodPumpPlugin.addPodStatusRequest(OmnipodStatusRequestType.SUSPEND_DELIVERY); commandQueue.customCommand(CommandSuspendDelivery(),
commandQueue.readStatus("Clicked Suspend Delivery", null) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_suspend_delivery), true)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_confirmation_suspended_delivery)))
} }
omnipod_button_pulse_log.setOnClickListener { omnipod_overview_button_set_time.setOnClickListener {
disablePodActionButtons() disablePodActionButtons()
omnipodPumpPlugin.addPodStatusRequest(OmnipodStatusRequestType.GET_PULSE_LOG); commandQueue.customCommand(CommandHandleTimeChange(true),
commandQueue.readStatus("Clicked Pulse Log", null) DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_set_time), true)
.messageOnSuccess(resourceHelper.gs(R.string.omnipod_confirmation_time_on_pod_updated)))
}
omnipod_overview_button_pulse_log.setOnClickListener {
disablePodActionButtons()
commandQueue.customCommand(CommandReadPulseLog(),
DisplayResultDialogCallback(resourceHelper.gs(R.string.omnipod_error_failed_to_read_pulse_log), false))
} }
} }
@ -166,9 +178,9 @@ class OmnipodFragment : DaggerFragment() {
}, { fabricPrivacy.logException(it) }) }, { fabricPrivacy.logException(it) })
disposables += rxBus disposables += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({ .subscribe({
updatePulseLogButton() updatePodActionButtons()
}, { fabricPrivacy.logException(it) }) }, { fabricPrivacy.logException(it) })
updateUi() updateUi()
} }
@ -193,7 +205,7 @@ class OmnipodFragment : DaggerFragment() {
val resourceId = rileyLinkServiceState.getResourceId() val resourceId = rileyLinkServiceState.getResourceId()
val rileyLinkError = rileyLinkServiceData.rileyLinkError val rileyLinkError = rileyLinkServiceData.rileyLinkError
omnipod_rl_status.text = omnipod_overview_riley_link_status.text =
when { when {
rileyLinkServiceState == RileyLinkServiceState.NotStarted -> resourceHelper.gs(resourceId) rileyLinkServiceState == RileyLinkServiceState.NotStarted -> resourceHelper.gs(resourceId)
rileyLinkServiceState.isConnecting -> "{fa-bluetooth-b spin} " + resourceHelper.gs(resourceId) rileyLinkServiceState.isConnecting -> "{fa-bluetooth-b spin} " + resourceHelper.gs(resourceId)
@ -201,7 +213,7 @@ class OmnipodFragment : DaggerFragment() {
rileyLinkServiceState.isError && rileyLinkError != null -> "{fa-bluetooth-b} " + resourceHelper.gs(rileyLinkError.getResourceId(RileyLinkTargetDevice.Omnipod)) rileyLinkServiceState.isError && rileyLinkError != null -> "{fa-bluetooth-b} " + resourceHelper.gs(rileyLinkError.getResourceId(RileyLinkTargetDevice.Omnipod))
else -> "{fa-bluetooth-b} " + resourceHelper.gs(resourceId) else -> "{fa-bluetooth-b} " + resourceHelper.gs(resourceId)
} }
omnipod_rl_status.setTextColor(if (rileyLinkServiceState.isError || rileyLinkError != null) Color.RED else Color.WHITE) omnipod_overview_riley_link_status.setTextColor(if (rileyLinkServiceState.isError || rileyLinkError != null) Color.RED else Color.WHITE)
} }
private fun updateOmnipodStatus() { private fun updateOmnipodStatus() {
@ -210,7 +222,7 @@ class OmnipodFragment : DaggerFragment() {
updateTempBasal() updateTempBasal()
updatePodStatus() updatePodStatus()
val errors = ArrayList<String>(); val errors = ArrayList<String>()
if (omnipodPumpPlugin.rileyLinkService != null) { if (omnipodPumpPlugin.rileyLinkService != null) {
val rileyLinkErrorDescription = omnipodPumpPlugin.rileyLinkService.errorDescription val rileyLinkErrorDescription = omnipodPumpPlugin.rileyLinkService.errorDescription
if (StringUtils.isNotEmpty(rileyLinkErrorDescription)) { if (StringUtils.isNotEmpty(rileyLinkErrorDescription)) {
@ -219,32 +231,41 @@ class OmnipodFragment : DaggerFragment() {
} }
if (!podStateManager.hasPodState() || !podStateManager.isPodInitialized) { if (!podStateManager.hasPodState() || !podStateManager.isPodInitialized) {
omnipod_pod_address.text = if (podStateManager.hasPodState()) { omnipod_overview_pod_address.text = if (podStateManager.hasPodState()) {
podStateManager.address.toString() podStateManager.address.toString()
} else { } else {
PLACEHOLDER PLACEHOLDER
} }
omnipod_pod_lot.text = PLACEHOLDER omnipod_overview_pod_lot.text = PLACEHOLDER
omnipod_pod_tid.text = PLACEHOLDER omnipod_overview_pod_tid.text = PLACEHOLDER
omnipod_pod_firmware_version.text = PLACEHOLDER omnipod_overview_firmware_version.text = PLACEHOLDER
omnipod_pod_expiry.text = PLACEHOLDER omnipod_overview_time_on_pod.text = PLACEHOLDER
omnipod_pod_expiry.setTextColor(Color.WHITE) omnipod_overview_pod_expiry_date.text = PLACEHOLDER
omnipod_base_basal_rate.text = PLACEHOLDER omnipod_overview_pod_expiry_date.setTextColor(Color.WHITE)
omnipod_total_delivered.text = PLACEHOLDER omnipod_overview_base_basal_rate.text = PLACEHOLDER
omnipod_reservoir.text = PLACEHOLDER omnipod_overview_total_delivered.text = PLACEHOLDER
omnipod_pod_active_alerts.text = PLACEHOLDER omnipod_overview_reservoir.text = PLACEHOLDER
omnipod_overview_reservoir.setTextColor(Color.WHITE)
omnipod_overview_pod_active_alerts.text = PLACEHOLDER
} else { } else {
omnipod_pod_address.text = podStateManager.address.toString() omnipod_overview_pod_address.text = podStateManager.address.toString()
omnipod_pod_lot.text = podStateManager.lot.toString() omnipod_overview_pod_lot.text = podStateManager.lot.toString()
omnipod_pod_tid.text = podStateManager.tid.toString() omnipod_overview_pod_tid.text = podStateManager.tid.toString()
omnipod_pod_firmware_version.text = resourceHelper.gs(R.string.omnipod_pod_firmware_version_value, podStateManager.pmVersion.toString(), podStateManager.piVersion.toString()) omnipod_overview_firmware_version.text = resourceHelper.gs(R.string.omnipod_firmware_version_value, podStateManager.pmVersion.toString(), podStateManager.piVersion.toString())
omnipod_overview_time_on_pod.text = readableZonedTime(podStateManager.time)
omnipod_overview_time_on_pod.setTextColor(if (podStateManager.timeDeviatesMoreThan(Duration.standardMinutes(5))) {
Color.RED
} else {
Color.WHITE
})
val expiresAt = podStateManager.expiresAt val expiresAt = podStateManager.expiresAt
if (expiresAt == null) { if (expiresAt == null) {
omnipod_pod_expiry.text = PLACEHOLDER omnipod_overview_pod_expiry_date.text = PLACEHOLDER
omnipod_pod_expiry.setTextColor(Color.WHITE) omnipod_overview_pod_expiry_date.setTextColor(Color.WHITE)
} else { } else {
omnipod_pod_expiry.text = dateUtil.dateAndTimeString(expiresAt.toDate()) omnipod_overview_pod_expiry_date.text = readableZonedTime(expiresAt)
omnipod_pod_expiry.setTextColor(if (DateTime.now().isAfter(expiresAt)) { omnipod_overview_pod_expiry_date.setTextColor(if (DateTime.now().isAfter(expiresAt)) {
Color.RED Color.RED
} else { } else {
Color.WHITE Color.WHITE
@ -259,29 +280,29 @@ class OmnipodFragment : DaggerFragment() {
val now = DateTime.now() val now = DateTime.now()
// base basal rate // base basal rate
omnipod_base_basal_rate.text = if (podStateManager.isPodActivationCompleted) { omnipod_overview_base_basal_rate.text = if (podStateManager.isPodActivationCompleted) {
resourceHelper.gs(R.string.pump_basebasalrate, omnipodPumpPlugin.model().determineCorrectBasalSize(podStateManager.basalSchedule.rateAt(Duration(now.withTimeAtStartOfDay(), now)))) resourceHelper.gs(R.string.pump_basebasalrate, omnipodPumpPlugin.model().determineCorrectBasalSize(podStateManager.basalSchedule.rateAt(Duration(now.withTimeAtStartOfDay(), now))))
} else { } else {
PLACEHOLDER PLACEHOLDER
} }
// total delivered // total delivered
omnipod_total_delivered.text = if (podStateManager.isPodActivationCompleted && podStateManager.totalInsulinDelivered != null) { omnipod_overview_total_delivered.text = if (podStateManager.isPodActivationCompleted && podStateManager.totalInsulinDelivered != null) {
resourceHelper.gs(R.string.omnipod_total_delivered, podStateManager.totalInsulinDelivered - OmnipodConstants.POD_SETUP_UNITS); resourceHelper.gs(R.string.omnipod_overview_total_delivered_value, podStateManager.totalInsulinDelivered - OmnipodConstants.POD_SETUP_UNITS)
} else { } else {
PLACEHOLDER PLACEHOLDER
} }
// reservoir // reservoir
if (podStateManager.reservoirLevel == null) { if (podStateManager.reservoirLevel == null) {
omnipod_reservoir.text = resourceHelper.gs(R.string.omnipod_reservoir_over50) omnipod_overview_reservoir.text = resourceHelper.gs(R.string.omnipod_overview_reservoir_value_over50)
omnipod_reservoir.setTextColor(Color.WHITE) omnipod_overview_reservoir.setTextColor(Color.WHITE)
} else { } else {
omnipod_reservoir.text = resourceHelper.gs(R.string.omnipod_reservoir_left, podStateManager.reservoirLevel) omnipod_overview_reservoir.text = resourceHelper.gs(R.string.omnipod_overview_reservoir_value, podStateManager.reservoirLevel)
warnColors.setColorInverse(omnipod_reservoir, podStateManager.reservoirLevel, 50.0, 20.0) warnColors.setColorInverse(omnipod_overview_reservoir, podStateManager.reservoirLevel, 50.0, 20.0)
} }
omnipod_pod_active_alerts.text = if (podStateManager.hasActiveAlerts()) { omnipod_overview_pod_active_alerts.text = if (podStateManager.hasActiveAlerts()) {
TextUtils.join(System.lineSeparator(), omnipodUtil.getTranslatedActiveAlerts(podStateManager)) TextUtils.join(System.lineSeparator(), omnipodUtil.getTranslatedActiveAlerts(podStateManager))
} else { } else {
PLACEHOLDER PLACEHOLDER
@ -289,27 +310,27 @@ class OmnipodFragment : DaggerFragment() {
} }
if (errors.size == 0) { if (errors.size == 0) {
omnipod_errors.text = PLACEHOLDER omnipod_overview_errors.text = PLACEHOLDER
omnipod_errors.setTextColor(Color.WHITE) omnipod_overview_errors.setTextColor(Color.WHITE)
} else { } else {
omnipod_errors.text = StringUtils.join(errors, System.lineSeparator()) omnipod_overview_errors.text = StringUtils.join(errors, System.lineSeparator())
omnipod_errors.setTextColor(Color.RED) omnipod_overview_errors.setTextColor(Color.RED)
} }
} }
private fun updateLastConnection() { private fun updateLastConnection() {
if (podStateManager.isPodInitialized && podStateManager.lastSuccessfulCommunication != null) { if (podStateManager.isPodInitialized && podStateManager.lastSuccessfulCommunication != null) {
omnipod_last_connection.text = readableDuration(podStateManager.lastSuccessfulCommunication) omnipod_overview_last_connection.text = readableDuration(podStateManager.lastSuccessfulCommunication)
val lastConnectionColor = val lastConnectionColor =
if (omnipodPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) { if (omnipodPumpPlugin.isUnreachableAlertTimeoutExceeded(getPumpUnreachableTimeout().millis)) {
Color.RED Color.RED
} else { } else {
Color.WHITE Color.WHITE
} }
omnipod_last_connection.setTextColor(lastConnectionColor) omnipod_overview_last_connection.setTextColor(lastConnectionColor)
} else { } else {
omnipod_last_connection.setTextColor(Color.WHITE) omnipod_overview_last_connection.setTextColor(Color.WHITE)
omnipod_last_connection.text = if (podStateManager.hasPodState() && podStateManager.lastSuccessfulCommunication != null) { omnipod_overview_last_connection.text = if (podStateManager.hasPodState() && podStateManager.lastSuccessfulCommunication != null) {
readableDuration(podStateManager.lastSuccessfulCommunication) readableDuration(podStateManager.lastSuccessfulCommunication)
} else { } else {
PLACEHOLDER PLACEHOLDER
@ -318,16 +339,16 @@ class OmnipodFragment : DaggerFragment() {
} }
private fun updatePodStatus() { private fun updatePodStatus() {
omnipod_pod_status.text = if (!podStateManager.hasPodState()) { omnipod_overview_pod_status.text = if (!podStateManager.hasPodState()) {
resourceHelper.gs(R.string.omnipod_pod_status_no_active_pod) resourceHelper.gs(R.string.omnipod_pod_status_no_active_pod)
} else if (!podStateManager.isPodActivationCompleted) { } else if (!podStateManager.isPodActivationCompleted) {
if (!podStateManager.isPodInitialized) { if (!podStateManager.isPodInitialized) {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_pair_and_prime) resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_activation)
} else { } else {
if (PodProgressStatus.ACTIVATION_TIME_EXCEEDED == podStateManager.podProgressStatus) { if (PodProgressStatus.ACTIVATION_TIME_EXCEEDED == podStateManager.podProgressStatus) {
resourceHelper.gs(R.string.omnipod_pod_status_activation_time_exceeded) resourceHelper.gs(R.string.omnipod_pod_status_activation_time_exceeded)
} else if (podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)) { } else if (podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_pair_and_prime) resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_activation)
} else { } else {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_cannula_insertion) resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_cannula_insertion)
} }
@ -353,12 +374,12 @@ class OmnipodFragment : DaggerFragment() {
} else { } else {
Color.WHITE Color.WHITE
} }
omnipod_pod_status.setTextColor(podStatusColor) omnipod_overview_pod_status.setTextColor(podStatusColor)
} }
private fun updateLastBolus() { private fun updateLastBolus() {
if (podStateManager.isPodActivationCompleted && podStateManager.hasLastBolus()) { if (podStateManager.isPodActivationCompleted && podStateManager.hasLastBolus()) {
var text = resourceHelper.gs(R.string.omnipod_last_bolus, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime)) var text = resourceHelper.gs(R.string.omnipod_overview_last_bolus_value, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime))
val textColor: Int val textColor: Int
if (podStateManager.isLastBolusCertain) { if (podStateManager.isLastBolusCertain) {
@ -368,12 +389,12 @@ class OmnipodFragment : DaggerFragment() {
text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")"
} }
omnipod_last_bolus.text = text; omnipod_overview_last_bolus.text = text
omnipod_last_bolus.setTextColor(textColor) omnipod_overview_last_bolus.setTextColor(textColor)
} else { } else {
omnipod_last_bolus.text = PLACEHOLDER omnipod_overview_last_bolus.text = PLACEHOLDER
omnipod_last_bolus.setTextColor(Color.WHITE) omnipod_overview_last_bolus.setTextColor(Color.WHITE)
} }
} }
@ -381,15 +402,15 @@ class OmnipodFragment : DaggerFragment() {
if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) { if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) {
val now = DateTime.now() val now = DateTime.now()
val startTime = podStateManager.tempBasalStartTime; val startTime = podStateManager.tempBasalStartTime
val amount = podStateManager.tempBasalAmount val amount = podStateManager.tempBasalAmount
val duration = podStateManager.tempBasalDuration; val duration = podStateManager.tempBasalDuration
val minutesRunning = Duration(startTime, now).standardMinutes val minutesRunning = Duration(startTime, now).standardMinutes
var text: String var text: String
val textColor: Int val textColor: Int
text = resourceHelper.gs(R.string.omnipod_temp_basal, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes) text = resourceHelper.gs(R.string.omnipod_overview_temp_basal_value, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes)
if (podStateManager.isTempBasalCertain) { if (podStateManager.isTempBasalCertain) {
textColor = Color.WHITE textColor = Color.WHITE
} else { } else {
@ -397,20 +418,20 @@ class OmnipodFragment : DaggerFragment() {
text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")"
} }
omnipod_temp_basal.text = text; omnipod_overview_temp_basal.text = text
omnipod_temp_basal.setTextColor(textColor) omnipod_overview_temp_basal.setTextColor(textColor)
} else { } else {
omnipod_temp_basal.text = PLACEHOLDER omnipod_overview_temp_basal.text = PLACEHOLDER
omnipod_temp_basal.setTextColor(Color.WHITE) omnipod_overview_temp_basal.setTextColor(Color.WHITE)
} }
} }
private fun updateQueueStatus() { private fun updateQueueStatus() {
if (isQueueEmpty()) { if (isQueueEmpty()) {
omnipod_queue.visibility = View.GONE omnipod_overview_queue.visibility = View.GONE
} else { } else {
omnipod_queue.visibility = View.VISIBLE omnipod_overview_queue.visibility = View.VISIBLE
omnipod_queue.text = commandQueue.spannedStatus().toString() omnipod_overview_queue.text = commandQueue.spannedStatus().toString()
} }
} }
@ -419,62 +440,111 @@ class OmnipodFragment : DaggerFragment() {
updateResumeDeliveryButton() updateResumeDeliveryButton()
updateAcknowledgeAlertsButton() updateAcknowledgeAlertsButton()
updateSuspendDeliveryButton() updateSuspendDeliveryButton()
updateSetTimeButton()
updatePulseLogButton() updatePulseLogButton()
} }
private fun disablePodActionButtons() { private fun disablePodActionButtons() {
omnipod_button_acknowledge_active_alerts.isEnabled = false omnipod_overview_button_acknowledge_active_alerts.isEnabled = false
omnipod_button_resume_delivery.isEnabled = false omnipod_overview_button_resume_delivery.isEnabled = false
omnipod_button_refresh_status.isEnabled = false omnipod_overview_button_suspend_delivery.isEnabled = false
omnipod_button_pulse_log.isEnabled = false omnipod_overview_button_set_time.isEnabled = false
omnipod_overview_button_refresh_status.isEnabled = false
omnipod_overview_button_pulse_log.isEnabled = false
} }
private fun updateRefreshStatusButton() { private fun updateRefreshStatusButton() {
omnipod_button_refresh_status.isEnabled = podStateManager.isPodInitialized && podStateManager.podProgressStatus.isAtLeast(PodProgressStatus.PAIRING_COMPLETED) omnipod_overview_button_refresh_status.isEnabled = podStateManager.isPodInitialized && podStateManager.podProgressStatus.isAtLeast(PodProgressStatus.PAIRING_COMPLETED)
&& rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty() && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} }
private fun updateResumeDeliveryButton() { private fun updateResumeDeliveryButton() {
val queueEmptyOrStartingPump = isQueueEmpty() || commandQueue.isRunning(Command.CommandType.START_PUMP) if (podStateManager.isPodRunning && (podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandResumeDelivery::class.java))) {
if (podStateManager.isPodActivationCompleted && podStateManager.isSuspended && queueEmptyOrStartingPump) { omnipod_overview_button_resume_delivery.visibility = View.VISIBLE
omnipod_button_resume_delivery.visibility = View.VISIBLE omnipod_overview_button_resume_delivery.isEnabled = rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
omnipod_button_resume_delivery.isEnabled = rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} else { } else {
omnipod_button_resume_delivery.visibility = View.GONE omnipod_overview_button_resume_delivery.visibility = View.GONE
} }
} }
private fun updateAcknowledgeAlertsButton() { private fun updateAcknowledgeAlertsButton() {
omnipod_button_acknowledge_active_alerts.isEnabled = podStateManager.isPodActivationCompleted && podStateManager.hasActiveAlerts() if (podStateManager.isPodRunning && (podStateManager.hasActiveAlerts() || commandQueue.isCustomCommandInQueue(CommandAcknowledgeAlerts::class.java))) {
&& !podStateManager.isPodDead && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty() omnipod_overview_button_acknowledge_active_alerts.visibility = View.VISIBLE
omnipod_overview_button_acknowledge_active_alerts.isEnabled = rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} else {
omnipod_overview_button_acknowledge_active_alerts.visibility = View.GONE
}
} }
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.
if (omnipodManager.isSuspendDeliveryButtonEnabled && !(podStateManager.isPodRunning && podStateManager.isSuspended)) { if (omnipodManager.isSuspendDeliveryButtonEnabled && podStateManager.isPodRunning && (!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))) {
omnipod_button_suspend_delivery.visibility = View.VISIBLE omnipod_overview_button_suspend_delivery.visibility = View.VISIBLE
omnipod_button_suspend_delivery.isEnabled = podStateManager.isPodRunning && !podStateManager.isSuspended && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty() omnipod_overview_button_suspend_delivery.isEnabled = podStateManager.isPodRunning && !podStateManager.isSuspended && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} else { } else {
omnipod_button_suspend_delivery.visibility = View.GONE omnipod_overview_button_suspend_delivery.visibility = View.GONE
}
}
private fun updateSetTimeButton() {
if (podStateManager.isPodRunning && (podStateManager.timeDeviatesMoreThan(Duration.standardMinutes(5)) || commandQueue.isCustomCommandInQueue(CommandHandleTimeChange::class.java))) {
omnipod_overview_button_set_time.visibility = View.VISIBLE
omnipod_overview_button_set_time.isEnabled = !podStateManager.isSuspended && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} else {
omnipod_overview_button_set_time.visibility = View.GONE
} }
} }
private fun updatePulseLogButton() { private fun updatePulseLogButton() {
if (omnipodManager.isPulseLogButtonEnabled) { if (omnipodManager.isPulseLogButtonEnabled) {
omnipod_button_pulse_log.visibility = View.VISIBLE omnipod_overview_button_pulse_log.visibility = View.VISIBLE
omnipod_button_pulse_log.isEnabled = podStateManager.isPodActivationCompleted && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty() omnipod_overview_button_pulse_log.isEnabled = podStateManager.isPodActivationCompleted && rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
} else { } else {
omnipod_button_pulse_log.visibility = View.GONE omnipod_overview_button_pulse_log.visibility = View.GONE
} }
} }
private fun displayNotConfiguredDialog() { private fun displayNotConfiguredDialog() {
context?.let { context?.let {
UIRunnable(Runnable {
OKDialog.show(it, resourceHelper.gs(R.string.omnipod_warning), OKDialog.show(it, resourceHelper.gs(R.string.omnipod_warning),
resourceHelper.gs(R.string.omnipod_error_operation_not_possible_no_configuration), null) resourceHelper.gs(R.string.omnipod_error_operation_not_possible_no_configuration), null)
}).run()
} }
} }
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
context?.let {
val i = Intent(it, ErrorHelperActivity::class.java)
i.putExtra("soundid", if (withSound) R.raw.boluserror else 0)
i.putExtra("status", message)
i.putExtra("title", title)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
it.startActivity(i)
}
}
private fun displayOkDialog(title: String, message: String) {
context?.let {
UIRunnable(Runnable {
OKDialog.show(it, title, message, null)
}).run()
}
}
private fun readableZonedTime(time: DateTime): String {
val timeAsJavaData = time.toLocalDateTime().toDate()
val timeZone = podStateManager.timeZone.toTimeZone()
if (timeZone == TimeZone.getDefault()) {
return dateUtil.dateAndTimeString(timeAsJavaData)
}
val isDaylightTime = timeZone.inDaylightTime(timeAsJavaData)
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_time_with_timezone, dateUtil.dateAndTimeString(timeAsJavaData), timeZoneDisplayName)
}
private fun readableDuration(dateTime: DateTime): String { private fun readableDuration(dateTime: DateTime): String {
val duration = Duration(dateTime, DateTime.now()) val duration = Duration(dateTime, DateTime.now())
val hours = duration.standardHours.toInt() val hours = duration.standardHours.toInt()
@ -521,4 +591,24 @@ class OmnipodFragment : DaggerFragment() {
return Duration.standardMinutes(sp.getInt(resourceHelper.gs(R.string.key_pump_unreachable_threshold_minutes), Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES).toLong()) return Duration.standardMinutes(sp.getInt(resourceHelper.gs(R.string.key_pump_unreachable_threshold_minutes), Constants.DEFAULT_PUMP_UNREACHABLE_THRESHOLD_MINUTES).toLong())
} }
inner class DisplayResultDialogCallback(private val errorMessagePrefix: String, private val withSoundOnError: Boolean) : Callback() {
private var messageOnSuccess: String? = null
override fun run() {
if (result.success) {
val messageOnSuccess = this.messageOnSuccess
if (messageOnSuccess != null) {
displayOkDialog(resourceHelper.gs(R.string.omnipod_confirmation), messageOnSuccess)
}
} else {
displayErrorDialog(resourceHelper.gs(R.string.omnipod_warning), resourceHelper.gs(R.string.omnipod_two_strings_concatenated_by_colon, errorMessagePrefix, result.comment), withSoundOnError)
}
}
fun messageOnSuccess(message: String): DisplayResultDialogCallback {
messageOnSuccess = message
return this
}
}
} }

View file

@ -250,11 +250,11 @@ public class PodHistoryActivity extends NoSplashAppCompatActivity {
case SET_TEMPORARY_BASAL: { case SET_TEMPORARY_BASAL: {
TempBasalPair tempBasalPair = aapsOmnipodUtil.getGsonInstance().fromJson(historyEntry.getData(), TempBasalPair.class); TempBasalPair tempBasalPair = aapsOmnipodUtil.getGsonInstance().fromJson(historyEntry.getData(), TempBasalPair.class);
valueView.setText(resourceHelper.gs(R.string.omnipod_cmd_tbr_value, tempBasalPair.getInsulinRate(), tempBasalPair.getDurationMinutes())); valueView.setText(resourceHelper.gs(R.string.omnipod_history_tbr_value, tempBasalPair.getInsulinRate(), tempBasalPair.getDurationMinutes()));
} }
break; break;
case FILL_CANNULA_SET_BASAL_PROFILE: case INSERT_CANNULA:
case SET_BASAL_SCHEDULE: { case SET_BASAL_SCHEDULE: {
if (historyEntry.getData() != null) { if (historyEntry.getData() != null) {
setProfileValue(historyEntry.getData(), valueView); setProfileValue(historyEntry.getData(), valueView);
@ -265,9 +265,9 @@ public class PodHistoryActivity extends NoSplashAppCompatActivity {
case SET_BOLUS: { case SET_BOLUS: {
if (historyEntry.getData().contains(";")) { if (historyEntry.getData().contains(";")) {
String[] splitVal = historyEntry.getData().split(";"); String[] splitVal = historyEntry.getData().split(";");
valueView.setText(resourceHelper.gs(R.string.omnipod_cmd_bolus_value_with_carbs, Double.valueOf(splitVal[0]), Double.valueOf(splitVal[1]))); valueView.setText(resourceHelper.gs(R.string.omnipod_history_bolus_value_with_carbs, Double.valueOf(splitVal[0]), Double.valueOf(splitVal[1])));
} else { } else {
valueView.setText(resourceHelper.gs(R.string.omnipod_cmd_bolus_value, Double.valueOf(historyEntry.getData()))); valueView.setText(resourceHelper.gs(R.string.omnipod_history_bolus_value, Double.valueOf(historyEntry.getData())));
} }
} }
break; break;
@ -275,13 +275,13 @@ public class PodHistoryActivity extends NoSplashAppCompatActivity {
case GET_POD_STATUS: case GET_POD_STATUS:
case GET_POD_INFO: case GET_POD_INFO:
case SET_TIME: case SET_TIME:
case PAIR_AND_PRIME: case INITIALIZE_POD:
case CANCEL_TEMPORARY_BASAL_BY_DRIVER: case CANCEL_TEMPORARY_BASAL_BY_DRIVER:
case CANCEL_TEMPORARY_BASAL: case CANCEL_TEMPORARY_BASAL:
case CONFIGURE_ALERTS: case CONFIGURE_ALERTS:
case CANCEL_BOLUS: case CANCEL_BOLUS:
case DEACTIVATE_POD: case DEACTIVATE_POD:
case RESET_POD_STATE: case DISCARD_POD:
case ACKNOWLEDGE_ALERTS: case ACKNOWLEDGE_ALERTS:
case SUSPEND_DELIVERY: case SUSPEND_DELIVERY:
case RESUME_DELIVERY: case RESUME_DELIVERY:

View file

@ -2,13 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import androidx.fragment.app.FragmentStatePagerAdapter
import com.atech.android.library.wizardpager.WizardPagerActivity
import com.atech.android.library.wizardpager.WizardPagerContext
import com.atech.android.library.wizardpager.data.WizardPagerSettings
import com.atech.android.library.wizardpager.defs.WizardStepsWayType
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
@ -16,22 +9,19 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
import info.nightscout.androidaps.plugins.pump.omnipod.R import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.defs.PodActionType import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.PodActivationWizardActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.model.FullInitPodWizardModel import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.PodDeactivationWizardActivity
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.model.RemovePodWizardModel
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.model.ShortInitPodWizardModel
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.pages.InitPodRefreshAction
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.omnipod_pod_mgmt.* import kotlinx.android.synthetic.main.omnipod_pod_management.*
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -52,22 +42,25 @@ class PodManagementActivity : NoSplashAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.omnipod_pod_mgmt) setContentView(R.layout.omnipod_pod_management)
initpod_init_pod.setOnClickListener { omnipod_pod_management_button_activate_pod.setOnClickListener {
initPodAction() startActivity(Intent(this, PodActivationWizardActivity::class.java))
} }
initpod_remove_pod.setOnClickListener { omnipod_pod_management_button_deactivate_pod.setOnClickListener {
deactivatePodAction() startActivity(Intent(this, PodDeactivationWizardActivity::class.java))
} }
initpod_reset_pod.setOnClickListener { omnipod_pod_management_button_discard_pod.setOnClickListener {
discardPodAction() OKDialog.showConfirmation(this,
resourceHelper.gs(R.string.omnipod_pod_management_discard_pod_state_confirmation), Thread {
aapsOmnipodManager.discardPodState()
})
} }
initpod_pod_history.setOnClickListener { omnipod_pod_management_button_pod_history.setOnClickListener {
showPodHistory() startActivity(Intent(this, PodHistoryActivity::class.java))
} }
} }
@ -90,86 +83,27 @@ class PodManagementActivity : NoSplashAppCompatActivity() {
disposables.clear() disposables.clear()
} }
private fun initPodAction() {
val pagerSettings = WizardPagerSettings()
var refreshAction = InitPodRefreshAction(injector, PodActionType.INIT_POD)
pagerSettings.setWizardStepsWayType(WizardStepsWayType.CancelNext)
pagerSettings.setFinishStringResourceId(R.string.close)
pagerSettings.setFinishButtonBackground(R.drawable.finish_background)
pagerSettings.setNextButtonBackground(R.drawable.selectable_item_background)
pagerSettings.setBackStringResourceId(R.string.cancel)
pagerSettings.cancelAction = refreshAction
pagerSettings.finishAction = refreshAction
pagerSettings.pagerAdapterBehavior = FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
val wizardPagerContext = WizardPagerContext.getInstance()
wizardPagerContext.clearContext()
wizardPagerContext.pagerSettings = pagerSettings
val isFullInit = !podStateManager.isPodInitialized || podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)
if (isFullInit) {
wizardPagerContext.wizardModel = FullInitPodWizardModel(applicationContext)
} else {
wizardPagerContext.wizardModel = ShortInitPodWizardModel(applicationContext)
}
val myIntent = Intent(this@PodManagementActivity, WizardPagerActivity::class.java)
this@PodManagementActivity.startActivity(myIntent)
}
private fun deactivatePodAction() {
val pagerSettings = WizardPagerSettings()
var refreshAction = InitPodRefreshAction(injector, PodActionType.DEACTIVATE_POD)
pagerSettings.setWizardStepsWayType(WizardStepsWayType.CancelNext)
pagerSettings.setFinishStringResourceId(R.string.close)
pagerSettings.setFinishButtonBackground(R.drawable.finish_background)
pagerSettings.setNextButtonBackground(R.drawable.selectable_item_background)
pagerSettings.setBackStringResourceId(R.string.cancel)
pagerSettings.cancelAction = refreshAction
pagerSettings.finishAction = refreshAction
pagerSettings.pagerAdapterBehavior = FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
val wizardPagerContext = WizardPagerContext.getInstance();
wizardPagerContext.clearContext()
wizardPagerContext.pagerSettings = pagerSettings
wizardPagerContext.wizardModel = RemovePodWizardModel(applicationContext)
val myIntent = Intent(this@PodManagementActivity, WizardPagerActivity::class.java)
this@PodManagementActivity.startActivity(myIntent)
}
private fun discardPodAction() {
OKDialog.showConfirmation(this,
resourceHelper.gs(R.string.omnipod_cmd_discard_pod_desc), Thread {
aapsOmnipodManager.discardPodState()
rxBus.send(EventOmnipodPumpValuesChanged())
})
}
private fun showPodHistory() {
startActivity(Intent(applicationContext, PodHistoryActivity::class.java))
}
private fun refreshButtons() { private fun refreshButtons() {
initpod_init_pod.isEnabled = !podStateManager.isPodActivationCompleted // Only show the discard button to reset a cached Pod address before the Pod has actually been initialized
initpod_remove_pod.isEnabled = podStateManager.isPodInitialized // Otherwise, users should use the Deactivate Pod Wizard. In case proper deactivation fails,
initpod_reset_pod.isEnabled = podStateManager.hasPodState() // they will get an option to discard the Pod state there
// TODO maybe rename this button and the confirmation dialog text (see onCreate)
val waitingForRlView = findViewById<LinearLayout>(R.id.initpod_waiting_for_rl_layout) val discardButtonEnabled = podStateManager.hasPodState() && !podStateManager.isPodInitialized
omnipod_pod_management_button_discard_pod.visibility = discardButtonEnabled.toVisibility()
omnipod_pod_management_waiting_for_rl_layout.visibility = (!rileyLinkServiceData.rileyLinkServiceState.isReady).toVisibility()
if (rileyLinkServiceData.rileyLinkServiceState.isReady) { if (rileyLinkServiceData.rileyLinkServiceState.isReady) {
waitingForRlView.visibility = View.GONE omnipod_pod_management_button_activate_pod.isEnabled = !podStateManager.isPodActivationCompleted
omnipod_pod_management_button_deactivate_pod.isEnabled = podStateManager.isPodInitialized
if (discardButtonEnabled) {
omnipod_pod_management_button_discard_pod.isEnabled = true
}
} else { } else {
// if rileylink is not running we disable all operations that require a RL connection omnipod_pod_management_button_activate_pod.isEnabled = false
waitingForRlView.visibility = View.VISIBLE omnipod_pod_management_button_deactivate_pod.isEnabled = false
initpod_init_pod.isEnabled = false if (discardButtonEnabled) {
initpod_remove_pod.isEnabled = false omnipod_pod_management_button_discard_pod.isEnabled = false
initpod_reset_pod.isEnabled = false }
} }
} }

View file

@ -0,0 +1,60 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation
import android.os.Bundle
import androidx.annotation.IdRes
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.activity.OmnipodWizardActivityBase
import javax.inject.Inject
class PodActivationWizardActivity : OmnipodWizardActivityBase() {
companion object {
const val KEY_START_DESTINATION = "startDestination"
}
@Inject
lateinit var podStateManager: PodStateManager
@IdRes
private var startDestination: Int = R.id.fillPodInfoFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.omnipod_pod_activation_wizard_activity)
startDestination = savedInstanceState?.getInt(KEY_START_DESTINATION, R.id.fillPodInfoFragment)
?: if (!podStateManager.isPodInitialized || podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
R.id.fillPodInfoFragment
} else {
R.id.attachPodInfoFragment
}
setStartDestination(startDestination)
}
private fun setStartDestination(@IdRes startDestination: Int) {
this.startDestination = startDestination
val navController = getNavController()
val navInflater = navController.navInflater
val graph = navInflater.inflate(R.navigation.omnipod_pod_activation_wizard_navigation_graph)
graph.startDestination = startDestination
navController.graph = graph
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(KEY_START_DESTINATION, startDestination)
}
override fun getTotalDefinedNumberOfSteps(): Int = 5
override fun getActualNumberOfSteps(): Int {
if (startDestination == R.id.attachPodInfoFragment) {
return 3
}
return 5
}
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import android.app.AlertDialog
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.navigation.fragment.findNavController
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment.InfoFragmentBase
import kotlinx.android.synthetic.main.omnipod_wizard_nav_buttons.*
class AttachPodInfoFragment : InfoFragmentBase() {
@StringRes
override fun getTitleId(): Int = R.string.omnipod_pod_activation_wizard_attach_pod_title
@StringRes
override fun getTextId(): Int = R.string.omnipod_pod_activation_wizard_attach_pod_text
@IdRes
override fun getNextPageActionId(): Int = R.id.action_attachPodInfoFragment_to_insertCannulaActionFragment
override fun getIndex(): Int = 3
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omnipod_wizard_button_next.setOnClickListener {
AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(getString(getTitleId()))
.setMessage(getString(R.string.omnipod_pod_activation_wizard_attach_pod_confirm_insert_cannula_text))
.setPositiveButton(getString(R.string.omnipod_ok)) { _, _ -> findNavController().navigate(getNextPageActionId()) }
.setNegativeButton(getString(R.string.omnipod_cancel), null)
.show()
}
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment.InfoFragmentBase
class FillPodInfoFragment : InfoFragmentBase() {
@StringRes
override fun getTitleId(): Int = R.string.omnipod_pod_activation_wizard_fill_pod_title
@StringRes
override fun getTextId(): Int = R.string.omnipod_pod_activation_wizard_fill_pod_text
@IdRes
override fun getNextPageActionId(): Int = R.id.action_fillPodInfoFragment_to_initializePodActionFragment
override fun getIndex(): Int = 1
}

View file

@ -0,0 +1,35 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel.InitializePodActionViewModel
import javax.inject.Inject
class InitializePodActionFragment : PodActivationActionFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: InitializePodActionViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@StringRes
override fun getTitleId(): Int = R.string.omnipod_pod_activation_wizard_initialize_pod_title
@StringRes
override fun getTextId(): Int = R.string.omnipod_pod_activation_wizard_initialize_pod_text
@IdRes
override fun getNextPageActionId(): Int = R.id.action_initializePodActionFragment_to_attachPodInfoFragment
override fun getIndex(): Int = 2
}

View file

@ -0,0 +1,35 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodPluginQualifier
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel.InsertCannulaActionViewModel
import javax.inject.Inject
class InsertCannulaActionFragment : PodActivationActionFragmentBase() {
@Inject
@OmnipodPluginQualifier
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm: InsertCannulaActionViewModel by viewModels { viewModelFactory }
this.viewModel = vm
}
@StringRes
override fun getTitleId(): Int = R.string.omnipod_pod_activation_wizard_insert_cannula_title
@StringRes
override fun getTextId(): Int = R.string.omnipod_pod_activation_wizard_insert_cannula_text
@IdRes
override fun getNextPageActionId(): Int = R.id.action_insertCannulaActionFragment_to_PodActivatedInfoFragment
override fun getIndex(): Int = 4
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment.InfoFragmentBase
class PodActivatedInfoFragment : InfoFragmentBase() {
@StringRes
override fun getTitleId(): Int = R.string.omnipod_pod_activation_wizard_pod_activated_title
@StringRes
override fun getTextId(): Int = R.string.omnipod_pod_activation_wizard_pod_activated_text
@IdRes
override fun getNextPageActionId(): Int? = null
override fun getIndex(): Int = 5
}

View file

@ -0,0 +1,36 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fragment
import android.content.Intent
import android.os.Bundle
import android.view.View
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment.ActionFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.PodDeactivationWizardActivity
import kotlinx.android.synthetic.main.omnipod_wizard_action_page_fragment.*
import javax.inject.Inject
abstract class PodActivationActionFragmentBase : ActionFragmentBase() {
@Inject
protected lateinit var podStateManager: PodStateManager
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omnipod_wizard_button_deactivate_pod.setOnClickListener {
activity?.let {
startActivity(Intent(it, PodDeactivationWizardActivity::class.java))
it.finish()
}
}
}
override fun onActionFailure() {
if (podStateManager.isPodInitialized && podStateManager.podProgressStatus == PodProgressStatus.ACTIVATION_TIME_EXCEEDED) {
omnipod_wizard_action_error.setText(R.string.omnipod_error_pod_fault_activation_time_exceeded)
omnipod_wizard_button_retry.visibility = View.GONE
omnipod_wizard_button_deactivate_pod.visibility = View.VISIBLE
}
}
}

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.viewmodel.ActionViewModelBase
import javax.inject.Inject
class InitializePodActionViewModel @Inject constructor(private val aapsOmnipodManager: AapsOmnipodManager) : ActionViewModelBase() {
override fun doExecuteAction(): PumpEnactResult = aapsOmnipodManager.initializePod()
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.viewmodel
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.viewmodel.ActionViewModelBase
import javax.inject.Inject
class InsertCannulaActionViewModel @Inject constructor(private val aapsOmnipodManager: AapsOmnipodManager, private val profileFunction: ProfileFunction) : ActionViewModelBase() {
override fun doExecuteAction(): PumpEnactResult = aapsOmnipodManager.insertCannula(profileFunction.getProfile())
}

View file

@ -0,0 +1,34 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.activity
import android.app.AlertDialog
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.plugins.pump.omnipod.R
abstract class OmnipodWizardActivityBase : NoSplashAppCompatActivity() {
override fun onBackPressed() {
exitActivityAfterConfirmation()
}
fun exitActivityAfterConfirmation() {
if (getNavController().previousBackStackEntry == null) {
finish()
} else {
AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(getString(R.string.omnipod_wizard_exit_confirmation_title))
.setMessage(getString(R.string.omnipod_wizard_exit_confirmation_text))
.setPositiveButton(getString(R.string.omnipod_yes)) { _, _ -> finish() }
.setNegativeButton(getString(R.string.omnipod_no), null)
.show()
}
}
protected fun getNavController(): NavController =
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController
abstract fun getTotalDefinedNumberOfSteps(): Int
abstract fun getActualNumberOfSteps(): Int
}

View file

@ -0,0 +1,79 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment
import android.os.Bundle
import android.view.View
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.lifecycle.Observer
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.viewmodel.ActionViewModelBase
import info.nightscout.androidaps.utils.extensions.toVisibility
import kotlinx.android.synthetic.main.omnipod_wizard_action_page_fragment.*
import kotlinx.android.synthetic.main.omnipod_wizard_nav_buttons.*
abstract class ActionFragmentBase : WizardFragmentBase() {
protected lateinit var viewModel: ActionViewModelBase
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omnipod_wizard_button_next.isEnabled = false
omnipod_wizard_action_page_text.setText(getTextId())
omnipod_wizard_button_retry.setOnClickListener { viewModel.executeAction() }
viewModel.isActionExecutingLiveData.observe(viewLifecycleOwner, Observer { isExecuting ->
if (isExecuting) {
omnipod_wizard_action_error.visibility = View.GONE
omnipod_wizard_button_deactivate_pod.visibility = View.GONE
omnipod_wizard_button_discard_pod.visibility = View.GONE
omnipod_wizard_button_retry.visibility = View.GONE
}
omnipod_wizard_action_progress_indication.visibility = isExecuting.toVisibility()
omnipod_wizard_button_cancel.isEnabled = !isExecuting
})
viewModel.actionResultLiveData.observe(viewLifecycleOwner, Observer { result ->
result?.let {
val isExecuting = isActionExecuting()
omnipod_wizard_button_next.isEnabled = result.success
omnipod_wizard_action_success.visibility = result.success.toVisibility()
omnipod_wizard_action_error.visibility = (!isExecuting && !result.success).toVisibility()
omnipod_wizard_button_retry.visibility = (!isExecuting && !result.success).toVisibility()
if (result.success) {
onActionSuccess()
} else {
omnipod_wizard_action_error.text = result.comment
onActionFailure()
}
}
})
if (savedInstanceState == null && !isActionExecuting()) {
viewModel.executeAction()
}
}
protected fun isActionExecuting() = viewModel.isActionExecutingLiveData.value!!
override fun onDestroyView() {
super.onDestroyView()
viewModel.isActionExecutingLiveData.removeObservers(viewLifecycleOwner)
viewModel.actionResultLiveData.removeObservers(viewLifecycleOwner)
}
fun onActionSuccess() {}
open fun onActionFailure() {}
@StringRes
abstract fun getTextId(): Int
@LayoutRes
override fun getLayoutId(): Int {
return R.layout.omnipod_wizard_action_page_fragment
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment
import android.os.Bundle
import android.view.View
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import info.nightscout.androidaps.plugins.pump.omnipod.R
import kotlinx.android.synthetic.main.omnipod_wizard_info_page_fragment.*
abstract class InfoFragmentBase : WizardFragmentBase() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omnipod_wizard_info_page_text.setText(getTextId())
}
@StringRes
abstract fun getTextId(): Int
@LayoutRes
override fun getLayoutId(): Int {
return R.layout.omnipod_wizard_info_page_fragment
}
}

View file

@ -0,0 +1,84 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment
import android.content.res.ColorStateList
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.navigation.fragment.findNavController
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.activity.OmnipodWizardActivityBase
import kotlinx.android.synthetic.main.omnipod_wizard_base_fragment.*
import kotlinx.android.synthetic.main.omnipod_wizard_nav_buttons.*
import kotlinx.android.synthetic.main.omnipod_wizard_progress_indication.*
import kotlin.math.roundToInt
abstract class WizardFragmentBase : DaggerFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val baseView = inflater.inflate(R.layout.omnipod_wizard_base_fragment, container, false)
val contentView = baseView.findViewById<ViewStub>(R.id.omnipod_wizard_base_fragment_content)
contentView?.let {
it.layoutResource = getLayoutId()
it.inflate()
}
return baseView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omnipod_wizard_base_fragment_title.setText(getTitleId())
val nextPage = getNextPageActionId()
if (nextPage == null) {
omnipod_wizard_button_next.text = getString(R.string.omnipod_wizard_button_finish)
omnipod_wizard_button_next.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.review_green, context?.theme))
}
updateProgressIndication()
omnipod_wizard_button_next.setOnClickListener {
if (nextPage == null) {
activity?.finish()
} else {
findNavController().navigate(nextPage)
}
}
omnipod_wizard_button_cancel.setOnClickListener {
(activity as? OmnipodWizardActivityBase)?.exitActivityAfterConfirmation()
}
}
private fun updateProgressIndication() {
(activity as? OmnipodWizardActivityBase)?.let {
val numberOfSteps = it.getActualNumberOfSteps()
val currentFragment = getIndex() - (it.getTotalDefinedNumberOfSteps() - numberOfSteps)
val progressPercentage = (currentFragment / numberOfSteps.toDouble() * 100).roundToInt()
omnipod_wizard_progress_indication.progress = progressPercentage
}
}
@LayoutRes
protected abstract fun getLayoutId(): Int
@IdRes
protected abstract fun getNextPageActionId(): Int?
@StringRes
protected abstract fun getTitleId(): Int
protected abstract fun getIndex(): Int
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import info.nightscout.androidaps.data.PumpEnactResult
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.SingleSubject
abstract class ActionViewModelBase : ViewModel() {
private val _isActionExecutingLiveData = MutableLiveData<Boolean>(false)
val isActionExecutingLiveData: LiveData<Boolean> = _isActionExecutingLiveData
private val _actionResultLiveData = MutableLiveData<PumpEnactResult?>(null)
val actionResultLiveData: LiveData<PumpEnactResult?> = _actionResultLiveData
fun executeAction() {
_isActionExecutingLiveData.postValue(true)
val disposable = SingleSubject.fromCallable<PumpEnactResult>(this::doExecuteAction)
.subscribeOn(Schedulers.io())
.doOnSuccess { result ->
_isActionExecutingLiveData.postValue(false)
_actionResultLiveData.postValue(result)
}
.subscribe()
}
protected abstract fun doExecuteAction(): PumpEnactResult
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation
import android.os.Bundle
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.activity.OmnipodWizardActivityBase
class PodDeactivationWizardActivity : OmnipodWizardActivityBase() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.omnipod_pod_deactivation_wizard_activity)
}
override fun getTotalDefinedNumberOfSteps(): Int = 3
override fun getActualNumberOfSteps(): Int = 3
}

Some files were not shown because too many files have changed in this diff Show more