diff --git a/app/build.gradle b/app/build.gradle index 63b7948b91..85354209ff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -111,7 +111,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "2.8.2.1-dev-e4" + version "2.8.2.1-dev-e5" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' @@ -186,6 +186,7 @@ dependencies { implementation project(':danars') implementation project(':danar') implementation project(':insight') + implementation project(':pump-common') implementation project(':rileylink') implementation project(':medtronic') implementation project(':omnipod-common') @@ -197,8 +198,6 @@ dependencies { /* Dagger2 - We are going to use dagger.android which includes * support for Activity and fragment injection so we need to include * the following dependencies */ - - annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version" kapt "com.google.dagger:dagger-android-processor:$dagger_version" diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index 1421d586ed..5b74972932 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -8,13 +8,14 @@ import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.automation.di.AutomationModule import info.nightscout.androidaps.combo.di.ComboModule import info.nightscout.androidaps.dana.di.DanaHistoryModule -import info.nightscout.androidaps.di.CoreModule import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danars.di.DanaRSModule import info.nightscout.androidaps.database.DatabaseModule +import info.nightscout.androidaps.di.CoreModule import info.nightscout.androidaps.insight.di.InsightDatabaseModule import info.nightscout.androidaps.insight.di.InsightModule +import info.nightscout.androidaps.plugins.pump.common.di.PumpCommonModule import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule @@ -36,6 +37,7 @@ import javax.inject.Singleton CommandQueueModule::class, ObjectivesModule::class, WizardModule::class, + PumpCommonModule::class, RileyLinkModule::class, MedtronicModule::class, OmnipodErosModule::class, diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 65f3d692e7..639472c4ff 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -72,13 +72,17 @@ open class AppModule { @Module interface AppBindings { + @Binds fun bindContext(mainApp: MainApp): Context @Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector @Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin @Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider @Binds fun bindConfigInterface(config: ConfigImpl): Config - @Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder + + @Binds + fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder @Binds fun bindTreatmentsInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface + @Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs @@ -87,6 +91,7 @@ open class AppModule { @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator @Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector + @Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt index 75946070ed..0f6029064b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt @@ -184,4 +184,4 @@ class PluginStore @Inject constructor( override fun getPluginsList(): ArrayList = ArrayList(plugins) -} \ No newline at end of file +} diff --git a/core/src/main/java/info/nightscout/androidaps/db/DbObjectBase.kt b/core/src/main/java/info/nightscout/androidaps/db/DbObjectBase.kt deleted file mode 100644 index 76228abe9b..0000000000 --- a/core/src/main/java/info/nightscout/androidaps/db/DbObjectBase.kt +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.db - -interface DbObjectBase { - - val date: Long - val pumpId: Long -} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/db/OmnipodHistoryRecord.java b/core/src/main/java/info/nightscout/androidaps/db/OmnipodHistoryRecord.java index 0f4b56b096..584f6cc150 100644 --- a/core/src/main/java/info/nightscout/androidaps/db/OmnipodHistoryRecord.java +++ b/core/src/main/java/info/nightscout/androidaps/db/OmnipodHistoryRecord.java @@ -9,7 +9,7 @@ import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; * Created by andy on 30.11.2019. */ @DatabaseTable(tableName = "PodHistory") -public class OmnipodHistoryRecord implements DbObjectBase, Comparable { +public class OmnipodHistoryRecord implements Comparable { @DatabaseField(id = true) public long date; @@ -42,7 +42,6 @@ public class OmnipodHistoryRecord implements DbObjectBase, ComparableTime detection %1$dh %2$dm - - Operation not supported by pump and/or driver. - Operation not YET supported by pump. - OK - - - Never contacted - Waking up - Error with communication - Timeout on communication - Pump unreachable - Invalid configuration - Active - Sleeping - - - Basals - Configurations - Notifications - Statistics - Unknowns - All - Boluses - Prime - Alarms - Glucose Mute for 5 minutes diff --git a/gradle/test_dependencies.gradle b/gradle/test_dependencies.gradle index 3a484c419c..059f92d2a7 100644 --- a/gradle/test_dependencies.gradle +++ b/gradle/test_dependencies.gradle @@ -18,6 +18,7 @@ dependencies { androidTestImplementation "androidx.test:rules:$androidx_rules" androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + testImplementation 'org.mockito:mockito-inline:2.8.47' } android { diff --git a/medtronic/build.gradle b/medtronic/build.gradle index a3cb9f789e..4c3af48c8a 100644 --- a/medtronic/build.gradle +++ b/medtronic/build.gradle @@ -16,5 +16,6 @@ android { dependencies { implementation project(':core') + implementation project(':pump-common') implementation project(':rileylink') } diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java deleted file mode 100644 index d418787039..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java +++ /dev/null @@ -1,476 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common; - -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.convertedToAbsolute; -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes; - -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; - -import androidx.annotation.NonNull; - -import org.json.JSONException; -import org.json.JSONObject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.core.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.interfaces.Profile; -import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventCustomActionsChanged; -import info.nightscout.androidaps.extensions.PumpStateExtensionKt; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; -import info.nightscout.androidaps.interfaces.Constraints; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.interfaces.Pump; -import info.nightscout.androidaps.interfaces.PumpPluginBase; -import info.nightscout.androidaps.interfaces.PumpSync; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.common.ManufacturerType; -import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; -import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import info.nightscout.androidaps.utils.sharedPreferences.SP; -import io.reactivex.disposables.CompositeDisposable; - -/** - * Created by andy on 23.04.18. - */ - -// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin) - -public abstract class PumpPluginAbstract extends PumpPluginBase implements Pump, Constraints { - private final CompositeDisposable disposable = new CompositeDisposable(); - - protected HasAndroidInjector injector; - protected AAPSLogger aapsLogger; - protected RxBusWrapper rxBus; - protected ActivePlugin activePlugin; - protected Context context; - protected FabricPrivacy fabricPrivacy; - protected ResourceHelper resourceHelper; - protected CommandQueueProvider commandQueue; - protected SP sp; - protected DateUtil dateUtil; - protected PumpDescription pumpDescription = new PumpDescription(); - protected ServiceConnection serviceConnection; - protected boolean serviceRunning = false; - protected PumpDriverState pumpState = PumpDriverState.NotInitialized; - protected boolean displayConnectionMessages = false; - protected PumpType pumpType; - protected AapsSchedulers aapsSchedulers; - protected PumpSync pumpSync; - - protected PumpPluginAbstract( - PluginDescription pluginDescription, - PumpType pumpType, - HasAndroidInjector injector, - ResourceHelper resourceHelper, - AAPSLogger aapsLogger, - CommandQueueProvider commandQueue, - RxBusWrapper rxBus, - ActivePlugin activePlugin, - SP sp, - Context context, - FabricPrivacy fabricPrivacy, - DateUtil dateUtil, - AapsSchedulers aapsSchedulers, - PumpSync pumpSync - ) { - - super(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue); - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - this.activePlugin = activePlugin; - this.context = context; - this.fabricPrivacy = fabricPrivacy; - this.resourceHelper = resourceHelper; - this.sp = sp; - this.commandQueue = commandQueue; - - pumpDescription.fillFor(pumpType); - this.pumpType = pumpType; - this.dateUtil = dateUtil; - this.aapsSchedulers = aapsSchedulers; - this.pumpSync = pumpSync; - } - - - public abstract void initPumpStatusData(); - - - @Override - protected void onStart() { - super.onStart(); - - initPumpStatusData(); - - Intent intent = new Intent(context, getServiceClass()); - context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); - - serviceRunning = true; - - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> context.unbindService(serviceConnection), fabricPrivacy::logException) - ); - onStartCustomActions(); - } - - - @Override - protected void onStop() { - aapsLogger.debug(LTag.PUMP, this.deviceID() + " onStop()"); - - context.unbindService(serviceConnection); - - serviceRunning = false; - - disposable.clear(); - super.onStop(); - } - - - /** - * If we need to run any custom actions in onStart (triggering events, etc) - */ - public abstract void onStartCustomActions(); - - /** - * Service class (same one you did serviceConnection for) - * - * @return Class - */ - public abstract Class getServiceClass(); - - public abstract PumpStatus getPumpStatusData(); - - - public boolean isInitialized() { - return pumpState.isInitialized(); - } - - - public boolean isSuspended() { - return pumpState == PumpDriverState.Suspended; - } - - - public boolean isBusy() { - return pumpState == PumpDriverState.Busy; - } - - - public boolean isConnected() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isConnected [PumpPluginAbstract]."); - return pumpState.isConnected(); - } - - - public boolean isConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isConnecting [PumpPluginAbstract]."); - return pumpState == PumpDriverState.Connecting; - } - - - public void connect(@NonNull String reason) { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "connect (reason={}) [PumpPluginAbstract] - default (empty) implementation." + reason); - } - - - public void disconnect(@NonNull String reason) { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "disconnect (reason={}) [PumpPluginAbstract] - default (empty) implementation." + reason); - } - - - public void stopConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation."); - } - - - @Override - public boolean isHandshakeInProgress() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isHandshakeInProgress [PumpPluginAbstract] - default (empty) implementation."); - return false; - } - - - @Override - public void finishHandshaking() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "finishHandshaking [PumpPluginAbstract] - default (empty) implementation."); - } - - // Upload to pump new basal profile - @NonNull public PumpEnactResult setNewBasalProfile(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "setNewBasalProfile [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - public boolean isThisProfileSet(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "isThisProfileSet [PumpPluginAbstract] - Not implemented."); - return true; - } - - - public long lastDataTime() { - aapsLogger.debug(LTag.PUMP, "lastDataTime [PumpPluginAbstract]."); - return getPumpStatusData().lastConnection; - } - - - public double getBaseBasalRate() { - aapsLogger.debug(LTag.PUMP, "getBaseBasalRate [PumpPluginAbstract] - Not implemented."); - return 0.0d; - } // base basal rate, not temp basal - - - public void stopBolusDelivering() { - aapsLogger.debug(LTag.PUMP, "stopBolusDelivering [PumpPluginAbstract] - Not implemented."); - } - - - @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) { - aapsLogger.debug(LTag.PUMP, "setExtendedBolus [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - // some pumps might set a very short temp close to 100% as cancelling a temp can be noisy - // when the cancel request is requested by the user (forced), the pump should always do a real cancel - - @NonNull public PumpEnactResult cancelTempBasal(boolean enforceNew) { - aapsLogger.debug(LTag.PUMP, "cancelTempBasal [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull public PumpEnactResult cancelExtendedBolus() { - aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - // Status to be passed to NS - - // public JSONObject getJSONStatus(Profile profile, String profileName) { - // return pumpDriver.getJSONStatus(profile, profileName); - // } - - public String deviceID() { - aapsLogger.debug(LTag.PUMP, "deviceID [PumpPluginAbstract] - Not implemented."); - return "FakeDevice"; - } - - - // Pump capabilities - - @NonNull public PumpDescription getPumpDescription() { - return pumpDescription; - } - - - // Short info for SMS, Wear etc - - public boolean isFakingTempsByExtendedBoluses() { - aapsLogger.debug(LTag.PUMP, "isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented."); - return false; - } - - - @NonNull @Override - public PumpEnactResult loadTDDs() { - aapsLogger.debug(LTag.PUMP, "loadTDDs [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull @Override - public JSONObject getJSONStatus(@NonNull Profile profile, @NonNull String profileName, @NonNull String version) { - - if ((getPumpStatusData().lastConnection + 60 * 60 * 1000L) < System.currentTimeMillis()) { - return new JSONObject(); - } - - long now = System.currentTimeMillis(); - JSONObject pump = new JSONObject(); - JSONObject battery = new JSONObject(); - JSONObject status = new JSONObject(); - JSONObject extended = new JSONObject(); - try { - battery.put("percent", getPumpStatusData().batteryRemaining); - status.put("status", getPumpStatusData().pumpStatusType != null ? getPumpStatusData().pumpStatusType.getStatus() : "normal"); - extended.put("Version", version); - try { - extended.put("ActiveProfile", profileName); - } catch (Exception ignored) { - } - - PumpSync.PumpState.TemporaryBasal tb = pumpSync.expectedPumpState().getTemporaryBasal(); - if (tb != null) { - extended.put("TempBasalAbsoluteRate", convertedToAbsolute(tb, now, profile)); - extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.getTimestamp())); - extended.put("TempBasalRemaining", getPlannedRemainingMinutes(tb)); - } - - PumpSync.PumpState.ExtendedBolus eb = pumpSync.expectedPumpState().getExtendedBolus(); - if (eb != null) { - extended.put("ExtendedBolusAbsoluteRate", eb.getRate()); - extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.getTimestamp())); - extended.put("ExtendedBolusRemaining", getPlannedRemainingMinutes(eb)); - } - - status.put("timestamp", dateUtil.toISOString(dateUtil.now())); - - pump.put("battery", battery); - pump.put("status", status); - pump.put("extended", extended); - pump.put("reservoir", getPumpStatusData().reservoirRemainingUnits); - pump.put("clock", dateUtil.toISOString(dateUtil.now())); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - return pump; - } - - - // FIXME i18n, null checks: iob, TDD - @NonNull @Override - public String shortStatus(boolean veryShort) { - String ret = ""; - if (getPumpStatusData().lastConnection != 0) { - long agoMsec = System.currentTimeMillis() - getPumpStatusData().lastConnection; - int agoMin = (int) (agoMsec / 60d / 1000d); - ret += "LastConn: " + agoMin + " min ago\n"; - } - if (getPumpStatusData().lastBolusTime != null && getPumpStatusData().lastBolusTime.getTime() != 0) { - ret += "LastBolus: " + DecimalFormatter.INSTANCE.to2Decimal(getPumpStatusData().lastBolusAmount) + "U @" + // - android.text.format.DateFormat.format("HH:mm", getPumpStatusData().lastBolusTime) + "\n"; - } - PumpSync.PumpState.TemporaryBasal activeTemp = pumpSync.expectedPumpState().getTemporaryBasal(); - if (activeTemp != null) { - ret += "Temp: " + PumpStateExtensionKt.toStringFull(activeTemp, dateUtil) + "\n"; - } - PumpSync.PumpState.ExtendedBolus activeExtendedBolus = pumpSync.expectedPumpState().getExtendedBolus(); - if (activeExtendedBolus != null) { - ret += "Extended: " + PumpStateExtensionKt.toStringFull(activeExtendedBolus, dateUtil) + "\n"; - } - // if (!veryShort) { - // ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " - // + pumpStatus.maxDailyTotalUnits + " U\n"; - // } - ret += "IOB: " + getPumpStatusData().iob + "U\n"; - ret += "Reserv: " + DecimalFormatter.INSTANCE.to0Decimal(getPumpStatusData().reservoirRemainingUnits) + "U\n"; - ret += "Batt: " + getPumpStatusData().batteryRemaining + "\n"; - return ret; - } - - - @NonNull @Override - public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { - - try { - if (detailedBolusInfo.insulin == 0 && detailedBolusInfo.carbs == 0) { - // neither carbs nor bolus requested - aapsLogger.error("deliverTreatment: Invalid input"); - return new PumpEnactResult(getInjector()).success(false).enacted(false).bolusDelivered(0d).carbsDelivered(0d) - .comment(R.string.invalidinput); - } else if (detailedBolusInfo.insulin > 0) { - // bolus needed, ask pump to deliver it - return deliverBolus(detailedBolusInfo); - } else { - //if (MedtronicHistoryData.doubleBolusDebug) - // aapsLogger.debug("DoubleBolusDebug: deliverTreatment::(carb only entry)"); - - // no bolus required, carb only treatment - activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, true); - - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; - bolusingEvent.setT(new EventOverviewBolusProgress.Treatment(0, 0, detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB)); - bolusingEvent.setPercent(100); - rxBus.send(bolusingEvent); - - aapsLogger.debug(LTag.PUMP, "deliverTreatment: Carb only treatment."); - - return new PumpEnactResult(getInjector()).success(true).enacted(true).bolusDelivered(0d) - .carbsDelivered(detailedBolusInfo.carbs).comment(R.string.common_resultok); - } - } finally { - triggerUIChange(); - } - - } - - - protected void refreshCustomActionsList() { - rxBus.send(new EventCustomActionsChanged()); - } - - - @NonNull public ManufacturerType manufacturer() { - return pumpType.getManufacturer(); - } - - @NonNull - public PumpType model() { - return pumpType; - } - - - public PumpType getPumpType() { - return pumpType; - } - - - public void setPumpType(PumpType pumpType) { - this.pumpType = pumpType; - this.pumpDescription.fillFor(pumpType); - } - - - public boolean canHandleDST() { - return false; - } - - - protected abstract PumpEnactResult deliverBolus(DetailedBolusInfo detailedBolusInfo); - - protected abstract void triggerUIChange(); - - private PumpEnactResult getOperationNotSupportedWithCustomText(int resourceId) { - return new PumpEnactResult(getInjector()).success(false).enacted(false).comment(resourceId); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.java deleted file mode 100644 index 505cd1388f..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.java +++ /dev/null @@ -1,71 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common.data; - -import java.util.Date; - -import info.nightscout.androidaps.interfaces.ProfileStore; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpStatusType; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.utils.DateUtil; - -/** - * Created by andy on 4/28/18. - */ - -public abstract class PumpStatus { - - // connection - public long lastDataTime; - public long lastConnection = 0L; - public long previousConnection = 0L; // here should be stored last connection of previous session (so needs to be - // read before lastConnection is modified for first time). - - // last bolus - public Date lastBolusTime; - public Double lastBolusAmount; - - // other pump settings - public String activeProfileName = "0"; - public double reservoirRemainingUnits = 0.0d; - public int reservoirFullUnits = 0; - public int batteryRemaining = 0; // percent, so 0-100 - public Double batteryVoltage = null; - - - // iob - public String iob = null; - - // TDD - public Double dailyTotalUnits; - public String maxDailyTotalUnits; - public boolean validBasalRateProfileSelectedOnPump = true; - public ProfileStore profileStore; - public String units; // Constants.MGDL or Constants.MMOL - public PumpStatusType pumpStatusType = PumpStatusType.Running; - public Double[] basalsByHour; - public double currentBasal = 0; - public int tempBasalInProgress = 0; - public int tempBasalRatio = 0; - public int tempBasalRemainMin = 0; - public Date tempBasalStart; - public PumpType pumpType; - //protected PumpDescription pumpDescription; - - - public PumpStatus(PumpType pumpType) { - // public PumpStatus(PumpDescription pumpDescription) { - // this.pumpDescription = pumpDescription; - -// this.initSettings(); - this.pumpType = pumpType; - } - - public abstract void initSettings(); - - public void setLastCommunicationToNow() { - this.lastDataTime = System.currentTimeMillis(); - this.lastConnection = System.currentTimeMillis(); - } - - public abstract String getErrorInfo(); - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt index d9124ec15e..95274476fa 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt @@ -12,7 +12,6 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.events.EventTempBasalChange -import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.PumpSync @@ -99,7 +98,7 @@ class MedtronicFragment : DaggerFragment() { binding.pumpStatusIcon.text = "{fa-bed}" binding.history.setOnClickListener { - if (medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() == true) { + if (medtronicPumpPlugin.rileyLinkService.verifyConfiguration() == true) { startActivity(Intent(context, MedtronicHistoryActivity::class.java)) } else { displayNotConfiguredDialog() @@ -107,7 +106,7 @@ class MedtronicFragment : DaggerFragment() { } binding.refresh.setOnClickListener { - if (medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() != true) { + if (medtronicPumpPlugin.rileyLinkService.verifyConfiguration() != true) { displayNotConfiguredDialog() } else { binding.refresh.isEnabled = false @@ -121,7 +120,7 @@ class MedtronicFragment : DaggerFragment() { } binding.stats.setOnClickListener { - if (medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() == true) { + if (medtronicPumpPlugin.rileyLinkService.verifyConfiguration() == true) { startActivity(Intent(context, RileyLinkStatusActivity::class.java)) } else { displayNotConfiguredDialog() @@ -161,7 +160,7 @@ class MedtronicFragment : DaggerFragment() { .observeOn(aapsSchedulers.main) .subscribe({ aapsLogger.debug(LTag.PUMP, "EventMedtronicPumpConfigurationChanged triggered") - medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() + medtronicPumpPlugin.rileyLinkService.verifyConfiguration() updateGUI() }, fabricPrivacy::logException) disposable += rxBus @@ -193,7 +192,7 @@ class MedtronicFragment : DaggerFragment() { @Synchronized private fun setDeviceStatus() { val resourceId = rileyLinkServiceData.rileyLinkServiceState.resourceId - val rileyLinkError = medtronicPumpPlugin.rileyLinkService?.error + val rileyLinkError = medtronicPumpPlugin.rileyLinkService.error binding.rlStatus.text = when { rileyLinkServiceData.rileyLinkServiceState == RileyLinkServiceState.NotStarted -> resourceHelper.gs(resourceId) @@ -210,35 +209,38 @@ class MedtronicFragment : DaggerFragment() { } ?: "-" when (medtronicPumpStatus.pumpDeviceState) { - null, - PumpDeviceState.Sleeping -> binding.pumpStatusIcon.text = "{fa-bed} " // + pumpStatus.pumpDeviceState.name()); + PumpDeviceState.Sleeping -> + binding.pumpStatusIcon.text = "{fa-bed} " // + pumpStatus.pumpDeviceState.name()); + PumpDeviceState.NeverContacted, PumpDeviceState.WakingUp, PumpDeviceState.PumpUnreachable, PumpDeviceState.ErrorWhenCommunicating, PumpDeviceState.TimeoutWhenCommunicating, - PumpDeviceState.InvalidConfiguration -> binding.pumpStatusIcon.text = " " + resourceHelper.gs(medtronicPumpStatus.pumpDeviceState.resourceId) + PumpDeviceState.InvalidConfiguration -> + binding.pumpStatusIcon.text = " " + resourceHelper.gs(medtronicPumpStatus.pumpDeviceState.resourceId) PumpDeviceState.Active -> { - val cmd = medtronicUtil.currentCommand + val cmd = medtronicUtil.getCurrentCommand() if (cmd == null) binding.pumpStatusIcon.text = " " + resourceHelper.gs(medtronicPumpStatus.pumpDeviceState.resourceId) else { aapsLogger.debug(LTag.PUMP, "Command: $cmd") - val cmdResourceId = cmd.resourceId + val cmdResourceId = cmd.resourceId //!! if (cmd == MedtronicCommandType.GetHistoryData) { binding.pumpStatusIcon.text = medtronicUtil.frameNumber?.let { - resourceHelper.gs(cmdResourceId, medtronicUtil.pageNumber, medtronicUtil.frameNumber) + resourceHelper.gs(cmdResourceId!!, medtronicUtil.pageNumber, medtronicUtil.frameNumber) } ?: resourceHelper.gs(R.string.medtronic_cmd_desc_get_history_request, medtronicUtil.pageNumber) } else { binding.pumpStatusIcon.text = " " + (cmdResourceId?.let { resourceHelper.gs(it) } - ?: cmd.getCommandDescription()) + ?: cmd.commandDescription) } } } - else -> aapsLogger.warn(LTag.PUMP, "Unknown pump state: " + medtronicPumpStatus.pumpDeviceState) + else -> + aapsLogger.warn(LTag.PUMP, "Unknown pump state: " + medtronicPumpStatus.pumpDeviceState) } val status = commandQueue.spannedStatus() @@ -298,26 +300,31 @@ class MedtronicFragment : DaggerFragment() { val bolus = medtronicPumpStatus.lastBolusAmount val bolusTime = medtronicPumpStatus.lastBolusTime if (bolus != null && bolusTime != null) { - val agoMsc = System.currentTimeMillis() - medtronicPumpStatus.lastBolusTime.time + val agoMsc = System.currentTimeMillis() - bolusTime.time val bolusMinAgo = agoMsc.toDouble() / 60.0 / 1000.0 val unit = resourceHelper.gs(R.string.insulin_unit_shortname) val ago = when { agoMsc < 60 * 1000 -> resourceHelper.gs(R.string.medtronic_pump_connected_now) - bolusMinAgo < 60 -> dateUtil.minAgo(resourceHelper, medtronicPumpStatus.lastBolusTime.time) - else -> dateUtil.hourAgo(medtronicPumpStatus.lastBolusTime.time, resourceHelper) + bolusMinAgo < 60 -> dateUtil.minAgo(resourceHelper, bolusTime.time) + else -> dateUtil.hourAgo(bolusTime.time, resourceHelper) } binding.lastBolus.text = resourceHelper.gs(R.string.mdt_last_bolus, bolus, unit, ago) } else { binding.lastBolus.text = "" } - val pumpState = pumpSync.expectedPumpState() // base basal rate binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") " + resourceHelper.gs(R.string.pump_basebasalrate, medtronicPumpPlugin.baseBasalRate)) - binding.tempBasal.text = pumpState.temporaryBasal?.toStringFull(dateUtil) - ?: "" + // TBR + var tbrStr = "" + val tbrRemainingTime: Int? = medtronicPumpStatus.tbrRemainingTime + + if (tbrRemainingTime != null) { + tbrStr = resourceHelper.gs(R.string.mdt_tbr_remaining, medtronicPumpStatus.tempBasalAmount, tbrRemainingTime); + } + binding.tempBasal.text = tbrStr // battery if (medtronicPumpStatus.batteryType == BatteryType.None || medtronicPumpStatus.batteryVoltage == null) { @@ -331,7 +338,7 @@ class MedtronicFragment : DaggerFragment() { binding.reservoir.text = resourceHelper.gs(R.string.reservoirvalue, medtronicPumpStatus.reservoirRemainingUnits, medtronicPumpStatus.reservoirFullUnits) warnColors.setColorInverse(binding.reservoir, medtronicPumpStatus.reservoirRemainingUnits, 50.0, 20.0) - medtronicPumpPlugin.rileyLinkService?.verifyConfiguration() + medtronicPumpPlugin.rileyLinkService.verifyConfiguration() binding.errors.text = medtronicPumpStatus.errorInfo val showRileyLinkBatteryLevel: Boolean = rileyLinkServiceData.showBatteryLevel diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java deleted file mode 100644 index b77081c48b..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ /dev/null @@ -1,1600 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic; - -import android.content.ComponentName; -import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.SystemClock; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.Preference; - -import org.joda.time.LocalDateTime; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.activities.ErrorHelperActivity; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.interfaces.Profile; -import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.Pump; -import info.nightscout.androidaps.interfaces.PumpSync; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.common.ManufacturerType; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract; -import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpDevice; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpInfo; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUITask; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfileEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCustomActionType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicStatusRefreshType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged; -import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged; -import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.TimeChangeType; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - - -/** - * Created by andy on 23.04.18. - * - * @author Andy Rozman (andy.rozman@gmail.com) - */ -@Singleton -public class MedtronicPumpPlugin extends PumpPluginAbstract implements Pump, RileyLinkPumpDevice { - - private final SP sp; - private final MedtronicUtil medtronicUtil; - private final MedtronicPumpStatus medtronicPumpStatus; - private final MedtronicHistoryData medtronicHistoryData; - private final RileyLinkServiceData rileyLinkServiceData; - private final ServiceTaskExecutor serviceTaskExecutor; - - private RileyLinkMedtronicService rileyLinkMedtronicService; - - // variables for handling statuses and history - private boolean firstRun = true; - private boolean isRefresh = false; - private final Map statusRefreshMap = new HashMap<>(); - private boolean isInitialized = false; - private PumpHistoryEntry lastPumpHistoryEntry; - - public static boolean isBusy = false; - private final List busyTimestamps = new ArrayList<>(); - private boolean hasTimeDateOrTimeZoneChanged = false; - - - @Inject - public MedtronicPumpPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - Context context, - ResourceHelper resourceHelper, - ActivePlugin activePlugin, - SP sp, - CommandQueueProvider commandQueue, - FabricPrivacy fabricPrivacy, - MedtronicUtil medtronicUtil, - MedtronicPumpStatus medtronicPumpStatus, - MedtronicHistoryData medtronicHistoryData, - RileyLinkServiceData rileyLinkServiceData, - ServiceTaskExecutor serviceTaskExecutor, - DateUtil dateUtil, - AapsSchedulers aapsSchedulers, - PumpSync pumpSync - ) { - - super(new PluginDescription() // - .mainType(PluginType.PUMP) // - .fragmentClass(MedtronicFragment.class.getName()) // - .pluginIcon(R.drawable.ic_veo_128) - .pluginName(R.string.medtronic_name) // - .shortName(R.string.medtronic_name_short) // - .preferencesId(R.xml.pref_medtronic) - .description(R.string.description_pump_medtronic), // - PumpType.MEDTRONIC_522_722, // we default to most basic model, correct model from config is loaded later - injector, resourceHelper, aapsLogger, commandQueue, rxBus, activePlugin, sp, context, fabricPrivacy, dateUtil, aapsSchedulers, pumpSync - ); - - this.medtronicUtil = medtronicUtil; - this.sp = sp; - this.medtronicPumpStatus = medtronicPumpStatus; - this.medtronicHistoryData = medtronicHistoryData; - this.rileyLinkServiceData = rileyLinkServiceData; - this.serviceTaskExecutor = serviceTaskExecutor; - - displayConnectionMessages = false; - } - - - @Override - protected void onStart() { - aapsLogger.debug(LTag.PUMP, this.deviceID() + " started."); - - serviceConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is disconnected"); - rileyLinkMedtronicService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is connected"); - RileyLinkMedtronicService.LocalBinder mLocalBinder = (RileyLinkMedtronicService.LocalBinder) service; - rileyLinkMedtronicService = mLocalBinder.getServiceInstance(); - rileyLinkMedtronicService.verifyConfiguration(); - - new Thread(() -> { - - for (int i = 0; i < 20; i++) { - SystemClock.sleep(5000); - - aapsLogger.debug(LTag.PUMP, "Starting Medtronic-RileyLink service"); - if (rileyLinkMedtronicService.setNotInPreInit()) { - break; - } - } - }).start(); - } - }; - - super.onStart(); - } - - @Override - public void updatePreferenceSummary(@NonNull Preference pref) { - super.updatePreferenceSummary(pref); - - if (pref.getKey().equals(getResourceHelper().gs(R.string.key_rileylink_mac_address))) { - String value = sp.getStringOrNull(R.string.key_rileylink_mac_address, null); - pref.setSummary(value == null ? getResourceHelper().gs(R.string.not_set_short) : value); - } - } - - private String getLogPrefix() { - return "MedtronicPumpPlugin::"; - } - - @Override - public void initPumpStatusData() { - - medtronicPumpStatus.lastConnection = sp.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); - medtronicPumpStatus.lastDataTime = medtronicPumpStatus.lastConnection; - medtronicPumpStatus.previousConnection = medtronicPumpStatus.lastConnection; - - //if (rileyLinkMedtronicService != null) rileyLinkMedtronicService.verifyConfiguration(); - - aapsLogger.debug(LTag.PUMP, "initPumpStatusData: " + this.medtronicPumpStatus); - - // this is only thing that can change, by being configured - pumpDescription.setMaxTempAbsolute((medtronicPumpStatus.maxBasal != null) ? medtronicPumpStatus.maxBasal : 35.0d); - - // set first Medtronic Pump Start - if (!sp.contains(MedtronicConst.Statistics.FirstPumpStart)) { - sp.putLong(MedtronicConst.Statistics.FirstPumpStart, System.currentTimeMillis()); - } - - migrateSettings(); - - } - - @Override - public void triggerPumpConfigurationChangedEvent() { - rxBus.send(new EventMedtronicPumpConfigurationChanged()); - } - - private void migrateSettings() { - - if ("US (916 MHz)".equals(sp.getString(MedtronicConst.Prefs.PumpFrequency, "US (916 MHz)"))) { - sp.putString(MedtronicConst.Prefs.PumpFrequency, getResourceHelper().gs(R.string.key_medtronic_pump_frequency_us_ca)); - } - - String encoding = sp.getString(MedtronicConst.Prefs.Encoding, "RileyLink 4b6b Encoding"); - - if ("RileyLink 4b6b Encoding".equals(encoding)) { - sp.putString(MedtronicConst.Prefs.Encoding, getResourceHelper().gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink)); - } - - if ("Local 4b6b Encoding".equals(encoding)) { - sp.putString(MedtronicConst.Prefs.Encoding, getResourceHelper().gs(R.string.key_medtronic_pump_encoding_4b6b_local)); - } - } - - - public void onStartCustomActions() { - - // check status every minute (if any status needs refresh we send readStatus command) - new Thread(() -> { - - do { - SystemClock.sleep(60000); - - if (this.isInitialized) { - - Map statusRefresh = workWithStatusRefresh( - StatusRefreshAction.GetData, null, null); - - if (doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { - if (!getCommandQueue().statusInQueue()) { - getCommandQueue().readStatus("Scheduled Status Refresh", null); - } - } - - clearBusyQueue(); - } - - } while (serviceRunning); - - }).start(); - } - - - public Class getServiceClass() { - return RileyLinkMedtronicService.class; - } - - @Override public PumpStatus getPumpStatusData() { - return medtronicPumpStatus; - } - - @Override - public String deviceID() { - return "Medtronic"; - } - - - @Override - public boolean isFakingTempsByExtendedBoluses() { - return false; - } - - - @Override - public boolean canHandleDST() { - return false; - } - - - // Pump Plugin - - private boolean isServiceSet() { - return rileyLinkMedtronicService != null; - } - - @Nullable - public RileyLinkMedtronicService getRileyLinkService() { - return rileyLinkMedtronicService; - } - - @Override public RileyLinkPumpInfo getPumpInfo() { - String frequency = resourceHelper.gs(medtronicPumpStatus.pumpFrequency.equals("medtronic_pump_frequency_us_ca") ? R.string.medtronic_pump_frequency_us_ca : R.string.medtronic_pump_frequency_worldwide); - String model = medtronicPumpStatus.medtronicDeviceType == null ? "???" : "Medtronic " + medtronicPumpStatus.medtronicDeviceType.getPumpModel(); - String serialNumber = medtronicPumpStatus.serialNumber; - return new RileyLinkPumpInfo(frequency, model, serialNumber); - } - - @Override public long getLastConnectionTimeMillis() { - return medtronicPumpStatus.lastConnection; - } - - @Override public void setLastCommunicationToNow() { - medtronicPumpStatus.setLastCommunicationToNow(); - } - - @Override - public boolean isInitialized() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isInitialized"); - return isServiceSet() && isInitialized; - } - - - @Override - public void setBusy(boolean busy) { - isBusy = busy; - } - - @Override - public boolean isBusy() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isBusy"); - - if (isServiceSet()) { - - if (isBusy) - return true; - - if (busyTimestamps.size() > 0) { - - clearBusyQueue(); - - return busyTimestamps.size() > 0; - } - } - - return false; - } - - - private synchronized void clearBusyQueue() { - - if (busyTimestamps.size() == 0) { - return; - } - - Set deleteFromQueue = new HashSet<>(); - - for (Long busyTimestamp : busyTimestamps) { - - if (System.currentTimeMillis() > busyTimestamp) { - deleteFromQueue.add(busyTimestamp); - } - } - - if (deleteFromQueue.size() == busyTimestamps.size()) { - busyTimestamps.clear(); - setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false); - } - - if (deleteFromQueue.size() > 0) { - busyTimestamps.removeAll(deleteFromQueue); - } - - } - - - @Override - public boolean isConnected() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnected"); - return isServiceSet() && rileyLinkMedtronicService.isInitialized(); - } - - - @Override - public boolean isConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnecting"); - return !isServiceSet() || !rileyLinkMedtronicService.isInitialized(); - } - - - @Override - public void getPumpStatus(String reason) { - boolean needRefresh = true; - - if (firstRun) { - needRefresh = initializePump(!isRefresh); - } else { - refreshAnyStatusThatNeedsToBeRefreshed(); - } - - if (needRefresh) - rxBus.send(new EventMedtronicPumpValuesChanged()); - } - - - void resetStatusState() { - firstRun = true; - isRefresh = true; - } - - - private boolean isPumpNotReachable() { - - RileyLinkServiceState rileyLinkServiceState = rileyLinkServiceData.rileyLinkServiceState; - - if (rileyLinkServiceState == null) { - aapsLogger.debug(LTag.PUMP, "RileyLink unreachable. RileyLinkServiceState is null."); - return false; - } - - if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // - && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // - && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice) { - aapsLogger.debug(LTag.PUMP, "RileyLink unreachable."); - return false; - } - - return (!rileyLinkMedtronicService.getDeviceCommunicationManager().isDeviceReachable()); - } - - - private void refreshAnyStatusThatNeedsToBeRefreshed() { - - Map statusRefresh = workWithStatusRefresh(StatusRefreshAction.GetData, null, - null); - - if (!doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { - return; - } - - boolean resetTime = false; - - if (isPumpNotReachable()) { - aapsLogger.error("Pump unreachable."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, getResourceHelper(), rxBus); - - return; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - - if (hasTimeDateOrTimeZoneChanged) { - - checkTimeAndOptionallySetTime(); - - // read time if changed, set new time - hasTimeDateOrTimeZoneChanged = false; - } - - - // execute - Set refreshTypesNeededToReschedule = new HashSet<>(); - - for (Map.Entry refreshType : statusRefresh.entrySet()) { - - if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { - - switch (refreshType.getKey()) { - case PumpHistory: { - readPumpHistory(); - } - break; - - case PumpTime: { - checkTimeAndOptionallySetTime(); - refreshTypesNeededToReschedule.add(refreshType.getKey()); - resetTime = true; - } - break; - - case BatteryStatus: - case RemainingInsulin: { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(refreshType.getKey().getCommandType(medtronicUtil.getMedtronicPumpModel())); - refreshTypesNeededToReschedule.add(refreshType.getKey()); - resetTime = true; - } - break; - - case Configuration: { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(refreshType.getKey().getCommandType(medtronicUtil.getMedtronicPumpModel())); - resetTime = true; - } - break; - } - } - - // reschedule - for (MedtronicStatusRefreshType refreshType2 : refreshTypesNeededToReschedule) { - scheduleNextRefresh(refreshType2); - } - - } - - if (resetTime) - medtronicPumpStatus.setLastCommunicationToNow(); - - } - - - private boolean doWeHaveAnyStatusNeededRefereshing(Map statusRefresh) { - - for (Map.Entry refreshType : statusRefresh.entrySet()) { - - if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { - return true; - } - } - - return hasTimeDateOrTimeZoneChanged; - } - - - private void setRefreshButtonEnabled(boolean enabled) { - rxBus.send(new EventRefreshButtonState(enabled)); - } - - - private boolean initializePump(boolean realInit) { - - if (rileyLinkMedtronicService == null) - return false; - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "initializePump - start"); - - rileyLinkMedtronicService.getDeviceCommunicationManager().setDoWakeUpBeforeCommand(false); - - setRefreshButtonEnabled(false); - - if (isRefresh) { - if (isPumpNotReachable()) { - aapsLogger.error(getLogPrefix() + "initializePump::Pump unreachable."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, getResourceHelper(), rxBus); - - setRefreshButtonEnabled(true); - - return true; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - } - - // model (once) - if (medtronicUtil.getMedtronicPumpModel() == null) { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.PumpModel); - } else { - if (medtronicPumpStatus.medtronicDeviceType != medtronicUtil.getMedtronicPumpModel()) { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "Configured pump is not the same as one detected."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, getResourceHelper(), rxBus); - } - } - - this.pumpState = PumpDriverState.Connected; - - // time (1h) - checkTimeAndOptionallySetTime(); - - readPumpHistory(); - - // remaining insulin (>50 = 4h; 50-20 = 1h; 15m) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRemainingInsulin); - scheduleNextRefresh(MedtronicStatusRefreshType.RemainingInsulin, 10); - - // remaining power (1h) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBatteryStatus); - scheduleNextRefresh(MedtronicStatusRefreshType.BatteryStatus, 20); - - // configuration (once and then if history shows config changes) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.getSettings(medtronicUtil.getMedtronicPumpModel())); - - // read profile (once, later its controlled by isThisProfileSet method) - getBasalProfiles(); - - int errorCount = rileyLinkMedtronicService.getMedtronicUIComm().getInvalidResponsesCount(); - - if (errorCount >= 5) { - aapsLogger.error("Number of error counts was 5 or more. Starting tunning."); - setRefreshButtonEnabled(true); - serviceTaskExecutor.startTask(new WakeAndTuneTask(getInjector())); - return true; - } - - medtronicPumpStatus.setLastCommunicationToNow(); - setRefreshButtonEnabled(true); - - if (!isRefresh) { - pumpState = PumpDriverState.Initialized; - } - - isInitialized = true; - // this.pumpState = PumpDriverState.Initialized; - - this.firstRun = false; - - return true; - } - - private void getBasalProfiles() { - - MedtronicUITask medtronicUITask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBasalProfileSTD); - - if (medtronicUITask.getResponseType() == MedtronicUIResponseType.Error) { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBasalProfileSTD); - } - } - - - @Override - public boolean isThisProfileSet(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "isThisProfileSet: basalInitalized=" + medtronicPumpStatus.basalProfileStatus); - - if (!isInitialized) - return true; - - if (medtronicPumpStatus.basalProfileStatus == BasalProfileStatus.NotInitialized) { - // this shouldn't happen, but if there was problem we try again - getBasalProfiles(); - return isProfileSame(profile); - } else if (medtronicPumpStatus.basalProfileStatus == BasalProfileStatus.ProfileChanged) { - return false; - } - - return (medtronicPumpStatus.basalProfileStatus != BasalProfileStatus.ProfileOK) || isProfileSame(profile); - } - - - private boolean isProfileSame(Profile profile) { - - boolean invalid = false; - Double[] basalsByHour = medtronicPumpStatus.basalsByHour; - - aapsLogger.debug(LTag.PUMP, "Current Basals (h): " - + (basalsByHour == null ? "null" : BasalProfile.getProfilesByHourToString(basalsByHour))); - - // int index = 0; - - if (basalsByHour == null) - return true; // we don't want to set profile again, unless we are sure - - StringBuilder stringBuilder = new StringBuilder("Requested Basals (h): "); - - for (Profile.ProfileValue basalValue : profile.getBasalValues()) { - - double basalValueValue = pumpDescription.getPumpType().determineCorrectBasalSize(basalValue.getValue()); - - int hour = basalValue.getTimeAsSeconds() / (60 * 60); - - if (!MedtronicUtil.isSame(basalsByHour[hour], basalValueValue)) { - invalid = true; - } - - stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)); - stringBuilder.append(" "); - } - - aapsLogger.debug(LTag.PUMP, stringBuilder.toString()); - - if (!invalid) { - aapsLogger.debug(LTag.PUMP, "Basal profile is same as AAPS one."); - } else { - aapsLogger.debug(LTag.PUMP, "Basal profile on Pump is different than the AAPS one."); - } - - return (!invalid); - } - - - @Override - public long lastDataTime() { - - if (medtronicPumpStatus.lastConnection != 0) { - return medtronicPumpStatus.lastConnection; - } - - return System.currentTimeMillis(); - } - - - @Override - public double getBaseBasalRate() { - return medtronicPumpStatus.getBasalProfileForHour(); - } - - - @Override - public double getReservoirLevel() { - return medtronicPumpStatus.reservoirRemainingUnits; - } - - - @Override - public int getBatteryLevel() { - return medtronicPumpStatus.batteryRemaining; - } - - protected void triggerUIChange() { - rxBus.send(new EventMedtronicPumpValuesChanged()); - } - - private BolusDeliveryType bolusDeliveryType = BolusDeliveryType.Idle; - - private enum BolusDeliveryType { - Idle, // - DeliveryPrepared, // - Delivering, // - CancelDelivery - } - - - private void checkTimeAndOptionallySetTime() { - - aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Start"); - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Pump Unreachable."); - setRefreshButtonEnabled(true); - return; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRealTimeClock); - - ClockDTO clock = medtronicUtil.getPumpTime(); - - if (clock == null) { // retry - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRealTimeClock); - - clock = medtronicUtil.getPumpTime(); - } - - if (clock == null) - return; - - int timeDiff = Math.abs(clock.timeDifference); - - if (timeDiff > 20) { - - if ((clock.localDeviceTime.getYear() <= 2015) || (timeDiff <= 24 * 60 * 60)) { - - aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Set time on pump.", timeDiff)); - - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetRealTimeClock); - - if (clock.timeDifference == 0) { - Notification notification = new Notification(Notification.INSIGHT_DATE_TIME_UPDATED, getResourceHelper().gs(R.string.pump_time_updated), Notification.INFO, 60); - rxBus.send(new EventNewNotification(notification)); - } - } else { - if ((clock.localDeviceTime.getYear() > 2015)) { - aapsLogger.error(String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference over 24h requested [diff=%d s]. Doing nothing.", timeDiff)); - medtronicUtil.sendNotification(MedtronicNotificationType.TimeChangeOver24h, getResourceHelper(), rxBus); - } - } - - } else { - aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Do nothing.", timeDiff)); - } - - scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0); - } - - - @NonNull - protected PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) { - - aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - " + BolusDeliveryType.DeliveryPrepared); - - setRefreshButtonEnabled(false); - - if (detailedBolusInfo.insulin > medtronicPumpStatus.reservoirRemainingUnits) { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(getResourceHelper().gs(R.string.medtronic_cmd_bolus_could_not_be_delivered_no_insulin, - medtronicPumpStatus.reservoirRemainingUnits, - detailedBolusInfo.insulin)); - } - - bolusDeliveryType = BolusDeliveryType.DeliveryPrepared; - - if (isPumpNotReachable()) { - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - Pump Unreachable."); - return setNotReachable(true, false); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled."); - return setNotReachable(true, true); - } - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Starting wait period."); - - int sleepTime = sp.getInt(MedtronicConst.Prefs.BolusDelay, 10) * 1000; - - SystemClock.sleep(sleepTime); - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled, before wait period."); - return setNotReachable(true, true); - } - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - End wait period. Start delivery"); - - try { - - bolusDeliveryType = BolusDeliveryType.Delivering; - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Start delivery"); - - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetBolus, - detailedBolusInfo.insulin); - - Boolean response = (Boolean) responseTask.returnData; - - setRefreshButtonEnabled(true); - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Response: {}", response); - - if (response) { - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled after Bolus started."); - - new Thread(() -> { - // Looper.prepare(); - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog - before"); - SystemClock.sleep(2000); - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog. Context: " - // + MainApp.instance().getApplicationContext()); - - ErrorHelperActivity.Companion.runAlarm(context, getResourceHelper().gs(R.string.medtronic_cmd_cancel_bolus_not_supported), getResourceHelper().gs(R.string.medtronic_warning), R.raw.boluserror); - }).start(); - } - - long now = System.currentTimeMillis(); - - detailedBolusInfo.timestamp = now; - detailedBolusInfo.deliverAtTheLatest = now; // not sure about that one - - activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, true); - - // we subtract insulin, exact amount will be visible with next remainingInsulin update. - medtronicPumpStatus.reservoirRemainingUnits -= detailedBolusInfo.insulin; - - incrementStatistics(detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB ? MedtronicConst.Statistics.SMBBoluses - : MedtronicConst.Statistics.StandardBoluses); - - - // calculate time for bolus and set driver to busy for that time - int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d); - long time = now + (bolusTime * 1000); - - this.busyTimestamps.add(time); - setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true); - - return new PumpEnactResult(getInjector()).success(true) // - .enacted(true) // - .bolusDelivered(detailedBolusInfo.insulin) // - .carbsDelivered(detailedBolusInfo.carbs); - - } else { - return new PumpEnactResult(getInjector()) // - .success(bolusDeliveryType == BolusDeliveryType.CancelDelivery) // - .enacted(false) // - .comment(R.string.medtronic_cmd_bolus_could_not_be_delivered); - } - - } finally { - finishAction("Bolus"); - this.bolusDeliveryType = BolusDeliveryType.Idle; - } - } - - - private PumpEnactResult setNotReachable(boolean isBolus, boolean success) { - setRefreshButtonEnabled(true); - - if (isBolus) { - bolusDeliveryType = BolusDeliveryType.Idle; - } - - if (success) { - return new PumpEnactResult(getInjector()) // - .success(true) // - .enacted(false); - } else { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - } - - - public void stopBolusDelivering() { - - this.bolusDeliveryType = BolusDeliveryType.CancelDelivery; - - // if (isLoggingEnabled()) - // LOG.warn("MedtronicPumpPlugin::deliverBolus - Stop Bolus Delivery."); - } - - - private void incrementStatistics(String statsKey) { - long currentCount = sp.getLong(statsKey, 0L); - currentCount++; - sp.putLong(statsKey, currentCount); - } - - - // if enforceNew===true current temp basal is canceled 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 - @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute: rate: " + absoluteRate + ", duration=" + durationInMinutes); - - // read current TBR - TempBasalPair tbrCurrent = readTBR(); - - if (tbrCurrent == null) { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - Could not read current TBR, canceling operation."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_read_tbr); - } else { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute: Current Basal: duration: " + tbrCurrent.getDurationMinutes() + " min, rate=" + tbrCurrent.getInsulinRate()); - } - - if (!enforceNew) { - - if (MedtronicUtil.isSame(tbrCurrent.getInsulinRate(), absoluteRate)) { - - boolean sameRate = true; - if (MedtronicUtil.isSame(0.0d, absoluteRate) && durationInMinutes > 0) { - // if rate is 0.0 and duration>0 then the rate is not the same - sameRate = false; - } - - if (sameRate) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - No enforceNew and same rate. Exiting."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(true).enacted(false); - } - } - // if not the same rate, we cancel and start new - } - - // if TBR is running we will cancel it. - if (tbrCurrent.getInsulinRate() != 0.0f && tbrCurrent.getDurationMinutes() > 0) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - TBR running - so canceling it."); - - // CANCEL - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.CancelTBR); - - Boolean response = (Boolean) responseTask2.returnData; - - if (response) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - Current TBR cancelled."); - } else { - aapsLogger.error(getLogPrefix() + "setTempBasalAbsolute - Cancel TBR failed."); - - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_cancel_tbr_stop_op); - } - } - - // now start new TBR - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetTemporaryBasal, - absoluteRate, durationInMinutes); - - Boolean response = (Boolean) responseTask.returnData; - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - setTBR. Response: " + response); - - if (response) { - // FIXME put this into UIPostProcessor - medtronicPumpStatus.tempBasalStart = new Date(); - medtronicPumpStatus.tempBasalAmount = absoluteRate; - medtronicPumpStatus.tempBasalLength = durationInMinutes; - - TemporaryBasal tempStart = new TemporaryBasal(getInjector()) // - .date(System.currentTimeMillis()) // - .duration(durationInMinutes) // - .absolute(absoluteRate) // - .source(Source.USER); - - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); - - incrementStatistics(MedtronicConst.Statistics.TBRsSet); - - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(true).enacted(true) // - .absolute(absoluteRate).duration(durationInMinutes); - - } else { - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(false).enacted(false) // - .comment(R.string.medtronic_cmd_tbr_could_not_be_delivered); - } - - } - - - @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - if (percent == 0) { - return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew, tbrType); - } else { - double absoluteValue = profile.getBasal() * (percent / 100.0d); - absoluteValue = pumpDescription.getPumpType().determineCorrectBasalSize(absoluteValue); - aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (" + percent + "). This will start setTempBasalAbsolute, with calculated value (" + absoluteValue + "). Result might not be 100% correct."); - return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType); - } - } - - - private void finishAction(String overviewKey) { - - if (overviewKey != null) - rxBus.send(new EventRefreshOverview(overviewKey, false)); - - triggerUIChange(); - - setRefreshButtonEnabled(true); - } - - - private void readPumpHistory() { - -// if (isLoggingEnabled()) -// LOG.error(getLogPrefix() + "readPumpHistory WIP."); - - readPumpHistoryLogic(); - - scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory); - - if (medtronicHistoryData.hasRelevantConfigurationChanged()) { - scheduleNextRefresh(MedtronicStatusRefreshType.Configuration, -1); - } - - if (medtronicHistoryData.hasPumpTimeChanged()) { - scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, -1); - } - - if (this.medtronicPumpStatus.basalProfileStatus != BasalProfileStatus.NotInitialized - && medtronicHistoryData.hasBasalProfileChanged()) { - medtronicHistoryData.processLastBasalProfileChange(pumpDescription.getPumpType(), medtronicPumpStatus); - } - - PumpDriverState previousState = this.pumpState; - - if (medtronicHistoryData.isPumpSuspended()) { - this.pumpState = PumpDriverState.Suspended; - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "isPumpSuspended: true"); - } else { - if (previousState == PumpDriverState.Suspended) { - this.pumpState = PumpDriverState.Ready; - } - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "isPumpSuspended: false"); - } - - medtronicHistoryData.processNewHistoryData(); - - this.medtronicHistoryData.finalizeNewHistoryRecords(); - // this.medtronicHistoryData.setLastHistoryRecordTime(this.lastPumpHistoryEntry.atechDateTime); - - } - - - private void readPumpHistoryLogic() { - - boolean debugHistory = false; - - LocalDateTime targetDate = null; - - if (lastPumpHistoryEntry == null) { - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: null"); - - Long lastPumpHistoryEntryTime = getLastPumpEntryTime(); - - LocalDateTime timeMinus36h = new LocalDateTime(); - timeMinus36h = timeMinus36h.minusHours(36); - medtronicHistoryData.setIsInInit(true); - - if (lastPumpHistoryEntryTime == 0L) { - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: 0L - targetDate: " - + targetDate); - targetDate = timeMinus36h; - } else { - // LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: " + lastPumpHistoryEntryTime + " - targetDate: " + targetDate); - - medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntryTime); - - LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); - - lastHistoryRecordTime = lastHistoryRecordTime.minusHours(12); // we get last 12 hours of history to - // determine pump state - // (we don't process that data), we process only - - if (timeMinus36h.isAfter(lastHistoryRecordTime)) { - targetDate = timeMinus36h; - } - - targetDate = (timeMinus36h.isAfter(lastHistoryRecordTime) ? timeMinus36h : lastHistoryRecordTime); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): targetDate: " + targetDate); - } - } else { - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: not null - " + medtronicUtil.gsonInstance.toJson(lastPumpHistoryEntry)); - medtronicHistoryData.setIsInInit(false); - // medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntry.atechDateTime); - - // targetDate = lastPumpHistoryEntry.atechDateTime; - } - - //aapsLogger.debug(LTag.PUMP, "HST: Target Date: " + targetDate); - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetHistoryData, - lastPumpHistoryEntry, targetDate); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: After task"); - - PumpHistoryResult historyResult = (PumpHistoryResult) responseTask2.returnData; - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: History Result: " + historyResult.toString()); - - PumpHistoryEntry latestEntry = historyResult.getLatestEntry(); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "Last entry: " + latestEntry); - - if (latestEntry == null) // no new history to read - return; - - this.lastPumpHistoryEntry = latestEntry; - sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, latestEntry.atechDateTime); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: History: valid=" + historyResult.validEntries.size() + ", unprocessed=" + historyResult.unprocessedEntries.size()); - - this.medtronicHistoryData.addNewHistory(historyResult); - this.medtronicHistoryData.filterNewEntries(); - - // determine if first run, if yes detrmine how much of update do we need - // first run: - // get last hiostory entry, if not there download 1.5 days of data - // - there: check if last entry is older than 1.5 days - // - yes: download 1.5 days - // - no: download with last entry - // - not there: download 1.5 days - // - // upload all new entries to NightScout (TBR, Bolus) - // determine pump status - // - // save last entry - // - // not first run: - // update to last entry - // - save - // - determine pump status - } - - - private Long getLastPumpEntryTime() { - Long lastPumpEntryTime = sp.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, 0L); - - try { - LocalDateTime localDateTime = DateTimeUtil.toLocalDateTime(lastPumpEntryTime); - - if (localDateTime.getYear() != (new GregorianCalendar().get(Calendar.YEAR))) { - aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid. Year was not the same."); - return 0L; - } - - return lastPumpEntryTime; - - } catch (Exception ex) { - aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid."); - return 0L; - } - - } - - - private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType) { - scheduleNextRefresh(refreshType, 0); - } - - - private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType, int additionalTimeInMinutes) { - switch (refreshType) { - - case RemainingInsulin: { - double remaining = medtronicPumpStatus.reservoirRemainingUnits; - int min; - if (remaining > 50) - min = 4 * 60; - else if (remaining > 20) - min = 60; - else - min = 15; - - workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)); - } - break; - - case PumpTime: - case Configuration: - case BatteryStatus: - case PumpHistory: { - workWithStatusRefresh(StatusRefreshAction.Add, refreshType, - getTimeInFutureFromMinutes(refreshType.getRefreshTime() + additionalTimeInMinutes)); - } - break; - } - } - - private enum StatusRefreshAction { - Add, // - GetData - } - - - private synchronized Map workWithStatusRefresh(StatusRefreshAction action, // - MedtronicStatusRefreshType statusRefreshType, // - Long time) { - - switch (action) { - - case Add: { - statusRefreshMap.put(statusRefreshType, time); - return null; - } - - case GetData: { - return new HashMap<>(statusRefreshMap); - } - - default: - return null; - - } - - } - - - private long getTimeInFutureFromMinutes(int minutes) { - return System.currentTimeMillis() + getTimeInMs(minutes); - } - - - private long getTimeInMs(int minutes) { - return minutes * 60 * 1000L; - } - - - private TempBasalPair readTBR() { - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.ReadTemporaryBasal); - - if (responseTask.hasData()) { - TempBasalPair tbr = (TempBasalPair) responseTask.returnData; - - // we sometimes get rate returned even if TBR is no longer running - if (tbr.getDurationMinutes() == 0) { - tbr.setInsulinRate(0.0d); - } - - return tbr; - } else { - return null; - } - } - - - @NonNull @Override - public PumpEnactResult cancelTempBasal(boolean enforceNew) { - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - started"); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - setRefreshButtonEnabled(false); - - TempBasalPair tbrCurrent = readTBR(); - - if (tbrCurrent != null) { - if (tbrCurrent.getInsulinRate() == 0.0f && tbrCurrent.getDurationMinutes() == 0) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - TBR already canceled."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(true).enacted(false); - } - } else { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Could not read currect TBR, canceling operation."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_read_tbr); - } - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.CancelTBR); - - Boolean response = (Boolean) responseTask2.returnData; - - finishAction("TBR"); - - if (response) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Cancel TBR successful."); - - TemporaryBasal tempBasal = new TemporaryBasal(getInjector()) // - .date(System.currentTimeMillis()) // - .duration(0) // - .source(Source.USER); - - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempBasal); - - return new PumpEnactResult(getInjector()).success(true).enacted(true) // - .isTempCancel(true); - } else { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Cancel TBR failed."); - - return new PumpEnactResult(getInjector()).success(response).enacted(response) // - .comment(R.string.medtronic_cmd_cant_cancel_tbr); - } - } - - @NonNull @Override - public ManufacturerType manufacturer() { - return pumpDescription.getPumpType().getManufacturer(); - } - - @NonNull @Override - public PumpType model() { - return pumpDescription.getPumpType(); - } - - @NonNull @Override - public String serialNumber() { - return medtronicPumpStatus.serialNumber; - } - - @NonNull @Override - public PumpEnactResult setNewBasalProfile(@NonNull Profile profile) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setNewBasalProfile"); - - // this shouldn't be needed, but let's do check if profile setting we are setting is same as current one - if (isProfileSame(profile)) { - return new PumpEnactResult(getInjector()) // - .success(true) // - .enacted(false) // - .comment(R.string.medtronic_cmd_basal_profile_not_set_is_same); - } - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - BasalProfile basalProfile = convertProfileToMedtronicProfile(profile); - - String profileInvalid = isProfileValid(basalProfile); - - if (profileInvalid != null) { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(getResourceHelper().gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid)); - } - - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetBasalProfileSTD, - basalProfile); - - Boolean response = (Boolean) responseTask.returnData; - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "Basal Profile was set: " + response); - - if (response) { - return new PumpEnactResult(getInjector()).success(true).enacted(true); - } else { - return new PumpEnactResult(getInjector()).success(response).enacted(response) // - .comment(R.string.medtronic_cmd_basal_profile_could_not_be_set); - } - } - - - private String isProfileValid(BasalProfile basalProfile) { - - StringBuilder stringBuilder = new StringBuilder(); - - if (medtronicPumpStatus.maxBasal == null) - return null; - - for (BasalProfileEntry profileEntry : basalProfile.getEntries()) { - - if (profileEntry.rate > medtronicPumpStatus.maxBasal) { - stringBuilder.append(profileEntry.startTime.toString("HH:mm")); - stringBuilder.append("="); - stringBuilder.append(profileEntry.rate); - } - } - - return stringBuilder.length() == 0 ? null : stringBuilder.toString(); - } - - - @NonNull - private BasalProfile convertProfileToMedtronicProfile(Profile profile) { - - BasalProfile basalProfile = new BasalProfile(aapsLogger); - - for (int i = 0; i < 24; i++) { - double rate = profile.getBasalTimeFromMidnight(i * 60 * 60); - - double v = pumpDescription.getPumpType().determineCorrectBasalSize(rate); - - BasalProfileEntry basalEntry = new BasalProfileEntry(v, i, 0); - basalProfile.addEntry(basalEntry); - - } - - basalProfile.generateRawDataFromEntries(); - - return basalProfile; - } - - // OPERATIONS not supported by Pump or Plugin - - private List customActions = null; - - private final CustomAction customActionWakeUpAndTune = new CustomAction(R.string.medtronic_custom_action_wake_and_tune, - MedtronicCustomActionType.WakeUpAndTune); - - private final CustomAction customActionClearBolusBlock = new CustomAction( - R.string.medtronic_custom_action_clear_bolus_block, MedtronicCustomActionType.ClearBolusBlock, false); - - private final CustomAction customActionResetRLConfig = new CustomAction( - R.string.medtronic_custom_action_reset_rileylink, MedtronicCustomActionType.ResetRileyLinkConfiguration, true); - - - @Override - public List getCustomActions() { - - if (customActions == null) { - this.customActions = Arrays.asList(customActionWakeUpAndTune, // - customActionClearBolusBlock, // - customActionResetRLConfig); - } - - return this.customActions; - } - - - @Override - public void executeCustomAction(@NonNull CustomActionType customActionType) { - - MedtronicCustomActionType mcat = (MedtronicCustomActionType) customActionType; - - switch (mcat) { - - case WakeUpAndTune: { - if (rileyLinkMedtronicService.verifyConfiguration()) { - serviceTaskExecutor.startTask(new WakeAndTuneTask(getInjector())); - } else { - ErrorHelperActivity.Companion.runAlarm(context, getResourceHelper().gs(R.string.medtronic_error_operation_not_possible_no_configuration), getResourceHelper().gs(R.string.medtronic_warning), R.raw.boluserror); - } - } - break; - - case ClearBolusBlock: { - this.busyTimestamps.clear(); - this.customActionClearBolusBlock.setEnabled(false); - refreshCustomActionsList(); - } - break; - - case ResetRileyLinkConfiguration: { - serviceTaskExecutor.startTask(new ResetRileyLinkConfigurationTask(getInjector())); - } - break; - - default: - break; - } - - } - - @Override - public void timezoneOrDSTChanged(@NonNull TimeChangeType changeType) { - - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "Time or TimeZone changed. "); - - this.hasTimeDateOrTimeZoneChanged = true; - } - - @Override - public boolean setNeutralTempAtFullHour() { - return sp.getBoolean(R.string.key_set_neutral_temps, true); - } - - - @SuppressWarnings("SameParameterValue") private void setEnableCustomAction(MedtronicCustomActionType customAction, boolean isEnabled) { - - if (customAction == MedtronicCustomActionType.ClearBolusBlock) { - this.customActionClearBolusBlock.setEnabled(isEnabled); - } else if (customAction == MedtronicCustomActionType.ResetRileyLinkConfiguration) { - this.customActionResetRLConfig.setEnabled(isEnabled); - } - - refreshCustomActionsList(); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt new file mode 100644 index 0000000000..9f7bb11d92 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt @@ -0,0 +1,1174 @@ +package info.nightscout.androidaps.plugins.pump.medtronic + +import android.content.ComponentName +import android.content.Context +import android.content.ServiceConnection +import android.os.IBinder +import android.os.SystemClock +import androidx.preference.Preference +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.PumpSync.TemporaryBasalType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.common.ManufacturerType +import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpDevice +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpInfo +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.common.utils.ProfileUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile.Companion.getProfilesByHourToString +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfileEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair +import info.nightscout.androidaps.plugins.pump.medtronic.defs.* +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType.Companion.getSettings +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.Companion.isSame +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.TimeChangeType +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 23.04.18. + * + * @author Andy Rozman (andy.rozman@gmail.com) + */ +@Singleton +class MedtronicPumpPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rxBus: RxBusWrapper, + context: Context, + resourceHelper: ResourceHelper, + activePlugin: ActivePlugin, + sp: SP, + commandQueue: CommandQueueProvider, + fabricPrivacy: FabricPrivacy, + private val medtronicUtil: MedtronicUtil, + private val medtronicPumpStatus: MedtronicPumpStatus, + private val medtronicHistoryData: MedtronicHistoryData, + private val rileyLinkServiceData: RileyLinkServiceData, + private val serviceTaskExecutor: ServiceTaskExecutor, + dateUtil: DateUtil, + aapsSchedulers: AapsSchedulers, + pumpSync: PumpSync, + pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage +) : info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract(PluginDescription() // + .mainType(PluginType.PUMP) // + .fragmentClass(MedtronicFragment::class.java.name) // + .pluginIcon(R.drawable.ic_veo_128) + .pluginName(R.string.medtronic_name) // + .shortName(R.string.medtronic_name_short) // + .preferencesId(R.xml.pref_medtronic) + .description(R.string.description_pump_medtronic), // + PumpType.MEDTRONIC_522_722, // we default to most basic model, correct model from config is loaded later + injector, resourceHelper, aapsLogger, commandQueue, rxBus, activePlugin, sp, context, fabricPrivacy, dateUtil, aapsSchedulers, pumpSync, pumpSyncStorage +), Pump, RileyLinkPumpDevice, info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncEntriesCreator { + + private lateinit var rileyLinkMedtronicService: RileyLinkMedtronicService + + // variables for handling statuses and history + private var firstRun = true + private var isRefresh = false + private val statusRefreshMap: MutableMap = mutableMapOf() + private var isInitialized = false + private var lastPumpHistoryEntry: PumpHistoryEntry? = null + private val busyTimestamps: MutableList = ArrayList() + private var hasTimeDateOrTimeZoneChanged = false + private var isBusy = false + + override fun onStart() { + aapsLogger.debug(LTag.PUMP, deviceID() + " started.") + serviceConnection = object : ServiceConnection { + override fun onServiceDisconnected(name: ComponentName) { + aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is disconnected") + //rileyLinkMedtronicService = null + } + + override fun onServiceConnected(name: ComponentName, service: IBinder) { + aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is connected") + val mLocalBinder = service as RileyLinkMedtronicService.LocalBinder + rileyLinkMedtronicService = mLocalBinder.serviceInstance + isServiceSet = true + rileyLinkMedtronicService.verifyConfiguration() + Thread(Runnable { + for (i in 0..19) { + SystemClock.sleep(5000) + aapsLogger.debug(LTag.PUMP, "Starting Medtronic-RileyLink service") + if (rileyLinkMedtronicService.setNotInPreInit()) { + break + } + } + }).start() + } + } + super.onStart() + } + + override fun updatePreferenceSummary(pref: Preference) { + super.updatePreferenceSummary(pref) + if (pref.key == resourceHelper.gs(R.string.key_rileylink_mac_address)) { + val value = sp.getStringOrNull(R.string.key_rileylink_mac_address, null) + pref.summary = value ?: resourceHelper.gs(R.string.not_set_short) + } + } + + private val logPrefix: String + get() = "MedtronicPumpPlugin::" + + override fun initPumpStatusData() { + medtronicPumpStatus.lastConnection = sp.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L) + medtronicPumpStatus.lastDataTime = medtronicPumpStatus.lastConnection + medtronicPumpStatus.previousConnection = medtronicPumpStatus.lastConnection + + //if (rileyLinkMedtronicService != null) rileyLinkMedtronicService.verifyConfiguration(); + aapsLogger.debug(LTag.PUMP, "initPumpStatusData: " + medtronicPumpStatus) + + // this is only thing that can change, by being configured + pumpDescription.maxTempAbsolute = if (medtronicPumpStatus.maxBasal != null) medtronicPumpStatus.maxBasal!! else 35.0 + + // set first Medtronic Pump Start + if (!sp.contains(MedtronicConst.Statistics.FirstPumpStart)) { + sp.putLong(MedtronicConst.Statistics.FirstPumpStart, System.currentTimeMillis()) + } + migrateSettings() + + pumpSyncStorage.initStorage() + + this.displayConnectionMessages = false + } + + override fun triggerPumpConfigurationChangedEvent() { + rxBus.send(EventMedtronicPumpConfigurationChanged()) + } + + private fun migrateSettings() { + if ("US (916 MHz)" == sp.getString(MedtronicConst.Prefs.PumpFrequency, "US (916 MHz)")) { + sp.putString(MedtronicConst.Prefs.PumpFrequency, resourceHelper.gs(R.string.key_medtronic_pump_frequency_us_ca)) + } + val encoding = sp.getString(MedtronicConst.Prefs.Encoding, "RileyLink 4b6b Encoding") + if ("RileyLink 4b6b Encoding" == encoding) { + sp.putString(MedtronicConst.Prefs.Encoding, resourceHelper.gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink)) + } + if ("Local 4b6b Encoding" == encoding) { + sp.putString(MedtronicConst.Prefs.Encoding, resourceHelper.gs(R.string.key_medtronic_pump_encoding_4b6b_local)) + } + } + + override fun onStartCustomActions() { + + // check status every minute (if any status needs refresh we send readStatus command) + Thread(Runnable { + do { + SystemClock.sleep(60000) + if (this.isInitialized) { + val statusRefresh = workWithStatusRefresh( + StatusRefreshAction.GetData, null, null)!! + if (doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + if (!commandQueue.statusInQueue()) { + commandQueue.readStatus("Scheduled Status Refresh", null) + } + } + clearBusyQueue() + } + } while (serviceRunning) + }).start() + } + + override val serviceClass: Class<*> + get() = RileyLinkMedtronicService::class.java + + override val pumpStatusData: info.nightscout.androidaps.plugins.pump.common.data.PumpStatus + get() = medtronicPumpStatus + + override fun deviceID(): String { + return "Medtronic" + } + + override val isFakingTempsByExtendedBoluses: Boolean + get() = false + + override fun canHandleDST(): Boolean { + return false + } + + // Pump Plugin + private var isServiceSet: Boolean = false + + override fun getRileyLinkService(): RileyLinkMedtronicService { + return rileyLinkMedtronicService + } + + override fun getPumpInfo(): RileyLinkPumpInfo { + val frequency = resourceHelper.gs(if (medtronicPumpStatus.pumpFrequency == "medtronic_pump_frequency_us_ca") R.string.medtronic_pump_frequency_us_ca else R.string.medtronic_pump_frequency_worldwide) + val model = if (!medtronicUtil.isModelSet) "???" else "Medtronic " + medtronicPumpStatus.medtronicDeviceType.pumpModel + val serialNumber = medtronicPumpStatus.serialNumber + return RileyLinkPumpInfo(frequency, model, serialNumber) + } + + override fun getLastConnectionTimeMillis(): Long { + return medtronicPumpStatus.lastConnection + } + + override fun setLastCommunicationToNow() { + medtronicPumpStatus.setLastCommunicationToNow() + } + + override fun isInitialized(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isInitialized") + return isServiceSet && isInitialized + } + + override fun setBusy(busy: Boolean) { + isBusy = busy + } + + override fun isBusy(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isBusy") + if (isServiceSet) { + if (isBusy) return true + if (busyTimestamps.size > 0) { + clearBusyQueue() + return busyTimestamps.size > 0 + } + } + return false + } + + @Synchronized + private fun clearBusyQueue() { + if (busyTimestamps.size == 0) { + return + } + val deleteFromQueue: MutableSet = HashSet() + for (busyTimestamp in busyTimestamps) { + if (System.currentTimeMillis() > busyTimestamp) { + deleteFromQueue.add(busyTimestamp) + } + } + if (deleteFromQueue.size == busyTimestamps.size) { + busyTimestamps.clear() + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false) + } + if (deleteFromQueue.size > 0) { + busyTimestamps.removeAll(deleteFromQueue) + } + } + + override fun isConnected(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnected") + return isServiceSet && rileyLinkMedtronicService.isInitialized + } + + override fun isConnecting(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnecting") + return !isServiceSet || !rileyLinkMedtronicService.isInitialized + } + + override fun getPumpStatus(reason: String) { + var needRefresh = true + if (firstRun) { + needRefresh = initializePump() /*!isRefresh*/ + } else { + refreshAnyStatusThatNeedsToBeRefreshed() + } + if (needRefresh) rxBus.send(EventMedtronicPumpValuesChanged()) + } + + fun resetStatusState() { + firstRun = true + isRefresh = true + }// + + private val isPumpNotReachable: Boolean + get() { + val rileyLinkServiceState = rileyLinkServiceData.rileyLinkServiceState + if (rileyLinkServiceState == null) { + aapsLogger.debug(LTag.PUMP, "RileyLink unreachable. RileyLinkServiceState is null.") + return false + } + if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // + && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // + && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice) { + aapsLogger.debug(LTag.PUMP, "RileyLink unreachable.") + return false + } + return !rileyLinkMedtronicService.deviceCommunicationManager.isDeviceReachable + } + + private fun refreshAnyStatusThatNeedsToBeRefreshed() { + val statusRefresh = workWithStatusRefresh(StatusRefreshAction.GetData, null, + null)!! + if (!doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + return + } + var resetTime = false + if (isPumpNotReachable) { + aapsLogger.error("Pump unreachable.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, resourceHelper, rxBus) + return + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + if (hasTimeDateOrTimeZoneChanged) { + checkTimeAndOptionallySetTime() + + // read time if changed, set new time + hasTimeDateOrTimeZoneChanged = false + } + + // execute + val refreshTypesNeededToReschedule: MutableSet = mutableSetOf() + for ((key, value) in statusRefresh) { + if (value > 0 && System.currentTimeMillis() > value) { + when (key) { + MedtronicStatusRefreshType.PumpHistory -> { + readPumpHistory() + } + + MedtronicStatusRefreshType.PumpTime -> { + checkTimeAndOptionallySetTime() + refreshTypesNeededToReschedule.add(key) + resetTime = true + } + + MedtronicStatusRefreshType.BatteryStatus, MedtronicStatusRefreshType.RemainingInsulin -> { + rileyLinkMedtronicService.medtronicUIComm.executeCommand(key.getCommandType(medtronicUtil.medtronicPumpModel)!!) + refreshTypesNeededToReschedule.add(key) + resetTime = true + } + + MedtronicStatusRefreshType.Configuration -> { + rileyLinkMedtronicService.medtronicUIComm.executeCommand(key.getCommandType(medtronicUtil.medtronicPumpModel)!!) + resetTime = true + } + } + } + + // reschedule + for (refreshType2 in refreshTypesNeededToReschedule) { + scheduleNextRefresh(refreshType2) + } + } + + if (resetTime) medtronicPumpStatus.setLastCommunicationToNow() + } + + private fun doWeHaveAnyStatusNeededRefereshing(statusRefresh: Map): Boolean { + for ((_, value) in statusRefresh) { + if (value > 0 && System.currentTimeMillis() > value) { + return true + } + } + return hasTimeDateOrTimeZoneChanged + } + + private fun setRefreshButtonEnabled(enabled: Boolean) { + rxBus.send(EventRefreshButtonState(enabled)) + } + + private fun initializePump(): Boolean { + if (!isServiceSet) return false + aapsLogger.info(LTag.PUMP, logPrefix + "initializePump - start") + rileyLinkMedtronicService.deviceCommunicationManager.setDoWakeUpBeforeCommand(false) + setRefreshButtonEnabled(false) + if (isRefresh) { + if (isPumpNotReachable) { + aapsLogger.error(logPrefix + "initializePump::Pump unreachable.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, resourceHelper, rxBus) + setRefreshButtonEnabled(true) + return true + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + } + + // model (once) + if (!medtronicUtil.isModelSet) { + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.PumpModel) + } else { + if (medtronicPumpStatus.medtronicDeviceType !== medtronicUtil.medtronicPumpModel) { + aapsLogger.warn(LTag.PUMP, logPrefix + "Configured pump is not the same as one detected.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, resourceHelper, rxBus) + } + } + pumpState = PumpDriverState.Connected + + // time (1h) + checkTimeAndOptionallySetTime() + readPumpHistory() + + // remaining insulin (>50 = 4h; 50-20 = 1h; 15m) + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetRemainingInsulin) + scheduleNextRefresh(MedtronicStatusRefreshType.RemainingInsulin, 10) + + // remaining power (1h) + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetBatteryStatus) + scheduleNextRefresh(MedtronicStatusRefreshType.BatteryStatus, 20) + + // configuration (once and then if history shows config changes) + rileyLinkMedtronicService.medtronicUIComm.executeCommand(getSettings(medtronicUtil.medtronicPumpModel)) + + // read profile (once, later its controlled by isThisProfileSet method) + basalProfiles + val errorCount = rileyLinkMedtronicService.medtronicUIComm.invalidResponsesCount + if (errorCount >= 5) { + aapsLogger.error("Number of error counts was 5 or more. Starting tunning.") + setRefreshButtonEnabled(true) + serviceTaskExecutor.startTask(WakeAndTuneTask(injector)) + return true + } + medtronicPumpStatus.setLastCommunicationToNow() + setRefreshButtonEnabled(true) + if (!isRefresh) { + pumpState = PumpDriverState.Initialized + } + isInitialized = true + // this.pumpState = PumpDriverState.Initialized; + firstRun = false + return true + } + + private val basalProfiles: Unit + get() { + val medtronicUITask = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD) + if (medtronicUITask.responseType === MedtronicUIResponseType.Error) { + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD) + } + } + + override fun isThisProfileSet(profile: Profile): Boolean { + aapsLogger.debug(LTag.PUMP, "isThisProfileSet: basalInitalized=" + medtronicPumpStatus.basalProfileStatus) + if (!isInitialized) return true + if (medtronicPumpStatus.basalProfileStatus === BasalProfileStatus.NotInitialized) { + // this shouldn't happen, but if there was problem we try again + basalProfiles + return isProfileSame(profile) + } else if (medtronicPumpStatus.basalProfileStatus === BasalProfileStatus.ProfileChanged) { + return false + } + return medtronicPumpStatus.basalProfileStatus !== BasalProfileStatus.ProfileOK || isProfileSame(profile) + } + + private fun isProfileSame(profile: Profile): Boolean { + var invalid = false + val basalsByHour: DoubleArray? = medtronicPumpStatus.basalsByHour + aapsLogger.debug(LTag.PUMP, "Current Basals (h): " + + (basalsByHour?.let { getProfilesByHourToString(it) } ?: "null")) + + // int index = 0; + if (basalsByHour == null) return true // we don't want to set profile again, unless we are sure + val stringBuilder = StringBuilder("Requested Basals (h): ") + stringBuilder.append(ProfileUtil.getBasalProfilesDisplayableAsStringOfArray(profile, this.pumpType)) + + for (basalValue in profile.getBasalValues()) { + val basalValueValue = pumpDescription.pumpType.determineCorrectBasalSize(basalValue.value) + val hour = basalValue.timeAsSeconds / (60 * 60) + if (!isSame(basalsByHour[hour], basalValueValue)) { + invalid = true + } + // stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)) + // stringBuilder.append(" ") + } + aapsLogger.debug(LTag.PUMP, stringBuilder.toString()) + if (!invalid) { + aapsLogger.debug(LTag.PUMP, "Basal profile is same as AAPS one.") + } else { + aapsLogger.debug(LTag.PUMP, "Basal profile on Pump is different than the AAPS one.") + } + return !invalid + } + + override fun lastDataTime(): Long { + return if (medtronicPumpStatus.lastConnection > 0) { + medtronicPumpStatus.lastConnection + } else System.currentTimeMillis() + } + + override val baseBasalRate: Double + get() = medtronicPumpStatus.basalProfileForHour + + override val reservoirLevel: Double + get() = medtronicPumpStatus.reservoirRemainingUnits + + override val batteryLevel: Int + get() = medtronicPumpStatus.batteryRemaining + + override fun triggerUIChange() { + rxBus.send(EventMedtronicPumpValuesChanged()) + } + + override fun generateTempId(objectA: Any): Long { + val timestamp: Long = objectA as Long + return DateTimeUtil.toATechDate(timestamp) + } + + private var bolusDeliveryType = BolusDeliveryType.Idle + + private enum class BolusDeliveryType { + Idle, // + DeliveryPrepared, // + Delivering, // + CancelDelivery + } + + private fun checkTimeAndOptionallySetTime() { + aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Start") + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Pump Unreachable.") + setRefreshButtonEnabled(true) + return + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock) + var clock = medtronicUtil.pumpTime + if (clock == null) { // retry + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock) + clock = medtronicUtil.pumpTime + } + if (clock == null) return + val timeDiff = Math.abs(clock.timeDifference) + if (timeDiff > 20) { + if (clock.localDeviceTime.year <= 2015 || timeDiff <= 24 * 60 * 60) { + aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Set time on pump.", timeDiff)) + rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.SetRealTimeClock) + if (clock.timeDifference == 0) { + val notification = Notification(Notification.INSIGHT_DATE_TIME_UPDATED, resourceHelper.gs(R.string.pump_time_updated), Notification.INFO, 60) + rxBus.send(EventNewNotification(notification)) + } + } else { + if (clock.localDeviceTime.year > 2015) { + aapsLogger.error(String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference over 24h requested [diff=%d s]. Doing nothing.", timeDiff)) + medtronicUtil.sendNotification(MedtronicNotificationType.TimeChangeOver24h, resourceHelper, rxBus) + } + } + } else { + aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Do nothing.", timeDiff)) + } + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0) + } + + override fun deliverBolus(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { + aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - " + BolusDeliveryType.DeliveryPrepared) + setRefreshButtonEnabled(false) + if (detailedBolusInfo.insulin > medtronicPumpStatus.reservoirRemainingUnits) { + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(resourceHelper.gs(R.string.medtronic_cmd_bolus_could_not_be_delivered_no_insulin, + medtronicPumpStatus.reservoirRemainingUnits, + detailedBolusInfo.insulin)) + } + bolusDeliveryType = BolusDeliveryType.DeliveryPrepared + if (isPumpNotReachable) { + aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - Pump Unreachable.") + return setNotReachable(true, false) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled."); + return setNotReachable(true, true) + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Starting wait period."); + val sleepTime = sp.getInt(MedtronicConst.Prefs.BolusDelay, 10) * 1000 + SystemClock.sleep(sleepTime.toLong()) + return if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled, before wait period."); + setNotReachable(true, true) + } else try { + bolusDeliveryType = BolusDeliveryType.Delivering + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Start delivery"); + val responseTask = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.SetBolus, + arrayListOf(detailedBolusInfo.insulin)) + val response = responseTask.result as Boolean? + setRefreshButtonEnabled(true) + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Response: {}", response); + return if (response == null || !response) { + PumpEnactResult(injector) // + .success(bolusDeliveryType == BolusDeliveryType.CancelDelivery) // + .enacted(false) // + .comment(R.string.medtronic_cmd_bolus_could_not_be_delivered) + } else { + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled after Bolus started."); + Thread(Runnable { + SystemClock.sleep(2000) + runAlarm(context, resourceHelper.gs(R.string.medtronic_cmd_cancel_bolus_not_supported), resourceHelper.gs(R.string.medtronic_warning), R.raw.boluserror) + }).start() + } + val now = System.currentTimeMillis() + detailedBolusInfo.timestamp = now + detailedBolusInfo.deliverAtTheLatest = now // not sure about that one + + pumpSyncStorage.addBolusWithTempId(detailedBolusInfo, true, this) + + // we subtract insulin, exact amount will be visible with next remainingInsulin update. + medtronicPumpStatus.reservoirRemainingUnits = medtronicPumpStatus.reservoirRemainingUnits - detailedBolusInfo.insulin + incrementStatistics(if (detailedBolusInfo.bolusType === DetailedBolusInfo.BolusType.SMB) MedtronicConst.Statistics.SMBBoluses else MedtronicConst.Statistics.StandardBoluses) + + // calculate time for bolus and set driver to busy for that time + val bolusTime = (detailedBolusInfo.insulin * 42.0).toInt() + val time = now + bolusTime * 1000 + busyTimestamps.add(time) + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true) + PumpEnactResult(injector).success(true) // + .enacted(true) // + .bolusDelivered(detailedBolusInfo.insulin) // + .carbsDelivered(detailedBolusInfo.carbs) + } + } finally { + finishAction("Bolus") + bolusDeliveryType = BolusDeliveryType.Idle + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - End wait period. Start delivery"); + } + + private fun setNotReachable(isBolus: Boolean, success: Boolean): PumpEnactResult { + setRefreshButtonEnabled(true) + if (isBolus) { + bolusDeliveryType = BolusDeliveryType.Idle + } + return if (success) { + PumpEnactResult(injector) // + .success(true) // + .enacted(false) + } else { + PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + } + + override fun stopBolusDelivering() { + bolusDeliveryType = BolusDeliveryType.CancelDelivery + + // if (isLoggingEnabled()) + // LOG.warn("MedtronicPumpPlugin::deliverBolus - Stop Bolus Delivery."); + } + + private fun incrementStatistics(statsKey: String) { + var currentCount = sp.getLong(statsKey, 0L) + currentCount++ + sp.putLong(statsKey, currentCount) + } + + // if enforceNew===true current temp basal is canceled 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 + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute: rate: " + absoluteRate + ", duration=" + durationInMinutes) + + // read current TBR + val tbrCurrent = readTBR() + if (tbrCurrent == null) { + aapsLogger.warn(LTag.PUMP, logPrefix + "setTempBasalAbsolute - Could not read current TBR, canceling operation.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_read_tbr) + } else { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute: Current Basal: duration: " + tbrCurrent.durationMinutes + " min, rate=" + tbrCurrent.insulinRate) + } + if (!enforceNew) { + if (isSame(tbrCurrent.insulinRate, absoluteRate)) { + var sameRate = true + if (isSame(0.0, absoluteRate) && durationInMinutes > 0) { + // if rate is 0.0 and duration>0 then the rate is not the same + sameRate = false + } + if (sameRate) { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - No enforceNew and same rate. Exiting.") + finishAction("TBR") + return PumpEnactResult(injector).success(true).enacted(false) + } + } + // if not the same rate, we cancel and start new + } + + // if TBR is running we will cancel it. + if (tbrCurrent.insulinRate > 0.0 && tbrCurrent.durationMinutes > 0) { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - TBR running - so canceling it.") + + // CANCEL + val responseTask2 = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.CancelTBR) + val response = responseTask2.result as Boolean? + if (response == null || !response) { + aapsLogger.error(logPrefix + "setTempBasalAbsolute - Cancel TBR failed.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_cancel_tbr_stop_op) + } else { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - Current TBR cancelled.") + } + } + + // now start new TBR + val responseTask = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.SetTemporaryBasal, + arrayListOf(absoluteRate, durationInMinutes)) + val response = responseTask.result as Boolean? + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - setTBR. Response: " + response) + return if (response == null || !response) { + finishAction("TBR") + PumpEnactResult(injector).success(false).enacted(false) // + .comment(R.string.medtronic_cmd_tbr_could_not_be_delivered) + } else { + medtronicPumpStatus.tempBasalStart = Date() + medtronicPumpStatus.tempBasalAmount = absoluteRate + medtronicPumpStatus.tempBasalLength = durationInMinutes + + val tempData = info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR(absoluteRate, true, durationInMinutes, tbrType) + + pumpSyncStorage.addTemporaryBasalRateWithTempId(tempData, true, this) + + incrementStatistics(MedtronicConst.Statistics.TBRsSet) + finishAction("TBR") + PumpEnactResult(injector).success(true).enacted(true) // + .absolute(absoluteRate).duration(durationInMinutes) + } + } + + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + return if (percent == 0) { + setTempBasalAbsolute(0.0, durationInMinutes, profile, enforceNew, tbrType) + } else { + var absoluteValue = profile.getBasal() * (percent / 100.0) + absoluteValue = pumpDescription.pumpType.determineCorrectBasalSize(absoluteValue) + aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% ($percent). This will start setTempBasalAbsolute, with calculated value ($absoluteValue). Result might not be 100% correct.") + setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType) + } + } + + private fun finishAction(overviewKey: String?) { + if (overviewKey != null) rxBus.send(EventRefreshOverview(overviewKey, false)) + triggerUIChange() + setRefreshButtonEnabled(true) + } + + private fun readPumpHistory() { + +// if (isLoggingEnabled()) +// LOG.error(getLogPrefix() + "readPumpHistory WIP."); + readPumpHistoryLogic() + scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory) + if (medtronicHistoryData.hasRelevantConfigurationChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.Configuration, -1) + } + if (medtronicHistoryData.hasPumpTimeChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, -1) + } + if (medtronicPumpStatus.basalProfileStatus !== BasalProfileStatus.NotInitialized + && medtronicHistoryData.hasBasalProfileChanged()) { + medtronicHistoryData.processLastBasalProfileChange(pumpDescription.pumpType, medtronicPumpStatus) + } + val previousState = pumpState + if (medtronicHistoryData.isPumpSuspended()) { + pumpState = PumpDriverState.Suspended + aapsLogger.debug(LTag.PUMP, logPrefix + "isPumpSuspended: true") + } else { + if (previousState === PumpDriverState.Suspended) { + pumpState = PumpDriverState.Ready + } + aapsLogger.debug(LTag.PUMP, logPrefix + "isPumpSuspended: false") + } + medtronicHistoryData.processNewHistoryData() + medtronicHistoryData.finalizeNewHistoryRecords() + } + + private fun readPumpHistoryLogic() { + + val debugHistory = false + var targetDate: LocalDateTime? = null + if (lastPumpHistoryEntry == null) { // first read + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntry: null") + val lastPumpHistoryEntryTime = lastPumpEntryTime + var timeMinus36h = LocalDateTime() + timeMinus36h = timeMinus36h.minusHours(36) + medtronicHistoryData.setIsInInit(true) + if (lastPumpHistoryEntryTime == 0L) { + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: 0L - targetDate: " + + targetDate) + targetDate = timeMinus36h + } else { + // LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: " + lastPumpHistoryEntryTime + " - targetDate: " + targetDate) + //medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntryTime) + var lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime) + lastHistoryRecordTime = lastHistoryRecordTime.minusHours(12) // we get last 12 hours of history to + // determine pump state + // (we don't process that data), we process only + targetDate = if (timeMinus36h.isAfter(lastHistoryRecordTime)) timeMinus36h else lastHistoryRecordTime + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): targetDate: " + targetDate) + } + } else { // all other reads + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntry: not null - " + medtronicUtil.gsonInstance.toJson(lastPumpHistoryEntry)) + medtronicHistoryData.setIsInInit(false) + // we need to read 35 minutes in the past so that we can repair any TBR or Bolus values if neeeded + targetDate = LocalDateTime(DateTimeUtil.getMillisFromATDWithAddedMinutes(lastPumpHistoryEntry!!.atechDateTime, -35)) + } + + //aapsLogger.debug(LTag.PUMP, "HST: Target Date: " + targetDate); + val responseTask2 = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.GetHistoryData, + arrayListOf(/*lastPumpHistoryEntry*/ null, targetDate) as ArrayList?) + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: After task") + val historyResult = responseTask2.result as PumpHistoryResult? + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: History Result: " + historyResult.toString()) + val latestEntry = historyResult!!.latestEntry + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "Last entry: " + latestEntry) + if (latestEntry == null) // no new history to read + return + lastPumpHistoryEntry = latestEntry + sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, latestEntry.atechDateTime) + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: History: valid=" + historyResult.validEntries.size + ", unprocessed=" + historyResult.unprocessedEntries.size) + medtronicHistoryData.addNewHistory(historyResult) + medtronicHistoryData.filterNewEntries() + + // determine if first run, if yes detrmine how much of update do we need + // - first run: + // - get last history entry + // - if not there download 1.5 days of data + // - there: check if last entry is older than 1.5 days + // - yes: download 1.5 days + // - no: download with last entry TODO 5min + // - not there: download 1.5 days + // + // upload all new entries to NightScout (TBR, Bolus) + // determine pump status + // save last entry + // + // - not first run: + // - update to last entry TODO 5min + // - save + // - determine pump status + } + + private val lastPumpEntryTime: Long + get() { + val lastPumpEntryTime = sp.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, 0L) + return try { + val localDateTime = DateTimeUtil.toLocalDateTime(lastPumpEntryTime) + if (localDateTime.year != GregorianCalendar()[Calendar.YEAR]) { + aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid. Year was not the same.") + return 0L + } + lastPumpEntryTime + } catch (ex: Exception) { + aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid.") + 0L + } + } + + private fun scheduleNextRefresh(refreshType: MedtronicStatusRefreshType?, additionalTimeInMinutes: Int = 0) { + when (refreshType) { + MedtronicStatusRefreshType.RemainingInsulin -> { + val remaining = medtronicPumpStatus.reservoirRemainingUnits + val min: Int + min = if (remaining > 50) 4 * 60 else if (remaining > 20) 60 else 15 + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)) + } + + MedtronicStatusRefreshType.PumpTime, MedtronicStatusRefreshType.Configuration, MedtronicStatusRefreshType.BatteryStatus, MedtronicStatusRefreshType.PumpHistory -> { + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, + getTimeInFutureFromMinutes(refreshType.refreshTime + additionalTimeInMinutes)) + } + } + } + + private enum class StatusRefreshAction { + Add, // + GetData + } + + @Synchronized + private fun workWithStatusRefresh(action: StatusRefreshAction, // + statusRefreshType: MedtronicStatusRefreshType?, // + time: Long?): Map? { + return when (action) { + StatusRefreshAction.Add -> { + statusRefreshMap[statusRefreshType!!] = time!! + null + } + + StatusRefreshAction.GetData -> { + HashMap(statusRefreshMap) + } + + } + } + + private fun getTimeInFutureFromMinutes(minutes: Int): Long { + return System.currentTimeMillis() + getTimeInMs(minutes) + } + + private fun getTimeInMs(minutes: Int): Long { + return minutes * 60 * 1000L + } + + private fun readTBR(): TempBasalPair? { + val responseTask = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.ReadTemporaryBasal) + return if (responseTask.hasData()) { + val tbr = responseTask.result as TempBasalPair? + + // we sometimes get rate returned even if TBR is no longer running + if (tbr != null) { + if (tbr.durationMinutes == 0) { + tbr.insulinRate = 0.0 + } + tbr + } else + null + } else { + null + } + } + + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - started") + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + setRefreshButtonEnabled(false) + val tbrCurrent = readTBR() + if (tbrCurrent != null) { + if (tbrCurrent.insulinRate > 0.0f && tbrCurrent.durationMinutes == 0) { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - TBR already canceled.") + finishAction("TBR") + return PumpEnactResult(injector).success(true).enacted(false) + } + } else { + aapsLogger.warn(LTag.PUMP, logPrefix + "cancelTempBasal - Could not read currect TBR, canceling operation.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_read_tbr) + } + val responseTask2 = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.CancelTBR) + val response = responseTask2.result as Boolean? + finishAction("TBR") + return if (response == null || !response) { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - Cancel TBR failed.") + PumpEnactResult(injector).success(false).enacted(false) // + .comment(R.string.medtronic_cmd_cant_cancel_tbr) + } else { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - Cancel TBR successful.") + + val runningTBR = medtronicPumpStatus.runningTBR + + if (runningTBR != null) { + if (medtronicHistoryData.isTBRActive(runningTBR)) { + + val differenceTime = System.currentTimeMillis() - runningTBR.date + val tbrData = runningTBR.tbrData!! + + val result = pumpSync.syncTemporaryBasalWithPumpId( + runningTBR.date, + tbrData.rate, + differenceTime, + tbrData.isAbsolute, + tbrData.tbrType, + runningTBR.pumpId!!, + runningTBR.pumpType, + runningTBR.serialNumber) + + val differenceTimeMin = Math.floor(differenceTime / (60.0 * 1000.0)) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "canceling running TBR - syncTemporaryBasalWithPumpId [date=%d, pumpId=%d, rate=%.2f U, duration=%d, pumpSerial=%s] - Result: %b", + runningTBR.date, runningTBR.pumpId, + tbrData.rate, differenceTimeMin.toInt(), + medtronicPumpStatus.serialNumber, result)) + } + } + + PumpEnactResult(injector).success(true).enacted(true) // + .isTempCancel(true) + } + } + + override fun manufacturer(): ManufacturerType { + return ManufacturerType.Medtronic + } + + override fun model(): PumpType { + return pumpDescription.pumpType + } + + override fun serialNumber(): String { + return medtronicPumpStatus.serialNumber + } + + override fun setNewBasalProfile(profile: Profile): PumpEnactResult { + aapsLogger.info(LTag.PUMP, logPrefix + "setNewBasalProfile") + + // this shouldn't be needed, but let's do check if profile setting we are setting is same as current one + if (isProfileSame(profile)) { + return PumpEnactResult(injector) // + .success(true) // + .enacted(false) // + .comment(R.string.medtronic_cmd_basal_profile_not_set_is_same) + } + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + val basalProfile = convertProfileToMedtronicProfile(profile) + aapsLogger.debug("Basal Profile: $basalProfile") + val profileInvalid = isProfileValid(basalProfile) + if (profileInvalid != null) { + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(resourceHelper.gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid)) + } + val responseTask = rileyLinkMedtronicService.medtronicUIComm.executeCommand(MedtronicCommandType.SetBasalProfileSTD, + arrayListOf(basalProfile)) + val response = responseTask.result as Boolean? + aapsLogger.info(LTag.PUMP, logPrefix + "Basal Profile was set: " + response) + return if (response == null || !response) { + PumpEnactResult(injector).success(false).enacted(false) // + .comment(R.string.medtronic_cmd_basal_profile_could_not_be_set) + } else { + PumpEnactResult(injector).success(true).enacted(true) + } + } + + private fun isProfileValid(basalProfile: BasalProfile): String? { + val stringBuilder = StringBuilder() + if (medtronicPumpStatus.maxBasal == null) return null + for (profileEntry in basalProfile.getEntries()) { + if (profileEntry.rate > medtronicPumpStatus.maxBasal!!) { + stringBuilder.append(profileEntry.startTime!!.toString("HH:mm")) + stringBuilder.append("=") + stringBuilder.append(profileEntry.rate) + } + } + return if (stringBuilder.length == 0) null else stringBuilder.toString() + } + + private fun convertProfileToMedtronicProfile(profile: Profile): BasalProfile { + val basalProfile = BasalProfile(aapsLogger) + for (i in 0..23) { + val rate = profile.getBasalTimeFromMidnight(i * 60 * 60) + val v = pumpType.determineCorrectBasalSize(rate) + val basalEntry = BasalProfileEntry(v, i, 0) + basalProfile.addEntry(basalEntry) + } + basalProfile.generateRawDataFromEntries() + return basalProfile + } + + // OPERATIONS not supported by Pump or Plugin + private var customActions: List? = null + private val customActionWakeUpAndTune = CustomAction(R.string.medtronic_custom_action_wake_and_tune, + MedtronicCustomActionType.WakeUpAndTune) + private val customActionClearBolusBlock = CustomAction( + R.string.medtronic_custom_action_clear_bolus_block, MedtronicCustomActionType.ClearBolusBlock, false) + private val customActionResetRLConfig = CustomAction( + R.string.medtronic_custom_action_reset_rileylink, MedtronicCustomActionType.ResetRileyLinkConfiguration, true) + + override fun getCustomActions(): List? { + if (customActions == null) { + customActions = Arrays.asList(customActionWakeUpAndTune, // + customActionClearBolusBlock, // + customActionResetRLConfig) + } + return customActions + } + + override fun executeCustomAction(customActionType: CustomActionType) { + val mcat = customActionType as? MedtronicCustomActionType + when (mcat) { + MedtronicCustomActionType.WakeUpAndTune -> { + if (rileyLinkMedtronicService.verifyConfiguration()) { + serviceTaskExecutor.startTask(WakeAndTuneTask(injector)) + } else { + runAlarm(context, resourceHelper.gs(R.string.medtronic_error_operation_not_possible_no_configuration), resourceHelper.gs(R.string.medtronic_warning), R.raw.boluserror) + } + } + + MedtronicCustomActionType.ClearBolusBlock -> { + busyTimestamps.clear() + customActionClearBolusBlock.isEnabled = false + refreshCustomActionsList() + } + + MedtronicCustomActionType.ResetRileyLinkConfiguration -> { + serviceTaskExecutor.startTask(ResetRileyLinkConfigurationTask(injector)) + } + + null -> { + + } + } + } + + override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) { + aapsLogger.warn(LTag.PUMP, logPrefix + "Time or TimeZone changed. ") + hasTimeDateOrTimeZoneChanged = true + } + + override fun setNeutralTempAtFullHour(): Boolean { + return sp.getBoolean(R.string.key_set_neutral_temps, true) + } + + private fun setEnableCustomAction(customAction: MedtronicCustomActionType, isEnabled: Boolean) { + if (customAction === MedtronicCustomActionType.ClearBolusBlock) { + customActionClearBolusBlock.isEnabled = isEnabled + } else if (customAction === MedtronicCustomActionType.ResetRileyLinkConfiguration) { + customActionResetRLConfig.isEnabled = isEnabled + } + refreshCustomActionsList() + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java deleted file mode 100644 index 32963e8d9a..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java +++ /dev/null @@ -1,919 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm; - -import android.os.SystemClock; - -import org.joda.time.LocalDateTime; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.LTag; -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.RileyLinkConst; -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.RadioPacket; -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.service.tasks.WakeAndTuneTask; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.CarelinkLongMessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.CarelinkShortMessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.GetHistoryPageCarelinkMessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.MessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PacketType; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpAckMessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpMessage; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Original file created by geoff on 5/30/16. - *

- * Split into 2 implementations, so that we can split it by target device. - Andy - * This was mostly rewritten from Original version, and lots of commands and - * functionality added. - */ -@Singleton -public class MedtronicCommunicationManager extends RileyLinkCommunicationManager { - - @Inject MedtronicPumpStatus medtronicPumpStatus; - @Inject MedtronicPumpPlugin medtronicPumpPlugin; - @Inject MedtronicConverter medtronicConverter; - @Inject MedtronicUtil medtronicUtil; - @Inject MedtronicPumpHistoryDecoder medtronicPumpHistoryDecoder; - - private final int MAX_COMMAND_TRIES = 3; - private final int DEFAULT_TIMEOUT = 2000; - private final long RILEYLINK_TIMEOUT = 15 * 60 * 1000; // 15 min - - private String errorMessage; - private final boolean debugSetCommands = false; - - private boolean doWakeUpBeforeCommand = true; - - // This empty constructor must be kept, otherwise dagger injection might break! - @Inject - public MedtronicCommunicationManager() { - } - - @Inject - public void onInit() { - // we can't do this in the constructor, as sp only gets injected after the constructor has returned - medtronicPumpStatus.previousConnection = sp.getLong( - RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); - } - - @Override - public PumpMessage createResponseMessage(byte[] payload) { - return new PumpMessage(aapsLogger, payload); - } - - @Override - public void setPumpDeviceState(PumpDeviceState pumpDeviceState) { - this.medtronicPumpStatus.setPumpDeviceState(pumpDeviceState); - } - - public void setDoWakeUpBeforeCommand(boolean doWakeUp) { - this.doWakeUpBeforeCommand = doWakeUp; - } - - - @Override - public boolean isDeviceReachable() { - return isDeviceReachable(false); - } - - - /** - * We do actual wakeUp and compare PumpModel with currently selected one. If returned model is - * not Unknown, pump is reachable. - * - * @return - */ - public boolean isDeviceReachable(boolean canPreventTuneUp) { - - PumpDeviceState state = medtronicPumpStatus.getPumpDeviceState(); - - if (state != PumpDeviceState.PumpUnreachable) - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.WakingUp); - - for (int retry = 0; retry < 5; retry++) { - - aapsLogger.debug(LTag.PUMPCOMM, "isDeviceReachable. Waking pump... " + (retry != 0 ? " (retry " + retry + ")" : "")); - - boolean connected = connectToDevice(); - - if (connected) - return true; - - SystemClock.sleep(1000); - - } - - if (state != PumpDeviceState.PumpUnreachable) - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.PumpUnreachable); - - if (!canPreventTuneUp) { - - long diff = System.currentTimeMillis() - medtronicPumpStatus.lastConnection; - - if (diff > RILEYLINK_TIMEOUT) { - serviceTaskExecutor.startTask(new WakeAndTuneTask(injector)); - } - } - - return false; - } - - - private boolean connectToDevice() { - - PumpDeviceState state = medtronicPumpStatus.getPumpDeviceState(); - - // check connection - - byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); // simple - RFSpyResponse rfSpyResponse = rfspy.transmitThenReceive(new RadioPacket(injector, pumpMsgContent), (byte) 0, (byte) 200, - (byte) 0, (byte) 0, 25000, (byte) 0); - aapsLogger.info(LTag.PUMPCOMM, "wakeup: raw response is " + ByteUtil.shortHexString(rfSpyResponse.getRaw())); - - if (rfSpyResponse.wasTimeout()) { - aapsLogger.error(LTag.PUMPCOMM, "isDeviceReachable. Failed to find pump (timeout)."); - } else if (rfSpyResponse.looksLikeRadioPacket()) { - RadioResponse radioResponse = new RadioResponse(injector); - - try { - - radioResponse.init(rfSpyResponse.getRaw()); - - if (radioResponse.isValid()) { - - PumpMessage pumpResponse = createResponseMessage(radioResponse.getPayload()); - - if (!pumpResponse.isValid()) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Response is invalid ! [interrupted=%b, timeout=%b]", rfSpyResponse.wasInterrupted(), - rfSpyResponse.wasTimeout())); - } else { - - // radioResponse.rssi; - Object dataResponse = medtronicConverter.convertResponse(medtronicPumpPlugin.getPumpDescription().getPumpType(), MedtronicCommandType.PumpModel, - pumpResponse.getRawContent()); - - MedtronicDeviceType pumpModel = (MedtronicDeviceType) dataResponse; - boolean valid = (pumpModel != MedtronicDeviceType.Unknown_Device); - - if (medtronicUtil.getMedtronicPumpModel() == null && valid) { - medtronicUtil.setMedtronicPumpModel(pumpModel); - } - - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "isDeviceReachable. PumpModel is %s - Valid: %b (rssi=%d)", pumpModel.name(), valid, - radioResponse.rssi)); - - if (valid) { - if (state == PumpDeviceState.PumpUnreachable) - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.WakingUp); - else - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - rememberLastGoodDeviceCommunicationTime(); - - return true; - - } else { - if (state != PumpDeviceState.PumpUnreachable) - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.PumpUnreachable); - } - - } - - } else { - aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Failed to parse radio response: " - + ByteUtil.shortHexString(rfSpyResponse.getRaw())); - } - - } catch (RileyLinkCommunicationException e) { - aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Failed to decode radio response: " - + ByteUtil.shortHexString(rfSpyResponse.getRaw())); - } - - } else { - aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Unknown response: " + ByteUtil.shortHexString(rfSpyResponse.getRaw())); - } - - return false; - } - - - @Override - public boolean tryToConnectToDevice() { - return isDeviceReachable(true); - } - - - private PumpMessage runCommandWithArgs(PumpMessage msg) throws RileyLinkCommunicationException { - - if (debugSetCommands) - aapsLogger.debug(LTag.PUMPCOMM, "Run command with Args: "); - - PumpMessage rval; - PumpMessage shortMessage = makePumpMessage(msg.commandType, new CarelinkShortMessageBody(new byte[]{0})); - // look for ack from short message - PumpMessage shortResponse = sendAndListen(shortMessage); - if (shortResponse.commandType == MedtronicCommandType.CommandACK) { - if (debugSetCommands) - aapsLogger.debug(LTag.PUMPCOMM, "Run command with Args: Got ACK response"); - - rval = sendAndListen(msg); - if (debugSetCommands) - aapsLogger.debug(LTag.PUMPCOMM, "2nd Response: " + rval); - - return rval; - } else { - aapsLogger.error(LTag.PUMPCOMM, "runCommandWithArgs: Pump did not ack Attention packet"); - return new PumpMessage(aapsLogger, "No ACK after Attention packet."); - } - } - - - private PumpMessage runCommandWithFrames(MedtronicCommandType commandType, List> frames) - throws RileyLinkCommunicationException { - - aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: " + commandType.name()); - - PumpMessage rval = null; - PumpMessage shortMessage = makePumpMessage(commandType, new CarelinkShortMessageBody(new byte[]{0})); - // look for ack from short message - PumpMessage shortResponse = sendAndListen(shortMessage); - - if (shortResponse.commandType != MedtronicCommandType.CommandACK) { - aapsLogger.error(LTag.PUMPCOMM, "runCommandWithFrames: Pump did not ack Attention packet"); - - return new PumpMessage(aapsLogger, "No ACK after start message."); - } else { - aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: Got ACK response for Attention packet"); - } - - int frameNr = 1; - - for (List frame : frames) { - - byte[] frameData = MedtronicUtil.createByteArray(frame); - - // aapsLogger.debug(LTag.PUMPCOMM,"Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData)); - - PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(frameData)); - - rval = sendAndListen(msg); - - // aapsLogger.debug(LTag.PUMPCOMM,"PumpResponse: " + rval); - - if (rval.commandType != MedtronicCommandType.CommandACK) { - aapsLogger.error(LTag.PUMPCOMM, "runCommandWithFrames: Pump did not ACK frame #" + frameNr); - - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Run command with Frames FAILED (command=%s, response=%s)", commandType.name(), - rval.toString())); - - return new PumpMessage(aapsLogger, "No ACK after frame #" + frameNr); - } else { - aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: Got ACK response for frame #" + frameNr); - } - - frameNr++; - } - - return rval; - - } - - - public PumpHistoryResult getPumpHistory(PumpHistoryEntry lastEntry, LocalDateTime targetDate) { - - PumpHistoryResult pumpTotalResult = new PumpHistoryResult(aapsLogger, lastEntry, targetDate == null ? null - : DateTimeUtil.toATechDate(targetDate)); - - if (doWakeUpBeforeCommand) - wakeUp(receiverDeviceAwakeForMinutes, false); - - aapsLogger.debug(LTag.PUMPCOMM, "Current command: " + medtronicUtil.getCurrentCommand()); - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Active); - boolean doneWithError = false; - - for (int pageNumber = 0; pageNumber < 5; pageNumber++) { - - RawHistoryPage rawHistoryPage = new RawHistoryPage(aapsLogger); - // wakeUp(receiverDeviceAwakeForMinutes, false); - PumpMessage getHistoryMsg = makePumpMessage(MedtronicCommandType.GetHistoryData, - new GetHistoryPageCarelinkMessageBody(pageNumber)); - - aapsLogger.info(LTag.PUMPCOMM, "getPumpHistory: Page " + pageNumber); - // aapsLogger.info(LTag.PUMPCOMM,"getPumpHistoryPage("+pageNumber+"): "+ByteUtil.shortHexString(getHistoryMsg.getTxData())); - // Ask the pump to transfer history (we get first frame?) - - PumpMessage firstResponse = null; - boolean failed = false; - - medtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, null); - - for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { - - try { - firstResponse = runCommandWithArgs(getHistoryMsg); - failed = false; - break; - } catch (RileyLinkCommunicationException e) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "First call for PumpHistory failed (retry=%d)", retries)); - failed = true; - } - } - - if (failed) { - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - return pumpTotalResult; - } - - // aapsLogger.info(LTag.PUMPCOMM,"getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents())); - - PumpMessage ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, new PumpAckMessageBody()); - GetHistoryPageCarelinkMessageBody currentResponse = new GetHistoryPageCarelinkMessageBody(firstResponse - .getMessageBody().getTxData()); - int expectedFrameNum = 1; - boolean done = false; - // while (expectedFrameNum == currentResponse.getFrameNumber()) { - - int failures = 0; - while (!done) { - // examine current response for problems. - byte[] frameData = currentResponse.getFrameData(); - if ((frameData != null) && (frameData.length > 0) - && currentResponse.getFrameNumber() == expectedFrameNum) { - // success! got a frame. - if (frameData.length != 64) { - aapsLogger.warn(LTag.PUMPCOMM, "Expected frame of length 64, got frame of length " + frameData.length); - // but append it anyway? - } - // handle successful frame data - rawHistoryPage.appendData(currentResponse.getFrameData()); - // RileyLinkMedtronicService.getInstance().announceProgress(((100 / 16) * - // currentResponse.getFrameNumber() + 1)); - medtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, - currentResponse.getFrameNumber()); - - aapsLogger.info(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Got frame %d of Page %d", currentResponse.getFrameNumber(), pageNumber)); - // Do we need to ask for the next frame? - if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722 - expectedFrameNum++; - } else { - done = true; // successful completion - } - } else { - if (frameData == null) { - aapsLogger.error(LTag.PUMPCOMM, "null frame data, retrying"); - } else if (currentResponse.getFrameNumber() != expectedFrameNum) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Expected frame number %d, received %d (retrying)", expectedFrameNum, - currentResponse.getFrameNumber())); - } else if (frameData.length == 0) { - aapsLogger.warn(LTag.PUMPCOMM, "Frame has zero length, retrying"); - } - failures++; - if (failures == 6) { - aapsLogger.error(LTag.PUMPCOMM, - String.format(Locale.ENGLISH, "getPumpHistory: 6 failures in attempting to download frame %d of page %d, giving up.", - expectedFrameNum, pageNumber)); - done = true; // failure completion. - doneWithError = true; - } - } - - if (!done) { - // ask for next frame - PumpMessage nextMsg = null; - - for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { - - try { - nextMsg = sendAndListen(ackMsg); - break; - } catch (RileyLinkCommunicationException e) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Problem acknowledging frame response. (retry=%d)", retries)); - } - } - - if (nextMsg != null) - currentResponse = new GetHistoryPageCarelinkMessageBody(nextMsg.getMessageBody().getTxData()); - else { - aapsLogger.error(LTag.PUMPCOMM, "We couldn't acknowledge frame from pump, aborting operation."); - } - } - } - - if (rawHistoryPage.getLength() != 1024) { - aapsLogger.warn(LTag.PUMPCOMM, "getPumpHistory: short page. Expected length of 1024, found length of " - + rawHistoryPage.getLength()); - doneWithError = true; - } - - if (!rawHistoryPage.isChecksumOK()) { - aapsLogger.error(LTag.PUMPCOMM, "getPumpHistory: checksum is wrong"); - doneWithError = true; - } - - if (doneWithError) { - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - return pumpTotalResult; - } - - rawHistoryPage.dumpToDebug(); - - List medtronicHistoryEntries = medtronicPumpHistoryDecoder.processPageAndCreateRecords(rawHistoryPage); - - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Found %d history entries.", medtronicHistoryEntries.size())); - - pumpTotalResult.addHistoryEntries(medtronicHistoryEntries, pageNumber); - - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Search status: Search finished: %b", pumpTotalResult.isSearchFinished())); - - if (pumpTotalResult.isSearchFinished()) { - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - return pumpTotalResult; - } - } - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - return pumpTotalResult; - - } - - - public String getErrorResponse() { - return this.errorMessage; - } - - - @Override - public byte[] createPumpMessageContent(RLMessageType type) { - switch (type) { - case PowerOn: - return medtronicUtil.buildCommandPayload(rileyLinkServiceData, MedtronicCommandType.RFPowerOn, // - new byte[]{2, 1, (byte) receiverDeviceAwakeForMinutes}); // maybe this is better FIXME - - case ReadSimpleData: - return medtronicUtil.buildCommandPayload(rileyLinkServiceData, MedtronicCommandType.PumpModel, null); - } - return new byte[0]; - } - - - private PumpMessage makePumpMessage(MedtronicCommandType messageType, byte[] body) { - return makePumpMessage(messageType, body == null ? new CarelinkShortMessageBody() - : new CarelinkShortMessageBody(body)); - } - - - private PumpMessage makePumpMessage(MedtronicCommandType messageType) { - return makePumpMessage(messageType, (byte[]) null); - } - - - private PumpMessage makePumpMessage(MedtronicCommandType messageType, MessageBody messageBody) { - PumpMessage msg = new PumpMessage(aapsLogger); - msg.init(PacketType.Carelink, rileyLinkServiceData.pumpIDBytes, messageType, messageBody); - return msg; - } - - - private PumpMessage sendAndGetResponse(MedtronicCommandType commandType) throws RileyLinkCommunicationException { - - return sendAndGetResponse(commandType, null, DEFAULT_TIMEOUT); - } - - - /** - * Main wrapper method for sending data - (for getting responses) - * - * @param commandType - * @param bodyData - * @param timeoutMs - * @return - */ - private PumpMessage sendAndGetResponse(MedtronicCommandType commandType, byte[] bodyData, int timeoutMs) - throws RileyLinkCommunicationException { - // wakeUp - if (doWakeUpBeforeCommand) - wakeUp(receiverDeviceAwakeForMinutes, false); - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Active); - - // create message - PumpMessage msg; - - if (bodyData == null) - msg = makePumpMessage(commandType); - else - msg = makePumpMessage(commandType, bodyData); - - // send and wait for response - PumpMessage response = sendAndListen(msg, timeoutMs); - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - return response; - } - - - private PumpMessage sendAndListen(PumpMessage msg) throws RileyLinkCommunicationException { - return sendAndListen(msg, 4000); // 2000 - } - - - // All pump communications go through this function. - @Override - protected PumpMessage sendAndListen(PumpMessage msg, int timeout_ms) throws RileyLinkCommunicationException { - return super.sendAndListen(msg, timeout_ms); - } - - - private Object sendAndGetResponseWithCheck(MedtronicCommandType commandType) { - - return sendAndGetResponseWithCheck(commandType, null); - } - - - private Object sendAndGetResponseWithCheck(MedtronicCommandType commandType, byte[] bodyData) { - - aapsLogger.debug(LTag.PUMPCOMM, "getDataFromPump: " + commandType); - - for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { - - try { - PumpMessage response = sendAndGetResponse(commandType, bodyData, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); - - String check = checkResponseContent(response, commandType.commandDescription, commandType.expectedLength); - - if (check == null) { - - Object dataResponse = medtronicConverter.convertResponse(medtronicPumpPlugin.getPumpDescription().getPumpType(), commandType, response.getRawContent()); - - if (dataResponse != null) { - this.errorMessage = null; - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Converted response for %s is %s.", commandType.name(), dataResponse)); - - return dataResponse; - } else { - this.errorMessage = "Error decoding response."; - } - } else { - this.errorMessage = check; - // return null; - } - - } catch (RileyLinkCommunicationException e) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.getMessage(), retries + 1)); - } - - } - - return null; - } - - - private String checkResponseContent(PumpMessage response, String method, int expectedLength) { - - if (!response.isValid()) { - String responseData = String.format("%s: Invalid response.", method); - aapsLogger.warn(LTag.PUMPCOMM, responseData); - return responseData; - } - - byte[] contents = response.getRawContent(); - - if (contents != null) { - if (contents.length >= expectedLength) { - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: Content: %s", method, ByteUtil.shortHexString(contents))); - return null; - - } else { - String responseData = String.format( - "%s: Cannot return data. Data is too short [expected=%s, received=%s].", method, "" - + expectedLength, "" + contents.length); - - aapsLogger.warn(LTag.PUMPCOMM, responseData); - return responseData; - } - } else { - String responseData = String.format("%s: Cannot return data. Null response.", method); - aapsLogger.warn(LTag.PUMPCOMM, responseData); - return responseData; - } - } - - - // PUMP SPECIFIC COMMANDS - - public Float getRemainingInsulin() { - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetRemainingInsulin); - - return responseObject == null ? null : (Float) responseObject; - } - - - public MedtronicDeviceType getPumpModel() { - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.PumpModel); - - return responseObject == null ? null : (MedtronicDeviceType) responseObject; - } - - - public BasalProfile getBasalProfile() { - - // wakeUp - if (doWakeUpBeforeCommand) - wakeUp(receiverDeviceAwakeForMinutes, false); - - MedtronicCommandType commandType = MedtronicCommandType.GetBasalProfileSTD; - - aapsLogger.debug(LTag.PUMPCOMM, "getDataFromPump: " + commandType); - - medtronicUtil.setCurrentCommand(commandType); - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Active); - - for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { - - try { - // create message - PumpMessage msg; - - msg = makePumpMessage(commandType); - - // send and wait for response - - PumpMessage response = sendAndListen(msg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); - -// aapsLogger.debug(LTag.PUMPCOMM,"1st Response: " + HexDump.toHexStringDisplayable(response.getRawContent())); -// aapsLogger.debug(LTag.PUMPCOMM,"1st Response: " + HexDump.toHexStringDisplayable(response.getMessageBody().getTxData())); - - String check = checkResponseContent(response, commandType.commandDescription, 1); - - byte[] data = null; - - if (check == null) { - - data = response.getRawContentOfFrame(); - - PumpMessage ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, new PumpAckMessageBody()); - - while (checkIfWeHaveMoreData(commandType, response, data)) { - - response = sendAndListen(ackMsg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); - -// aapsLogger.debug(LTag.PUMPCOMM,"{} Response: {}", runs, HexDump.toHexStringDisplayable(response2.getRawContent())); -// aapsLogger.debug(LTag.PUMPCOMM,"{} Response: {}", runs, -// HexDump.toHexStringDisplayable(response2.getMessageBody().getTxData())); - - String check2 = checkResponseContent(response, commandType.commandDescription, 1); - - if (check2 == null) { - - data = ByteUtil.concat(data, response.getRawContentOfFrame()); - - } else { - this.errorMessage = check2; - aapsLogger.error(LTag.PUMPCOMM, "Error with response got GetProfile: " + check2); - } - } - - } else { - errorMessage = check; - } - - BasalProfile basalProfile = (BasalProfile) medtronicConverter.convertResponse(medtronicPumpPlugin.getPumpDescription().getPumpType(), commandType, data); - - if (basalProfile != null) { - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Converted response for %s is %s.", commandType.name(), basalProfile)); - - medtronicUtil.setCurrentCommand(null); - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - return basalProfile; - } - - } catch (RileyLinkCommunicationException e) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.getMessage(), retries + 1)); - } - } - - aapsLogger.warn(LTag.PUMPCOMM, "Error reading profile in max retries."); - medtronicUtil.setCurrentCommand(null); - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.Sleeping); - - return null; - - } - - - private boolean checkIfWeHaveMoreData(MedtronicCommandType commandType, PumpMessage response, byte[] data) { - - if (commandType == MedtronicCommandType.GetBasalProfileSTD || // - commandType == MedtronicCommandType.GetBasalProfileA || // - commandType == MedtronicCommandType.GetBasalProfileB) { - byte[] responseRaw = response.getRawContentOfFrame(); - - int last = responseRaw.length - 1; - - aapsLogger.debug(LTag.PUMPCOMM, "Length: " + data.length); - - if (data.length >= BasalProfile.MAX_RAW_DATA_SIZE) { - return false; - } - - if (responseRaw.length < 2) { - return false; - } - - return !(responseRaw[last] == 0x00 && responseRaw[last - 1] == 0x00 && responseRaw[last - 2] == 0x00); - } - - return false; - } - - - public ClockDTO getPumpTime() { - - ClockDTO clockDTO = new ClockDTO(); - clockDTO.localDeviceTime = new LocalDateTime(); - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetRealTimeClock); - - if (responseObject != null) { - clockDTO.pumpTime = (LocalDateTime) responseObject; - return clockDTO; - } - - return null; - } - - - public TempBasalPair getTemporaryBasal() { - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.ReadTemporaryBasal); - - return responseObject == null ? null : (TempBasalPair) responseObject; - } - - - public Map getPumpSettings() { - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.getSettings(medtronicUtil - .getMedtronicPumpModel())); - - return responseObject == null ? null : (Map) responseObject; - } - - - public Boolean setBolus(double units) { - - aapsLogger.info(LTag.PUMPCOMM, "setBolus: " + units); - - return setCommand(MedtronicCommandType.SetBolus, medtronicUtil.getBolusStrokes(units)); - - } - - - public boolean setTBR(TempBasalPair tbr) { - - aapsLogger.info(LTag.PUMPCOMM, "setTBR: " + tbr.getDescription()); - - return setCommand(MedtronicCommandType.SetTemporaryBasal, tbr.getAsRawData()); - } - - - public Boolean setPumpTime() { - - GregorianCalendar gc = new GregorianCalendar(); - gc.add(Calendar.SECOND, 5); - - aapsLogger.info(LTag.PUMPCOMM, "setPumpTime: " + DateTimeUtil.toString(gc)); - - int i = 1; - byte[] data = new byte[8]; - data[0] = 7; - data[i] = (byte) gc.get(Calendar.HOUR_OF_DAY); - data[i + 1] = (byte) gc.get(Calendar.MINUTE); - data[i + 2] = (byte) gc.get(Calendar.SECOND); - - byte[] yearByte = MedtronicUtil.getByteArrayFromUnsignedShort(gc.get(Calendar.YEAR), true); - - data[i + 3] = yearByte[0]; - data[i + 4] = yearByte[1]; - - data[i + 5] = (byte) (gc.get(Calendar.MONTH) + 1); - data[i + 6] = (byte) gc.get(Calendar.DAY_OF_MONTH); - - //aapsLogger.info(LTag.PUMPCOMM,"setPumpTime: Body: " + ByteUtil.getHex(data)); - - return setCommand(MedtronicCommandType.SetRealTimeClock, data); - - } - - - private boolean setCommand(MedtronicCommandType commandType, byte[] body) { - - for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { - - try { - if (this.doWakeUpBeforeCommand) - wakeUp(false); - - if (debugSetCommands) - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: Body - %s", commandType.getCommandDescription(), - ByteUtil.getHex(body))); - - PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(body)); - - PumpMessage pumpMessage = runCommandWithArgs(msg); - - if (debugSetCommands) - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: %s", commandType.getCommandDescription(), pumpMessage.getResponseContent())); - - if (pumpMessage.commandType == MedtronicCommandType.CommandACK) { - return true; - } else { - aapsLogger.warn(LTag.PUMPCOMM, "We received non-ACK response from pump: " + pumpMessage.getResponseContent()); - } - - } catch (RileyLinkCommunicationException e) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.getMessage(), retries + 1)); - } - } - - return false; - } - - - public boolean cancelTBR() { - return setTBR(new TempBasalPair(0.0d, false, 0)); - } - - - public BatteryStatusDTO getRemainingBattery() { - - Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBatteryStatus); - - return responseObject == null ? null : (BatteryStatusDTO) responseObject; - } - - - public Boolean setBasalProfile(BasalProfile basalProfile) { - - List> basalProfileFrames = medtronicUtil.getBasalProfileFrames(basalProfile.getRawData()); - - for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { - - PumpMessage responseMessage = null; - try { - responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileSTD, - basalProfileFrames); - - if (responseMessage.commandType == MedtronicCommandType.CommandACK) - return true; - - } catch (RileyLinkCommunicationException e) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.getMessage(), retries + 1)); - } - - if (responseMessage != null) - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Set Basal Profile: Invalid response: commandType=%s,rawData=%s", responseMessage.commandType, ByteUtil.shortHexString(responseMessage.getRawContent()))); - else - aapsLogger.warn(LTag.PUMPCOMM, "Set Basal Profile: Null response."); - } - - return false; - - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.kt new file mode 100644 index 0000000000..4f918f1ceb --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.kt @@ -0,0 +1,679 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm + +import android.os.SystemClock +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +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.ble.RileyLinkCommunicationException +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.defs.RLMessageType +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.* +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType.Companion.getSettings +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.Companion.createByteArray +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.Companion.getByteArrayFromUnsignedShort +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Original file created by geoff on 5/30/16. + * + * + * Split into 2 implementations, so that we can split it by target device. - Andy + * This was mostly rewritten from Original version, and lots of commands and + * functionality added. + */ +@Singleton +class MedtronicCommunicationManager // This empty constructor must be kept, otherwise dagger injection might break! +@Inject constructor() : RileyLinkCommunicationManager() { + + @Inject lateinit var medtronicPumpStatus: MedtronicPumpStatus + @Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin + @Inject lateinit var medtronicConverter: MedtronicConverter + @Inject lateinit var medtronicUtil: MedtronicUtil + @Inject lateinit var medtronicPumpHistoryDecoder: MedtronicPumpHistoryDecoder + + private val MAX_COMMAND_TRIES = 3 + private val DEFAULT_TIMEOUT = 2000 + private val RILEYLINK_TIMEOUT: Long = 15 * 60 * 1000 // 15 min + + var errorResponse: String? = null + private set + private val debugSetCommands = false + private var doWakeUpBeforeCommand = true + + @Inject + fun onInit(): Unit { + // we can't do this in the constructor, as sp only gets injected after the constructor has returned + medtronicPumpStatus.previousConnection = sp.getLong( + RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L) + } + + override fun createResponseMessage(payload: ByteArray): PumpMessage { + return PumpMessage(aapsLogger, payload) + } + + override fun setPumpDeviceState(pumpDeviceState: PumpDeviceState) { + medtronicPumpStatus.pumpDeviceState = pumpDeviceState + } + + fun setDoWakeUpBeforeCommand(doWakeUp: Boolean) { + doWakeUpBeforeCommand = doWakeUp + } + + override fun isDeviceReachable(): Boolean { + return isDeviceReachable(false) + } + + /** + * We do actual wakeUp and compare PumpModel with currently selected one. If returned model is + * not Unknown, pump is reachable. + * + * @return + */ + fun isDeviceReachable(canPreventTuneUp: Boolean): Boolean { + val state = medtronicPumpStatus.pumpDeviceState + if (state !== PumpDeviceState.PumpUnreachable) medtronicPumpStatus.pumpDeviceState = PumpDeviceState.WakingUp + for (retry in 0..4) { + aapsLogger.debug(LTag.PUMPCOMM, "isDeviceReachable. Waking pump... " + if (retry != 0) " (retry $retry)" else "") + val connected = connectToDevice() + if (connected) return true + SystemClock.sleep(1000) + } + if (state !== PumpDeviceState.PumpUnreachable) medtronicPumpStatus.pumpDeviceState = PumpDeviceState.PumpUnreachable + if (!canPreventTuneUp) { + val diff = System.currentTimeMillis() - medtronicPumpStatus.lastConnection + if (diff > RILEYLINK_TIMEOUT) { + serviceTaskExecutor.startTask(WakeAndTuneTask(injector)) + } + } + return false + } + + private fun connectToDevice(): Boolean { + val state = medtronicPumpStatus.pumpDeviceState + + // check connection + val pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData) // simple + val rfSpyResponse = rfspy.transmitThenReceive(RadioPacket(injector, pumpMsgContent), 0.toByte(), 200.toByte(), + 0.toByte(), 0.toByte(), 25000, 0.toByte()) + aapsLogger.info(LTag.PUMPCOMM, "wakeup: raw response is " + ByteUtil.shortHexString(rfSpyResponse.raw)) + if (rfSpyResponse.wasTimeout()) { + aapsLogger.error(LTag.PUMPCOMM, "isDeviceReachable. Failed to find pump (timeout).") + } else if (rfSpyResponse.looksLikeRadioPacket()) { + val radioResponse = RadioResponse(injector) + try { + radioResponse.init(rfSpyResponse.raw) + if (radioResponse.isValid) { + val pumpResponse = createResponseMessage(radioResponse.payload) + if (!pumpResponse.isValid) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Response is invalid ! [interrupted=%b, timeout=%b]", rfSpyResponse.wasInterrupted(), + rfSpyResponse.wasTimeout())) + } else { + + // radioResponse.rssi; + val dataResponse = medtronicConverter.decodeModel(pumpResponse.rawContent) + val pumpModel = dataResponse as MedtronicDeviceType? + val valid = pumpModel !== MedtronicDeviceType.Unknown_Device + if (!medtronicUtil.isModelSet && valid) { + medtronicUtil.medtronicPumpModel = pumpModel!! + medtronicUtil.isModelSet = true + } + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "isDeviceReachable. PumpModel is %s - Valid: %b (rssi=%d)", medtronicUtil.medtronicPumpModel, valid, + radioResponse.rssi)) + if (valid) { + if (state === PumpDeviceState.PumpUnreachable) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.WakingUp + else + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + rememberLastGoodDeviceCommunicationTime() + return true + } else { + if (state !== PumpDeviceState.PumpUnreachable) medtronicPumpStatus.pumpDeviceState = PumpDeviceState.PumpUnreachable + } + } + } else { + aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Failed to parse radio response: " + + ByteUtil.shortHexString(rfSpyResponse.raw)) + } + } catch (e: RileyLinkCommunicationException) { + aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Failed to decode radio response: " + + ByteUtil.shortHexString(rfSpyResponse.raw)) + } + } else { + aapsLogger.warn(LTag.PUMPCOMM, "isDeviceReachable. Unknown response: " + ByteUtil.shortHexString(rfSpyResponse.raw)) + } + return false + } + + override fun tryToConnectToDevice(): Boolean { + return isDeviceReachable(true) + } + + @Throws(RileyLinkCommunicationException::class) + private fun runCommandWithArgs(msg: PumpMessage): PumpMessage { + if (debugSetCommands) aapsLogger.debug(LTag.PUMPCOMM, "Run command with Args: ") + val rval: PumpMessage + val shortMessage = makePumpMessage(msg.commandType, CarelinkShortMessageBody(byteArrayOf(0))) + // look for ack from short message + val shortResponse = sendAndListen(shortMessage) + return if (shortResponse.commandType === MedtronicCommandType.CommandACK) { + if (debugSetCommands) aapsLogger.debug(LTag.PUMPCOMM, "Run command with Args: Got ACK response") + rval = sendAndListen(msg) + if (debugSetCommands) aapsLogger.debug(LTag.PUMPCOMM, "2nd Response: $rval") + rval + } else { + aapsLogger.error(LTag.PUMPCOMM, "runCommandWithArgs: Pump did not ack Attention packet") + PumpMessage(aapsLogger, "No ACK after Attention packet.") + } + } + + @Throws(RileyLinkCommunicationException::class) + private fun runCommandWithFrames(commandType: MedtronicCommandType, frames: List>): PumpMessage? { + aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: " + commandType.name) + var rval: PumpMessage? = null + val shortMessage = makePumpMessage(commandType, CarelinkShortMessageBody(byteArrayOf(0))) + // look for ack from short message + val shortResponse = sendAndListen(shortMessage) + if (shortResponse.commandType !== MedtronicCommandType.CommandACK) { + aapsLogger.error(LTag.PUMPCOMM, "runCommandWithFrames: Pump did not ack Attention packet") + return PumpMessage(aapsLogger, "No ACK after start message.") + } else { + aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: Got ACK response for Attention packet") + } + var frameNr = 1 + for (frame in frames) { + val frameData = createByteArray(frame) + + // aapsLogger.debug(LTag.PUMPCOMM,"Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData)); + val msg = makePumpMessage(commandType, CarelinkLongMessageBody(frameData)) + rval = sendAndListen(msg) + + // aapsLogger.debug(LTag.PUMPCOMM,"PumpResponse: " + rval); + if (rval.commandType !== MedtronicCommandType.CommandACK) { + aapsLogger.error(LTag.PUMPCOMM, "runCommandWithFrames: Pump did not ACK frame #$frameNr") + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Run command with Frames FAILED (command=%s, response=%s)", commandType.name, + rval.toString())) + return PumpMessage(aapsLogger, "No ACK after frame #$frameNr") + } else { + aapsLogger.debug(LTag.PUMPCOMM, "Run command with Frames: Got ACK response for frame #$frameNr") + } + frameNr++ + } + return rval + } + + fun getPumpHistory(lastEntry: PumpHistoryEntry?, targetDate: LocalDateTime?): PumpHistoryResult { + val pumpTotalResult = PumpHistoryResult(aapsLogger, lastEntry, if (targetDate == null) null else DateTimeUtil.toATechDate(targetDate)) + if (doWakeUpBeforeCommand) wakeUp(receiverDeviceAwakeForMinutes, false) + aapsLogger.debug(LTag.PUMPCOMM, "Current command: " + medtronicUtil.getCurrentCommand()) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Active + var doneWithError = false + for (pageNumber in 0..4) { + val rawHistoryPage = RawHistoryPage(aapsLogger) + // wakeUp(receiverDeviceAwakeForMinutes, false); + val getHistoryMsg = makePumpMessage(MedtronicCommandType.GetHistoryData, + GetHistoryPageCarelinkMessageBody(pageNumber)) + aapsLogger.info(LTag.PUMPCOMM, "getPumpHistory: Page $pageNumber") + // aapsLogger.info(LTag.PUMPCOMM,"getPumpHistoryPage("+pageNumber+"): "+ByteUtil.shortHexString(getHistoryMsg.getTxData())); + // Ask the pump to transfer history (we get first frame?) + var firstResponse: PumpMessage? = null + var failed = false + medtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, null) + for (retries in 0 until MAX_COMMAND_TRIES) { + try { + firstResponse = runCommandWithArgs(getHistoryMsg) + failed = false + break + } catch (e: RileyLinkCommunicationException) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "First call for PumpHistory failed (retry=%d)", retries)) + failed = true + } + } + if (failed) { + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return pumpTotalResult + } + + // aapsLogger.info(LTag.PUMPCOMM,"getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents())); + val ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, PumpAckMessageBody()) + var currentResponse = GetHistoryPageCarelinkMessageBody(firstResponse!!.messageBody!!.txData) + var expectedFrameNum = 1 + var done = false + // while (expectedFrameNum == currentResponse.getFrameNumber()) { + var failures = 0 + while (!done) { + // examine current response for problems. + val frameData = currentResponse.frameData + if (frameData.size > 0 && currentResponse.frameNumber == expectedFrameNum) { + // success! got a frame. + if (frameData.size != 64) { + aapsLogger.warn(LTag.PUMPCOMM, "Expected frame of length 64, got frame of length " + frameData.size) + // but append it anyway? + } + // handle successful frame data + rawHistoryPage.appendData(currentResponse.frameData) + // RileyLinkMedtronicService.getInstance().announceProgress(((100 / 16) * + // currentResponse.getFrameNumber() + 1)); + medtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, + currentResponse.frameNumber) + aapsLogger.info(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Got frame %d of Page %d", currentResponse.frameNumber, pageNumber)) + // Do we need to ask for the next frame? + if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722 + expectedFrameNum++ + } else { + done = true // successful completion + } + } else { + if (frameData.size == 0) { + aapsLogger.error(LTag.PUMPCOMM, "null frame data, retrying") + } else if (currentResponse.frameNumber != expectedFrameNum) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Expected frame number %d, received %d (retrying)", expectedFrameNum, + currentResponse.frameNumber)) + } else if (frameData.size == 0) { + aapsLogger.warn(LTag.PUMPCOMM, "Frame has zero length, retrying") + } + failures++ + if (failures == 6) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: 6 failures in attempting to download frame %d of page %d, giving up.", + expectedFrameNum, pageNumber)) + done = true // failure completion. + doneWithError = true + } + } + if (!done) { + // ask for next frame + var nextMsg: PumpMessage? = null + for (retries in 0 until MAX_COMMAND_TRIES) { + try { + nextMsg = sendAndListen(ackMsg) + break + } catch (e: RileyLinkCommunicationException) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Problem acknowledging frame response. (retry=%d)", retries)) + } + } + if (nextMsg != null) + currentResponse = GetHistoryPageCarelinkMessageBody(nextMsg.messageBody!!.txData) + else { + aapsLogger.error(LTag.PUMPCOMM, "We couldn't acknowledge frame from pump, aborting operation.") + } + } + } + if (rawHistoryPage.length != 1024) { + aapsLogger.warn(LTag.PUMPCOMM, "getPumpHistory: short page. Expected length of 1024, found length of " + + rawHistoryPage.length) + doneWithError = true + } + if (!rawHistoryPage.isChecksumOK) { + aapsLogger.error(LTag.PUMPCOMM, "getPumpHistory: checksum is wrong") + doneWithError = true + } + if (doneWithError) { + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return pumpTotalResult + } + rawHistoryPage.dumpToDebug() + val medtronicHistoryEntries = medtronicPumpHistoryDecoder.processPageAndCreateRecords(rawHistoryPage) + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Found %d history entries.", medtronicHistoryEntries.size)) + pumpTotalResult.addHistoryEntries(medtronicHistoryEntries) //, pageNumber) + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getPumpHistory: Search status: Search finished: %b", pumpTotalResult.isSearchFinished)) + if (pumpTotalResult.isSearchFinished) { + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return pumpTotalResult + } + } + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return pumpTotalResult + } + + override fun createPumpMessageContent(type: RLMessageType): ByteArray { + return when (type) { + RLMessageType.PowerOn -> medtronicUtil.buildCommandPayload(rileyLinkServiceData, MedtronicCommandType.RFPowerOn, byteArrayOf(2, 1, receiverDeviceAwakeForMinutes.toByte())) + RLMessageType.ReadSimpleData -> medtronicUtil.buildCommandPayload(rileyLinkServiceData, MedtronicCommandType.PumpModel, null) + else -> ByteArray(0) + } + } + + private fun makePumpMessage(messageType: MedtronicCommandType, body: ByteArray? = null): PumpMessage { + return makePumpMessage(messageType, body?.let { CarelinkShortMessageBody(it) } + ?: CarelinkShortMessageBody()) + } + + private fun makePumpMessage(messageType: MedtronicCommandType?, messageBody: MessageBody): PumpMessage { + val msg = PumpMessage(aapsLogger) + msg.init(PacketType.Carelink, rileyLinkServiceData.pumpIDBytes, messageType, messageBody) + return msg + } + + /** + * Main wrapper method for sending data - (for getting responses) + * + * @param commandType + * @param bodyData + * @param timeoutMs + * @return + */ + @Throws(RileyLinkCommunicationException::class) + private fun sendAndGetResponse(commandType: MedtronicCommandType, bodyData: ByteArray? = null, timeoutMs: Int = DEFAULT_TIMEOUT): PumpMessage { + // wakeUp + if (doWakeUpBeforeCommand) wakeUp(receiverDeviceAwakeForMinutes, false) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Active + + // create message + val msg: PumpMessage + msg = bodyData?.let { makePumpMessage(commandType, it) } ?: makePumpMessage(commandType) + + // send and wait for response + val response = sendAndListen(msg, timeoutMs) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return response + } + + @Throws(RileyLinkCommunicationException::class) + private fun sendAndListen(msg: PumpMessage): PumpMessage { + return sendAndListen(msg, 4000) // 2000 + } + + // All pump communications go through this function. + @Throws(RileyLinkCommunicationException::class) + protected /*override*/ fun sendAndListen(msg: PumpMessage, timeout_ms: Int): PumpMessage { + return super.sendAndListen(msg, timeout_ms)!! + } + + private inline fun sendAndGetResponseWithCheck( + commandType: MedtronicCommandType, + bodyData: ByteArray? = null, + decode: (pumpType: PumpType, commandType: MedtronicCommandType, rawContent: ByteArray) -> T + ): T? { + aapsLogger.debug(LTag.PUMPCOMM, "getDataFromPump: $commandType") + for (retries in 0 until MAX_COMMAND_TRIES) { + try { + val response = sendAndGetResponse(commandType, bodyData, DEFAULT_TIMEOUT + DEFAULT_TIMEOUT * retries) + val check = checkResponseContent(response, commandType.commandDescription, commandType.expectedLength) + if (check == null) { + + checkResponseRawContent(response.rawContent, commandType) { return@sendAndGetResponseWithCheck null } + + val dataResponse = decode(medtronicPumpStatus.pumpType, commandType, response.rawContent) + if (dataResponse != null) { + errorResponse = null + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Converted response for %s is %s.", commandType.name, dataResponse)) + return dataResponse + } else { + errorResponse = "Error decoding response." + } + } else { + errorResponse = check + // return null; + } + } catch (e: RileyLinkCommunicationException) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.message, retries + 1)) + } + } + return null + } + + private inline fun checkResponseRawContent(rawContent: ByteArray?, commandType: MedtronicCommandType, errorCase: () -> Unit) { + if (rawContent?.isEmpty() != false && commandType != MedtronicCommandType.PumpModel) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Content is empty or too short, no data to convert (type=%s,isNull=%b,length=%s)", + commandType.name, rawContent == null, rawContent?.size ?: "-")) + errorCase.invoke() + } else { + aapsLogger.debug(LTag.PUMPCOMM, "Raw response before convert: " + ByteUtil.shortHexString(rawContent)) + } + } + + private fun checkResponseContent(response: PumpMessage, method: String, expectedLength: Int): String? { + if (!response.isValid) { + val responseData = String.format("%s: Invalid response.", method) + aapsLogger.warn(LTag.PUMPCOMM, responseData) + return responseData + } + val contents = response.rawContent + return if (contents.size > 0) { + if (contents.size >= expectedLength) { + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: Content: %s", method, ByteUtil.shortHexString(contents))) + null + } else { + val responseData = String.format( + "%s: Cannot return data. Data is too short [expected=%s, received=%s].", method, "" + + expectedLength, "" + contents.size) + aapsLogger.warn(LTag.PUMPCOMM, responseData) + responseData + } + } else { + val responseData = String.format("%s: Cannot return data. Zero length response.", method) + aapsLogger.warn(LTag.PUMPCOMM, responseData) + responseData + } + } + + // PUMP SPECIFIC COMMANDS + fun getRemainingInsulin(): Double? { + return sendAndGetResponseWithCheck(MedtronicCommandType.GetRemainingInsulin) { _, _, rawContent -> + medtronicConverter.decodeRemainingInsulin(rawContent) + } + } + + fun getPumpModel(): MedtronicDeviceType? { + return sendAndGetResponseWithCheck(MedtronicCommandType.PumpModel) { _, _, rawContent -> + medtronicConverter.decodeModel(rawContent) + } + } + + fun getBasalProfile(): BasalProfile? { + + // wakeUp + if (doWakeUpBeforeCommand) wakeUp(receiverDeviceAwakeForMinutes, false) + val commandType = MedtronicCommandType.GetBasalProfileSTD + aapsLogger.debug(LTag.PUMPCOMM, "getDataFromPump: $commandType") + medtronicUtil.setCurrentCommand(commandType) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Active + for (retries in 0..MAX_COMMAND_TRIES) { + try { + // create message + var msg: PumpMessage + msg = makePumpMessage(commandType) + + // send and wait for response + var response = sendAndListen(msg, DEFAULT_TIMEOUT + DEFAULT_TIMEOUT * retries) + +// aapsLogger.debug(LTag.PUMPCOMM,"1st Response: " + HexDump.toHexStringDisplayable(response.getRawContent())); +// aapsLogger.debug(LTag.PUMPCOMM,"1st Response: " + HexDump.toHexStringDisplayable(response.getMessageBody().getTxData())); + val check = checkResponseContent(response, commandType.commandDescription, 1) + var data: ByteArray = byteArrayOf() + if (check == null) { + data = response.rawContentOfFrame + val ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, PumpAckMessageBody()) + while (checkIfWeHaveMoreData(commandType, response, data)) { + response = sendAndListen(ackMsg, DEFAULT_TIMEOUT + DEFAULT_TIMEOUT * retries) + +// aapsLogger.debug(LTag.PUMPCOMM,"{} Response: {}", runs, HexDump.toHexStringDisplayable(response2.getRawContent())); +// aapsLogger.debug(LTag.PUMPCOMM,"{} Response: {}", runs, +// HexDump.toHexStringDisplayable(response2.getMessageBody().getTxData())); + val check2 = checkResponseContent(response, commandType.commandDescription, 1) + if (check2 == null) { + data = ByteUtil.concat(data, response.rawContentOfFrame) + } else { + errorResponse = check2 + aapsLogger.error(LTag.PUMPCOMM, "Error with response got GetProfile: $check2") + } + } + } else { + errorResponse = check + } + + + aapsLogger.debug(LTag.PUMPCOMM, "End Response: {}", ByteUtil.getHex(data)) + + var basalProfile: BasalProfile? = medtronicConverter.decodeBasalProfile(medtronicPumpPlugin.pumpDescription.pumpType, data) + // checkResponseRawContent(data, commandType) { + // basalProfile = medtronicConverter.decodeBasalProfile(medtronicPumpPlugin.pumpDescription.pumpType, data) + // } + + if (basalProfile != null) { + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Converted response for %s is %s.", commandType.name, basalProfile)) + medtronicUtil.setCurrentCommand(null) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return basalProfile + } + } catch (e: RileyLinkCommunicationException) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.message, retries + 1)) + } + } + aapsLogger.warn(LTag.PUMPCOMM, "Error reading profile in max retries.") + medtronicUtil.setCurrentCommand(null) + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.Sleeping + return null + } + + private fun checkIfWeHaveMoreData(commandType: MedtronicCommandType, response: PumpMessage, data: ByteArray): Boolean { + if (commandType === MedtronicCommandType.GetBasalProfileSTD || // + commandType === MedtronicCommandType.GetBasalProfileA || // + commandType === MedtronicCommandType.GetBasalProfileB) { + val responseRaw = response.rawContentOfFrame + val last = responseRaw.size - 1 + aapsLogger.debug(LTag.PUMPCOMM, "Length: " + data.size) + if (data.size >= BasalProfile.MAX_RAW_DATA_SIZE) { + return false + } + return if (responseRaw.size < 2) { + false + } else !(responseRaw[last] == 0x00.toByte() && responseRaw[last - 1] == 0x00.toByte() && responseRaw[last - 2] == 0x00.toByte()) + } + return false + } + + fun getPumpTime(): ClockDTO? { + val localTime = LocalDateTime() + val responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetRealTimeClock) { _, _, rawContent -> + medtronicConverter.decodeTime(rawContent) + } + if (responseObject != null) { + return ClockDTO(localDeviceTime = localTime, pumpTime = responseObject) + } + return null + } + + fun getTemporaryBasal(): TempBasalPair? { + return sendAndGetResponseWithCheck(MedtronicCommandType.ReadTemporaryBasal) { _, _, rawContent -> + TempBasalPair(aapsLogger, rawContent) + } + } + + fun getPumpSettings(): Map? { + return sendAndGetResponseWithCheck(getSettings(medtronicUtil.medtronicPumpModel)) { _, _, rawContent -> + medtronicConverter.decodeSettingsLoop(rawContent) + } + } + + fun setBolus(units: Double): Boolean { + aapsLogger.info(LTag.PUMPCOMM, "setBolus: $units") + return setCommand(MedtronicCommandType.SetBolus, medtronicUtil.getBolusStrokes(units)) + } + + fun setTemporaryBasal(tbr: TempBasalPair): Boolean { + aapsLogger.info(LTag.PUMPCOMM, "setTBR: " + tbr.description) + return setCommand(MedtronicCommandType.SetTemporaryBasal, tbr.asRawData) + } + + fun setPumpTime(): Boolean { + val gc = GregorianCalendar() + gc.add(Calendar.SECOND, 5) + aapsLogger.info(LTag.PUMPCOMM, "setPumpTime: " + DateTimeUtil.toString(gc)) + val yearByte = getByteArrayFromUnsignedShort(gc[Calendar.YEAR], true) + // val i = 1 + // val data = ByteArray(8) + // data[0] = 7 + // data[i] = gc[Calendar.HOUR_OF_DAY].toByte() + // data[i + 1] = gc[Calendar.MINUTE].toByte() + // data[i + 2] = gc[Calendar.SECOND].toByte() + // val yearByte = getByteArrayFromUnsignedShort(gc[Calendar.YEAR], true) + // data[i + 3] = yearByte[0] + // data[i + 4] = yearByte[1] + // data[i + 5] = (gc[Calendar.MONTH] + 1).toByte() + // data[i + 6] = gc[Calendar.DAY_OF_MONTH].toByte() + + val timeData = byteArrayOf( + 7, + gc[Calendar.HOUR_OF_DAY].toByte(), + gc[Calendar.MINUTE].toByte(), + gc[Calendar.SECOND].toByte(), + yearByte[0], + yearByte[1], + (gc[Calendar.MONTH] + 1).toByte(), + gc[Calendar.DAY_OF_MONTH].toByte() + ) + + //aapsLogger.info(LTag.PUMPCOMM,"setPumpTime: Body: " + ByteUtil.getHex(data)); + return setCommand(MedtronicCommandType.SetRealTimeClock, timeData) + } + + private fun setCommand(commandType: MedtronicCommandType, body: ByteArray): Boolean { + for (retries in 0..MAX_COMMAND_TRIES) { + try { + if (doWakeUpBeforeCommand) wakeUp(false) + if (debugSetCommands) aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: Body - %s", commandType.commandDescription, + ByteUtil.getHex(body))) + val msg = makePumpMessage(commandType, CarelinkLongMessageBody(body)) + val pumpMessage = runCommandWithArgs(msg) + if (debugSetCommands) aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "%s: %s", commandType.commandDescription, pumpMessage.responseContent)) + if (pumpMessage.commandType === MedtronicCommandType.CommandACK) { + return true + } else { + aapsLogger.warn(LTag.PUMPCOMM, "We received non-ACK response from pump: " + pumpMessage.responseContent) + } + } catch (e: RileyLinkCommunicationException) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.message, retries + 1)) + } + } + return false + } + + fun cancelTBR(): Boolean { + return setTemporaryBasal(TempBasalPair(0.0, false, 0)) + } + + fun getRemainingBattery(): BatteryStatusDTO? { + return sendAndGetResponseWithCheck(MedtronicCommandType.GetBatteryStatus) { _, _, rawContent -> + medtronicConverter.decodeBatteryStatus(rawContent) + } + } + + fun setBasalProfile(basalProfile: BasalProfile): Boolean { + val basalProfileFrames = medtronicUtil.getBasalProfileFrames(basalProfile.rawData) + for (retries in 0..MAX_COMMAND_TRIES) { + var responseMessage: PumpMessage? = null + try { + responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileSTD, + basalProfileFrames) + if (responseMessage!!.commandType === MedtronicCommandType.CommandACK) return true + } catch (e: RileyLinkCommunicationException) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.message, retries + 1)) + } + if (responseMessage != null) aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Set Basal Profile: Invalid response: commandType=%s,rawData=%s", responseMessage.commandType, ByteUtil.shortHexString(responseMessage.rawContent))) else aapsLogger.warn(LTag.PUMPCOMM, "Set Basal Profile: Null response.") + } + return false + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java deleted file mode 100644 index f9ad49b723..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java +++ /dev/null @@ -1,444 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm; - -import org.joda.time.IllegalFieldValueException; -import org.joda.time.LocalDateTime; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -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.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by andy on 5/9/18. - * High level decoder for data returned through MedtroniUIComm - */ - -@Singleton -public class MedtronicConverter { - - private final AAPSLogger aapsLogger; - private final MedtronicUtil medtronicUtil; - - @Inject - public MedtronicConverter( - AAPSLogger aapsLogger, - MedtronicUtil medtronicUtil - ) { - this.aapsLogger = aapsLogger; - this.medtronicUtil = medtronicUtil; - } - - Object convertResponse(PumpType pumpType, MedtronicCommandType commandType, byte[] rawContent) { - - if ((rawContent == null || rawContent.length < 1) && commandType != MedtronicCommandType.PumpModel) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Content is empty or too short, no data to convert (type=%s,isNull=%b,length=%s)", - commandType.name(), rawContent == null, rawContent == null ? "-" : rawContent.length)); - return null; - } - - aapsLogger.debug(LTag.PUMPCOMM, "Raw response before convert: " + ByteUtil.shortHexString(rawContent)); - - switch (commandType) { - - case PumpModel: { - return decodeModel(rawContent); - } - - case GetRealTimeClock: { - return decodeTime(rawContent); - } - - case GetRemainingInsulin: { - return decodeRemainingInsulin(rawContent); - } - - case GetBatteryStatus: { - return decodeBatteryStatus(rawContent); // 1 - } - - case GetBasalProfileSTD: - case GetBasalProfileA: - case GetBasalProfileB: { - return decodeBasalProfile(pumpType, rawContent); - - } - - case ReadTemporaryBasal: { - return new TempBasalPair(aapsLogger, rawContent); // 5 - } - - case Settings_512: { - return decodeSettingsLoop(rawContent); - } - - case Settings: { - return decodeSettingsLoop(rawContent); - } - - case SetBolus: { - return rawContent; // 1 - } - - default: { - throw new RuntimeException("Unsupported command Type: " + commandType); - } - - } - - } - - - private BasalProfile decodeBasalProfile(PumpType pumpType, byte[] rawContent) { - - BasalProfile basalProfile = new BasalProfile(aapsLogger, rawContent); - - return basalProfile.verify(pumpType) ? basalProfile : null; - } - - - private MedtronicDeviceType decodeModel(byte[] rawContent) { - - if ((rawContent == null || rawContent.length < 4)) { - aapsLogger.warn(LTag.PUMPCOMM, "Error reading PumpModel, returning Unknown_Device"); - return MedtronicDeviceType.Unknown_Device; - } - - String rawModel = StringUtil.fromBytes(ByteUtil.substring(rawContent, 1, 3)); - MedtronicDeviceType pumpModel = MedtronicDeviceType.getByDescription(rawModel); - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "PumpModel: [raw=%s, resolved=%s]", rawModel, pumpModel.name())); - - if (pumpModel != MedtronicDeviceType.Unknown_Device) { - if (!medtronicUtil.isModelSet()) { - medtronicUtil.setMedtronicPumpModel(pumpModel); - } - } - - return pumpModel; - } - - - private BatteryStatusDTO decodeBatteryStatus(byte[] rawData) { - // 00 7C 00 00 - - BatteryStatusDTO batteryStatus = new BatteryStatusDTO(); - - int status = rawData[0]; - - if (status == 0) { - batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Normal; - } else if (status == 1) { - batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Low; - } else if (status == 2) { - batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Unknown; - } - - if (rawData.length > 1) { - - Double d = null; - - // if response in 3 bytes then we add additional information - if (rawData.length == 2) { - d = (rawData[1] * 1.0d) / 100.0d; - } else { - d = (ByteUtil.toInt(rawData[1], rawData[2]) * 1.0d) / 100.0d; - } - - batteryStatus.voltage = d; - batteryStatus.extendedDataReceived = true; - } - - return batteryStatus; - } - - - private Float decodeRemainingInsulin(byte[] rawData) { - int startIdx = 0; - - MedtronicDeviceType pumpModel = medtronicUtil.getMedtronicPumpModel(); - - int strokes = pumpModel == null ? 10 : pumpModel.getBolusStrokes(); - - if (strokes == 40) { - startIdx = 2; - } - - int reqLength = startIdx + 1; - float value = 0; - - if (reqLength >= rawData.length) { - value = rawData[startIdx] / (1.0f * strokes); - } else { - value = ByteUtil.toInt(rawData[startIdx], rawData[startIdx + 1]) / (1.0f * strokes); - } - - aapsLogger.debug(LTag.PUMPCOMM, "Remaining insulin: " + value); - return value; - } - - - private LocalDateTime decodeTime(byte[] rawContent) { - - int hours = ByteUtil.asUINT8(rawContent[0]); - int minutes = ByteUtil.asUINT8(rawContent[1]); - int seconds = ByteUtil.asUINT8(rawContent[2]); - int year = (ByteUtil.asUINT8(rawContent[4]) & 0x3f) + 1984; - int month = ByteUtil.asUINT8(rawContent[5]); - int day = ByteUtil.asUINT8(rawContent[6]); - try { - LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds); - return pumpTime; - } catch (IllegalFieldValueException e) { - aapsLogger.error(LTag.PUMPCOMM, - String.format(Locale.ENGLISH, "decodeTime: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d", - year, month, day, hours, minutes, seconds)); - return null; - } - - } - - - private Map decodeSettingsLoop(byte[] rd) { - - Map map = new HashMap<>(); - - addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map); - addSettingToMap( - "PCFG_MAX_BASAL", - "" - + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()], - rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map); - addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h", - PumpConfigurationGroup.General, map); - - addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map); - - if (rd[10] == 1) { - String patt; - switch (rd[11]) { - case 0: - patt = "STD"; - break; - - case 1: - patt = "A"; - break; - - case 2: - patt = "B"; - break; - - default: - patt = "???"; - break; - } - - addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map); - - } else { - addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", "STD", PumpConfigurationGroup.Basal, map); - } - - addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map); - - return map; - } - - - private Map decodeSettings512(byte[] rd) { - - Map map = new HashMap<>(); - - addSettingToMap("PCFG_AUTOOFF_TIMEOUT", "" + rd[0], PumpConfigurationGroup.General, map); - - if (rd[1] == 4) { - addSettingToMap("PCFG_ALARM_MODE", "Silent", PumpConfigurationGroup.Sound, map); - } else { - addSettingToMap("PCFG_ALARM_MODE", "Normal", PumpConfigurationGroup.Sound, map); - addSettingToMap("PCFG_ALARM_BEEP_VOLUME", "" + rd[1], PumpConfigurationGroup.Sound, map); - } - - addSettingToMap("PCFG_AUDIO_BOLUS_ENABLED", parseResultEnable(rd[2]), PumpConfigurationGroup.Bolus, map); - - if (rd[2] == 1) { - addSettingToMap("PCFG_AUDIO_BOLUS_STEP_SIZE", "" + decodeBolusInsulin(ByteUtil.asUINT8(rd[3])), - PumpConfigurationGroup.Bolus, map); - } - - addSettingToMap("PCFG_VARIABLE_BOLUS_ENABLED", parseResultEnable(rd[4]), PumpConfigurationGroup.Bolus, map); - addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map); - addSettingToMap( - "PCFG_MAX_BASAL", - "" - + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()], - rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map); - addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h", - PumpConfigurationGroup.General, map); - - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) { - addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] == 0 ? 50 : 100), PumpConfigurationGroup.Insulin, - map); -// LOG.debug("Insulin concentration: " + rd[9]); - } else { - addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] != 0 ? 50 : 100), PumpConfigurationGroup.Insulin, - map); -// LOG.debug("Insulin concentration: " + rd[9]); - } - addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map); - - if (rd[10] == 1) { - String patt; - switch (rd[11]) { - case 0: - patt = "STD"; - break; - - case 1: - patt = "A"; - break; - - case 2: - patt = "B"; - break; - - default: - patt = "???"; - break; - } - - addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map); - - } - - addSettingToMap("CFG_MM_RF_ENABLED", parseResultEnable(rd[12]), PumpConfigurationGroup.General, map); - addSettingToMap("CFG_MM_BLOCK_ENABLED", parseResultEnable(rd[13]), PumpConfigurationGroup.General, map); - - addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map); - - if (rd[14] == 1) { - addSettingToMap("PCFG_TEMP_BASAL_PERCENT", "" + rd[15], PumpConfigurationGroup.Basal, map); - } - - addSettingToMap("CFG_PARADIGM_LINK_ENABLE", parseResultEnable(rd[16]), PumpConfigurationGroup.General, map); - - decodeInsulinActionSetting(rd, map); - - return map; - } - - - private void addSettingToMap(String key, String value, PumpConfigurationGroup group, Map map) { - map.put(key, new PumpSettingDTO(key, value, group)); - } - - - public Map decodeSettings(byte[] rd) { - Map map = decodeSettings512(rd); - - addSettingToMap("PCFG_MM_RESERVOIR_WARNING_TYPE_TIME", rd[18] != 0 ? "PCFG_MM_RESERVOIR_WARNING_TYPE_TIME" - : "PCFG_MM_RESERVOIR_WARNING_TYPE_UNITS", PumpConfigurationGroup.Other, map); - - addSettingToMap("PCFG_MM_SRESERVOIR_WARNING_POINT", "" + ByteUtil.asUINT8(rd[19]), - PumpConfigurationGroup.Other, map); - - addSettingToMap("CFG_MM_KEYPAD_LOCKED", parseResultEnable(rd[20]), PumpConfigurationGroup.Other, map); - - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) { - - addSettingToMap("PCFG_BOLUS_SCROLL_STEP_SIZE", "" + rd[21], PumpConfigurationGroup.Bolus, map); - addSettingToMap("PCFG_CAPTURE_EVENT_ENABLE", parseResultEnable(rd[22]), PumpConfigurationGroup.Other, map); - addSettingToMap("PCFG_OTHER_DEVICE_ENABLE", parseResultEnable(rd[23]), PumpConfigurationGroup.Other, map); - addSettingToMap("PCFG_OTHER_DEVICE_PAIRED_STATE", parseResultEnable(rd[24]), PumpConfigurationGroup.Other, - map); - } - - return map; - } - - - private String parseResultEnable(int i) { - switch (i) { - case 0: - return "No"; - case 1: - return "Yes"; - default: - return "???"; - } - } - - - private float getStrokesPerUnit(boolean isBasal) { - return isBasal ? 40.0f : 10; // pumpModel.getBolusStrokes(); - } - - - // 512 - private void decodeInsulinActionSetting(byte[] ai, Map map) { - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_512_712)) { - addSettingToMap("PCFG_INSULIN_ACTION_TYPE", (ai[17] != 0 ? "Regular" : "Fast"), - PumpConfigurationGroup.Insulin, map); - } else { - int i = ai[17]; - String s; - - if ((i == 0) || (i == 1)) { - s = ai[17] != 0 ? "Regular" : "Fast"; - } else { - if (i == 15) - s = "Unset"; - else - s = "Curve: " + i; - } - - addSettingToMap("PCFG_INSULIN_ACTION_TYPE", s, PumpConfigurationGroup.Insulin, map); - } - } - - - private double decodeBasalInsulin(int i) { - return (double) i / (double) getStrokesPerUnit(true); - } - - - private double decodeBolusInsulin(int i) { - - return (double) i / (double) getStrokesPerUnit(false); - } - - - private int getSettingIndexMaxBasal() { - return is523orHigher() ? 7 : 6; - } - - - private int getSettingIndexTimeDisplayFormat() { - return is523orHigher() ? 9 : 8; - } - - - private double decodeMaxBolus(byte[] ai) { - return is523orHigher() ? decodeBolusInsulin(ByteUtil.toInt(ai[5], ai[6])) : decodeBolusInsulin(ByteUtil - .asUINT8(ai[5])); - } - - - private boolean is523orHigher() { - return (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.kt new file mode 100644 index 0000000000..07da3aceaa --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.kt @@ -0,0 +1,265 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +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.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import org.joda.time.IllegalFieldValueException +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 5/9/18. + * High level decoder for data returned through MedtroniUIComm + */ +@Singleton +class MedtronicConverter @Inject constructor( + private val aapsLogger: AAPSLogger, + private val medtronicUtil: MedtronicUtil +) { + + fun decodeBasalProfile(pumpType: PumpType, rawContent: ByteArray): BasalProfile? { + val basalProfile = BasalProfile(aapsLogger, rawContent) + return if (basalProfile.verify(pumpType)) basalProfile else null + } + + fun decodeModel(rawContent: ByteArray): MedtronicDeviceType { + if (rawContent.size < 4) { + aapsLogger.warn(LTag.PUMPCOMM, "Error reading PumpModel, returning Unknown_Device") + return MedtronicDeviceType.Unknown_Device + } + val rawModel = StringUtil.fromBytes(ByteUtil.substring(rawContent, 1, 3)) + val pumpModel = MedtronicDeviceType.getByDescription(rawModel) + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "PumpModel: [raw=%s, resolved=%s]", rawModel, pumpModel.name)) + if (pumpModel != MedtronicDeviceType.Unknown_Device) { + if (!medtronicUtil.isModelSet) { + medtronicUtil.medtronicPumpModel = pumpModel + medtronicUtil.isModelSet = true + } + } + return pumpModel + } + + fun decodeBatteryStatus(rawData: ByteArray): BatteryStatusDTO { + // 00 7C 00 00 + val batteryStatus = BatteryStatusDTO() + val status = rawData[0].toInt() + if (status == 0) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Normal + } else if (status == 1) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Low + } else if (status == 2) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Unknown + } + if (rawData.size > 1) { + var d: Double? //= null + + // if response in 3 bytes then we add additional information + d = if (rawData.size == 2) { + rawData[1] * 1.0 / 100.0 + } else { + ByteUtil.toInt(rawData[1], rawData[2]) * 1.0 / 100.0 + } + batteryStatus.voltage = d + batteryStatus.extendedDataReceived = true + } + return batteryStatus + } + + fun decodeRemainingInsulin(rawData: ByteArray): Double { + var startIdx = 0 + val pumpModel = medtronicUtil.medtronicPumpModel + val strokes = pumpModel.bolusStrokes //?: 10 + if (strokes == 40) { + startIdx = 2 + } + val reqLength = startIdx + 1 + val value: Double + value = if (reqLength >= rawData.size) { + rawData[startIdx] / (1.0 * strokes) + } else { + ByteUtil.toInt(rawData[startIdx], rawData[startIdx + 1]) / (1.0 * strokes) + } + aapsLogger.debug(LTag.PUMPCOMM, "Remaining insulin: $value") + return value + } + + fun decodeTime(rawContent: ByteArray): LocalDateTime? { + val hours = ByteUtil.asUINT8(rawContent[0]) + val minutes = ByteUtil.asUINT8(rawContent[1]) + val seconds = ByteUtil.asUINT8(rawContent[2]) + val year = (ByteUtil.asUINT8(rawContent[4]) and 0x3f) + 1984 + val month = ByteUtil.asUINT8(rawContent[5]) + val day = ByteUtil.asUINT8(rawContent[6]) + return try { + LocalDateTime(year, month, day, hours, minutes, seconds) + } catch (e: IllegalFieldValueException) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "decodeTime: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d", + year, month, day, hours, minutes, seconds)) + null + } + } + + public fun decodeSettingsLoop(rd: ByteArray): Map { + val map: MutableMap = HashMap() + addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map) + addSettingToMap( + "PCFG_MAX_BASAL", "" + + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[settingIndexMaxBasal].toInt(), + rd[settingIndexMaxBasal + 1].toInt())), PumpConfigurationGroup.Basal, map) + addSettingToMap("CFG_BASE_CLOCK_MODE", if (rd[settingIndexTimeDisplayFormat].toInt() == 0) "12h" else "24h", + PumpConfigurationGroup.General, map) + addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10].toInt()), PumpConfigurationGroup.Basal, map) + if (rd[10].toInt() == 1) { + val patt: String + patt = when (rd[11].toInt()) { + 0 -> "STD" + 1 -> "A" + 2 -> "B" + else -> "???" + } + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map) + } else { + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", "STD", PumpConfigurationGroup.Basal, map) + } + addSettingToMap("PCFG_TEMP_BASAL_TYPE", if (rd[14].toInt() != 0) "Percent" else "Units", PumpConfigurationGroup.Basal, map) + return map + } + + private fun decodeSettings512(rd: ByteArray): MutableMap { + val map: MutableMap = HashMap() + addSettingToMap("PCFG_AUTOOFF_TIMEOUT", "" + rd[0], PumpConfigurationGroup.General, map) + if (rd[1].toInt() == 4) { + addSettingToMap("PCFG_ALARM_MODE", "Silent", PumpConfigurationGroup.Sound, map) + } else { + addSettingToMap("PCFG_ALARM_MODE", "Normal", PumpConfigurationGroup.Sound, map) + addSettingToMap("PCFG_ALARM_BEEP_VOLUME", "" + rd[1], PumpConfigurationGroup.Sound, map) + } + addSettingToMap("PCFG_AUDIO_BOLUS_ENABLED", parseResultEnable(rd[2].toInt()), PumpConfigurationGroup.Bolus, map) + if (rd[2].toInt() == 1) { + addSettingToMap("PCFG_AUDIO_BOLUS_STEP_SIZE", "" + decodeBolusInsulin(ByteUtil.asUINT8(rd[3])), + PumpConfigurationGroup.Bolus, map) + } + addSettingToMap("PCFG_VARIABLE_BOLUS_ENABLED", parseResultEnable(rd[4].toInt()), PumpConfigurationGroup.Bolus, map) + addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map) + addSettingToMap( + "PCFG_MAX_BASAL", "" + + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[settingIndexMaxBasal].toInt(), + rd[settingIndexMaxBasal + 1].toInt())), PumpConfigurationGroup.Basal, map) + addSettingToMap("CFG_BASE_CLOCK_MODE", if (rd[settingIndexTimeDisplayFormat].toInt() == 0) "12h" else "24h", + PumpConfigurationGroup.General, map) + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + if (rd[9].toInt() == 0) 50 else 100, PumpConfigurationGroup.Insulin, + map) + // LOG.debug("Insulin concentration: " + rd[9]); + } else { + addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + if (rd[9].toInt() != 0) 50 else 100, PumpConfigurationGroup.Insulin, + map) + // LOG.debug("Insulin concentration: " + rd[9]); + } + addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10].toInt()), PumpConfigurationGroup.Basal, map) + if (rd[10].toInt() == 1) { + val patt: String + patt = when (rd[11].toInt()) { + 0 -> "STD" + 1 -> "A" + 2 -> "B" + else -> "???" + } + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map) + } + addSettingToMap("CFG_MM_RF_ENABLED", parseResultEnable(rd[12].toInt()), PumpConfigurationGroup.General, map) + addSettingToMap("CFG_MM_BLOCK_ENABLED", parseResultEnable(rd[13].toInt()), PumpConfigurationGroup.General, map) + addSettingToMap("PCFG_TEMP_BASAL_TYPE", if (rd[14].toInt() != 0) "Percent" else "Units", PumpConfigurationGroup.Basal, map) + if (rd[14].toInt() == 1) { + addSettingToMap("PCFG_TEMP_BASAL_PERCENT", "" + rd[15], PumpConfigurationGroup.Basal, map) + } + addSettingToMap("CFG_PARADIGM_LINK_ENABLE", parseResultEnable(rd[16].toInt()), PumpConfigurationGroup.General, map) + decodeInsulinActionSetting(rd, map) + return map + } + + private fun addSettingToMap(key: String, value: String, group: PumpConfigurationGroup, map: MutableMap) { + map[key] = PumpSettingDTO(key, value, group) + } + + fun decodeSettings(rd: ByteArray): Map { + val map = decodeSettings512(rd) + addSettingToMap("PCFG_MM_RESERVOIR_WARNING_TYPE_TIME", if (rd[18].toInt() != 0) "PCFG_MM_RESERVOIR_WARNING_TYPE_TIME" else "PCFG_MM_RESERVOIR_WARNING_TYPE_UNITS", PumpConfigurationGroup.Other, map) + addSettingToMap("PCFG_MM_SRESERVOIR_WARNING_POINT", "" + ByteUtil.asUINT8(rd[19]), + PumpConfigurationGroup.Other, map) + addSettingToMap("CFG_MM_KEYPAD_LOCKED", parseResultEnable(rd[20].toInt()), PumpConfigurationGroup.Other, map) + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + addSettingToMap("PCFG_BOLUS_SCROLL_STEP_SIZE", "" + rd[21], PumpConfigurationGroup.Bolus, map) + addSettingToMap("PCFG_CAPTURE_EVENT_ENABLE", parseResultEnable(rd[22].toInt()), PumpConfigurationGroup.Other, map) + addSettingToMap("PCFG_OTHER_DEVICE_ENABLE", parseResultEnable(rd[23].toInt()), PumpConfigurationGroup.Other, map) + addSettingToMap("PCFG_OTHER_DEVICE_PAIRED_STATE", parseResultEnable(rd[24].toInt()), PumpConfigurationGroup.Other, + map) + } + return map + } + + private fun parseResultEnable(i: Int): String { + return when (i) { + 0 -> "No" + 1 -> "Yes" + else -> "???" + } + } + + private fun getStrokesPerUnit(isBasal: Boolean): Float { + return if (isBasal) 40.0f else 10.0f // pumpModel.getBolusStrokes(); + } + + // 512 + private fun decodeInsulinActionSetting(ai: ByteArray, map: MutableMap) { + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_512_712)) { + addSettingToMap("PCFG_INSULIN_ACTION_TYPE", if (ai[17].toInt() != 0) "Regular" else "Fast", + PumpConfigurationGroup.Insulin, map) + } else { + val i = ai[17].toInt() + val s: String + s = if (i == 0 || i == 1) { + if (ai[17].toInt() != 0) "Regular" else "Fast" + } else { + if (i == 15) "Unset" else "Curve: $i" + } + addSettingToMap("PCFG_INSULIN_ACTION_TYPE", s, PumpConfigurationGroup.Insulin, map) + } + } + + private fun decodeBasalInsulin(i: Int): Double { + return i.toDouble() / getStrokesPerUnit(true).toDouble() + } + + private fun decodeBolusInsulin(i: Int): Double { + return i.toDouble() / getStrokesPerUnit(false).toDouble() + } + + private val settingIndexMaxBasal: Int + get() = if (is523orHigher()) 7 else 6 + + private val settingIndexTimeDisplayFormat: Int + get() = if (is523orHigher()) 9 else 8 + + private fun decodeMaxBolus(ai: ByteArray): Double { + return if (is523orHigher()) + decodeBolusInsulin(ByteUtil.toInt(ai[5], ai[6])) + else + decodeBolusInsulin(ByteUtil.asUINT8(ai[5])) + } + + private fun is523orHigher(): Boolean { + return MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher) + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java deleted file mode 100644 index 2b33249503..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java +++ /dev/null @@ -1,182 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; - -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.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public abstract class MedtronicHistoryDecoder implements MedtronicHistoryDecoderInterface { - - @Inject protected AAPSLogger aapsLogger; - @Inject protected MedtronicUtil medtronicUtil; - - protected ByteUtil bitUtils; - - // STATISTICS (remove at later time or not) - protected boolean statisticsEnabled = true; - protected Map unknownOpCodes; - protected Map> mapStatistics; - - - public MedtronicHistoryDecoder() { - } - - - // public abstract Class getHistoryEntryClass(); - - // public abstract RecordDecodeStatus decodeRecord(T record); - - public abstract void postProcess(); - - - protected abstract void runPostDecodeTasks(); - - - // TODO_ extend this to also use bigger pages (for now we support only 1024 pages) - private List checkPage(RawHistoryPage page, boolean partial) throws RuntimeException { - List byteList = new ArrayList(); - - // if (!partial && page.getData().length != 1024 /* page.commandType.getRecordLength() */) { - // LOG.error("Page size is not correct. Size should be {}, but it was {} instead.", 1024, - // page.getData().length); - // // throw exception perhaps - // return byteList; - // } - - if (medtronicUtil.getMedtronicPumpModel() == null) { - aapsLogger.error(LTag.PUMPCOMM, "Device Type is not defined."); - return byteList; - } - - if (page.getData().length != 1024) { - return ByteUtil.getListFromByteArray(page.getData()); - } else if (page.isChecksumOK()) { - return ByteUtil.getListFromByteArray(page.getOnlyData()); - } else { - return null; - } - } - - - public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage) { - return processPageAndCreateRecords(rawHistoryPage, false); - } - - - protected void prepareStatistics() { - if (!statisticsEnabled) - return; - - unknownOpCodes = new HashMap<>(); - mapStatistics = new HashMap<>(); - - for (RecordDecodeStatus stat : RecordDecodeStatus.values()) { - mapStatistics.put(stat, new HashMap<>()); - } - } - - - protected void addToStatistics(MedtronicHistoryEntryInterface pumpHistoryEntry, RecordDecodeStatus status, Integer opCode) { - if (!statisticsEnabled) - return; - - if (opCode != null) { - if (!unknownOpCodes.containsKey(opCode)) { - unknownOpCodes.put(opCode, opCode); - } - return; - } - - if (!mapStatistics.get(status).containsKey(pumpHistoryEntry.getEntryTypeName())) { - mapStatistics.get(status).put(pumpHistoryEntry.getEntryTypeName(), ""); - } - } - - - protected void showStatistics() { - StringBuilder sb = new StringBuilder(); - - for (Map.Entry unknownEntry : unknownOpCodes.entrySet()) { - StringUtil.appendToStringBuilder(sb, "" + unknownEntry.getKey(), ", "); - } - - aapsLogger.info(LTag.PUMPCOMM, "STATISTICS OF PUMP DECODE"); - - if (unknownOpCodes.size() > 0) { - aapsLogger.warn(LTag.PUMPCOMM, "Unknown Op Codes: " + sb.toString()); - } - - for (Map.Entry> entry : mapStatistics.entrySet()) { - sb = new StringBuilder(); - - if (entry.getKey() != RecordDecodeStatus.OK) { - if (entry.getValue().size() == 0) - continue; - - for (Map.Entry entrysub : entry.getValue().entrySet()) { - StringUtil.appendToStringBuilder(sb, entrysub.getKey(), ", "); - } - - String spaces = StringUtils.repeat(" ", 14 - entry.getKey().name().length()); - - aapsLogger.info(LTag.PUMPCOMM, String.format(Locale.ENGLISH, " %s%s - %d. Elements: %s", entry.getKey().name(), spaces, entry.getValue().size(), sb.toString())); - } else { - aapsLogger.info(LTag.PUMPCOMM, String.format(Locale.ENGLISH, " %s - %d", entry.getKey().name(), entry.getValue().size())); - } - } - } - - - private int getUnsignedByte(byte value) { - if (value < 0) - return value + 256; - else - return value; - } - - - protected int getUnsignedInt(int value) { - if (value < 0) - return value + 256; - else - return value; - } - - - public String getFormattedFloat(float value, int decimals) { - return StringUtil.getFormatedValueUS(value, decimals); - } - - - private List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, boolean partial) { - List dataClear = checkPage(rawHistoryPage, partial); - List records = createRecords(dataClear); - - for (T record : records) { - decodeRecord(record); - } - - runPostDecodeTasks(); - - return records; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.kt new file mode 100644 index 0000000000..c834e92711 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.kt @@ -0,0 +1,118 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +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.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import org.apache.commons.lang3.StringUtils + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +abstract class MedtronicHistoryDecoder(var aapsLogger: AAPSLogger, + var medtronicUtil: MedtronicUtil, + var bitUtils: ByteUtil) : MedtronicHistoryDecoderInterface { + + // STATISTICS (remove at later time or not) + protected var statisticsEnabled = true + protected var unknownOpCodes: MutableMap = mutableMapOf() + protected var mapStatistics: MutableMap> = mutableMapOf() + + abstract fun postProcess() + protected abstract fun runPostDecodeTasks() + + // TODO_ extend this to also use bigger pages (for now we support only 1024 pages) + @Throws(RuntimeException::class) + private fun checkPage(page: RawHistoryPage): MutableList { + if (!medtronicUtil.isModelSet) { + aapsLogger.error(LTag.PUMPCOMM, "Device Type is not defined.") + return mutableListOf() + } + return if (page.data.size != 1024) { + page.data.toMutableList() + } else if (page.isChecksumOK) { + page.onlyData.toMutableList() + } else { + mutableListOf() + } + } + + fun processPageAndCreateRecords(rawHistoryPage: RawHistoryPage): MutableList { + val dataClear = checkPage(rawHistoryPage) + val records: MutableList = createRecords(dataClear) + for (record in records) { + decodeRecord(record) + } + runPostDecodeTasks() + return records + } + + protected fun prepareStatistics() { + if (!statisticsEnabled) return + // unknownOpCodes = HashMap() + // mapStatistics = HashMap() + for (stat in RecordDecodeStatus.values()) { + mapStatistics[stat] = hashMapOf() + //(mapStatistics as HashMap>)[stat] = hashMapOf() + } + } + + protected fun addToStatistics(pumpHistoryEntry: MedtronicHistoryEntryInterface, status: RecordDecodeStatus, opCode: Int?) { + if (!statisticsEnabled) return + if (opCode != null) { + if (!unknownOpCodes.containsKey(opCode)) { + unknownOpCodes[opCode] = opCode + } + return + } + if (!mapStatistics[status]!!.containsKey(pumpHistoryEntry.entryTypeName)) { + mapStatistics[status]!!.put(pumpHistoryEntry.entryTypeName, "") + } + } + + protected fun showStatistics() { + var sb = StringBuilder() + for ((key) in unknownOpCodes) { + StringUtil.appendToStringBuilder(sb, "" + key, ", ") + } + aapsLogger.info(LTag.PUMPCOMM, "STATISTICS OF PUMP DECODE") + if (unknownOpCodes.size > 0) { + aapsLogger.warn(LTag.PUMPCOMM, "Unknown Op Codes: $sb") + } + for ((key, value) in mapStatistics) { + sb = StringBuilder() + if (key !== RecordDecodeStatus.OK) { + if (value.size == 0) continue + for ((key1) in value) { + StringUtil.appendToStringBuilder(sb, key1, ", ") + } + val spaces = StringUtils.repeat(" ", 14 - key.name.length) + aapsLogger.info(LTag.PUMPCOMM, " ${key.name}$spaces - ${value.size}. Elements: ${sb.toString()}") + } else { + aapsLogger.info(LTag.PUMPCOMM, " ${key.name} - ${value.size}") + } + } + } + + private fun getUnsignedByte(value: Byte): Int { + return if (value < 0) value + 256 else value.toInt() + } + + protected fun getUnsignedInt(value: Int): Int { + return if (value < 0) value + 256 else value + } + + protected fun getUnsignedInt(value: Byte): Int { + return if (value < 0) value + 256 else value.toInt() + } + + fun getFormattedFloat(value: Float, decimals: Int): String { + return StringUtil.getFormatedValueUS(value, decimals) + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java deleted file mode 100644 index b98b2d7d33..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java +++ /dev/null @@ -1,15 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -import java.util.List; - -/** - * Created by andy on 3/10/19. - */ - -public interface MedtronicHistoryDecoderInterface { - - RecordDecodeStatus decodeRecord(T record); - - List createRecords(List dataClear); - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.kt new file mode 100644 index 0000000000..fa45bf1400 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +/** + * Created by andy on 3/10/19. + */ +interface MedtronicHistoryDecoderInterface { + + fun decodeRecord(record: T): RecordDecodeStatus? + fun createRecords(dataClearInput: MutableList): MutableList +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java deleted file mode 100644 index b5d41f6bfa..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java +++ /dev/null @@ -1,316 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -import com.google.gson.annotations.Expose; - -import org.slf4j.Logger; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public abstract class MedtronicHistoryEntry implements MedtronicHistoryEntryInterface { - - protected List rawData; - - public static final Logger LOG = StacktraceLoggerWrapper.getLogger(MedtronicHistoryEntry.class); - - protected int[] sizes = new int[3]; - - protected byte[] head; - protected byte[] datetime; - protected byte[] body; - - // protected LocalDateTime dateTime; - - public long id; - - @Expose - public String DT; - - @Expose - public Long atechDateTime; - - @Expose - protected Map decodedData; - - public long phoneDateTime; // time on phone - - /** - * Pump id that will be used with AAPS object (time * 1000 + historyType (max is FF = 255) - */ - protected Long pumpId; - - /** - * if history object is already linked to AAPS object (either Treatment, TempBasal or TDD (tdd's - * are not actually - * linked)) - */ - public boolean linked = false; - - /** - * Linked object, see linked - */ - public Object linkedObject = null; - - - public void setLinkedObject(Object linkedObject) { - this.linked = true; - this.linkedObject = linkedObject; - } - - - public void setData(List listRawData, boolean doNotProcess) { - this.rawData = listRawData; - - // System.out.println("Head: " + sizes[0] + ", dates: " + sizes[1] + - // ", body=" + sizes[2]); - - if (doNotProcess) - return; - - head = new byte[getHeadLength() - 1]; - for (int i = 1; i < (getHeadLength()); i++) { - head[i - 1] = listRawData.get(i); - } - - if (getDateTimeLength() > 0) { - datetime = new byte[getDateTimeLength()]; - - for (int i = getHeadLength(), j = 0; j < getDateTimeLength(); i++, j++) { - datetime[j] = listRawData.get(i); - } - } - - if (getBodyLength() > 0) { - body = new byte[getBodyLength()]; - - for (int i = (getHeadLength() + getDateTimeLength()), j = 0; j < getBodyLength(); i++, j++) { - body[j] = listRawData.get(i); - } - - } - - } - - - public String getDateTimeString() { - return this.DT == null ? "Unknown" : this.DT; - } - - - public String getDecodedDataAsString() { - if (decodedData == null) - if (isNoDataEntry()) - return "No data"; - else - return ""; - else - return decodedData.toString(); - } - - - public boolean hasData() { - return (decodedData != null) || (isNoDataEntry()) || getEntryTypeName().equals("UnabsorbedInsulin"); - } - - - public boolean isNoDataEntry() { - return (sizes[0] == 2 && sizes[1] == 5 && sizes[2] == 0); - } - - - public Map getDecodedData() { - return this.decodedData; - } - - - public Object getDecodedDataEntry(String key) { - return this.decodedData != null ? this.decodedData.get(key) : null; - } - - - public boolean hasDecodedDataEntry(String key) { - return this.decodedData.containsKey(key); - } - - - public boolean showRaw() { - return getEntryTypeName().equals("EndResultTotals"); - } - - - public int getHeadLength() { - return sizes[0]; - } - - - public int getDateTimeLength() { - return sizes[1]; - } - - - public int getBodyLength() { - return sizes[2]; - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - if (this.DT == null) { - LOG.error("DT is null. RawData=" + ByteUtil.getHex(this.rawData)); - } - - sb.append(getToStringStart()); - sb.append(", DT: " + (this.DT == null ? "null" : StringUtil.getStringInLength(this.DT, 19))); - sb.append(", length="); - sb.append(getHeadLength()); - sb.append(","); - sb.append(getDateTimeLength()); - sb.append(","); - sb.append(getBodyLength()); - sb.append("("); - sb.append((getHeadLength() + getDateTimeLength() + getBodyLength())); - sb.append(")"); - - boolean hasData = hasData(); - - if (hasData) { - sb.append(", data=" + getDecodedDataAsString()); - } - - if (hasData && !showRaw()) { - sb.append("]"); - return sb.toString(); - } - - if (head != null) { - sb.append(", head="); - sb.append(ByteUtil.shortHexString(this.head)); - } - - if (datetime != null) { - sb.append(", datetime="); - sb.append(ByteUtil.shortHexString(this.datetime)); - } - - if (body != null) { - sb.append(", body="); - sb.append(ByteUtil.shortHexString(this.body)); - } - - sb.append(", rawData="); - sb.append(ByteUtil.shortHexString(this.rawData)); - sb.append("]"); - - // sb.append(" DT: "); - // sb.append(this.dateTime == null ? " - " : this.dateTime.toString("dd.MM.yyyy HH:mm:ss")); - - // sb.append(" Ext: "); - - return sb.toString(); - } - - - public abstract int getOpCode(); - - - public abstract String getToStringStart(); - - - public List getRawData() { - return rawData; - } - - - public byte getRawDataByIndex(int index) { - return rawData.get(index); - } - - - public int getUnsignedRawDataByIndex(int index) { - return ByteUtil.convertUnsignedByteToInt(rawData.get(index)); - } - - - public void setRawData(List rawData) { - this.rawData = rawData; - } - - - public byte[] getHead() { - return head; - } - - - public void setHead(byte[] head) { - this.head = head; - } - - - public byte[] getDatetime() { - return datetime; - } - - - public void setDatetime(byte[] datetime) { - this.datetime = datetime; - } - - - public byte[] getBody() { - return body; - } - - - public void setBody(byte[] body) { - this.body = body; - } - - - public void setAtechDateTime(long dt) { - this.atechDateTime = dt; - this.DT = DateTimeUtil.toString(this.atechDateTime); - } - - - public void addDecodedData(String key, Object value) { - if (decodedData == null) - decodedData = new HashMap<>(); - - decodedData.put(key, value); - } - - - public String toShortString() { - if (head == null) { - return "Unidentified record. "; - } else { - return "HistoryRecord: head=[" + ByteUtil.shortHexString(this.head) + "]"; - } - } - - public boolean containsDecodedData(String key) { - if (decodedData == null) - return false; - - return decodedData.containsKey(key); - } - - // if we extend to CGMS this need to be changed back - // public abstract PumpHistoryEntryType getEntryType(); - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.kt new file mode 100644 index 0000000000..d9baa82acc --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.kt @@ -0,0 +1,223 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +abstract class MedtronicHistoryEntry : MedtronicHistoryEntryInterface { + + lateinit var rawData: List + + protected var sizes = IntArray(3) + get() = field + + lateinit var head: ByteArray + lateinit var datetime: ByteArray + lateinit var body: ByteArray + + var id: Long = 0 + set(value) { + field = value + } + + @Expose + var DT: String? = null + get() = field + + @Expose + var atechDateTime: Long = 0L + get() = field + set(value) { + field = value + DT = DateTimeUtil.toString(value) + if (isEntryTypeSet() && value != 0L) pumpId = generatePumpId() + } + + @Expose + var decodedData: MutableMap = mutableMapOf() + get() = field + + /** + * Pump id that will be used with AAPS object (time * 1000 + historyType (max is FF = 255) + */ + open var pumpId: Long = 0L + + /** + * if history object is already linked to AAPS object (either Treatment, TempBasal or TDD (tdd's + * are not actually + * linked)) + */ + var linked = false + + /** + * Linked object, see linked + */ + var linkedObject: Any? = null + get() = field + set(value) { + linked = true + field = value + } + + abstract fun generatePumpId(): Long + + abstract fun isEntryTypeSet(): Boolean + + override fun setData(listRawData: MutableList, doNotProcess: Boolean) { + rawData = listRawData + + // System.out.println("Head: " + sizes[0] + ", dates: " + sizes[1] + + // ", body=" + sizes[2]); + if (!doNotProcess) { + head = ByteArray(headLength - 1) + for (i in 1 until headLength) { + head[i - 1] = listRawData[i] + } + if (dateTimeLength > 0) { + datetime = ByteArray(dateTimeLength) + var i = headLength + var j = 0 + while (j < dateTimeLength) { + datetime[j] = listRawData[i] + i++ + j++ + } + } else + datetime = byteArrayOf() + + if (bodyLength > 0) { + body = ByteArray(bodyLength) + var i = headLength + dateTimeLength + var j = 0 + while (j < bodyLength) { + body[j] = listRawData[i] + i++ + j++ + } + } else + body = byteArrayOf() + } + return + } + + val dateTimeString: String + get() = if (DT == null) "Unknown" else DT!! + + val decodedDataAsString: String + get() = if (decodedData.size == 0) if (isNoDataEntry) "No data" else "" else decodedData.toString() + + fun hasData(): Boolean { + return decodedData.size == 0 || isNoDataEntry || entryTypeName == "UnabsorbedInsulin" + } + + val isNoDataEntry: Boolean + get() = sizes[0] == 2 && sizes[1] == 5 && sizes[2] == 0 + + fun getDecodedDataEntry(key: String?): Any? { + return if (decodedData.containsKey(key)) decodedData[key] else null + } + + fun hasDecodedDataEntry(key: String?): Boolean { + return decodedData.containsKey(key) + } + + fun showRaw(): Boolean { + return entryTypeName == "EndResultTotals" + } + + val headLength: Int + get() = sizes[0] + + val dateTimeLength: Int + get() = sizes[1] + + val bodyLength: Int + get() = sizes[2] + + override fun toString(): String { + val sb = StringBuilder() + // if (DT == null) { + // Log.e("", "DT is null. RawData=" + ByteUtil.getHex(rawData)) + // } + sb.append(toStringStart) + sb.append(", DT: " + if (DT == null) "null" else StringUtil.getStringInLength(DT, 19)) + sb.append(", length=") + sb.append(headLength) + sb.append(",") + sb.append(dateTimeLength) + sb.append(",") + sb.append(bodyLength) + sb.append("(") + sb.append(headLength + dateTimeLength + bodyLength) + sb.append(")") + val hasData = hasData() + if (hasData) { + sb.append(", data=$decodedDataAsString") + } + if (hasData && !showRaw()) { + sb.append("]") + return sb.toString() + } + if (head.size != 0) { + sb.append(", head=") + sb.append(ByteUtil.shortHexString(head)) + } + if (datetime.size != 0) { + sb.append(", datetime=") + sb.append(ByteUtil.shortHexString(datetime)) + } + if (body.size != 0) { + sb.append(", body=") + sb.append(ByteUtil.shortHexString(body)) + } + sb.append(", rawData=") + sb.append(ByteUtil.shortHexString(rawData)) + sb.append("]") + + // sb.append(" DT: "); + // sb.append(this.dateTime == null ? " - " : this.dateTime.toString("dd.MM.yyyy HH:mm:ss")); + + // sb.append(" Ext: "); + return sb.toString() + } + + abstract val opCode: Byte? + abstract val toStringStart: String? + + fun getRawDataByIndex(index: Int): Byte { + return rawData[index] + } + + fun getRawDataByIndexInt(index: Int): Int { + return rawData[index].toInt() + } + + fun getUnsignedRawDataByIndex(index: Int): Int { + return ByteUtil.convertUnsignedByteToInt(rawData[index]) + } + + fun addDecodedData(key: String, value: Any) { + decodedData.put(key, value) + } + + fun toShortString(): String { + return if (head.size != 0) { + "Unidentified record. " + } else { + "HistoryRecord: head=[" + ByteUtil.shortHexString(head) + "]" + } + } + + fun containsDecodedData(key: String?): Boolean { + return decodedData.containsKey(key) + } // if we extend to CGMS this need to be changed back + // public abstract PumpHistoryEntryType getEntryType(); +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java deleted file mode 100644 index 38b7e1dbeb..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -import java.util.List; - -/** - * Created by andy on 7/24/18. - */ -public interface MedtronicHistoryEntryInterface { - - String getEntryTypeName(); - - void setData(List listRawData, boolean doNotProcess); - - int getDateLength(); - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.kt new file mode 100644 index 0000000000..21098edd1e --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +/** + * Created by andy on 7/24/18. + */ +interface MedtronicHistoryEntryInterface { + + val entryTypeName: String + fun setData(listRawData: MutableList, doNotProcess: Boolean) + val dateLength: Int +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java deleted file mode 100644 index 182e09b1bd..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java +++ /dev/null @@ -1,87 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -import java.util.Arrays; -import java.util.Locale; - -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.CRC; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by geoff on 6/4/16. - */ -public class RawHistoryPage { - - private final AAPSLogger aapsLogger; - - private byte[] data = new byte[0]; - - - public RawHistoryPage(AAPSLogger aapsLogger) { - this.aapsLogger = aapsLogger; - } - - - public void appendData(byte[] newdata) { - data = ByteUtil.concat(data, newdata); - } - - - public byte[] getData() { - return data; - } - - - byte[] getOnlyData() { - return Arrays.copyOfRange(data, 0, 1022); - } - - - public int getLength() { - return data.length; - } - - - public boolean isChecksumOK() { - if (getLength() != 1024) { - return false; - } - byte[] computedCRC = CRC.calculate16CCITT(ByteUtil.substring(data, 0, 1022)); - - int crcCalculated = ByteUtil.toInt(computedCRC[0], computedCRC[1]); - int crcStored = ByteUtil.toInt(data[1022], data[1023]); - - if (crcCalculated != crcStored) { - aapsLogger.error(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Stored CRC (%d) is different than calculated (%d), but ignored for now.", crcStored, - crcCalculated)); - } else { - if (MedtronicUtil.isLowLevelDebug()) - aapsLogger.debug(LTag.PUMPBTCOMM, "CRC ok."); - } - - return crcCalculated == crcStored; - } - - - public void dumpToDebug() { - int linesize = 80; - int offset = 0; - - StringBuilder sb = new StringBuilder(); - - while (offset < data.length) { - int bytesToLog = linesize; - if (offset + linesize > data.length) { - bytesToLog = data.length - offset; - } - sb.append(ByteUtil.shortHexString(ByteUtil.substring(data, offset, bytesToLog)) + " "); - // sb.append("\n"); - - offset += linesize; - } - - aapsLogger.info(LTag.PUMPBTCOMM, "History Page Data:\n" + sb.toString()); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.kt new file mode 100644 index 0000000000..c4036ea46e --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.kt @@ -0,0 +1,61 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +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.CRC +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import java.util.* + +/** + * Created by geoff on 6/4/16. + */ +class RawHistoryPage(private val aapsLogger: AAPSLogger) { + + var data = ByteArray(0) + private set + + fun appendData(newdata: ByteArray?) { + data = ByteUtil.concat(data, newdata) + } + + val onlyData: ByteArray + get() = Arrays.copyOfRange(data, 0, 1022) + + val length: Int + get() = data.size + + val isChecksumOK: Boolean + get() { + if (length != 1024) { + return false + } + val computedCRC = CRC.calculate16CCITT(ByteUtil.substring(data, 0, 1022)) + val crcCalculated = ByteUtil.toInt(computedCRC[0].toInt(), computedCRC[1].toInt()) + val crcStored = ByteUtil.toInt(data[1022].toInt(), data[1023].toInt()) + if (crcCalculated != crcStored) { + aapsLogger.error(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Stored CRC (%d) is different than calculated (%d), but ignored for now.", crcStored, + crcCalculated)) + } else { + if (MedtronicUtil.isLowLevelDebug) aapsLogger.debug(LTag.PUMPBTCOMM, "CRC ok.") + } + return crcCalculated == crcStored + } + + fun dumpToDebug() { + val linesize = 80 + var offset = 0 + val sb = StringBuilder() + while (offset < data.size) { + var bytesToLog = linesize + if (offset + linesize > data.size) { + bytesToLog = data.size - offset + } + sb.append(ByteUtil.shortHexString(ByteUtil.substring(data, offset, bytesToLog)) + " ") + // sb.append("\n"); + offset += linesize + } + aapsLogger.info(LTag.PUMPBTCOMM, "History Page Data:\n$sb") + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java deleted file mode 100644 index 18cb02cfad..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java +++ /dev/null @@ -1,30 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - * - * Author: Andy {andy.rozman@gmail.com} - */ - -public enum RecordDecodeStatus { - OK("OK "), // - Ignored("IGNORE "), // - NotSupported("N/A YET"), // - Error("ERROR "), // - WIP("WIP "), // - Unknown("UNK "); - - String description; - - - RecordDecodeStatus(String description) { - this.description = description; - - } - - - public String getDescription() { - return description; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.kt new file mode 100644 index 0000000000..82194b1f03 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class RecordDecodeStatus(var description: String) { + + OK("OK "), // + Ignored("IGNORE "), // + NotSupported("N/A YET"), // + Error("ERROR "), // + WIP("WIP "), // + Unknown("UNK "); + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java deleted file mode 100644 index 4d35426fb8..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java +++ /dev/null @@ -1,92 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.LocalDateTime; - -import java.util.List; - -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - * - * Author: Andy {andy.rozman@gmail.com} - */ - -public class CGMSHistoryEntry extends MedtronicHistoryEntry { - - private CGMSHistoryEntryType entryType; - private Integer opCode; // this is set only when we have unknown entry... - - - public CGMSHistoryEntryType getEntryType() { - return entryType; - } - - - public void setEntryType(CGMSHistoryEntryType entryType) { - this.entryType = entryType; - - this.sizes[0] = entryType.getHeadLength(); - this.sizes[1] = entryType.getDateLength(); - this.sizes[2] = entryType.getBodyLength(); - } - - - @Override - public String getEntryTypeName() { - return this.entryType.name(); - } - - - public void setData(List listRawData, boolean doNotProcess) { - if (this.entryType.schemaSet) { - super.setData(listRawData, doNotProcess); - } else { - this.rawData = listRawData; - } - } - - - @Override - public int getDateLength() { - return this.entryType.getDateLength(); - } - - - @Override - public int getOpCode() { - if (opCode == null) - return entryType.getOpCode(); - else - return opCode; - } - - - public void setOpCode(Integer opCode) { - this.opCode = opCode; - } - - - public boolean hasTimeStamp() { - return (this.entryType.hasDate()); - } - - - @Override - public String getToStringStart() { - - return "CGMSHistoryEntry [type=" + StringUtils.rightPad(entryType.name(), 18) + " [" - + StringUtils.leftPad("" + getOpCode(), 3) + ", 0x" + ByteUtil.getCorrectHexValue(getOpCode()) + "]"; - } - - - public void setDateTime(LocalDateTime timeStamp, int getIndex) { - - setAtechDateTime(DateTimeUtil.toATechDate(timeStamp.plusMinutes(getIndex * 5))); - - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.kt new file mode 100644 index 0000000000..26e9cea75a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.kt @@ -0,0 +1,63 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry +import org.apache.commons.lang3.StringUtils +import org.joda.time.LocalDateTime + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ +class CGMSHistoryEntry : MedtronicHistoryEntry() { + + var entryType: CGMSHistoryEntryType = CGMSHistoryEntryType.UnknownOpCode + private set + + override var opCode: Byte? = null // this is set only when we have unknown entry... + get() = if (field == null) entryType.code.toByte() else field + + fun setEntryType(entryType: CGMSHistoryEntryType) { + this.entryType = entryType + sizes[0] = entryType.headLength + sizes[1] = entryType.dateLength + sizes[2] = entryType.bodyLength + } + + override val entryTypeName: String + get() = entryType.name + + override fun generatePumpId(): Long { + return entryType.code + atechDateTime * 1000L + } + + override fun isEntryTypeSet(): Boolean { + return entryType != CGMSHistoryEntryType.UnknownOpCode + } + + override fun setData(listRawData: MutableList, doNotProcess: Boolean) { + if (entryType.schemaSet) { + super.setData(listRawData, doNotProcess) + } else { + rawData = listRawData + } + } + + override val dateLength: Int + get() = entryType.dateLength + + fun hasTimeStamp(): Boolean { + return entryType.hasDate() + } + + override val toStringStart: String + get() = ("CGMSHistoryEntry [type=" + StringUtils.rightPad(entryType.name, 18) + " [" + + StringUtils.leftPad("" + opCode, 3) + ", 0x" + ByteUtil.getCorrectHexValue(opCode!!) + "]") + + fun setDateTime(timeStamp: LocalDateTime, getIndex: Int) { + atechDateTime = (DateTimeUtil.toATechDate(timeStamp.plusMinutes(getIndex * 5))) + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java deleted file mode 100644 index 64161c720a..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java +++ /dev/null @@ -1,135 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; - -import java.util.HashMap; -import java.util.Map; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - * - * Author: Andy {andy.rozman@gmail.com} - */ - -public enum CGMSHistoryEntryType { - - None(0, "None", 1, 0, 0, DateType.None), // - - DataEnd(0x01, "DataEnd", 1, 0, 0, DateType.PreviousTimeStamp), // - SensorWeakSignal(0x02, "SensorWeakSignal", 1, 0, 0, DateType.PreviousTimeStamp), // - SensorCal(0x03, "SensorCal", 1, 0, 1, DateType.PreviousTimeStamp), // - SensorPacket(0x04, "SensorPacket", 1, 0, 1, DateType.PreviousTimeStamp), - SensorError(0x05, "SensorError", 1, 0, 1, DateType.PreviousTimeStamp), - SensorDataLow(0x06, "SensorDataLow", 1, 0, 1, DateType.PreviousTimeStamp), - SensorDataHigh(0x07, "SensorDataHigh", 1, 0, 1, DateType.PreviousTimeStamp), - SensorTimestamp(0x08, "SensorTimestamp", 1, 4, 0, DateType.MinuteSpecific), // - BatteryChange(0x0a, "BatteryChange", 1, 4, 0, DateType.MinuteSpecific), // - SensorStatus(0x0b, "SensorStatus", 1, 4, 0, DateType.MinuteSpecific), // - DateTimeChange(0x0c, "DateTimeChange", 1, 4, 0, DateType.SecondSpecific), // - SensorSync(0x0d, "SensorSync',packet_size=4", 1, 4, 0, DateType.MinuteSpecific), // - CalBGForGH(0x0e, "CalBGForGH',packet_size=5", 1, 4, 1, DateType.MinuteSpecific), // - SensorCalFactor(0x0f, "SensorCalFactor", 1, 4, 2, DateType.MinuteSpecific), // - Something10(0x10, "10-Something", 1, 4, 0, DateType.MinuteSpecific), // - Something19(0x13, "19-Something", 1, 0, 0, DateType.PreviousTimeStamp), - GlucoseSensorData(0xFF, "GlucoseSensorData", 1, 0, 0, DateType.PreviousTimeStamp); - - private static final Map opCodeMap = new HashMap<>(); - - static { - for (CGMSHistoryEntryType type : values()) { - opCodeMap.put(type.opCode, type); - } - } - - public boolean schemaSet; - private final int opCode; - private final String description; - private final int headLength; - private final int dateLength; - private final int bodyLength; - private final int totalLength; - private final DateType dateType; - - - CGMSHistoryEntryType(int opCode, String name, int head, int date, int body, DateType dateType) { - this.opCode = opCode; - this.description = name; - this.headLength = head; - this.dateLength = date; - this.bodyLength = body; - this.totalLength = (head + date + body); - this.schemaSet = true; - this.dateType = dateType; - } - - - // private CGMSHistoryEntryType(int opCode, String name, int length) - // { - // this.opCode = opCode; - // this.description = name; - // this.headLength = 0; - // this.dateLength = 0; - // this.bodyLength = 0; - // this.totalLength = length + 1; // opCode - // } - - public static CGMSHistoryEntryType getByCode(int opCode) { - if (opCodeMap.containsKey(opCode)) { - return opCodeMap.get(opCode); - } else - return CGMSHistoryEntryType.None; - } - - - public int getCode() { - return this.opCode; - } - - - public int getTotalLength() { - return totalLength; - } - - - public int getOpCode() { - return opCode; - } - - - public String getDescription() { - return description; - } - - - public int getHeadLength() { - return headLength; - } - - - public int getDateLength() { - return dateLength; - } - - - public int getBodyLength() { - return bodyLength; - } - - - public DateType getDateType() { - return dateType; - } - - - public boolean hasDate() { - return (this.dateType == DateType.MinuteSpecific) || (this.dateType == DateType.SecondSpecific); - } - - public enum DateType { - None, // - MinuteSpecific, // - SecondSpecific, // - PreviousTimeStamp // - - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.kt new file mode 100644 index 0000000000..bf56899830 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.kt @@ -0,0 +1,65 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class CGMSHistoryEntryType(val code: Int, val description: String, val headLength: Int, val dateLength: Int, val bodyLength: Int, dateType: DateType) { + + None(0, "None", 1, 0, 0, DateType.None), // + DataEnd(0x01, "DataEnd", 1, 0, 0, DateType.PreviousTimeStamp), // + SensorWeakSignal(0x02, "SensorWeakSignal", 1, 0, 0, DateType.PreviousTimeStamp), // + SensorCal(0x03, "SensorCal", 1, 0, 1, DateType.PreviousTimeStamp), // + SensorPacket(0x04, "SensorPacket", 1, 0, 1, DateType.PreviousTimeStamp), SensorError(0x05, "SensorError", 1, 0, 1, DateType.PreviousTimeStamp), SensorDataLow(0x06, "SensorDataLow", 1, 0, 1, DateType.PreviousTimeStamp), SensorDataHigh(0x07, "SensorDataHigh", 1, 0, 1, DateType.PreviousTimeStamp), SensorTimestamp(0x08, "SensorTimestamp", 1, 4, 0, DateType.MinuteSpecific), // + BatteryChange(0x0a, "BatteryChange", 1, 4, 0, DateType.MinuteSpecific), // + SensorStatus(0x0b, "SensorStatus", 1, 4, 0, DateType.MinuteSpecific), // + DateTimeChange(0x0c, "DateTimeChange", 1, 4, 0, DateType.SecondSpecific), // + SensorSync(0x0d, "SensorSync',packet_size=4", 1, 4, 0, DateType.MinuteSpecific), // + CalBGForGH(0x0e, "CalBGForGH',packet_size=5", 1, 4, 1, DateType.MinuteSpecific), // + SensorCalFactor(0x0f, "SensorCalFactor", 1, 4, 2, DateType.MinuteSpecific), // + Something10(0x10, "10-Something", 1, 4, 0, DateType.MinuteSpecific), // + Something19(0x13, "19-Something", 1, 0, 0, DateType.PreviousTimeStamp), GlucoseSensorData(0xFF, "GlucoseSensorData", 1, 0, 0, DateType.PreviousTimeStamp), + UnknownOpCode(0xFF, "Unknown", 0, 0, 0, DateType.None); + + companion object { + + private val opCodeMap: MutableMap = mutableMapOf() + + @JvmStatic + fun getByCode(opCode: Int): CGMSHistoryEntryType { + return if (opCodeMap.containsKey(opCode)) + opCodeMap[opCode]!! + else + UnknownOpCode + } + + init { + for (type in values()) { + opCodeMap[type.code] = type + } + } + } + + @JvmField var schemaSet: Boolean + val totalLength: Int + val dateType: DateType + + fun hasDate(): Boolean { + return dateType == DateType.MinuteSpecific || dateType == DateType.SecondSpecific + } + + enum class DateType { + None, // + MinuteSpecific, // + SecondSpecific, // + PreviousTimeStamp // + } + + init { + totalLength = headLength + dateLength + bodyLength + schemaSet = true + this.dateType = dateType + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java deleted file mode 100644 index 2854d49c41..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java +++ /dev/null @@ -1,469 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; - -import org.joda.time.LocalDateTime; -import org.slf4j.Logger; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { - - //private static final Logger LOG = StacktraceLoggerWrapper.getLogger(LTag.PUMPCOMM); - - - // CGMSValuesWriter cgmsValuesWriter = null; - - public MedtronicCGMSHistoryDecoder() { - } - - - public RecordDecodeStatus decodeRecord(CGMSHistoryEntry record) { - try { - return decodeRecord(record, false); - } catch (Exception ex) { - //LOG.error(" Error decoding: type={}, ex={}", record.getEntryType().name(), ex.getMessage(), ex); - return RecordDecodeStatus.Error; - } - } - - - public RecordDecodeStatus decodeRecord(CGMSHistoryEntry entry, boolean x) { - - if (entry.getDateTimeLength() > 0) { - parseDate(entry); - } - - switch (entry.getEntryType()) { - - case SensorPacket: - decodeSensorPacket(entry); - break; - - case SensorError: - decodeSensorError(entry); - break; - - case SensorDataLow: - decodeDataHighLow(entry, 40); - break; - - case SensorDataHigh: - decodeDataHighLow(entry, 400); - break; - - case SensorTimestamp: - decodeSensorTimestamp(entry); - break; - - case SensorCal: - decodeSensorCal(entry); - break; - - case SensorCalFactor: - decodeSensorCalFactor(entry); - break; - - case SensorSync: - decodeSensorSync(entry); - break; - - case SensorStatus: - decodeSensorStatus(entry); - break; - - case CalBGForGH: - decodeCalBGForGH(entry); - break; - - case GlucoseSensorData: - decodeGlucoseSensorData(entry); - break; - - // just timestamp - case BatteryChange: - case Something10: - case DateTimeChange: - break; - - // just relative timestamp - case Something19: - case DataEnd: - case SensorWeakSignal: - break; - - case None: - break; - - } - - return RecordDecodeStatus.NotSupported; - } - - - @Override - public void postProcess() { - - } - - - public List createRecords(List dataClearInput) { - - List dataClear = reverseList(dataClearInput, Byte.class); - - prepareStatistics(); - - int counter = 0; - - List outList = new ArrayList(); - - // create CGMS entries (without dates) - do { - int opCode = getUnsignedInt(dataClear.get(counter)); - counter++; - - CGMSHistoryEntryType entryType; - - if (opCode == 0) { - // continue; - } else if ((opCode > 0) && (opCode < 20)) { - entryType = CGMSHistoryEntryType.getByCode(opCode); - - if (entryType == CGMSHistoryEntryType.None) { - this.unknownOpCodes.put(opCode, opCode); - //LOG.warn("GlucoseHistoryEntry with unknown code: " + opCode); - - CGMSHistoryEntry pe = new CGMSHistoryEntry(); - pe.setEntryType(CGMSHistoryEntryType.None); - pe.setOpCode(opCode); - - pe.setData(Arrays.asList((byte) opCode), false); - - outList.add(pe); - } else { - // System.out.println("OpCode: " + opCode); - - List listRawData = new ArrayList(); - listRawData.add((byte) opCode); - - for (int j = 0; j < (entryType.getTotalLength() - 1); j++) { - listRawData.add(dataClear.get(counter)); - counter++; - } - - CGMSHistoryEntry pe = new CGMSHistoryEntry(); - pe.setEntryType(entryType); - - pe.setOpCode(opCode); - pe.setData(listRawData, false); - - // System.out.println("Record: " + pe); - - outList.add(pe); - } - } else { - CGMSHistoryEntry pe = new CGMSHistoryEntry(); - pe.setEntryType(CGMSHistoryEntryType.GlucoseSensorData); - - pe.setData(Arrays.asList((byte) opCode), false); - - outList.add(pe); - } - - } while (counter < dataClear.size()); - - List reversedOutList = reverseList(outList, CGMSHistoryEntry.class); - - Long timeStamp = null; - LocalDateTime dateTime = null; - int getIndex = 0; - - for (CGMSHistoryEntry entry : reversedOutList) { - - decodeRecord(entry); - - if (entry.hasTimeStamp()) { - timeStamp = entry.atechDateTime; - dateTime = DateTimeUtil.toLocalDateTime(timeStamp); - getIndex = 0; - } else if (entry.getEntryType() == CGMSHistoryEntryType.GlucoseSensorData) { - getIndex++; - if (dateTime != null) - entry.setDateTime(dateTime, getIndex); - } else { - if (dateTime != null) - entry.setDateTime(dateTime, getIndex); - } - - //LOG.debug("Record: {}", entry); - } - - return reversedOutList; - - } - - - private List reverseList(List dataClearInput, Class clazz) { - - List outList = new ArrayList(); - - for (int i = dataClearInput.size() - 1; i > 0; i--) { - outList.add(dataClearInput.get(i)); - } - - return outList; - } - - - private int parseMinutes(int one) { - return (one & Integer.parseInt("0111111", 2)); - } - - - private int parseHours(int one) { - return (one & 0x1F); - } - - - private int parseDay(int one) { - return one & 0x1F; - } - - - private int parseMonths(int first_byte, int second_byte) { - - int first_two_bits = first_byte >> 6; - int second_two_bits = second_byte >> 6; - - return (first_two_bits << 2) + second_two_bits; - } - - - private int parseYear(int year) { - return (year & 0x0F) + 2000; - } - - - private Long parseDate(CGMSHistoryEntry entry) { - - if (!entry.getEntryType().hasDate()) - return null; - - byte[] data = entry.getDatetime(); - - if (entry.getEntryType().getDateType() == CGMSHistoryEntryType.DateType.MinuteSpecific) { - - Long atechDateTime = DateTimeUtil.toATechDate(parseYear(data[3]), parseMonths(data[0], data[1]), - parseDay(data[2]), parseHours(data[0]), parseMinutes(data[1]), 0); - - entry.setAtechDateTime(atechDateTime); - - return atechDateTime; - - } else if (entry.getEntryType().getDateType() == CGMSHistoryEntryType.DateType.SecondSpecific) { - //LOG.warn("parseDate for SecondSpecific type is not implemented."); - throw new RuntimeException(); - // return null; - } else - return null; - - } - - - private void decodeGlucoseSensorData(CGMSHistoryEntry entry) { - int sgv = entry.getUnsignedRawDataByIndex(0) * 2; - entry.addDecodedData("sgv", sgv); - } - - - private void decodeCalBGForGH(CGMSHistoryEntry entry) { - - int amount = ((entry.getRawDataByIndex(3) & 0b00100000) << 3) | entry.getRawDataByIndex(5); - // - String originType; - - switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { - case 0x00: - originType = "rf"; - break; - - default: - originType = "unknown"; - - } - - entry.addDecodedData("amount", amount); - entry.addDecodedData("originType", originType); - - } - - - private void decodeSensorSync(CGMSHistoryEntry entry) { - - String syncType; - - switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { - case 0x01: - syncType = "new"; - break; - - case 0x02: - syncType = "old"; - break; - - default: - syncType = "find"; - break; - - } - - entry.addDecodedData("syncType", syncType); - } - - - private void decodeSensorStatus(CGMSHistoryEntry entry) { - - String statusType; - - switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { - case 0x00: - statusType = "off"; - break; - - case 0x01: - statusType = "on"; - break; - - case 0x02: - statusType = "lost"; - break; - - default: - statusType = "unknown"; - } - - entry.addDecodedData("statusType", statusType); - - } - - - private void decodeSensorCalFactor(CGMSHistoryEntry entry) { - - double factor = (entry.getRawDataByIndex(5) << 8 | entry.getRawDataByIndex(6)) / 1000.0d; - - entry.addDecodedData("factor", factor); - } - - - private void decodeSensorCal(CGMSHistoryEntry entry) { - - String calibrationType; - - switch (entry.getRawDataByIndex(1)) { - case 0x00: - calibrationType = "meter_bg_now"; - break; - - case 0x01: - calibrationType = "waiting"; - break; - - case 0x02: - calibrationType = "cal_error"; - break; - - default: - calibrationType = "unknown"; - } - - entry.addDecodedData("calibrationType", calibrationType); - - } - - - private void decodeSensorTimestamp(CGMSHistoryEntry entry) { - - String sensorTimestampType; - - switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { - - case 0x00: - sensorTimestampType = "LastRf"; - break; - - case 0x01: - sensorTimestampType = "PageEnd"; - break; - - case 0x02: - sensorTimestampType = "Gap"; - break; - - default: - sensorTimestampType = "Unknown"; - break; - - } - - entry.addDecodedData("sensorTimestampType", sensorTimestampType); - } - - - private void decodeSensorPacket(CGMSHistoryEntry entry) { - - String packetType; - - switch (entry.getRawDataByIndex(1)) { - case 0x02: - packetType = "init"; - break; - - default: - packetType = "unknown"; - } - - entry.addDecodedData("packetType", packetType); - } - - - private void decodeSensorError(CGMSHistoryEntry entry) { - - String errorType; - - switch (entry.getRawDataByIndex(1)) { - case 0x01: - errorType = "end"; - break; - - default: - errorType = "unknown"; - } - - entry.addDecodedData("errorType", errorType); - } - - - private void decodeDataHighLow(CGMSHistoryEntry entry, int sgv) { - entry.addDecodedData("sgv", sgv); - } - - - @Override - protected void runPostDecodeTasks() { - this.showStatistics(); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.kt new file mode 100644 index 0000000000..218330d6be --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.kt @@ -0,0 +1,278 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms + +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.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms.CGMSHistoryEntryType.Companion.getByCode +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import okhttp3.internal.and +import org.joda.time.LocalDateTime +import java.util.* + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +class MedtronicCGMSHistoryDecoder constructor( + aapsLogger: AAPSLogger, + medtronicUtil: MedtronicUtil, + bitUtils: ByteUtil +) : MedtronicHistoryDecoder(aapsLogger, medtronicUtil, bitUtils) { + + override fun decodeRecord(record: CGMSHistoryEntry): RecordDecodeStatus? { + return try { + decodeRecordInternal(record) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPCOMM, " Error decoding: type={}, ex={}", record.entryType.name, ex.message, ex) + RecordDecodeStatus.Error + } + } + + fun decodeRecordInternal(entry: CGMSHistoryEntry): RecordDecodeStatus { + if (entry.dateTimeLength > 0) { + parseDate(entry) + } + when (entry.entryType) { + CGMSHistoryEntryType.SensorPacket -> decodeSensorPacket(entry) + CGMSHistoryEntryType.SensorError -> decodeSensorError(entry) + CGMSHistoryEntryType.SensorDataLow -> decodeDataHighLow(entry, 40) + CGMSHistoryEntryType.SensorDataHigh -> decodeDataHighLow(entry, 400) + CGMSHistoryEntryType.SensorTimestamp -> decodeSensorTimestamp(entry) + CGMSHistoryEntryType.SensorCal -> decodeSensorCal(entry) + CGMSHistoryEntryType.SensorCalFactor -> decodeSensorCalFactor(entry) + CGMSHistoryEntryType.SensorSync -> decodeSensorSync(entry) + CGMSHistoryEntryType.SensorStatus -> decodeSensorStatus(entry) + CGMSHistoryEntryType.CalBGForGH -> decodeCalBGForGH(entry) + CGMSHistoryEntryType.GlucoseSensorData -> decodeGlucoseSensorData(entry) + + CGMSHistoryEntryType.BatteryChange, + CGMSHistoryEntryType.Something10, + CGMSHistoryEntryType.DateTimeChange -> { + } + + CGMSHistoryEntryType.Something19, + CGMSHistoryEntryType.DataEnd, + CGMSHistoryEntryType.SensorWeakSignal -> { + } + + CGMSHistoryEntryType.UnknownOpCode, + CGMSHistoryEntryType.None -> { + } + } + return RecordDecodeStatus.NotSupported + } + + override fun postProcess() {} + + override fun createRecords(dataClearInput: MutableList): MutableList { + dataClearInput.reverse() + val dataClear = dataClearInput //reverseList(dataClearInput, Byte::class.java) + prepareStatistics() + var counter = 0 + val outList: MutableList = mutableListOf() + + // create CGMS entries (without dates) + do { + val opCode = getUnsignedInt(dataClear[counter]) + counter++ + var entryType: CGMSHistoryEntryType? + if (opCode == 0) { + // continue; + } else if (opCode > 0 && opCode < 20) { + entryType = getByCode(opCode) + if (entryType === CGMSHistoryEntryType.None) { + unknownOpCodes[opCode] = opCode + aapsLogger.warn(LTag.PUMPCOMM, "GlucoseHistoryEntry with unknown code: $opCode") + val pe = CGMSHistoryEntry() + pe.setEntryType(CGMSHistoryEntryType.None) + pe.opCode = opCode.toByte() + pe.setData(Arrays.asList(opCode.toByte()), false) + outList.add(pe) + } else { + // System.out.println("OpCode: " + opCode); + val listRawData: MutableList = ArrayList() + listRawData.add(opCode.toByte()) + for (j in 0 until entryType.totalLength - 1) { + listRawData.add(dataClear[counter]) + counter++ + } + val pe = CGMSHistoryEntry() + pe.setEntryType(entryType) + pe.opCode = opCode.toByte() + pe.setData(listRawData, false) + + // System.out.println("Record: " + pe); + outList.add(pe) + } + } else { + val pe = CGMSHistoryEntry() + pe.setEntryType(CGMSHistoryEntryType.GlucoseSensorData) + pe.setData(Arrays.asList(opCode.toByte()), false) + outList.add(pe) + } + } while (counter < dataClear.size) + outList.reverse() + val reversedOutList = outList // reverseList(outList, CGMSHistoryEntry::class.java) + //var timeStamp: Long? = null + var dateTime: LocalDateTime? = null + var getIndex = 0 + for (entry in reversedOutList) { + decodeRecord(entry) + if (entry.hasTimeStamp()) { + //timeStamp = entry.atechDateTime + dateTime = DateTimeUtil.toLocalDateTime(entry.atechDateTime) + getIndex = 0 + } else if (entry.entryType == CGMSHistoryEntryType.GlucoseSensorData) { + getIndex++ + if (dateTime != null) entry.setDateTime(dateTime, getIndex) + } else { + if (dateTime != null) entry.setDateTime(dateTime, getIndex) + } + aapsLogger.debug(LTag.PUMPCOMM, "Record: {}", entry) + } + return reversedOutList + } + + // private fun reverseList(dataClearInput: List, clazz: Class): List { + // val outList: MutableList = ArrayList() + // for (i in dataClearInput.size - 1 downTo 1) { + // outList.add(dataClearInput[i]) + // } + // return outList + // } + + private fun parseMinutes(one: Int): Int { + return one and "0111111".toInt(2) + } + + private fun parseHours(one: Int): Int { + return one and 0x1F + } + + private fun parseDay(one: Int): Int { + return one and 0x1F + } + + private fun parseMonths(first_byte: Int, second_byte: Int): Int { + val first_two_bits = first_byte shr 6 + val second_two_bits = second_byte shr 6 + return (first_two_bits shl 2) + second_two_bits + } + + private fun parseYear(year: Int): Int { + return (year and 0x0F) + 2000 + } + + private fun parseDate(entry: CGMSHistoryEntry): Long? { + if (!entry.entryType.hasDate()) return null + val data = entry.datetime + return if (entry.entryType.dateType === CGMSHistoryEntryType.DateType.MinuteSpecific) { + val atechDateTime = DateTimeUtil.toATechDate(parseYear(data[3].toInt()), parseMonths(data[0].toInt(), data[1].toInt()), + parseDay(data[2].toInt()), parseHours(data[0].toInt()), parseMinutes(data[1].toInt()), 0) + entry.atechDateTime = atechDateTime + atechDateTime + } else if (entry.entryType.dateType === CGMSHistoryEntryType.DateType.SecondSpecific) { + aapsLogger.warn(LTag.PUMPCOMM, "parseDate for SecondSpecific type is not implemented.") + throw RuntimeException() + // return null; + } else null + } + + private fun decodeGlucoseSensorData(entry: CGMSHistoryEntry) { + val sgv = entry.getUnsignedRawDataByIndex(0) * 2 + entry.addDecodedData("sgv", sgv) + } + + private fun decodeCalBGForGH(entry: CGMSHistoryEntry) { + val amount: Int = entry.getRawDataByIndex(3) and 32 shl 3 or entry.getRawDataByIndexInt(5) + // + val originType: String + originType = when (entry.getRawDataByIndexInt(3) shr 5 and 3) { + 0x00 -> "rf" + else -> "unknown" + } + entry.addDecodedData("amount", amount) + entry.addDecodedData("originType", originType) + } + + private fun decodeSensorSync(entry: CGMSHistoryEntry) { + val syncType: String + syncType = when (entry.getRawDataByIndexInt(3) shr 5 and 3) { + 0x01 -> "new" + 0x02 -> "old" + else -> "find" + } + entry.addDecodedData("syncType", syncType) + } + + private fun decodeSensorStatus(entry: CGMSHistoryEntry) { + val statusType: String + statusType = when (entry.getRawDataByIndexInt(3) shr 5 and 3) { + 0x00 -> "off" + 0x01 -> "on" + 0x02 -> "lost" + else -> "unknown" + } + entry.addDecodedData("statusType", statusType) + } + + private fun decodeSensorCalFactor(entry: CGMSHistoryEntry) { + val factor: Double = (entry.getRawDataByIndexInt(5) shl 8 or entry.getRawDataByIndexInt(6)) / 1000.0 + entry.addDecodedData("factor", factor) + } + + private fun decodeSensorCal(entry: CGMSHistoryEntry) { + val calibrationType: String + calibrationType = when (entry.getRawDataByIndexInt(1)) { + 0x00 -> "meter_bg_now" + 0x01 -> "waiting" + 0x02 -> "cal_error" + else -> "unknown" + } + entry.addDecodedData("calibrationType", calibrationType) + } + + private fun decodeSensorTimestamp(entry: CGMSHistoryEntry) { + val sensorTimestampType: String + sensorTimestampType = when (entry.getRawDataByIndex(3).toInt() shr 5 and 3) { + 0x00 -> "LastRf" + 0x01 -> "PageEnd" + 0x02 -> "Gap" + else -> "Unknown" + } + entry.addDecodedData("sensorTimestampType", sensorTimestampType) + } + + private fun decodeSensorPacket(entry: CGMSHistoryEntry) { + val packetType: String + packetType = when (entry.getRawDataByIndex(1)) { + 0x02.toByte() -> "init" + else -> "unknown" + } + entry.addDecodedData("packetType", packetType) + } + + private fun decodeSensorError(entry: CGMSHistoryEntry) { + val errorType: String + errorType = when (entry.getRawDataByIndexInt(1)) { + 0x01 -> "end" + else -> "unknown" + } + entry.addDecodedData("errorType", errorType) + } + + private fun decodeDataHighLow(entry: CGMSHistoryEntry, sgv: Int) { + entry.addDecodedData("sgv", sgv) + } + + override fun runPostDecodeTasks() { + showStatistics() + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java deleted file mode 100644 index b8d57edb7c..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java +++ /dev/null @@ -1,724 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import javax.inject.Inject; -import javax.inject.Singleton; - -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.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusWizardDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -@Singleton -public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { - - private PumpHistoryEntry tbrPreviousRecord; - private PumpHistoryEntry changeTimeRecord; - - @Inject - public MedtronicPumpHistoryDecoder( - AAPSLogger aapsLogger, - MedtronicUtil medtronicUtil - ) { - super.aapsLogger = aapsLogger; - this.medtronicUtil = medtronicUtil; - } - - - public List createRecords(List dataClear) { - prepareStatistics(); - - int counter = 0; - int record = 0; - boolean incompletePacket; - - List outList = new ArrayList<>(); - String skipped = null; - - if (dataClear.size() == 0) { - aapsLogger.error(LTag.PUMPBTCOMM, "Empty page."); - return outList; - } - - do { - int opCode = dataClear.get(counter); - boolean special = false; - incompletePacket = false; - boolean skippedRecords = false; - - if (opCode == 0) { - counter++; - if (skipped == null) - skipped = "0x00"; - else - skipped += " 0x00"; - continue; - } else { - if (skipped != null) { - aapsLogger.warn(LTag.PUMPBTCOMM, " ... Skipped " + skipped); - skipped = null; - skippedRecords = true; - } - } - - if (skippedRecords) { - aapsLogger.error(LTag.PUMPBTCOMM, "We had some skipped bytes, which might indicate error in pump history. Please report this problem."); - } - - PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(opCode); - - PumpHistoryEntry pe = new PumpHistoryEntry(); - pe.setEntryType(medtronicUtil.getMedtronicPumpModel(), entryType); - pe.setOffset(counter); - - counter++; - - if (counter >= 1022) { - break; - } - - List listRawData = new ArrayList<>(); - listRawData.add((byte) opCode); - - if (entryType == PumpHistoryEntryType.UnabsorbedInsulin - || entryType == PumpHistoryEntryType.UnabsorbedInsulin512) { - int elements = dataClear.get(counter); - listRawData.add((byte) elements); - counter++; - - int els = getUnsignedInt(elements); - - for (int k = 0; k < (els - 2); k++) { - if (counter < 1022) { - listRawData.add(dataClear.get(counter)); - counter++; - } - } - - special = true; - } else { - - for (int j = 0; j < (entryType.getTotalLength(medtronicUtil.getMedtronicPumpModel()) - 1); j++) { - - try { - listRawData.add(dataClear.get(counter)); - counter++; - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPBTCOMM, "OpCode: " + ByteUtil.shortHexString((byte) opCode) + ", Invalid package: " - + ByteUtil.getHex(listRawData)); - // throw ex; - incompletePacket = true; - break; - } - - } - - if (incompletePacket) - break; - - } - - if (entryType == PumpHistoryEntryType.None) { - aapsLogger.error(LTag.PUMPBTCOMM, "Error in code. We should have not come into this branch."); - } else { - - if (pe.getEntryType() == PumpHistoryEntryType.UnknownBasePacket) { - pe.setOpCode(opCode); - } - - if (entryType.getHeadLength(medtronicUtil.getMedtronicPumpModel()) == 0) - special = true; - - pe.setData(listRawData, special); - - RecordDecodeStatus decoded = decodeRecord(pe); - - if ((decoded == RecordDecodeStatus.OK) || (decoded == RecordDecodeStatus.Ignored)) { - //Log.i(TAG, "#" + record + " " + decoded.getDescription() + " " + pe); - } else { - aapsLogger.warn(LTag.PUMPBTCOMM, "#" + record + " " + decoded.getDescription() + " " + pe); - } - - addToStatistics(pe, decoded, null); - - record++; - - if (decoded == RecordDecodeStatus.OK) // we add only OK records, all others are ignored - { - outList.add(pe); - } - } - - } while (counter < dataClear.size()); - - return outList; - } - - - public RecordDecodeStatus decodeRecord(PumpHistoryEntry record) { - try { - return decodeRecord(record, false); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, " Error decoding: type=%s, ex=%s", record.getEntryType().name(), ex.getMessage(), ex)); - return RecordDecodeStatus.Error; - } - } - - - private RecordDecodeStatus decodeRecord(PumpHistoryEntry entry, boolean x) { - - if (entry.getDateTimeLength() > 0) { - decodeDateTime(entry); - } - - switch (entry.getEntryType()) { - - // Valid entries, but not processed - case ChangeBasalPattern: - case CalBGForPH: - case ChangeRemoteId: - case ClearAlarm: - case ChangeAlarmNotifyMode: // ChangeUtility: - case EnableDisableRemote: - case BGReceived: // Ian3F: CGMS - case SensorAlert: // Ian08 CGMS - case ChangeTimeFormat: - case ChangeReservoirWarningTime: - case ChangeBolusReminderEnable: - case SetBolusReminderTime: - case ChangeChildBlockEnable: - case BolusWizardEnabled: - case ChangeBGReminderOffset: - case ChangeAlarmClockTime: - case ChangeMeterId: - case ChangeParadigmID: - case JournalEntryMealMarker: - case JournalEntryExerciseMarker: - case DeleteBolusReminderTime: - case SetAutoOff: - case SelfTest: - case JournalEntryInsulinMarker: - case JournalEntryOtherMarker: - case BolusWizardSetup512: - case ChangeSensorSetup2: - case ChangeSensorAlarmSilenceConfig: - case ChangeSensorRateOfChangeAlertSetup: - case ChangeBolusScrollStepSize: - case BolusWizardSetup: - case ChangeVariableBolus: - case ChangeAudioBolus: - case ChangeBGReminderEnable: - case ChangeAlarmClockEnable: - case BolusReminder: - case DeleteAlarmClockTime: - case ChangeCarbUnits: - case ChangeWatchdogEnable: - case ChangeOtherDeviceID: - case ReadOtherDevicesIDs: - case BGReceived512: - case SensorStatus: - case ReadCaptureEventEnabled: - case ChangeCaptureEventEnable: - case ReadOtherDevicesStatus: - return RecordDecodeStatus.OK; - - case Sensor_0x54: - case Sensor_0x55: - case Sensor_0x51: - case Sensor_0x52: -// case EventUnknown_MM522_0x45: -// case EventUnknown_MM522_0x46: -// case EventUnknown_MM522_0x47: -// case EventUnknown_MM522_0x48: -// case EventUnknown_MM522_0x49: -// case EventUnknown_MM522_0x4a: -// case EventUnknown_MM522_0x4b: -// case EventUnknown_MM522_0x4c: -// case EventUnknown_MM512_0x10: - case EventUnknown_MM512_0x2e: -// case EventUnknown_MM512_0x37: -// case EventUnknown_MM512_0x38: -// case EventUnknown_MM512_0x4e: -// case EventUnknown_MM522_0x70: -// case EventUnknown_MM512_0x88: -// case EventUnknown_MM512_0x94: -// case EventUnknown_MM522_0xE8: -// case EventUnknown_0x4d: -// case EventUnknown_MM522_0x25: -// case EventUnknown_MM522_0x05: - aapsLogger.debug(LTag.PUMPBTCOMM, " -- ignored Unknown Pump Entry: " + entry); - return RecordDecodeStatus.Ignored; - - case UnabsorbedInsulin: - case UnabsorbedInsulin512: - return RecordDecodeStatus.Ignored; - - // **** Implemented records **** - - case DailyTotals522: - case DailyTotals523: - case DailyTotals515: - case EndResultTotals: - return decodeDailyTotals(entry); - - case ChangeBasalProfile_OldProfile: - case ChangeBasalProfile_NewProfile: - return decodeBasalProfile(entry); - - case BasalProfileStart: - return decodeBasalProfileStart(entry); - - case ChangeTime: - changeTimeRecord = entry; - return RecordDecodeStatus.OK; - - case NewTimeSet: - decodeChangeTime(entry); - return RecordDecodeStatus.OK; - - case TempBasalDuration: - // decodeTempBasal(entry); - return RecordDecodeStatus.OK; - - case TempBasalRate: - // decodeTempBasal(entry); - return RecordDecodeStatus.OK; - - case Bolus: - decodeBolus(entry); - return RecordDecodeStatus.OK; - - case BatteryChange: - decodeBatteryActivity(entry); - return RecordDecodeStatus.OK; - - case LowReservoir: - decodeLowReservoir(entry); - return RecordDecodeStatus.OK; - - case LowBattery: - case SuspendPump: - case ResumePump: - case Rewind: - case NoDeliveryAlarm: - case ChangeTempBasalType: - case ChangeMaxBolus: - case ChangeMaxBasal: - case ClearSettings: - case SaveSettings: - return RecordDecodeStatus.OK; - - case BolusWizard: - return decodeBolusWizard(entry); - - case BolusWizard512: - return decodeBolusWizard512(entry); - - case Prime: - decodePrime(entry); - return RecordDecodeStatus.OK; - - case TempBasalCombined: - return RecordDecodeStatus.Ignored; - - case None: - case UnknownBasePacket: - return RecordDecodeStatus.Error; - - default: { - aapsLogger.debug(LTag.PUMPBTCOMM, "Not supported: " + entry.getEntryType()); - return RecordDecodeStatus.NotSupported; - } - - } - - // return RecordDecodeStatus.Error; - - } - - - private RecordDecodeStatus decodeDailyTotals(PumpHistoryEntry entry) { - - entry.addDecodedData("Raw Data", ByteUtil.getHex(entry.getRawData())); - - DailyTotalsDTO totals = new DailyTotalsDTO(entry); - - entry.addDecodedData("Object", totals); - - return RecordDecodeStatus.OK; - } - - - private RecordDecodeStatus decodeBasalProfile(PumpHistoryEntry entry) { - - // LOG.debug("decodeBasalProfile: {}", entry); - - BasalProfile basalProfile = new BasalProfile(aapsLogger); - basalProfile.setRawDataFromHistory(entry.getBody()); - - // LOG.debug("decodeBasalProfile BasalProfile: {}", basalProfile); - - entry.addDecodedData("Object", basalProfile); - - return RecordDecodeStatus.OK; - } - - - private void decodeChangeTime(PumpHistoryEntry entry) { - if (changeTimeRecord == null) - return; - - entry.setDisplayableValue(entry.getDateTimeString()); - - this.changeTimeRecord = null; - } - - - private void decodeBatteryActivity(PumpHistoryEntry entry) { - // this.writeData(PumpBaseType.Event, entry.getHead()[0] == 0 ? PumpEventType.BatteryRemoved : - // PumpEventType.BatteryReplaced, entry.getATechDate()); - - entry.setDisplayableValue(entry.getHead()[0] == 0 ? "Battery Removed" : "Battery Replaced"); - } - - - private static String getFormattedValue(float value, int decimals) { - return String.format(Locale.ENGLISH, "%." + decimals + "f", value); - } - - - private RecordDecodeStatus decodeBasalProfileStart(PumpHistoryEntry entry) { - byte[] body = entry.getBody(); - // int bodyOffset = headerSize + timestampSize; - int offset = body[0] * 1000 * 30 * 60; - Float rate = null; - int index = entry.getHead()[0]; - - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) { - rate = body[1] * 0.025f; - } - - //LOG.info("Basal Profile Start: offset={}, rate={}, index={}, body_raw={}", offset, rate, index, body); - - if (rate == null) { - aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Basal Profile Start (ERROR): offset=%d, rate=%.3f, index=%d, body_raw=%s", offset, rate, index, ByteUtil.getHex(body))); - return RecordDecodeStatus.Error; - } else { - entry.addDecodedData("Value", getFormattedFloat(rate, 3)); - entry.setDisplayableValue(getFormattedFloat(rate, 3)); - return RecordDecodeStatus.OK; - } - - } - - - private RecordDecodeStatus decodeBolusWizard(PumpHistoryEntry entry) { - byte[] body = entry.getBody(); - - BolusWizardDTO dto = new BolusWizardDTO(); - - float bolusStrokes = 10.0f; - - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) { - // https://github.com/ps2/minimed_rf/blob/master/lib/minimed_rf/log_entries/bolus_wizard.rb#L102 - bolusStrokes = 40.0f; - - dto.carbs = ((body[1] & 0x0c) << 6) + body[0]; - - dto.bloodGlucose = ((body[1] & 0x03) << 8) + entry.getHead()[0]; - dto.carbRatio = body[1] / 10.0f; - // carb_ratio (?) = (((self.body[2] & 0x07) << 8) + self.body[3]) / - // 10.0s - dto.insulinSensitivity = new Float(body[4]); - dto.bgTargetLow = (int) body[5]; - dto.bgTargetHigh = (int) body[14]; - dto.correctionEstimate = (((body[9] & 0x38) << 5) + body[6]) / bolusStrokes; - dto.foodEstimate = ((body[7] << 8) + body[8]) / bolusStrokes; - dto.unabsorbedInsulin = ((body[10] << 8) + body[11]) / bolusStrokes; - dto.bolusTotal = ((body[12] << 8) + body[13]) / bolusStrokes; - } else { - dto.bloodGlucose = (((body[1] & 0x0F) << 8) | entry.getHead()[0]); - dto.carbs = (int) body[0]; - dto.carbRatio = Float.valueOf(body[2]); - dto.insulinSensitivity = new Float(body[3]); - dto.bgTargetLow = (int) body[4]; - dto.bgTargetHigh = (int) body[12]; - dto.bolusTotal = body[11] / bolusStrokes; - dto.foodEstimate = body[6] / bolusStrokes; - dto.unabsorbedInsulin = body[9] / bolusStrokes; - dto.bolusTotal = body[11] / bolusStrokes; - dto.correctionEstimate = (body[7] + (body[5] & 0x0F)) / bolusStrokes; - } - - if (dto.bloodGlucose != null && dto.bloodGlucose < 0) { - dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.byteValue()); - } - - dto.atechDateTime = entry.atechDateTime; - entry.addDecodedData("Object", dto); - entry.setDisplayableValue(dto.getDisplayableValue()); - - return RecordDecodeStatus.OK; - } - - - private RecordDecodeStatus decodeBolusWizard512(PumpHistoryEntry entry) { - byte[] body = entry.getBody(); - - BolusWizardDTO dto = new BolusWizardDTO(); - - float bolusStrokes = 10.0f; - - dto.bloodGlucose = ((body[1] & 0x03 << 8) | entry.getHead()[0]); - dto.carbs = (body[1] & 0xC) << 6 | body[0]; // (int)body[0]; - dto.carbRatio = Float.valueOf(body[2]); - dto.insulinSensitivity = new Float(body[3]); - dto.bgTargetLow = (int) body[4]; - dto.foodEstimate = body[6] / 10.0f; - dto.correctionEstimate = (body[7] + (body[5] & 0x0F)) / bolusStrokes; - dto.unabsorbedInsulin = body[9] / bolusStrokes; - dto.bolusTotal = body[11] / bolusStrokes; - dto.bgTargetHigh = dto.bgTargetLow; - - if (dto.bloodGlucose != null && dto.bloodGlucose < 0) { - dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.byteValue()); - } - - dto.atechDateTime = entry.atechDateTime; - entry.addDecodedData("Object", dto); - entry.setDisplayableValue(dto.getDisplayableValue()); - - return RecordDecodeStatus.OK; - } - - - private void decodeLowReservoir(PumpHistoryEntry entry) { - float amount = (getUnsignedInt(entry.getHead()[0]) * 1.0f / 10.0f) * 2; - - entry.setDisplayableValue(getFormattedValue(amount, 1)); - } - - - private void decodePrime(PumpHistoryEntry entry) { - float amount = ByteUtil.toInt(entry.getHead()[2], entry.getHead()[3]) / 10.0f; - float fixed = ByteUtil.toInt(entry.getHead()[0], entry.getHead()[1]) / 10.0f; - -// amount = (double)(asUINT8(data[4]) << 2) / 40.0; -// programmedAmount = (double)(asUINT8(data[2]) << 2) / 40.0; -// primeType = programmedAmount == 0 ? "manual" : "fixed"; - - entry.addDecodedData("Amount", amount); - entry.addDecodedData("FixedAmount", fixed); - - entry.setDisplayableValue("Amount=" + getFormattedValue(amount, 2) + ", Fixed Amount=" - + getFormattedValue(fixed, 2)); - } - - - private void decodeChangeTempBasalType(PumpHistoryEntry entry) { - entry.addDecodedData("isPercent", ByteUtil.asUINT8(entry.getRawDataByIndex(0)) == 1); // index moved from 1 -> 0 - } - - - private void decodeBgReceived(PumpHistoryEntry entry) { - entry.addDecodedData("amount", (ByteUtil.asUINT8(entry.getRawDataByIndex(0)) << 3) + (ByteUtil.asUINT8(entry.getRawDataByIndex(3)) >> 5)); - entry.addDecodedData("meter", ByteUtil.substring(entry.getRawData(), 6, 3)); // index moved from 1 -> 0 - } - - - private void decodeCalBGForPH(PumpHistoryEntry entry) { - entry.addDecodedData("amount", ((ByteUtil.asUINT8(entry.getRawDataByIndex(5)) & 0x80) << 1) + ByteUtil.asUINT8(entry.getRawDataByIndex(0))); // index moved from 1 -> 0 - } - - - private void decodeNoDeliveryAlarm(PumpHistoryEntry entry) { - //rawtype = asUINT8(data[1]); - // not sure if this is actually NoDelivery Alarm? - } - - - @Override - public void postProcess() { - } - - - @Override - protected void runPostDecodeTasks() { - this.showStatistics(); - } - - - private void decodeBolus(PumpHistoryEntry entry) { - BolusDTO bolus = new BolusDTO(); - - byte[] data = entry.getHead(); - - if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) { - bolus.setRequestedAmount(ByteUtil.toInt(data[0], data[1]) / 40.0d); - bolus.setDeliveredAmount(ByteUtil.toInt(data[2], data[3]) / 40.0d); - bolus.setInsulinOnBoard(ByteUtil.toInt(data[4], data[5]) / 40.0d); - bolus.setDuration(data[6] * 30); - } else { - bolus.setRequestedAmount(ByteUtil.asUINT8(data[0]) / 10.0d); - bolus.setDeliveredAmount(ByteUtil.asUINT8(data[1]) / 10.0d); - bolus.setDuration(ByteUtil.asUINT8(data[2]) * 30); - } - - bolus.setBolusType((bolus.getDuration() != null && (bolus.getDuration() > 0)) ? PumpBolusType.Extended - : PumpBolusType.Normal); - bolus.setAtechDateTime(entry.atechDateTime); - - entry.addDecodedData("Object", bolus); - entry.setDisplayableValue(bolus.getDisplayableValue()); - - } - - - private void decodeTempBasal(PumpHistoryEntry entry) { - - if (this.tbrPreviousRecord == null) { - // LOG.debug(this.tbrPreviousRecord.toString()); - this.tbrPreviousRecord = entry; - return; - } - - decodeTempBasal(this.tbrPreviousRecord, entry); - - tbrPreviousRecord = null; - } - - - public void decodeTempBasal(PumpHistoryEntry tbrPreviousRecord, PumpHistoryEntry entry) { - - PumpHistoryEntry tbrRate = null, tbrDuration = null; - - if (entry.getEntryType() == PumpHistoryEntryType.TempBasalRate) { - tbrRate = entry; - } else { - tbrDuration = entry; - } - - if (tbrRate != null) { - tbrDuration = tbrPreviousRecord; - } else { - tbrRate = tbrPreviousRecord; - } - -// TempBasalPair tbr = new TempBasalPair( -// tbrRate.getHead()[0], -// tbrDuration.getHead()[0], -// (ByteUtil.asUINT8(tbrRate.getDatetime()[4]) >> 3) == 0); - - TempBasalPair tbr = new TempBasalPair( - tbrRate.getHead()[0], - tbrRate.getBody()[0], - tbrDuration.getHead()[0], - (ByteUtil.asUINT8(tbrRate.getDatetime()[4]) >> 3) == 0); - - // System.out.println("TBR: amount=" + tbr.getInsulinRate() + ", duration=" + tbr.getDurationMinutes() - // // + " min. Packed: " + tbr.getValue() - // ); - - entry.addDecodedData("Object", tbr); - entry.setDisplayableValue(tbr.getDescription()); - - } - - - private void decodeDateTime(PumpHistoryEntry entry) { - byte[] dt = entry.getDatetime(); - - if (dt == null) { - aapsLogger.warn(LTag.PUMPBTCOMM, "DateTime not set."); - } - - if (entry.getDateTimeLength() == 5) { - - int seconds = dt[0] & 0x3F; - int minutes = dt[1] & 0x3F; - int hour = dt[2] & 0x1F; - - int month = ((dt[0] >> 4) & 0x0c) + ((dt[1] >> 6) & 0x03); - // ((dt[0] & 0xC0) >> 6) | ((dt[1] & 0xC0) >> 4); - - int dayOfMonth = dt[3] & 0x1F; - int year = fix2DigitYear(dt[4] & 0x3F); // Assuming this is correct, need to verify. Otherwise this will be - // a problem in 2016. - - entry.setAtechDateTime(DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds)); - - } else if (entry.getDateTimeLength() == 2) { - int low = ByteUtil.asUINT8(dt[0]) & 0x1F; - int mhigh = (ByteUtil.asUINT8(dt[0]) & 0xE0) >> 4; - int mlow = (ByteUtil.asUINT8(dt[1]) & 0x80) >> 7; - int month = mhigh + mlow; - // int dayOfMonth = low + 1; - int dayOfMonth = dt[0] & 0x1F; - int year = 2000 + (ByteUtil.asUINT8(dt[1]) & 0x7F); - - int hour = 0; - int minutes = 0; - int seconds = 0; - - //LOG.debug("DT: {} {} {}", year, month, dayOfMonth); - - if (dayOfMonth == 32) { - aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Entry: Day 32 %s = [%s] %s", entry.getEntryType().name(), - ByteUtil.getHex(entry.getRawData()), entry)); - } - - if (isEndResults(entry.getEntryType())) { - hour = 23; - minutes = 59; - seconds = 59; - } - - entry.setAtechDateTime(DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds)); - - } else { - aapsLogger.warn(LTag.PUMPBTCOMM, "Unknown datetime format: " + entry.getDateTimeLength()); - } - - } - - - private boolean isEndResults(PumpHistoryEntryType entryType) { - - return (entryType == PumpHistoryEntryType.EndResultTotals || - entryType == PumpHistoryEntryType.DailyTotals515 || - entryType == PumpHistoryEntryType.DailyTotals522 || - entryType == PumpHistoryEntryType.DailyTotals523); - } - - - private int fix2DigitYear(int year) { - if (year > 90) { - year += 1900; - } else { - year += 2000; - } - - return year; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.kt new file mode 100644 index 0000000000..3152c423da --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.kt @@ -0,0 +1,534 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump + +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.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType.Companion.getByCode +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusWizardDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.experimental.and + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +@Singleton +class MedtronicPumpHistoryDecoder @Inject constructor( + aapsLogger: AAPSLogger, + medtronicUtil: MedtronicUtil, + bitUtils: ByteUtil +) : MedtronicHistoryDecoder(aapsLogger, medtronicUtil, bitUtils) { + + //private var tbrPreviousRecord: PumpHistoryEntry? = null + private var changeTimeRecord: PumpHistoryEntry? = null + + override fun createRecords(dataClearInput: MutableList): MutableList { + prepareStatistics() + var counter = 0 + var record = 0 + var incompletePacket: Boolean + val outList: MutableList = mutableListOf() + var skipped: String? = null + if (dataClearInput.size == 0) { + aapsLogger.error(LTag.PUMPBTCOMM, "Empty page.") + return outList + } + do { + val opCode: Int = dataClearInput[counter].toInt() + var special = false + incompletePacket = false + var skippedRecords = false + if (opCode == 0) { + counter++ + if (skipped == null) skipped = "0x00" else skipped += " 0x00" + continue + } else { + if (skipped != null) { + aapsLogger.warn(LTag.PUMPBTCOMM, " ... Skipped $skipped") + skipped = null + skippedRecords = true + } + } + if (skippedRecords) { + aapsLogger.error(LTag.PUMPBTCOMM, "We had some skipped bytes, which might indicate error in pump history. Please report this problem.") + } + val entryType = getByCode(opCode.toByte()) + val pe = PumpHistoryEntry() + pe.setEntryType(medtronicUtil.medtronicPumpModel, entryType, if (entryType == PumpHistoryEntryType.UnknownBasePacket) opCode.toByte() else null) + pe.offset = counter + counter++ + if (counter >= 1022) { + break + } + val listRawData: MutableList = ArrayList() + listRawData.add(opCode.toByte()) + if (entryType === PumpHistoryEntryType.UnabsorbedInsulin + || entryType === PumpHistoryEntryType.UnabsorbedInsulin512) { + val elements: Int = dataClearInput[counter].toInt() + listRawData.add(elements.toByte()) + counter++ + val els = getUnsignedInt(elements) + for (k in 0 until els - 2) { + if (counter < 1022) { + listRawData.add(dataClearInput[counter]) + counter++ + } + } + special = true + } else { + for (j in 0 until entryType.getTotalLength(medtronicUtil.medtronicPumpModel) - 1) { + try { + listRawData.add(dataClearInput[counter]) + counter++ + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPBTCOMM, "OpCode: " + ByteUtil.shortHexString(opCode.toByte()) + ", Invalid package: " + + ByteUtil.getHex(listRawData)) + // throw ex; + incompletePacket = true + break + } + } + if (incompletePacket) break + } + if (entryType === PumpHistoryEntryType.None) { + aapsLogger.error(LTag.PUMPBTCOMM, "Error in code. We should have not come into this branch.") + } else { + if (pe.entryType === PumpHistoryEntryType.UnknownBasePacket) { + pe.opCode = opCode.toByte() + } + if (entryType.getHeadLength(medtronicUtil.medtronicPumpModel) == 0) special = true + pe.setData(listRawData, special) + val decoded = decodeRecord(pe) + if (decoded === RecordDecodeStatus.OK || decoded === RecordDecodeStatus.Ignored) { + //Log.i(TAG, "#" + record + " " + decoded.getDescription() + " " + pe); + } else { + aapsLogger.warn(LTag.PUMPBTCOMM, "#" + record + " " + decoded.description + " " + pe) + } + addToStatistics(pe, decoded, null) + record++ + if (decoded === RecordDecodeStatus.OK) // we add only OK records, all others are ignored + { + outList.add(pe) + } + } + } while (counter < dataClearInput.size) + return outList + } + + override fun decodeRecord(record: PumpHistoryEntry): RecordDecodeStatus { + return try { + decodeRecordInternal(record) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, " Error decoding: type=%s, ex=%s", record.entryType.name, ex.message, ex)) + //ex.printStackTrace() + RecordDecodeStatus.Error + } + } + + private fun decodeRecordInternal(entry: PumpHistoryEntry): RecordDecodeStatus { + if (entry.dateTimeLength > 0) { + decodeDateTime(entry) + } + return when (entry.entryType) { + PumpHistoryEntryType.ChangeBasalPattern, + PumpHistoryEntryType.CalBGForPH, + PumpHistoryEntryType.ChangeRemoteId, + PumpHistoryEntryType.ClearAlarm, + PumpHistoryEntryType.ChangeAlarmNotifyMode, + PumpHistoryEntryType.EnableDisableRemote, + PumpHistoryEntryType.BGReceived, + PumpHistoryEntryType.SensorAlert, + PumpHistoryEntryType.ChangeTimeFormat, + PumpHistoryEntryType.ChangeReservoirWarningTime, + PumpHistoryEntryType.ChangeBolusReminderEnable, + PumpHistoryEntryType.SetBolusReminderTime, + PumpHistoryEntryType.ChangeChildBlockEnable, + PumpHistoryEntryType.BolusWizardEnabled, + PumpHistoryEntryType.ChangeBGReminderOffset, + PumpHistoryEntryType.ChangeAlarmClockTime, + PumpHistoryEntryType.ChangeMeterId, + PumpHistoryEntryType.ChangeParadigmID, + PumpHistoryEntryType.JournalEntryMealMarker, + PumpHistoryEntryType.JournalEntryExerciseMarker, + PumpHistoryEntryType.DeleteBolusReminderTime, + PumpHistoryEntryType.SetAutoOff, + PumpHistoryEntryType.SelfTest, + PumpHistoryEntryType.JournalEntryInsulinMarker, + PumpHistoryEntryType.JournalEntryOtherMarker, + PumpHistoryEntryType.BolusWizardSetup512, + PumpHistoryEntryType.ChangeSensorSetup2, + PumpHistoryEntryType.ChangeSensorAlarmSilenceConfig, + PumpHistoryEntryType.ChangeSensorRateOfChangeAlertSetup, + PumpHistoryEntryType.ChangeBolusScrollStepSize, + PumpHistoryEntryType.BolusWizardSetup, + PumpHistoryEntryType.ChangeVariableBolus, + PumpHistoryEntryType.ChangeAudioBolus, + PumpHistoryEntryType.ChangeBGReminderEnable, + PumpHistoryEntryType.ChangeAlarmClockEnable, + PumpHistoryEntryType.BolusReminder, + PumpHistoryEntryType.DeleteAlarmClockTime, + PumpHistoryEntryType.ChangeCarbUnits, + PumpHistoryEntryType.ChangeWatchdogEnable, + PumpHistoryEntryType.ChangeOtherDeviceID, + PumpHistoryEntryType.ReadOtherDevicesIDs, + PumpHistoryEntryType.BGReceived512, + PumpHistoryEntryType.SensorStatus, + PumpHistoryEntryType.ReadCaptureEventEnabled, + PumpHistoryEntryType.ChangeCaptureEventEnable, + PumpHistoryEntryType.ReadOtherDevicesStatus -> RecordDecodeStatus.OK + + PumpHistoryEntryType.Sensor_0x54, + PumpHistoryEntryType.Sensor_0x55, + PumpHistoryEntryType.Sensor_0x51, + PumpHistoryEntryType.Sensor_0x52, + PumpHistoryEntryType.EventUnknown_MM512_0x2e -> { + aapsLogger.debug(LTag.PUMPBTCOMM, " -- ignored Unknown Pump Entry: $entry") + RecordDecodeStatus.Ignored + } + + PumpHistoryEntryType.UnabsorbedInsulin, + PumpHistoryEntryType.UnabsorbedInsulin512 -> RecordDecodeStatus.Ignored + PumpHistoryEntryType.DailyTotals522, + PumpHistoryEntryType.DailyTotals523, + PumpHistoryEntryType.DailyTotals515, + PumpHistoryEntryType.EndResultTotals -> decodeDailyTotals(entry) + PumpHistoryEntryType.ChangeBasalProfile_OldProfile, + PumpHistoryEntryType.ChangeBasalProfile_NewProfile -> decodeBasalProfile(entry) + PumpHistoryEntryType.BasalProfileStart -> decodeBasalProfileStart(entry) + + PumpHistoryEntryType.ChangeTime -> { + changeTimeRecord = entry + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.NewTimeSet -> { + decodeChangeTime(entry) + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.TempBasalDuration -> // decodeTempBasal(entry); + RecordDecodeStatus.OK + + PumpHistoryEntryType.TempBasalRate -> // decodeTempBasal(entry); + RecordDecodeStatus.OK + + PumpHistoryEntryType.Bolus -> { + decodeBolus(entry) + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.BatteryChange -> { + decodeBatteryActivity(entry) + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.LowReservoir -> { + decodeLowReservoir(entry) + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.LowBattery, + PumpHistoryEntryType.SuspendPump, + PumpHistoryEntryType.ResumePump, + PumpHistoryEntryType.Rewind, + PumpHistoryEntryType.NoDeliveryAlarm, + PumpHistoryEntryType.ChangeTempBasalType, + PumpHistoryEntryType.ChangeMaxBolus, + PumpHistoryEntryType.ChangeMaxBasal, + PumpHistoryEntryType.ClearSettings, + PumpHistoryEntryType.SaveSettings -> RecordDecodeStatus.OK + PumpHistoryEntryType.BolusWizard -> decodeBolusWizard(entry) + PumpHistoryEntryType.BolusWizard512 -> decodeBolusWizard512(entry) + + PumpHistoryEntryType.Prime -> { + decodePrime(entry) + RecordDecodeStatus.OK + } + + PumpHistoryEntryType.TempBasalCombined -> RecordDecodeStatus.Ignored + PumpHistoryEntryType.None, PumpHistoryEntryType.UnknownBasePacket -> RecordDecodeStatus.Error + + else -> { + aapsLogger.debug(LTag.PUMPBTCOMM, "Not supported: " + entry.entryType) + RecordDecodeStatus.NotSupported + } + } + } + + private fun decodeDailyTotals(entry: PumpHistoryEntry): RecordDecodeStatus { + entry.addDecodedData("Raw Data", ByteUtil.getHex(entry.rawData)) + val totals = DailyTotalsDTO(entry) + entry.addDecodedData("Object", totals) + return RecordDecodeStatus.OK + } + + private fun decodeBasalProfile(entry: PumpHistoryEntry): RecordDecodeStatus { + val basalProfile = BasalProfile(aapsLogger) + basalProfile.setRawDataFromHistory(entry.body) + entry.addDecodedData("Object", basalProfile) + return RecordDecodeStatus.OK + } + + private fun decodeChangeTime(entry: PumpHistoryEntry) { + if (changeTimeRecord == null) return + entry.displayableValue = entry.dateTimeString + changeTimeRecord = null + } + + private fun decodeBatteryActivity(entry: PumpHistoryEntry) { + entry.displayableValue = if (entry.head[0] == 0.toByte()) "Battery Removed" else "Battery Replaced" + } + + private fun decodeBasalProfileStart(entry: PumpHistoryEntry): RecordDecodeStatus { + val body = entry.body + val offset = body[0] * 1000 * 30 * 60 + var rate: Float? = null + val index = entry.head[0].toInt() + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + rate = body[1] * 0.025f + } + + //LOG.info("Basal Profile Start: offset={}, rate={}, index={}, body_raw={}", offset, rate, index, body); + return if (rate == null) { + aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Basal Profile Start (ERROR): offset=%d, rate=%.3f, index=%d, body_raw=%s", offset, rate, index, ByteUtil.getHex(body))) + RecordDecodeStatus.Error + } else { + entry.addDecodedData("Value", getFormattedFloat(rate, 3)) + entry.displayableValue = getFormattedFloat(rate, 3) + RecordDecodeStatus.OK + } + } + + private fun decodeBolusWizard(entry: PumpHistoryEntry): RecordDecodeStatus { + val body = entry.body + val dto = BolusWizardDTO() + var bolusStrokes = 10.0f + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + // https://github.com/ps2/minimed_rf/blob/master/lib/minimed_rf/log_entries/bolus_wizard.rb#L102 + bolusStrokes = 40.0f + dto.carbs = ((body[1] and 0x0c.toByte()).toInt() shl 6) + body[0] + dto.bloodGlucose = ((body[1] and 0x03).toInt() shl 8) + entry.head[0] + dto.carbRatio = body[1] / 10.0f + // carb_ratio (?) = (((self.body[2] & 0x07) << 8) + self.body[3]) / + // 10.0s + dto.insulinSensitivity = body[4].toFloat() + dto.bgTargetLow = body[5].toInt() + dto.bgTargetHigh = body[14].toInt() + dto.correctionEstimate = (((body[9] and 0x38).toInt() shl 5) + body[6]) / bolusStrokes + dto.foodEstimate = ((body[7].toInt() shl 8) + body[8]) / bolusStrokes + dto.unabsorbedInsulin = ((body[10].toInt() shl 8) + body[11]) / bolusStrokes + dto.bolusTotal = ((body[12].toInt() shl 8) + body[13]) / bolusStrokes + } else { + dto.bloodGlucose = (body.get(1) and 0x0F).toInt() shl 8 or entry.head.get(0).toInt() + dto.carbs = body[0].toInt() + dto.carbRatio = body[2].toFloat() + dto.insulinSensitivity = body[3].toFloat() + dto.bgTargetLow = body.get(4).toInt() + dto.bgTargetHigh = body.get(12).toInt() + dto.bolusTotal = body.get(11) / bolusStrokes + dto.foodEstimate = body.get(6) / bolusStrokes + dto.unabsorbedInsulin = body.get(9) / bolusStrokes + dto.bolusTotal = body.get(11) / bolusStrokes + dto.correctionEstimate = (body.get(7) + (body.get(5) and 0x0F)) / bolusStrokes + } + if (dto.bloodGlucose < 0) { + dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.toByte()) + } + dto.atechDateTime = entry.atechDateTime + entry.addDecodedData("Object", dto) + entry.displayableValue = dto.displayableValue + return RecordDecodeStatus.OK + } + + private fun decodeBolusWizard512(entry: PumpHistoryEntry): RecordDecodeStatus { + val body = entry.body + val dto = BolusWizardDTO() + val bolusStrokes = 10.0f + dto.bloodGlucose = (body.get(1) and 0x03).toInt() shl 8 or entry.head.get(0).toInt() + dto.carbs = body.get(1).toInt() and 0xC shl 6 or body.get(0).toInt() // (int)body[0]; + dto.carbRatio = body.get(2).toFloat() + dto.insulinSensitivity = body.get(3).toFloat() + dto.bgTargetLow = body.get(4).toInt() + dto.foodEstimate = body.get(6) / 10.0f + dto.correctionEstimate = (body.get(7) + (body.get(5) and 0x0F)) / bolusStrokes + dto.unabsorbedInsulin = body.get(9) / bolusStrokes + dto.bolusTotal = body.get(11) / bolusStrokes + dto.bgTargetHigh = dto.bgTargetLow + if (dto.bloodGlucose < 0) { + dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.toByte()) + } + dto.atechDateTime = entry.atechDateTime + entry.addDecodedData("Object", dto) + entry.displayableValue = dto.displayableValue + return RecordDecodeStatus.OK + } + + private fun decodeLowReservoir(entry: PumpHistoryEntry) { + val amount = getUnsignedInt(entry.head.get(0)) * 1.0f / 10.0f * 2 + entry.displayableValue = getFormattedValue(amount, 1) + } + + private fun decodePrime(entry: PumpHistoryEntry) { + val amount = ByteUtil.toInt(entry.head.get(2), entry.head.get(3)) / 10.0f + val fixed = ByteUtil.toInt(entry.head.get(0), entry.head.get(1)) / 10.0f + +// amount = (double)(asUINT8(data[4]) << 2) / 40.0; +// programmedAmount = (double)(asUINT8(data[2]) << 2) / 40.0; +// primeType = programmedAmount == 0 ? "manual" : "fixed"; + entry.addDecodedData("Amount", amount) + entry.addDecodedData("FixedAmount", fixed) + entry.displayableValue = ("Amount=" + getFormattedValue(amount, 2) + ", Fixed Amount=" + + getFormattedValue(fixed, 2)) + } + + private fun decodeChangeTempBasalType(entry: PumpHistoryEntry) { + entry.addDecodedData("isPercent", ByteUtil.asUINT8(entry.getRawDataByIndex(0)) == 1) // index moved from 1 -> 0 + } + + private fun decodeBgReceived(entry: PumpHistoryEntry) { + entry.addDecodedData("amount", (ByteUtil.asUINT8(entry.getRawDataByIndex(0)) shl 3) + (ByteUtil.asUINT8(entry.getRawDataByIndex(3)) shr 5)) + entry.addDecodedData("meter", ByteUtil.substring(entry.rawData, 6, 3)) // index moved from 1 -> 0 + } + + private fun decodeCalBGForPH(entry: PumpHistoryEntry) { + entry.addDecodedData("amount", (ByteUtil.asUINT8(entry.getRawDataByIndex(5)) and 0x80 shl 1) + ByteUtil.asUINT8(entry.getRawDataByIndex(0))) // index moved from 1 -> 0 + } + + // private fun decodeNoDeliveryAlarm(entry: PumpHistoryEntry) { + // //rawtype = asUINT8(data[1]); + // // not sure if this is actually NoDelivery Alarm? + // } + + override fun postProcess() {} + + override fun runPostDecodeTasks() { + showStatistics() + } + + private fun decodeBolus(entry: PumpHistoryEntry) { + val bolus: BolusDTO? + val data = entry.head + if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + bolus = BolusDTO(atechDateTime = entry.atechDateTime, + requestedAmount = ByteUtil.toInt(data.get(0), data.get(1)) / 40.0, + deliveredAmount = ByteUtil.toInt(data.get(2), data.get(3)) / 40.0, + duration = data.get(6) * 30) + bolus.insulinOnBoard = ByteUtil.toInt(data.get(4), data.get(5)) / 40.0 + } else { + bolus = BolusDTO(atechDateTime = entry.atechDateTime, + requestedAmount = ByteUtil.asUINT8(data.get(0)) / 10.0, + deliveredAmount = ByteUtil.asUINT8(data.get(1)) / 10.0, + duration = ByteUtil.asUINT8(data.get(2)) * 30) + } + bolus.bolusType = if (bolus.duration > 0) PumpBolusType.Extended else PumpBolusType.Normal + entry.addDecodedData("Object", bolus) + entry.displayableValue = bolus.displayableValue + } + + fun decodeTempBasal(tbrPreviousRecord: PumpHistoryEntry, entry: PumpHistoryEntry) { + var tbrRate: PumpHistoryEntry? = null + var tbrDuration: PumpHistoryEntry? = null + if (entry.entryType === PumpHistoryEntryType.TempBasalRate) { + tbrRate = entry + } else { + tbrDuration = entry + } + if (tbrRate != null) { + tbrDuration = tbrPreviousRecord + } else { + tbrRate = tbrPreviousRecord + } + + val tbr = TempBasalPair( + tbrRate.head.get(0), + tbrRate.body.get(0), + tbrDuration!!.head.get(0).toInt(), + ByteUtil.asUINT8(tbrRate.datetime.get(4)) shr 3 == 0) + + entry.addDecodedData("Object", tbr) + entry.displayableValue = tbr.description + } + + private fun decodeDateTime(entry: PumpHistoryEntry) { + if (entry.datetime.size == 0) { + aapsLogger.warn(LTag.PUMPBTCOMM, "DateTime not set.") + } + val dt = entry.datetime + if (entry.dateTimeLength == 5) { + val seconds: Int = (dt.get(0) and 0x3F.toByte()).toInt() + val minutes: Int = (dt.get(1) and 0x3F.toByte()).toInt() + val hour: Int = (dt.get(2) and 0x1F).toInt() + val month: Int = (dt.get(0).toInt() shr 4 and 0x0c) + (dt.get(1).toInt() shr 6 and 0x03) + // ((dt[0] & 0xC0) >> 6) | ((dt[1] & 0xC0) >> 4); + val dayOfMonth: Int = (dt.get(3) and 0x1F).toInt() + val year = fix2DigitYear((dt.get(4) and 0x3F.toByte()).toInt()) // Assuming this is correct, need to verify. Otherwise this will be + // a problem in 2016. + entry.atechDateTime = DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds) + } else if (entry.dateTimeLength == 2) { + //val low = ByteUtil.asUINT8(dt.get(0)) and 0x1F + val mhigh = ByteUtil.asUINT8(dt.get(0)) and 0xE0 shr 4 + val mlow = ByteUtil.asUINT8(dt.get(1)) and 0x80 shr 7 + val month = mhigh + mlow + // int dayOfMonth = low + 1; + val dayOfMonth: Int = (dt.get(0) and 0x1F).toInt() + val year = 2000 + (ByteUtil.asUINT8(dt.get(1)) and 0x7F) + var hour = 0 + var minutes = 0 + var seconds = 0 + + //LOG.debug("DT: {} {} {}", year, month, dayOfMonth); + if (dayOfMonth == 32) { + aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "Entry: Day 32 %s = [%s] %s", entry.entryType.name, + ByteUtil.getHex(entry.rawData), entry)) + } + if (isEndResults(entry.entryType)) { + hour = 23 + minutes = 59 + seconds = 59 + } + entry.atechDateTime = DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds) + } else { + aapsLogger.warn(LTag.PUMPBTCOMM, "Unknown datetime format: " + entry.dateTimeLength) + } + } + + private fun isEndResults(entryType: PumpHistoryEntryType?): Boolean { + return entryType === PumpHistoryEntryType.EndResultTotals || + entryType === PumpHistoryEntryType.DailyTotals515 || + entryType === PumpHistoryEntryType.DailyTotals522 || + entryType === PumpHistoryEntryType.DailyTotals523 + } + + private fun fix2DigitYear(year: Int): Int { + var yearInternal = year + yearInternal += if (yearInternal > 90) { + 1900 + } else { + 2000 + } + return yearInternal + } + + companion object { + + private fun getFormattedValue(value: Float, decimals: Int): String { + return String.format(Locale.ENGLISH, "%." + decimals + "f", value) + } + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java deleted file mode 100644 index 3866e3e6ba..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java +++ /dev/null @@ -1,174 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; - -import com.google.gson.annotations.Expose; - -import java.util.Objects; - -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public class PumpHistoryEntry extends MedtronicHistoryEntry { - - @Expose - private PumpHistoryEntryType entryType; - private Integer opCode; // this is set only when we have unknown entry... - private int offset; - private String displayableValue = ""; - - - public PumpHistoryEntryType getEntryType() { - return entryType; - } - - - public void setEntryType(MedtronicDeviceType medtronicDeviceType, PumpHistoryEntryType entryType) { - this.entryType = entryType; - - this.sizes[0] = entryType.getHeadLength(medtronicDeviceType); - this.sizes[1] = entryType.getDateLength(); - this.sizes[2] = entryType.getBodyLength(medtronicDeviceType); - - if (this.entryType != null && this.atechDateTime != null) - setPumpId(); - } - - - private void setPumpId() { - this.pumpId = this.entryType.getCode() + (this.atechDateTime * 1000L); - } - - - @Override - public int getOpCode() { - if (opCode == null) - return entryType.getOpCode(); - else - return opCode; - } - - - public void setOpCode(Integer opCode) { - this.opCode = opCode; - } - - - @Override - public String getToStringStart() { - return "PumpHistoryEntry [type=" + StringUtil.getStringInLength(entryType.name(), 20) + " [" - + StringUtil.getStringInLength("" + getOpCode(), 3) + ", 0x" - + ByteUtil.shortHexString((byte) getOpCode()) + "]"; - } - - - public String toString() { - return super.toString(); -// Object object = this.getDecodedDataEntry("Object"); -// -// if (object == null) { -// return super.toString(); -// } else { -// return super.toString() + "PumpHistoryEntry [type=" + StringUtil.getStringInLength(entryType.name(), 20) + ", DT: " + DT + ", Object=" + object.toString() + "]"; -// } - } - - - public int getOffset() { - return offset; - } - - - public void setOffset(int offset) { - this.offset = offset; - } - - - @Override - public String getEntryTypeName() { - return this.entryType.name(); - } - - - @Override - public int getDateLength() { - return this.entryType.getDateLength(); - } - - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - - if (!(o instanceof PumpHistoryEntry)) - return false; - - PumpHistoryEntry that = (PumpHistoryEntry) o; - - return entryType == that.entryType && // - this.atechDateTime == that.atechDateTime; // && // - // Objects.equals(this.decodedData, that.decodedData); - } - - - @Override - public int hashCode() { - return Objects.hash(entryType, opCode, offset); - } - - - // public boolean isAfter(LocalDateTime dateTimeIn) { - // // LOG.debug("Entry: " + this.dateTime); - // // LOG.debug("Datetime: " + dateTimeIn); - // // LOG.debug("Item after: " + this.dateTime.isAfter(dateTimeIn)); - // return this.dateTime.isAfter(dateTimeIn); - // } - - public boolean isAfter(long atechDateTime) { - if (this.atechDateTime == null) { - LOG.error("Date is null. Show object: " + toString()); - return false; // FIXME shouldn't happen - } - - return atechDateTime < this.atechDateTime; - } - - - public void setDisplayableValue(String displayableValue) { - this.displayableValue = displayableValue; - } - - - public String getDisplayableValue() { - return displayableValue; - } - - public static class Comparator implements java.util.Comparator { - - @Override - public int compare(PumpHistoryEntry o1, PumpHistoryEntry o2) { - int data = (int) (o2.atechDateTime - o1.atechDateTime); - - if (data != 0) - return data; - - return o2.getEntryType().getCode() - o1.getEntryType().getCode(); - } - } - - - public Long getPumpId() { - setPumpId(); - - return pumpId; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.kt new file mode 100644 index 0000000000..ea6d521e74 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.kt @@ -0,0 +1,137 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump + +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import java.util.* + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +class PumpHistoryEntry : MedtronicHistoryEntry() { + + @Expose + var entryType: PumpHistoryEntryType = PumpHistoryEntryType.None + private set + + override var opCode: Byte? = null + // this is set only when we have unknown entry... + get() = if (field == null) entryType.code else field + set(value) { + field = value + } + + var offset = 0 + var displayableValue = "" + get() = field + set(value) { + field = value + } + + fun setEntryType(medtronicDeviceType: MedtronicDeviceType, entryType: PumpHistoryEntryType, opCode: Byte? = null) { + this.entryType = entryType + sizes[0] = entryType.getHeadLength(medtronicDeviceType) + sizes[1] = entryType.dateLength + sizes[2] = entryType.getBodyLength(medtronicDeviceType) + if (isEntryTypeSet() && atechDateTime != 0L) pumpId = generatePumpId() + this.opCode = opCode + } + + override fun generatePumpId(): Long { + return entryType.code + atechDateTime * 1000L + } + + override fun isEntryTypeSet(): Boolean { + return this.entryType != PumpHistoryEntryType.None + } + + override val toStringStart: String + get() = ("PumpHistoryEntry [type=" + StringUtil.getStringInLength(entryType.name, 20) + " [" + + StringUtil.getStringInLength("" + opCode, 3) + ", 0x" + + ByteUtil.shortHexString(opCode!!) + "]") + + override fun toString(): String { + return super.toString() + // Object object = this.getDecodedDataEntry("Object"); +// +// if (object == null) { +// return super.toString(); +// } else { +// return super.toString() + "PumpHistoryEntry [type=" + StringUtil.getStringInLength(entryType.name(), 20) + ", DT: " + DT + ", Object=" + object.toString() + "]"; +// } + } + + override val entryTypeName: String + get() = entryType.name + + override val dateLength: Int + get() = entryType.dateLength + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PumpHistoryEntry) return false + val that = other //as PumpHistoryEntry + return this.pumpId == that.pumpId + // return entryType == that.entryType && // + // atechDateTime === that.atechDateTime // && // + // Objects.equals(this.decodedData, that.decodedData); + } + + override fun hashCode(): Int { + return Objects.hash(entryType, opCode, offset) + } + + // public boolean isAfter(LocalDateTime dateTimeIn) { + // // LOG.debug("Entry: " + this.dateTime); + // // LOG.debug("Datetime: " + dateTimeIn); + // // LOG.debug("Item after: " + this.dateTime.isAfter(dateTimeIn)); + // return this.dateTime.isAfter(dateTimeIn); + // } + fun isAfter(atechDateTime: Long): Boolean { + if (this.atechDateTime == 0L) { + // Log.e("PumpHistoryEntry", "Date is null. Show object: " + toString()) + return false // FIXME shouldn't happen + } + return atechDateTime < this.atechDateTime + } + + class Comparator : java.util.Comparator { + + override fun compare(o1: PumpHistoryEntry, o2: PumpHistoryEntry): Int { + val data = (o2.atechDateTime - o1.atechDateTime).toInt() + return if (data != 0) data else o2.entryType.code - o1.entryType.code + } + } + + override var pumpId: Long = 0L + get() { + if (field == 0L) { + field = generatePumpId() + } + return field + } + set(pumpId) { + field = pumpId + } + + fun hasBolusChanged(entry: PumpHistoryEntry): Boolean { + if (entryType == PumpHistoryEntryType.Bolus) { + val thisOne: BolusDTO = this.decodedData["Object"] as BolusDTO + + if (entry.entryType == PumpHistoryEntryType.Bolus) { + val otherOne: BolusDTO = entry.decodedData["Object"] as BolusDTO + return (thisOne.value.equals(otherOne.value)) + } else + return false + } + + return false + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java deleted file mode 100644 index 3a8212d1f9..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java +++ /dev/null @@ -1,378 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public enum PumpHistoryEntryType // implements CodeEnum -{ - // all commented out are probably not the real items - None(0, "None", PumpHistoryEntryGroup.Unknown, 1, 0, 0), - Bolus(0x01, "Bolus", PumpHistoryEntryGroup.Bolus, 4, 5, 0), // 523+[H=8] 9/13 - Prime(0x03, "Prime", PumpHistoryEntryGroup.Prime, 5, 5, 0), // - // /**/EventUnknown_MM522_0x05((byte) 0x05, "Unknown Event 0x05", PumpHistoryEntryGroup.Unknown, 2, 5, 28), // - NoDeliveryAlarm(0x06, "No Delivery", PumpHistoryEntryGroup.Alarm, 4, 5, 0), // - EndResultTotals(0x07, "End Result Totals", PumpHistoryEntryGroup.Statistic, 5, 2, 0), - ChangeBasalProfile_OldProfile(0x08, "Change Basal Profile (Old)", PumpHistoryEntryGroup.Basal, 2, 5, 145), - ChangeBasalProfile_NewProfile(0x09, "Change Basal Profile (New)", PumpHistoryEntryGroup.Basal, 2, 5, 145), // - // /**/EventUnknown_MM512_0x10(0x10, "Unknown Event 0x10", PumpHistoryEntryGroup.Unknown), // 29, 5, 0 - CalBGForPH(0x0a, "BG Capture", PumpHistoryEntryGroup.Glucose), // - SensorAlert(0x0b, "Sensor Alert", PumpHistoryEntryGroup.Alarm, 3, 5, 0), // Ian08 - ClearAlarm(0x0c, "Clear Alarm", PumpHistoryEntryGroup.Alarm, 2, 5, 0), // 2,5,4 - ChangeBasalPattern(0x14, "Change Basal Pattern", PumpHistoryEntryGroup.Basal), // - TempBasalDuration(0x16, "TBR Duration", PumpHistoryEntryGroup.Basal), // - ChangeTime(0x17, "Change Time", PumpHistoryEntryGroup.Configuration), // - NewTimeSet(0x18, "New Time Set", PumpHistoryEntryGroup.Notification), // - LowBattery(0x19, "LowBattery", PumpHistoryEntryGroup.Notification), // - BatteryChange(0x1a, "Battery Change", PumpHistoryEntryGroup.Notification), // - SetAutoOff(0x1b, "Set Auto Off", PumpHistoryEntryGroup.Configuration), // - SuspendPump(0x1e, "Suspend", PumpHistoryEntryGroup.Basal), // - ResumePump(0x1f, "Resume", PumpHistoryEntryGroup.Basal), // - SelfTest(0x20, "Self Test", PumpHistoryEntryGroup.Statistic), // - Rewind(0x21, "Rewind", PumpHistoryEntryGroup.Prime), // - ClearSettings(0x22, "Clear Settings", PumpHistoryEntryGroup.Configuration), // - ChangeChildBlockEnable(0x23, "Change Child Block Enable", PumpHistoryEntryGroup.Configuration), // - ChangeMaxBolus(0x24, "Change Max Bolus", PumpHistoryEntryGroup.Configuration), // - // /**/EventUnknown_MM522_0x25(0x25, "Unknown Event 0x25", PumpHistoryEntryGroup.Unknown), // 8? - EnableDisableRemote(0x26, "Enable/Disable Remote", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // 2, 5, 14 V6:2,5,14 - ChangeRemoteId(0x27, "Change Remote ID", PumpHistoryEntryGroup.Configuration), // ?? - ChangeMaxBasal(0x2c, "Change Max Basal", PumpHistoryEntryGroup.Configuration), // - BolusWizardEnabled(0x2d, "Bolus Wizard Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? - /* TODO */EventUnknown_MM512_0x2e(0x2e, "Unknown Event 0x2e", PumpHistoryEntryGroup.Unknown, 2, 5, 100), // - BolusWizard512(0x2f, "Bolus Wizard (512)", PumpHistoryEntryGroup.Bolus, 2, 5, 12), // - UnabsorbedInsulin512(0x30, "Unabsorbed Insulin (512)", PumpHistoryEntryGroup.Statistic, 5, 0, 0), // FIXME - ChangeBGReminderOffset(0x31, "Change BG Reminder Offset", PumpHistoryEntryGroup.Configuration), // - ChangeAlarmClockTime(0x32, "Change Alarm Clock Time", PumpHistoryEntryGroup.Configuration), // - TempBasalRate(0x33, "TBR Rate", PumpHistoryEntryGroup.Basal, 2, 5, 1), // - LowReservoir(0x34, "Low Reservoir", PumpHistoryEntryGroup.Notification), // - ChangeAlarmClock(0x35, "Change Alarm Clock", PumpHistoryEntryGroup.Configuration), // - ChangeMeterId(0x36, "Change Meter ID", PumpHistoryEntryGroup.Configuration), // - // /**/EventUnknown_MM512_0x37(0x37, "Unknown Event 0x37", PumpHistoryEntryGroup.Unknown), // V:MM512 -// /**/EventUnknown_MM512_0x38(0x38, "Unknown Event 0x38", PumpHistoryEntryGroup.Unknown), // - BGReceived512(0x39, "BG Received (512)", PumpHistoryEntryGroup.Glucose, 2, 5, 3), // - /* TODO */ConfirmInsulinChange(0x3a, "Confirm Insulin Change", PumpHistoryEntryGroup.Unknown), // - SensorStatus(0x3b, "Sensor Status", PumpHistoryEntryGroup.Glucose), // - ChangeParadigmID(0x3c, "Change Paradigm ID", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // V3 ? V6: 2,5,14 ?? is it this length or just 7 - // EventUnknown_MM512_0x3D(0x3d, "Unknown Event 0x3D", PumpHistoryEntryGroup.Unknown), // -// EventUnknown_MM512_0x3E(0x3e, "Unknown Event 0x3E", PumpHistoryEntryGroup.Unknown), // - BGReceived(0x3f, "BG Received", PumpHistoryEntryGroup.Glucose, 2, 5, 3), // Ian3F - JournalEntryMealMarker(0x40, "Meal Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 2), // is size just 7??? V6 - JournalEntryExerciseMarker(0x41, "Exercise Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // ?? - JournalEntryInsulinMarker(0x42, "Insulin Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 0), // V6 = body(0)/was=1 - JournalEntryOtherMarker(0x43, "Other Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // V6 = body(1)/was=0 - EnableSensorAutoCal(0x44, "Enable Sensor AutoCal", PumpHistoryEntryGroup.Glucose), // - // /**/EventUnknown_MM522_0x45(0x45, "Unknown Event 0x45", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x46(0x46, "Unknown Event 0x46", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x47(0x47, "Unknown Event 0x47", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x48(0x48, "Unknown Event 0x48", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x49(0x49, "Unknown Event 0x49", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x4a(0x4a, "Unknown Event 0x4a", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x4b(0x4b, "Unknown Event 0x4b", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_MM522_0x4c(0x4c, "Unknown Event 0x4c", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // -// /**/EventUnknown_0x4d(0x4d, "Unknown Event 0x4d", PumpHistoryEntryGroup.Unknown), // V5: 512: 7, 522: 8 ????NS -// /**/EventUnknown_MM512_0x4e(0x4e, "Unknown Event 0x4e", PumpHistoryEntryGroup.Unknown), // /**/ - BolusWizardSetup512(0x4f, "Bolus Wizard Setup (512)", PumpHistoryEntryGroup.Configuration, 2, 5, 32), // - ChangeSensorSetup2(0x50, "Sensor Setup2", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // Ian50 - /* TODO */Sensor_0x51(0x51, "Unknown Event 0x51", PumpHistoryEntryGroup.Unknown), // - /* TODO */Sensor_0x52(0x52, "Unknown Event 0x52", PumpHistoryEntryGroup.Unknown), // - ChangeSensorAlarmSilenceConfig(0x53, "Sensor Alarm Silence Config", PumpHistoryEntryGroup.Configuration, 2, 5, 1), // 8 - /* TODO */Sensor_0x54(0x54, "Unknown Event 0x54", PumpHistoryEntryGroup.Unknown), // Ian54 - /* TODO */Sensor_0x55(0x55, "Unknown Event 0x55", PumpHistoryEntryGroup.Unknown), // - ChangeSensorRateOfChangeAlertSetup(0x56, "Sensor Rate Of Change Alert Setup", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 - ChangeBolusScrollStepSize(0x57, "Change Bolus Scroll Step Size", PumpHistoryEntryGroup.Configuration), // - BolusWizardSetup(0x5a, "Bolus Wizard Setup (522)", PumpHistoryEntryGroup.Configuration, 2, 5, 117), - // V2: 522+[B=143]; V6: 124, v6: 137, v7: 117/137 [523] - BolusWizard(0x5b, "Bolus Wizard Estimate", PumpHistoryEntryGroup.Configuration, 2, 5, 13), // 15 // - UnabsorbedInsulin(0x5c, "Unabsorbed Insulin", PumpHistoryEntryGroup.Statistic, 5, 0, 0), // head[1] -> body - SaveSettings(0x5d, "Save Settings", PumpHistoryEntryGroup.Configuration), // - ChangeVariableBolus(0x5e, "Change Variable Bolus", PumpHistoryEntryGroup.Configuration), // - ChangeAudioBolus(0x5f, "Easy Bolus Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? - ChangeBGReminderEnable(0x60, "BG Reminder Enable", PumpHistoryEntryGroup.Configuration), // questionable60 - ChangeAlarmClockEnable(0x61, "Alarm Clock Enable", PumpHistoryEntryGroup.Configuration), // - ChangeTempBasalType((byte) 0x62, "Change Basal Type", PumpHistoryEntryGroup.Configuration), // ChangeTempBasalTypePumpEvent - ChangeAlarmNotifyMode(0x63, "Change Alarm Notify Mode", PumpHistoryEntryGroup.Configuration), // - ChangeTimeFormat(0x64, "Change Time Format", PumpHistoryEntryGroup.Configuration), // - ChangeReservoirWarningTime((byte) 0x65, "Change Reservoir Warning Time", PumpHistoryEntryGroup.Configuration), // - ChangeBolusReminderEnable(0x66, "Change Bolus Reminder Enable", PumpHistoryEntryGroup.Configuration), // 9 -> 7 - SetBolusReminderTime((byte) 0x67, "Change Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 - DeleteBolusReminderTime((byte) 0x68, "Delete Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 - BolusReminder(0x69, "Bolus Reminder", PumpHistoryEntryGroup.Configuration, 2, 5, 0), // Ian69 - DeleteAlarmClockTime(0x6a, "Delete Alarm Clock Time", PumpHistoryEntryGroup.Configuration, 2, 5, 7), // 14 - DailyTotals515(0x6c, "Daily Totals (515)", PumpHistoryEntryGroup.Statistic, 1, 2, 35), // v4: 0,0,36. v5: 1,2,33 - DailyTotals522(0x6d, "Daily Totals (522)", PumpHistoryEntryGroup.Statistic, 1, 2, 41), // - DailyTotals523(0x6e, "Daily Totals (523)", PumpHistoryEntryGroup.Statistic, 1, 2, 49), // 1102014-03-17T00:00:00 - ChangeCarbUnits((byte) 0x6f, "Change Carb Units", PumpHistoryEntryGroup.Configuration), // - // /**/EventUnknown_MM522_0x70((byte) 0x70, "Unknown Event 0x70", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // - BasalProfileStart(0x7b, "Basal Profile Start", PumpHistoryEntryGroup.Basal, 2, 5, 3), // // 722 - ChangeWatchdogEnable((byte) 0x7c, "Change Watchdog Enable", PumpHistoryEntryGroup.Configuration), // - ChangeOtherDeviceID((byte) 0x7d, "Change Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // - ChangeWatchdogMarriageProfile(0x81, "Change Watchdog Marriage Profile", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 - DeleteOtherDeviceID(0x82, "Delete Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // - ChangeCaptureEventEnable(0x83, "Change Capture Event Enable", PumpHistoryEntryGroup.Configuration), // - // /**/EventUnknown_MM512_0x88(0x88, "Unknown Event 0x88", PumpHistoryEntryGroup.Unknown), // -// /**/EventUnknown_MM512_0x94(0x94, "Unknown Event 0x94", PumpHistoryEntryGroup.Unknown), // - /**/ IanA8(0xA8, "Ian A8 (Unknown)", PumpHistoryEntryGroup.Unknown, 10, 5, 0), // - // /**/EventUnknown_MM522_0xE8(0xe8, "Unknown Event 0xE8", PumpHistoryEntryGroup.Unknown, 2, 5, 25), // - // F0 - F3 are not in go code, but they are in GGC one and thet are used - ReadOtherDevicesIDs(0xf0, "Read Other Devices IDs", PumpHistoryEntryGroup.Configuration), // ? - ReadCaptureEventEnabled(0xf1, "Read Capture Event Enabled", PumpHistoryEntryGroup.Configuration), // ? - ChangeCaptureEventEnable2(0xf2, "Change Capture Event Enable2", PumpHistoryEntryGroup.Configuration), // ? - ReadOtherDevicesStatus(0xf3, "Read Other Devices Status", PumpHistoryEntryGroup.Configuration), // ? - - TempBasalCombined(0xfe, "TBR", PumpHistoryEntryGroup.Basal), // - UnknownBasePacket(0xff, "Unknown Base Packet", PumpHistoryEntryGroup.Unknown); - - private static final Map opCodeMap = new HashMap<>(); - - static { - for (PumpHistoryEntryType type : values()) { - opCodeMap.put(type.opCode, type); - } - - setSpecialRulesForEntryTypes(); - } - - private final int opCode; - private final String description; - private final int headLength; - private final int dateLength; - // private MinimedDeviceType deviceType; - private final int bodyLength; - private final int totalLength; - // special rules need to be put in list from highest to lowest (e.g.: - // 523andHigher=12, 515andHigher=10 and default (set in cnstr) would be 8) - private List specialRulesHead; - private List specialRulesBody; - private boolean hasSpecialRules = false; - private final PumpHistoryEntryGroup group; - - - PumpHistoryEntryType(int opCode, String name, PumpHistoryEntryGroup group) { - this(opCode, name, group, 2, 5, 0); - } - - - PumpHistoryEntryType(int opCode, PumpHistoryEntryGroup group) { - this(opCode, null, group, 2, 5, 0); - } - - - PumpHistoryEntryType(int opCode, PumpHistoryEntryGroup group, int head, int date, int body) { - this(opCode, null, group, head, date, body); - } - - - PumpHistoryEntryType(int opCode, String name, PumpHistoryEntryGroup group, int head, int date, int body) { - this.opCode = (byte) opCode; - this.description = name; - this.headLength = head; - this.dateLength = date; - this.bodyLength = body; - this.totalLength = (head + date + body); - this.group = group; - } - - - static void setSpecialRulesForEntryTypes() { - EndResultTotals.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 3)); - Bolus.addSpecialRuleHead(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 8)); - BolusWizardSetup.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 137)); - BolusWizard.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 15)); - BolusReminder.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 2)); - ChangeSensorSetup2.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 34)); - } - - - public static PumpHistoryEntryType getByCode(int opCode) { - if (opCodeMap.containsKey(opCode)) { - return opCodeMap.get(opCode); - } else { - return PumpHistoryEntryType.UnknownBasePacket; - } - } - - - // - // private PumpHistoryEntryType(int opCode, String name, int head, int date, - // int body) - // { - // this.opCode = (byte) opCode; - // this.description = name; - // this.headLength = head; - // this.dateLength = date; - // this.bodyLength = body; - // this.totalLength = (head + date + body); - // } - // - - public static boolean isAAPSRelevantEntry(PumpHistoryEntryType entryType) { - return (entryType == PumpHistoryEntryType.Bolus || // Treatments - entryType == PumpHistoryEntryType.TempBasalRate || // - entryType == PumpHistoryEntryType.TempBasalDuration || // - - entryType == PumpHistoryEntryType.Prime || // Pump Status Change - entryType == PumpHistoryEntryType.SuspendPump || // - entryType == PumpHistoryEntryType.ResumePump || // - entryType == PumpHistoryEntryType.Rewind || // - entryType == PumpHistoryEntryType.NoDeliveryAlarm || // no delivery - entryType == PumpHistoryEntryType.BasalProfileStart || // - - entryType == PumpHistoryEntryType.ChangeTime || // Time Change - entryType == PumpHistoryEntryType.NewTimeSet || // - - entryType == PumpHistoryEntryType.ChangeBasalPattern || // Configuration - entryType == PumpHistoryEntryType.ClearSettings || // - entryType == PumpHistoryEntryType.SaveSettings || // - entryType == PumpHistoryEntryType.ChangeMaxBolus || // - entryType == PumpHistoryEntryType.ChangeMaxBasal || // - entryType == PumpHistoryEntryType.ChangeTempBasalType || // - - entryType == PumpHistoryEntryType.ChangeBasalProfile_NewProfile || // Basal profile - - entryType == PumpHistoryEntryType.DailyTotals515 || // Daily Totals - entryType == PumpHistoryEntryType.DailyTotals522 || // - entryType == PumpHistoryEntryType.DailyTotals523 || // - entryType == PumpHistoryEntryType.EndResultTotals); - } - - - public static boolean isRelevantEntry() { - return true; - } - - - public int getCode() { - return this.opCode; - } - - - public int getTotalLength(MedtronicDeviceType medtronicDeviceType) { - if (hasSpecialRules()) { - return getHeadLength(medtronicDeviceType) + getBodyLength(medtronicDeviceType) + getDateLength(); - } else { - return totalLength; - } - } - - - private boolean hasSpecialRules() { - return hasSpecialRules; - } - - - void addSpecialRuleHead(SpecialRule rule) { - if (isEmpty(specialRulesHead)) { - specialRulesHead = new ArrayList<>(); - } - - specialRulesHead.add(rule); - hasSpecialRules = true; - } - - - void addSpecialRuleBody(SpecialRule rule) { - if (isEmpty(specialRulesBody)) { - specialRulesBody = new ArrayList<>(); - } - - specialRulesBody.add(rule); - hasSpecialRules = true; - } - - - public int getOpCode() { - return opCode; - } - - - public String getDescription() { - return this.description == null ? name() : this.description; - } - - - public int getHeadLength(MedtronicDeviceType medtronicDeviceType) { - if (hasSpecialRules) { - if (isNotEmpty(specialRulesHead)) { - return determineSizeByRule(medtronicDeviceType, headLength, specialRulesHead); - } else { - return headLength; - } - } else { - return headLength; - } - } - - - public int getDateLength() { - return dateLength; - } - - - public int getBodyLength(MedtronicDeviceType medtronicDeviceType) { - if (hasSpecialRules) { - if (isNotEmpty(specialRulesBody)) { - return determineSizeByRule(medtronicDeviceType, bodyLength, specialRulesBody); - } else { - return bodyLength; - } - } else { - return bodyLength; - } - } - - - private boolean isNotEmpty(List list) { - return list != null && !list.isEmpty(); - } - - - private boolean isEmpty(List list) { - return list == null || list.isEmpty(); - } - - - // byte[] dh = { 2, 3 }; - - private int determineSizeByRule(MedtronicDeviceType medtronicDeviceType, int defaultValue, List rules) { - int size = defaultValue; - - for (SpecialRule rule : rules) { - if (MedtronicDeviceType.isSameDevice(medtronicDeviceType, rule.deviceType)) { - size = rule.size; - break; - } - } - - return size; - } - - - public PumpHistoryEntryGroup getGroup() { - - return group; - } - - public static class SpecialRule { - - MedtronicDeviceType deviceType; - int size; - - - SpecialRule(MedtronicDeviceType deviceType, int size) { - this.deviceType = deviceType; - this.size = size; - } - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.kt new file mode 100644 index 0000000000..1fc9645f18 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.kt @@ -0,0 +1,267 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump + +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import java.util.* + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class PumpHistoryEntryType // implements CodeEnum +constructor(var code: Byte, + var description: String, + var group: PumpHistoryEntryGroup, + var headLength: Int = 2, + var dateLength: Int = 5, + var bodyLength: Int = 0) { + + // all commented out are probably not the real items + None(0, "None", PumpHistoryEntryGroup.Unknown, 1, 0, 0), + Bolus(0x01, "Bolus", PumpHistoryEntryGroup.Bolus, 4, 5, 0), // 523+[H=8] 9/13 + Prime(0x03, "Prime", PumpHistoryEntryGroup.Prime, 5, 5, 0), // + + // /**/EventUnknown_MM522_0x05((byte) 0x05, "Unknown Event 0x05", PumpHistoryEntryGroup.Unknown, 2, 5, 28), // + NoDeliveryAlarm(0x06, "No Delivery", PumpHistoryEntryGroup.Alarm, 4, 5, 0), // + EndResultTotals(0x07, "End Result Totals", PumpHistoryEntryGroup.Statistic, 5, 2, 0), ChangeBasalProfile_OldProfile(0x08, "Change Basal Profile (Old)", PumpHistoryEntryGroup.Basal, 2, 5, 145), ChangeBasalProfile_NewProfile(0x09, "Change Basal Profile (New)", PumpHistoryEntryGroup.Basal, 2, 5, 145), // + + // /**/EventUnknown_MM512_0x10(0x10, "Unknown Event 0x10", PumpHistoryEntryGroup.Unknown), // 29, 5, 0 + CalBGForPH(0x0a, "BG Capture", PumpHistoryEntryGroup.Glucose), // + SensorAlert(0x0b, "Sensor Alert", PumpHistoryEntryGroup.Alarm, 3, 5, 0), // Ian08 + ClearAlarm(0x0c, "Clear Alarm", PumpHistoryEntryGroup.Alarm, 2, 5, 0), // 2,5,4 + ChangeBasalPattern(0x14, "Change Basal Pattern", PumpHistoryEntryGroup.Basal), // + TempBasalDuration(0x16, "TBR Duration", PumpHistoryEntryGroup.Basal), // + ChangeTime(0x17, "Change Time", PumpHistoryEntryGroup.Configuration), // + NewTimeSet(0x18, "New Time Set", PumpHistoryEntryGroup.Notification), // + LowBattery(0x19, "LowBattery", PumpHistoryEntryGroup.Notification), // + BatteryChange(0x1a, "Battery Change", PumpHistoryEntryGroup.Notification), // + SetAutoOff(0x1b, "Set Auto Off", PumpHistoryEntryGroup.Configuration), // + SuspendPump(0x1e, "Suspend", PumpHistoryEntryGroup.Basal), // + ResumePump(0x1f, "Resume", PumpHistoryEntryGroup.Basal), // + SelfTest(0x20, "Self Test", PumpHistoryEntryGroup.Statistic), // + Rewind(0x21, "Rewind", PumpHistoryEntryGroup.Prime), // + ClearSettings(0x22, "Clear Settings", PumpHistoryEntryGroup.Configuration), // + ChangeChildBlockEnable(0x23, "Change Child Block Enable", PumpHistoryEntryGroup.Configuration), // + ChangeMaxBolus(0x24, "Change Max Bolus", PumpHistoryEntryGroup.Configuration), // + + // /**/EventUnknown_MM522_0x25(0x25, "Unknown Event 0x25", PumpHistoryEntryGroup.Unknown), // 8? + EnableDisableRemote(0x26, "Enable/Disable Remote", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // 2, 5, 14 V6:2,5,14 + ChangeRemoteId(0x27, "Change Remote ID", PumpHistoryEntryGroup.Configuration), // ?? + ChangeMaxBasal(0x2c, "Change Max Basal", PumpHistoryEntryGroup.Configuration), // + BolusWizardEnabled(0x2d, "Bolus Wizard Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? + /* TODO */ EventUnknown_MM512_0x2e(0x2e, "Unknown Event 0x2e", PumpHistoryEntryGroup.Unknown, 2, 5, 100), // + BolusWizard512(0x2f, "Bolus Wizard (512)", PumpHistoryEntryGroup.Bolus, 2, 5, 12), // + UnabsorbedInsulin512(0x30, "Unabsorbed Insulin (512)", PumpHistoryEntryGroup.Statistic, 5, 0, 0), + ChangeBGReminderOffset(0x31, "Change BG Reminder Offset", PumpHistoryEntryGroup.Configuration), // + ChangeAlarmClockTime(0x32, "Change Alarm Clock Time", PumpHistoryEntryGroup.Configuration), // + TempBasalRate(0x33, "TBR Rate", PumpHistoryEntryGroup.Basal, 2, 5, 1), // + LowReservoir(0x34, "Low Reservoir", PumpHistoryEntryGroup.Notification), // + ChangeAlarmClock(0x35, "Change Alarm Clock", PumpHistoryEntryGroup.Configuration), // + ChangeMeterId(0x36, "Change Meter ID", PumpHistoryEntryGroup.Configuration), // + + // /**/EventUnknown_MM512_0x37(0x37, "Unknown Event 0x37", PumpHistoryEntryGroup.Unknown), // V:MM512 + // /**/EventUnknown_MM512_0x38(0x38, "Unknown Event 0x38", PumpHistoryEntryGroup.Unknown), // + BGReceived512(0x39, "BG Received (512)", PumpHistoryEntryGroup.Glucose, 2, 5, 3), // + /* TODO */ ConfirmInsulinChange(0x3a, "Confirm Insulin Change", PumpHistoryEntryGroup.Unknown), // + SensorStatus(0x3b, "Sensor Status", PumpHistoryEntryGroup.Glucose), // + ChangeParadigmID(0x3c, "Change Paradigm ID", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // V3 ? V6: 2,5,14 ?? is it this length or just 7 + + // EventUnknown_MM512_0x3D(0x3d, "Unknown Event 0x3D", PumpHistoryEntryGroup.Unknown), // + // EventUnknown_MM512_0x3E(0x3e, "Unknown Event 0x3E", PumpHistoryEntryGroup.Unknown), // + BGReceived(0x3f, "BG Received", PumpHistoryEntryGroup.Glucose, 2, 5, 3), // Ian3F + JournalEntryMealMarker(0x40, "Meal Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 2), // is size just 7??? V6 + JournalEntryExerciseMarker(0x41, "Exercise Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // ?? + JournalEntryInsulinMarker(0x42, "Insulin Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 0), // V6 = body(0)/was=1 + JournalEntryOtherMarker(0x43, "Other Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // V6 = body(1) was=0 + EnableSensorAutoCal(0x44, "Enable Sensor AutoCal", PumpHistoryEntryGroup.Glucose), // + + // /**/EventUnknown_MM522_0x45(0x45, "Unknown Event 0x45", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x46(0x46, "Unknown Event 0x46", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x47(0x47, "Unknown Event 0x47", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x48(0x48, "Unknown Event 0x48", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x49(0x49, "Unknown Event 0x49", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x4a(0x4a, "Unknown Event 0x4a", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x4b(0x4b, "Unknown Event 0x4b", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_MM522_0x4c(0x4c, "Unknown Event 0x4c", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + // /**/EventUnknown_0x4d(0x4d, "Unknown Event 0x4d", PumpHistoryEntryGroup.Unknown), // V5: 512: 7, 522: 8 ????NS + // /**/EventUnknown_MM512_0x4e(0x4e, "Unknown Event 0x4e", PumpHistoryEntryGroup.Unknown), // /**/ + BolusWizardSetup512(0x4f, "Bolus Wizard Setup (512)", PumpHistoryEntryGroup.Configuration, 2, 5, 32), // + ChangeSensorSetup2(0x50, "Sensor Setup2", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // Ian50 + + /* TODO */ Sensor_0x51(0x51, "Unknown Event 0x51", PumpHistoryEntryGroup.Unknown), // + /* TODO */ Sensor_0x52(0x52, "Unknown Event 0x52", PumpHistoryEntryGroup.Unknown), // + ChangeSensorAlarmSilenceConfig(0x53, "Sensor Alarm Silence Config", PumpHistoryEntryGroup.Configuration, 2, 5, 1), // 8 + /* TODO */ Sensor_0x54(0x54, "Unknown Event 0x54", PumpHistoryEntryGroup.Unknown), // Ian54 + /* TODO */ Sensor_0x55(0x55, "Unknown Event 0x55", PumpHistoryEntryGroup.Unknown), // + ChangeSensorRateOfChangeAlertSetup(0x56, "Sensor Rate Of Change Alert Setup", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 + ChangeBolusScrollStepSize(0x57, "Change Bolus Scroll Step Size", PumpHistoryEntryGroup.Configuration), // + BolusWizardSetup(0x5a, "Bolus Wizard Setup (522)", PumpHistoryEntryGroup.Configuration, 2, 5, 117), // V2: 522+[B=143]; V6: 124, v6: 137, v7: 117/137 [523] + BolusWizard(0x5b, "Bolus Wizard Estimate", PumpHistoryEntryGroup.Configuration, 2, 5, 13), // 15 // + UnabsorbedInsulin(0x5c, "Unabsorbed Insulin", PumpHistoryEntryGroup.Statistic, 5, 0, 0), // head[1] -> body + SaveSettings(0x5d, "Save Settings", PumpHistoryEntryGroup.Configuration), // + ChangeVariableBolus(0x5e, "Change Variable Bolus", PumpHistoryEntryGroup.Configuration), // + ChangeAudioBolus(0x5f, "Easy Bolus Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? + ChangeBGReminderEnable(0x60, "BG Reminder Enable", PumpHistoryEntryGroup.Configuration), // questionable60 + ChangeAlarmClockEnable(0x61, "Alarm Clock Enable", PumpHistoryEntryGroup.Configuration), // + ChangeTempBasalType(0x62.toByte(), "Change Basal Type", PumpHistoryEntryGroup.Configuration), // ChangeTempBasalTypePumpEvent + ChangeAlarmNotifyMode(0x63, "Change Alarm Notify Mode", PumpHistoryEntryGroup.Configuration), // + ChangeTimeFormat(0x64, "Change Time Format", PumpHistoryEntryGroup.Configuration), // + ChangeReservoirWarningTime(0x65.toByte(), "Change Reservoir Warning Time", PumpHistoryEntryGroup.Configuration), // + ChangeBolusReminderEnable(0x66, "Change Bolus Reminder Enable", PumpHistoryEntryGroup.Configuration), // 9 + SetBolusReminderTime(0x67.toByte(), "Change Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 + DeleteBolusReminderTime(0x68.toByte(), "Delete Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 + BolusReminder(0x69, "Bolus Reminder", PumpHistoryEntryGroup.Configuration, 2, 5, 0), // Ian69 + DeleteAlarmClockTime(0x6a, "Delete Alarm Clock Time", PumpHistoryEntryGroup.Configuration, 2, 5, 7), // 14 + DailyTotals515(0x6c, "Daily Totals (515)", PumpHistoryEntryGroup.Statistic, 1, 2, 35), // v4: 0,0,36. v5: 1,2,33 + DailyTotals522(0x6d, "Daily Totals (522)", PumpHistoryEntryGroup.Statistic, 1, 2, 41), // + DailyTotals523(0x6e, "Daily Totals (523)", PumpHistoryEntryGroup.Statistic, 1, 2, 49), // 1102014-03-17T00:00:00 + ChangeCarbUnits(0x6f.toByte(), "Change Carb Units", PumpHistoryEntryGroup.Configuration), // + + // /**/EventUnknown_MM522_0x70((byte) 0x70, "Unknown Event 0x70", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + BasalProfileStart(0x7b, "Basal Profile Start", PumpHistoryEntryGroup.Basal, 2, 5, 3), // // 722 + ChangeWatchdogEnable(0x7c, "Change Watchdog Enable", PumpHistoryEntryGroup.Configuration), // + ChangeOtherDeviceID(0x7d, "Change Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // + ChangeWatchdogMarriageProfile(0x81.toByte(), "Change Watchdog Marriage Profile", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 + DeleteOtherDeviceID(0x82.toByte(), "Delete Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // + ChangeCaptureEventEnable(0x83.toByte(), "Change Capture Event Enable", PumpHistoryEntryGroup.Configuration), // + + // /**/EventUnknown_MM512_0x88(0x88, "Unknown Event 0x88", PumpHistoryEntryGroup.Unknown), // + // /**/EventUnknown_MM512_0x94(0x94, "Unknown Event 0x94", PumpHistoryEntryGroup.Unknown), // + /**/ + IanA8(0xA8.toByte(), "Ian A8 (Unknown)", PumpHistoryEntryGroup.Unknown, 10, 5, 0), // + + // /**/EventUnknown_MM522_0xE8(0xe8, "Unknown Event 0xE8", PumpHistoryEntryGroup.Unknown, 2, 5, 25), // + // F0 - F3 are not in go code, but they are in GGC one and thet are used + ReadOtherDevicesIDs(0xf0.toByte(), "Read Other Devices IDs", PumpHistoryEntryGroup.Configuration), // ? + ReadCaptureEventEnabled(0xf1.toByte(), "Read Capture Event Enabled", PumpHistoryEntryGroup.Configuration), // ? + ChangeCaptureEventEnable2(0xf2.toByte(), "Change Capture Event Enable2", PumpHistoryEntryGroup.Configuration), // ? + ReadOtherDevicesStatus(0xf3.toByte(), "Read Other Devices Status", PumpHistoryEntryGroup.Configuration), // ? + TempBasalCombined(0xfe.toByte(), "TBR", PumpHistoryEntryGroup.Basal), // + UnknownBasePacket(0xff.toByte(), "Unknown Base Packet", PumpHistoryEntryGroup.Unknown); + + companion object { + + private val opCodeMap: MutableMap = HashMap() + fun setSpecialRulesForEntryTypes() { + EndResultTotals.addSpecialRuleBody(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 3)) + Bolus.addSpecialRuleHead(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 8)) + BolusWizardSetup.addSpecialRuleBody(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 137)) + BolusWizard.addSpecialRuleBody(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 15)) + BolusReminder.addSpecialRuleBody(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 2)) + ChangeSensorSetup2.addSpecialRuleBody(SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 34)) + } + + fun getByCode(opCode: Byte): PumpHistoryEntryType { + return if (opCodeMap.containsKey(opCode)) { + opCodeMap[opCode]!! + } else { + UnknownBasePacket + } + } + + fun isAAPSRelevantEntry(entryType: PumpHistoryEntryType): Boolean { + return entryType == Bolus || // Treatments + entryType == TempBasalRate || // + entryType == TempBasalDuration || // + entryType == Prime || // Pump Status Change + entryType == SuspendPump || // + entryType == ResumePump || // + entryType == Rewind || // + entryType == NoDeliveryAlarm || // no delivery + entryType == BasalProfileStart || // + entryType == ChangeTime || // Time Change + entryType == NewTimeSet || // + entryType == ChangeBasalPattern || // Configuration + entryType == ClearSettings || // + entryType == SaveSettings || // + entryType == ChangeMaxBolus || // + entryType == ChangeMaxBasal || // + entryType == ChangeTempBasalType || // + entryType == ChangeBasalProfile_NewProfile || // Basal profile + entryType == DailyTotals515 || // Daily Totals + entryType == DailyTotals522 || // + entryType == DailyTotals523 || // + entryType == EndResultTotals + } + + val isRelevantEntry: Boolean + get() = true + + init { + for (type in values()) { + opCodeMap[type.code] = type + } + setSpecialRulesForEntryTypes() + } + } + + private val totalLength: Int + + // special rules need to be put in list from highest to lowest (e.g.: + // 523andHigher=12, 515andHigher=10 and default (set in cnstr) would be 8) + private var specialRulesHead: MutableList? = null + private var specialRulesBody: MutableList? = null + private var hasSpecialRules = false + get() = field + + fun getTotalLength(medtronicDeviceType: MedtronicDeviceType): Int { + return if (hasSpecialRules) { + getHeadLength(medtronicDeviceType) + getBodyLength(medtronicDeviceType) + dateLength + } else { + totalLength + } + } + + fun addSpecialRuleHead(rule: SpecialRule) { + if (specialRulesHead.isNullOrEmpty()) { + specialRulesHead = ArrayList() + } + specialRulesHead!!.add(rule) + hasSpecialRules = true + } + + fun addSpecialRuleBody(rule: SpecialRule) { + if (specialRulesBody.isNullOrEmpty()) { + specialRulesBody = ArrayList() + } + specialRulesBody!!.add(rule) + hasSpecialRules = true + } + + fun getHeadLength(medtronicDeviceType: MedtronicDeviceType): Int { + return if (hasSpecialRules && !specialRulesHead.isNullOrEmpty()) { + determineSizeByRule(medtronicDeviceType, headLength, specialRulesHead!!) + } else { + headLength + } + } + + fun getBodyLength(medtronicDeviceType: MedtronicDeviceType): Int { + return if (hasSpecialRules && !specialRulesBody.isNullOrEmpty()) { + determineSizeByRule(medtronicDeviceType, bodyLength, specialRulesBody!!) + } else { + bodyLength + } + } + + // byte[] dh = { 2, 3 }; + private fun determineSizeByRule(medtronicDeviceType: MedtronicDeviceType, defaultValue: Int, rules: List): Int { + var size = defaultValue + for (rule in rules) { + if (MedtronicDeviceType.isSameDevice(medtronicDeviceType, rule.deviceType)) { + size = rule.size + break + } + } + return size + } + + class SpecialRule internal constructor(var deviceType: MedtronicDeviceType, var size: Int) + + init { + totalLength = headLength + dateLength + bodyLength + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java deleted file mode 100644 index 4bb67cfb3e..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java +++ /dev/null @@ -1,177 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; - -/** - * History page contains data, sorted from newest to oldest (0=newest..n=oldest) - *

- * Created by andy on 9/23/18. - */ -public class PumpHistoryResult { - - private final AAPSLogger aapsLogger; - - private boolean searchFinished = false; - private final PumpHistoryEntry searchEntry = null; - private Long searchDate = null; - private SearchType searchType = SearchType.None; - public List unprocessedEntries; - public List validEntries; - - - public PumpHistoryResult(AAPSLogger aapsLogger, PumpHistoryEntry searchEntry, Long targetDate) { - this.aapsLogger = aapsLogger; - if (searchEntry != null) { - /* - * this.searchEntry = searchEntry; - * this.searchType = SearchType.LastEntry; - * aapsLogger.debug(LTag.PUMPCOMM,"PumpHistoryResult. Search parameters: Last Entry: " + searchEntry.atechDateTime + " type=" - * + searchEntry.getEntryType().name()); - */ - this.searchDate = searchEntry.atechDateTime; - this.searchType = SearchType.Date; - aapsLogger.debug(LTag.PUMPCOMM, "PumpHistoryResult. Search parameters: Date(with searchEntry): " + targetDate); - } else if (targetDate != null) { - this.searchDate = targetDate; - this.searchType = SearchType.Date; - aapsLogger.debug(LTag.PUMPCOMM, "PumpHistoryResult. Search parameters: Date: " + targetDate); - } - - // this.unprocessedEntries = new ArrayList<>(); - this.validEntries = new ArrayList<>(); - } - - - public void addHistoryEntries(List entries, int page) { - this.unprocessedEntries = entries; - //aapsLogger.debug(LTag.PUMPCOMM,"PumpHistoryResult. Unprocessed entries: {}", MedtronicUtil.getGsonInstance().toJson(entries)); - processEntries(); - } - - // TODO Bug #145 need to check if we had timeChange that went -1, that situation needs to be evaluated separately - public void processEntries() { - int olderEntries = 0; - - Collections.reverse(this.unprocessedEntries); - - switch (searchType) { - case None: - //aapsLogger.debug(LTag.PUMPCOMM,"PE. None search"); - this.validEntries.addAll(this.unprocessedEntries); - break; - - case LastEntry: { - aapsLogger.debug(LTag.PUMPCOMM, "PE. Last entry search"); - - //Collections.sort(this.unprocessedEntries, new PumpHistoryEntry.Comparator()); - - aapsLogger.debug(LTag.PUMPCOMM, "PE. PumpHistoryResult. Search entry date: " + searchEntry.atechDateTime); - - Long date = searchEntry.atechDateTime; - - for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) { - - if (unprocessedEntry.equals(searchEntry)) { - //aapsLogger.debug(LTag.PUMPCOMM,"PE. Item found {}.", unprocessedEntry); - searchFinished = true; - break; - } - - //aapsLogger.debug(LTag.PUMPCOMM,"PE. Entry {} added.", unprocessedEntry); - this.validEntries.add(unprocessedEntry); - } - } - break; - case Date: { - aapsLogger.debug(LTag.PUMPCOMM, "PE. Date search: Search date: " + this.searchDate); - - - for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) { - - if (unprocessedEntry.atechDateTime == null || unprocessedEntry.atechDateTime == 0) { - aapsLogger.debug(LTag.PUMPCOMM, "PE. PumpHistoryResult. Search entry date: Entry with no date: " + unprocessedEntry); - continue; - } - - if (unprocessedEntry.isAfter(this.searchDate)) { - this.validEntries.add(unprocessedEntry); - } else { -// aapsLogger.debug(LTag.PUMPCOMM,"PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", -// DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); - if (DateTimeUtil.getYear(unprocessedEntry.atechDateTime) > 2015) - olderEntries++; - } - } - - if (olderEntries > 0) { - //Collections.sort(this.validEntries, new PumpHistoryEntry.Comparator()); - - searchFinished = true; - } - } - break; - - } // switch - - //aapsLogger.debug(LTag.PUMPCOMM,"PE. Valid Entries: {}", validEntries); - } - - - public String toString() { - return "PumpHistoryResult [unprocessed=" + (unprocessedEntries != null ? "" + unprocessedEntries.size() : "0") + // - ", valid=" + (validEntries != null ? "" + validEntries.size() : "0") + // - ", searchEntry=" + searchEntry + // - ", searchDate=" + searchDate + // - ", searchType=" + searchType + // - ", searchFinished=" + searchFinished + // - "]"; - - } - - - /** - * Return latest entry (entry with highest date time) - * - * @return - */ - public PumpHistoryEntry getLatestEntry() { - if (this.validEntries == null || this.validEntries.size() == 0) - return null; - else { - return this.validEntries.get(0); - // PumpHistoryEntry pumpHistoryEntry = this.validEntries.get(0); - // - // if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.EndResultTotals) - // return pumpHistoryEntry; - // else - // return this.validEntries.get(1); - } - } - - - public boolean isSearchRequired() { - return searchType != SearchType.None; - } - - - public boolean isSearchFinished() { - return searchFinished; - } - - - public List getValidEntries() { - return validEntries; - } - - enum SearchType { - None, // - LastEntry, // - Date - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.kt new file mode 100644 index 0000000000..7bdc42d1ef --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.kt @@ -0,0 +1,130 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import java.util.* +import kotlin.collections.ArrayList + +/** + * History page contains data, sorted from newest to oldest (0=newest..n=oldest) + * + * + * Created by andy on 9/23/18. + */ +class PumpHistoryResult(private val aapsLogger: AAPSLogger, searchEntry: PumpHistoryEntry?, targetDate: Long?) { + + var isSearchFinished = false + private set + private val searchEntry: PumpHistoryEntry? = null + private var searchDate: Long? = null + private var searchType = SearchType.None + var unprocessedEntries: List = ArrayList() + var validEntries: MutableList = ArrayList() + + fun addHistoryEntries(entries: List /*, page: Int*/) { + unprocessedEntries = entries + //aapsLogger.debug(LTag.PUMPCOMM,"PumpHistoryResult. Unprocessed entries: {}", MedtronicUtil.getGsonInstance().toJson(entries)); + processEntries() + } + + // TODO Bug #145 need to check if we had timeChange that went -1, that situation needs to be evaluated separately + fun processEntries() { + var olderEntries = 0 + Collections.reverse(unprocessedEntries) + when (searchType) { + SearchType.None -> //aapsLogger.debug(LTag.PUMPCOMM,"PE. None search"); + validEntries.addAll(unprocessedEntries) + + SearchType.LastEntry -> { + aapsLogger.debug(LTag.PUMPCOMM, "PE. Last entry search") + + //Collections.sort(this.unprocessedEntries, new PumpHistoryEntry.Comparator()); + aapsLogger.debug(LTag.PUMPCOMM, "PE. PumpHistoryResult. Search entry date: " + searchEntry!!.atechDateTime) + //val date = searchEntry.atechDateTime + for (unprocessedEntry in unprocessedEntries) { + if (unprocessedEntry.equals(searchEntry)) { + //aapsLogger.debug(LTag.PUMPCOMM,"PE. Item found {}.", unprocessedEntry); + isSearchFinished = true + break + } + + //aapsLogger.debug(LTag.PUMPCOMM,"PE. Entry {} added.", unprocessedEntry); + + validEntries.add(unprocessedEntry) + } + // TODO 5minutes back + } + + SearchType.Date -> { + aapsLogger.debug(LTag.PUMPCOMM, "PE. Date search: Search date: " + searchDate) + for (unprocessedEntry in unprocessedEntries) { + if (unprocessedEntry.atechDateTime == 0L) { + aapsLogger.debug(LTag.PUMPCOMM, "PE. PumpHistoryResult. Search entry date: Entry with no date: $unprocessedEntry") + continue + } + if (unprocessedEntry.isAfter(searchDate!!)) { + validEntries.add(unprocessedEntry) + } else { +// aapsLogger.debug(LTag.PUMPCOMM,"PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", +// DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); + if (DateTimeUtil.getYear(unprocessedEntry.atechDateTime) > 2015) olderEntries++ + } + } + if (olderEntries > 0) { + //Collections.sort(this.validEntries, new PumpHistoryEntry.Comparator()); + isSearchFinished = true + } + } + } + + //aapsLogger.debug(LTag.PUMPCOMM,"PE. Valid Entries: {}", validEntries); + } + + override fun toString(): String { + return "PumpHistoryResult [unprocessed=" + unprocessedEntries.size + // + ", valid=" + validEntries.size + // + ", searchEntry=" + searchEntry + // + ", searchDate=" + searchDate + // + ", searchType=" + searchType + // + ", searchFinished=" + isSearchFinished + // + "]" + } + + /** + * Return latest entry (entry with highest date time) + * @return + */ + val latestEntry: PumpHistoryEntry? + get() = if (validEntries.size == 0) null else validEntries[0] + + // val isSearchRequired: Boolean + // get() = searchType != SearchType.None + + internal enum class SearchType { + None, // + LastEntry, // + Date + } + + init { + if (searchEntry != null) { + /* + * this.searchEntry = searchEntry; + * this.searchType = SearchType.LastEntry; + * aapsLogger.debug(LTag.PUMPCOMM,"PumpHistoryResult. Search parameters: Last Entry: " + searchEntry.atechDateTime + " type=" + * + searchEntry.getEntryType().name()); + */ + searchDate = searchEntry.atechDateTime + searchType = SearchType.Date + aapsLogger.debug(LTag.PUMPCOMM, "PumpHistoryResult. Search parameters: Date(with searchEntry): $targetDate") + } else if (targetDate != null) { + searchDate = targetDate + searchType = SearchType.Date + aapsLogger.debug(LTag.PUMPCOMM, "PumpHistoryResult. Search parameters: Date: $targetDate") + } + + // this.unprocessedEntries = new ArrayList<>(); + //validEntries = ArrayList() + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java deleted file mode 100644 index 7f2c51e032..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java +++ /dev/null @@ -1,49 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -/** - * Created by geoff on 6/2/16. - */ -public class CarelinkLongMessageBody extends MessageBody { - - private static final int LONG_MESSAGE_BODY_LENGTH = 65; - protected byte[] data; - - - CarelinkLongMessageBody() { - init(new byte[0]); - } - - - public CarelinkLongMessageBody(byte[] payload) { - init(payload); - } - - @Override - public void init(byte[] rxData) { - - if (rxData != null && rxData.length == LONG_MESSAGE_BODY_LENGTH) { - data = rxData; - } else { - data = new byte[LONG_MESSAGE_BODY_LENGTH]; - if (rxData != null) { - int size = rxData.length < LONG_MESSAGE_BODY_LENGTH ? rxData.length : LONG_MESSAGE_BODY_LENGTH; - for (int i = 0; i < size; i++) { - data[i] = rxData[i]; - } - } - } - } - - - @Override - public int getLength() { - return LONG_MESSAGE_BODY_LENGTH; - } - - - @Override - public byte[] getTxData() { - return data; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.kt new file mode 100644 index 0000000000..e46e8feb6a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.kt @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +/** + * Created by geoff on 6/2/16. + */ +open class CarelinkLongMessageBody : MessageBody { + + //protected var data: ByteArray? = null + + internal constructor() { + init(ByteArray(0)) + } + + constructor(payload: ByteArray) { + init(payload) + } + + override fun init(rxData: ByteArray?) { + if (rxData != null && rxData.size == LONG_MESSAGE_BODY_LENGTH) { + data = rxData + } else { + data = ByteArray(LONG_MESSAGE_BODY_LENGTH) + if (rxData != null) { + val size = if (rxData.size < LONG_MESSAGE_BODY_LENGTH) rxData.size else LONG_MESSAGE_BODY_LENGTH + for (i in 0 until size) { + data!![i] = rxData[i] + } + } + } + } + + override val length: Int + get() = LONG_MESSAGE_BODY_LENGTH + + // { + // return LONG_MESSAGE_BODY_LENGTH + // } + + // override fun getTxData(): ByteArray { + // return data + // } + + companion object { + private const val LONG_MESSAGE_BODY_LENGTH = 65 + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java deleted file mode 100644 index e2ebbff085..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java +++ /dev/null @@ -1,53 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -/** - * Created by geoff on 5/29/16. - */ -// Andy: See comments in message body -public class CarelinkShortMessageBody extends MessageBody { - - byte[] body; - - - public CarelinkShortMessageBody() { - init(new byte[] { 0 }); - } - - - public CarelinkShortMessageBody(byte[] data) { - init(data); - } - - - @Override - public int getLength() { - return body.length; - } - - - @Override - public void init(byte[] rxData) { - body = rxData; - } - - - public byte[] getRxData() { - return body; - } - - - public void setRxData(byte[] rxData) { - init(rxData); - } - - - @Override - public byte[] getTxData() { - return body; - } - - - public void setTxData(byte[] txData) { - init(txData); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.kt new file mode 100644 index 0000000000..7d99b45ae2 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.kt @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +/** + * Created by geoff on 5/29/16. + */ +// Andy: See comments in message body +open class CarelinkShortMessageBody : MessageBody { + + //var body: ByteArray? + + constructor() { + init(byteArrayOf(0)) + } + + constructor(data: ByteArray?) { + init(data) + } + + override val length: Int + get() = data!!.size + + override fun init(rxData: ByteArray?) { + data = rxData + } + + var rxData: ByteArray? + get() = data + set(rxData) { + init(rxData) + } + + // override var txData: ByteArray? + // get() = body + // set(txData) { + // init(txData) + // } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java deleted file mode 100644 index 25e8aa087c..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java +++ /dev/null @@ -1,59 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; - -/** - * Created by geoff on 6/2/16. - */ -public class GetHistoryPageCarelinkMessageBody extends CarelinkLongMessageBody { - - // public boolean wasLastFrame = false; - // public int frameNumber = 0; - // public byte[] frame = new byte[] {}; - - public GetHistoryPageCarelinkMessageBody(byte[] frameData) { - init(frameData); - } - - - public GetHistoryPageCarelinkMessageBody(int pageNum) { - init(pageNum); - } - - - @Override - public int getLength() { - return data.length; - } - - - @Override - public void init(byte[] rxData) { - super.init(rxData); - } - - - public void init(int pageNum) { - byte numArgs = 1; - super.init(new byte[] { numArgs, (byte)pageNum }); - } - - - public int getFrameNumber() { - if (data.length > 0) { - return data[0] & 0x7f; - } - return 255; - } - - - public boolean wasLastFrame() { - return (data[0] & 0x80) != 0; - } - - - public byte[] getFrameData() { - return ByteUtil.substring(data, 1, data.length - 1); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.kt new file mode 100644 index 0000000000..1f86c1446a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.kt @@ -0,0 +1,51 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import kotlin.experimental.and + +/** + * Created by geoff on 6/2/16. + */ +class GetHistoryPageCarelinkMessageBody : CarelinkLongMessageBody { + + // public boolean wasLastFrame = false; + // public int frameNumber = 0; + // public byte[] frame = new byte[] {}; + constructor(frameData: ByteArray?) { + init(frameData) + } + + constructor(pageNum: Int) { + init(pageNum) + } + + override val length: Int + get() = data!!.size + + override fun init(rxData: ByteArray?) { + super.init(rxData) + } + + fun init(pageNum: Int) { + val numArgs: Byte = 1 + super.init(byteArrayOf(numArgs, pageNum.toByte())) + } + + val frameNumber: Int + get() = if (data!!.size > 0) { + (data!![0] and 0x7f.toByte()).toInt() + } else 255 + + fun wasLastFrame(): Boolean { + return (data!![0] and 0x80.toByte()).toInt() != 0 + } + + val frameData: ByteArray + get() { + return if (data == null) { + byteArrayOf() + } else { + ByteUtil.substring(data!!, 1, data!!.size - 1) + } + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java deleted file mode 100644 index fa4a3817be..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java +++ /dev/null @@ -1,34 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; - -/** - * Created by geoff on 5/29/16. - */ -public class MessageBody { - - public int getLength() { - return 0; - } - - - public void init(byte[] rxData) { - } - - - public byte[] getTxData() { - return new byte[]{}; - } - - - public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - - sb.append(" [txData="); - sb.append(ByteUtil.shortHexString(getTxData())); - sb.append("]"); - - return sb.toString(); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.kt new file mode 100644 index 0000000000..45bfdcf5de --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.kt @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil + +/** + * Created by geoff on 5/29/16. + */ +open class MessageBody { + + protected var data: ByteArray? = null + + open val length: Int + get() = 0 + + open fun init(rxData: ByteArray?) {} + + open val txData: ByteArray? + get() = if (data == null) byteArrayOf() else data + + override fun toString(): String { + val sb = StringBuilder(javaClass.simpleName) + sb.append(" [txData=") + sb.append(ByteUtil.shortHexString(txData)) + sb.append("]") + return sb.toString() + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java deleted file mode 100644 index f153dd8cac..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java +++ /dev/null @@ -1,47 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -import java.util.HashMap; -import java.util.Map; - -/** - * Created by geoff on 5/29/16. - * refactored into enum - */ -public enum PacketType { - Invalid(0x00), // - MySentry(0xa2), // - Meter(0xa5), // - Carelink(0xa7), // - Sensor(0xa8) // - ; - - public static Map mapByValue; - - static { - mapByValue = new HashMap<>(); - - for (PacketType packetType : values()) { - mapByValue.put(packetType.value, packetType); - } - } - - private final byte value; - - - PacketType(int value) { - this.value = (byte)value; - } - - - public static PacketType getByValue(short value) { - if (mapByValue.containsKey(value)) - return mapByValue.get(value); - else - return PacketType.Invalid; - } - - - public byte getValue() { - return value; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.kt new file mode 100644 index 0000000000..68769944b1 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.kt @@ -0,0 +1,37 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +import java.util.* + +/** + * Created by geoff on 5/29/16. + * refactored into enum + */ +enum class PacketType(value: Int) { + + Invalid(0x00), // + MySentry(0xa2), // + Meter(0xa5), // + Carelink(0xa7), // + Sensor(0xa8 // + ); + + companion object { + var mapByValue: MutableMap = HashMap() + + fun getByValue(value: Short): PacketType? { + return if (mapByValue.containsKey(value.toByte())) mapByValue.get(value.toByte()) else Invalid + } + + init { + for (packetType in values()) { + mapByValue[packetType.value] = packetType + } + } + } + + val value: Byte + + init { + this.value = value.toByte() + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java deleted file mode 100644 index 4cfda08975..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -/** - * Created by geoff on 5/29/16. - */ -public class PumpAckMessageBody extends CarelinkShortMessageBody { - - public PumpAckMessageBody() { - init(new byte[] { 0 }); - } - - - public PumpAckMessageBody(byte[] bodyData) { - init(bodyData); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.kt new file mode 100644 index 0000000000..39d85deb44 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.kt @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +/** + * Created by geoff on 5/29/16. + */ +class PumpAckMessageBody : CarelinkShortMessageBody { + + constructor() { + init(byteArrayOf(0)) + } + + constructor(bodyData: ByteArray?) { + init(bodyData) + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java deleted file mode 100644 index 71d50c4691..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java +++ /dev/null @@ -1,214 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; - -/** - * Created by geoff on 5/29/16. - */ -public class PumpMessage implements RLMessage { - - private final AAPSLogger aapsLogger; - - private PacketType packetType = PacketType.Carelink; - public byte[] address = new byte[]{0, 0, 0}; - public MedtronicCommandType commandType; - public Byte invalidCommandType; - public MessageBody messageBody = new MessageBody(); - public String error = null; - - public static final int FRAME_DATA_LENGTH = 64; - - - public PumpMessage(AAPSLogger aapsLogger, String error) { - this.error = error; - this.aapsLogger = aapsLogger; - } - - - public PumpMessage(AAPSLogger aapsLogger, byte[] rxData) { - this.aapsLogger = aapsLogger; - init(rxData); - } - - - public PumpMessage(AAPSLogger aapsLogger) { - this.aapsLogger = aapsLogger; - } - - - public boolean isErrorResponse() { - return (this.error != null); - } - - - public void init(PacketType packetType, byte[] address, MedtronicCommandType commandType, MessageBody messageBody) { - this.packetType = packetType; - this.address = address; - this.commandType = commandType; - this.messageBody = messageBody; - } - - - public void init(byte[] rxData) { - if (rxData == null) { - return; - } - if (rxData.length > 0) { - this.packetType = PacketType.getByValue(rxData[0]); - } - if (rxData.length > 3) { - this.address = ByteUtil.substring(rxData, 1, 3); - } - if (rxData.length > 4) { - this.commandType = MedtronicCommandType.getByCode(rxData[4]); - if (this.commandType == MedtronicCommandType.InvalidCommand) { - aapsLogger.error(LTag.PUMPCOMM, "PumpMessage - Unknown commandType " + rxData[4]); - } - } - if (rxData.length > 5) { - this.messageBody = MedtronicCommandType.constructMessageBody(commandType, - ByteUtil.substring(rxData, 5, rxData.length - 5)); - } - } - - - @Override - public byte[] getTxData() { - byte[] rval = ByteUtil.concat(new byte[]{packetType.getValue()}, address); - rval = ByteUtil.concat(rval, commandType.getCommandCode()); - rval = ByteUtil.concat(rval, messageBody.getTxData()); - return rval; - } - - - public byte[] getContents() { - return ByteUtil.concat(new byte[]{commandType.getCommandCode()}, messageBody.getTxData()); - } - - - // rawContent = just response without code (contents-2, messageBody.txData-1); - public byte[] getRawContent() { - - if ((messageBody == null) || (messageBody.getTxData() == null) || (messageBody.getTxData().length == 0)) - return null; - - byte[] data = messageBody.getTxData(); - - int length = ByteUtil.asUINT8(data[0]); // length is not always correct so, we check whole array if we have - // data, after length - int originalLength = length; - - // check if displayed length is invalid - if (length > data.length - 1) { - return data; - } - - // check Old Way - boolean oldWay = false; - for (int i = (length + 1); i < data.length; i++) { - if (data[i] != 0x00) { - oldWay = true; - } - } - - if (oldWay) { - length = data.length - 1; - } - - byte[] arrayOut = new byte[length]; - - System.arraycopy(messageBody.getTxData(), 1, arrayOut, 0, length); - -// if (isLogEnabled()) -// LOG.debug("PumpMessage - Length: " + length + ", Original Length: " + originalLength + ", CommandType: " -// + commandType); - - return arrayOut; - } - - - public byte[] getRawContentOfFrame() { - byte[] raw = messageBody.getTxData(); - if (raw==null || raw.length==0) { - return new byte[0]; - } else { - return ByteUtil.substring(raw, 1, Math.min(FRAME_DATA_LENGTH, raw.length - 1)); - } - } - - - public boolean isValid() { - if (packetType == null) - return false; - if (address == null) - return false; - if (commandType == null) - return false; - return messageBody != null; - } - - - public MessageBody getMessageBody() { - return messageBody; - } - - - public String getResponseContent() { - StringBuilder sb = new StringBuilder("PumpMessage [response="); - boolean showData = true; - - if (commandType != null) { - if (commandType == MedtronicCommandType.CommandACK) { - sb.append("Acknowledged"); - showData = false; - } else if (commandType == MedtronicCommandType.CommandNAK) { - sb.append("NOT Acknowledged"); - showData = false; - } else { - sb.append(commandType.name()); - } - } else { - sb.append("Unknown_Type"); - sb.append(" (" + invalidCommandType + ")"); - } - - if (showData) { - sb.append(", rawResponse="); - sb.append(ByteUtil.shortHexString(getRawContent())); - } - - sb.append("]"); - - return sb.toString(); - } - - - public String toString() { - StringBuilder sb = new StringBuilder("PumpMessage ["); - - sb.append("packetType="); - sb.append(packetType == null ? "null" : packetType.name()); - - sb.append(", address=("); - sb.append(ByteUtil.shortHexString(this.address)); - - sb.append("), commandType="); - sb.append(commandType == null ? "null" : commandType.name()); - - if (invalidCommandType != null) { - sb.append(", invalidCommandType="); - sb.append(invalidCommandType); - } - - sb.append(", messageBody=("); - sb.append(this.messageBody == null ? "null" : this.messageBody); - - sb.append(")]"); - - return sb.toString(); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.kt new file mode 100644 index 0000000000..82633d015d --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.kt @@ -0,0 +1,184 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType + +/** + * Created by geoff on 5/29/16. + */ +class PumpMessage : RLMessage { + + private val aapsLogger: AAPSLogger + private var packetType: PacketType? = PacketType.Carelink + var address: ByteArray? = byteArrayOf(0, 0, 0) + var commandType: MedtronicCommandType? = null + var invalidCommandType: Byte? = null + var messageBody: MessageBody? = MessageBody() + var error: String? = null + + constructor(aapsLogger: AAPSLogger, error: String?) { + this.error = error + this.aapsLogger = aapsLogger + } + + constructor(aapsLogger: AAPSLogger, rxData: ByteArray?) { + this.aapsLogger = aapsLogger + init(rxData) + } + + constructor(aapsLogger: AAPSLogger) { + this.aapsLogger = aapsLogger + } + + val isErrorResponse: Boolean + get() = error != null + + fun init(packetType: PacketType?, address: ByteArray?, commandType: MedtronicCommandType?, messageBody: MessageBody?) { + this.packetType = packetType + this.address = address + this.commandType = commandType + this.messageBody = messageBody + } + + fun init(rxData: ByteArray?) { + if (rxData == null) { + return + } + if (rxData.size > 0) { + packetType = PacketType.getByValue(rxData[0].toShort()) + } + if (rxData.size > 3) { + address = ByteUtil.substring(rxData, 1, 3) + } + if (rxData.size > 4) { + commandType = MedtronicCommandType.getByCode(rxData[4]) + if (commandType == MedtronicCommandType.InvalidCommand) { + aapsLogger.error(LTag.PUMPCOMM, "PumpMessage - Unknown commandType " + rxData[4]) + } + } + if (rxData.size > 5) { + messageBody = MedtronicCommandType.constructMessageBody(commandType, + ByteUtil.substring(rxData, 5, rxData.size - 5)) + } + } + + override fun getTxData(): ByteArray { + var rval = ByteUtil.concat(byteArrayOf(packetType!!.value), address) + rval = ByteUtil.concat(rval, commandType!!.commandCode) + rval = ByteUtil.concat(rval, messageBody!!.txData) + return rval + } + + val contents: ByteArray + get() = ByteUtil.concat(byteArrayOf(commandType!!.commandCode), messageBody!!.txData)// length is not always correct so, we check whole array if we have + // data, after length + + // check if displayed length is invalid + + // check Old Way + +// if (isLogEnabled()) +// LOG.debug("PumpMessage - Length: " + length + ", Original Length: " + originalLength + ", CommandType: " +// + commandType); + + // rawContent = just response without code (contents-2, messageBody.txData-1); + val rawContent: ByteArray + get() { + if (messageBody == null || messageBody!!.txData == null || messageBody!!.txData!!.size == 0) return byteArrayOf() + val data = messageBody!!.txData + var length = ByteUtil.asUINT8(data!![0]) // length is not always correct so, we check whole array if we have + // data, after length + //val originalLength = length + + // check if displayed length is invalid + if (length > data.size - 1) { + return data + } + + // check Old Way + var oldWay = false + for (i in length + 1 until data.size) { + if (data[i] != 0x00.toByte()) { + oldWay = true + } + } + if (oldWay) { + length = data.size - 1 + } + val arrayOut = ByteArray(length) + System.arraycopy(messageBody!!.txData, 1, arrayOut, 0, length) + +// if (isLogEnabled()) +// LOG.debug("PumpMessage - Length: " + length + ", Original Length: " + originalLength + ", CommandType: " +// + commandType); + return arrayOut + } + + val rawContentOfFrame: ByteArray + get() { + val raw = messageBody!!.txData + return if (raw == null || raw.size == 0) { + byteArrayOf() + } else { + ByteUtil.substring(raw, 1, Math.min(FRAME_DATA_LENGTH, raw.size - 1)) + } + } + + override fun isValid(): Boolean { + if (packetType == null) return false + if (address == null) return false + return if (commandType == null) false else messageBody != null + } + + val responseContent: String + get() { + val sb = StringBuilder("PumpMessage [response=") + var showData = true + if (commandType != null) { + if (commandType == MedtronicCommandType.CommandACK) { + sb.append("Acknowledged") + showData = false + } else if (commandType == MedtronicCommandType.CommandNAK) { + sb.append("NOT Acknowledged") + showData = false + } else { + sb.append(commandType!!.name) + } + } else { + sb.append("Unknown_Type") + sb.append(" ($invalidCommandType)") + } + if (showData) { + sb.append(", rawResponse=") + sb.append(ByteUtil.shortHexString(rawContent)) + } + sb.append("]") + return sb.toString() + } + + override fun toString(): String { + val sb = StringBuilder("PumpMessage [") + sb.append("packetType=") + sb.append(if (packetType == null) "null" else packetType!!.name) + sb.append(", address=(") + sb.append(ByteUtil.shortHexString(address)) + sb.append("), commandType=") + sb.append(if (commandType == null) "null" else commandType!!.name) + if (invalidCommandType != null) { + sb.append(", invalidCommandType=") + sb.append(invalidCommandType) + } + sb.append(", messageBody=(") + sb.append(if (messageBody == null) "null" else messageBody) + sb.append(")]") + return sb.toString() + } + + companion object { + + const val FRAME_DATA_LENGTH = 64 + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java deleted file mode 100644 index 675a794bd8..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java +++ /dev/null @@ -1,46 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; - -/** - * Created by geoff on 5/29/16. - */ -public class UnknownMessageBody extends MessageBody { - - public byte[] rxData; - - - public UnknownMessageBody(byte[] data) { - this.rxData = data; - } - - - @Override - public int getLength() { - return 0; - } - - - @Override - public void init(byte[] rxData) { - } - - - public byte[] getRxData() { - return rxData; - } - - - public void setRxData(byte[] rxData) { - this.rxData = rxData; - } - - - @Override - public byte[] getTxData() { - return rxData; - } - - - public void setTxData(byte[] txData) { - this.rxData = txData; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.kt new file mode 100644 index 0000000000..ba3c998d9e --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.kt @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message + +/** + * Created by geoff on 5/29/16. + */ +class UnknownMessageBody(override var txData: ByteArray) : MessageBody() { + + override val length: Int + get() = 0 + + override fun init(rxData: ByteArray?) { + data = rxData + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java deleted file mode 100644 index 0eed54e71f..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java +++ /dev/null @@ -1,58 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by andy on 6/14/18. - */ -public class MedtronicUIComm { - - private final HasAndroidInjector injector; - private final AAPSLogger aapsLogger; - private final MedtronicUtil medtronicUtil; - private final MedtronicCommunicationManager medtronicCommunicationManager; - private final MedtronicUIPostprocessor medtronicUIPostprocessor; - - public MedtronicUIComm( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - MedtronicUtil medtronicUtil, - MedtronicUIPostprocessor medtronicUIPostprocessor, - MedtronicCommunicationManager medtronicCommunicationManager - ) { - this.injector = injector; - this.aapsLogger = aapsLogger; - this.medtronicUtil = medtronicUtil; - this.medtronicUIPostprocessor = medtronicUIPostprocessor; - this.medtronicCommunicationManager = medtronicCommunicationManager; - } - - public synchronized MedtronicUITask executeCommand(MedtronicCommandType commandType, Object... parameters) { - - aapsLogger.info(LTag.PUMP, "Execute Command: " + commandType.name()); - - MedtronicUITask task = new MedtronicUITask(injector, commandType, parameters); - - medtronicUtil.setCurrentCommand(commandType); - - task.execute(medtronicCommunicationManager); - - if (!task.isReceived()) { - aapsLogger.warn(LTag.PUMP, "Reply not received for " + commandType); - } - - task.postProcess(medtronicUIPostprocessor); - - return task; - - } - - public int getInvalidResponsesCount() { - return medtronicCommunicationManager.getNotConnectedCount(); - } -} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.kt new file mode 100644 index 0000000000..3bff5105ee --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.kt @@ -0,0 +1,45 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import javax.inject.Inject + +/** + * Created by andy on 6/14/18. + */ +class MedtronicUIComm @Inject constructor( + private val injector: HasAndroidInjector, + private val aapsLogger: AAPSLogger, + private val medtronicUtil: MedtronicUtil, + private val medtronicUIPostprocessor: MedtronicUIPostprocessor, + private val medtronicCommunicationManager: MedtronicCommunicationManager +) { + + fun executeCommand(commandType: MedtronicCommandType): MedtronicUITask { + return executeCommand(commandType, null) + } + + @Synchronized + fun executeCommand(commandType: MedtronicCommandType, parameters: ArrayList?): MedtronicUITask { + + aapsLogger.info(LTag.PUMP, "Execute Command: " + commandType.name) + val task = MedtronicUITask(injector, commandType, parameters) + medtronicUtil.setCurrentCommand(commandType) + task.execute(medtronicCommunicationManager) + + if (!task.isReceived) { + aapsLogger.warn(LTag.PUMP, "Reply not received for $commandType") + } + + task.postProcess(medtronicUIPostprocessor) + return task + } + + val invalidResponsesCount: Int + get() = medtronicCommunicationManager.notConnectedCount + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java deleted file mode 100644 index 8022384731..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java +++ /dev/null @@ -1,252 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; - -import org.joda.time.DateTimeZone; -import org.joda.time.Duration; - -import java.util.Date; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - - -/** - * Created by andy on 6/15/18. - */ - -@Singleton -public class MedtronicUIPostprocessor { - - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - private final ResourceHelper resourceHelper; - private final MedtronicUtil medtronicUtil; - private final MedtronicPumpStatus medtronicPumpStatus; - private final MedtronicPumpPlugin medtronicPumpPlugin; - - @Inject - public MedtronicUIPostprocessor( - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - ResourceHelper resourceHelper, - MedtronicUtil medtronicUtil, - MedtronicPumpStatus medtronicPumpStatus, - MedtronicPumpPlugin medtronicPumpPlugin) { - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - this.resourceHelper = resourceHelper; - this.medtronicUtil = medtronicUtil; - this.medtronicPumpStatus = medtronicPumpStatus; - this.medtronicPumpPlugin = medtronicPumpPlugin; - } - - - // this is mostly intended for command that return certain statuses (Remaining Insulin, ...), and - // where responses won't be directly used - void postProcessData(MedtronicUITask uiTask) { - - switch (uiTask.commandType) { - - case SetBasalProfileSTD: { - Boolean response = (Boolean) uiTask.returnData; - - if (response) { - BasalProfile basalProfile = (BasalProfile) uiTask.getParameter(0); - - medtronicPumpStatus.basalsByHour = basalProfile.getProfilesByHour(medtronicPumpPlugin.getPumpDescription().getPumpType()); - } - } - break; - - case GetBasalProfileSTD: { - BasalProfile basalProfile = (BasalProfile) uiTask.returnData; - - try { - Double[] profilesByHour = basalProfile.getProfilesByHour(medtronicPumpPlugin.getPumpDescription().getPumpType()); - - if (profilesByHour != null) { - medtronicPumpStatus.basalsByHour = profilesByHour; - medtronicPumpStatus.basalProfileStatus = BasalProfileStatus.ProfileOK; - } else { - uiTask.responseType = MedtronicUIResponseType.Error; - uiTask.errorDescription = "No profile found."; - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Basal Profile was NOT valid. [%s]", basalProfile.basalProfileToStringError())); - } - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Basal Profile was returned, but was invalid. [%s]", basalProfile.basalProfileToStringError())); - uiTask.responseType = MedtronicUIResponseType.Error; - uiTask.errorDescription = "No profile found."; - } - } - break; - - case SetBolus: { - medtronicPumpStatus.lastBolusAmount = uiTask.getDoubleFromParameters(0); - medtronicPumpStatus.lastBolusTime = new Date(); - } - break; - - case GetRemainingInsulin: { - medtronicPumpStatus.reservoirRemainingUnits = (Float) uiTask.returnData; - } - break; - - case CancelTBR: { - medtronicPumpStatus.tempBasalStart = null; - medtronicPumpStatus.tempBasalAmount = null; - medtronicPumpStatus.tempBasalLength = null; - } - break; - - case GetRealTimeClock: { - processTime(uiTask); - } - break; - - case SetRealTimeClock: { - boolean response = (Boolean) uiTask.returnData; - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "New time was %s set.", response ? "" : "NOT")); - - if (response) { - medtronicUtil.getPumpTime().timeDifference = 0; - } - } - break; - - - case GetBatteryStatus: { - BatteryStatusDTO batteryStatusDTO = (BatteryStatusDTO) uiTask.returnData; - - medtronicPumpStatus.batteryRemaining = batteryStatusDTO.getCalculatedPercent(medtronicPumpStatus.batteryType); - - if (batteryStatusDTO.voltage != null) { - medtronicPumpStatus.batteryVoltage = batteryStatusDTO.voltage; - } - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "BatteryStatus: %s", batteryStatusDTO.toString())); - - } - break; - - case PumpModel: { - if (medtronicPumpStatus.medtronicDeviceType != medtronicUtil.getMedtronicPumpModel()) { - aapsLogger.warn(LTag.PUMP, "Configured pump is different then pump detected !"); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, resourceHelper, rxBus); - } - } - break; - - case Settings_512: - case Settings: { - postProcessSettings(uiTask); - } - break; - - // no postprocessing - - default: - break; - //aapsLogger.error(LTag.PUMP, "Post-processing not implemented for {}.", uiTask.commandType.name()); - - } - - } - - - private void processTime(MedtronicUITask uiTask) { - - ClockDTO clockDTO = (ClockDTO) uiTask.returnData; - - Duration dur = new Duration(clockDTO.pumpTime.toDateTime(DateTimeZone.UTC), - clockDTO.localDeviceTime.toDateTime(DateTimeZone.UTC)); - - clockDTO.timeDifference = (int) dur.getStandardSeconds(); - - medtronicUtil.setPumpTime(clockDTO); - - aapsLogger.debug(LTag.PUMP, "Pump Time: " + clockDTO.localDeviceTime + ", DeviceTime=" + clockDTO.pumpTime + // - ", diff: " + dur.getStandardSeconds() + " s"); - -// if (dur.getStandardMinutes() >= 10) { -// if (isLogEnabled()) -// LOG.warn("Pump clock needs update, pump time: " + clockDTO.pumpTime.toString("HH:mm:ss") + " (difference: " -// + dur.getStandardSeconds() + " s)"); -// sendNotification(MedtronicNotificationType.PumpWrongTimeUrgent); -// } else if (dur.getStandardMinutes() >= 4) { -// if (isLogEnabled()) -// LOG.warn("Pump clock needs update, pump time: " + clockDTO.pumpTime.toString("HH:mm:ss") + " (difference: " -// + dur.getStandardSeconds() + " s)"); -// sendNotification(MedtronicNotificationType.PumpWrongTimeNormal); -// } - - } - - - private void postProcessSettings(MedtronicUITask uiTask) { - - Map settings = (Map) uiTask.returnData; - - medtronicUtil.setSettings(settings); - - PumpSettingDTO checkValue; - - medtronicPumpPlugin.getRileyLinkService().verifyConfiguration(); - - // check profile - if (!"Yes".equals(settings.get("PCFG_BASAL_PROFILES_ENABLED").value)) { - aapsLogger.error(LTag.PUMP, "Basal profiles are not enabled on pump."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpBasalProfilesNotEnabled, resourceHelper, rxBus); - - } else { - checkValue = settings.get("PCFG_ACTIVE_BASAL_PROFILE"); - - if (!"STD".equals(checkValue.value)) { - aapsLogger.error("Basal profile set on pump is incorrect (must be STD)."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpIncorrectBasalProfileSelected, resourceHelper, rxBus); - } - } - - // TBR - - checkValue = settings.get("PCFG_TEMP_BASAL_TYPE"); - - if (!"Units".equals(checkValue.value)) { - aapsLogger.error("Wrong TBR type set on pump (must be Absolute)."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongTBRTypeSet, resourceHelper, rxBus); - } - - // MAXes - - checkValue = settings.get("PCFG_MAX_BOLUS"); - - if (!MedtronicUtil.isSame(Double.parseDouble(checkValue.value), medtronicPumpStatus.maxBolus)) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Wrong Max Bolus set on Pump (current=%s, required=%.2f).", checkValue.value, medtronicPumpStatus.maxBolus)); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongMaxBolusSet, resourceHelper, rxBus, medtronicPumpStatus.maxBolus); - } - - checkValue = settings.get("PCFG_MAX_BASAL"); - - if (!MedtronicUtil.isSame(Double.parseDouble(checkValue.value), medtronicPumpStatus.maxBasal)) { - aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Wrong Max Basal set on Pump (current=%s, required=%.2f).", checkValue.value, medtronicPumpStatus.maxBasal)); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongMaxBasalSet, resourceHelper, rxBus, medtronicPumpStatus.maxBasal); - } - - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.kt new file mode 100644 index 0000000000..9ad461c0d6 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.kt @@ -0,0 +1,196 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.utils.resources.ResourceHelper +import org.joda.time.DateTimeZone +import org.joda.time.Duration +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 6/15/18. + */ +@Singleton +class MedtronicUIPostprocessor @Inject constructor( + private val aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + private val resourceHelper: ResourceHelper, + private val medtronicUtil: MedtronicUtil, + private val medtronicPumpStatus: MedtronicPumpStatus, + private val medtronicPumpPlugin: MedtronicPumpPlugin) { + + // this is mostly intended for command that return certain statuses (Remaining Insulin, ...), and + // where responses won't be directly used + fun postProcessData(uiTask: MedtronicUITask) { + when (uiTask.commandType) { + MedtronicCommandType.SetBasalProfileSTD -> { + val response = uiTask.result as Boolean? + if (response != null && response) { + val basalProfile = uiTask.getParameter(0) as BasalProfile + aapsLogger.debug("D: basal profile returned after set: $basalProfile") + + medtronicPumpStatus.basalsByHour = basalProfile.getProfilesByHour(medtronicPumpPlugin.pumpDescription.pumpType) + } + } + + MedtronicCommandType.GetBasalProfileSTD -> { + val basalProfile = uiTask.result as BasalProfile? + + //aapsLogger.debug("D: basal profile on read: " + basalProfile); + try { + if (basalProfile != null) { + val profilesByHour = basalProfile.getProfilesByHour(medtronicPumpPlugin.pumpDescription.pumpType) + if (!BasalProfile.isBasalProfileByHourUndefined(profilesByHour)) { + medtronicPumpStatus.basalsByHour = profilesByHour + medtronicPumpStatus.basalProfileStatus = BasalProfileStatus.ProfileOK + //aapsLogger.debug("D: basal profile on read: basalsByHour: " + BasalProfile.getProfilesByHourToString(medtronicPumpStatus.basalsByHour)); + } else { + uiTask.responseType = MedtronicUIResponseType.Error + uiTask.errorDescription = "No profile found." + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Basal Profile was NOT valid. [%s]", basalProfile.basalProfileToStringError())) + } + } + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Basal Profile was returned, but was invalid. [%s]", basalProfile!!.basalProfileToStringError())) + uiTask.responseType = MedtronicUIResponseType.Error + uiTask.errorDescription = "No profile found." + } + } + + MedtronicCommandType.SetBolus -> { + medtronicPumpStatus.lastBolusAmount = uiTask.getDoubleFromParameters(0) + medtronicPumpStatus.lastBolusTime = Date() + } + + MedtronicCommandType.GetRemainingInsulin -> { + medtronicPumpStatus.reservoirRemainingUnits = uiTask.result as Double + } + + MedtronicCommandType.CancelTBR -> { + medtronicPumpStatus.tempBasalStart = null + medtronicPumpStatus.tempBasalAmount = null + medtronicPumpStatus.tempBasalLength = null + } + + MedtronicCommandType.GetRealTimeClock -> { + processTime(uiTask) + } + + MedtronicCommandType.SetRealTimeClock -> { + val response = uiTask.result as Boolean + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "New time was %s set.", if (response) "" else "NOT")) + if (response) { + medtronicUtil.pumpTime!!.timeDifference = 0 + } + } + + MedtronicCommandType.GetBatteryStatus -> { + val batteryStatusDTO = uiTask.result as BatteryStatusDTO? + if (batteryStatusDTO != null) { + medtronicPumpStatus.batteryRemaining = batteryStatusDTO.getCalculatedPercent(medtronicPumpStatus.batteryType) + if (batteryStatusDTO.voltage != null) { + medtronicPumpStatus.batteryVoltage = batteryStatusDTO.voltage + } + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "BatteryStatus: %s", batteryStatusDTO.toString())) + } else { + medtronicPumpStatus.batteryVoltage = null + } + } + + MedtronicCommandType.PumpModel -> { + if (medtronicPumpStatus.medtronicDeviceType !== medtronicUtil.medtronicPumpModel) { + aapsLogger.warn(LTag.PUMP, "Configured pump is different then pump detected !") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, resourceHelper, rxBus) + } + } + + MedtronicCommandType.Settings_512, + MedtronicCommandType.Settings -> { + postProcessSettings(uiTask) + } + + else -> { + } + } + } + + private fun processTime(uiTask: MedtronicUITask) { + val clockDTO = uiTask.result as ClockDTO? + if (clockDTO != null) { + val dur = Duration(clockDTO.pumpTime.toDateTime(DateTimeZone.UTC), + clockDTO.localDeviceTime.toDateTime(DateTimeZone.UTC)) + clockDTO.timeDifference = dur.standardSeconds.toInt() + medtronicUtil.pumpTime = clockDTO + aapsLogger.debug(LTag.PUMP, "Pump Time: " + clockDTO.localDeviceTime + ", DeviceTime=" + clockDTO.pumpTime + // + ", diff: " + dur.standardSeconds + " s") + } else { + aapsLogger.debug(LTag.PUMP, "Problem with returned data: " + medtronicUtil.gsonInstance.toJson(uiTask.result)) + } + } + + private fun postProcessSettings(uiTask: MedtronicUITask) { + val settings = uiTask.result as? Map + + if (settings == null) + return + + medtronicUtil.settings = settings + var checkValue: PumpSettingDTO + medtronicPumpPlugin.rileyLinkService.verifyConfiguration() + + // check profile + if (settings.containsKey("PCFG_BASAL_PROFILES_ENABLED") && settings.containsKey("PCFG_ACTIVE_BASAL_PROFILE")) { + checkValue = settings["PCFG_BASAL_PROFILES_ENABLED"]!! + if ("Yes" != checkValue.value) { + aapsLogger.error(LTag.PUMP, "Basal profiles are not enabled on pump.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpBasalProfilesNotEnabled, resourceHelper, rxBus) + } else { + checkValue = settings["PCFG_ACTIVE_BASAL_PROFILE"]!! + if ("STD" != checkValue.value) { + aapsLogger.error("Basal profile set on pump is incorrect (must be STD).") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpIncorrectBasalProfileSelected, resourceHelper, rxBus) + } + } + } + + // TBR + if (settings.containsKey("PCFG_TEMP_BASAL_TYPE")) { + if ("Units" != settings["PCFG_TEMP_BASAL_TYPE"]!!.value) { + aapsLogger.error("Wrong TBR type set on pump (must be Absolute).") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongTBRTypeSet, resourceHelper, rxBus) + } + } + + // MAXes + if (settings.containsKey("PCFG_MAX_BOLUS")) { + checkValue = settings["PCFG_MAX_BOLUS"]!! + if (!MedtronicUtil.isSame(checkValue.value.toDouble(), medtronicPumpStatus.maxBolus!!)) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Wrong Max Bolus set on Pump (current=%s, required=%.2f).", checkValue.value, medtronicPumpStatus.maxBolus)) + medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongMaxBolusSet, resourceHelper, rxBus, medtronicPumpStatus.maxBolus) + } + } + + if (settings.containsKey("PCFG_MAX_BASAL")) { + checkValue = settings["PCFG_MAX_BASAL"]!! + if (!MedtronicUtil.isSame(checkValue.value.toDouble(), medtronicPumpStatus.maxBasal!!)) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Wrong Max Basal set on Pump (current=%s, required=%.2f).", checkValue.value, medtronicPumpStatus.maxBasal)) + medtronicUtil.sendNotification(MedtronicNotificationType.PumpWrongMaxBasalSet, resourceHelper, rxBus, medtronicPumpStatus.maxBasal) + } + } + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java deleted file mode 100644 index f8ef1ee190..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java +++ /dev/null @@ -1,234 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; - -import org.joda.time.LocalDateTime; - -import java.util.Locale; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; -import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by andy on 6/14/18. - */ - -public class MedtronicUITask { - - @Inject RxBusWrapper rxBus; - @Inject AAPSLogger aapsLogger; - @Inject MedtronicPumpStatus medtronicPumpStatus; - @Inject MedtronicUtil medtronicUtil; - - private final HasAndroidInjector injector; - - public MedtronicCommandType commandType; - public Object returnData; - String errorDescription; - // boolean invalid = false; - private Object[] parameters; - // private boolean received; - MedtronicUIResponseType responseType; - - - public MedtronicUITask(HasAndroidInjector injector, MedtronicCommandType commandType) { - this.injector = injector; - this.injector.androidInjector().inject(this); - this.commandType = commandType; - } - - - public MedtronicUITask(HasAndroidInjector injector, MedtronicCommandType commandType, Object... parameters) { - this.injector = injector; - this.injector.androidInjector().inject(this); - this.commandType = commandType; - this.parameters = parameters; - } - - - public void execute(MedtronicCommunicationManager communicationManager) { - - aapsLogger.debug(LTag.PUMP, "MedtronicUITask: @@@ In execute. " + commandType); - - switch (commandType) { - case PumpModel: { - returnData = communicationManager.getPumpModel(); - } - break; - - case GetBasalProfileSTD: { - returnData = communicationManager.getBasalProfile(); - } - break; - - case GetRemainingInsulin: { - returnData = communicationManager.getRemainingInsulin(); - } - break; - - case GetRealTimeClock: { - returnData = communicationManager.getPumpTime(); - medtronicUtil.setPumpTime(null); - } - break; - - case SetRealTimeClock: { - returnData = communicationManager.setPumpTime(); - } - break; - - case GetBatteryStatus: { - returnData = communicationManager.getRemainingBattery(); - } - break; - - case SetTemporaryBasal: { - TempBasalPair tbr = getTBRSettings(); - if (tbr != null) { - returnData = communicationManager.setTBR(tbr); - } - } - break; - - case ReadTemporaryBasal: { - returnData = communicationManager.getTemporaryBasal(); - } - break; - - - case Settings: - case Settings_512: { - returnData = communicationManager.getPumpSettings(); - } - break; - - case SetBolus: { - Double amount = getDoubleFromParameters(0); - - if (amount != null) - returnData = communicationManager.setBolus(amount); - } - break; - - case CancelTBR: { - returnData = communicationManager.cancelTBR(); - } - break; - - case SetBasalProfileSTD: - case SetBasalProfileA: { - BasalProfile profile = (BasalProfile) parameters[0]; - - returnData = communicationManager.setBasalProfile(profile); - } - break; - - case GetHistoryData: { - returnData = communicationManager.getPumpHistory((PumpHistoryEntry) parameters[0], - (LocalDateTime) parameters[1]); - } - break; - - default: { - aapsLogger.warn(LTag.PUMP, String.format(Locale.ENGLISH, "This commandType is not supported (yet) - %s.", commandType)); - // invalid = true; - responseType = MedtronicUIResponseType.Invalid; - } - - } - - if (responseType == null) { - if (returnData == null) { - errorDescription = communicationManager.getErrorResponse(); - this.responseType = MedtronicUIResponseType.Error; - } else { - this.responseType = MedtronicUIResponseType.Data; - } - } - - } - - - private TempBasalPair getTBRSettings() { - return new TempBasalPair(getDoubleFromParameters(0), // - false, // - getIntegerFromParameters(1)); - } - - - private Float getFloatFromParameters(int index) { - return (Float) parameters[index]; - } - - - Double getDoubleFromParameters(int index) { - return (Double) parameters[index]; - } - - - private Integer getIntegerFromParameters(int index) { - return (Integer) parameters[index]; - } - - - public Object getResult() { - return returnData; - } - - - public boolean isReceived() { - return (returnData != null || errorDescription != null); - } - - - void postProcess(MedtronicUIPostprocessor postprocessor) { - - aapsLogger.debug(LTag.PUMP, "MedtronicUITask: @@@ In execute. " + commandType); - - if (responseType == MedtronicUIResponseType.Data) { - postprocessor.postProcessData(this); - } - - if (responseType == MedtronicUIResponseType.Invalid) { - rxBus.send(new EventRileyLinkDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, - "Unsupported command in MedtronicUITask")); - } else if (responseType == MedtronicUIResponseType.Error) { - rxBus.send(new EventRileyLinkDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, - errorDescription)); - } else { - rxBus.send(new EventMedtronicPumpValuesChanged()); - medtronicPumpStatus.setLastCommunicationToNow(); - } - - medtronicUtil.setCurrentCommand(null); - } - - - public boolean hasData() { - return (responseType == MedtronicUIResponseType.Data); - } - - - Object getParameter(int index) { - return parameters[index]; - } - - - public MedtronicUIResponseType getResponseType() { - return this.responseType; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.kt new file mode 100644 index 0000000000..a401e061fa --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.kt @@ -0,0 +1,178 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState +import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject + +/** + * Created by andy on 6/14/18. + */ +class MedtronicUITask { + + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var medtronicPumpStatus: MedtronicPumpStatus + @Inject lateinit var medtronicUtil: MedtronicUtil + + private val injector: HasAndroidInjector + var commandType: MedtronicCommandType + var result: Any? = null + var errorDescription: String? = null + var parameters: List? = null + var responseType: MedtronicUIResponseType? = null + + constructor(injector: HasAndroidInjector, commandType: MedtronicCommandType) { + this.injector = injector + this.injector.androidInjector().inject(this) + this.commandType = commandType + } + + constructor(injector: HasAndroidInjector, commandType: MedtronicCommandType, parameters: List?) { + this.injector = injector + this.injector.androidInjector().inject(this) + this.commandType = commandType + this.parameters = parameters //as Array + } + + fun execute(communicationManager: MedtronicCommunicationManager) { + aapsLogger.debug(LTag.PUMP, "MedtronicUITask: @@@ In execute. $commandType") + when (commandType) { + MedtronicCommandType.PumpModel -> { + result = communicationManager.getPumpModel() + } + + MedtronicCommandType.GetBasalProfileSTD -> { + result = communicationManager.getBasalProfile() + } + + MedtronicCommandType.GetRemainingInsulin -> { + result = communicationManager.getRemainingInsulin() + } + + MedtronicCommandType.GetRealTimeClock -> { + result = communicationManager.getPumpTime() + //medtronicUtil.pumpTime = null + } + + MedtronicCommandType.SetRealTimeClock -> { + result = communicationManager.setPumpTime() + } + + MedtronicCommandType.GetBatteryStatus -> { + result = communicationManager.getRemainingBattery() + } + + MedtronicCommandType.SetTemporaryBasal -> { + val tbr = getTbrSettings() + if (tbr != null) { + result = communicationManager.setTemporaryBasal(tbr) + } + } + + MedtronicCommandType.ReadTemporaryBasal -> { + result = communicationManager.getTemporaryBasal() + } + + MedtronicCommandType.Settings, MedtronicCommandType.Settings_512 -> { + result = communicationManager.getPumpSettings() + } + + MedtronicCommandType.SetBolus -> { + val amount = getDoubleFromParameters(0) + if (amount != null) result = communicationManager.setBolus(amount) + } + + MedtronicCommandType.CancelTBR -> { + result = communicationManager.cancelTBR() + } + + MedtronicCommandType.SetBasalProfileSTD, + MedtronicCommandType.SetBasalProfileA -> { + val profile = parameters!![0] as BasalProfile + result = communicationManager.setBasalProfile(profile) + } + + MedtronicCommandType.GetHistoryData -> { + result = communicationManager.getPumpHistory(parameters!![0] as PumpHistoryEntry?, + parameters!![1] as LocalDateTime?) + } + + else -> { + aapsLogger.warn(LTag.PUMP, String.format(Locale.ENGLISH, "This commandType is not supported (yet) - %s.", commandType)) + // invalid = true; + responseType = MedtronicUIResponseType.Invalid + } + } + if (responseType == null) { + if (result == null) { + errorDescription = communicationManager.errorResponse + responseType = MedtronicUIResponseType.Error + } else { + responseType = MedtronicUIResponseType.Data + } + } + } + + private fun getTbrSettings(): TempBasalPair? { + return TempBasalPair(getDoubleFromParameters(0)!!, // + false, // + getIntegerFromParameters(1)) + } + + private fun getFloatFromParameters(index: Int): Float { + return parameters!![index] as Float + } + + fun getDoubleFromParameters(index: Int): Double? { + return parameters!![index] as Double? + } + + private fun getIntegerFromParameters(index: Int): Int { + return parameters!![index] as Int + } + + val isReceived: Boolean + get() = result != null || errorDescription != null + + fun postProcess(postprocessor: MedtronicUIPostprocessor) { + aapsLogger.debug(LTag.PUMP, "MedtronicUITask: @@@ In execute. $commandType") + if (responseType === MedtronicUIResponseType.Data) { + postprocessor.postProcessData(this) + } + if (responseType === MedtronicUIResponseType.Invalid) { + rxBus.send(EventRileyLinkDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, + "Unsupported command in MedtronicUITask")) + } else if (responseType === MedtronicUIResponseType.Error) { + rxBus.send(EventRileyLinkDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, + errorDescription)) + } else { + rxBus.send(EventMedtronicPumpValuesChanged()) + medtronicPumpStatus.setLastCommunicationToNow() + } + medtronicUtil.setCurrentCommand(null) + } + + fun hasData(): Boolean { + return responseType === MedtronicUIResponseType.Data + } + + fun getParameter(index: Int): Any? { + return parameters!![index] + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java deleted file mode 100644 index cebbe9cec4..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java +++ /dev/null @@ -1,1605 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import org.apache.commons.lang3.StringUtils; -import org.joda.time.LocalDateTime; -import org.joda.time.Minutes; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.db.DbObjectBase; -import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.db.Treatment; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.PumpSync; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusWizardDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalProcessDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.plugins.treatments.TreatmentUpdateReturn; -import info.nightscout.androidaps.utils.Round; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - - -/** - * Created by andy on 10/12/18. - */ - -// TODO: After release we need to refactor how data is retrieved from pump, each entry in history needs to be marked, and sorting -// needs to happen according those markings, not on time stamp (since AAPS can change time anytime it drifts away). This -// needs to include not returning any records if TZ goes into -x area. To fully support this AAPS would need to take note of -// all times that time changed (TZ, DST, etc.). Data needs to be returned in batches (time_changed batches, so that we can -// handle it. It would help to assign sort_ids to items (from oldest (1) to newest (x) - -// All things marked with "TODO: Fix db code" needs to be updated in new 2.5 database code - -@Singleton -public class MedtronicHistoryData { - - private final HasAndroidInjector injector; - private final AAPSLogger aapsLogger; - private final SP sp; - private final ActivePlugin activePlugin; - private final MedtronicUtil medtronicUtil; - private final MedtronicPumpHistoryDecoder medtronicPumpHistoryDecoder; - private final MedtronicPumpStatus medtronicPumpStatus; - private final DatabaseHelperInterface databaseHelper; - private final PumpSync pumpSync; - - private final List allHistory; - private List newHistory = null; - - private boolean isInit = false; - - private Gson gson; // cannot be initialized in constructor because of injection - private Gson gsonCore; // cannot be initialized in constructor because of injection - - private ClockDTO pumpTime; - - private long lastIdUsed = 0; - - /** - * Double bolus debug. We seem to have small problem with double Boluses (or sometimes also missing boluses - * from history. This flag turns on debugging for that (default is off=false)... Debugging is pretty detailed, - * so log files will get bigger. - * Note: June 2020. Since this seems to be fixed, I am disabling this per default. I will leave code inside - * in case we need it again. Code that turns this on is commented out RileyLinkMedtronicService#verifyConfiguration() - */ - public static final boolean doubleBolusDebug = false; - - @Inject - public MedtronicHistoryData( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - SP sp, - ActivePlugin activePlugin, - MedtronicUtil medtronicUtil, - MedtronicPumpHistoryDecoder medtronicPumpHistoryDecoder, - MedtronicPumpStatus medtronicPumpStatus, - DatabaseHelperInterface databaseHelperInterface, - PumpSync pumpSync - ) { - this.allHistory = new ArrayList<>(); - - this.injector = injector; - this.aapsLogger = aapsLogger; - this.sp = sp; - this.activePlugin = activePlugin; - this.medtronicUtil = medtronicUtil; - this.medtronicPumpHistoryDecoder = medtronicPumpHistoryDecoder; - this.medtronicPumpStatus = medtronicPumpStatus; - this.databaseHelper = databaseHelperInterface; - this.pumpSync = pumpSync; - } - - private Gson gson() { - if (gson == null) gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - return gson; - } - - private Gson gsonCore() { - if (gsonCore == null) gsonCore = new GsonBuilder().create(); - return gsonCore; - } - - /** - * Add New History entries - * - * @param result PumpHistoryResult instance - */ - public void addNewHistory(PumpHistoryResult result) { - - List validEntries = result.getValidEntries(); - - List newEntries = new ArrayList<>(); - - for (PumpHistoryEntry validEntry : validEntries) { - - if (!this.allHistory.contains(validEntry)) { - newEntries.add(validEntry); - } - } - - this.newHistory = newEntries; - - showLogs("List of history (before filtering): [" + this.newHistory.size() + "]", gson().toJson(this.newHistory)); - } - - - private void showLogs(String header, String data) { - if (header != null) { - aapsLogger.debug(LTag.PUMP, header); - } - - if (StringUtils.isNotBlank(data)) { - for (final String token : StringUtil.splitString(data, 3500)) { - aapsLogger.debug(LTag.PUMP, token); - } - } else { - aapsLogger.debug(LTag.PUMP, "No data."); - } - } - - - public List getAllHistory() { - return this.allHistory; - } - - - public void filterNewEntries() { - - List newHistory2 = new ArrayList<>(); - List TBRs = new ArrayList<>(); - List bolusEstimates = new ArrayList<>(); - long atechDate = DateTimeUtil.toATechDate(new GregorianCalendar()); - - //aapsLogger.debug(LTag.PUMP, "Filter new entries: Before {}", newHistory); - - if (!isCollectionEmpty(newHistory)) { - - for (PumpHistoryEntry pumpHistoryEntry : newHistory) { - - if (!this.allHistory.contains(pumpHistoryEntry)) { - - PumpHistoryEntryType type = pumpHistoryEntry.getEntryType(); - - if (type == PumpHistoryEntryType.TempBasalRate || type == PumpHistoryEntryType.TempBasalDuration) { - TBRs.add(pumpHistoryEntry); - } else if (type == PumpHistoryEntryType.BolusWizard || type == PumpHistoryEntryType.BolusWizard512) { - bolusEstimates.add(pumpHistoryEntry); - newHistory2.add(pumpHistoryEntry); - } else { - - if (type == PumpHistoryEntryType.EndResultTotals) { - if (!DateTimeUtil.isSameDay(atechDate, pumpHistoryEntry.atechDateTime)) { - newHistory2.add(pumpHistoryEntry); - } - } else { - newHistory2.add(pumpHistoryEntry); - } - } - } - } - - TBRs = preProcessTBRs(TBRs); - - if (bolusEstimates.size() > 0) { - extendBolusRecords(bolusEstimates, newHistory2); - } - - newHistory2.addAll(TBRs); - - this.newHistory = newHistory2; - - sort(this.newHistory); - } - - aapsLogger.debug(LTag.PUMP, "New History entries found: " + this.newHistory.size()); - - showLogs("List of history (after filtering): [" + this.newHistory.size() + "]", gson().toJson(this.newHistory)); - - } - - private void extendBolusRecords(List bolusEstimates, List newHistory2) { - - List boluses = getFilteredItems(newHistory2, PumpHistoryEntryType.Bolus); - - for (PumpHistoryEntry bolusEstimate : bolusEstimates) { - for (PumpHistoryEntry bolus : boluses) { - if (bolusEstimate.atechDateTime.equals(bolus.atechDateTime)) { - bolus.addDecodedData("Estimate", bolusEstimate.getDecodedData().get("Object")); - } - } - } - } - - - public void finalizeNewHistoryRecords() { - - if ((newHistory == null) || (newHistory.size() == 0)) - return; - - PumpHistoryEntry pheLast = newHistory.get(0); - - // find last entry - for (PumpHistoryEntry pumpHistoryEntry : newHistory) { - if (pumpHistoryEntry.atechDateTime != null && pumpHistoryEntry.isAfter(pheLast.atechDateTime)) { - pheLast = pumpHistoryEntry; - } - } - - // add new entries - Collections.reverse(newHistory); - - for (PumpHistoryEntry pumpHistoryEntry : newHistory) { - - if (!this.allHistory.contains(pumpHistoryEntry)) { - lastIdUsed++; - pumpHistoryEntry.id = lastIdUsed; - this.allHistory.add(pumpHistoryEntry); - } - - } - - - if (pheLast == null) // if we don't have any valid record we don't do the filtering and setting - return; - - this.setLastHistoryRecordTime(pheLast.atechDateTime); - sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, pheLast.atechDateTime); - - LocalDateTime dt = null; - - try { - dt = DateTimeUtil.toLocalDateTime(pheLast.atechDateTime); - } catch (Exception ex) { - aapsLogger.error("Problem decoding date from last record: " + pheLast); - } - - if (dt != null) { - - dt = dt.minusDays(1); // we keep 24 hours - - long dtRemove = DateTimeUtil.toATechDate(dt); - - List removeList = new ArrayList<>(); - - for (PumpHistoryEntry pumpHistoryEntry : allHistory) { - - if (!pumpHistoryEntry.isAfter(dtRemove)) { - removeList.add(pumpHistoryEntry); - } - } - - this.allHistory.removeAll(removeList); - - this.sort(this.allHistory); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "All History records [afterFilterCount=%d, removedItemsCount=%d, newItemsCount=%d]", - allHistory.size(), removeList.size(), newHistory.size())); - } else { - aapsLogger.error("Since we couldn't determine date, we don't clean full history. This is just workaround."); - } - - this.newHistory.clear(); - } - - - public boolean hasRelevantConfigurationChanged() { - return getStateFromFilteredList( // - PumpHistoryEntryType.ChangeBasalPattern, // - PumpHistoryEntryType.ClearSettings, // - PumpHistoryEntryType.SaveSettings, // - PumpHistoryEntryType.ChangeMaxBolus, // - PumpHistoryEntryType.ChangeMaxBasal, // - PumpHistoryEntryType.ChangeTempBasalType); - } - - - private boolean isCollectionEmpty(List col) { - return (col == null || col.isEmpty()); - } - - private boolean isCollectionNotEmpty(List col) { - return (col != null && !col.isEmpty()); - } - - - public boolean isPumpSuspended() { - - List items = getDataForPumpSuspends(); - - showLogs("isPumpSuspended: ", gson().toJson(items)); - - if (isCollectionNotEmpty(items)) { - - PumpHistoryEntryType pumpHistoryEntryType = items.get(0).getEntryType(); - - boolean isSuspended = !(pumpHistoryEntryType == PumpHistoryEntryType.TempBasalCombined || // - pumpHistoryEntryType == PumpHistoryEntryType.BasalProfileStart || // - pumpHistoryEntryType == PumpHistoryEntryType.Bolus || // - pumpHistoryEntryType == PumpHistoryEntryType.ResumePump || // - pumpHistoryEntryType == PumpHistoryEntryType.BatteryChange || // - pumpHistoryEntryType == PumpHistoryEntryType.Prime); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "isPumpSuspended. Last entry type=%s, isSuspended=%b", pumpHistoryEntryType, isSuspended)); - - return isSuspended; - } else - return false; - - } - - - private List getDataForPumpSuspends() { - - List newAndAll = new ArrayList<>(); - - if (isCollectionNotEmpty(this.allHistory)) { - newAndAll.addAll(this.allHistory); - } - - if (isCollectionNotEmpty(this.newHistory)) { - - for (PumpHistoryEntry pumpHistoryEntry : newHistory) { - if (!newAndAll.contains(pumpHistoryEntry)) { - newAndAll.add(pumpHistoryEntry); - } - } - } - - if (newAndAll.isEmpty()) - return newAndAll; - - this.sort(newAndAll); - - List newAndAll2 = getFilteredItems(newAndAll, // - PumpHistoryEntryType.Bolus, // - PumpHistoryEntryType.TempBasalCombined, // - PumpHistoryEntryType.Prime, // - PumpHistoryEntryType.SuspendPump, // - PumpHistoryEntryType.ResumePump, // - PumpHistoryEntryType.Rewind, // - PumpHistoryEntryType.NoDeliveryAlarm, // - PumpHistoryEntryType.BatteryChange, // - PumpHistoryEntryType.BasalProfileStart); - - newAndAll2 = filterPumpSuspend(newAndAll2, 10); - - return newAndAll2; - } - - - private List filterPumpSuspend(List newAndAll, int filterCount) { - - if (newAndAll.size() <= filterCount) { - return newAndAll; - } - - List newAndAllOut = new ArrayList<>(); - - for (int i = 0; i < filterCount; i++) { - newAndAllOut.add(newAndAll.get(i)); - } - - return newAndAllOut; - } - - - /** - * Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime) - */ - public void processNewHistoryData() { - - // TODO: Fix db code - // Prime (for reseting autosense) - List primeRecords = getFilteredItems(PumpHistoryEntryType.Prime); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Prime [count=%d, items=%s]", primeRecords.size(), gson().toJson(primeRecords))); - - if (isCollectionNotEmpty(primeRecords)) { - try { - processPrime(primeRecords); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Prime entries: " + ex.getMessage(), ex); - throw ex; - } - } - - // Rewind (for marking insulin change) - List rewindRecords = getFilteredItems(PumpHistoryEntryType.Rewind); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Rewind [count=%d, items=%s]", rewindRecords.size(), gson().toJson(rewindRecords))); - - if (isCollectionNotEmpty(rewindRecords)) { - try { - processRewind(rewindRecords); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Rewind entries: " + ex.getMessage(), ex); - throw ex; - } - } - - // TDD - List tdds = getFilteredItems(PumpHistoryEntryType.EndResultTotals, getTDDType()); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TDD [count=%d, items=%s]", tdds.size(), gson().toJson(tdds))); - - if (isCollectionNotEmpty(tdds)) { - try { - processTDDs(tdds); - } catch (Exception ex) { - aapsLogger.error("ProcessHistoryData: Error processing TDD entries: " + ex.getMessage(), ex); - throw ex; - } - } - - pumpTime = medtronicUtil.getPumpTime(); - - // Bolus - List treatments = getFilteredItems(PumpHistoryEntryType.Bolus); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Bolus [count=%d, items=%s]", treatments.size(), gson().toJson(treatments))); - - if (treatments.size() > 0) { - try { - processBolusEntries(treatments); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Bolus entries: " + ex.getMessage(), ex); - throw ex; - } - } - - // TBR - List tbrs = getFilteredItems(PumpHistoryEntryType.TempBasalCombined); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TBRs Processed [count=%d, items=%s]", tbrs.size(), gson().toJson(tbrs))); - - if (tbrs.size() > 0) { - try { - processTBREntries(tbrs); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing TBR entries: " + ex.getMessage(), ex); - throw ex; - } - } - - // 'Delivery Suspend' - List suspends; - - try { - suspends = getSuspends(); - } catch (Exception ex) { - aapsLogger.error("ProcessHistoryData: Error getting Suspend entries: " + ex.getMessage(), ex); - throw ex; - } - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: 'Delivery Suspend' Processed [count=%d, items=%s]", suspends.size(), - gson().toJson(suspends))); - - if (isCollectionNotEmpty(suspends)) { - try { - processSuspends(suspends); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Suspends entries: " + ex.getMessage(), ex); - throw ex; - } - } - } - - - private void processPrime(List primeRecords) { - - long maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(new GregorianCalendar(), -30); - - long lastPrimeRecord = 0L; - - for (PumpHistoryEntry primeRecord : primeRecords) { - Object fixedAmount = primeRecord.getDecodedDataEntry("FixedAmount"); - - if (fixedAmount != null && ((float) fixedAmount) == 0.0f) { - // non-fixed primes are used to prime the tubing - // fixed primes are used to prime the cannula - // so skip the prime entry if it was not a fixed prime - continue; - } - - if (primeRecord.atechDateTime > maxAllowedTimeInPast) { - if (lastPrimeRecord < primeRecord.atechDateTime) { - lastPrimeRecord = primeRecord.atechDateTime; - } - } - } - - if (lastPrimeRecord != 0L) { - long lastPrimeFromAAPS = sp.getLong(MedtronicConst.Statistics.LastPrime, 0L); - - if (lastPrimeRecord != lastPrimeFromAAPS) { - uploadCareportalEvent(DateTimeUtil.toMillisFromATD(lastPrimeRecord), DetailedBolusInfo.EventType.CANNULA_CHANGE); - - sp.putLong(MedtronicConst.Statistics.LastPrime, lastPrimeRecord); - } - } - } - - private void processRewind(List rewindRecords) { - long maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(new GregorianCalendar(), -30); - long lastRewindRecord = 0L; - - for (PumpHistoryEntry rewindRecord : rewindRecords) { - if (rewindRecord.atechDateTime > maxAllowedTimeInPast) { - if (lastRewindRecord < rewindRecord.atechDateTime) { - lastRewindRecord = rewindRecord.atechDateTime; - } - } - } - - if (lastRewindRecord != 0L) { - long lastRewindFromAAPS = sp.getLong(MedtronicConst.Statistics.LastRewind, 0L); - - if (lastRewindRecord != lastRewindFromAAPS) { - uploadCareportalEvent(DateTimeUtil.toMillisFromATD(lastRewindRecord), DetailedBolusInfo.EventType.INSULIN_CHANGE); - - sp.putLong(MedtronicConst.Statistics.LastRewind, lastRewindRecord); - } - } - } - - - private void uploadCareportalEvent(long date, DetailedBolusInfo.EventType event) { - pumpSync.insertTherapyEventIfNewWithTimestamp(date, event, null, null, medtronicPumpStatus.pumpType, medtronicPumpStatus.serialNumber); - } - - private void processTDDs(List tddsIn) { - - List tdds = filterTDDs(tddsIn); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, getLogPrefix() + "TDDs found: %d.\n%s", tdds.size(), gson().toJson(tdds))); - - - for (PumpHistoryEntry tdd : tdds) { - DailyTotalsDTO totalsDTO = (DailyTotalsDTO) tdd.getDecodedData().get("Object"); - Boolean result = pumpSync.createOrUpdateTotalDailyDose( - totalsDTO.timestamp(), - totalsDTO.insulinBasal(), - totalsDTO.insulinBolus(), - totalsDTO.insulinTotal(), - null, - medtronicPumpStatus.pumpType, - medtronicPumpStatus.serialNumber - ); - if (result) aapsLogger.debug(LTag.PUMP, "TDD Added/Updated: " + totalsDTO); - } -/* - List tddsDb = databaseHelper.getTDDsForLastXDays(3); - - for (PumpHistoryEntry tdd : tdds) { - - TDD tddDbEntry = findTDD(tdd.atechDateTime, tddsDb); - - DailyTotalsDTO totalsDTO = (DailyTotalsDTO) tdd.getDecodedData().get("Object"); - - //aapsLogger.debug(LTag.PUMP, "DailyTotals: {}", totalsDTO); - - if (tddDbEntry == null) { - TDD tddNew = new TDD(); - totalsDTO.setTDD(tddNew); - - aapsLogger.debug(LTag.PUMP, "TDD Add: " + tddNew); - - databaseHelper.createOrUpdateTDD(tddNew); - - } else { - - if (!totalsDTO.doesEqual(tddDbEntry)) { - totalsDTO.setTDD(tddDbEntry); - - aapsLogger.debug(LTag.PUMP, "TDD Edit: " + tddDbEntry); - - databaseHelper.createOrUpdateTDD(tddDbEntry); - } - } - } - - */ - } - - - private enum ProcessHistoryRecord { - Bolus("Bolus"), - TBR("TBR"), - Suspend("Suspend"); - - private final String description; - - ProcessHistoryRecord(String desc) { - this.description = desc; - } - - public String getDescription() { - return this.description; - } - - } - - - private void processBolusEntries(List entryList) { - - long oldestTimestamp = getOldestTimestamp(entryList); - - List entriesFromHistory = getDatabaseEntriesByLastTimestamp(oldestTimestamp, ProcessHistoryRecord.Bolus); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: List (before filter): %s, FromDb=%s", gson().toJson(entryList), - gsonCore().toJson(entriesFromHistory))); - - filterOutAlreadyAddedEntries(entryList, entriesFromHistory); - - if (entryList.isEmpty()) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: EntryList was filtered out."); - return; - } - - filterOutNonInsulinEntries(entriesFromHistory); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: List (after filter): %s, FromDb=%s", gson().toJson(entryList), - gsonCore().toJson(entriesFromHistory))); - - if (isCollectionEmpty(entriesFromHistory)) { - for (PumpHistoryEntry treatment : entryList) { - aapsLogger.debug(LTag.PUMP, "Add Bolus (no db entry): " + treatment); - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: Add Bolus: FromDb=null, Treatment=" + treatment); - - addBolus(treatment, null); - } - } else { - for (PumpHistoryEntry treatment : entryList) { - DbObjectBase treatmentDb = findDbEntry(treatment, entriesFromHistory); - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "Add Bolus %s - (entryFromDb=%s) ", treatment, treatmentDb)); - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: Add Bolus: FromDb=%s, Treatment=%s", treatmentDb, treatment)); - - addBolus(treatment, (Treatment) treatmentDb); - } - } - } - - - private void filterOutNonInsulinEntries(List entriesFromHistory) { - // when we try to pair PumpHistory with AAPS treatments, we need to ignore all non-insulin entries - List removeList = new ArrayList<>(); - - for (DbObjectBase dbObjectBase : entriesFromHistory) { - - Treatment treatment = (Treatment) dbObjectBase; - - if (Round.isSame(treatment.insulin, 0d)) { - removeList.add(dbObjectBase); - } - } - - entriesFromHistory.removeAll(removeList); - } - - - private void processTBREntries(List entryList) { - - Collections.reverse(entryList); - - TempBasalPair tbr = (TempBasalPair) entryList.get(0).getDecodedDataEntry("Object"); - - boolean readOldItem = false; - - if (tbr.isCancelTBR()) { - PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.TempBasalCombined); - - if (oneMoreEntryFromHistory != null) { - entryList.add(0, oneMoreEntryFromHistory); - readOldItem = true; - } else { - entryList.remove(0); - } - } - - long oldestTimestamp = getOldestTimestamp(entryList); - - List entriesFromHistory = getDatabaseEntriesByLastTimestamp(oldestTimestamp, ProcessHistoryRecord.TBR); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, ProcessHistoryRecord.TBR.getDescription() + " List (before filter): %s, FromDb=%s", gson().toJson(entryList), - gson().toJson(entriesFromHistory))); - - - TempBasalProcessDTO processDTO = null; - List processList = new ArrayList<>(); - - for (PumpHistoryEntry treatment : entryList) { - - TempBasalPair tbr2 = (TempBasalPair) treatment.getDecodedDataEntry("Object"); - - if (tbr2.isCancelTBR()) { - - if (processDTO != null) { - processDTO.itemTwo = treatment; - - if (readOldItem) { - processDTO.processOperation = TempBasalProcessDTO.Operation.Edit; - readOldItem = false; - } - } else { - aapsLogger.error("processDTO was null - shouldn't happen. ItemTwo=" + treatment); - } - } else { - if (processDTO != null) { - processList.add(processDTO); - } - - processDTO = new TempBasalProcessDTO(); - processDTO.itemOne = treatment; - processDTO.processOperation = TempBasalProcessDTO.Operation.Add; - } - } - - if (processDTO != null) { - processList.add(processDTO); - } - - - if (isCollectionNotEmpty(processList)) { - - for (TempBasalProcessDTO tempBasalProcessDTO : processList) { - - if (tempBasalProcessDTO.processOperation == TempBasalProcessDTO.Operation.Edit) { - // edit - TemporaryBasal tempBasal = findTempBasalWithPumpId(tempBasalProcessDTO.itemOne.getPumpId(), entriesFromHistory); - - if (tempBasal != null) { - - tempBasal.durationInMinutes = tempBasalProcessDTO.getDuration(); - - databaseHelper.createOrUpdate(tempBasal); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "Edit " + ProcessHistoryRecord.TBR.getDescription() + " - (entryFromDb=%s) ", tempBasal)); - } else { - aapsLogger.error(LTag.PUMP, "TempBasal not found. Item: " + tempBasalProcessDTO.itemOne); - } - - } else { - // add - - PumpHistoryEntry treatment = tempBasalProcessDTO.itemOne; - - TempBasalPair tbr2 = (TempBasalPair) treatment.getDecodedData().get("Object"); - tbr2.setDurationMinutes(tempBasalProcessDTO.getDuration()); - - TemporaryBasal tempBasal = findTempBasalWithPumpId(tempBasalProcessDTO.itemOne.getPumpId(), entriesFromHistory); - - if (tempBasal == null) { - DbObjectBase treatmentDb = findDbEntry(treatment, entriesFromHistory); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "Add " + ProcessHistoryRecord.TBR.getDescription() + " %s - (entryFromDb=%s) ", treatment, treatmentDb)); - - addTBR(treatment, (TemporaryBasal) treatmentDb); - } else { - // this shouldn't happen - if (tempBasal.durationInMinutes != tempBasalProcessDTO.getDuration()) { - aapsLogger.debug(LTag.PUMP, "Found entry with wrong duration (shouldn't happen)... updating"); - tempBasal.durationInMinutes = tempBasalProcessDTO.getDuration(); - } - - } - } // if - } // for - - } // collection - } - - - private TemporaryBasal findTempBasalWithPumpId(long pumpId, List entriesFromHistory) { - - for (DbObjectBase dbObjectBase : entriesFromHistory) { - TemporaryBasal tbr = (TemporaryBasal) dbObjectBase; - - if (tbr.pumpId == pumpId) { - return tbr; - } - } - - TemporaryBasal tempBasal = databaseHelper.findTempBasalByPumpId(pumpId); - return tempBasal; - } - - - /** - * findDbEntry - finds Db entries in database, while theoretically this should have same dateTime they - * don't. Entry on pump is few seconds before treatment in AAPS, and on manual boluses on pump there - * is no treatment at all. For now we look fro tratment that was from 0s - 1m59s within pump entry. - * - * @param treatment Pump Entry - * @param entriesFromHistory entries from history - * @return DbObject from AAPS (if found) - */ - private DbObjectBase findDbEntry(PumpHistoryEntry treatment, List entriesFromHistory) { - - long proposedTime = DateTimeUtil.toMillisFromATD(treatment.atechDateTime); - - //proposedTime += (this.pumpTime.timeDifference * 1000); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Treatment=%s, FromDb=%s", treatment, gson().toJson(entriesFromHistory))); - - if (entriesFromHistory.size() == 0) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Treatment=%s, FromDb=null", treatment)); - return null; - } else if (entriesFromHistory.size() == 1) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Treatment=%s, FromDb=%s. Type=SingleEntry", treatment, entriesFromHistory.get(0))); - - // TODO: Fix db code - // if difference is bigger than 2 minutes we discard entry - long maxMillisAllowed = DateTimeUtil.getMillisFromATDWithAddedMinutes(treatment.atechDateTime, 2); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry maxMillisAllowed=%d, AtechDateTime=%d (add 2 minutes). ", maxMillisAllowed, treatment.atechDateTime)); - - if (entriesFromHistory.get(0).getDate() > maxMillisAllowed) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: findDbEntry entry filtered out, returning null. "); - return null; - } - - return entriesFromHistory.get(0); - } - - for (int min = 0; min < 2; min += 1) { - - for (int sec = 0; sec <= 50; sec += 10) { - - if (min == 1 && sec == 50) { - sec = 59; - } - - int diff = (sec * 1000); - - List outList = new ArrayList<>(); - - for (DbObjectBase treatment1 : entriesFromHistory) { - - if ((treatment1.getDate() > proposedTime - diff) && (treatment1.getDate() < proposedTime + diff)) { - outList.add(treatment1); - } - } - - if (outList.size() == 1) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Treatment={}, FromDb={}. Type=EntrySelected, AtTimeMin={}, AtTimeSec={}", treatment, entriesFromHistory.get(0), min, sec)); - - return outList.get(0); - } - - if (min == 0 && sec == 10 && outList.size() > 1) { - aapsLogger.error(String.format(Locale.ENGLISH, "Too many entries (with too small diff): (timeDiff=[min=%d,sec=%d],count=%d,list=%s)", - min, sec, outList.size(), gson().toJson(outList))); - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Error - Too many entries (with too small diff): (timeDiff=[min=%d,sec=%d],count=%d,list=%s)", - min, sec, outList.size(), gson().toJson(outList))); - } - } - } - - return null; - } - - - private List getDatabaseEntriesByLastTimestamp(long startTimestamp, ProcessHistoryRecord processHistoryRecord) { - if (processHistoryRecord == ProcessHistoryRecord.Bolus) { - return activePlugin.getActiveTreatments().getTreatmentsFromHistoryAfterTimestamp(startTimestamp); - } else { - return databaseHelper.getTemporaryBasalsDataFromTime(startTimestamp, true); - } - } - - - private void filterOutAlreadyAddedEntries(List entryList, List treatmentsFromHistory) { - - if (isCollectionEmpty(treatmentsFromHistory)) - return; - - List removeTreatmentsFromHistory = new ArrayList<>(); - List removeTreatmentsFromPH = new ArrayList<>(); - - for (DbObjectBase treatment : treatmentsFromHistory) { - - if (treatment.getPumpId() != 0) { - - PumpHistoryEntry selectedBolus = null; - - for (PumpHistoryEntry bolus : entryList) { - if (bolus.getPumpId() == treatment.getPumpId()) { - selectedBolus = bolus; - break; - } - } - - if (selectedBolus != null) { - entryList.remove(selectedBolus); - - removeTreatmentsFromPH.add(selectedBolus); - removeTreatmentsFromHistory.add(treatment); - } - } - } - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: filterOutAlreadyAddedEntries: PumpHistory=%s, Treatments=%s", - gson().toJson(removeTreatmentsFromPH), - gsonCore().toJson(removeTreatmentsFromHistory))); - - treatmentsFromHistory.removeAll(removeTreatmentsFromHistory); - } - - - private void addBolus(PumpHistoryEntry bolus, Treatment treatment) { - - BolusDTO bolusDTO = (BolusDTO) bolus.getDecodedData().get("Object"); - - if (treatment == null) { - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: addBolus(tretament==null): Bolus=" + bolusDTO); - - switch (bolusDTO.getBolusType()) { - case Normal: { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - - detailedBolusInfo.setBolusTimestamp(tryToGetByLocalTime(bolus.atechDateTime)); - detailedBolusInfo.setPumpType(PumpType.MEDTRONIC_512_712); // TODO grab real model - detailedBolusInfo.setPumpSerial(medtronicPumpStatus.serialNumber); - detailedBolusInfo.setBolusPumpId(bolus.getPumpId()); - detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); - - addCarbsFromEstimate(detailedBolusInfo, bolus); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: addBolus(tretament==null): DetailedBolusInfo=" + detailedBolusInfo); - - boolean newRecord = activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, false); - - bolus.setLinkedObject(detailedBolusInfo); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "addBolus - [date=%d,pumpId=%d, insulin=%.2f, newRecord=%b]", detailedBolusInfo.timestamp, - detailedBolusInfo.getBolusPumpId(), detailedBolusInfo.insulin, newRecord)); - } - break; - - case Audio: - case Extended: { - ExtendedBolus extendedBolus = new ExtendedBolus(injector); - extendedBolus.date = tryToGetByLocalTime(bolus.atechDateTime); - extendedBolus.source = Source.PUMP; - extendedBolus.insulin = bolusDTO.getDeliveredAmount(); - extendedBolus.pumpId = bolus.getPumpId(); - extendedBolus.isValid = true; - extendedBolus.durationInMinutes = bolusDTO.getDuration(); - - bolus.setLinkedObject(extendedBolus); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, "DoubleBolusDebug: addBolus(tretament==null): ExtendedBolus=" + extendedBolus); - - activePlugin.getActiveTreatments().addToHistoryExtendedBolus(extendedBolus); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "addBolus - Extended [date=%d,pumpId=%d, insulin=%.3f, duration=%d]", extendedBolus.date, - extendedBolus.pumpId, extendedBolus.insulin, extendedBolus.durationInMinutes)); - - } - break; - } - - } else { - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: addBolus(OldTreatment=%s): Bolus=%s", treatment, bolusDTO)); - - treatment.source = Source.PUMP; - treatment.pumpId = bolus.getPumpId(); - treatment.insulin = bolusDTO.getDeliveredAmount(); - - TreatmentUpdateReturn updateReturn = activePlugin.getActiveTreatments().createOrUpdateMedtronic(treatment, false); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: addBolus(tretament!=null): NewTreatment=%s, UpdateReturn=%s", treatment, updateReturn)); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "editBolus - [date=%d,pumpId=%d, insulin=%.3f, newRecord=%s]", treatment.date, - treatment.pumpId, treatment.insulin, updateReturn.toString())); - - bolus.setLinkedObject(treatment); - - } - } - - - private void addCarbsFromEstimate(DetailedBolusInfo detailedBolusInfo, PumpHistoryEntry bolus) { - - if (bolus.containsDecodedData("Estimate")) { - - BolusWizardDTO bolusWizard = (BolusWizardDTO) bolus.getDecodedData().get("Estimate"); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: addCarbsFromEstimate: Bolus=%s, BolusWizardDTO=%s", bolus, bolusWizard)); - - detailedBolusInfo.carbs = bolusWizard.carbs; - } - } - - - private void addTBR(PumpHistoryEntry treatment, TemporaryBasal temporaryBasalDbInput) { - - TempBasalPair tbr = (TempBasalPair) treatment.getDecodedData().get("Object"); - - TemporaryBasal temporaryBasalDb = temporaryBasalDbInput; - String operation = "editTBR"; - - if (temporaryBasalDb == null) { - temporaryBasalDb = new TemporaryBasal(injector); - temporaryBasalDb.date = tryToGetByLocalTime(treatment.atechDateTime); - - operation = "addTBR"; - } - - temporaryBasalDb.source = Source.PUMP; - temporaryBasalDb.pumpId = treatment.getPumpId(); - temporaryBasalDb.durationInMinutes = tbr.getDurationMinutes(); - temporaryBasalDb.absoluteRate = tbr.getInsulinRate(); - temporaryBasalDb.isAbsolute = !tbr.isPercent(); - - treatment.setLinkedObject(temporaryBasalDb); - - databaseHelper.createOrUpdate(temporaryBasalDb); - - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, operation + " - [date=%d,pumpId=%d, rate=%s %s, duration=%d]", // - temporaryBasalDb.date, // - temporaryBasalDb.pumpId, // - temporaryBasalDb.isAbsolute ? String.format(Locale.ENGLISH, "%.2f", temporaryBasalDb.absoluteRate) : - String.format(Locale.ENGLISH, "%d", temporaryBasalDb.percentRate), // - temporaryBasalDb.isAbsolute ? "U/h" : "%", // - temporaryBasalDb.durationInMinutes)); - } - - - private void processSuspends(List tempBasalProcessList) { - - for (TempBasalProcessDTO tempBasalProcess : tempBasalProcessList) { - - TemporaryBasal tempBasal = databaseHelper.findTempBasalByPumpId(tempBasalProcess.itemOne.getPumpId()); - - if (tempBasal == null) { - // add - tempBasal = new TemporaryBasal(injector); - tempBasal.date = tryToGetByLocalTime(tempBasalProcess.itemOne.atechDateTime); - - tempBasal.source = Source.PUMP; - tempBasal.pumpId = tempBasalProcess.itemOne.getPumpId(); - tempBasal.durationInMinutes = tempBasalProcess.getDuration(); - tempBasal.absoluteRate = 0.0d; - tempBasal.isAbsolute = true; - - tempBasalProcess.itemOne.setLinkedObject(tempBasal); - tempBasalProcess.itemTwo.setLinkedObject(tempBasal); - - databaseHelper.createOrUpdate(tempBasal); - - } - } - - } - - - private List getSuspends() { - - List outList = new ArrayList<>(); - - // suspend/resume - outList.addAll(getSuspendResumeRecords()); - // no_delivery/prime & rewind/prime - outList.addAll(getNoDeliveryRewindPrimeRecords()); - - return outList; - } - - private List getSuspendResumeRecords() { - List filteredItems = getFilteredItems(this.newHistory, // - PumpHistoryEntryType.SuspendPump, // - PumpHistoryEntryType.ResumePump); - - List outList = new ArrayList<>(); - - if (filteredItems.size() > 0) { - - List filtered2Items = new ArrayList<>(); - - if ((filteredItems.size() % 2 == 0) && (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.ResumePump)) { - // full resume suspends (S R S R) - filtered2Items.addAll(filteredItems); - } else if ((filteredItems.size() % 2 == 0) && (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.SuspendPump)) { - // not full suspends, need to retrive one more record and discard first one (R S R S) -> ([S] R S R [xS]) - filteredItems.remove(0); - - PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.SuspendPump); - if (oneMoreEntryFromHistory != null) { - filteredItems.add(oneMoreEntryFromHistory); - } else { - filteredItems.remove(filteredItems.size() - 1); // remove last (unpaired R) - } - - filtered2Items.addAll(filteredItems); - } else { - if (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.ResumePump) { - // get one more from history (R S R) -> ([S] R S R) - - PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.SuspendPump); - if (oneMoreEntryFromHistory != null) { - filteredItems.add(oneMoreEntryFromHistory); - } else { - filteredItems.remove(filteredItems.size() - 1); // remove last (unpaired R) - } - - filtered2Items.addAll(filteredItems); - } else { - // remove last and have paired items - filteredItems.remove(0); - filtered2Items.addAll(filteredItems); - } - } - - if (filtered2Items.size() > 0) { - sort(filtered2Items); - Collections.reverse(filtered2Items); - - for (int i = 0; i < filtered2Items.size(); i += 2) { - TempBasalProcessDTO dto = new TempBasalProcessDTO(); - - dto.itemOne = filtered2Items.get(i); - dto.itemTwo = filtered2Items.get(i + 1); - - dto.processOperation = TempBasalProcessDTO.Operation.Add; - - outList.add(dto); - } - } - } - - return outList; - } - - - private List getNoDeliveryRewindPrimeRecords() { - List primeItems = getFilteredItems(this.newHistory, // - PumpHistoryEntryType.Prime); - - List outList = new ArrayList<>(); - - if (primeItems.size() == 0) - return outList; - - List filteredItems = getFilteredItems(this.newHistory, // - PumpHistoryEntryType.Prime, - PumpHistoryEntryType.Rewind, - PumpHistoryEntryType.NoDeliveryAlarm, - PumpHistoryEntryType.Bolus, - PumpHistoryEntryType.TempBasalCombined - ); - - List tempData = new ArrayList<>(); - boolean startedItems = false; - boolean finishedItems = false; - - for (PumpHistoryEntry filteredItem : filteredItems) { - if (filteredItem.getEntryType() == PumpHistoryEntryType.Prime) { - startedItems = true; - } - - if (startedItems) { - if (filteredItem.getEntryType() == PumpHistoryEntryType.Bolus || - filteredItem.getEntryType() == PumpHistoryEntryType.TempBasalCombined) { - finishedItems = true; - break; - } - - tempData.add(filteredItem); - } - } - - - if (!finishedItems) { - - List filteredItemsOld = getFilteredItems(this.allHistory, // - PumpHistoryEntryType.Rewind, - PumpHistoryEntryType.NoDeliveryAlarm, - PumpHistoryEntryType.Bolus, - PumpHistoryEntryType.TempBasalCombined - ); - - for (PumpHistoryEntry filteredItem : filteredItemsOld) { - - if (filteredItem.getEntryType() == PumpHistoryEntryType.Bolus || - filteredItem.getEntryType() == PumpHistoryEntryType.TempBasalCombined) { - finishedItems = true; - break; - } - - tempData.add(filteredItem); - } - } - - - if (!finishedItems) { - showLogs("NoDeliveryRewindPrimeRecords: Not finished Items: ", gson().toJson(tempData)); - return outList; - } - - showLogs("NoDeliveryRewindPrimeRecords: Records to evaluate: ", gson().toJson(tempData)); - - List items = getFilteredItems(tempData, // - PumpHistoryEntryType.Prime - ); - - - TempBasalProcessDTO processDTO = new TempBasalProcessDTO(); - - processDTO.itemTwo = items.get(0); - - items = getFilteredItems(tempData, // - PumpHistoryEntryType.NoDeliveryAlarm - ); - - if (items.size() > 0) { - - processDTO.itemOne = items.get(items.size() - 1); - processDTO.processOperation = TempBasalProcessDTO.Operation.Add; - - outList.add(processDTO); - return outList; - } - - - items = getFilteredItems(tempData, // - PumpHistoryEntryType.Rewind - ); - - if (items.size() > 0) { - - processDTO.itemOne = items.get(0); - processDTO.processOperation = TempBasalProcessDTO.Operation.Add; - - outList.add(processDTO); - return outList; - } - - return outList; - } - - - private PumpHistoryEntry getOneMoreEntryFromHistory(PumpHistoryEntryType entryType) { - List filteredItems = getFilteredItems(this.allHistory, entryType); - - return filteredItems.size() == 0 ? null : filteredItems.get(0); - } - - - private List filterTDDs(List tdds) { - List tddsOut = new ArrayList<>(); - - for (PumpHistoryEntry tdd : tdds) { - if (tdd.getEntryType() != PumpHistoryEntryType.EndResultTotals) { - tddsOut.add(tdd); - } - } - - return tddsOut.size() == 0 ? tdds : tddsOut; - } - -/* - private TDD findTDD(long atechDateTime, List tddsDb) { - - for (TDD tdd : tddsDb) { - - if (DateTimeUtil.isSameDayATDAndMillis(atechDateTime, tdd.date)) { - return tdd; - } - } - - return null; - } -*/ - private long tryToGetByLocalTime(long atechDateTime) { - return DateTimeUtil.toMillisFromATD(atechDateTime); - } - - - private int getOldestDateDifference(List treatments) { - - long dt = Long.MAX_VALUE; - PumpHistoryEntry currentTreatment = null; - - if (isCollectionEmpty(treatments)) { - return 8; // default return of 6 (5 for diif on history reading + 2 for max allowed difference) minutes - } - - for (PumpHistoryEntry treatment : treatments) { - - if (treatment.atechDateTime < dt) { - dt = treatment.atechDateTime; - currentTreatment = treatment; - } - } - - LocalDateTime oldestEntryTime; - - try { - - oldestEntryTime = DateTimeUtil.toLocalDateTime(dt); - oldestEntryTime = oldestEntryTime.minusMinutes(3); - -// if (this.pumpTime.timeDifference < 0) { -// oldestEntryTime = oldestEntryTime.plusSeconds(this.pumpTime.timeDifference); -// } - } catch (Exception ex) { - aapsLogger.error("Problem decoding date from last record: " + currentTreatment); - return 8; // default return of 6 minutes - } - - LocalDateTime now = new LocalDateTime(); - - Minutes minutes = Minutes.minutesBetween(oldestEntryTime, now); - - // returns oldest time in history, with calculated time difference between pump and phone, minus 5 minutes - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "Oldest entry: %d, pumpTimeDifference=%d, newDt=%s, currentTime=%s, differenceMin=%d", dt, - this.pumpTime.timeDifference, oldestEntryTime, now, minutes.getMinutes())); - - return minutes.getMinutes(); - } - - - private long getOldestTimestamp(List treatments) { - - long dt = Long.MAX_VALUE; - PumpHistoryEntry currentTreatment = null; - - for (PumpHistoryEntry treatment : treatments) { - - if (treatment.atechDateTime < dt) { - dt = treatment.atechDateTime; - currentTreatment = treatment; - } - } - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: getOldestTimestamp. Oldest entry found: time=%d, object=%s", dt, currentTreatment)); - - try { - - GregorianCalendar oldestEntryTime = DateTimeUtil.toGregorianCalendar(dt); - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: getOldestTimestamp. oldestEntryTime: %s", DateTimeUtil.toString(oldestEntryTime))); - oldestEntryTime.add(Calendar.MINUTE, -2); - - if (doubleBolusDebug) - aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: getOldestTimestamp. oldestEntryTime (-2m): %s, timeInMillis=%d", DateTimeUtil.toString(oldestEntryTime), oldestEntryTime.getTimeInMillis())); - - return oldestEntryTime.getTimeInMillis(); - - } catch (Exception ex) { - aapsLogger.error("Problem decoding date from last record: " + currentTreatment); - return 8; // default return of 6 minutes - } - - } - - - private PumpHistoryEntryType getTDDType() { - - if (medtronicUtil.getMedtronicPumpModel() == null) { - return PumpHistoryEntryType.EndResultTotals; - } - - switch (medtronicUtil.getMedtronicPumpModel()) { - - case Medtronic_515: - case Medtronic_715: - return PumpHistoryEntryType.DailyTotals515; - - case Medtronic_522: - case Medtronic_722: - return PumpHistoryEntryType.DailyTotals522; - - case Medtronic_523_Revel: - case Medtronic_723_Revel: - case Medtronic_554_Veo: - case Medtronic_754_Veo: - return PumpHistoryEntryType.DailyTotals523; - - default: { - return PumpHistoryEntryType.EndResultTotals; - } - } - } - - - public boolean hasBasalProfileChanged() { - - List filteredItems = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile); - - aapsLogger.debug(LTag.PUMP, "hasBasalProfileChanged. Items: " + gson().toJson(filteredItems)); - - return (filteredItems.size() > 0); - } - - - public void processLastBasalProfileChange(PumpType pumpType, MedtronicPumpStatus mdtPumpStatus) { - - List filteredItems = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile); - - aapsLogger.debug(LTag.PUMP, "processLastBasalProfileChange. Items: " + filteredItems); - - PumpHistoryEntry newProfile = null; - Long lastDate = null; - - if (filteredItems.size() == 1) { - newProfile = filteredItems.get(0); - } else if (filteredItems.size() > 1) { - - for (PumpHistoryEntry filteredItem : filteredItems) { - - if (lastDate == null || lastDate < filteredItem.atechDateTime) { - newProfile = filteredItem; - lastDate = newProfile.atechDateTime; - } - } - } - - if (newProfile != null) { - aapsLogger.debug(LTag.PUMP, "processLastBasalProfileChange. item found, setting new basalProfileLocally: " + newProfile); - BasalProfile basalProfile = (BasalProfile) newProfile.getDecodedData().get("Object"); - - mdtPumpStatus.basalsByHour = basalProfile.getProfilesByHour(pumpType); - } - } - - - public boolean hasPumpTimeChanged() { - return getStateFromFilteredList(PumpHistoryEntryType.NewTimeSet, // - PumpHistoryEntryType.ChangeTime); - } - - - public void setLastHistoryRecordTime(Long lastHistoryRecordTime) { - - // this.previousLastHistoryRecordTime = this.lastHistoryRecordTime; - } - - - public void setIsInInit(boolean init) { - this.isInit = init; - } - - - // HELPER METHODS - - private void sort(List list) { - if (list != null && !list.isEmpty()) { - Collections.sort(list, new PumpHistoryEntry.Comparator()); - } - } - - - private List preProcessTBRs(List TBRs_Input) { - List TBRs = new ArrayList<>(); - - Map map = new HashMap<>(); - - for (PumpHistoryEntry pumpHistoryEntry : TBRs_Input) { - if (map.containsKey(pumpHistoryEntry.DT)) { - medtronicPumpHistoryDecoder.decodeTempBasal(map.get(pumpHistoryEntry.DT), pumpHistoryEntry); - pumpHistoryEntry.setEntryType(medtronicUtil.getMedtronicPumpModel(), PumpHistoryEntryType.TempBasalCombined); - TBRs.add(pumpHistoryEntry); - map.remove(pumpHistoryEntry.DT); - } else { - map.put(pumpHistoryEntry.DT, pumpHistoryEntry); - } - } - - return TBRs; - } - - - private List getFilteredItems(PumpHistoryEntryType... entryTypes) { - return getFilteredItems(this.newHistory, entryTypes); - } - - - private boolean getStateFromFilteredList(PumpHistoryEntryType... entryTypes) { - if (isInit) { - return false; - } else { - List filteredItems = getFilteredItems(entryTypes); - - aapsLogger.debug(LTag.PUMP, "Items: " + filteredItems); - - return filteredItems.size() > 0; - } - } - - - private List getFilteredItems(List inList, PumpHistoryEntryType... entryTypes) { - - // aapsLogger.debug(LTag.PUMP, "InList: " + inList.size()); - List outList = new ArrayList<>(); - - if (inList != null && inList.size() > 0) { - for (PumpHistoryEntry pumpHistoryEntry : inList) { - - if (!isEmpty(entryTypes)) { - for (PumpHistoryEntryType pumpHistoryEntryType : entryTypes) { - - if (pumpHistoryEntry.getEntryType() == pumpHistoryEntryType) { - outList.add(pumpHistoryEntry); - break; - } - } - } else { - outList.add(pumpHistoryEntry); - } - } - } - - // aapsLogger.debug(LTag.PUMP, "OutList: " + outList.size()); - - return outList; - } - - - private boolean isEmpty(PumpHistoryEntryType... entryTypes) { - return (entryTypes == null || (entryTypes.length == 1 && entryTypes[0] == null)); - } - - - private String getLogPrefix() { - return "MedtronicHistoryData::"; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt new file mode 100644 index 0000000000..cb904b9afe --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.kt @@ -0,0 +1,1077 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data + +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.db.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.* +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.apache.commons.lang3.StringUtils +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 10/12/18. + */ +// TODO: After release we need to refactor how data is retrieved from pump, each entry in history needs to be marked, and sorting +// needs to happen according those markings, not on time stamp (since AAPS can change time anytime it drifts away). This +// needs to include not returning any records if TZ goes into -x area. To fully support this AAPS would need to take note of +// all times that time changed (TZ, DST, etc.). Data needs to be returned in batches (time_changed batches, so that we can +// handle it. It would help to assign sort_ids to items (from oldest (1) to newest (x) +// +@Suppress("DEPRECATION") +@Singleton +class MedtronicHistoryData @Inject constructor( + val injector: HasAndroidInjector, + val aapsLogger: AAPSLogger, + val sp: SP, + val activePlugin: ActivePlugin, + val medtronicUtil: MedtronicUtil, + val medtronicPumpHistoryDecoder: MedtronicPumpHistoryDecoder, + val medtronicPumpStatus: MedtronicPumpStatus, + val databaseHelper: DatabaseHelperInterface, + val pumpSync: PumpSync, + val pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage +) { + + val allHistory: MutableList = mutableListOf() + private var allPumpIds: MutableSet = mutableSetOf() + private var newHistory: MutableList = mutableListOf() + private var isInit = false + + private var pumpTime: ClockDTO? = null + private var lastIdUsed: Long = 0 + private var gson: Gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() + private var gsonCore: Gson = GsonBuilder().create() + + /** + * Add New History entries + * + * @param result PumpHistoryResult instance + */ + fun addNewHistory(result: PumpHistoryResult) { + val validEntries: List = result.validEntries + val newEntries: MutableList = mutableListOf() + for (validEntry in validEntries) { + if (!allPumpIds.contains(validEntry.pumpId)) { + newEntries.add(validEntry) + } else { + val entryByPumpId = getEntryByPumpId(validEntry.pumpId) + + if (entryByPumpId != null && entryByPumpId.hasBolusChanged(validEntry)) { + newEntries.add(validEntry) + allHistory.remove(entryByPumpId) + allPumpIds.remove(validEntry.pumpId); + } + } + } + newHistory = newEntries + showLogs("List of history (before filtering): [" + newHistory.size + "]", gson.toJson(newHistory)) + } + + private fun getEntryByPumpId(pumpId: Long): PumpHistoryEntry? { + val findFirst = this.allHistory.stream() + .filter { f -> f.pumpId == pumpId } + .findFirst() + + return if (findFirst.isPresent()) findFirst.get() else null + } + + private fun showLogs(header: String?, data: String) { + if (header != null) { + aapsLogger.debug(LTag.PUMP, header) + } + if (StringUtils.isNotBlank(data)) { + for (token in StringUtil.splitString(data, 3500)) { + aapsLogger.debug(LTag.PUMP, token) + } + } else { + aapsLogger.debug(LTag.PUMP, "No data.") + } + } + + fun filterNewEntries() { + val newHistory2: MutableList = mutableListOf() + var tbrs: MutableList = mutableListOf() + val bolusEstimates: MutableList = mutableListOf() + val atechDate = DateTimeUtil.toATechDate(GregorianCalendar()) + + //aapsLogger.debug(LTag.PUMP, "Filter new entries: Before {}", newHistory); + if (!isCollectionEmpty(newHistory)) { + for (pumpHistoryEntry in newHistory) { + if (!allPumpIds.contains(pumpHistoryEntry.pumpId)) { + val type = pumpHistoryEntry.entryType + if (type === PumpHistoryEntryType.TempBasalRate || type === PumpHistoryEntryType.TempBasalDuration) { + tbrs.add(pumpHistoryEntry) + } else if (type === PumpHistoryEntryType.BolusWizard || type === PumpHistoryEntryType.BolusWizard512) { + bolusEstimates.add(pumpHistoryEntry) + newHistory2.add(pumpHistoryEntry) + } else { + if (type === PumpHistoryEntryType.EndResultTotals) { + if (!DateTimeUtil.isSameDay(atechDate, pumpHistoryEntry.atechDateTime)) { + newHistory2.add(pumpHistoryEntry) + } + } else { + newHistory2.add(pumpHistoryEntry) + } + } + } + } + tbrs = preProcessTBRs(tbrs) + if (bolusEstimates.size > 0) { + extendBolusRecords(bolusEstimates, newHistory2) + } + newHistory2.addAll(tbrs) + + val newHistory3: MutableList = mutableListOf() + + for (pumpHistoryEntry in newHistory2) { + if (!allPumpIds.contains(pumpHistoryEntry.pumpId)) { + newHistory3.add(pumpHistoryEntry) + } + } + + newHistory = newHistory3 + sort(newHistory) + } + aapsLogger.debug(LTag.PUMP, "New History entries found: " + newHistory.size) + showLogs("List of history (after filtering): [" + newHistory.size + "]", gson.toJson(newHistory)) + } + + private fun extendBolusRecords(bolusEstimates: MutableList, newHistory2: MutableList) { + val boluses: MutableList = getFilteredItems(newHistory2, PumpHistoryEntryType.Bolus) + for (bolusEstimate in bolusEstimates) { + for (bolus in boluses) { + if (bolusEstimate.atechDateTime == bolus.atechDateTime) { + bolus.addDecodedData("Estimate", bolusEstimate.decodedData["Object"]!!) + } + } + } + } + + fun finalizeNewHistoryRecords() { + if (newHistory.isEmpty()) return + var pheLast = newHistory[0] + + // find last entry + for (pumpHistoryEntry in newHistory) { + if (pumpHistoryEntry.atechDateTime != 0L && pumpHistoryEntry.isAfter(pheLast.atechDateTime)) { + pheLast = pumpHistoryEntry + } + } + + // add new entries + newHistory.reverse() + for (pumpHistoryEntry in newHistory) { + if (!allPumpIds.contains(pumpHistoryEntry.pumpId)) { + lastIdUsed++ + pumpHistoryEntry.id = lastIdUsed + allHistory.add(pumpHistoryEntry) + allPumpIds.add(pumpHistoryEntry.pumpId) + } + } + + sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, pheLast.atechDateTime) + var dt: LocalDateTime? = null + try { + dt = DateTimeUtil.toLocalDateTime(pheLast.atechDateTime) + } catch (ex: Exception) { + aapsLogger.error("Problem decoding date from last record: $pheLast") + } + if (dt != null) { + dt = dt.minusDays(1) // we keep 24 hours + val dtRemove = DateTimeUtil.toATechDate(dt) + val removeList: MutableList = ArrayList() + for (pumpHistoryEntry in allHistory) { + if (!pumpHistoryEntry.isAfter(dtRemove)) { + removeList.add(pumpHistoryEntry) + allPumpIds.remove(pumpHistoryEntry.pumpId) + } + } + allHistory.removeAll(removeList) + this.sort(allHistory) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "All History records [afterFilterCount=%d, removedItemsCount=%d, newItemsCount=%d]", + allHistory.size, removeList.size, newHistory.size)) + } else { + aapsLogger.error("Since we couldn't determine date, we don't clean full history. This is just workaround.") + } + newHistory.clear() + } + + fun hasRelevantConfigurationChanged(): Boolean { + return getStateFromFilteredList( // + setOf(PumpHistoryEntryType.ChangeBasalPattern, // + PumpHistoryEntryType.ClearSettings, // + PumpHistoryEntryType.SaveSettings, // + PumpHistoryEntryType.ChangeMaxBolus, // + PumpHistoryEntryType.ChangeMaxBasal, // + PumpHistoryEntryType.ChangeTempBasalType)) + } + + private fun isCollectionEmpty(col: List<*>?): Boolean { + return col == null || col.isEmpty() + } + + private fun isCollectionNotEmpty(col: List<*>?): Boolean { + return col != null && !col.isEmpty() + } + + fun isPumpSuspended(): Boolean { + val items = getDataForPumpSuspends() + showLogs("isPumpSuspended: ", gson.toJson(items)) + return if (isCollectionNotEmpty(items)) { + val pumpHistoryEntryType = items[0].entryType + val isSuspended = !(pumpHistoryEntryType === PumpHistoryEntryType.TempBasalCombined || // + pumpHistoryEntryType === PumpHistoryEntryType.BasalProfileStart || // + pumpHistoryEntryType === PumpHistoryEntryType.Bolus || // + pumpHistoryEntryType === PumpHistoryEntryType.ResumePump || // + pumpHistoryEntryType === PumpHistoryEntryType.BatteryChange || // + pumpHistoryEntryType === PumpHistoryEntryType.Prime) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "isPumpSuspended. Last entry type=%s, isSuspended=%b", pumpHistoryEntryType, isSuspended)) + isSuspended + } else false + } + + private fun getDataForPumpSuspends(): MutableList { + val newAndAll: MutableList = mutableListOf() + if (isCollectionNotEmpty(allHistory)) { + newAndAll.addAll(allHistory) + } + if (isCollectionNotEmpty(newHistory)) { + for (pumpHistoryEntry in newHistory) { + if (!newAndAll.contains(pumpHistoryEntry)) { + newAndAll.add(pumpHistoryEntry) + } + } + } + if (newAndAll.isEmpty()) return newAndAll + this.sort(newAndAll) + var newAndAll2: MutableList = getFilteredItems(newAndAll, // + setOf(PumpHistoryEntryType.Bolus, // + PumpHistoryEntryType.TempBasalCombined, // + PumpHistoryEntryType.Prime, // + PumpHistoryEntryType.SuspendPump, // + PumpHistoryEntryType.ResumePump, // + PumpHistoryEntryType.Rewind, // + PumpHistoryEntryType.NoDeliveryAlarm, // + PumpHistoryEntryType.BatteryChange, // + PumpHistoryEntryType.BasalProfileStart)) + newAndAll2 = filterPumpSuspend(newAndAll2, 10) + return newAndAll2 + } + + private fun filterPumpSuspend(newAndAll: MutableList, filterCount: Int): MutableList { + if (newAndAll.size <= filterCount) { + return newAndAll + } + val newAndAllOut: MutableList = ArrayList() + for (i in 0 until filterCount) { + newAndAllOut.add(newAndAll[i]) + } + return newAndAllOut + } + + /** + * Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime) + */ + fun processNewHistoryData() { + + // Prime (for reseting autosense) + val primeRecords: MutableList = getFilteredItems(PumpHistoryEntryType.Prime) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Prime [count=%d, items=%s]", primeRecords.size, gson.toJson(primeRecords))) + if (isCollectionNotEmpty(primeRecords)) { + try { + processPrime(primeRecords) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Prime entries: " + ex.message, ex) + throw ex + } + } + + // Rewind (for marking insulin change) + val rewindRecords: MutableList = getFilteredItems(PumpHistoryEntryType.Rewind) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Rewind [count=%d, items=%s]", rewindRecords.size, gson.toJson(rewindRecords))) + if (isCollectionNotEmpty(rewindRecords)) { + try { + processRewind(rewindRecords) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Rewind entries: " + ex.message, ex) + throw ex + } + } + + // TDD + val tdds: MutableList = getFilteredItems(setOf(PumpHistoryEntryType.EndResultTotals, getTDDType())) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TDD [count=%d, items=%s]", tdds.size, gson.toJson(tdds))) + if (tdds.isNotEmpty()) { + try { + processTDDs(tdds) + } catch (ex: Exception) { + aapsLogger.error("ProcessHistoryData: Error processing TDD entries: " + ex.message, ex) + throw ex + } + } + pumpTime = medtronicUtil.pumpTime + + // Bolus + val treatments = getFilteredItems(PumpHistoryEntryType.Bolus) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: Bolus [count=%d, items=%s]", treatments.size, gson.toJson(treatments))) + if (treatments.isNotEmpty()) { + try { + processBolusEntries(treatments) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Bolus entries: " + ex.message, ex) + throw ex + } + } + + // TBR + val tbrs: MutableList = getFilteredItems(PumpHistoryEntryType.TempBasalCombined) + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: TBRs Processed [count=%d, items=%s]", tbrs.size, gson.toJson(tbrs))) + if (tbrs.isNotEmpty()) { + try { + processTBREntries(tbrs) // TODO not implemented yet + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing TBR entries: " + ex.message, ex) + throw ex + } + } + + // 'Delivery Suspend' + val suspends: MutableList + suspends = try { + getSuspendRecords() + } catch (ex: Exception) { + aapsLogger.error("ProcessHistoryData: Error getting Suspend entries: " + ex.message, ex) + throw ex + } + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "ProcessHistoryData: 'Delivery Suspend' Processed [count=%d, items=%s]", suspends.size, + gson.toJson(suspends))) + if (suspends.isNotEmpty()) { + try { + processSuspends(suspends) // TODO not tested yet + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMP, "ProcessHistoryData: Error processing Suspends entries: " + ex.message, ex) + throw ex + } + } + } + + private fun processPrime(primeRecords: List) { + val maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(GregorianCalendar(), -30) + var lastPrimeRecordTime = 0L + var lastPrimeRecord: PumpHistoryEntry? = null + for (primeRecord in primeRecords) { + val fixedAmount = primeRecord.getDecodedDataEntry("FixedAmount") + if (fixedAmount != null && fixedAmount as Float == 0.0f) { + // non-fixed primes are used to prime the tubing + // fixed primes are used to prime the cannula + // so skip the prime entry if it was not a fixed prime + continue + } + if (primeRecord.atechDateTime > maxAllowedTimeInPast) { + if (lastPrimeRecordTime != 0L && lastPrimeRecordTime < primeRecord.atechDateTime) { + lastPrimeRecordTime = primeRecord.atechDateTime + lastPrimeRecord = primeRecord + } + } + } + if (lastPrimeRecord != null) { + uploadCareportalEventIfFoundInHistory(lastPrimeRecord, + MedtronicConst.Statistics.LastPrime, + DetailedBolusInfo.EventType.CANNULA_CHANGE) + } + } + + private fun processRewind(rewindRecords: List) { + val maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(GregorianCalendar(), -30) + var lastRewindRecordTime = 0L + var lastRewindRecord: PumpHistoryEntry? = null + for (rewindRecord in rewindRecords) { + if (rewindRecord.atechDateTime > maxAllowedTimeInPast) { + if (lastRewindRecordTime < rewindRecord.atechDateTime) { + lastRewindRecordTime = rewindRecord.atechDateTime + lastRewindRecord = rewindRecord + } + } + } + if (lastRewindRecord != null) { + uploadCareportalEventIfFoundInHistory(lastRewindRecord, + MedtronicConst.Statistics.LastRewind, + DetailedBolusInfo.EventType.INSULIN_CHANGE) + } + } + + private fun uploadCareportalEventIfFoundInHistory(historyRecord: PumpHistoryEntry, eventSP: String, eventType: DetailedBolusInfo.EventType) { + val lastPrimeFromAAPS = sp.getLong(eventSP, 0L) + if (historyRecord.atechDateTime != lastPrimeFromAAPS) { + val result = pumpSync.insertTherapyEventIfNewWithTimestamp( + DateTimeUtil.toMillisFromATD(historyRecord.atechDateTime), + eventType, null, + historyRecord.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ROOT, "insertTherapyEventIfNewWithTimestamp [date=%d, eventType=%s, pumpId=%d, pumpSerial=%s] - Result: %b", + historyRecord.atechDateTime, eventType, historyRecord.pumpId, + medtronicPumpStatus.serialNumber, result)) + + sp.putLong(eventSP, historyRecord.atechDateTime) + } + } + + private fun processTDDs(tddsIn: MutableList) { + val tdds = filterTDDs(tddsIn) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, logPrefix + "TDDs found: %d.\n%s", + tdds.size, gson.toJson(tdds))) + + for (tdd in tdds) { + val totalsDTO = tdd.decodedData["Object"] as DailyTotalsDTO + + pumpSync.createOrUpdateTotalDailyDose( + DateTimeUtil.toMillisFromATD(tdd.atechDateTime), + totalsDTO.insulinBolus, + totalsDTO.insulinBasal, + totalsDTO.insulinTotal, + tdd.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber + ) + } + } + + private enum class ProcessHistoryRecord(val description: String) { + Bolus("Bolus"), + TBR("TBR"), + Suspend("Suspend"); + } + + private fun processBolusEntries(entryList: MutableList) { + + val boluses = pumpSyncStorage.getBoluses() + + for (bolus in entryList) { + + val bolusDTO = bolus.decodedData["Object"] as BolusDTO + var type: DetailedBolusInfo.BolusType = DetailedBolusInfo.BolusType.NORMAL + var multiwave = false + + if (bolusDTO.bolusType == PumpBolusType.Extended) { + addExtendedBolus(bolus, bolusDTO, multiwave) + continue; + } else if (bolusDTO.bolusType == PumpBolusType.Multiwave) { + multiwave = true + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "Multiwave bolus from pump, extended bolus and normal bolus will be added.")) + addExtendedBolus(bolus, bolusDTO, multiwave); + } + + val deliveredAmount: Double = if (multiwave) bolusDTO.immediateAmount!! else bolusDTO.deliveredAmount + + var temporaryId: Long? = null + + if (!multiwave) { + val entryWithTempId = findDbEntry(bolus, boluses) + + aapsLogger.debug(LTag.PUMP, String.format("DD: entryWithTempId=%s", gson.toJson(entryWithTempId))) + + if (entryWithTempId != null) { + temporaryId = entryWithTempId.temporaryId + pumpSyncStorage.removeBolusWithTemporaryId(temporaryId) + boluses.remove(entryWithTempId) + type = entryWithTempId.bolusData!!.bolusType + } + } + + if (temporaryId != null) { + val result = pumpSync.syncBolusWithTempId( + tryToGetByLocalTime(bolus.atechDateTime), + deliveredAmount, + temporaryId, + type, + bolus.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncBolusWithTempId [date=%d, temporaryId=%d, pumpId=%d, insulin=%.2f, pumpSerial=%s] - Result: %b", + bolus.atechDateTime, temporaryId, bolus.pumpId, deliveredAmount, + medtronicPumpStatus.serialNumber, result)) + } else { + val result = pumpSync.syncBolusWithPumpId( + tryToGetByLocalTime(bolus.atechDateTime), + deliveredAmount, + type, + bolus.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncBolusWithPumpId [date=%d, pumpId=%d, insulin=%.2f, pumpSerial=%s] - Result: %b", + bolus.atechDateTime, bolus.pumpId, deliveredAmount, + medtronicPumpStatus.serialNumber, result)) + } + + addCarbs(bolus) + } + } + + private fun addExtendedBolus(bolus: PumpHistoryEntry, bolusDTO: BolusDTO, isMultiwave: Boolean) { + val durationMs: Long = bolusDTO.duration * 60L * 1000L + + val result = pumpSync.syncExtendedBolusWithPumpId( + tryToGetByLocalTime(bolus.atechDateTime), + bolusDTO.deliveredAmount, + durationMs, + false, + bolus.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncExtendedBolusWithPumpId [date=%d, amount=%.2f, duration=%d, pumpId=%d, pumpSerial=%s, multiwave=%b] - Result: %b", + bolus.atechDateTime, bolusDTO.deliveredAmount, bolusDTO.duration, bolus.pumpId, + medtronicPumpStatus.serialNumber, isMultiwave, result)) + } + + private fun addCarbs(bolus: PumpHistoryEntry) { + if (bolus.containsDecodedData("Estimate")) { + val bolusWizard = bolus.decodedData["Estimate"] as BolusWizardDTO + + pumpSyncStorage.addCarbs(info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryCarbs( + tryToGetByLocalTime(bolus.atechDateTime), + bolusWizard.carbs.toDouble(), + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber, + bolus.pumpId + )) + } + } + + private fun processTBREntries(entryList: MutableList) { + Collections.reverse(entryList) + val tbr = entryList[0].getDecodedDataEntry("Object") as TempBasalPair + var readOldItem = false + if (tbr.isCancelTBR) { + val oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.TempBasalCombined) + if (oneMoreEntryFromHistory != null) { + entryList.add(0, oneMoreEntryFromHistory) + readOldItem = true + } else { + entryList.removeAt(0) + } + } + + val tbrRecords = pumpSyncStorage.getTBRs() + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, ProcessHistoryRecord.TBR.description + " List (before filter): %s, FromDb=%s", gson.toJson(entryList), + gson.toJson(tbrRecords))) + var processDTO: TempBasalProcessDTO? = null + val processList: MutableList = mutableListOf() + for (treatment in entryList) { + val tbr2 = treatment.getDecodedDataEntry("Object") as TempBasalPair + if (tbr2.isCancelTBR) { + if (processDTO != null) { + processDTO.itemTwo = treatment + processDTO.cancelPresent = true + if (readOldItem) { + processDTO.processOperation = TempBasalProcessDTO.Operation.Edit + readOldItem = false + } + } else { + aapsLogger.warn(LTag.PUMP, "processDTO was null - shouldn't happen, ignoring item. ItemTwo=$treatment") + } + } else { + if (processDTO != null) { + processList.add(processDTO) + } + processDTO = TempBasalProcessDTO( + itemOne = treatment, + processOperation = TempBasalProcessDTO.Operation.Add) + } + } + if (processDTO != null) { + processList.add(processDTO) + } + if (processList.isNotEmpty()) { + for (tempBasalProcessDTO in processList) { + + val entryWithTempId = findDbEntry(tempBasalProcessDTO.itemOne, tbrRecords) + + aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId: " + (if (entryWithTempId == null) "null" else entryWithTempId.toString())) + + val tbrEntry = tempBasalProcessDTO.itemOne.getDecodedDataEntry("Object") as TempBasalPair + + aapsLogger.debug(LTag.PUMP, String.format("DD: tbrEntry=%s, tempBasalProcessDTO=%s", gson.toJson(tbrEntry), gson.toJson(tempBasalProcessDTO))) + + if (entryWithTempId != null) { + + aapsLogger.debug(LTag.PUMP, String.format("DD: tempIdEntry=%s, tbrEntry=%s, tempBasalProcessDTO=%s, pumpType=%s, serial=%s", + gson.toJson(entryWithTempId), gson.toJson(tbrEntry), gson.toJson(tempBasalProcessDTO), medtronicPumpStatus.pumpType, medtronicPumpStatus.serialNumber)) + + val result = pumpSync.syncTemporaryBasalWithTempId( + tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), + tbrEntry.insulinRate, + tempBasalProcessDTO.duration * 60L * 1000L, + !tbrEntry.isPercent, + entryWithTempId.temporaryId, + PumpSync.TemporaryBasalType.NORMAL, + tempBasalProcessDTO.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncTemporaryBasalWithTempId [date=%d, temporaryId=%d, pumpId=%d, rate=%.2f %s, duration=%d, pumpSerial=%s] - Result: %b", + tempBasalProcessDTO.atechDateTime, entryWithTempId.temporaryId, tempBasalProcessDTO.pumpId, + tbrEntry.insulinRate, (if (tbrEntry.isPercent) "%" else "U"), tempBasalProcessDTO.duration, + medtronicPumpStatus.serialNumber, result)) + + pumpSyncStorage.removeTemporaryBasalWithTemporaryId(entryWithTempId.temporaryId) + tbrRecords.remove(entryWithTempId) + + entryWithTempId.pumpId = tempBasalProcessDTO.pumpId + entryWithTempId.date = tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime) + + if (isTBRActive(entryWithTempId)) { + medtronicPumpStatus.runningTBR = entryWithTempId + } + + } else { + val result = pumpSync.syncTemporaryBasalWithPumpId( + tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), + tbrEntry.insulinRate, + tempBasalProcessDTO.duration * 60L * 1000L, + !tbrEntry.isPercent, + PumpSync.TemporaryBasalType.NORMAL, + tempBasalProcessDTO.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncTemporaryBasalWithPumpId [date=%d, pumpId=%d, rate=%.2f %s, duration=%d, pumpSerial=%s] - Result: %b", + tempBasalProcessDTO.atechDateTime, tempBasalProcessDTO.pumpId, + tbrEntry.insulinRate, (if (tbrEntry.isPercent) "%" else "U"), tempBasalProcessDTO.duration, + medtronicPumpStatus.serialNumber, result)) + + if (medtronicPumpStatus.runningTBR != null) { + if (!isTBRActive(medtronicPumpStatus.runningTBR!!)) { + medtronicPumpStatus.runningTBR = null + } + } + + if (isTBRActive(tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), tempBasalProcessDTO.duration)) { + if (medtronicPumpStatus.runningTBR == null) { + medtronicPumpStatus.runningTBR = info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry(0L, + tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber, + null, + info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR(tbrEntry.insulinRate, !tbrEntry.isPercent, tempBasalProcessDTO.duration, PumpSync.TemporaryBasalType.NORMAL), + tempBasalProcessDTO.pumpId) + } + } + } + } // for + } // collection + } + + fun isTBRActive(dbEntry: info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry): Boolean { + return isTBRActive(dbEntry.date, dbEntry.tbrData!!.durationInMinutes) + } + + fun isTBRActive(startTimestamp: Long, durationMin: Int): Boolean { + val endDate = startTimestamp + (durationMin * 60 * 1000) + + return (endDate > System.currentTimeMillis()) + } + + /** + * findDbEntry - finds Db entries in database, while theoretically this should have same dateTime they + * don't. Entry on pump is few seconds before treatment in AAPS, and on manual boluses on pump there + * is no treatment at all. For now we look fro tratment that was from 0s - 1m59s within pump entry. + * + * @param treatment Pump Entry + * @param entriesFromHistory entries from history + * @return DbObject from AAPS (if found) + */ + + /** + * Looks at all boluses that have temporaryId and find one that is correct for us (if such entry exists) + */ + private fun findDbEntry(treatment: PumpHistoryEntry, temporaryEntries: MutableList): info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry? { + + if (temporaryEntries.isEmpty()) { + return null + } + + var proposedTime = DateTimeUtil.toMillisFromATD(treatment.atechDateTime) + + // pumpTime should never be null, but it can theoretically happen if reading of time from pump fails + this.pumpTime?.let { proposedTime += (it.timeDifference * 1000) } + + val proposedTimeDiff: LongArray = longArrayOf(proposedTime - (2 * 60 * 1000), proposedTime + (2L * 60L * 1000L)) + val tempEntriesList: MutableList = mutableListOf() + + for (temporaryEntry in temporaryEntries) { + if (temporaryEntry.date > proposedTimeDiff[0] && temporaryEntry.date < proposedTimeDiff[1]) { + tempEntriesList.add(temporaryEntry) + } + } + + if (tempEntriesList.isEmpty()) { + return null + } else if (tempEntriesList.size == 1) { + return tempEntriesList[0] + } + + var min = 0 + while (min < 2) { + var sec = 0 + while (sec <= 50) { + if (min == 1 && sec == 50) { + sec = 59 + } + val diff = sec * 1000 + val outList: MutableList = mutableListOf() + for (treatment1 in tempEntriesList) { + if (treatment1.date > proposedTime - diff && treatment1.date < proposedTime + diff) { + outList.add(treatment1) + } + } + if (outList.size == 1) { + if (doubleBolusDebug) aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Treatment={}, FromDb={}. Type=EntrySelected, AtTimeMin={}, AtTimeSec={}", treatment, outList[0], min, sec)) + return outList[0] + } + if (min == 0 && sec == 10 && outList.size > 1) { + aapsLogger.error(String.format(Locale.ENGLISH, "Too many entries (with too small diff): (timeDiff=[min=%d,sec=%d],count=%d,list=%s)", + min, sec, outList.size, gson.toJson(outList))) + if (doubleBolusDebug) aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "DoubleBolusDebug: findDbEntry Error - Too many entries (with too small diff): (timeDiff=[min=%d,sec=%d],count=%d,list=%s)", + min, sec, outList.size, gson.toJson(outList))) + } + sec += 10 + } + min += 1 + } + return null + } + + private fun processSuspends(tempBasalProcessList: List) { + for (tempBasalProcess in tempBasalProcessList) { + + val result = pumpSync.syncTemporaryBasalWithPumpId( + tryToGetByLocalTime(tempBasalProcess.itemOne.atechDateTime), + 0.0, + tempBasalProcess.duration * 60 * 1000L, + true, + PumpSync.TemporaryBasalType.PUMP_SUSPEND, + tempBasalProcess.itemOne.pumpId, + medtronicPumpStatus.pumpType, + medtronicPumpStatus.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "processSuspends::syncTemporaryBasalWithPumpId [date=%d, rate=%.2f, duration=%d, pumpId=%d, pumpSerial=%s] - Result: %b", + tempBasalProcess.itemOne.atechDateTime, 0.0, tempBasalProcess.duration, tempBasalProcess.itemOne.pumpId, + medtronicPumpStatus.serialNumber, result)) + + } + } + + // suspend/resume + // no_delivery/prime & rewind/prime + private fun getSuspendRecords(): MutableList { + val outList: MutableList = mutableListOf() + + // suspend/resume + outList.addAll(getSuspendResumeRecordsList()) + // no_delivery/prime & rewind/prime + outList.addAll(getNoDeliveryRewindPrimeRecordsList()) + return outList + } + + private fun getSuspendResumeRecordsList(): List { + val filteredItems = getFilteredItems(newHistory, // + setOf(PumpHistoryEntryType.SuspendPump, PumpHistoryEntryType.ResumePump)) + val outList: MutableList = mutableListOf() + if (filteredItems.size > 0) { + val filtered2Items: MutableList = mutableListOf() + if (filteredItems.size % 2 == 0 && filteredItems[0].entryType === PumpHistoryEntryType.ResumePump) { + // full resume suspends (S R S R) + filtered2Items.addAll(filteredItems) + } else if (filteredItems.size % 2 == 0 && filteredItems[0].entryType === PumpHistoryEntryType.SuspendPump) { + // not full suspends, need to retrive one more record and discard first one (R S R S) -> ([S] R S R [xS]) + filteredItems.removeAt(0) + val oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.SuspendPump) + if (oneMoreEntryFromHistory != null) { + filteredItems.add(oneMoreEntryFromHistory) + } else { + filteredItems.removeAt(filteredItems.size - 1) // remove last (unpaired R) + } + filtered2Items.addAll(filteredItems) + } else { + if (filteredItems[0].entryType === PumpHistoryEntryType.ResumePump) { + // get one more from history (R S R) -> ([S] R S R) + val oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.SuspendPump) + if (oneMoreEntryFromHistory != null) { + filteredItems.add(oneMoreEntryFromHistory) + } else { + filteredItems.removeAt(filteredItems.size - 1) // remove last (unpaired R) + } + filtered2Items.addAll(filteredItems) + } else { + // remove last and have paired items + filteredItems.removeAt(0) + filtered2Items.addAll(filteredItems) + } + } + if (filtered2Items.size > 0) { + sort(filtered2Items) + Collections.reverse(filtered2Items) + var i = 0 + while (i < filtered2Items.size) { + outList.add(TempBasalProcessDTO( + itemOne = filtered2Items[i], + itemTwo = filtered2Items[i + 1], + processOperation = TempBasalProcessDTO.Operation.Add)) + + i += 2 + } + } + } + return outList + } + + private fun getNoDeliveryRewindPrimeRecordsList(): List { + val primeItems: MutableList = getFilteredItems(newHistory, // + setOf(PumpHistoryEntryType.Prime)) + val outList: MutableList = ArrayList() + if (primeItems.size == 0) return outList + val filteredItems: MutableList = getFilteredItems(newHistory, // + setOf(PumpHistoryEntryType.Prime, + PumpHistoryEntryType.Rewind, + PumpHistoryEntryType.NoDeliveryAlarm, + PumpHistoryEntryType.Bolus, + PumpHistoryEntryType.TempBasalCombined) + ) + val tempData: MutableList = mutableListOf() + var startedItems = false + var finishedItems = false + for (filteredItem in filteredItems) { + if (filteredItem.entryType === PumpHistoryEntryType.Prime) { + startedItems = true + } + if (startedItems) { + if (filteredItem.entryType === PumpHistoryEntryType.Bolus || + filteredItem.entryType === PumpHistoryEntryType.TempBasalCombined) { + finishedItems = true + break + } + tempData.add(filteredItem) + } + } + if (!finishedItems) { + val filteredItemsOld: MutableList = getFilteredItems(allHistory, // + setOf(PumpHistoryEntryType.Rewind, + PumpHistoryEntryType.NoDeliveryAlarm, + PumpHistoryEntryType.Bolus, + PumpHistoryEntryType.TempBasalCombined) + ) + for (filteredItem in filteredItemsOld) { + if (filteredItem.entryType === PumpHistoryEntryType.Bolus || + filteredItem.entryType === PumpHistoryEntryType.TempBasalCombined) { + finishedItems = true + break + } + tempData.add(filteredItem) + } + } + if (!finishedItems) { + showLogs("NoDeliveryRewindPrimeRecords: Not finished Items: ", gson.toJson(tempData)) + return outList + } + showLogs("NoDeliveryRewindPrimeRecords: Records to evaluate: ", gson.toJson(tempData)) + var items: MutableList = getFilteredItems(tempData, PumpHistoryEntryType.Prime) + var itemTwo = items[0] + items = getFilteredItems(tempData, PumpHistoryEntryType.NoDeliveryAlarm) + if (items.size > 0) { + outList.add(TempBasalProcessDTO( + itemOne = items[items.size - 1], + itemTwo = itemTwo, + processOperation = TempBasalProcessDTO.Operation.Add)) + return outList + } + items = getFilteredItems(tempData, PumpHistoryEntryType.Rewind) + if (items.size > 0) { + outList.add(TempBasalProcessDTO( + itemOne = items[0], + processOperation = TempBasalProcessDTO.Operation.Add)) + return outList + } + return outList + } + + private fun getOneMoreEntryFromHistory(entryType: PumpHistoryEntryType): PumpHistoryEntry? { + val filteredItems: List = getFilteredItems(allHistory, entryType) + return if (filteredItems.size == 0) null else filteredItems[0] + } + + private fun filterTDDs(tdds: MutableList): MutableList { + val tddsOut: MutableList = mutableListOf() + for (tdd in tdds) { + if (tdd.entryType !== PumpHistoryEntryType.EndResultTotals) { + tddsOut.add(tdd) + } + } + return if (tddsOut.size == 0) tdds else tddsOut + } + + private fun tryToGetByLocalTime(atechDateTime: Long): Long { + return DateTimeUtil.toMillisFromATD(atechDateTime) + } + + private fun getTDDType(): PumpHistoryEntryType { + return if (!medtronicUtil.isModelSet) { + PumpHistoryEntryType.EndResultTotals + } else when (medtronicUtil.medtronicPumpModel) { + MedtronicDeviceType.Medtronic_515, + MedtronicDeviceType.Medtronic_715 -> PumpHistoryEntryType.DailyTotals515 + MedtronicDeviceType.Medtronic_522, + MedtronicDeviceType.Medtronic_722 -> PumpHistoryEntryType.DailyTotals522 + MedtronicDeviceType.Medtronic_523_Revel, + MedtronicDeviceType.Medtronic_723_Revel, + MedtronicDeviceType.Medtronic_554_Veo, + MedtronicDeviceType.Medtronic_754_Veo -> PumpHistoryEntryType.DailyTotals523 + + else -> { + PumpHistoryEntryType.EndResultTotals + } + } + } + + fun hasBasalProfileChanged(): Boolean { + val filteredItems: List = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile) + aapsLogger.debug(LTag.PUMP, "hasBasalProfileChanged. Items: " + gson.toJson(filteredItems)) + return filteredItems.size > 0 + } + + fun processLastBasalProfileChange(pumpType: PumpType, mdtPumpStatus: MedtronicPumpStatus) { + val filteredItems: List = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile) + aapsLogger.debug(LTag.PUMP, "processLastBasalProfileChange. Items: $filteredItems") + var newProfile: PumpHistoryEntry? = null + var lastDate: Long? = null + if (filteredItems.size == 1) { + newProfile = filteredItems[0] + } else if (filteredItems.size > 1) { + for (filteredItem in filteredItems) { + if (lastDate == null || lastDate < filteredItem.atechDateTime) { + newProfile = filteredItem + lastDate = newProfile.atechDateTime + } + } + } + if (newProfile != null) { + aapsLogger.debug(LTag.PUMP, "processLastBasalProfileChange. item found, setting new basalProfileLocally: $newProfile") + val basalProfile = newProfile.decodedData["Object"] as BasalProfile + mdtPumpStatus.basalsByHour = basalProfile.getProfilesByHour(pumpType) + } + } + + fun hasPumpTimeChanged(): Boolean { + return getStateFromFilteredList(setOf(PumpHistoryEntryType.NewTimeSet, // + PumpHistoryEntryType.ChangeTime)) + } + + fun setIsInInit(init: Boolean) { + isInit = init + } + + // HELPER METHODS + private fun sort(list: MutableList) { + // if (list != null && !list.isEmpty()) { + // Collections.sort(list, PumpHistoryEntry.Comparator()) + // } + list.sortWith(PumpHistoryEntry.Comparator()) + } + + private fun preProcessTBRs(TBRs_Input: MutableList): MutableList { + val TBRs: MutableList = mutableListOf() + val map: MutableMap = HashMap() + for (pumpHistoryEntry in TBRs_Input) { + if (map.containsKey(pumpHistoryEntry.DT)) { + medtronicPumpHistoryDecoder.decodeTempBasal(map[pumpHistoryEntry.DT]!!, pumpHistoryEntry) + pumpHistoryEntry.setEntryType(medtronicUtil.medtronicPumpModel, PumpHistoryEntryType.TempBasalCombined) + TBRs.add(pumpHistoryEntry) + map.remove(pumpHistoryEntry.DT) + } else { + map[pumpHistoryEntry.DT] = pumpHistoryEntry + } + } + return TBRs + } + + private fun getFilteredItems(entryTypes: Set?): MutableList { + return getFilteredItems(newHistory, entryTypes) + } + + private fun getFilteredItems(entryType: PumpHistoryEntryType): MutableList { + return getFilteredItems(newHistory, setOf(entryType)) + } + + private fun getStateFromFilteredList(entryTypes: Set?): Boolean { + return if (isInit) { + false + } else { + val filteredItems: List = getFilteredItems(entryTypes) + aapsLogger.debug(LTag.PUMP, "Items: $filteredItems") + filteredItems.size > 0 + } + } + + private fun getFilteredItems(inList: MutableList?, entryType: PumpHistoryEntryType): MutableList { + return getFilteredItems(inList, setOf(entryType)) + } + + private fun getFilteredItems(inList: MutableList?, entryTypes: Set?): MutableList { + val outList: MutableList = mutableListOf() + if (!inList.isNullOrEmpty()) { + for (pumpHistoryEntry in inList) { + if (entryTypes.isNullOrEmpty()) { + outList.add(pumpHistoryEntry) + } else { + if (entryTypes.contains(pumpHistoryEntry.entryType)) { + outList.add(pumpHistoryEntry) + } + } + } + } + return outList + } + + private val logPrefix: String + get() = "MedtronicHistoryData::" + + companion object { + + /** + * Double bolus debug. We seem to have small problem with double Boluses (or sometimes also missing boluses + * from history. This flag turns on debugging for that (default is off=false)... Debugging is pretty detailed, + * so log files will get bigger. + * Note: June 2020. Since this seems to be fixed, I am disabling this per default. I will leave code inside + * in case we need it again. Code that turns this on is commented out RileyLinkMedtronicService#verifyConfiguration() + */ + const val doubleBolusDebug = false + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java deleted file mode 100644 index 3cbd03f20d..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java +++ /dev/null @@ -1,384 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import androidx.annotation.NonNull; - -import com.google.gson.annotations.Expose; - -import org.joda.time.Instant; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by geoff on 6/1/15. - *

- * There are three basal profiles stored on the pump. (722 only?) They are all parsed the same, the user just has 3 to - * choose from: Standard, A, and B - *

- * The byte array is 48 times three byte entries long, plus a zero? If the profile is completely empty, it should have - * one entry: [0,0,0x3F]. The first entry of [0,0,0] marks the end of the used entries. - *

- * Each entry is assumed to span from the specified start time to the start time of the next entry, or to midnight if - * there are no more entries. - *

- * Individual entries are of the form [r,z,m] where r is the rate (in 0.025 U increments) z is zero (?) m is the start - * time-of-day for the basal rate period (in 30 minute increments) - */ -public class BasalProfile { - - private final AAPSLogger aapsLogger; - - public static final int MAX_RAW_DATA_SIZE = (48 * 3) + 1; - private static final boolean DEBUG_BASALPROFILE = false; - @Expose - private byte[] mRawData; // store as byte array to make transport (via parcel) easier - private List listEntries; - - - public BasalProfile(AAPSLogger aapsLogger) { - this.aapsLogger = aapsLogger; - init(); - } - - - public BasalProfile(AAPSLogger aapsLogger, byte[] data) { - this.aapsLogger = aapsLogger; - setRawData(data); - } - - - // this asUINT8 should be combined with Record.asUINT8, and placed in a new util class. - private static int readUnsignedByte(byte b) { - return (b < 0) ? b + 256 : b; - } - - - public void init() { - mRawData = new byte[MAX_RAW_DATA_SIZE]; - mRawData[0] = 0; - mRawData[1] = 0; - mRawData[2] = 0x3f; - } - - - private boolean setRawData(byte[] data) { - if (data == null) { - aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!"); - return false; - } - - // if we have just one entry through all day it looks like just length 1 - if (data.length == 1) { - data = MedtronicUtil.createByteArray(data[0], (byte) 0, (byte) 0); - } - - if (data.length == MAX_RAW_DATA_SIZE) { - mRawData = data; - } else { - int len = Math.min(MAX_RAW_DATA_SIZE, data.length); - mRawData = new byte[MAX_RAW_DATA_SIZE]; - System.arraycopy(data, 0, mRawData, 0, len); - } - - return true; - } - - - public boolean setRawDataFromHistory(byte[] data) { - if (data == null) { - aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!"); - return false; - } - - mRawData = new byte[MAX_RAW_DATA_SIZE]; - int item = 0; - - for (int i = 0; i < data.length - 2; i += 3) { - - if ((data[i] == 0) && (data[i + 1] == 0) && (data[i + 2] == 0)) { - mRawData[i] = 0; - mRawData[i + 1] = 0; - mRawData[i + 2] = 0; - } - - mRawData[i] = data[i + 1]; - mRawData[i + 1] = data[i + 2]; - mRawData[i + 2] = data[i]; - } - - return true; - } - - - public void dumpBasalProfile() { - aapsLogger.debug(LTag.PUMPCOMM, "Basal Profile entries:"); - List entries = getEntries(); - for (int i = 0; i < entries.size(); i++) { - BasalProfileEntry entry = entries.get(i); - String startString = entry.startTime.toString("HH:mm"); - // this doesn't work - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Entry %d, rate=%.3f (%s), start=%s (0x%02X)", i + 1, entry.rate, - ByteUtil.getHex(entry.rate_raw), startString, entry.startTime_raw)); - - } - } - - - public String getBasalProfileAsString() { - StringBuffer sb = new StringBuffer("Basal Profile entries:\n"); - List entries = getEntries(); - for (int i = 0; i < entries.size(); i++) { - BasalProfileEntry entry = entries.get(i); - String startString = entry.startTime.toString("HH:mm"); - - sb.append(String.format(Locale.ENGLISH, "Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString)); - } - - return sb.toString(); - } - - public String basalProfileToStringError() { - return "Basal Profile [rawData=" + ByteUtil.shortHexString(this.getRawData()) + "]"; - } - - - public String basalProfileToString() { - StringBuffer sb = new StringBuffer("Basal Profile ["); - List entries = getEntries(); - for (int i = 0; i < entries.size(); i++) { - BasalProfileEntry entry = entries.get(i); - String startString = entry.startTime.toString("HH:mm"); - - sb.append(String.format(Locale.ENGLISH, "%s=%.3f, ", startString, entry.rate)); - } - - sb.append("]"); - - return sb.toString(); - } - - - // TODO: this function must be expanded to include changes in which profile is in use. - // and changes to the profiles themselves. - public BasalProfileEntry getEntryForTime(Instant when) { - BasalProfileEntry rval = new BasalProfileEntry(); - List entries = getEntries(); - if (entries.size() == 0) { - aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): table is empty", - when.toDateTime().toLocalTime().toString("HH:mm"))); - return rval; - } - // Log.w(TAG,"Assuming first entry"); - rval = entries.get(0); - if (entries.size() == 1) { - aapsLogger.debug(LTag.PUMPCOMM, "getEntryForTime: Only one entry in profile"); - return rval; - } - - int localMillis = when.toDateTime().toLocalTime().getMillisOfDay(); - boolean done = false; - int i = 1; - while (!done) { - BasalProfileEntry entry = entries.get(i); - if (DEBUG_BASALPROFILE) { - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Comparing 'now'=%s to entry 'start time'=%s", when.toDateTime().toLocalTime() - .toString("HH:mm"), entry.startTime.toString("HH:mm"))); - } - if (localMillis >= entry.startTime.getMillisOfDay()) { - rval = entry; - if (DEBUG_BASALPROFILE) - aapsLogger.debug(LTag.PUMPCOMM, "Accepted Entry"); - } else { - // entry at i has later start time, keep older entry - if (DEBUG_BASALPROFILE) - aapsLogger.debug(LTag.PUMPCOMM, "Rejected Entry"); - done = true; - } - i++; - if (i >= entries.size()) { - done = true; - } - } - if (DEBUG_BASALPROFILE) { - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): Returning entry: rate=%.3f (%s), start=%s (%d)", when - .toDateTime().toLocalTime().toString("HH:mm"), rval.rate, ByteUtil.getHex(rval.rate_raw), - rval.startTime.toString("HH:mm"), rval.startTime_raw)); - } - return rval; - } - - - public List getEntries() { - List entries = new ArrayList<>(); - - if (mRawData == null || mRawData[2] == 0x3f) { - aapsLogger.warn(LTag.PUMPCOMM, "Raw Data is empty."); - return entries; // an empty list - } - int r, st; - - for (int i = 0; i < mRawData.length - 2; i += 3) { - - if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0)) - break; - - if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0x3f)) - break; - - r = MedtronicUtil.makeUnsignedShort(mRawData[i + 1], mRawData[i]); // readUnsignedByte(mRawData[i]); - st = readUnsignedByte(mRawData[i + 2]); - - try { - entries.add(new BasalProfileEntry(aapsLogger, r, st)); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPCOMM, "Error decoding basal profile from bytes: " + ByteUtil.shortHexString(mRawData)); - throw ex; - } - - } - - return entries; - } - - - /** - * This is used to prepare new profile - * - * @param entry - */ - public void addEntry(BasalProfileEntry entry) { - if (listEntries == null) - listEntries = new ArrayList<>(); - - listEntries.add(entry); - } - - - public void generateRawDataFromEntries() { - - List outData = new ArrayList<>(); - - for (BasalProfileEntry profileEntry : listEntries) { - - byte[] strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true); - - outData.add(profileEntry.rate_raw[0]); - outData.add(profileEntry.rate_raw[1]); - outData.add(profileEntry.startTime_raw); - } - - this.setRawData(MedtronicUtil.createByteArray(outData)); - - // return this.mRawData; - } - - - public Double[] getProfilesByHour(PumpType pumpType) { - - List entries = null; - - try { - entries = getEntries(); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPCOMM, "============================================================================="); - aapsLogger.error(LTag.PUMPCOMM, " Error generating entries. Ex.: " + ex, ex); - aapsLogger.error(LTag.PUMPCOMM, " rawBasalValues: " + ByteUtil.shortHexString(this.getRawData())); - aapsLogger.error(LTag.PUMPCOMM, "============================================================================="); - - //FabricUtil.createEvent("MedtronicBasalProfileGetByHourError", null); - } - - if (entries == null || entries.size() == 0) { - Double[] basalByHour = new Double[24]; - - for (int i = 0; i < 24; i++) { - basalByHour[i] = 0.0d; - } - - return basalByHour; - } - - Double[] basalByHour = new Double[24]; - - for (int i = 0; i < entries.size(); i++) { - BasalProfileEntry current = entries.get(i); - - int currentTime = (current.startTime_raw % 2 == 0) ? current.startTime_raw : current.startTime_raw - 1; - - currentTime = (currentTime * 30) / 60; - - int lastHour; - if ((i + 1) == entries.size()) { - lastHour = 24; - } else { - BasalProfileEntry basalProfileEntry = entries.get(i + 1); - - int rawTime = (basalProfileEntry.startTime_raw % 2 == 0) ? basalProfileEntry.startTime_raw - : basalProfileEntry.startTime_raw - 1; - - lastHour = (rawTime * 30) / 60; - } - - // System.out.println("Current time: " + currentTime + " Next Time: " + lastHour); - - for (int j = currentTime; j < lastHour; j++) { - if (pumpType == null) - basalByHour[j] = current.rate; - else - basalByHour[j] = pumpType.determineCorrectBasalSize(current.rate); - } - } - - return basalByHour; - } - - - public static String getProfilesByHourToString(Double[] data) { - - StringBuilder stringBuilder = new StringBuilder(); - - for (Double value : data) { - stringBuilder.append(String.format("%.3f", value)); - stringBuilder.append(" "); - } - - return stringBuilder.toString(); - - } - - - public byte[] getRawData() { - return this.mRawData; - } - - - @NonNull public String toString() { - return basalProfileToString(); - } - - - public boolean verify(PumpType pumpType) { - - try { - getEntries(); - } catch (Exception ex) { - return false; - } - - Double[] profilesByHour = getProfilesByHour(pumpType); - - for (Double aDouble : profilesByHour) { - if (aDouble > 35.0d) - return false; - } - - return true; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt new file mode 100644 index 0000000000..a3d373c5cf --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt @@ -0,0 +1,323 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import org.joda.time.Instant +import java.util.* + +/** + * Created by geoff on 6/1/15. + * + * + * There are three basal profiles stored on the pump. (722 only?) They are all parsed the same, the user just has 3 to + * choose from: Standard, A, and B + * + * + * The byte array is 48 times three byte entries long, plus a zero? If the profile is completely empty, it should have + * one entry: [0,0,0x3F]. The first entry of [0,0,0] marks the end of the used entries. + * + * + * Each entry is assumed to span from the specified start time to the start time of the next entry, or to midnight if + * there are no more entries. + * + * + * Individual entries are of the form [r,z,m] where r is the rate (in 0.025 U increments) z is zero (?) m is the start + * time-of-day for the basal rate period (in 30 minute increments) + */ +class BasalProfile { + + private val aapsLogger: AAPSLogger + + @Expose + lateinit var rawData: ByteArray // store as byte array to make transport (via parcel) easier + private set + + private var listEntries: MutableList? = null + + constructor(aapsLogger: AAPSLogger) { + this.aapsLogger = aapsLogger + init() + } + + constructor(aapsLogger: AAPSLogger, data: ByteArray) { + this.aapsLogger = aapsLogger + setRawData(data) + } + + fun init() { + rawData = byteArrayOf(0, 0, 0x3f) + } + + private fun setRawData(data: ByteArray): Boolean { + var dataInternal: ByteArray = data + // if (dataInternal == null) { + // aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!") + // return false + // } + + // if we have just one entry through all day it looks like just length 1 + if (dataInternal.size == 1) { + dataInternal = byteArrayOf(dataInternal[0], 0, 0) + } + if (dataInternal.size == MAX_RAW_DATA_SIZE) { + rawData = dataInternal + } else { + val len = Math.min(MAX_RAW_DATA_SIZE, data.size) + rawData = ByteArray(MAX_RAW_DATA_SIZE) + System.arraycopy(data, 0, rawData, 0, len) + } + return true + } + + fun setRawDataFromHistory(data: ByteArray?): Boolean { + if (data == null) { + aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!") + return false + } + rawData = ByteArray(MAX_RAW_DATA_SIZE) + var i = 0 + while (i < data.size - 2) { + if (data[i] == 0.toByte() && data[i + 1] == 0.toByte() && data[i + 2] == 0.toByte()) { + rawData[i] = 0 + rawData[i + 1] = 0 + rawData[i + 2] = 0 + } + rawData[i] = data[i + 1] + rawData[i + 1] = data[i + 2] + rawData[i + 2] = data[i] + i += 3 + } + return true + } + + fun dumpBasalProfile() { + aapsLogger.debug(LTag.PUMPCOMM, "Basal Profile entries:") + val entries = getEntries() + for (i in entries.indices) { + val entry = entries[i] + val startString = entry.startTime!!.toString("HH:mm") + // this doesn't work + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Entry %d, rate=%.3f (%s), start=%s (0x%02X)", i + 1, entry.rate, + ByteUtil.getHex(entry.rate_raw), startString, entry.startTime_raw)) + } + } + + val basalProfileAsString: String + get() { + val sb = StringBuffer("Basal Profile entries:\n") + val entries = getEntries() + for (i in entries.indices) { + val entry = entries[i] + val startString = entry.startTime!!.toString("HH:mm") + sb.append(String.format(Locale.ENGLISH, "Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString)) + } + return sb.toString() + } + + fun basalProfileToStringError(): String { + return "Basal Profile [rawData=" + ByteUtil.shortHexString(rawData) + "]" + } + + fun basalProfileToString(): String { + val sb = StringBuffer("Basal Profile [") + val entries = getEntries() + for (i in entries.indices) { + val entry = entries[i] + val startString = entry.startTime!!.toString("HH:mm") + sb.append(String.format(Locale.ENGLISH, "%s=%.3f, ", startString, entry.rate)) + } + sb.append("]") + return sb.toString() + } + + // TODO: this function must be expanded to include changes in which profile is in use. + // and changes to the profiles themselves. + fun getEntryForTime(`when`: Instant): BasalProfileEntry { + var rval = BasalProfileEntry() + val entries = getEntries() + if (entries.size == 0) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): table is empty", + `when`.toDateTime().toLocalTime().toString("HH:mm"))) + return rval + } + // Log.w(TAG,"Assuming first entry"); + rval = entries[0] + if (entries.size == 1) { + aapsLogger.debug(LTag.PUMPCOMM, "getEntryForTime: Only one entry in profile") + return rval + } + val localMillis = `when`.toDateTime().toLocalTime().millisOfDay + var done = false + var i = 1 + while (!done) { + val entry = entries[i] + if (DEBUG_BASALPROFILE) { + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Comparing 'now'=%s to entry 'start time'=%s", `when`.toDateTime().toLocalTime() + .toString("HH:mm"), entry.startTime!!.toString("HH:mm"))) + } + if (localMillis >= entry.startTime!!.millisOfDay) { + rval = entry + if (DEBUG_BASALPROFILE) aapsLogger.debug(LTag.PUMPCOMM, "Accepted Entry") + } else { + // entry at i has later start time, keep older entry + if (DEBUG_BASALPROFILE) aapsLogger.debug(LTag.PUMPCOMM, "Rejected Entry") + done = true + } + i++ + if (i >= entries.size) { + done = true + } + } + if (DEBUG_BASALPROFILE) { + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): Returning entry: rate=%.3f (%s), start=%s (%d)", `when` + .toDateTime().toLocalTime().toString("HH:mm"), rval.rate, ByteUtil.getHex(rval.rate_raw), + rval.startTime!!.toString("HH:mm"), rval.startTime_raw)) + } + return rval + }// readUnsignedByte(mRawData[i]); + + // an empty list + fun getEntries(): List { + val entries: MutableList = ArrayList() + if (rawData[2] == 0x3f.toByte()) { + aapsLogger.warn(LTag.PUMPCOMM, "Raw Data is empty.") + return entries // an empty list + } + var r: Int + var st: Int + var i = 0 + while (i < rawData.size - 2) { + if (rawData[i] == 0.toByte() && rawData[i + 1] == 0.toByte() && rawData[i + 2] == 0.toByte()) break + if (rawData[i] == 0.toByte() && rawData[i + 1] == 0.toByte() && rawData[i + 2] == 0x3f.toByte()) break + r = MedtronicUtil.makeUnsignedShort(rawData[i + 1].toInt(), rawData[i].toInt()) // readUnsignedByte(mRawData[i]); + st = readUnsignedByte(rawData[i + 2]) + try { + entries.add(BasalProfileEntry(aapsLogger, r, st)) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPCOMM, "Error decoding basal profile from bytes: " + ByteUtil.shortHexString(rawData)) + throw ex + } + i += 3 + } + return entries + } + + /** + * This is used to prepare new profile + * + * @param entry + */ + fun addEntry(entry: BasalProfileEntry) { + if (listEntries == null) listEntries = ArrayList() + listEntries!!.add(entry) + } + + fun generateRawDataFromEntries() { + val outData: MutableList = ArrayList() + for (profileEntry in listEntries!!) { + //val strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true) + outData.add(profileEntry.rate_raw[0]) + outData.add(profileEntry.rate_raw[1]) + outData.add(profileEntry.startTime_raw) + } + setRawData(MedtronicUtil.createByteArray(outData)) + + // return this.mRawData; + } + + fun getProfilesByHour(pumpType: PumpType): DoubleArray { + var entriesCopy: List? = null + try { + entriesCopy = getEntries() + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPCOMM, "=============================================================================") + aapsLogger.error(LTag.PUMPCOMM, " Error generating entries. Ex.: $ex", ex) + aapsLogger.error(LTag.PUMPCOMM, " rawBasalValues: " + ByteUtil.shortHexString(rawData)) + aapsLogger.error(LTag.PUMPCOMM, "=============================================================================") + //FabricUtil.createEvent("MedtronicBasalProfileGetByHourError", null); + } + + val basalByHour = DoubleArray(24) + + if (entriesCopy == null || entriesCopy.size == 0) { + for (i in 0..23) { + basalByHour[i] = 0.0 + } + return basalByHour + } + + for (i in entriesCopy.indices) { + val current = entriesCopy[i] + var currentTime = if (current.startTime_raw % 2 == 0) current.startTime_raw.toInt() else current.startTime_raw - 1 + currentTime = currentTime * 30 / 60 + var lastHour: Int + lastHour = if (i + 1 == entriesCopy.size) { + 24 + } else { + val basalProfileEntry = entriesCopy[i + 1] + val rawTime = if (basalProfileEntry.startTime_raw % 2 == 0) basalProfileEntry.startTime_raw.toInt() else basalProfileEntry.startTime_raw - 1 + rawTime * 30 / 60 + } + + // System.out.println("Current time: " + currentTime + " Next Time: " + lastHour); + for (j in currentTime until lastHour) { + // if (pumpType == null) + // basalByHour[j] = current.rate + // else + basalByHour[j] = pumpType.determineCorrectBasalSize(current.rate) + } + } + return basalByHour + } + + override fun toString(): String { + return basalProfileToString() + } + + fun verify(pumpType: PumpType): Boolean { + try { + getEntries() + } catch (ex: Exception) { + return false + } + val profilesByHour = getProfilesByHour(pumpType) + for (aDouble in profilesByHour) { + if (aDouble > 35.0) return false + } + return true + } + + companion object { + + const val MAX_RAW_DATA_SIZE = 48 * 3 + 1 + private const val DEBUG_BASALPROFILE = false + + private fun readUnsignedByte(b: Byte): Int { + return if (b < 0) b + 256 else b.toInt() + } + + @JvmStatic + fun getProfilesByHourToString(data: DoubleArray): String { + val stringBuilder = StringBuilder() + for (value in data) { + stringBuilder.append(String.format("%.3f", value)) + stringBuilder.append(" ") + } + return stringBuilder.toString() + } + + @JvmStatic + fun isBasalProfileByHourUndefined(basalByHour: DoubleArray): Boolean { + for (i in 0..23) { + if (basalByHour[i] > 0.0) { + return false; + } + } + return true + } + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java deleted file mode 100644 index da2a7181c0..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java +++ /dev/null @@ -1,81 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import org.joda.time.LocalTime; - -import java.util.Locale; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * Created by geoff on 6/1/15. - * This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile - * - fixed rate is not one bit but two - */ -public class BasalProfileEntry { - - byte[] rate_raw; - public double rate; - byte startTime_raw; - public LocalTime startTime; // Just a "time of day" - - public BasalProfileEntry() { - rate = -9.999E6; - rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(0xFF, true); - startTime = new LocalTime(0); - startTime_raw = (byte) 0xFF; - } - - public BasalProfileEntry(double rate, int hour, int minutes) { - byte[] data = MedtronicUtil.getBasalStrokes(rate, true); - - rate_raw = new byte[2]; - rate_raw[0] = data[1]; - rate_raw[1] = data[0]; - - int interval = hour * 2; - - if (minutes == 30) { - interval++; - } - - startTime_raw = (byte) interval; - startTime = new LocalTime(hour, minutes == 30 ? 30 : 0); - } - - BasalProfileEntry(AAPSLogger aapsLogger, int rateStrokes, int startTimeInterval) { - // rateByte is insulin delivery rate, U/hr, in 0.025 U increments - // startTimeByte is time-of-day, in 30 minute increments - rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateStrokes, true); - rate = rateStrokes * 0.025; - startTime_raw = (byte) startTimeInterval; - - try { - startTime = new LocalTime(startTimeInterval / 2, (startTimeInterval % 2) * 30); - } catch (Exception ex) { - aapsLogger.error(LTag.PUMPCOMM, - String.format(Locale.ENGLISH, "Error creating BasalProfileEntry: startTimeInterval=%d, startTime_raw=%d, hours=%d, rateStrokes=%d", - startTimeInterval, startTime_raw, startTimeInterval / 2, rateStrokes)); - throw ex; - } - - } - - BasalProfileEntry(byte rateByte, int startTimeByte) { - // rateByte is insulin delivery rate, U/hr, in 0.025 U increments - // startTimeByte is time-of-day, in 30 minute increments - rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateByte, true); - rate = rateByte * 0.025; - startTime_raw = (byte) startTimeByte; - startTime = new LocalTime(startTimeByte / 2, (startTimeByte % 2) * 30); - } - - public void setStartTime(LocalTime localTime) { - this.startTime = localTime; - } - - public void setRate(double rate) { - this.rate = rate; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.kt new file mode 100644 index 0000000000..6ffbefcf2d --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.kt @@ -0,0 +1,74 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import org.joda.time.LocalTime +import java.util.* + +/** + * Created by geoff on 6/1/15. + * This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile + * - fixed rate is not one bit but two + */ +class BasalProfileEntry { + + var rate_raw: ByteArray + var rate = 0.0 + set(value) { + field = value + } + + var startTime_raw: Byte + var startTime : LocalTime? = null // Just a "time of day" + set(value) { + field = value + } + + + constructor() { + rate = -9.999E6 + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(0xFF, true) + startTime = LocalTime(0) + startTime_raw = 0xFF.toByte() + } + + constructor(rate: Double, hour: Int, minutes: Int) { + val data = MedtronicUtil.getBasalStrokes(rate, true) + rate_raw = ByteArray(2) + rate_raw[0] = data[1] + rate_raw[1] = data[0] + var interval = hour * 2 + if (minutes == 30) { + interval++ + } + startTime_raw = interval.toByte() + startTime = LocalTime(hour, if (minutes == 30) 30 else 0) + } + + internal constructor(aapsLogger: AAPSLogger, rateStrokes: Int, startTimeInterval: Int) { + // rateByte is insulin delivery rate, U/hr, in 0.025 U increments + // startTimeByte is time-of-day, in 30 minute increments + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateStrokes, true) + rate = rateStrokes * 0.025 + startTime_raw = startTimeInterval.toByte() + startTime = try { + LocalTime(startTimeInterval / 2, startTimeInterval % 2 * 30) + } catch (ex: Exception) { + aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error creating BasalProfileEntry: startTimeInterval=%d, startTime_raw=%d, hours=%d, rateStrokes=%d", + startTimeInterval, startTime_raw, startTimeInterval / 2, rateStrokes)) + throw ex + } + } + + internal constructor(rateByte: Byte, startTimeByte: Int) { + // rateByte is insulin delivery rate, U/hr, in 0.025 U increments + // startTimeByte is time-of-day, in 30 minute increments + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateByte.toInt(), true) + rate = rateByte * 0.025 + startTime_raw = startTimeByte.toByte() + startTime = LocalTime(startTimeByte / 2, startTimeByte % 2 * 30) + } + + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java deleted file mode 100644 index 835d43c072..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java +++ /dev/null @@ -1,60 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import com.google.gson.annotations.Expose; - -import androidx.annotation.NonNull; - -import java.util.Locale; - -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; - -/** - * Created by andy on 6/14/18. - */ - -public class BatteryStatusDTO { - - @Expose - public BatteryStatusType batteryStatusType; - @Expose - public Double voltage; - - public boolean extendedDataReceived = false; - - - public int getCalculatedPercent(BatteryType batteryType) { - if (voltage == null || batteryType == BatteryType.None) { - return (batteryStatusType == BatteryStatusType.Low || batteryStatusType == BatteryStatusType.Unknown) ? 18 : 70; - } - - double percent = (voltage - batteryType.lowVoltage) / (batteryType.highVoltage - batteryType.lowVoltage); - - int percentInt = (int) (percent * 100.0d); - - if (percentInt < 0) - percentInt = 1; - - if (percentInt > 100) - percentInt = 100; - - return percentInt; - } - - - @NonNull public String toString() { - return String.format(Locale.ENGLISH, "BatteryStatusDTO [voltage=%.2f, alkaline=%d, lithium=%d, niZn=%d, nimh=%d]", - voltage == null ? 0.0f : voltage, - getCalculatedPercent(BatteryType.Alkaline), - getCalculatedPercent(BatteryType.Lithium), - getCalculatedPercent(BatteryType.NiZn), - getCalculatedPercent(BatteryType.NiMH)); - } - - - public enum BatteryStatusType { - Normal, - Low, - Unknown - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.kt new file mode 100644 index 0000000000..60128d51a5 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.kt @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType +import java.util.* + +/** + * Created by andy on 6/14/18. + */ +class BatteryStatusDTO { + + @Expose + var batteryStatusType: BatteryStatusType? = null + @Expose + var voltage: Double? = null + + var extendedDataReceived = false + + + fun getCalculatedPercent(batteryType: BatteryType): Int { + if (voltage == null || batteryType === BatteryType.None) { + return if (batteryStatusType == BatteryStatusType.Low || batteryStatusType == BatteryStatusType.Unknown) 18 else 70 + } + val percent = (voltage!! - batteryType.lowVoltage) / (batteryType.highVoltage - batteryType.lowVoltage) + var percentInt = (percent * 100.0).toInt() + if (percentInt < 0) percentInt = 1 + if (percentInt > 100) percentInt = 100 + return percentInt + } + + + override fun toString(): String { + return String.format(Locale.ENGLISH, "BatteryStatusDTO [voltage=%.2f, alkaline=%d, lithium=%d, niZn=%d, nimh=%d]", + if (voltage == null) 0.0f else voltage, + getCalculatedPercent(BatteryType.Alkaline), + getCalculatedPercent(BatteryType.Lithium), + getCalculatedPercent(BatteryType.NiZn), + getCalculatedPercent(BatteryType.NiMH)) + } + + enum class BatteryStatusType { + Normal, + Low, + Unknown + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java deleted file mode 100644 index 8574bd831a..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java +++ /dev/null @@ -1,160 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import com.google.gson.annotations.Expose; - -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType; - -/** - * Application: GGC - GNU Gluco Control - * Plug-in: Pump Tool (support for Pump devices) - *

- * See AUTHORS for copyright information. - *

- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later - * version. - *

- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - *

- * Filename: BolusDTO Description: Bolus DTO - *

- * Author: Andy {andy@atech-software.com} - */ - -public class BolusDTO extends PumpTimeStampedRecord { - - @Expose - private Double requestedAmount; - @Expose - private Double deliveredAmount; - @Expose - private Double immediateAmount; // when Multiwave this is used - @Expose - private Integer duration; - @Expose - private PumpBolusType bolusType; - private Double insulinOnBoard; - - - public BolusDTO() { - // this.decimalPrecission = 2; - } - - - public Double getRequestedAmount() { - return requestedAmount; - } - - - public void setRequestedAmount(Double requestedAmount) { - this.requestedAmount = requestedAmount; - } - - - public Double getDeliveredAmount() { - return deliveredAmount; - } - - - public void setDeliveredAmount(Double deliveredAmount) { - this.deliveredAmount = deliveredAmount; - } - - - public Integer getDuration() { - return duration; - } - - - public void setDuration(Integer duration) { - this.duration = duration; - } - - - public PumpBolusType getBolusType() { - return bolusType; - } - - - public void setBolusType(PumpBolusType bolusType) { - this.bolusType = bolusType; - } - - - public Double getInsulinOnBoard() { - return insulinOnBoard; - } - - - public void setInsulinOnBoard(Double insulinOnBoard) { - this.insulinOnBoard = insulinOnBoard; - } - - - private String getDurationString() { - int minutes = this.duration; - - int h = minutes / 60; - - minutes -= (h * 60); - - return StringUtil.getLeadingZero(h, 2) + ":" + StringUtil.getLeadingZero(minutes, 2); - } - - - public String getValue() { - if ((bolusType == PumpBolusType.Normal) || (bolusType == PumpBolusType.Audio)) { - return getFormattedDecimal(this.deliveredAmount); - } else if (bolusType == PumpBolusType.Extended) { - return String.format("AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(this.deliveredAmount), - getDurationString()); - } else { - return String.format("AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(this.immediateAmount), - getFormattedDecimal(this.deliveredAmount), getDurationString()); - } - } - - - public String getDisplayableValue() { - String value = getValue(); - - value = value.replace("AMOUNT_SQUARE=", "Amount Square: "); - value = value.replace("AMOUNT=", "Amount: "); - value = value.replace("DURATION=", "Duration: "); - - return value; - } - - - public Double getImmediateAmount() { - return immediateAmount; - } - - - public void setImmediateAmount(Double immediateAmount) { - this.immediateAmount = immediateAmount; - } - - - public String getFormattedDecimal(double value) { - return StringUtil.getFormatedValueUS(value, 2); - } - - - public String getBolusKey() { - return "Bolus_" + this.bolusType.name(); - - } - - - @Override - public String toString() { - return "BolusDTO [type=" + bolusType.name() + ", " + getValue() + "]"; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.kt new file mode 100644 index 0000000000..2b738c34f0 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.kt @@ -0,0 +1,85 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType + +/** + * Application: GGC - GNU Gluco Control + * Plug-in: Pump Tool (support for Pump devices) + * + * + * See AUTHORS for copyright information. + * + * + * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Filename: BolusDTO Description: Bolus DTO + * + * + * Author: Andy {andy@atech-software.com} + */ +class BolusDTO constructor(atechDateTime: Long, + @Expose var requestedAmount: Double, + @Expose var deliveredAmount: Double, + @Expose var duration: Int = 0 +) : PumpTimeStampedRecord(atechDateTime) { + + @Expose + var immediateAmount: Double? = null // when Multiwave this is used + + @Expose + lateinit var bolusType: PumpBolusType + + var insulinOnBoard: Double? = null + + private val durationString: String + get() { + var minutes = duration + val h = minutes / 60 + minutes -= h * 60 + return StringUtil.getLeadingZero(h, 2) + ":" + StringUtil.getLeadingZero(minutes, 2) + } + + val value: String + get() = if (bolusType === PumpBolusType.Normal || bolusType === PumpBolusType.Audio) { + getFormattedDecimal(deliveredAmount) + } else if (bolusType === PumpBolusType.Extended) { + String.format("AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(deliveredAmount), + durationString) + } else { + String.format("AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(immediateAmount!!), + getFormattedDecimal(deliveredAmount), durationString) + } + + val displayableValue: String + get() { + var valueTemp = value + valueTemp = valueTemp.replace("AMOUNT_SQUARE=", "Amount Square: ") + valueTemp = valueTemp.replace("AMOUNT=", "Amount: ") + valueTemp = valueTemp.replace("DURATION=", "Duration: ") + return valueTemp + } + + override fun getFormattedDecimal(value: Double): String { + return StringUtil.getFormatedValueUS(value, 2) + } + + val bolusKey: String + get() = "Bolus_" + bolusType.name + + override fun toString(): String { + return "BolusDTO [type=" + bolusType.name + ", " + value + "]" + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java deleted file mode 100644 index 24b63015c4..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java +++ /dev/null @@ -1,50 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import java.util.Locale; - -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; - -/** - * Created by andy on 18.05.15. - */ -public class BolusWizardDTO extends PumpTimeStampedRecord { - - // bloodGlucose and bgTarets are in mg/dL - public Integer bloodGlucose = 0; // mg/dL - public Integer carbs = 0; - public String chUnit = "g"; - - public Float carbRatio = 0.0f; - public Float insulinSensitivity = 0.0f; - public Integer bgTargetLow = 0; - public Integer bgTargetHigh = 0; - public Float bolusTotal = 0.0f; - public Float correctionEstimate = 0.0f; - public Float foodEstimate = 0.0f; - public Float unabsorbedInsulin = 0.0f; - - - // public LocalDateTime localDateTime; - // public long atechDateTime; - - public String getValue() { - return String.format(Locale.ENGLISH, "BG=%d;CH=%d;CH_UNIT=%s;CH_INS_RATIO=%5.3f;BG_INS_RATIO=%5.3f;" - + "BG_TARGET_LOW=%d;BG_TARGET_HIGH=%d;BOLUS_TOTAL=%5.3f;" - + "BOLUS_CORRECTION=%5.3f;BOLUS_FOOD=%5.3f;UNABSORBED_INSULIN=%5.3f", // - bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // - bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin); - } - - public String getDisplayableValue() { - return String.format(Locale.ENGLISH, "Bg=%d, CH=%d %s, Ch/Ins Ratio=%5.3f, Bg/Ins Ratio=%5.3f;" - + "Bg Target(L/H)=%d/%d, Bolus: Total=%5.3f, " - + "Correction=%5.3f, Food=%5.3f, IOB=%5.3f", // - bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // - bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin); - } - - - public String toString() { - return "BolusWizardDTO [dateTime=" + DateTimeUtil.toString(atechDateTime) + ", " + getValue() + "]"; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.kt new file mode 100644 index 0000000000..cf73a0506d --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.kt @@ -0,0 +1,44 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import java.util.* + +/** + * Created by andy on 18.05.15. + */ +class BolusWizardDTO : PumpTimeStampedRecord() { + + // bloodGlucose and bgTarets are in mg/dL + var bloodGlucose = 0 // mg/dL + var carbs = 0 + var chUnit = "g" + var carbRatio = 0.0f + var insulinSensitivity = 0.0f + var bgTargetLow = 0 + var bgTargetHigh = 0 + var bolusTotal = 0.0f + var correctionEstimate = 0.0f + var foodEstimate = 0.0f + var unabsorbedInsulin = 0.0f// + + + val value: String + get() = String.format(Locale.ENGLISH, "BG=%d;CH=%d;CH_UNIT=%s;CH_INS_RATIO=%5.3f;BG_INS_RATIO=%5.3f;" + + "BG_TARGET_LOW=%d;BG_TARGET_HIGH=%d;BOLUS_TOTAL=%5.3f;" + + "BOLUS_CORRECTION=%5.3f;BOLUS_FOOD=%5.3f;UNABSORBED_INSULIN=%5.3f", // + bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // + bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin) + + // + // + val displayableValue: String + get() = String.format(Locale.ENGLISH, "Bg=%d, CH=%d %s, Ch/Ins Ratio=%5.3f, Bg/Ins Ratio=%5.3f;" + + "Bg Target(L/H)=%d/%d, Bolus: Total=%5.3f, " + + "Correction=%5.3f, Food=%5.3f, IOB=%5.3f", // + bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // + bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin) + + override fun toString(): String { + return "BolusWizardDTO [dateTime=" + DateTimeUtil.toString(atechDateTime) + ", " + value + "]" + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java deleted file mode 100644 index 1623e03ff5..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import org.joda.time.LocalDateTime; - -/** - * Created by andy on 2/27/19. - */ - -public class ClockDTO { - - public LocalDateTime localDeviceTime; - - public LocalDateTime pumpTime; - - public int timeDifference; // s (pump -> local) -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.kt new file mode 100644 index 0000000000..be99c3b34f --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.kt @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import org.joda.time.LocalDateTime + +/** + * Created by andy on 2/27/19. + */ +class ClockDTO constructor(var localDeviceTime: LocalDateTime, + var pumpTime: LocalDateTime) { + // var localDeviceTime: LocalDateTime? = null + // var pumpTime: LocalDateTime? = null + var timeDifference = 0 +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java deleted file mode 100644 index af12ac6962..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java +++ /dev/null @@ -1,258 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import androidx.annotation.NonNull; - -import com.google.gson.annotations.Expose; - -import org.apache.commons.lang3.builder.ToStringBuilder; - -import java.util.Locale; - -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; - -/** - * Created by andy on 11/3/18. - */ - -/** - * NOTE: Decoding is only done for insulin part, everything else is pretty must left undecoded. - */ - -public class DailyTotalsDTO { - - // bg avg, bg low hi, number Bgs, - // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, - // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, - // Bolus=1.7, Fodd, Corr, Manual=1.7, - // Num bOlus=1, food/corr, Food+corr, manual bolus=1 - private Double bgAvg; - private Double bgLow; - private Double bgHigh; - private Integer bgCount; - - private Double sensorAvg; - private Double sensorMin; - private Double sensorMax; - private Integer sensorCalcCount; - private Integer sensorDataCount; - - @Expose - private Double insulinTotal = 0.0d; - @Expose - private Double insulinBasal = 0.0d; - @Expose - private Double insulinBolus = 0.0d; - private Double insulinCarbs; - - private Double bolusTotal; - private Double bolusFood; - private Double bolusFoodAndCorr; - private Double bolusCorrection; - private Double bolusManual; - - private Integer bolusCount; - private Integer bolusCountFoodOrCorr; - // Integer bolusCountCorr; - Integer bolusCountFoodAndCorr; - Integer bolusCountManual; - private Integer bolusCountFood; - private Integer bolusCountCorr; - - PumpHistoryEntry entry; - - - public DailyTotalsDTO(PumpHistoryEntry entry) { - this.entry = entry; - - switch (entry.getEntryType()) { - case EndResultTotals: - decodeEndResultsTotals(entry); - break; - - case DailyTotals515: - decodeDailyTotals515(entry.getBody()); - break; - - case DailyTotals522: - decodeDailyTotals522(entry.getBody()); - break; - - case DailyTotals523: - decodeDailyTotals523(entry.getBody()); - break; - - default: - break; - } - - setDisplayable(); - } - - - private void setDisplayable() { - - if (this.insulinBasal == null) { - this.entry.setDisplayableValue("Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2)); - } else { - this.entry.setDisplayableValue("Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2) - + ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2)); - } - - } - - - private void decodeEndResultsTotals(PumpHistoryEntry entry) { - double totals = ByteUtil.toInt((int) entry.getHead()[0], (int) entry.getHead()[1], (int) entry.getHead()[2], - (int) entry.getHead()[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d; - - this.insulinTotal = totals; - - entry.addDecodedData("Totals", totals); - } - - - private void testDecode(byte[] data) { - - // Daily - - byte[] body = data; // entry.getBody(); - //System.out.println("Totals 522"); - - for (int i = 0; i < body.length - 2; i++) { - - int j = ByteUtil.toInt(body[i], body[i + 1]); - int k = ByteUtil.toInt(body[i], body[i + 1], body[i + 2]); - - int j1 = ByteUtil.toInt(body[i + 1], body[i]); - int k1 = ByteUtil.toInt(body[i + 2], body[i + 1], body[i]); - - System.out.println(String.format(Locale.ENGLISH, - "index: %d, number=%d, del/40=%.3f, del/10=%.3f, singular=%d, sing_hex=%s", i, j, j / 40.0d, j / 10.0d, - body[i], ByteUtil.shortHexString(body[i]))); - - System.out.println(String.format(Locale.ENGLISH, " number[k,j1,k1]=%d / %d /%d, del/40=%.3f, del/40=%.3f, del/40=%.3f", - k, j1, k1, k / 40.0d, j1 / 40.0d, k1 / 40.0d)); - - } - } - - - private void decodeDailyTotals515(byte[] data) { - // LOG.debug("Can't decode DailyTotals515: Body={}", ByteUtil.getHex(data)); - - this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; - this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; - this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; - - // Delivery Stats: BG AVG: Bg Low/Hi=none,Number BGs=0 - // Delivery Stats: INSULIN: Basal 22.30, Bolus=4.20, Catbs = 0g (26.5) - // Delivery Stats: BOLUS: Food=0.00, Corr=0.00, Manual=4.20 - // Delivery Stats: NUM BOLUS: Food/Corr=0,Food+Corr=0, Manual=3 - - //LOG.debug("515: {}", toString()); - } - - - private void decodeDailyTotals522(byte[] data) { - - this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; - this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; - this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; - - this.bolusTotal = ByteUtil.toInt(data[17], data[18], data[19]) / 40.0d; - this.bolusFood = ByteUtil.toInt(data[21], data[22]) / 40.0d; - this.bolusCorrection = ByteUtil.toInt(data[23], data[24], data[25]) / 40.0d; - this.bolusManual = ByteUtil.toInt(data[26], data[27], data[28]) / 40.0d; - - bolusCount = ByteUtil.asUINT8(data[30]); - bolusCountFoodOrCorr = ByteUtil.asUINT8(data[31]); - bolusCountFoodAndCorr = ByteUtil.asUINT8(data[32]); - bolusCountManual = ByteUtil.asUINT8(data[33]); - - // bg avg, bg low hi, number Bgs, - // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, - // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, - // Bolus=1.7[18,19], Fodd, Corr, Manual=1.7[27,28], - // Num bOlus=1, food/corr, Food+corr, manual bolus=1 - - //LOG.debug("522: {}", toString()); - } - - - private void decodeDailyTotals523(byte[] data) { - - this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; - this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; - this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; - this.insulinCarbs = ByteUtil.toInt(data[16], data[17]) * 1.0d; - - this.bolusFood = ByteUtil.toInt(data[18], data[19]) / 40.0d; - this.bolusCorrection = ByteUtil.toInt(data[20], data[21]) / 40.0d; - this.bolusFoodAndCorr = ByteUtil.toInt(data[22], data[23]) / 40.0d; - this.bolusManual = ByteUtil.toInt(data[24], data[25]) / 40.0d; - - this.bolusCountFood = ByteUtil.asUINT8(data[26]); - this.bolusCountCorr = ByteUtil.asUINT8(data[27]); - this.bolusCountFoodAndCorr = ByteUtil.asUINT8(data[28]); - this.bolusCountManual = ByteUtil.asUINT8(data[29]); // + - - // Delivery Stats: Carbs=11, Total Insulin=3.850, Basal=2.000 - // Delivery Stats: Basal 52,Bolus 1.850, Bolus=48%o - // Delivery Stats: Food only=0.9, Food only#=1, Corr only = 0.0 - // Delivery Stats: #Corr_only=0,Food+Corr=0.000, #Food+Corr=0 - // Delivery Stats: Manual = 0.95, #Manual=5 - - //LOG.debug("523: {}", toString()); - } - - @Override @NonNull - public String toString() { - return new ToStringBuilder(this) - .append("bgAvg", bgAvg) - .append("bgLow", bgLow) - .append("bgHigh", bgHigh) - .append("bgCount", bgCount) - .append("sensorAvg", sensorAvg) - .append("sensorMin", sensorMin) - .append("sensorMax", sensorMax) - .append("sensorCalcCount", sensorCalcCount) - .append("sensorDataCount", sensorDataCount) - .append("insulinTotal", insulinTotal) - .append("insulinBasal", insulinBasal) - .append("insulinBolus", insulinBolus) - .append("insulinCarbs", insulinCarbs) - .append("bolusTotal", bolusTotal) - .append("bolusFood", bolusFood) - .append("bolusFoodAndCorr", bolusFoodAndCorr) - .append("bolusCorrection", bolusCorrection) - .append("bolusManual", bolusManual) - .append("bolusCount", bolusCount) - .append("bolusCountFoodOrCorr", bolusCountFoodOrCorr) - .append("bolusCountFoodAndCorr", bolusCountFoodAndCorr) - .append("bolusCountManual", bolusCountManual) - .append("bolusCountFood", bolusCountFood) - .append("bolusCountCorr", bolusCountCorr) - .append("entry", entry) - .toString(); - } - - public long timestamp() { - return DateTimeUtil.toMillisFromATD(this.entry.atechDateTime); - } - - public double insulinBasal() { - return insulinBasal; - } - - public double insulinBolus() { - return insulinBolus; - } - public double insulinTotal() { - return insulinTotal; - } - - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.kt new file mode 100644 index 0000000000..a295c76aff --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.kt @@ -0,0 +1,191 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +//import info.nightscout.androidaps.db.TDD +import com.google.gson.annotations.Expose +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType +import org.apache.commons.lang3.builder.ToStringBuilder +import java.util.* + +/** + * Created by andy on 11/3/18. + */ +/** + * NOTE: Decoding is only done for insulin part, everything else is pretty must left undecoded. + */ +class DailyTotalsDTO(var entry: PumpHistoryEntry) { + + // bg avg, bg low hi, number Bgs, + // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, + // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, + // Bolus=1.7, Fodd, Corr, Manual=1.7, + // Num bOlus=1, food/corr, Food+corr, manual bolus=1 + private val bgAvg: Double? = null + private val bgLow: Double? = null + private val bgHigh: Double? = null + private val bgCount: Int? = null + private val sensorAvg: Double? = null + private val sensorMin: Double? = null + private val sensorMax: Double? = null + private val sensorCalcCount: Int? = null + private val sensorDataCount: Int? = null + + @Expose + var insulinTotal = 0.0 + + @Expose + var insulinBasal: Double = 0.0 + + @Expose + var insulinBolus = 0.0 + private var insulinCarbs: Double? = null + private var bolusTotal: Double? = null + private var bolusFood: Double? = null + private var bolusFoodAndCorr: Double? = null + private var bolusCorrection: Double? = null + private var bolusManual: Double? = null + private var bolusCount: Int? = null + private var bolusCountFoodOrCorr: Int? = null + + // Integer bolusCountCorr; + var bolusCountFoodAndCorr: Int? = null + var bolusCountManual: Int? = null + private var bolusCountFood: Int? = null + private var bolusCountCorr: Int? = null + private fun setDisplayable() { + if (insulinBasal == 0.0) { + entry.displayableValue = "Total Insulin: " + StringUtil.getFormatedValueUS(insulinTotal, 2) + } else { + entry.displayableValue = ("Basal Insulin: " + StringUtil.getFormatedValueUS(insulinBasal, 2) + + ", Total Insulin: " + StringUtil.getFormatedValueUS(insulinTotal, 2)) + } + } + + private fun decodeEndResultsTotals(entry: PumpHistoryEntry) { + val totals = ByteUtil.toInt(entry.head[0].toInt(), entry.head[1].toInt(), entry.head[2].toInt(), + entry.head[3].toInt(), ByteUtil.BitConversion.BIG_ENDIAN) * 0.025 + insulinTotal = totals + entry.addDecodedData("Totals", totals) + } + + private fun testDecode(data: ByteArray) { + + // Daily + //System.out.println("Totals 522"); + for (i in 0 until data.size - 2) { + val j = ByteUtil.toInt(data[i], data[i + 1]) + val k: Int = ByteUtil.toInt(data[i], data[i + 1], data[i + 2]) + val j1 = ByteUtil.toInt(data[i + 1], data[i]) + val k1: Int = ByteUtil.toInt(data[i + 2], data[i + 1], data[i]) + println(String.format(Locale.ENGLISH, + "index: %d, number=%d, del/40=%.3f, del/10=%.3f, singular=%d, sing_hex=%s", i, j, j / 40.0, j / 10.0, + data[i], ByteUtil.shortHexString(data[i]))) + println(String.format(Locale.ENGLISH, " number[k,j1,k1]=%d / %d /%d, del/40=%.3f, del/40=%.3f, del/40=%.3f", + k, j1, k1, k / 40.0, j1 / 40.0, k1 / 40.0)) + } + } + + private fun decodeDailyTotals515(data: ByteArray) { + // LOG.debug("Can't decode DailyTotals515: Body={}", ByteUtil.getHex(data)); + insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0 + insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0 + insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0 + + // Delivery Stats: BG AVG: Bg Low/Hi=none,Number BGs=0 + // Delivery Stats: INSULIN: Basal 22.30, Bolus=4.20, Catbs = 0g (26.5) + // Delivery Stats: BOLUS: Food=0.00, Corr=0.00, Manual=4.20 + // Delivery Stats: NUM BOLUS: Food/Corr=0,Food+Corr=0, Manual=3 + + //LOG.debug("515: {}", toString()); + } + + private fun decodeDailyTotals522(data: ByteArray) { + insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0 + insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0 + insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0 + bolusTotal = ByteUtil.toInt(data[17], data[18], data[19]) / 40.0 + bolusFood = ByteUtil.toInt(data[21], data[22]) / 40.0 + bolusCorrection = ByteUtil.toInt(data[23], data[24], data[25]) / 40.0 + bolusManual = ByteUtil.toInt(data[26], data[27], data[28]) / 40.0 + bolusCount = ByteUtil.asUINT8(data[30]) + bolusCountFoodOrCorr = ByteUtil.asUINT8(data[31]) + bolusCountFoodAndCorr = ByteUtil.asUINT8(data[32]) + bolusCountManual = ByteUtil.asUINT8(data[33]) + + // bg avg, bg low hi, number Bgs, + // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, + // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, + // Bolus=1.7[18,19], Fodd, Corr, Manual=1.7[27,28], + // Num bOlus=1, food/corr, Food+corr, manual bolus=1 + + //LOG.debug("522: {}", toString()); + } + + private fun decodeDailyTotals523(data: ByteArray) { + insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0 + insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0 + insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0 + insulinCarbs = ByteUtil.toInt(data[16], data[17]) * 1.0 + bolusFood = ByteUtil.toInt(data[18], data[19]) / 40.0 + bolusCorrection = ByteUtil.toInt(data[20], data[21]) / 40.0 + bolusFoodAndCorr = ByteUtil.toInt(data[22], data[23]) / 40.0 + bolusManual = ByteUtil.toInt(data[24], data[25]) / 40.0 + bolusCountFood = ByteUtil.asUINT8(data[26]) + bolusCountCorr = ByteUtil.asUINT8(data[27]) + bolusCountFoodAndCorr = ByteUtil.asUINT8(data[28]) + bolusCountManual = ByteUtil.asUINT8(data[29]) // + + + // Delivery Stats: Carbs=11, Total Insulin=3.850, Basal=2.000 + // Delivery Stats: Basal 52,Bolus 1.850, Bolus=48%o + // Delivery Stats: Food only=0.9, Food only#=1, Corr only = 0.0 + // Delivery Stats: #Corr_only=0,Food+Corr=0.000, #Food+Corr=0 + // Delivery Stats: Manual = 0.95, #Manual=5 + + //LOG.debug("523: {}", toString()); + } + + override fun toString(): String { + return ToStringBuilder(this) + .append("bgAvg", bgAvg) + .append("bgLow", bgLow) + .append("bgHigh", bgHigh) + .append("bgCount", bgCount) + .append("sensorAvg", sensorAvg) + .append("sensorMin", sensorMin) + .append("sensorMax", sensorMax) + .append("sensorCalcCount", sensorCalcCount) + .append("sensorDataCount", sensorDataCount) + .append("insulinTotal", insulinTotal) + .append("insulinBasal", insulinBasal) + .append("insulinBolus", insulinBolus) + .append("insulinCarbs", insulinCarbs) + .append("bolusTotal", bolusTotal) + .append("bolusFood", bolusFood) + .append("bolusFoodAndCorr", bolusFoodAndCorr) + .append("bolusCorrection", bolusCorrection) + .append("bolusManual", bolusManual) + .append("bolusCount", bolusCount) + .append("bolusCountFoodOrCorr", bolusCountFoodOrCorr) + .append("bolusCountFoodAndCorr", bolusCountFoodAndCorr) + .append("bolusCountManual", bolusCountManual) + .append("bolusCountFood", bolusCountFood) + .append("bolusCountCorr", bolusCountCorr) + .append("entry", entry) + .toString() + } + + init { + when (entry.entryType) { + PumpHistoryEntryType.EndResultTotals -> decodeEndResultsTotals(entry) + PumpHistoryEntryType.DailyTotals515 -> decodeDailyTotals515(entry.body) + PumpHistoryEntryType.DailyTotals522 -> decodeDailyTotals522(entry.body) + PumpHistoryEntryType.DailyTotals523 -> decodeDailyTotals523(entry.body) + + else -> { + } + } + setDisplayable() + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java deleted file mode 100644 index 1868064d8b..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java +++ /dev/null @@ -1,29 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup; - -/** - * Created by andy on 6/6/18. - */ - -public class PumpSettingDTO { - - public String key; - public String value; - - PumpConfigurationGroup configurationGroup; - - - public PumpSettingDTO(String key, String value, PumpConfigurationGroup configurationGroup) { - this.key = key; - this.value = value; - this.configurationGroup = configurationGroup; - } - - - @Override - public String toString() { - return "PumpSettingDTO [key=" + key + ",value=" + value + ",group=" + configurationGroup.name() + "]"; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.kt new file mode 100644 index 0000000000..824151320e --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.kt @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup + +/** + * Created by andy on 6/6/18. + */ +class PumpSettingDTO(var key: String, var value: String, var configurationGroup: PumpConfigurationGroup) { + + override fun toString(): String { + return "PumpSettingDTO [key=" + key + ",value=" + value + ",group=" + configurationGroup.name + "]" + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java deleted file mode 100644 index 84ad0914b3..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java +++ /dev/null @@ -1,28 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; - -/** - * Created by andy on 6/2/18. - */ -public class PumpTimeStampedRecord { - - protected int decimalPrecission = 2; - public long atechDateTime; - - - public long getAtechDateTime() { - return this.atechDateTime; - } - - - public void setAtechDateTime(long atechDateTime) { - this.atechDateTime = atechDateTime; - } - - - public String getFormattedDecimal(double value) { - return StringUtil.getFormatedValueUS(value, this.decimalPrecission); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.kt new file mode 100644 index 0000000000..1f5cf7db0a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil + +/** + * Created by andy on 6/2/18. + */ +open class PumpTimeStampedRecord constructor(var atechDateTime: Long = 0) { + + var decimalPrecission = 2 + // var atechDateTime: Long = 0 + + open fun getFormattedDecimal(value: Double): String? { + return StringUtil.getFormatedValueUS(value, decimalPrecission) + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.java deleted file mode 100644 index b171f5a061..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.java +++ /dev/null @@ -1,28 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import org.joda.time.LocalDateTime; - -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -public class RLHistoryItemMedtronic extends RLHistoryItem { - - private final MedtronicCommandType medtronicCommandType; - - public RLHistoryItemMedtronic(MedtronicCommandType medtronicCommandType) { - super(new LocalDateTime(), RLHistoryItemSource.MedtronicCommand, RileyLinkTargetDevice.MedtronicPump); - this.medtronicCommandType = medtronicCommandType; - } - - @Override - public String getDescription(ResourceHelper resourceHelper) { - if (RLHistoryItemSource.MedtronicCommand.equals(source)) { - return medtronicCommandType.name(); - } - - return super.getDescription(resourceHelper); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.kt new file mode 100644 index 0000000000..4a99585508 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/RLHistoryItemMedtronic.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.utils.resources.ResourceHelper +import org.joda.time.LocalDateTime + +class RLHistoryItemMedtronic(private val medtronicCommandType: MedtronicCommandType) : + RLHistoryItem(LocalDateTime(), RLHistoryItemSource.MedtronicCommand, RileyLinkTargetDevice.MedtronicPump) { + + override fun getDescription(resourceHelper: ResourceHelper): String { + return if (RLHistoryItemSource.MedtronicCommand == source) { + medtronicCommandType.name + } else super.getDescription(resourceHelper) + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java deleted file mode 100644 index 418770d4b6..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java +++ /dev/null @@ -1,145 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -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.medtronic.util.MedtronicUtil; - -/** - * Created by geoff on 5/29/15. - *

- * Just need a class to keep the pair together, for parcel transport. - */ -public class TempBasalPair extends info.nightscout.androidaps.plugins.pump.common.defs.TempBasalPair { - - /** - * This constructor is for use with PumpHistoryDecoder - * - * @param rateByte - * @param startTimeByte - * @param isPercent - */ - public TempBasalPair(byte rateByte, int startTimeByte, boolean isPercent) { - super(); - int rateInt = ByteUtil.asUINT8(rateByte); - - if (isPercent) - this.setInsulinRate(rateByte); - else - this.setInsulinRate(rateInt * 0.025); - this.setDurationMinutes(startTimeByte * 30); - this.setPercent(isPercent); - } - - - /** - * This constructor is for use with PumpHistoryDecoder - * - * @param rateByte0 - * @param startTimeByte - * @param isPercent - */ - public TempBasalPair(byte rateByte0, byte rateByte1, int startTimeByte, boolean isPercent) { - if (isPercent) { - this.setInsulinRate(rateByte0); - } else { - this.setInsulinRate(ByteUtil.toInt(rateByte1, rateByte0) * 0.025); - } - this.setDurationMinutes(startTimeByte * 30); - this.setPercent(isPercent); - } - - public TempBasalPair(AAPSLogger aapsLogger, byte[] response) { - super(); - - aapsLogger.debug(LTag.PUMPBTCOMM, "Received TempBasal response: " + ByteUtil.getHex(response)); - - setPercent(response[0] == 1); - - if (isPercent()) { - setInsulinRate(response[1]); - } else { - int strokes = MedtronicUtil.makeUnsignedShort(response[2], response[3]); - - setInsulinRate(strokes / 40.0d); - } - - if (response.length < 6) { - setDurationMinutes(ByteUtil.asUINT8(response[4])); - } else { - setDurationMinutes(MedtronicUtil.makeUnsignedShort(response[4], response[5])); - } - - aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "TempBasalPair (with %d byte response): %s", response.length, toString())); - - } - - - public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) { - super(insulinRate, isPercent, durationMinutes); - } - - - public byte[] getAsRawData() { - - List list = new ArrayList<>(); - - list.add((byte) 5); - - byte[] insulinRate = MedtronicUtil.getBasalStrokes(this.getInsulinRate(), true); - byte timeMin = (byte) MedtronicUtil.getIntervalFromMinutes(getDurationMinutes()); - - // list.add((byte) 0); // ? - - // list.add((byte) 0); // is_absolute - - if (insulinRate.length == 1) - list.add((byte) 0x00); - else - list.add(insulinRate[0]); - - list.add(insulinRate[1]); - // list.add((byte) 0); // percent amount - - list.add(timeMin); // 3 (time) - OK - - if (insulinRate.length == 1) - list.add((byte) 0x00); - else - list.add(insulinRate[0]); - - list.add(insulinRate[1]); - - return MedtronicUtil.createByteArray(list); - } - - public boolean isCancelTBR() { - return (MedtronicUtil.isSame(getInsulinRate(), 0.0d) && getDurationMinutes() == 0); - } - - - public String getDescription() { - if (isCancelTBR()) { - return "Cancel TBR"; - } - - if (isPercent()) { - return String.format(Locale.ENGLISH, "Rate: %.0f%%, Duration: %d min", getInsulinRate(), getDurationMinutes()); - } else { - return String.format(Locale.ENGLISH, "Rate: %.3f U, Duration: %d min", getInsulinRate(), getDurationMinutes()); - } - } - - - @NonNull @Override - public String toString() { - return "TempBasalPair [" + "Rate=" + getInsulinRate() + ", DurationMinutes=" + getDurationMinutes() + ", IsPercent=" - + isPercent() + "]"; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.kt new file mode 100644 index 0000000000..1cd5f8dab7 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.kt @@ -0,0 +1,110 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.defs.TempBasalPair +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import java.util.* + +/** + * Created by geoff on 5/29/15. + * + * Just need a class to keep the pair together, for parcel transport. + */ +class TempBasalPair : TempBasalPair { + + /** + * This constructor is for use with PumpHistoryDecoder + * + * @param rateByte + * @param startTimeByte + * @param isPercent + */ + constructor(rateByte: Byte, startTimeByte: Int, isPercent: Boolean) : super() { + val rateInt = ByteUtil.asUINT8(rateByte) + if (isPercent) insulinRate = rateByte.toDouble() else insulinRate = rateInt * 0.025 + durationMinutes = startTimeByte * 30 + this.isPercent = isPercent + } + + /** + * This constructor is for use with PumpHistoryDecoder + * + * @param rateByte0 + * @param startTimeByte + * @param isPercent + */ + constructor(rateByte0: Byte, rateByte1: Byte, startTimeByte: Int, isPercent: Boolean) { + if (isPercent) { + insulinRate = rateByte0.toDouble() + } else { + insulinRate = ByteUtil.toInt(rateByte1.toInt(), rateByte0.toInt()) * 0.025 + } + durationMinutes = startTimeByte * 30 + this.isPercent = isPercent + } + + constructor(aapsLogger: AAPSLogger, response: ByteArray) : super() { + aapsLogger.debug(LTag.PUMPBTCOMM, "Received TempBasal response: " + ByteUtil.getHex(response)) + isPercent = response[0] == 1.toByte() + insulinRate = if (isPercent) { + response[1].toDouble() + } else { + val strokes = MedtronicUtil.makeUnsignedShort(response[2].toInt(), response[3].toInt()) + strokes / 40.0 + } + durationMinutes = if (response.size < 6) { + ByteUtil.asUINT8(response[4]) + } else { + MedtronicUtil.makeUnsignedShort(response[4].toInt(), response[5].toInt()) + } + aapsLogger.warn(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, "TempBasalPair (with %d byte response): %s", response.size, toString())) + } + + constructor(insulinRate: Double, isPercent: Boolean, durationMinutes: Int) : super(insulinRate, isPercent, durationMinutes) {} + + // list.add((byte) 0); // ? + + // list.add((byte) 0); // is_absolute + // list.add((byte) 0); // percent amount + // 3 (time) - OK + val asRawData: ByteArray + get() { + val list: MutableList = ArrayList() + list.add(5.toByte()) + val insulinRate = MedtronicUtil.getBasalStrokes(insulinRate, true) + val timeMin = MedtronicUtil.getIntervalFromMinutes(durationMinutes).toByte() + + // list.add((byte) 0); // ? + + // list.add((byte) 0); // is_absolute + if (insulinRate.size == 1) list.add(0x00.toByte()) else list.add(insulinRate[0]) + list.add(insulinRate[1]) + // list.add((byte) 0); // percent amount + list.add(timeMin) // 3 (time) - OK + if (insulinRate.size == 1) list.add(0x00.toByte()) else list.add(insulinRate[0]) + list.add(insulinRate[1]) + return MedtronicUtil.createByteArray(list) + } + + val isCancelTBR: Boolean + get() = MedtronicUtil.isSame(insulinRate, 0.0) && durationMinutes == 0 + + val description: String + get() { + if (isCancelTBR) { + return "Cancel TBR" + } + return if (isPercent) { + String.format(Locale.ENGLISH, "Rate: %.0f%%, Duration: %d min", insulinRate, durationMinutes) + } else { + String.format(Locale.ENGLISH, "Rate: %.3f U, Duration: %d min", insulinRate, durationMinutes) + } + } + + override fun toString(): String { + return ("TempBasalPair [" + "Rate=" + insulinRate + ", DurationMinutes=" + durationMinutes + ", IsPercent=" + + isPercent + "]") + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java deleted file mode 100644 index 86cba28184..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; - -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; - -public class TempBasalProcessDTO { - - public PumpHistoryEntry itemOne; - public PumpHistoryEntry itemTwo; - - public Operation processOperation = Operation.None; - - public int getDuration() { - if (itemTwo == null) { - TempBasalPair tbr = (TempBasalPair) itemOne.getDecodedDataEntry("Object"); - return tbr.getDurationMinutes(); - } else { - int difference = DateTimeUtil.getATechDateDiferenceAsMinutes(itemOne.atechDateTime, itemTwo.atechDateTime); - return difference; - } - } - - - public enum Operation { - None, - Add, - Edit - } - - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.kt new file mode 100644 index 0000000000..02ddb39f46 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.kt @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto + +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry + +class TempBasalProcessDTO constructor(var itemOne: PumpHistoryEntry, + var itemTwo: PumpHistoryEntry? = null, + var processOperation: Operation = Operation.None) { + var cancelPresent: Boolean = false + + val atechDateTime: Long + get() = itemOne.atechDateTime + + val pumpId: Long + get() = itemOne.pumpId + + val duration: Int + get() = if (itemTwo == null) { + val tbr = itemOne.getDecodedDataEntry("Object") as TempBasalPair + tbr.durationMinutes + } else { + DateTimeUtil.getATechDateDiferenceAsMinutes(itemOne.atechDateTime, itemTwo!!.atechDateTime) + } + + enum class Operation { + None, Add, Edit + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java deleted file mode 100644 index 0710e6ca4c..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -/** - * Created by andy on 1/20/19. - */ - -public enum BasalProfileStatus { - - NotInitialized, // - ProfileOK, // - ProfileChanged, // - ; - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.kt new file mode 100644 index 0000000000..5bce7a4d71 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +/** + * Created by andy on 1/20/19. + */ +enum class BasalProfileStatus { + + NotInitialized, // + ProfileOK, // + ProfileChanged + // +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java deleted file mode 100644 index 503074e640..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java +++ /dev/null @@ -1,34 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import androidx.annotation.StringRes; - -import java.util.HashMap; -import java.util.Map; - -import info.nightscout.androidaps.plugins.pump.medtronic.R; - - -/** - * Created by andy on 6/4/18. - */ - -public enum BatteryType { - - None(R.string.key_medtronic_pump_battery_no, 0, 0), - Alkaline(R.string.key_medtronic_pump_battery_alkaline, 1.20d, 1.47d), // - Lithium(R.string.key_medtronic_pump_battery_lithium, 1.22d, 1.64d), // - NiZn(R.string.key_medtronic_pump_battery_nizn, 1.40d, 1.70d), // - NiMH(R.string.key_medtronic_pump_battery_nimh, 1.10d, 1.40d) // - ; - - public final @StringRes int description; - public final double lowVoltage; - public final double highVoltage; - - - BatteryType(int resId, double lowVoltage, double highVoltage) { - this.description = resId; - this.lowVoltage = lowVoltage; - this.highVoltage = highVoltage; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.kt new file mode 100644 index 0000000000..a1c0aad63b --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import androidx.annotation.StringRes +import info.nightscout.androidaps.plugins.pump.medtronic.R + +/** + * Created by andy on 6/4/18. + */ +enum class BatteryType(@field:StringRes val description: Int, val lowVoltage: Double, val highVoltage: Double) { + + None(R.string.key_medtronic_pump_battery_no, 0.0, 0.0), + Alkaline(R.string.key_medtronic_pump_battery_alkaline, 1.20, 1.47), // + Lithium(R.string.key_medtronic_pump_battery_lithium, 1.22, 1.64), // + NiZn(R.string.key_medtronic_pump_battery_nizn, 1.40, 1.70), // + NiMH(R.string.key_medtronic_pump_battery_nimh, 1.10, 1.40 // + ); + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java deleted file mode 100644 index f061b860cf..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java +++ /dev/null @@ -1,33 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; - -/** - * Created by andy on 4/5/19. - */ - -public enum CommandValueDefinitionMDTType implements CommandValueDefinitionType { - GetModel, // - TuneUp, // - GetProfile, // - GetTBR, // - ; - - @Override - public String getName() { - return this.name(); - } - - - @Override - public String getDescription() { - return null; - } - - - @Override - public String commandAction() { - return null; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java deleted file mode 100755 index 0c14ac42f8..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java +++ /dev/null @@ -1,453 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.MessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpAckMessageBody; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.UnknownMessageBody; - -/** - * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) - *

- * Description: Medtronic Commands (Pump and CGMS) for all 512 and later models (just 5xx) - *

- * Link to original/unmodified file: - * https://sourceforge.net/p/ggc/code/HEAD/tree/trunk/ggc-plugins/ggc-plugins-base/src/ - * main/java/ggc/plugin/device/impl/minimed/enums/MinimedCommandType.java - *

- * A lot of stuff has been removed because it is not needed anymore (historical stuff from CareLink - * and Carelink USB communication. - *

- * Author: Andy {andy@atech-software.com} - */ -public enum MedtronicCommandType implements Serializable // , MinimedCommandTypeInterface -{ - InvalidCommand(0, "Invalid Command", null, null), // - - // Pump Responses (9) - CommandACK(0x06, "ACK - Acknowledge", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // - CommandNAK(0x15, "NAK - Not Acknowledged", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // - - // All (8) - PushAck(91, "Push ACK", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray(2)), // - - PushEsc(91, "Push Esc", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray(1)), // - - PushButton(0x5b, "Push Button", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // 91 - - RFPowerOn(93, "RF Power On", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray( - 1, 10)), // - - RFPowerOff(93, "RF Power Off", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray( - 0, 0)), // - - // SetSuspend(77, "Set Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All, - // MinimedCommandParameterType.FixedParameters, getByteArray(1)), // - - // CancelSuspend(77, "Cancel Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All, - // MinimedCommandParameterType.FixedParameters, getByteArray(0)), // - - PumpState(131, "Pump State", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // - - ReadPumpErrorStatus(117, "Pump Error Status", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // - - // 511 (InitCommand = 2, Config 7, Data = 1(+3) -// DetectBolus(75, "Detect Bolus", MedtronicDeviceType.Medtronic_511, MinimedCommandParameterType.FixedParameters, getByteArray( -// 0, 0, 0)), // - - // RemoteControlIds(118, "Remote Control Ids", MinimedTargetType.PumpConfiguration_NA, MedtronicDeviceType.All, - // MinimedCommandParameterType.NoParameters), // - - // FirmwareVersion(116, "Firmware Version", MinimedTargetType.InitCommand, MedtronicDeviceType.All, - // MinimedCommandParameterType.NoParameters), // - - // PumpId(113, "Pump Id", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.All, - // MinimedCommandParameterType.NoParameters), // init - - SetRealTimeClock(0x40, "Set Pump Time", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // - 0), // - - GetRealTimeClock(112, "Get Pump Time", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // - 7, R.string.medtronic_cmd_desc_get_time), // 0x70 - - GetBatteryStatus(0x72, "Get Battery Status", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // - // GetBattery((byte) 0x72), // - - GetRemainingInsulin(0x73, "Read Remaining Insulin", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, 2), // 115 - - SetBolus(0x42, "Set Bolus", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // - 0, R.string.medtronic_cmd_desc_set_bolus), // 66 - - // 512 - ReadTemporaryBasal(0x98, "Read Temporary Basal", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 5, R.string.medtronic_cmd_desc_get_tbr), // 152 - - SetTemporaryBasal(76, "Set Temporay Basal", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 0, R.string.medtronic_cmd_desc_set_tbr), - - // 512 Config - PumpModel(141, "Pump Model", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 5, R.string.medtronic_cmd_desc_get_model), // 0x8D - - // BGTargets_512(140, "BG Targets", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512_712, - // MinimedCommandParameterType.NoParameters), // - - // BGUnits(137, "BG Units", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, - // MinimedCommandParameterType.NoParameters), // - - // Language(134, "Language", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, - // MinimedCommandParameterType.NoParameters), // - - Settings_512(145, "Configuration", MedtronicDeviceType.Medtronic_512_712, MinimedCommandParameterType.NoParameters, // - 64, 1, 18, R.string.medtronic_cmd_desc_get_settings), // - - // BGAlarmClocks(142, "BG Alarm Clocks", MinimedTargetType.PumpConfiguration, - // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // - - // BGAlarmEnable(151, "BG Alarm Enable", MinimedTargetType.PumpConfiguration, - // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // - - // BGReminderEnable(144, "BG Reminder Enable", MinimedTargetType.PumpConfiguration, - // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // - - // ReadInsulinSensitivities(0x8b, "Read Insulin Sensitivities", MinimedTargetType.PumpConfiguration, - // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // 139 - - // 512 Data - GetHistoryData(128, "Get History", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.SubCommands, // - 1024, 16, 1024, R.string.medtronic_cmd_desc_get_history), // 0x80 - - GetBasalProfileSTD(146, "Get Profile Standard", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), // 146 - - GetBasalProfileA(147, "Get Profile A", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), - - GetBasalProfileB(148, "Get Profile B", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), // 148 - - SetBasalProfileSTD(0x6f, "Set Profile Standard", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 111 - - SetBasalProfileA(0x30, "Set Profile A", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 48 - - SetBasalProfileB(0x31, "Set Profile B", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // - 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 49 - - // 515 - PumpStatus(206, "Pump Status", MedtronicDeviceType.Medtronic_515andHigher, MinimedCommandParameterType.NoParameters), // PumpConfiguration - - Settings(192, "Configuration", MedtronicDeviceType.Medtronic_515andHigher, MinimedCommandParameterType.NoParameters, // - 64, 1, 21, R.string.medtronic_cmd_desc_get_settings), // - - // 522 - SensorSettings_522(153, "Sensor Configuration", MedtronicDeviceType.Medtronic_522andHigher, MinimedCommandParameterType.NoParameters), // - - GlucoseHistory(154, "Glucose History", MedtronicDeviceType.Medtronic_522andHigher, MinimedCommandParameterType.SubCommands, 1024, 32, 0, null), // - - // 523 - SensorSettings(207, "Sensor Configuration", MedtronicDeviceType.Medtronic_523andHigher, MinimedCommandParameterType.NoParameters), // - - // 553 - // 554 - - // var MESSAGES = { - // READ_TIME : 0x70, - // READ_BATTERY_STATUS: 0x72, - // READ_HISTORY : 0x80, - // READ_CARB_RATIOS : 0x8A, - // READ_INSULIN_SENSITIVITIES: 0x8B, - // READ_MODEL : 0x8D, - // READ_PROFILE_STD : 0x92, - // READ_PROFILE_A : 0x93, - // READ_PROFILE_B : 0x94, - // READ_CBG_HISTORY: 0x9A, - // READ_ISIG_HISTORY: 0x9B, - // READ_CURRENT_PAGE : 0x9D, - // READ_BG_TARGETS : 0x9F, - // READ_SETTINGS : 0xC0, 192 - // READ_CURRENT_CBG_PAGE : 0xCD - // }; - - // Fake Commands - - CancelTBR(), - ; - - static Map mapByCode; - - static { - MedtronicCommandType.RFPowerOn.maxAllowedTime = 17000; - MedtronicCommandType.RFPowerOn.allowedRetries = 0; - MedtronicCommandType.RFPowerOn.recordLength = 0; - MedtronicCommandType.RFPowerOn.minimalBufferSizeToStartReading = 1; - - mapByCode = new HashMap<>(); - - for (MedtronicCommandType medtronicCommandType : values()) { - mapByCode.put(medtronicCommandType.getCommandCode(), medtronicCommandType); - } - } - - public byte commandCode = 0; - public String commandDescription = ""; - public byte[] commandParameters = null; - public int commandParametersCount = 0; - public int maxRecords = 1; - private Integer resourceId; - public int command_type = 0; - public int allowedRetries = 2; - public int maxAllowedTime = 2000; - public MinimedCommandParameterType parameterType; - public int minimalBufferSizeToStartReading = 14; - public int expectedLength = 0; - //MinimedTargetType targetType; - MedtronicDeviceType devices; - private int recordLength = 64; - - - MedtronicCommandType() { - // this is for "fake" commands needed by AAPS MedtronicUITask - } - - - // MedtronicCommandType(int code, String description, MedtronicDeviceType devices, -// MinimedCommandParameterType parameterType) { -// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, 0); -// } -// -// -// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, -// MinimedCommandParameterType parameterType, int expectedLength) { -// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, expectedLength); -// } -// -// -// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, -// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) { -// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType, 0); -// } -// -// -// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, -// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType, -// int expectedLength) { -// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType, -// expectedLength); -// } -// -// - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, - MinimedCommandParameterType parameterType, byte[] cmd_params) { - this(code, description, devices, parameterType, 0, 1, 0, 0, 11, 0); - - this.commandParameters = cmd_params; - this.commandParametersCount = cmd_params.length; - } - - - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // - MinimedCommandParameterType parameterType) { - - this(code, description, devices, parameterType, 64, 1, 0, null); - } - - - // NEW - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, - MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) { - this(code, description, devices, parameterType, recordLength, maxRecords, 0, null); - } - - - // NEW - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // - MinimedCommandParameterType parameterType, int expectedLength) { - this(code, description, devices, parameterType, 64, 1, expectedLength, null); - } - - - // NEW - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // - MinimedCommandParameterType parameterType, int expectedLength, int resourceId) { - this(code, description, devices, parameterType, 64, 1, expectedLength, resourceId); - } - - - // NEW - MedtronicCommandType(int code, String description, - MedtronicDeviceType devices, // - MinimedCommandParameterType parameterType, int recordLength, int max_recs, int expectedLength, - Integer resourceId) { - this.commandCode = (byte) code; - this.commandDescription = description; - this.devices = devices; - this.recordLength = recordLength; - this.maxRecords = max_recs; - this.resourceId = resourceId; - - this.commandParametersCount = 0; - this.allowedRetries = 2; - this.parameterType = parameterType; - this.expectedLength = expectedLength; - - if (this.parameterType == MinimedCommandParameterType.SubCommands) { - this.minimalBufferSizeToStartReading = 200; - } - } - - - @Deprecated - MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // - MinimedCommandParameterType parameterType, int recordLength, int max_recs, int addy, // - int addy_len, int cmd_type, int expectedLength) { - this.commandCode = (byte) code; - this.commandDescription = description; - //this.targetType = targetType; - this.devices = devices; - this.recordLength = recordLength; - this.maxRecords = max_recs; - - this.command_type = cmd_type; - this.commandParametersCount = 0; - this.allowedRetries = 2; - this.parameterType = parameterType; - this.expectedLength = expectedLength; - - if (this.parameterType == MinimedCommandParameterType.SubCommands) { - this.minimalBufferSizeToStartReading = 200; - } - - } - - - private static HashMap getDeviceTypesArray(MedtronicDeviceType... types) { - HashMap hashMap = new HashMap(); - - for (MedtronicDeviceType type : types) { - hashMap.put(type, null); - } - - return hashMap; - } - - - private static byte[] getByteArray(int... data) { - byte[] array = new byte[data.length]; - - for (int i = 0; i < data.length; i++) { - array[i] = (byte) data[i]; - } - - return array; - } - - - private static int[] getIntArray(int... data) { - return data; - } - - - public static MedtronicCommandType getByCode(byte code) { - if (mapByCode.containsKey(code)) { - return mapByCode.get(code); - } else { - return MedtronicCommandType.InvalidCommand; - } - } - - - public static MessageBody constructMessageBody(MedtronicCommandType messageType, byte[] bodyData) { - switch (messageType) { - case CommandACK: - return new PumpAckMessageBody(bodyData); - default: - return new UnknownMessageBody(bodyData); - } - } - - - public static MedtronicCommandType getSettings(MedtronicDeviceType medtronicPumpModel) { - if (MedtronicDeviceType.isSameDevice(medtronicPumpModel, MedtronicDeviceType.Medtronic_512_712)) - return MedtronicCommandType.Settings_512; - else - return MedtronicCommandType.Settings; - } - - - /** - * Get Full Command Description - * - * @return command description - */ - public String getFullCommandDescription() { - return "Command [name=" + this.name() + ", id=" + this.commandCode + ",description=" + this.commandDescription - + "] "; - } - - - public boolean canReturnData() { - System.out.println("CanReturnData: ]id=" + this.name() + "max=" + this.maxRecords + "recLen=" + recordLength); - return (this.maxRecords * this.recordLength) > 0; - } - - - public int getRecordLength() { - return recordLength; - } - - - public int getMaxRecords() { - return maxRecords; - } - - - public byte getCommandCode() { - return commandCode; - } - - - public int getCommandParametersCount() { - if (this.commandParameters == null) { - return 0; - } else { - return this.commandParameters.length; - } - } - - - public byte[] getCommandParameters() { - return commandParameters; - } - - - public boolean hasCommandParameters() { - return (getCommandParametersCount() > 0); - } - - - public String toString() { - return name(); - } - - - public String getCommandDescription() { - return this.commandDescription; - } - - - public Integer getResourceId() { - return resourceId; - } - - public enum MinimedCommandParameterType { - NoParameters, // - FixedParameters, // - SubCommands // - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.kt new file mode 100755 index 0000000000..35c8ec3a7b --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.kt @@ -0,0 +1,192 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import info.nightscout.androidaps.plugins.pump.medtronic.R +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.MessageBody +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpAckMessageBody +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.UnknownMessageBody +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType.Companion.isSameDevice +import java.util.* + +/** + * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) + * + * + * Description: Medtronic Commands (Pump and CGMS) for all 512 and later models (just 5xx) + * + * + * Link to original/unmodified file: + * https://sourceforge.net/p/ggc/code/HEAD/tree/trunk/ggc-plugins/ggc-plugins-base/src/ + * main/java/ggc/plugin/device/impl/minimed/enums/MinimedCommandType.java + * + * + * A lot of stuff has been removed because it is not needed anymore (historical stuff from CareLink + * and Carelink USB communication. + * + * + * Author: Andy {andy@atech-software.com} + */ +enum class MedtronicCommandType( + code: Int, + description: String, + var devices: MedtronicDeviceType = MedtronicDeviceType.All, + var parameterType: MinimedCommandParameterType = MinimedCommandParameterType.NoParameters, + var recordLength: Int = 64, + var maxRecords: Int = 1, + var expectedLength: Int = 0, + var resourceId: Int? = null, + var commandParameters: ByteArray? = null) { + + InvalidCommand(code = 0, description = "Invalid Command"), // + + // Pump Responses (9) + CommandACK(code = 0x06, description = "ACK - Acknowledge"), // + CommandNAK(code = 0x15, description = "NAK - Not Acknowledged"), // + + // All (8) + PushAck(code = 91, description = "Push ACK", parameterType = MinimedCommandParameterType.FixedParameters, commandParameters = byteArrayOf(2)), // + PushEsc(code = 91, description = "Push Esc", parameterType = MinimedCommandParameterType.FixedParameters, commandParameters = byteArrayOf(1)), // + PushButton(code = 0x5b, description = "Push Button"), // 91 + RFPowerOn(code = 93, description = "RF Power On", parameterType = MinimedCommandParameterType.FixedParameters, commandParameters = byteArrayOf(1, 10)), // + RFPowerOff(code = 93, description = "RF Power Off", parameterType = MinimedCommandParameterType.FixedParameters, commandParameters = byteArrayOf(0, 0)), // + + // SetSuspend(77, "Set Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray(1)), // + // CancelSuspend(77, "Cancel Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All,MinimedCommandParameterType.FixedParameters, getByteArray(0)), // + PumpState(code = 131, description = "Pump State"), // + ReadPumpErrorStatus(code = 117, description = "Pump Error Status"), // + + // 511 (InitCommand = 2, Config 7, Data = 1(+3) + // DetectBolus(75, "Detect Bolus", MedtronicDeviceType.Medtronic_511, MinimedCommandParameterType.FixedParameters, getByteArray( + // 0, 0, 0)), // + // RemoteControlIds(118, "Remote Control Ids", MinimedTargetType.PumpConfiguration_NA, MedtronicDeviceType.All,MinimedCommandParameterType.NoParameters), // + // FirmwareVersion(116, "Firmware Version", MinimedTargetType.InitCommand, MedtronicDeviceType.All,MinimedCommandParameterType.NoParameters), // + // PumpId(113, "Pump Id", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.All,MinimedCommandParameterType.NoParameters), // init + SetRealTimeClock(code = 0x40, description = "Set Pump Time", recordLength = 0, resourceId = R.string.medtronic_cmd_desc_set_time), // + GetRealTimeClock(112, description = "Get Pump Time", recordLength = 7, resourceId = R.string.medtronic_cmd_desc_get_time), // 0x70 + GetBatteryStatus(code = 0x72, description = "Get Battery Status", recordLength = 0, resourceId = R.string.medtronic_cmd_desc_get_battery_status), // + GetRemainingInsulin(code = 0x73, description = "Read Remaining Insulin", + recordLength = 2, resourceId = R.string.medtronic_cmd_desc_get_remaining_insulin), // 115 + SetBolus(code = 0x42, description = "Set Bolus", recordLength = 0, resourceId = R.string.medtronic_cmd_desc_set_bolus), // 66 + + // 512 + ReadTemporaryBasal(code = 0x98, description = "Read Temporary Basal", devices = MedtronicDeviceType.Medtronic_512andHigher, // + recordLength = 5, resourceId = R.string.medtronic_cmd_desc_get_tbr), // 152 + SetTemporaryBasal(code = 76, description = "Set Temporay Basal", devices = MedtronicDeviceType.Medtronic_512andHigher, // + recordLength = 0, resourceId = R.string.medtronic_cmd_desc_set_tbr), // 512 Config + PumpModel(code = 141, description = "Pump Model", devices = MedtronicDeviceType.Medtronic_512andHigher, // + recordLength = 5, resourceId = R.string.medtronic_cmd_desc_get_model), // 0x8D + + // BGTargets_512(140, "BG Targets", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512_712, + // MinimedCommandParameterType.NoParameters), // + // BGUnits(137, "BG Units", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, + // MinimedCommandParameterType.NoParameters), // + // Language(134, "Language", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, + // MinimedCommandParameterType.NoParameters), // + Settings_512(code = 145, description = "Configuration", devices = MedtronicDeviceType.Medtronic_512_712, + expectedLength = 18, resourceId = R.string.medtronic_cmd_desc_get_settings), // + + // 512 Data + GetHistoryData(code = 128, description = "Get History", devices = MedtronicDeviceType.Medtronic_512andHigher, + parameterType = MinimedCommandParameterType.SubCommands, recordLength = 1024, maxRecords = 16, + expectedLength = 1024, resourceId = R.string.medtronic_cmd_desc_get_history), // 0x80 + GetBasalProfileSTD(code = 146, description = "Get Profile Standard", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_get_basal_profile), // 146 + GetBasalProfileA(code = 147, description = "Get Profile A", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_get_basal_profile), + GetBasalProfileB(code = 148, description = "Get Profile B", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_get_basal_profile), // 148 + SetBasalProfileSTD(code = 0x6f, description = "Set Profile Standard", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_set_basal_profile), // 111 + SetBasalProfileA(code = 0x30, description = "Set Profile A", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_set_basal_profile), // 48 + SetBasalProfileB(code = 0x31, description = "Set Profile B", devices = MedtronicDeviceType.Medtronic_512andHigher, // + maxRecords = 3, expectedLength = 192, resourceId = R.string.medtronic_cmd_desc_set_basal_profile), // 49 + + // 515 + PumpStatus(code = 206, description = "Pump Status", devices = MedtronicDeviceType.Medtronic_515andHigher), // PumpConfiguration + Settings(code = 192, description = "Configuration", devices = MedtronicDeviceType.Medtronic_515andHigher, + maxRecords = 1, expectedLength = 21, resourceId = R.string.medtronic_cmd_desc_get_settings), // + + // 522 + SensorSettings_522(code = 153, description = "Sensor Configuration", devices = MedtronicDeviceType.Medtronic_522andHigher), // + GlucoseHistory(code = 154, description = "Glucose History", devices = MedtronicDeviceType.Medtronic_522andHigher, + MinimedCommandParameterType.SubCommands, recordLength = 1024, maxRecords = 32, expectedLength = 0), // + + // 523 + SensorSettings(code = 207, description = "Sensor Configuration", devices = MedtronicDeviceType.Medtronic_523andHigher), // + + // 553 + // 554 + // var MESSAGES = { + // READ_CARB_RATIOS : 0x8A, + // READ_INSULIN_SENSITIVITIES: 0x8B, + // READ_CBG_HISTORY: 0x9A, + // READ_ISIG_HISTORY: 0x9B, + // READ_CURRENT_PAGE : 0x9D, + // READ_BG_TARGETS : 0x9F, + // READ_SETTINGS : 0xC0, 192 + // READ_CURRENT_CBG_PAGE : 0xCD + // }; + // Fake Commands + CancelTBR(code = 250, description = "Cancel TBR", resourceId = R.string.medtronic_cmd_desc_cancel_tbr); + + companion object { + + var mapByCode: MutableMap = HashMap() + + fun getByCode(code: Byte): MedtronicCommandType? { + return if (mapByCode.containsKey(code)) { + mapByCode[code] + } else { + InvalidCommand + } + } + + fun constructMessageBody(messageType: MedtronicCommandType?, bodyData: ByteArray): MessageBody { + return when (messageType) { + CommandACK -> PumpAckMessageBody(bodyData) + else -> UnknownMessageBody(bodyData) + } + } + + @JvmStatic + fun getSettings(medtronicPumpModel: MedtronicDeviceType): MedtronicCommandType { + return if (isSameDevice(medtronicPumpModel, MedtronicDeviceType.Medtronic_512_712)) + Settings_512 + else + Settings + } + + init { + for (medtronicCommandType in values()) { + mapByCode[medtronicCommandType.commandCode] = medtronicCommandType + } + } + } + + var commandCode: Byte = 0 + var commandDescription = description + var commandParametersCount = 0 + var allowedRetries = 2 + + //var maxAllowedTime = 2000 + var minimalBufferSizeToStartReading = 14 + + init { + commandCode = code.toByte() + this.commandParametersCount = commandParameters?.size ?: 0 + allowedRetries = 2 + if (this.parameterType == MinimedCommandParameterType.SubCommands) { + minimalBufferSizeToStartReading = 200 + } + } + + override fun toString(): String { + return name + } + + enum class MinimedCommandParameterType { + NoParameters, // + FixedParameters, // + SubCommands // + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java deleted file mode 100644 index af0be33cfa..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java +++ /dev/null @@ -1,20 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; - -/** - * Created by andy on 11/3/18. - */ - -public enum MedtronicCustomActionType implements CustomActionType { - - WakeUpAndTune(), // - ClearBolusBlock(), // - ResetRileyLinkConfiguration(), // - ; - - @Override - public String getKey() { - return this.name(); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.kt new file mode 100644 index 0000000000..80713a7fa5 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType + +/** + * Created by andy on 11/3/18. + */ +enum class MedtronicCustomActionType : CustomActionType { + + WakeUpAndTune, // + ClearBolusBlock, // + ResetRileyLinkConfiguration; + + // + + override fun getKey(): String { + return name + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java deleted file mode 100644 index 6623a6f9d8..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java +++ /dev/null @@ -1,140 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import java.util.HashMap; -import java.util.Map; - -/** - * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) - *

- * Author: Andy {andy@atech-software.com} - */ - -public enum MedtronicDeviceType { - Unknown_Device, // - - // Pump - Medtronic_511("511"), // - - Medtronic_512("512"), // - Medtronic_712("712"), // - Medtronic_512_712(Medtronic_512, Medtronic_712), // - - Medtronic_515("515"), // - Medtronic_715("715"), // - Medtronic_515_715(Medtronic_515, Medtronic_715), // - - Medtronic_522("522"), // - Medtronic_722("722"), // - Medtronic_522_722(Medtronic_522, Medtronic_722), // - - Medtronic_523_Revel("523"), // - Medtronic_723_Revel("723"), // - - Medtronic_554_Veo("554"), // - Medtronic_754_Veo("754"), // - - Medtronic_512andHigher(Medtronic_512, Medtronic_712, Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, // - Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo), // - - Medtronic_515andHigher(Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, // - Medtronic_554_Veo, Medtronic_754_Veo), // - Medtronic_522andHigher(Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, // - Medtronic_554_Veo, Medtronic_754_Veo), // - Medtronic_523andHigher(Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, // - Medtronic_754_Veo), // - - Medtronic_554andHigher(Medtronic_554_Veo, Medtronic_754_Veo), // - - - // - All; - - static Map mapByDescription; - - static { - - mapByDescription = new HashMap<>(); - - for (MedtronicDeviceType minimedDeviceType : values()) { - - if (!minimedDeviceType.isFamily) { - mapByDescription.put(minimedDeviceType.pumpModel, minimedDeviceType); - } - } - - } - - private String pumpModel; - - private final boolean isFamily; - private MedtronicDeviceType[] familyMembers = null; - - - MedtronicDeviceType(String pumpModel) { - this.isFamily = false; - this.pumpModel = pumpModel; - } - - - MedtronicDeviceType(MedtronicDeviceType... familyMembers) { - this.familyMembers = familyMembers; - this.isFamily = true; - } - - - public static boolean isSameDevice(MedtronicDeviceType deviceWeCheck, MedtronicDeviceType deviceSources) { - if (deviceSources.isFamily) { - for (MedtronicDeviceType mdt : deviceSources.familyMembers) { - if (mdt == deviceWeCheck) - return true; - } - } else { - return (deviceWeCheck == deviceSources); - } - - return false; - } - - - public static MedtronicDeviceType getByDescription(String desc) { - if (mapByDescription.containsKey(desc)) { - return mapByDescription.get(desc); - } else { - return MedtronicDeviceType.Unknown_Device; - } - } - - -// public static boolean isLargerFormat(MedtronicDeviceType model) { -// return isSameDevice(model, Medtronic_523andHigher); -// } - - - public boolean isFamily() { - return isFamily; - } - - - public MedtronicDeviceType[] getFamilyMembers() { - return familyMembers; - } - - -// public boolean isLargerFormat() { -// return isSameDevice(this, Medtronic_523andHigher); -// } - - public boolean isMedtronic_523orHigher() { - return isSameDevice(this, Medtronic_523andHigher); - } - - - public int getBolusStrokes() { - return (isMedtronic_523orHigher()) ? 40 : 10; - } - - - public String getPumpModel() { - return pumpModel; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.kt new file mode 100644 index 0000000000..6f8be29bd6 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.kt @@ -0,0 +1,101 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import java.util.* + +/** + * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) + * + * + * Author: Andy {andy@atech-software.com} + */ +enum class MedtronicDeviceType { + + Unknown_Device, // + + // Pump + Medtronic_511("511"), // + Medtronic_512("512"), // + Medtronic_712("712"), // + Medtronic_512_712(listOf(Medtronic_512, Medtronic_712)), // + Medtronic_515("515"), // + Medtronic_715("715"), // + Medtronic_515_715(listOf(Medtronic_515, Medtronic_715)), // + Medtronic_522("522"), // + Medtronic_722("722"), // + Medtronic_522_722(listOf(Medtronic_522, Medtronic_722)), // + Medtronic_523_Revel("523"), // + Medtronic_723_Revel("723"), // + Medtronic_554_Veo("554"), // + Medtronic_754_Veo("754"), // + Medtronic_512andHigher(listOf(Medtronic_512, Medtronic_712, Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo)), // + Medtronic_515andHigher(listOf(Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo)), // + Medtronic_522andHigher(listOf(Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo)), // + Medtronic_523andHigher(listOf(Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo)), // + Medtronic_554andHigher(listOf(Medtronic_554_Veo, Medtronic_754_Veo)), // + + // + All; + + companion object { + var mapByDescription: MutableMap = mutableMapOf() + + @JvmStatic + fun isSameDevice(deviceWeCheck: MedtronicDeviceType, deviceSources: MedtronicDeviceType): Boolean { + if (deviceSources.isFamily) { + for (mdt in deviceSources.familyMembers!!) { + if (mdt == deviceWeCheck) return true + } + } else { + return deviceWeCheck == deviceSources + } + return false + } + + fun getByDescription(desc: String): MedtronicDeviceType { + return if (mapByDescription.containsKey(desc)) { + mapByDescription[desc]!! + } else { + Unknown_Device + } + } + + init { + for (minimedDeviceType in values()) { + if (!minimedDeviceType.isFamily && minimedDeviceType.pumpModel!=null) { + mapByDescription[minimedDeviceType.pumpModel!!] = minimedDeviceType + } + } + } + } + + var pumpModel: String? = null + private set + + // public static boolean isLargerFormat(MedtronicDeviceType model) { + // return isSameDevice(model, Medtronic_523andHigher); + // } + val isFamily: Boolean + var familyMembers: List? = null + private set + + constructor() { + isFamily = false + } + + constructor(pumpModel: String?) { + isFamily = false + this.pumpModel = pumpModel + } + + constructor(familyMembers: List) { + this.familyMembers = familyMembers + isFamily = true + } + + val isMedtronic_523orHigher: Boolean + get() = isSameDevice(this, Medtronic_523andHigher) + + val bolusStrokes: Int + get() = if (isMedtronic_523orHigher) 40 else 10 + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java deleted file mode 100644 index b05fce81fc..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java +++ /dev/null @@ -1,65 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.pump.medtronic.R; - -/** - * Created by andy on 10/15/18. - */ - -public enum MedtronicNotificationType { - - PumpUnreachable(Notification.RILEYLINK_CONNECTION, R.string.medtronic_pump_status_pump_unreachable, Notification.NORMAL), // - PumpTypeNotSame(R.string.medtronic_error_pump_type_set_differs_from_detected, Notification.NORMAL), // - PumpBasalProfilesNotEnabled(R.string.medtronic_error_pump_basal_profiles_not_enabled, Notification.URGENT), // - PumpIncorrectBasalProfileSelected(R.string.medtronic_error_pump_incorrect_basal_profile_selected, Notification.URGENT), // - PumpWrongTBRTypeSet(R.string.medtronic_error_pump_wrong_tbr_type_set, Notification.URGENT), // - PumpWrongMaxBolusSet(R.string.medtronic_error_pump_wrong_max_bolus_set, Notification.NORMAL), // - PumpWrongMaxBasalSet(R.string.medtronic_error_pump_wrong_max_basal_set, Notification.NORMAL), // - PumpWrongTimeUrgent(R.string.medtronic_notification_check_time_date, Notification.URGENT), - PumpWrongTimeNormal(R.string.medtronic_notification_check_time_date, Notification.NORMAL), - TimeChangeOver24h(Notification.OVER_24H_TIME_CHANGE_REQUESTED, R.string.medtronic_error_pump_24h_time_change_requested, Notification.URGENT), - // - ; - - private int notificationType; - private final int resourceId; - private final int notificationUrgency; - - - MedtronicNotificationType(int resourceId, int notificationUrgency) { - this(Notification.MEDTRONIC_PUMP_ALARM, resourceId, notificationUrgency); - } - - - MedtronicNotificationType(int notificationType, int resourceId, int notificationUrgency) { - this.notificationType = notificationType; - this.resourceId = resourceId; - this.notificationUrgency = notificationUrgency; - } - - - public int getNotificationType() { - return notificationType; - } - - - public void setNotificationType(int notificationType) { - this.notificationType = notificationType; - } - - - public int getResourceId() { - - return resourceId; - } - - - public int getNotificationUrgency() { - - return notificationUrgency; - } - - // Notification.MEDTRONIC_PUMP_ALARM R.string.medtronic_pump_status_pump_unreachable, Notification.NORMAL - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.kt new file mode 100644 index 0000000000..aad989679d --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.kt @@ -0,0 +1,23 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.pump.medtronic.R + +/** + * Created by andy on 10/15/18. + */ +enum class MedtronicNotificationType(var notificationType: Int, + val resourceId: Int, + val notificationUrgency: Int) { + + PumpUnreachable(Notification.RILEYLINK_CONNECTION, R.string.medtronic_pump_status_pump_unreachable, Notification.NORMAL), // + PumpTypeNotSame(R.string.medtronic_error_pump_type_set_differs_from_detected, Notification.NORMAL), // + PumpBasalProfilesNotEnabled(R.string.medtronic_error_pump_basal_profiles_not_enabled, Notification.URGENT), // + PumpIncorrectBasalProfileSelected(R.string.medtronic_error_pump_incorrect_basal_profile_selected, Notification.URGENT), // + PumpWrongTBRTypeSet(R.string.medtronic_error_pump_wrong_tbr_type_set, Notification.URGENT), // + PumpWrongMaxBolusSet(R.string.medtronic_error_pump_wrong_max_bolus_set, Notification.NORMAL), // + PumpWrongMaxBasalSet(R.string.medtronic_error_pump_wrong_max_basal_set, Notification.NORMAL), // + PumpWrongTimeUrgent(R.string.medtronic_notification_check_time_date, Notification.URGENT), PumpWrongTimeNormal(R.string.medtronic_notification_check_time_date, Notification.NORMAL), TimeChangeOver24h(Notification.OVER_24H_TIME_CHANGE_REQUESTED, R.string.medtronic_error_pump_24h_time_change_requested, Notification.URGENT); + + constructor(resourceId: Int, notificationUrgency: Int) : this(Notification.MEDTRONIC_PUMP_ALARM, resourceId, notificationUrgency) {} +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java deleted file mode 100644 index f84f6d3678..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java +++ /dev/null @@ -1,37 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -/** - * Created by andy on 6/28/18. - */ - -public enum MedtronicStatusRefreshType { - - PumpHistory(5, null), // - Configuration(0, null), // - RemainingInsulin(-1, MedtronicCommandType.GetRemainingInsulin), // - BatteryStatus(55, MedtronicCommandType.GetBatteryStatus), // - PumpTime(60, MedtronicCommandType.GetRealTimeClock) // - ; - - private final int refreshTime; - private final MedtronicCommandType commandType; - - - MedtronicStatusRefreshType(int refreshTime, MedtronicCommandType commandType) { - this.refreshTime = refreshTime; - this.commandType = commandType; - } - - - public int getRefreshTime() { - return refreshTime; - } - - - public MedtronicCommandType getCommandType(MedtronicDeviceType medtronicDeviceType) { - if (this == Configuration) { - return MedtronicCommandType.getSettings(medtronicDeviceType); - } else - return commandType; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.kt new file mode 100644 index 0000000000..3185212739 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.kt @@ -0,0 +1,23 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +/** + * Created by andy on 6/28/18. + */ +enum class MedtronicStatusRefreshType(val refreshTime: Int, + private val commandType: MedtronicCommandType?) { + + PumpHistory(5, null), // + Configuration(0, null), // + RemainingInsulin(-1, MedtronicCommandType.GetRemainingInsulin), // + BatteryStatus(55, MedtronicCommandType.GetBatteryStatus), // + PumpTime(60, MedtronicCommandType.GetRealTimeClock // + ); + + fun getCommandType(medtronicDeviceType: MedtronicDeviceType): MedtronicCommandType? { + return if (this == Configuration) { + MedtronicCommandType.getSettings(medtronicDeviceType) + } else + commandType + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java deleted file mode 100644 index 8ab1fc0871..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -/** - * Created by andy on 10/18/18. - */ - -public enum MedtronicUIResponseType { - - Data, - Error, - Invalid - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.kt new file mode 100644 index 0000000000..6067085071 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +/** + * Created by andy on 10/18/18. + */ +enum class MedtronicUIResponseType { + + Data, Error, Invalid +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java deleted file mode 100644 index 820bff8a5b..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java +++ /dev/null @@ -1,129 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -import java.util.HashMap; - -/** - * Application: GGC - GNU Gluco Control - * Plug-in: Pump Tool (support for Pump devices) - *

- * See AUTHORS for copyright information. - *

- * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later - * version. - *

- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - *

- * Filename: PumpBolusType Description: Pump Bolus Types - *

- * Author: Andy {andy@atech-software.com} - */ - -public enum PumpBolusType // implements CodeEnumWithTranslation -{ - None(0, "NONE"), // - Normal(1, "BOLUS_STANDARD"), // - Audio(2, "BOLUS_AUDIO"), // - Extended(3, "BOLUS_SQUARE", "AMOUNT_SQUARE=%s;DURATION=%s"), // - Multiwave(4, "BOLUS_MULTIWAVE", "AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s"); - - static String[] descriptions; - // static HashMap translationMapping = new HashMap(); - static HashMap codeMapping = new HashMap(); - private static boolean translated; - - static { - for (PumpBolusType pbt : values()) { - codeMapping.put(pbt.code, pbt); - } - } - - // public static void translateKeywords(I18nControlAbstract ic) - // { - // if (translated) - // return; - // - // for (PumpBolusType pbt : values()) - // { - // pbt.setTranslation(ic.getMessage(pbt.i18nKey)); - // translationMapping.put(pbt.getTranslation(), pbt); - // } - // - // String[] bolusDescriptions = { ic.getMessage("SELECT_BOLUS_TYPE"), // - // ic.getMessage("BOLUS_STANDARD"), // - // ic.getMessage("BOLUS_AUDIO"), // - // ic.getMessage("BOLUS_SQUARE"), // - // ic.getMessage("BOLUS_MULTIWAVE"), }; - // - // descriptions = bolusDescriptions; - // - // translated = true; - // } - - int code; - String i18nKey; - String translation; - String valueTemplate; - - - PumpBolusType(int code, String i18nKey) { - this.code = code; - this.i18nKey = i18nKey; - } - - - PumpBolusType(int code, String i18nKey, String valueTemplate) { - this.code = code; - this.i18nKey = i18nKey; - this.valueTemplate = valueTemplate; - } - - - public static PumpBolusType getByCode(int code) { - if (codeMapping.containsKey(code)) { - return codeMapping.get(code); - } else { - return PumpBolusType.None; - } - } - - - /** - * Get Descriptions (array) - * - * @return array of strings with description - */ - public static String[] getDescriptions() { - return descriptions; - } - - - public String getTranslation() { - return translation; - } - - - public void setTranslation(String translation) { - this.translation = translation; - } - - - public int getCode() { - return code; - } - - - public String getI18nKey() { - return i18nKey; - } - - - public String getName() { - return this.name(); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.kt new file mode 100644 index 0000000000..02e3bdbd2b --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.kt @@ -0,0 +1,54 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +import java.util.* + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class PumpBolusType { + + None(0, "NONE"), // + Normal(1, "BOLUS_STANDARD"), // + Audio(2, "BOLUS_AUDIO"), // + Extended(3, "BOLUS_SQUARE", "AMOUNT_SQUARE=%s;DURATION=%s"), // + Multiwave(4, "BOLUS_MULTIWAVE", "AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s"); + + companion object { + var codeMapping = HashMap() + + fun getByCode(code: Int): PumpBolusType? { + return if (codeMapping.containsKey(code)) { + codeMapping[code] + } else { + None + } + } + + init { + for (pbt in values()) { + codeMapping[pbt.code] = pbt + } + } + } + + var code: Int + var i18nKey: String + var valueTemplate: String? = null + + constructor(code: Int, i18nKey: String) { + this.code = code + this.i18nKey = i18nKey + } + + constructor(code: Int, i18nKey: String, valueTemplate: String?) { + this.code = code + this.i18nKey = i18nKey + this.valueTemplate = valueTemplate + } + + //override val name: String +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java deleted file mode 100644 index 1c69641c98..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java +++ /dev/null @@ -1,58 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.defs; - -/** - * Created by andy on 27.02.15. - */ -public enum PumpConfigurationGroup { - General(1, "GROUP_GENERAL"), // - Device(2, "GROUP_DEVICE"), // - - Insulin(3, "GROUP_INSULIN"), // - - Basal(4, "GROUP_BASAL"), // - Bolus(5, "GROUP_BOLUS"), // - Sound(6, "GROUP_SOUND"), // - - Other(20, "GROUP_OTHER"), // - - UnknownGroup(21, "GROUP_UNKNOWN"), // - - ; // - - static boolean translated; - int code; - String i18nKey; - String translation; - - - PumpConfigurationGroup(int code, String i18nKey) { - this.code = code; - this.i18nKey = i18nKey; - } - - - public String getTranslation() { - return translation; - } - - - public void setTranslation(String translation) { - this.translation = translation; - } - - - public int getCode() { - return code; - } - - - public String getI18nKey() { - return i18nKey; - } - - - public String getName() { - return this.name(); - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.kt new file mode 100644 index 0000000000..6a1c13a628 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.kt @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class PumpConfigurationGroup(var code: Int) { + + General(1), // + Device(2), // + Insulin(3), // + Basal(4), // + Bolus(5), // + Sound(6), // + Other(20), // + UnknownGroup(21); + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/di/MedtronicModule.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/di/MedtronicModule.kt index 0a324648e8..0be7f1ba73 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/di/MedtronicModule.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/di/MedtronicModule.kt @@ -1,23 +1,35 @@ package info.nightscout.androidaps.plugins.pump.medtronic.di import dagger.Module +import dagger.Provides import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager +import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUIComm import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUITask import info.nightscout.androidaps.plugins.pump.medtronic.dialog.MedtronicHistoryActivity -import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService @Module @Suppress("unused") abstract class MedtronicModule { - @ContributesAndroidInjector abstract fun contributesMedtronicHistoryActivity(): MedtronicHistoryActivity + + @ContributesAndroidInjector + abstract fun contributesMedtronicHistoryActivity(): MedtronicHistoryActivity @ContributesAndroidInjector abstract fun contributesMedtronicFragment(): MedtronicFragment - @ContributesAndroidInjector abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService + @ContributesAndroidInjector + abstract fun contributesRileyLinkMedtronicService(): RileyLinkMedtronicService - @ContributesAndroidInjector abstract fun medtronicCommunicationManagerProvider(): MedtronicCommunicationManager + @ContributesAndroidInjector + abstract fun medtronicCommunicationManagerProvider(): MedtronicCommunicationManager @ContributesAndroidInjector abstract fun medtronicUITaskProvider(): MedtronicUITask - @ContributesAndroidInjector abstract fun contributesRileyLinkStatusDeviceMedtronic(): RileyLinkStatusDeviceMedtronic + @ContributesAndroidInjector abstract fun medtronicUICommProvider(): MedtronicUIComm + + companion object { + + @Provides + fun byteUtilProvider(): ByteUtil = ByteUtil(); + } } \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java deleted file mode 100644 index fc986f4f83..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java +++ /dev/null @@ -1,250 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.dialog; - -import android.os.Bundle; -import android.os.SystemClock; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup; -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - - -public class MedtronicHistoryActivity extends NoSplashAppCompatActivity { - - @Inject MedtronicHistoryData medtronicHistoryData; - @Inject ResourceHelper resourceHelper; - - Spinner historyTypeSpinner; - TextView statusView; - RecyclerView recyclerView; - LinearLayoutManager llm; - - static TypeList showingType = null; - static PumpHistoryEntryGroup selectedGroup = PumpHistoryEntryGroup.All; - List filteredHistoryList = new ArrayList<>(); - - RecyclerViewAdapter recyclerViewAdapter; - boolean manualChange = false; - - List typeListFull; - - - private void filterHistory(PumpHistoryEntryGroup group) { - - this.filteredHistoryList.clear(); - - List list = new ArrayList<>(); - list.addAll(medtronicHistoryData.getAllHistory()); - - //LOG.debug("Items on full list: {}", list.size()); - - if (group == PumpHistoryEntryGroup.All) { - this.filteredHistoryList.addAll(list); - } else { - for (PumpHistoryEntry pumpHistoryEntry : list) { - if (pumpHistoryEntry.getEntryType().getGroup() == group) { - this.filteredHistoryList.add(pumpHistoryEntry); - } - } - } - - if (this.recyclerViewAdapter != null) { - this.recyclerViewAdapter.setHistoryList(this.filteredHistoryList); - this.recyclerViewAdapter.notifyDataSetChanged(); - } - - //LOG.debug("Items on filtered list: {}", filteredHistoryList.size()); - } - - - @Override - protected void onResume() { - super.onResume(); - filterHistory(selectedGroup); - setHistoryTypeSpinner(); - } - - - private void setHistoryTypeSpinner() { - this.manualChange = true; - - for (int i = 0; i < typeListFull.size(); i++) { - if (typeListFull.get(i).entryGroup == selectedGroup) { - historyTypeSpinner.setSelection(i); - break; - } - } - - SystemClock.sleep(200); - this.manualChange = false; - } - - - @Override - protected void onPause() { - super.onPause(); - } - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.medtronic_history_activity); - - historyTypeSpinner = findViewById(R.id.medtronic_historytype); - statusView = findViewById(R.id.medtronic_historystatus); - recyclerView = findViewById(R.id.medtronic_history_recyclerview); - - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(this); - recyclerView.setLayoutManager(llm); - - recyclerViewAdapter = new RecyclerViewAdapter(filteredHistoryList); - recyclerView.setAdapter(recyclerViewAdapter); - - statusView.setVisibility(View.GONE); - - typeListFull = getTypeList(PumpHistoryEntryGroup.getTranslatedList(resourceHelper)); - - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, R.layout.spinner_centered, typeListFull); - historyTypeSpinner.setAdapter(spinnerAdapter); - - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - if (manualChange) - return; - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - showingType = selected; - selectedGroup = selected.entryGroup; - filterHistory(selectedGroup); - } - - - @Override - public void onNothingSelected(AdapterView parent) { - if (manualChange) - return; - filterHistory(PumpHistoryEntryGroup.All); - } - }); - - } - - - private List getTypeList(List list) { - - ArrayList typeList = new ArrayList<>(); - - for (PumpHistoryEntryGroup pumpHistoryEntryGroup : list) { - typeList.add(new TypeList(pumpHistoryEntryGroup)); - } - - return typeList; - } - - public static class TypeList { - - PumpHistoryEntryGroup entryGroup; - String name; - - - TypeList(PumpHistoryEntryGroup entryGroup) { - this.entryGroup = entryGroup; - this.name = entryGroup.getTranslated(); - } - - - @Override - public String toString() { - return name; - } - } - - public static class RecyclerViewAdapter extends RecyclerView.Adapter { - - List historyList; - - - RecyclerViewAdapter(List historyList) { - this.historyList = historyList; - } - - - public void setHistoryList(List historyList) { - // this.historyList.clear(); - // this.historyList.addAll(historyList); - - this.historyList = historyList; - - // this.notifyDataSetChanged(); - } - - - @Override - public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.medtronic_history_item, // - viewGroup, false); - return new HistoryViewHolder(v); - } - - - @Override - public void onBindViewHolder(HistoryViewHolder holder, int position) { - PumpHistoryEntry record = historyList.get(position); - - if (record != null) { - holder.timeView.setText(record.getDateTimeString()); - holder.typeView.setText(record.getEntryType().getDescription()); - holder.valueView.setText(record.getDisplayableValue()); - } - } - - - @Override - public int getItemCount() { - return historyList.size(); - } - - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - static class HistoryViewHolder extends RecyclerView.ViewHolder { - - TextView timeView; - TextView typeView; - TextView valueView; - - - HistoryViewHolder(View itemView) { - super(itemView); - // cv = (CardView)itemView.findViewById(R.id.rileylink_history_item); - timeView = itemView.findViewById(R.id.medtronic_history_time); - typeView = itemView.findViewById(R.id.medtronic_history_source); - valueView = itemView.findViewById(R.id.medtronic_history_description); - } - } - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt new file mode 100644 index 0000000000..6257099574 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt @@ -0,0 +1,190 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.dialog + +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Spinner +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.android.DaggerActivity +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import info.nightscout.androidaps.plugins.pump.medtronic.R +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* +import javax.inject.Inject + +class MedtronicHistoryActivity : DaggerActivity() { + + @Inject lateinit var medtronicHistoryData: MedtronicHistoryData + @Inject lateinit var resourceHelper: ResourceHelper + + lateinit var historyTypeSpinner: Spinner + lateinit var statusView: TextView + lateinit var recyclerView: RecyclerView + lateinit var llm: LinearLayoutManager + lateinit var recyclerViewAdapter: RecyclerViewAdapter + + var filteredHistoryList: MutableList = ArrayList() + var manualChange = false + lateinit var typeListFull: List + + //private var _binding: MedtronicHistoryActivityBinding? = null + + //@Inject + //var fragmentInjector: DispatchingAndroidInjector? = null + + // This property is only valid between onCreateView and + // onDestroyView. + //private val binding get() = _binding!! + + private fun filterHistory(group: PumpHistoryEntryGroup) { + filteredHistoryList.clear() + val list: MutableList = ArrayList() + list.addAll(medtronicHistoryData.allHistory) + + //LOG.debug("Items on full list: {}", list.size()); + if (group === PumpHistoryEntryGroup.All) { + filteredHistoryList.addAll(list) + } else { + for (pumpHistoryEntry in list) { + if (pumpHistoryEntry.entryType.group === group) { + filteredHistoryList.add(pumpHistoryEntry) + } + } + } + + recyclerViewAdapter.setHistoryListInternal(filteredHistoryList) + recyclerViewAdapter.notifyDataSetChanged() + + //LOG.debug("Items on filtered list: {}", filteredHistoryList.size()); + } + + override fun onResume() { + super.onResume() + filterHistory(selectedGroup) + setHistoryTypeSpinner() + } + + private fun setHistoryTypeSpinner() { + manualChange = true + for (i in typeListFull.indices) { + if (typeListFull[i].entryGroup === selectedGroup) { + historyTypeSpinner.setSelection(i) + break + } + } + SystemClock.sleep(200) + manualChange = false + } + + override fun onPause() { + super.onPause() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.medtronic_history_activity) + historyTypeSpinner = findViewById(R.id.medtronic_historytype) + statusView = findViewById(R.id.medtronic_historystatus) + recyclerView = findViewById(R.id.medtronic_history_recyclerview) + recyclerView.setHasFixedSize(true) + llm = LinearLayoutManager(this) + recyclerView.setLayoutManager(llm) + recyclerViewAdapter = RecyclerViewAdapter(filteredHistoryList) + recyclerView.setAdapter(recyclerViewAdapter) + statusView.setVisibility(View.GONE) + typeListFull = getTypeList(PumpHistoryEntryGroup.getTranslatedList(resourceHelper)) + val spinnerAdapter = ArrayAdapter(this, R.layout.spinner_centered, typeListFull) + historyTypeSpinner.setAdapter(spinnerAdapter) + historyTypeSpinner.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + if (manualChange) return + val selected = historyTypeSpinner.getSelectedItem() as TypeList + showingType = selected + selectedGroup = selected.entryGroup + filterHistory(selectedGroup) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + if (manualChange) return + filterHistory(PumpHistoryEntryGroup.All) + } + }) + } + + private fun getTypeList(list: List): List { + val typeList = ArrayList() + for (pumpHistoryEntryGroup in list) { + typeList.add(TypeList(pumpHistoryEntryGroup)) + } + return typeList + } + + class TypeList internal constructor(var entryGroup: PumpHistoryEntryGroup) { + + var name: String + override fun toString(): String { + return name + } + + init { + name = entryGroup.translated!! + } + } + + class RecyclerViewAdapter internal constructor(var historyList: List) : RecyclerView.Adapter() { + + fun setHistoryListInternal(historyList: List) { + // this.historyList.clear(); + // this.historyList.addAll(historyList); + this.historyList = historyList + + // this.notifyDataSetChanged(); + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder { + val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.medtronic_history_item, // + viewGroup, false) + return HistoryViewHolder(v) + } + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val record = historyList[position] + holder.timeView.text = record.dateTimeString + holder.typeView.text = record.entryType.description + holder.valueView.text = record.displayableValue + } + + override fun getItemCount(): Int { + return historyList.size + } + + class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var timeView: TextView + var typeView: TextView + var valueView: TextView + + init { + // cv = (CardView)itemView.findViewById(R.id.rileylink_history_item); + timeView = itemView.findViewById(R.id.medtronic_history_time) + typeView = itemView.findViewById(R.id.medtronic_history_source) + valueView = itemView.findViewById(R.id.medtronic_history_description) + } + } + + } + + companion object { + + var showingType: TypeList? = null + var selectedGroup = PumpHistoryEntryGroup.All + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java deleted file mode 100644 index e4b6c0e13a..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java +++ /dev/null @@ -1,166 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.dialog; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.support.DaggerFragment; -import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -/** - * Created by andy on 5/19/18. - *

- * This is for 3rd tab, called Medtronic (in RileyLink stats), that should work similarly as the one in Loop. - *

- * Showing currently selected RL, speed of RL, ability to issue simple commands (getModel, tuneUp, gerProfile) - */ - -// TODO needs to be implemented -public class RileyLinkStatusDeviceMedtronic extends DaggerFragment implements RefreshableInterface { - - @Inject ResourceHelper resourceHelper; - @Inject DateUtil dateUtil; - - // @BindView(R.id.rileylink_history_list) - ListView listView; - - RileyLinkCommandListAdapter adapter; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.rileylink_status_device, container, false); - - adapter = new RileyLinkCommandListAdapter(); - - return rootView; - } - - - @Override - public void onStart() { - super.onStart(); - - this.listView = getActivity().findViewById(R.id.rileylink_history_list); - - listView.setAdapter(adapter); - - refreshData(); - } - - - @Override - public void refreshData() { - // adapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); - } - - static class ViewHolder { - - TextView itemTime; - TextView itemSource; - TextView itemDescription; - } - - private class RileyLinkCommandListAdapter extends BaseAdapter { - - private final List historyItemList; - private final LayoutInflater mInflator; - - - public RileyLinkCommandListAdapter() { - super(); - historyItemList = new ArrayList<>(); - mInflator = RileyLinkStatusDeviceMedtronic.this.getLayoutInflater(); - } - - - public void addItem(RLHistoryItem item) { - if (!historyItemList.contains(item)) { - historyItemList.add(item); - notifyDataSetChanged(); - } - } - - - public RLHistoryItem getHistoryItem(int position) { - return historyItemList.get(position); - } - - - public void addItemsAndClean(List items) { - this.historyItemList.clear(); - - for (RLHistoryItem item : items) { - - if (!historyItemList.contains(item)) { - historyItemList.add(item); - } - } - - notifyDataSetChanged(); - } - - - public void clear() { - historyItemList.clear(); - notifyDataSetChanged(); - } - - - @Override - public int getCount() { - return historyItemList.size(); - } - - - @Override - public Object getItem(int i) { - return historyItemList.get(i); - } - - - @Override - public long getItemId(int i) { - return i; - } - - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - RileyLinkStatusDeviceMedtronic.ViewHolder viewHolder; - // General ListView optimization code. - if (view == null) { - view = mInflator.inflate(R.layout.rileylink_status_device_item, null); - viewHolder = new RileyLinkStatusDeviceMedtronic.ViewHolder(); - viewHolder.itemTime = view.findViewById(R.id.rileylink_history_time); - viewHolder.itemSource = view.findViewById(R.id.rileylink_history_source); - viewHolder.itemDescription = view.findViewById(R.id.rileylink_history_description); - view.setTag(viewHolder); - } else { - viewHolder = (RileyLinkStatusDeviceMedtronic.ViewHolder) view.getTag(); - } - - RLHistoryItem item = historyItemList.get(i); - viewHolder.itemTime.setText(StringUtil.toDateTimeString(dateUtil, item.getDateTime())); - viewHolder.itemSource.setText("Riley Link"); // for now - viewHolder.itemDescription.setText(item.getDescription(resourceHelper)); - - return view; - } - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java deleted file mode 100644 index 905a005e29..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java +++ /dev/null @@ -1,180 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.driver; - -import androidx.annotation.NonNull; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - - -/** - * Created by andy on 4/28/18. - */ - -@Singleton -public class MedtronicPumpStatus extends info.nightscout.androidaps.plugins.pump.common.data.PumpStatus { - - private final ResourceHelper resourceHelper; - private final SP sp; - private final RileyLinkUtil rileyLinkUtil; - private final RxBusWrapper rxBus; - - public String errorDescription = null; - public String serialNumber; - public String pumpFrequency = null; - public Double maxBolus; - public Double maxBasal; - - // statuses - private PumpDeviceState pumpDeviceState = PumpDeviceState.NeverContacted; - public MedtronicDeviceType medtronicDeviceType = null; - public Date tempBasalStart; - public Double tempBasalAmount = 0.0d; - - // fixme - public Integer tempBasalLength = 0; - - private Map medtronicPumpMap = null; - private Map medtronicDeviceTypeMap = null; - public BasalProfileStatus basalProfileStatus = BasalProfileStatus.NotInitialized; - public BatteryType batteryType = BatteryType.None; - - - @Inject - public MedtronicPumpStatus(ResourceHelper resourceHelper, - SP sp, - RxBusWrapper rxBus, - RileyLinkUtil rileyLinkUtil - ) { - super(PumpType.MEDTRONIC_522_722); - this.resourceHelper = resourceHelper; - this.sp = sp; - this.rxBus = rxBus; - this.rileyLinkUtil = rileyLinkUtil; - initSettings(); - } - - - public void initSettings() { - - this.activeProfileName = "STD"; - this.reservoirRemainingUnits = 75d; - this.batteryRemaining = 75; - - if (this.medtronicPumpMap == null) - createMedtronicPumpMap(); - - if (this.medtronicDeviceTypeMap == null) - createMedtronicDeviceTypeMap(); - - this.lastConnection = sp.getLong(MedtronicConst.Statistics.LastGoodPumpCommunicationTime, 0L); - this.lastDataTime = this.lastConnection; - } - - - private void createMedtronicDeviceTypeMap() { - medtronicDeviceTypeMap = new HashMap<>(); - medtronicDeviceTypeMap.put("512", MedtronicDeviceType.Medtronic_512); - medtronicDeviceTypeMap.put("712", MedtronicDeviceType.Medtronic_712); - medtronicDeviceTypeMap.put("515", MedtronicDeviceType.Medtronic_515); - medtronicDeviceTypeMap.put("715", MedtronicDeviceType.Medtronic_715); - - medtronicDeviceTypeMap.put("522", MedtronicDeviceType.Medtronic_522); - medtronicDeviceTypeMap.put("722", MedtronicDeviceType.Medtronic_722); - medtronicDeviceTypeMap.put("523", MedtronicDeviceType.Medtronic_523_Revel); - medtronicDeviceTypeMap.put("723", MedtronicDeviceType.Medtronic_723_Revel); - medtronicDeviceTypeMap.put("554", MedtronicDeviceType.Medtronic_554_Veo); - medtronicDeviceTypeMap.put("754", MedtronicDeviceType.Medtronic_754_Veo); - } - - - private void createMedtronicPumpMap() { - - medtronicPumpMap = new HashMap<>(); - medtronicPumpMap.put("512", PumpType.MEDTRONIC_512_712); - medtronicPumpMap.put("712", PumpType.MEDTRONIC_512_712); - medtronicPumpMap.put("515", PumpType.MEDTRONIC_515_715); - medtronicPumpMap.put("715", PumpType.MEDTRONIC_515_715); - - medtronicPumpMap.put("522", PumpType.MEDTRONIC_522_722); - medtronicPumpMap.put("722", PumpType.MEDTRONIC_522_722); - medtronicPumpMap.put("523", PumpType.MEDTRONIC_523_723_REVEL); - medtronicPumpMap.put("723", PumpType.MEDTRONIC_523_723_REVEL); - medtronicPumpMap.put("554", PumpType.MEDTRONIC_554_754_VEO); - medtronicPumpMap.put("754", PumpType.MEDTRONIC_554_754_VEO); - - } - - public Map getMedtronicPumpMap() { - return medtronicPumpMap; - } - - public Map getMedtronicDeviceTypeMap() { - return medtronicDeviceTypeMap; - } - - public double getBasalProfileForHour() { - if (basalsByHour != null) { - GregorianCalendar c = new GregorianCalendar(); - int hour = c.get(Calendar.HOUR_OF_DAY); - - return basalsByHour[hour]; - } - - return 0; - } - - // Battery type - private Map mapByDescription; - - public BatteryType getBatteryTypeByDescription(String batteryTypeStr) { - if (mapByDescription == null) { - mapByDescription = new HashMap<>(); - for (BatteryType value : BatteryType.values()) { - mapByDescription.put(resourceHelper.gs(value.description), value); - } - } - if (mapByDescription.containsKey(batteryTypeStr)) { - return mapByDescription.get(batteryTypeStr); - } - return BatteryType.None; - } - - @NonNull - public String getErrorInfo() { - return (errorDescription == null) ? "-" : errorDescription; - } - - - public PumpDeviceState getPumpDeviceState() { - return pumpDeviceState; - } - - - public void setPumpDeviceState(PumpDeviceState pumpDeviceState) { - this.pumpDeviceState = pumpDeviceState; - - rileyLinkUtil.getRileyLinkHistory().add(new RLHistoryItem(pumpDeviceState, RileyLinkTargetDevice.MedtronicPump)); - - rxBus.send(new EventRileyLinkDeviceStatusChange(pumpDeviceState)); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt new file mode 100644 index 0000000000..109ae89acd --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.kt @@ -0,0 +1,140 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.driver + +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 4/28/18. + */ +@Singleton +class MedtronicPumpStatus @Inject constructor(private val resourceHelper: ResourceHelper, + private val sp: SP, + private val rxBus: RxBusWrapper, + private val rileyLinkUtil: RileyLinkUtil +) : info.nightscout.androidaps.plugins.pump.common.data.PumpStatus(PumpType.MEDTRONIC_522_722) { + + var errorDescription: String? = null + lateinit var serialNumber: String //? = null + var pumpFrequency: String? = null + var maxBolus: Double? = null + var maxBasal: Double? = null + var runningTBR: info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry? = null + + // statuses + var pumpDeviceState = PumpDeviceState.NeverContacted + set(pumpDeviceState) { + field = pumpDeviceState + rileyLinkUtil.rileyLinkHistory.add(RLHistoryItem(pumpDeviceState, RileyLinkTargetDevice.MedtronicPump)) + rxBus.send(EventRileyLinkDeviceStatusChange(pumpDeviceState)) + } + + var medtronicDeviceType: MedtronicDeviceType = MedtronicDeviceType.Medtronic_522 + var medtronicPumpMap: MutableMap = mutableMapOf() + var medtronicDeviceTypeMap: MutableMap = mutableMapOf() + var basalProfileStatus = BasalProfileStatus.NotInitialized + var batteryType = BatteryType.None + + override fun initSettings() { + activeProfileName = "STD" + reservoirRemainingUnits = 75.0 + batteryRemaining = 75 + if (medtronicPumpMap.isEmpty()) createMedtronicPumpMap() + if (medtronicDeviceTypeMap.isEmpty()) createMedtronicDeviceTypeMap() + lastConnection = sp.getLong(MedtronicConst.Statistics.LastGoodPumpCommunicationTime, 0L) + lastDataTime = lastConnection + var serial = sp.getStringOrNull(MedtronicConst.Prefs.PumpSerial, null) + if (serial != null) { + serialNumber = serial + } + } + + private fun createMedtronicDeviceTypeMap() { + medtronicDeviceTypeMap["512"] = MedtronicDeviceType.Medtronic_512 + medtronicDeviceTypeMap["712"] = MedtronicDeviceType.Medtronic_712 + medtronicDeviceTypeMap["515"] = MedtronicDeviceType.Medtronic_515 + medtronicDeviceTypeMap["715"] = MedtronicDeviceType.Medtronic_715 + medtronicDeviceTypeMap["522"] = MedtronicDeviceType.Medtronic_522 + medtronicDeviceTypeMap["722"] = MedtronicDeviceType.Medtronic_722 + medtronicDeviceTypeMap["523"] = MedtronicDeviceType.Medtronic_523_Revel + medtronicDeviceTypeMap["723"] = MedtronicDeviceType.Medtronic_723_Revel + medtronicDeviceTypeMap["554"] = MedtronicDeviceType.Medtronic_554_Veo + medtronicDeviceTypeMap["754"] = MedtronicDeviceType.Medtronic_754_Veo + } + + private fun createMedtronicPumpMap() { + medtronicPumpMap = HashMap() + medtronicPumpMap["512"] = PumpType.MEDTRONIC_512_712 + medtronicPumpMap["712"] = PumpType.MEDTRONIC_512_712 + medtronicPumpMap["515"] = PumpType.MEDTRONIC_515_715 + medtronicPumpMap["715"] = PumpType.MEDTRONIC_515_715 + medtronicPumpMap["522"] = PumpType.MEDTRONIC_522_722 + medtronicPumpMap["722"] = PumpType.MEDTRONIC_522_722 + medtronicPumpMap["523"] = PumpType.MEDTRONIC_523_723_REVEL + medtronicPumpMap["723"] = PumpType.MEDTRONIC_523_723_REVEL + medtronicPumpMap["554"] = PumpType.MEDTRONIC_554_754_VEO + medtronicPumpMap["754"] = PumpType.MEDTRONIC_554_754_VEO + } + + val basalProfileForHour: Double + get() { + if (basalsByHour != null) { + val c = GregorianCalendar() + val hour = c[Calendar.HOUR_OF_DAY] + return basalsByHour!![hour] + } + return 0.0 + } + + // Battery type + private var batteryTypeByDescMap: MutableMap = HashMap() + + fun getBatteryTypeByDescription(batteryTypeStr: String?): BatteryType? { + if (batteryTypeByDescMap.size == 0) { + for (value in BatteryType.values()) { + batteryTypeByDescMap[resourceHelper.gs(value.description)] = value + } + } + return if (batteryTypeByDescMap.containsKey(batteryTypeStr)) { + batteryTypeByDescMap[batteryTypeStr] + } else BatteryType.None + } + + override val errorInfo: String + get() = if (errorDescription == null) "-" else errorDescription!! + + val tbrRemainingTime: Int? + get() { + if (tempBasalStart == null) return null + if (tempBasalEnd == null) { + val startTime = tempBasalStart!!.time + tempBasalEnd = startTime + tempBasalLength!! * 60 * 1000 + } + if (System.currentTimeMillis() > tempBasalEnd!!) { + tempBasalStart = null + tempBasalEnd = null + tempBasalLength = null + tempBasalAmount = null + return null + } + val timeMinutes = (tempBasalEnd!! - System.currentTimeMillis()) / (1000 * 60) + return timeMinutes.toInt() + } + + init { + initSettings() + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java deleted file mode 100644 index 2c5274ba0b..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java +++ /dev/null @@ -1,388 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.service; - -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Binder; -import android.os.IBinder; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUIComm; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUIPostprocessor; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; - -/** - * RileyLinkMedtronicService is intended to stay running when the gui-app is closed. - */ -@Singleton -public class RileyLinkMedtronicService extends RileyLinkService { - - @Inject MedtronicPumpPlugin medtronicPumpPlugin; - @Inject MedtronicUtil medtronicUtil; - @Inject MedtronicUIPostprocessor medtronicUIPostprocessor; - @Inject MedtronicPumpStatus medtronicPumpStatus; - @Inject RFSpy rfSpy; - @Inject MedtronicCommunicationManager medtronicCommunicationManager; - - private MedtronicUIComm medtronicUIComm; - private final IBinder mBinder = new LocalBinder(); - - private boolean serialChanged = false; - private String[] frequencies; - private String rileyLinkAddress = null; - private boolean rileyLinkAddressChanged = false; - private RileyLinkEncodingType encodingType; - private boolean encodingChanged = false; - private boolean inPreInit = true; - - - // This empty constructor must be kept, otherwise dagger injection might break! - @Inject - public RileyLinkMedtronicService() { - } - - - @Override public void onCreate() { - super.onCreate(); - aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly created"); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - aapsLogger.warn(LTag.PUMPCOMM, "onConfigurationChanged"); - super.onConfigurationChanged(newConfig); - } - - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - - @Override - public RileyLinkEncodingType getEncoding() { - return RileyLinkEncodingType.FourByteSixByteLocal; - } - - - /** - * If you have customized RileyLinkServiceData you need to override this - */ - public void initRileyLinkServiceData() { - - frequencies = new String[2]; - frequencies[0] = resourceHelper.gs(R.string.key_medtronic_pump_frequency_us_ca); - frequencies[1] = resourceHelper.gs(R.string.key_medtronic_pump_frequency_worldwide); - - rileyLinkServiceData.targetDevice = RileyLinkTargetDevice.MedtronicPump; - - setPumpIDString(sp.getString(MedtronicConst.Prefs.PumpSerial, "000000")); - - // get most recently used RileyLink address and name - rileyLinkServiceData.rileyLinkAddress = sp.getString(RileyLinkConst.Prefs.RileyLinkAddress, ""); - rileyLinkServiceData.rileyLinkName = sp.getString(RileyLinkConst.Prefs.RileyLinkName, ""); - - rfspy.startReader(); - - medtronicUIComm = new MedtronicUIComm(injector, aapsLogger, medtronicUtil, medtronicUIPostprocessor, medtronicCommunicationManager); - - aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly constructed"); - } - - public MedtronicCommunicationManager getDeviceCommunicationManager() { - return this.medtronicCommunicationManager; - } - - - @Override - public void setPumpDeviceState(PumpDeviceState pumpDeviceState) { - this.medtronicPumpStatus.setPumpDeviceState(pumpDeviceState); - } - - - public MedtronicUIComm getMedtronicUIComm() { - return medtronicUIComm; - } - - public void setPumpIDString(String pumpID) { - if (pumpID.length() != 6) { - aapsLogger.error("setPumpIDString: invalid pump id string: " + pumpID); - return; - } - - byte[] pumpIDBytes = ByteUtil.fromHexString(pumpID); - - if (pumpIDBytes == null) { - aapsLogger.error("Invalid pump ID? - PumpID is null."); - - rileyLinkServiceData.setPumpID("000000", new byte[]{0, 0, 0}); - - } else if (pumpIDBytes.length != 3) { - aapsLogger.error("Invalid pump ID? " + ByteUtil.shortHexString(pumpIDBytes)); - - rileyLinkServiceData.setPumpID("000000", new byte[]{0, 0, 0}); - - } else if (pumpID.equals("000000")) { - aapsLogger.error("Using pump ID " + pumpID); - - rileyLinkServiceData.setPumpID(pumpID, new byte[]{0, 0, 0}); - - } else { - aapsLogger.info(LTag.PUMPBTCOMM, "Using pump ID " + pumpID); - - String oldId = rileyLinkServiceData.pumpID; - - rileyLinkServiceData.setPumpID(pumpID, pumpIDBytes); - - if (oldId != null && !oldId.equals(pumpID)) { - medtronicUtil.setMedtronicPumpModel(null); // if we change pumpId, model probably changed too - } - - return; - } - - medtronicPumpStatus.setPumpDeviceState(PumpDeviceState.InvalidConfiguration); - - // LOG.info("setPumpIDString: saved pumpID " + idString); - } - - public class LocalBinder extends Binder { - - public RileyLinkMedtronicService getServiceInstance() { - return RileyLinkMedtronicService.this; - } - } - - - /* private functions */ - - // PumpInterface - REMOVE - - public boolean isInitialized() { - return rileyLinkServiceData.rileyLinkServiceState.isReady(); - } - - - public boolean verifyConfiguration(boolean forceRileyLinkAddressRenewal) { - try { - String regexSN = "[0-9]{6}"; - String regexMac = "([\\da-fA-F]{1,2}(?::|$)){6}"; - - medtronicPumpStatus.errorDescription = "-"; - - String serialNr = sp.getStringOrNull(MedtronicConst.Prefs.PumpSerial, null); - - if (serialNr == null) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_serial_not_set); - return false; - } else { - if (!serialNr.matches(regexSN)) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_serial_invalid); - return false; - } else { - if (!serialNr.equals(medtronicPumpStatus.serialNumber)) { - medtronicPumpStatus.serialNumber = serialNr; - serialChanged = true; - } - } - } - - String pumpTypePref = sp.getStringOrNull(MedtronicConst.Prefs.PumpType, null); - - if (pumpTypePref == null) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_type_not_set); - return false; - } else { - String pumpTypePart = pumpTypePref.substring(0, 3); - - if (!pumpTypePart.matches("[0-9]{3}")) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_type_invalid); - return false; - } else { - PumpType pumpType = medtronicPumpStatus.getMedtronicPumpMap().get(pumpTypePart); - medtronicPumpStatus.medtronicDeviceType = medtronicPumpStatus.getMedtronicDeviceTypeMap().get(pumpTypePart); - medtronicPumpPlugin.setPumpType(pumpType); - - if (pumpTypePart.startsWith("7")) - medtronicPumpStatus.reservoirFullUnits = 300; - else - medtronicPumpStatus.reservoirFullUnits = 176; - } - } - - String pumpFrequency = sp.getStringOrNull(MedtronicConst.Prefs.PumpFrequency, null); - - if (pumpFrequency == null) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_frequency_not_set); - return false; - } else { - if (!pumpFrequency.equals(frequencies[0]) && !pumpFrequency.equals(frequencies[1])) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_frequency_invalid); - return false; - } else { - medtronicPumpStatus.pumpFrequency = pumpFrequency; - boolean isFrequencyUS = pumpFrequency.equals(frequencies[0]); - - RileyLinkTargetFrequency newTargetFrequency = isFrequencyUS ? // - RileyLinkTargetFrequency.Medtronic_US - : RileyLinkTargetFrequency.Medtronic_WorldWide; - - if (rileyLinkServiceData.rileyLinkTargetFrequency != newTargetFrequency) { - rileyLinkServiceData.rileyLinkTargetFrequency = newTargetFrequency; - } - - } - } - - String rileyLinkAddress = sp.getStringOrNull(RileyLinkConst.Prefs.RileyLinkAddress, null); - - if (rileyLinkAddress == null) { - aapsLogger.debug(LTag.PUMP, "RileyLink address invalid: null"); - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_rileylink_address_invalid); - return false; - } else { - if (!rileyLinkAddress.matches(regexMac)) { - medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_rileylink_address_invalid); - aapsLogger.debug(LTag.PUMP, "RileyLink address invalid: %s", rileyLinkAddress); - } else { - if (!rileyLinkAddress.equals(this.rileyLinkAddress)) { - this.rileyLinkAddress = rileyLinkAddress; - rileyLinkAddressChanged = true; - } - } - } - - double maxBolusLcl = checkParameterValue(MedtronicConst.Prefs.MaxBolus, "25.0", 25.0d); - - if (medtronicPumpStatus.maxBolus == null || !medtronicPumpStatus.maxBolus.equals(maxBolusLcl)) { - medtronicPumpStatus.maxBolus = maxBolusLcl; - - //LOG.debug("Max Bolus from AAPS settings is " + maxBolus); - } - - double maxBasalLcl = checkParameterValue(MedtronicConst.Prefs.MaxBasal, "35.0", 35.0d); - - if (medtronicPumpStatus.maxBasal == null || !medtronicPumpStatus.maxBasal.equals(maxBasalLcl)) { - medtronicPumpStatus.maxBasal = maxBasalLcl; - - //LOG.debug("Max Basal from AAPS settings is " + maxBasal); - } - - - String encodingTypeStr = sp.getStringOrNull(MedtronicConst.Prefs.Encoding, null); - - if (encodingTypeStr == null) { - return false; - } - - RileyLinkEncodingType newEncodingType = RileyLinkEncodingType.getByDescription(encodingTypeStr, resourceHelper); - - if (encodingType == null) { - encodingType = newEncodingType; - } else if (encodingType != newEncodingType) { - encodingType = newEncodingType; - encodingChanged = true; - } - - String batteryTypeStr = sp.getStringOrNull(MedtronicConst.Prefs.BatteryType, null); - - if (batteryTypeStr == null) - return false; - - BatteryType batteryType = medtronicPumpStatus.getBatteryTypeByDescription(batteryTypeStr); - - if (medtronicPumpStatus.batteryType != batteryType) { - medtronicPumpStatus.batteryType = batteryType; - } - - //String bolusDebugEnabled = sp.getStringOrNull(MedtronicConst.Prefs.BolusDebugEnabled, null); - //boolean bolusDebug = bolusDebugEnabled != null && bolusDebugEnabled.equals(resourceHelper.gs(R.string.common_on)); - //MedtronicHistoryData.doubleBolusDebug = bolusDebug; - - rileyLinkServiceData.showBatteryLevel = sp.getBoolean(RileyLinkConst.Prefs.ShowBatteryLevel, false); - - reconfigureService(forceRileyLinkAddressRenewal); - - return true; - - } catch (Exception ex) { - medtronicPumpStatus.errorDescription = ex.getMessage(); - aapsLogger.error(LTag.PUMP, "Error on Verification: " + ex.getMessage(), ex); - return false; - } - } - - private boolean reconfigureService(boolean forceRileyLinkAddressRenewal) { - - if (!inPreInit) { - - if (serialChanged) { - setPumpIDString(medtronicPumpStatus.serialNumber); // short operation - serialChanged = false; - } - - if (rileyLinkAddressChanged || forceRileyLinkAddressRenewal) { - rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkNewAddressSet, this); - rileyLinkAddressChanged = false; - } - - if (encodingChanged) { - changeRileyLinkEncoding(encodingType); - encodingChanged = false; - } - } - - - // if (targetFrequencyChanged && !inPreInit && MedtronicUtil.getMedtronicService() != null) { - // RileyLinkUtil.setRileyLinkTargetFrequency(targetFrequency); - // // RileyLinkUtil.getRileyLinkCommunicationManager().refreshRileyLinkTargetFrequency(); - // targetFrequencyChanged = false; - // } - - return (!rileyLinkAddressChanged && !serialChanged && !encodingChanged); // && !targetFrequencyChanged); - } - - private double checkParameterValue(int key, String defaultValue, double defaultValueDouble) { - double val; - - String value = sp.getString(key, defaultValue); - - try { - val = Double.parseDouble(value); - } catch (Exception ex) { - aapsLogger.error("Error parsing setting: %s, value found %s", key, value); - val = defaultValueDouble; - } - - if (val > defaultValueDouble) { - sp.putString(key, defaultValue); - val = defaultValueDouble; - } - - return val; - } - - public boolean setNotInPreInit() { - this.inPreInit = false; - - return reconfigureService(false); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt new file mode 100644 index 0000000000..30d7f123df --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt @@ -0,0 +1,290 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.service + +import android.content.Intent +import android.content.res.Configuration +import android.os.Binder +import android.os.IBinder +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.medtronic.R +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager +import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUIComm +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import javax.inject.Inject +import javax.inject.Singleton + +/** + * RileyLinkMedtronicService is intended to stay running when the gui-app is closed. + */ +@Singleton +class RileyLinkMedtronicService // This empty constructor must be kept, otherwise dagger injection might break! +@Inject constructor() : RileyLinkService() { + + @Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin + @Inject lateinit var medtronicUtil: MedtronicUtil + @Inject lateinit var medtronicPumpStatus: MedtronicPumpStatus + @Inject lateinit var rfSpy: RFSpy + @Inject lateinit var medtronicCommunicationManager: MedtronicCommunicationManager + @Inject lateinit var medtronicUIComm: MedtronicUIComm + + private val mBinder: IBinder = LocalBinder() + private var serialChanged = false + lateinit var frequencies: Array + private var rileyLinkAddress: String? = null + private var rileyLinkAddressChanged = false + private var encodingType: RileyLinkEncodingType? = null + private var encodingChanged = false + private var inPreInit = true + + override fun onCreate() { + super.onCreate() + aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly created") + } + + override fun onConfigurationChanged(newConfig: Configuration) { + aapsLogger.warn(LTag.PUMPCOMM, "onConfigurationChanged") + super.onConfigurationChanged(newConfig) + } + + override fun onBind(intent: Intent): IBinder { + return mBinder + } + + override fun getEncoding(): RileyLinkEncodingType { + return RileyLinkEncodingType.FourByteSixByteLocal + } + + /** + * If you have customized RileyLinkServiceData you need to override this + */ + override fun initRileyLinkServiceData() { + frequencies = arrayOf(resourceHelper.gs(R.string.key_medtronic_pump_frequency_us_ca), + resourceHelper.gs(R.string.key_medtronic_pump_frequency_worldwide)) + // frequencies[0] = resourceHelper.gs(R.string.key_medtronic_pump_frequency_us_ca) + // frequencies[1] = resourceHelper.gs(R.string.key_medtronic_pump_frequency_worldwide) + rileyLinkServiceData.targetDevice = RileyLinkTargetDevice.MedtronicPump + setPumpIDString(sp.getString(MedtronicConst.Prefs.PumpSerial, "000000")) + + // get most recently used RileyLink address and name + rileyLinkServiceData.rileyLinkAddress = sp.getString(RileyLinkConst.Prefs.RileyLinkAddress, "") + rileyLinkServiceData.rileyLinkName = sp.getString(RileyLinkConst.Prefs.RileyLinkName, "") + rfspy.startReader() + aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly constructed") + } + + override fun getDeviceCommunicationManager(): MedtronicCommunicationManager { + return medtronicCommunicationManager + } + + override fun setPumpDeviceState(pumpDeviceState: PumpDeviceState) { + medtronicPumpStatus.pumpDeviceState = pumpDeviceState + } + + fun setPumpIDString(pumpID: String) { + if (pumpID.length != 6) { + aapsLogger.error("setPumpIDString: invalid pump id string: $pumpID") + return + } + val pumpIDBytes = ByteUtil.fromHexString(pumpID) + if (pumpIDBytes == null) { + aapsLogger.error("Invalid pump ID? - PumpID is null.") + rileyLinkServiceData.setPumpID("000000", byteArrayOf(0, 0, 0)) + } else if (pumpIDBytes.size != 3) { + aapsLogger.error("Invalid pump ID? " + ByteUtil.shortHexString(pumpIDBytes)) + rileyLinkServiceData.setPumpID("000000", byteArrayOf(0, 0, 0)) + } else if (pumpID == "000000") { + aapsLogger.error("Using pump ID $pumpID") + rileyLinkServiceData.setPumpID(pumpID, byteArrayOf(0, 0, 0)) + } else { + aapsLogger.info(LTag.PUMPBTCOMM, "Using pump ID $pumpID") + val oldId = rileyLinkServiceData.pumpID + rileyLinkServiceData.setPumpID(pumpID, pumpIDBytes) + if (oldId != null && oldId != pumpID) { + medtronicUtil.medtronicPumpModel = MedtronicDeviceType.Medtronic_522 // if we change pumpId, model probably changed too + medtronicUtil.isModelSet = false + } + return + } + medtronicPumpStatus.pumpDeviceState = PumpDeviceState.InvalidConfiguration + + // LOG.info("setPumpIDString: saved pumpID " + idString); + } + + inner class LocalBinder : Binder() { + + val serviceInstance: RileyLinkMedtronicService + get() = this@RileyLinkMedtronicService + } + + /* private functions */ // PumpInterface - REMOVE + val isInitialized: Boolean + get() = rileyLinkServiceData.rileyLinkServiceState.isReady + + override fun verifyConfiguration(forceRileyLinkAddressRenewal: Boolean): Boolean { + return try { + val regexSN = "[0-9]{6}" + val regexMac = "([\\da-fA-F]{1,2}(?::|$)){6}" + medtronicPumpStatus.errorDescription = "-" + val serialNr = sp.getStringOrNull(MedtronicConst.Prefs.PumpSerial, null) + if (serialNr == null) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_serial_not_set) + return false + } else { + if (!serialNr.matches(regexSN.toRegex())) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_serial_invalid) + return false + } else { + if (serialNr != medtronicPumpStatus.serialNumber) { + medtronicPumpStatus.serialNumber = serialNr + serialChanged = true + } + } + } + val pumpTypePref = sp.getStringOrNull(MedtronicConst.Prefs.PumpType, null) + if (pumpTypePref == null) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_type_not_set) + return false + } else { + val pumpTypePart = pumpTypePref.substring(0, 3) + if (!pumpTypePart.matches("[0-9]{3}".toRegex())) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_type_invalid) + return false + } else { + val pumpType = medtronicPumpStatus.medtronicPumpMap[pumpTypePart]!! + medtronicPumpStatus.medtronicDeviceType = medtronicPumpStatus.medtronicDeviceTypeMap[pumpTypePart]!! + medtronicPumpStatus.pumpType = pumpType + medtronicPumpPlugin.pumpType = pumpType + if (pumpTypePart.startsWith("7")) medtronicPumpStatus.reservoirFullUnits = 300 else medtronicPumpStatus.reservoirFullUnits = 176 + } + } + val pumpFrequency = sp.getStringOrNull(MedtronicConst.Prefs.PumpFrequency, null) + if (pumpFrequency == null) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_frequency_not_set) + return false + } else { + if (pumpFrequency != frequencies[0] && pumpFrequency != frequencies[1]) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_pump_frequency_invalid) + return false + } else { + medtronicPumpStatus.pumpFrequency = pumpFrequency + val isFrequencyUS = pumpFrequency == frequencies[0] + val newTargetFrequency = if (isFrequencyUS) // + RileyLinkTargetFrequency.Medtronic_US else RileyLinkTargetFrequency.Medtronic_WorldWide + if (rileyLinkServiceData.rileyLinkTargetFrequency != newTargetFrequency) { + rileyLinkServiceData.rileyLinkTargetFrequency = newTargetFrequency + } + } + } + val rileyLinkAddress = sp.getStringOrNull(RileyLinkConst.Prefs.RileyLinkAddress, null) + if (rileyLinkAddress == null) { + aapsLogger.debug(LTag.PUMP, "RileyLink address invalid: null") + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_rileylink_address_invalid) + return false + } else { + if (!rileyLinkAddress.matches(regexMac.toRegex())) { + medtronicPumpStatus.errorDescription = resourceHelper.gs(R.string.medtronic_error_rileylink_address_invalid) + aapsLogger.debug(LTag.PUMP, "RileyLink address invalid: %s", rileyLinkAddress) + } else { + if (rileyLinkAddress != this.rileyLinkAddress) { + this.rileyLinkAddress = rileyLinkAddress + rileyLinkAddressChanged = true + } + } + } + val maxBolusLcl = checkParameterValue(MedtronicConst.Prefs.MaxBolus, "25.0", 25.0) + if (medtronicPumpStatus.maxBolus == null || medtronicPumpStatus.maxBolus != maxBolusLcl) { + medtronicPumpStatus.maxBolus = maxBolusLcl + + //LOG.debug("Max Bolus from AAPS settings is " + maxBolus); + } + val maxBasalLcl = checkParameterValue(MedtronicConst.Prefs.MaxBasal, "35.0", 35.0) + if (medtronicPumpStatus.maxBasal == null || medtronicPumpStatus.maxBasal != maxBasalLcl) { + medtronicPumpStatus.maxBasal = maxBasalLcl + + //LOG.debug("Max Basal from AAPS settings is " + maxBasal); + } + val encodingTypeStr = sp.getStringOrNull(MedtronicConst.Prefs.Encoding, null) + ?: return false + val newEncodingType = RileyLinkEncodingType.getByDescription(encodingTypeStr, resourceHelper) + if (encodingType == null) { + encodingType = newEncodingType + } else if (encodingType != newEncodingType) { + encodingType = newEncodingType + encodingChanged = true + } + val batteryTypeStr = sp.getStringOrNull(MedtronicConst.Prefs.BatteryType, null) + ?: return false + val batteryType = medtronicPumpStatus.getBatteryTypeByDescription(batteryTypeStr) + if (medtronicPumpStatus.batteryType !== batteryType) { + medtronicPumpStatus.batteryType = batteryType!! + } + + //String bolusDebugEnabled = sp.getStringOrNull(MedtronicConst.Prefs.BolusDebugEnabled, null); + //boolean bolusDebug = bolusDebugEnabled != null && bolusDebugEnabled.equals(resourceHelper.gs(R.string.common_on)); + //MedtronicHistoryData.doubleBolusDebug = bolusDebug; + rileyLinkServiceData.showBatteryLevel = sp.getBoolean(RileyLinkConst.Prefs.ShowBatteryLevel, false) + reconfigureService(forceRileyLinkAddressRenewal) + true + } catch (ex: Exception) { + medtronicPumpStatus.errorDescription = ex.message + aapsLogger.error(LTag.PUMP, "Error on Verification: " + ex.message, ex) + false + } + } + + private fun reconfigureService(forceRileyLinkAddressRenewal: Boolean): Boolean { + if (!inPreInit) { + if (serialChanged) { + setPumpIDString(medtronicPumpStatus.serialNumber) // short operation + serialChanged = false + } + if (rileyLinkAddressChanged || forceRileyLinkAddressRenewal) { + rileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkNewAddressSet, this) + rileyLinkAddressChanged = false + } + if (encodingChanged) { + changeRileyLinkEncoding(encodingType) + encodingChanged = false + } + } + + // if (targetFrequencyChanged && !inPreInit && MedtronicUtil.getMedtronicService() != null) { + // RileyLinkUtil.setRileyLinkTargetFrequency(targetFrequency); + // // RileyLinkUtil.getRileyLinkCommunicationManager().refreshRileyLinkTargetFrequency(); + // targetFrequencyChanged = false; + // } + return !rileyLinkAddressChanged && !serialChanged && !encodingChanged // && !targetFrequencyChanged); + } + + private fun checkParameterValue(key: Int, defaultValue: String, defaultValueDouble: Double): Double { + var valueDouble: Double + val value = sp.getString(key, defaultValue) + valueDouble = try { + value.toDouble() + } catch (ex: Exception) { + aapsLogger.error("Error parsing setting: %s, value found %s", key, value) + defaultValueDouble + } + if (valueDouble > defaultValueDouble) { + sp.putString(key, defaultValue) + valueDouble = defaultValueDouble + } + return valueDouble + } + + fun setNotInPreInit(): Boolean { + inPreInit = false + return reconfigureService(false) + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java deleted file mode 100644 index 8a05ad6d35..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java +++ /dev/null @@ -1,39 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.util; - -import info.nightscout.androidaps.plugins.pump.medtronic.R; - -/** - * Created by andy on 5/12/18. - */ - -public class MedtronicConst { - - static final String Prefix = "AAPS.Medtronic."; - - public static class Prefs { - public static final int PumpSerial = R.string.key_medtronic_serial; - public static final int PumpType = R.string.key_medtronic_pump_type; - public static final int PumpFrequency = R.string.key_medtronic_frequency; - public static final int MaxBolus = R.string.key_medtronic_max_bolus; - public static final int MaxBasal = R.string.key_medtronic_max_basal; - public static final int BolusDelay = R.string.key_medtronic_bolus_delay; - public static final int Encoding = R.string.key_medtronic_encoding; - public static final int BatteryType = R.string.key_medtronic_battery_type; - public static final int BolusDebugEnabled = R.string.key_medtronic_bolus_debug; - } - - public static class Statistics { - - public static final String StatsPrefix = "medtronic_"; - public static final String FirstPumpStart = Prefix + "first_pump_use"; - public static final String LastGoodPumpCommunicationTime = Prefix + "lastGoodPumpCommunicationTime"; - public static final String LastGoodPumpFrequency = Prefix + "LastGoodPumpFrequency"; - public static final String TBRsSet = StatsPrefix + "tbrs_set"; - public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered"; - public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered"; - public static final String LastPumpHistoryEntry = StatsPrefix + "pump_history_entry"; - public static final String LastPrime = StatsPrefix + "last_sent_prime"; - public static final String LastRewind = StatsPrefix + "last_sent_rewind"; - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.kt new file mode 100644 index 0000000000..80d284f80a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.kt @@ -0,0 +1,37 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.util + +import info.nightscout.androidaps.plugins.pump.medtronic.R + +/** + * Created by andy on 5/12/18. + */ +object MedtronicConst { + + const val Prefix = "AAPS.Medtronic." + + object Prefs { + @JvmField val PumpSerial = R.string.key_medtronic_serial + @JvmField val PumpType = R.string.key_medtronic_pump_type + @JvmField val PumpFrequency = R.string.key_medtronic_frequency + @JvmField val MaxBolus = R.string.key_medtronic_max_bolus + @JvmField val MaxBasal = R.string.key_medtronic_max_basal + @JvmField val BolusDelay = R.string.key_medtronic_bolus_delay + @JvmField val Encoding = R.string.key_medtronic_encoding + @JvmField val BatteryType = R.string.key_medtronic_battery_type + val BolusDebugEnabled = R.string.key_medtronic_bolus_debug + } + + object Statistics { + const val StatsPrefix = "medtronic_" + const val FirstPumpStart = Prefix + "first_pump_use" + const val LastGoodPumpCommunicationTime = Prefix + "lastGoodPumpCommunicationTime" + const val LastGoodPumpFrequency = Prefix + "LastGoodPumpFrequency" + const val TBRsSet = StatsPrefix + "tbrs_set" + const val StandardBoluses = StatsPrefix + "std_boluses_delivered" + const val SMBBoluses = StatsPrefix + "smb_boluses_delivered" + const val LastPumpHistoryEntry = StatsPrefix + "pump_history_entry" + const val LastPrime = StatsPrefix + "last_sent_prime" + const val LastRewind = StatsPrefix + "last_sent_rewind" + const val InternalTemporaryDatabase = StatsPrefix + "temporary_entries" + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java deleted file mode 100644 index 680896751c..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java +++ /dev/null @@ -1,456 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.util; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import org.joda.time.LocalTime; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -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.notifications.Notification; -import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.RLHistoryItemMedtronic; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -/** - * Created by andy on 5/9/18. - */ - -@Singleton -public class MedtronicUtil { - - private final int ENVELOPE_SIZE = 4; // 0xA7 S1 S2 S3 CMD PARAM_COUNT [PARAMS] - private static final boolean lowLevelDebug = true; - //private MedtronicDeviceType medtronicPumpModel; - private MedtronicCommandType currentCommand; - private Map settings; - private final int BIG_FRAME_LENGTH = 65; - private final int doneBit = 1 << 7; - private ClockDTO pumpTime; - public Gson gsonInstance = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - private final RileyLinkUtil rileyLinkUtil; - private final MedtronicPumpStatus medtronicPumpStatus; - - @Inject - public MedtronicUtil( - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - RileyLinkUtil rileyLinkUtil, - MedtronicPumpStatus medtronicPumpStatus - ) { - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - this.rileyLinkUtil = rileyLinkUtil; - this.medtronicPumpStatus = medtronicPumpStatus; - } - - public LocalTime getTimeFrom30MinInterval(int interval) { - if (interval % 2 == 0) { - return new LocalTime(interval / 2, 0); - } else { - return new LocalTime((interval - 1) / 2, 30); - } - } - - - public static int getIntervalFromMinutes(int minutes) { - return minutes / 30; - } - - - public static int makeUnsignedShort(int b2, int b1) { - int k = (b2 & 0xff) << 8 | b1 & 0xff; - return k; - } - - public static byte[] getByteArrayFromUnsignedShort(int shortValue, boolean returnFixedSize) { - byte highByte = (byte) (shortValue >> 8 & 0xFF); - byte lowByte = (byte) (shortValue & 0xFF); - - if (highByte > 0) { - return createByteArray(highByte, lowByte); - } else { - return returnFixedSize ? createByteArray(highByte, lowByte) : createByteArray(lowByte); - } - - } - - - public static byte[] createByteArray(byte... data) { - return data; - } - - - public static byte[] createByteArray(List data) { - - byte[] array = new byte[data.size()]; - - for (int i = 0; i < data.size(); i++) { - array[i] = data.get(i); - } - - return array; - } - - - public double decodeBasalInsulin(int i, int j) { - return decodeBasalInsulin(makeUnsignedShort(i, j)); - } - - - public double decodeBasalInsulin(int i) { - return (double) i / 40.0d; - } - - - public byte[] getBasalStrokes(double amount) { - return getBasalStrokes(amount, false); - } - - - public static byte[] getBasalStrokes(double amount, boolean returnFixedSize) { - return getStrokes(amount, 40, returnFixedSize); - } - - - public int getBasalStrokesInt(double amount) { - return getStrokesInt(amount, 40); - } - - - public byte[] getBolusStrokes(double amount) { - - int strokesPerUnit = medtronicPumpStatus.medtronicDeviceType.getBolusStrokes(); - - int length; - int scrollRate; - - if (strokesPerUnit >= 40) { - length = 2; - - // 40-stroke pumps scroll faster for higher unit values - - if (amount > 10) - scrollRate = 4; - else if (amount > 1) - scrollRate = 2; - else - scrollRate = 1; - - } else { - length = 1; - scrollRate = 1; - } - - int strokes = (int) (amount * ((strokesPerUnit * 1.0d) / (scrollRate * 1.0d))) * scrollRate; - - byte[] body = ByteUtil.fromHexString(String.format("%02x%0" + (2 * length) + "x", length, strokes)); - - return body; - } - - - public byte[] createCommandBody(byte[] input) { - - return ByteUtil.concat((byte) input.length, input); - } - - - public static byte[] getStrokes(double amount, int strokesPerUnit, boolean returnFixedSize) { - - int strokes = getStrokesInt(amount, strokesPerUnit); - - return getByteArrayFromUnsignedShort(strokes, returnFixedSize); - - } - - - public static int getStrokesInt(double amount, int strokesPerUnit) { - - int length = 1; - int scrollRate = 1; - - if (strokesPerUnit >= 40) { - length = 2; - - // 40-stroke pumps scroll faster for higher unit values - if (amount > 10) - scrollRate = 4; - else if (amount > 1) - scrollRate = 2; - } - - int strokes = (int) (amount * (strokesPerUnit / (scrollRate * 1.0d))); - - strokes *= scrollRate; - - return strokes; - - } - - - public void sendNotification(MedtronicNotificationType notificationType, ResourceHelper resourceHelper, RxBusWrapper rxBus) { - Notification notification = new Notification( // - notificationType.getNotificationType(), // - resourceHelper.gs(notificationType.getResourceId()), // - notificationType.getNotificationUrgency()); - rxBus.send(new EventNewNotification(notification)); - } - - - public void sendNotification(MedtronicNotificationType notificationType, ResourceHelper resourceHelper, RxBusWrapper rxBus, Object... parameters) { - Notification notification = new Notification( // - notificationType.getNotificationType(), // - resourceHelper.gs(notificationType.getResourceId(), parameters), // - notificationType.getNotificationUrgency()); - rxBus.send(new EventNewNotification(notification)); - } - - - public void dismissNotification(MedtronicNotificationType notificationType, RxBusWrapper rxBus) { - rxBus.send(new EventDismissNotification(notificationType.getNotificationType())); - } - - -// public byte[] buildCommandPayload(MessageType commandType, byte[] parameters) { -// return buildCommandPayload(commandType.getValue(), parameters); -// } - - - public byte[] buildCommandPayload(RileyLinkServiceData rileyLinkServiceData, MedtronicCommandType commandType, byte[] parameters) { - return buildCommandPayload(rileyLinkServiceData, commandType.commandCode, parameters); - } - - - public byte[] buildCommandPayload(RileyLinkServiceData rileyLinkServiceData, byte commandType, byte[] parameters) { - // A7 31 65 51 C0 00 52 - - byte commandLength = (byte) (parameters == null ? 2 : 2 + parameters.length); - - ByteBuffer sendPayloadBuffer = ByteBuffer.allocate(ENVELOPE_SIZE + commandLength); // + CRC_SIZE - sendPayloadBuffer.order(ByteOrder.BIG_ENDIAN); - - byte[] serialNumberBCD = rileyLinkServiceData.pumpIDBytes; - - sendPayloadBuffer.put((byte) 0xA7); - sendPayloadBuffer.put(serialNumberBCD[0]); - sendPayloadBuffer.put(serialNumberBCD[1]); - sendPayloadBuffer.put(serialNumberBCD[2]); - - sendPayloadBuffer.put(commandType); - - if (parameters == null) { - sendPayloadBuffer.put((byte) 0x00); - } else { - sendPayloadBuffer.put((byte) parameters.length); // size - - for (byte val : parameters) { - sendPayloadBuffer.put(val); - } - } - - byte[] payload = sendPayloadBuffer.array(); - - aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "buildCommandPayload [%s]", ByteUtil.shortHexString(payload))); - - // int crc = computeCRC8WithPolynomial(payload, 0, payload.length - 1); - - // LOG.info("crc: " + crc); - - // sendPayloadBuffer.put((byte) crc); - - return sendPayloadBuffer.array(); - } - - - // Note: at the moment supported only for 24 items, if you will use it for more than - // that you will need to add - public List> getBasalProfileFrames(byte[] data) { - - boolean done = false; - int start = 0; - int frame = 1; - - List> frames = new ArrayList<>(); - boolean lastFrame = false; - - do { - int frameLength = BIG_FRAME_LENGTH - 1; - - if (start + frameLength > data.length) { - frameLength = data.length - start; - } - - // System.out.println("Framelength: " + frameLength); - - byte[] substring = ByteUtil.substring(data, start, frameLength); - - // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); - // System.out.println("Subarray Lenths: " + substring.length); - - List frameData = ByteUtil.getListFromByteArray(substring); - - if (isEmptyFrame(frameData)) { - byte b = (byte) frame; - // b |= 0x80; - b |= 0b1000_0000; - // b |= doneBit; - - frameData.add(0, b); - - checkAndAppenLastFrame(frameData); - - lastFrame = true; - - done = true; - } else { - frameData.add(0, (byte) frame); - } - - // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); - - frames.add(frameData); - - frame++; - start += (BIG_FRAME_LENGTH - 1); - - if (start == data.length) { - done = true; - } - - } while (!done); - - if (!lastFrame) { - List frameData = new ArrayList<>(); - - byte b = (byte) frame; - b |= 0b1000_0000; - // b |= doneBit; - - frameData.add(b); - - checkAndAppenLastFrame(frameData); - } - - return frames; - - } - - - private void checkAndAppenLastFrame(List frameData) { - - if (frameData.size() == BIG_FRAME_LENGTH) - return; - - int missing = BIG_FRAME_LENGTH - frameData.size(); - - for (int i = 0; i < missing; i++) { - frameData.add((byte) 0x00); - } - } - - - private boolean isEmptyFrame(List frameData) { - - for (Byte frameDateEntry : frameData) { - if (frameDateEntry != 0x00) { - return false; - } - } - - return true; - } - - - public static boolean isLowLevelDebug() { - return lowLevelDebug; - } - - public boolean isModelSet() { - return medtronicPumpStatus.medtronicDeviceType != null; - } - - public MedtronicDeviceType getMedtronicPumpModel() { - return medtronicPumpStatus.medtronicDeviceType; - } - - public void setMedtronicPumpModel(MedtronicDeviceType medtronicPumpModel) { - this.medtronicPumpStatus.medtronicDeviceType = medtronicPumpModel; - } - - public MedtronicCommandType getCurrentCommand() { - return this.currentCommand; - } - - public void setCurrentCommand(MedtronicCommandType currentCommand) { - this.currentCommand = currentCommand; - - if (currentCommand != null) - rileyLinkUtil.getRileyLinkHistory().add(new RLHistoryItemMedtronic(currentCommand)); - - } - - public int pageNumber; - public Integer frameNumber; - - - public void setCurrentCommand(MedtronicCommandType currentCommand, int pageNumber_, Integer frameNumber_) { - pageNumber = pageNumber_; - frameNumber = frameNumber_; - - if (this.currentCommand != currentCommand) { - setCurrentCommand(currentCommand); - } - - rxBus.send(new EventRileyLinkDeviceStatusChange(medtronicPumpStatus.getPumpDeviceState())); - } - - - public static boolean isSame(Double d1, Double d2) { - double diff = d1 - d2; - - return (Math.abs(diff) <= 0.000001); - } - - - public Map getSettings() { - return settings; - } - - public void setSettings(Map settings) { - this.settings = settings; - } - - public void setPumpTime(ClockDTO pumpTime) { - this.pumpTime = pumpTime; - } - - public ClockDTO getPumpTime() { - return this.pumpTime; - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.kt new file mode 100644 index 0000000000..5826a1e0e6 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.kt @@ -0,0 +1,314 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.util + +import com.google.gson.GsonBuilder +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +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.notifications.Notification +import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.RLHistoryItemMedtronic +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.utils.resources.ResourceHelper +import org.joda.time.LocalTime +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.experimental.or + +/** + * Created by andy on 5/9/18. + */ +@Singleton +class MedtronicUtil @Inject constructor( + private val aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + private val rileyLinkUtil: RileyLinkUtil, + private val medtronicPumpStatus: MedtronicPumpStatus +) { + + private val ENVELOPE_SIZE = 4 // 0xA7 S1 S2 S3 CMD PARAM_COUNT [PARAMS] + + //private MedtronicDeviceType medtronicPumpModel; + private var currentCommand: MedtronicCommandType? = null + var settings: Map? = null + private val BIG_FRAME_LENGTH = 65 + private val doneBit = 1 shl 7 + var pumpTime: ClockDTO? = null + var gsonInstance = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() + + fun getTimeFrom30MinInterval(interval: Int): LocalTime { + return if (interval % 2 == 0) { + LocalTime(interval / 2, 0) + } else { + LocalTime((interval - 1) / 2, 30) + } + } + + fun decodeBasalInsulin(i: Int, j: Int): Double { + return decodeBasalInsulin(makeUnsignedShort(i, j)) + } + + fun decodeBasalInsulin(i: Int): Double { + return i.toDouble() / 40.0 + } + + fun getBasalStrokes(amount: Double): ByteArray { + return getBasalStrokes(amount, false) + } + + fun getBasalStrokesInt(amount: Double): Int { + return getStrokesInt(amount, 40) + } + + fun getBolusStrokes(amount: Double): ByteArray { + val strokesPerUnit = medtronicPumpStatus.medtronicDeviceType.bolusStrokes + val length: Int + val scrollRate: Int + if (strokesPerUnit >= 40) { + length = 2 + + // 40-stroke pumps scroll faster for higher unit values + scrollRate = if (amount > 10) 4 else if (amount > 1) 2 else 1 + } else { + length = 1 + scrollRate = 1 + } + val strokes = (amount * (strokesPerUnit * 1.0 / (scrollRate * 1.0))).toInt() * scrollRate + return ByteUtil.fromHexString(String.format("%02x%0" + 2 * length + "x", length, strokes)) + } + + fun createCommandBody(input: ByteArray): ByteArray { + return ByteUtil.concat(input.size.toByte(), input) + } + + fun sendNotification(notificationType: MedtronicNotificationType, resourceHelper: ResourceHelper, rxBus: RxBusWrapper) { + val notification = Notification( // + notificationType.notificationType, // + resourceHelper.gs(notificationType.resourceId), // + notificationType.notificationUrgency) + rxBus.send(EventNewNotification(notification)) + } + + fun sendNotification(notificationType: MedtronicNotificationType, resourceHelper: ResourceHelper, rxBus: RxBusWrapper, vararg parameters: Any?) { + val notification = Notification( // + notificationType.notificationType, // + resourceHelper.gs(notificationType.resourceId, *parameters), // + notificationType.notificationUrgency) + rxBus.send(EventNewNotification(notification)) + } + + fun dismissNotification(notificationType: MedtronicNotificationType, rxBus: RxBusWrapper) { + rxBus.send(EventDismissNotification(notificationType.notificationType)) + } + + fun buildCommandPayload(rileyLinkServiceData: RileyLinkServiceData, commandType: MedtronicCommandType, parameters: ByteArray?): ByteArray { + return buildCommandPayload(rileyLinkServiceData, commandType.commandCode, parameters) + } + + fun buildCommandPayload(rileyLinkServiceData: RileyLinkServiceData, commandType: Byte, parameters: ByteArray?): ByteArray { + // A7 31 65 51 C0 00 52 + val commandLength = (if (parameters == null) 2 else 2 + parameters.size).toByte() + val sendPayloadBuffer = ByteBuffer.allocate(ENVELOPE_SIZE + commandLength) // + CRC_SIZE + sendPayloadBuffer.order(ByteOrder.BIG_ENDIAN) + val serialNumberBCD = rileyLinkServiceData.pumpIDBytes + sendPayloadBuffer.put(0xA7.toByte()) + sendPayloadBuffer.put(serialNumberBCD[0]) + sendPayloadBuffer.put(serialNumberBCD[1]) + sendPayloadBuffer.put(serialNumberBCD[2]) + sendPayloadBuffer.put(commandType) + if (parameters == null) { + sendPayloadBuffer.put(0x00.toByte()) + } else { + sendPayloadBuffer.put(parameters.size.toByte()) // size + for (`val` in parameters) { + sendPayloadBuffer.put(`val`) + } + } + val payload = sendPayloadBuffer.array() + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "buildCommandPayload [%s]", ByteUtil.shortHexString(payload))) + + // int crc = computeCRC8WithPolynomial(payload, 0, payload.length - 1); + + // LOG.info("crc: " + crc); + + // sendPayloadBuffer.put((byte) crc); + return sendPayloadBuffer.array() + } + + // Note: at the moment supported only for 24 items, if you will use it for more than + // that you will need to add + fun getBasalProfileFrames(data: ByteArray): List> { + var done = false + var start = 0 + var frame = 1 + val frames: MutableList> = ArrayList() + var lastFrame = false + do { + var frameLength = BIG_FRAME_LENGTH - 1 + if (start + frameLength > data.size) { + frameLength = data.size - start + } + + // System.out.println("Framelength: " + frameLength); + val substring = ByteUtil.substring(data, start, frameLength) + + // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); + // System.out.println("Subarray Lenths: " + substring.length); + val frameData = ByteUtil.getListFromByteArray(substring) + if (isEmptyFrame(frameData)) { + var b = frame.toByte() + // b |= 0x80; + b = b or 128.toByte() + // b |= doneBit; + frameData.add(0, b) + checkAndAppendLastFrame(frameData) + lastFrame = true + done = true + } else { + frameData.add(0, frame.toByte()) + } + + // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); + frames.add(frameData) + frame++ + start += BIG_FRAME_LENGTH - 1 + if (start == data.size) { + done = true + } + } while (!done) + if (!lastFrame) { + val frameData: MutableList = ArrayList() + var b = frame.toByte() + b = b or 128.toByte() + // b |= doneBit; + frameData.add(b) + checkAndAppendLastFrame(frameData) + } + return frames + } + + private fun checkAndAppendLastFrame(frameData: MutableList) { + if (frameData.size == BIG_FRAME_LENGTH) return + val missing = BIG_FRAME_LENGTH - frameData.size + for (i in 0 until missing) { + frameData.add(0x00.toByte()) + } + } + + private fun isEmptyFrame(frameData: List): Boolean { + for (frameDateEntry in frameData) { + if (frameDateEntry.toInt() != 0x00) { + return false + } + } + return true + } + + var isModelSet: Boolean = false + // get() = medtronicPumpStatus.medtronicDeviceType != null + + var medtronicPumpModel: MedtronicDeviceType + get() = medtronicPumpStatus.medtronicDeviceType + set(medtronicPumpModel) { + medtronicPumpStatus.medtronicDeviceType = medtronicPumpModel + } + + fun getCurrentCommand(): MedtronicCommandType? { + return currentCommand + } + + fun setCurrentCommand(currentCommandIn: MedtronicCommandType?) { + this.currentCommand = currentCommandIn + if (currentCommand != null) rileyLinkUtil.rileyLinkHistory.add(RLHistoryItemMedtronic(currentCommandIn!!)) + } + + var pageNumber = 0 + var frameNumber: Int? = null + + fun setCurrentCommand(currentCommand: MedtronicCommandType, pageNumber_: Int, frameNumber_: Int?) { + pageNumber = pageNumber_ + frameNumber = frameNumber_ + if (this.currentCommand !== currentCommand) { + setCurrentCommand(currentCommand) + } + rxBus.send(EventRileyLinkDeviceStatusChange(medtronicPumpStatus.pumpDeviceState)) + } + + companion object { + + const val isLowLevelDebug = true + fun getIntervalFromMinutes(minutes: Int): Int { + return minutes / 30 + } + + fun makeUnsignedShort(b2: Int, b1: Int): Int { + return b2 and 0xff shl 8 or b1 and 0xff + } + + @JvmStatic + fun getByteArrayFromUnsignedShort(shortValue: Int, returnFixedSize: Boolean): ByteArray { + val highByte = (shortValue shr 8 and 0xFF).toByte() + val lowByte = (shortValue and 0xFF).toByte() + return if (highByte > 0) { + createByteArray(highByte, lowByte) + } else { + if (returnFixedSize) createByteArray(highByte, lowByte) else createByteArray(lowByte) + } + } + + fun createByteArray(vararg data: Byte): ByteArray { + return data + } + + @JvmStatic + fun createByteArray(data: List): ByteArray { + val array = ByteArray(data.size) + for (i in data.indices) { + array[i] = data[i] + } + return array + } + + fun getBasalStrokes(amount: Double, returnFixedSize: Boolean): ByteArray { + return getStrokes(amount, 40, returnFixedSize) + } + + fun getStrokes(amount: Double, strokesPerUnit: Int, returnFixedSize: Boolean): ByteArray { + val strokes = getStrokesInt(amount, strokesPerUnit) + return getByteArrayFromUnsignedShort(strokes, returnFixedSize) + } + + fun getStrokesInt(amount: Double, strokesPerUnit: Int): Int { + //var length = 1 + var scrollRate = 1 + if (strokesPerUnit >= 40) { + // length = 2 + + // 40-stroke pumps scroll faster for higher unit values + if (amount > 10) scrollRate = 4 else if (amount > 1) scrollRate = 2 + } + var strokes = (amount * (strokesPerUnit / (scrollRate * 1.0))).toInt() + strokes *= scrollRate + return strokes + } + + @JvmStatic + fun isSame(d1: Double, d2: Double): Boolean { + val diff = d1 - d2 + return Math.abs(diff) <= 0.000001 + } + } + +} \ No newline at end of file diff --git a/medtronic/src/main/res/values/strings.xml b/medtronic/src/main/res/values/strings.xml index 32715ce7b4..56d216e9cf 100644 --- a/medtronic/src/main/res/values/strings.xml +++ b/medtronic/src/main/res/values/strings.xml @@ -95,13 +95,18 @@ Get History - Page %1$d (%2$d/16) Get History - Page %1$d Get Pump Time + Set Pump Time + Get Battery Status + Get Settings Get Pump Model Get Basal Profile Set Basal Profile Get Temporary Basal Set Temporary Basal + Cancel Temporary Basal Set Bolus + Get Remaining Insulin Pump unreachable Warning @@ -115,8 +120,7 @@ set_neutral_temps Set neutral temp basals If enabled, it will cancel a temporary basal before the end of each hour. This method can help stop some pumps beeping/vibrating on the hour. - - + %1$.1f U/h (%2$d min remaining) ^\\d{6} \ No newline at end of file diff --git a/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java b/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java deleted file mode 100644 index 14a4c2926a..0000000000 --- a/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.java +++ /dev/null @@ -1,234 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.util.List; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.AAPSLoggerTest; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; -import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.rx.TestAapsSchedulers; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -/** - * Created by andy on 11/1/18. - */ -@RunWith(PowerMockRunner.class) -public class MedtronicPumpHistoryDecoderUTest { - - @Mock HasAndroidInjector injector; - AAPSLogger aapsLogger = new AAPSLoggerTest(); - RxBusWrapper rxBusWrapper = new RxBusWrapper(new TestAapsSchedulers()); - @Mock ResourceHelper resourceHelper; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) ActivePlugin activePlugin; - @Mock RileyLinkUtil rileyLinkUtil; - @Mock SP sp; - MedtronicPumpStatus medtronicPumpStatus; - MedtronicUtil medtronicUtil; - MedtronicPumpHistoryDecoder decoder; - - - @Before - public void setup() { - medtronicPumpStatus = new MedtronicPumpStatus(resourceHelper, sp, rxBusWrapper, rileyLinkUtil); - medtronicUtil = new MedtronicUtil(aapsLogger, rxBusWrapper, rileyLinkUtil, medtronicPumpStatus); - decoder = new MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil); - } - - - -/* - - // @Test - public void decodeRecord() throws Exception { - - byte[] data = new byte[] { 0x07, 0x00, 0x00, 0x05, (byte)0xFA, (byte)0xBF, 0x12 }; - - PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(0x07); - - PumpHistoryEntry phe = new PumpHistoryEntry(); - phe.setEntryType(entryType); - phe.setData(ByteUtil.getListFromByteArray(data), false); - - decoder.decodeRecord(phe); - - System.out.println("Record: " + phe); - - } - - - // @Test - public void decodeLowAmount() { - byte[] data = new byte[] { 52, -12, 22, -81, 46, 3, 19 }; - - PumpHistoryEntryGroup.doNotTranslate = true; - PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(52); - - PumpHistoryEntry phe = new PumpHistoryEntry(); - phe.setEntryType(entryType); - phe.setData(ByteUtil.getListFromByteArray(data), false); - - decoder.decodeRecord(phe); - - System.out.println("Record: " + phe); - System.out.println("Record: " + phe.getDisplayableValue()); - - } - - - // @Test - public void decodeDailyTotals522() { - // PumpHistoryRecord [type=DailyTotals522 [109, 0x6D], DT: 01.11.2018 00:00:00, length=1,2,41(44), data={Raw - // Data=0x6D 0xA1 0x92 0x05 0x0C 0x00 0xE8 0x00 0x00 0x00 0x00 0x04 0x0A 0x04 0x0A 0x64 0x00 0x00 0x00 0x00 0x00 - // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0C 0x00 0xE8 0x00 0x00 - // 0x00}] - byte[] data4443 = new byte[] { - 0x6D, (byte)0xA1, (byte)0x92, 0x05, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0A, 0x04, 0x0A, - 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00 }; - - byte[] data = new byte[] { - 0x6D, (byte)0xA2, (byte)0x92, 0x05, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x18, 0x02, - (byte)0xD4, 0x5B, 0x00, 0x44, 0x09, 0x00, 0x00, 0x00, 0x44, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, 0x64, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00 }; - - // basal 18.1, bolus 1.7 manual = 1.7 - // All (bg low hi, number Bgs, Sen Lo/Hi, Sens Cal/Data, Basal, Bolus, Carbs, Fodd, Corr, Manual=1.7, food/corr, - // Food+corr, manual bolus=1 - testRecord(data); - - } - - - // @Test - public void decodeDailyTotals515() { - - byte[] data = ByteUtil - .createByteArrayFromHexString("0x6C 0x17 0x93 0x06 0x08 0x00 0x2B 0x00 0x00 0x00 0x00 0x04 0x24 0x03 0x7C 0x54 0x00 0xA8 0x10 0x00 0x00 0x00 0xA8 0x10" - + " 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xA8 0x64 0x03 0x00 0x00"); - - // 0x6C 0x17 0x93 0x06 0x08 0x00 0x2B 0x00 0x00 0x00 0x00 0x04 0x24 0x03 0x7C 0x54 0x00 0xA8 0x10 0x00 0x00 0x00 - // 0xA8 0x10 - // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xA8 0x64 0x03 0x00 0x00 - - // Delivery Stats: BG AVG: Bg Low/Hi=none,Number BGs=0 - // Delivery Stats: INSULIN: Basal 22.30, Bolus=4.20, Catbs = 0g - // Delivery Stats: BOLUS: Food=0.00, Corr=0.00, Manual=4.20 - // Delivery Stats: NUM BOLUS: Food/Corr=0,Food+Corr=0, Manual=3 - - testRecord(data); - - } - - - // @Test - public void decodeDailyTotals523() { - - byte[] data = new byte[] { - 0x6E, (byte)0xB1, (byte)0x92, 0x05, 0x00, (byte)0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, (byte)0x9A, 0x00, - 0x50, 0x34, 0x00, 0x4A, 0x30, 0x00, 0x0B, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x80, (byte)0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }; - - // Carbs=11, total=3.850,basal=2.000, bolus=1.850, basal 52%, blus=48%, Manual=0.95, #manual=5, - // Food only=0.9, #Food Only=1,Corr Only =0, #Corr only=0,Food+Corr=0 - - // Delivery Stats: Carbs=11, Total Insulin=3.850, Basal=2.000 - // Delivery Stats: Basal 52,Bolus 1.850, Bolus=48%o - // Delivery Stats: Food only=0.9, Food only#=1, Corr only = 0.0 - // Delivery Stats: #Corr_only=0,Food+Corr=0.000, #Food+Corr=0 - // Delivery Stats: Manual = 0.95, #Manual=5 - - testRecord(data); - - } - - - private void testRecord(byte[] data) { - // byte[] data = new byte[] { 0x07, 0x00, 0x00, 0x05, (byte)0xFA, (byte)0xBF, 0x12 }; - - PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(data[0]); - - PumpHistoryEntry phe = new PumpHistoryEntry(); - phe.setEntryType(entryType); - phe.setData(ByteUtil.getListFromByteArray(data), false); - - System.out.println("EntryType: " + entryType); - - decoder.decodeRecord(phe); - - System.out.println("Record: " + phe); - - } -*/ - - - @Test - public void historyProblem_148_amunhateb() throws Exception { - List pumpHistoryEntries = readAndParseHistoryPage("5A 0F 20 F4 0C 03 15 19 11 00 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1A 11 00 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 01 12 12 00 25 DE 2D 43 15 01 50 50 00 26 EA 2D 43 15 01 4B 4B 00 2C C9 34 43 15 62 00 2F CB 17 03 15 01 33 33 00 16 DE 37 43 15 07 00 00 07 FE 23 95 6D 23 95 0A 08 00 2B 00 00 00 00 07 FE 03 8E 2C 04 70 38 00 00 04 70 38 00 00 00 00 00 00 04 70 64 06 00 00 00 06 08 00 2B 00 00 00 2C A0 2F E3 01 04 15 33 00 2F E7 04 44 15 00 16 03 2F E7 04 44 15 33 28 3B C2 06 44 15 00 16 01 3B C2 06 44 15 08 08 17 DB 0B 44 15 00 26 00 06 26 00 0C 26 00 12 28 00 18 26 00 1E 26 00 24 24 00 2A 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 18 17 DB 0B 44 15 00 26 00 02 26 00 04 26 00 06 24 00 08 24 00 0A 24 00 0C 26 00 0E 26 00 10 26 00 12 28 00 14 28 00 16 28 00 18 26 00 1A 26 00 1C 26 00 1E 26 00 20 26 00 22 26 00 24 24 00 26 24 00 28 24 00 2A 26 00 2C 26 00 2E 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 45 45 00 28 E9 2B 44 15 19 00 00 C1 0D 04 15 1A 00 15 C3 0D 04 15 1A 01 33 C3 0D 04 15 01 28 28 00 07 CC 2E 44 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 2D", - MedtronicDeviceType.Medtronic_522_722); - - Assert.assertEquals(20, pumpHistoryEntries.size()); - } - - - @Test - public void historyProblem_423_duzy78() throws Exception { - List pumpHistoryEntries = readAndParseHistoryPage("16 00 08 D0 0E 51 15 33 60 0A D0 0E 51 15 00 16 01 0A D0 0E 51 15 33 00 07 DF 0E 51 15 00 16 00 07 DF 0E 51 15 33 6C 09 DF 0E 51 15 00 16 01 09 DF 0E 51 15 33 00 25 ED 0E 51 15 00 16 00 25 ED 0E 51 15 33 2C 27 ED 0E 51 15 00 16 01 27 ED 0E 51 15 33 00 07 F4 0E 51 15 00 16 00 07 F4 0E 51 15 33 00 09 F4 0E 51 15 00 16 01 09 F4 0E 51 15 33 2C 25 D5 0F 51 15 00 16 01 25 D5 0F 51 15 01 3C 3C 00 30 D5 4F 51 15 33 2C 25 F7 0F 51 15 00 16 01 25 F7 0F 51 15 33 00 07 C6 10 51 15 00 16 00 07 C6 10 51 15 33 30 09 C6 10 51 15 00 16 01 09 C6 10 51 15 33 30 25 E8 10 51 15 00 16 01 25 E8 10 51 15 33 30 24 CF 11 51 15 00 16 01 24 CF 11 51 15 33 00 23 E4 11 51 15 00 16 00 23 E4 11 51 15 33 3C 25 E4 11 51 15 00 16 01 25 E4 11 51 15 33 00 23 E8 11 51 15 00 16 00 23 E8 11 51 15 33 4A 25 E8 11 51 15 00 16 01 25 E8 11 51 15 33 00 19 EE 11 51 15 00 16 00 19 EE 11 51 15 33 30 1B EE 11 51 15 00 16 01 1B EE 11 51 15 33 00 23 F2 11 51 15 00 16 00 23 F2 11 51 15 33 3E 25 F2 11 51 15 00 16 01 25 F2 11 51 15 33 00 24 C0 12 51 15 00 16 00 24 C0 12 51 15 33 5E 25 C0 12 51 15 00 16 01 25 C0 12 51 15 33 00 23 CF 12 51 15 00 16 00 23 CF 12 51 15 33 64 25 CF 12 51 15 00 16 01 25 CF 12 51 15 33 00 23 D9 12 51 15 00 16 00 23 D9 12 51 15 33 6A 25 D9 12 51 15 00 16 01 25 D9 12 51 15 33 00 23 E9 12 51 15 00 16 00 23 E9 12 51 15 33 30 25 E9 12 51 15 00 16 01 25 E9 12 51 15 01 16 16 00 10 C2 53 51 15 33 30 24 CF 13 51 15 00 16 01 24 CF 13 51 15 33 30 04 EE 13 51 15 00 16 01 04 EE 13 51 15 01 14 14 00 3B F0 53 51 15 33 00 22 C0 14 51 15 00 16 00 22 C0 14 51 15 33 22 24 C0 14 51 15 00 16 01 24 C0 14 51 15 33 22 03 DF 14 51 15 00 16 01 03 DF 14 51 15 1E 00 37 E1 14 11 15 1F 00 01 EE 14 11 15 33 22 03 C6 15 51 15 00 16 01 03 C6 15 51 15 33 00 20 D9 15 51 15 00 16 00 20 D9 15 51 15 33 34 22 D9 15 51 15 00 16 01 22 D9 15 51 15 39 14 0E DF 35 71 15 83 92 40 01 0B 0B 00 37 E0 55 51 15 33 00 21 E3 15 51 15 00 16 00 21 E3 15 51 15 33 22 22 E3 15 51 15 00 16 01 22 E3 15 51 15 33 00 21 E8 15 51 15 00 16 00 21 E8 15 51 15 33 30 23 E8 15 51 15 00 16 01 23 E8 15 51 15 33 00 20 ED 15 51 15 00 16 00 20 ED 15 51 15 33 22 22 ED 15 51 15 00 16 01 22 ED 15 51 15 33 00 03 F8 15 51 15 00 16 00 03 F8 15 51 15 33 32 05 F8 15 51 15 00 16 01 05 F8 15 51 15 33 00 01 CB 16 51 15 00 16 00 01 CB 16 51 15 33 20 03 CB 16 51 15 00 16 01 03 CB 16 51 15 33 2A 20 ED 16 51 15 00 16 01 20 ED 16 51 15 33 00 02 F8 16 51 15 00 16 00 02 F8 16 51 15 33 2C 04 F8 16 51 15 00 16 01 04 F8 16 51 15 33 00 1F CA 17 51 15 00 16 00 1F CA 17 51 15 33 34 21 CA 17 51 15 00 16 01 21 CA 17 51 15 33 00 1F D4 17 51 15 00 16 00 1F D4 17 51 15 33 38 21 D4 17 51 15 00 16 01 21 D4 17 51 15 33 00 15 EE 17 51 15 00 16 00 15 EE 17 51 15 33 42 17 EE 17 51 15 00 16 01 17 EE 17 51 15 07 00 00 08 0A 31 95 6C 31 95 05 00 A1 A1 A1 01 00 00 08 0A 04 8E 39 03 7C 2B 00 00 03 7C 2B 00 00 00 00 00 00 03 7C 64 07 00 00 00 07 33 00 05 C1 00 52 15 00 16 00 05 C1 00 52 15 33 50 07 C1 00 52 15 00 16 01 07 C1 00 52 15 33 00 01 CB 00 52 15 00 16 00 01 CB 00 52 15 33 26 03 CB 00 52 15 00 16 01 03 CB 00 52 15 33 00 1E DE 00 52 15 00 00 00 8F 0E", - MedtronicDeviceType.Medtronic_515_715); - - Assert.assertEquals(131, pumpHistoryEntries.size()); - } - - @Test - public void historyProblem_476_OpossumGit() throws Exception { - List pumpHistoryEntries = readAndParseHistoryPage("08 07 50 05 0D 4D 15 00 18 00 08 14 00 0E 10 00 14 08 00 1E 12 00 26 16 00 2B 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 18 50 05 0D 4D 15 00 18 00 02 18 00 04 18 00 06 18 00 08 14 00 0A 14 00 0C 14 00 0E 10 00 10 10 00 12 10 00 14 08 00 16 08 00 18 08 00 1A 08 00 1C 08 00 1E 14 00 20 14 00 22 14 00 24 14 00 26 16 00 28 16 00 2A 16 00 2C 1C 00 2E 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 04 04 00 44 09 4D 4D 15 33 06 57 15 0D 4D 15 00 16 01 57 15 0D 4D 15 33 00 77 15 0D 4D 15 00 16 00 77 15 0D 4D 15 33 00 5D 16 0D 4D 15 00 16 04 5D 16 0D 4D 15 33 00 61 1A 0D 4D 15 00 16 00 61 1A 0D 4D 15 5D 00 5E 31 0D 0D 15 1A 00 6E 31 0D 0D 15 06 03 04 D2 6E 31 6D 0D 15 0C 03 11 40 00 01 05 64 01 13 40 00 01 05 17 00 0A 41 00 01 05 18 00 40 39 15 0D 15 21 00 53 04 16 0D 15 03 00 00 00 58 57 09 36 0D 15 5D 01 78 0A 16 0D 15 64 01 78 0A 16 8D 15 2C 68 78 0A 16 8D 15 24 3C 78 0A 16 8D 15 63 02 78 0A 16 8D 15 1B 12 78 0A 16 8D 15 65 61 78 0A 16 8D 15 61 00 78 0A 16 8D 15 32 0E 78 0A 16 8D 15 66 00 78 0A 16 8D 15 3C 01 78 0A 16 8D 15 3D 88 32 93 00 00 00 3E 00 00 00 00 00 00 26 01 78 0A 16 8D 15 27 01 B2 08 00 00 00 28 00 00 00 00 00 00 60 00 78 0A 16 4D 15 23 00 78 0A 16 8D 15 5E 01 78 0A 16 8D 15 2D 01 78 0A 16 8D 15 5A 0F 78 0A 16 8D 15 49 13 00 07 12 0A 1E 0B 2A 0A 00 00 00 00 00 00 00 00 00 23 08 27 2C 23 00 00 00 00 00 00 00 00 00 00 00 32 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 13 00 07 12 0A 1E 0B 2A 0A 00 00 00 00 00 00 00 00 00 23 08 27 2C 23 00 00 00 00 00 00 00 00 00 00 00 32 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 62 00 78 0A 16 8D 15 5F 51 78 0A 16 8D 15 4F 00 78 0A 16 8D 15 40 01 00 6F 1C 16 1E 00 3C 14 00 1E 3C 1F 15 70 40 01 00 6F 1C 16 1E 00 3C 14 00 1E 3C 1F 15 70 08 18 78 0A 16 8D 15 00 18 00 02 18 00 04 18 00 06 18 00 08 14 00 0A 14 00 0C 14 00 0E 10 00 10 10 00 12 10 00 14 08 00 16 08 00 18 08 00 1A 08 00 1C 08 00 1E 14 00 20 14 00 22 14 00 24 14 00 26 16 00 28 16 00 2A 16 00 2C 1C 00 2E 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 06", - MedtronicDeviceType.Medtronic_522_722); - - Assert.assertEquals(41, pumpHistoryEntries.size()); - } - - - private List readAndParseHistoryPage(String historyPageString, MedtronicDeviceType medtronicDeviceType) throws Exception { - byte[] historyPageData = ByteUtil.createByteArrayFromString(historyPageString); - - aapsLogger.debug("History Page Length:" + historyPageData.length); - - medtronicUtil.setMedtronicPumpModel(medtronicDeviceType); - - RawHistoryPage historyPage = new RawHistoryPage(aapsLogger); - historyPage.appendData(historyPageData); - - List pumpHistoryEntries = decoder.processPageAndCreateRecords(historyPage); - - displayHistoryRecords(pumpHistoryEntries); - - return pumpHistoryEntries; - } - - - private void displayHistoryRecords(List pumpHistoryEntries) { - aapsLogger.debug("PumpHistoryEntries: " + pumpHistoryEntries.size()); - - for (PumpHistoryEntry pumpHistoryEntry : pumpHistoryEntries) { - aapsLogger.debug(pumpHistoryEntry.toString()); - } - } - - -} diff --git a/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.kt b/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.kt new file mode 100644 index 0000000000..d94c453c95 --- /dev/null +++ b/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoderUTest.kt @@ -0,0 +1,217 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.AAPSLoggerTest +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.TestAapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.Mock +import org.powermock.modules.junit4.PowerMockRunner + +/** + * Created by andy on 11/1/18. + */ +@RunWith(PowerMockRunner::class) +class MedtronicPumpHistoryDecoderUTest { + + @Mock + var injector: HasAndroidInjector? = null + var aapsLogger: AAPSLogger = AAPSLoggerTest() + var rxBusWrapper = RxBusWrapper(TestAapsSchedulers()) + + @Mock + var resourceHelper: ResourceHelper? = null + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + var activePlugin: ActivePlugin? = null + + @Mock + var rileyLinkUtil: RileyLinkUtil? = null + + @Mock + var sp: SP? = null + var medtronicPumpStatus: MedtronicPumpStatus? = null + var medtronicUtil: MedtronicUtil? = null + var decoder: MedtronicPumpHistoryDecoder? = null + @Before fun setup() { + medtronicPumpStatus = MedtronicPumpStatus(resourceHelper!!, sp!!, rxBusWrapper, rileyLinkUtil!!) + medtronicUtil = MedtronicUtil(aapsLogger, rxBusWrapper, rileyLinkUtil!!, medtronicPumpStatus!!) + decoder = MedtronicPumpHistoryDecoder(aapsLogger, medtronicUtil!!, ByteUtil()) + } + + /* + + // @Test + public void decodeRecord() throws Exception { + + byte[] data = new byte[] { 0x07, 0x00, 0x00, 0x05, (byte)0xFA, (byte)0xBF, 0x12 }; + + PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(0x07); + + PumpHistoryEntry phe = new PumpHistoryEntry(); + phe.setEntryType(entryType); + phe.setData(ByteUtil.getListFromByteArray(data), false); + + decoder.decodeRecord(phe); + + System.out.println("Record: " + phe); + + } + + + // @Test + public void decodeLowAmount() { + byte[] data = new byte[] { 52, -12, 22, -81, 46, 3, 19 }; + + PumpHistoryEntryGroup.doNotTranslate = true; + PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(52); + + PumpHistoryEntry phe = new PumpHistoryEntry(); + phe.setEntryType(entryType); + phe.setData(ByteUtil.getListFromByteArray(data), false); + + decoder.decodeRecord(phe); + + System.out.println("Record: " + phe); + System.out.println("Record: " + phe.getDisplayableValue()); + + } + + + // @Test + public void decodeDailyTotals522() { + // PumpHistoryRecord [type=DailyTotals522 [109, 0x6D], DT: 01.11.2018 00:00:00, length=1,2,41(44), data={Raw + // Data=0x6D 0xA1 0x92 0x05 0x0C 0x00 0xE8 0x00 0x00 0x00 0x00 0x04 0x0A 0x04 0x0A 0x64 0x00 0x00 0x00 0x00 0x00 + // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0C 0x00 0xE8 0x00 0x00 + // 0x00}] + byte[] data4443 = new byte[] { + 0x6D, (byte)0xA1, (byte)0x92, 0x05, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0A, 0x04, 0x0A, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00 }; + + byte[] data = new byte[] { + 0x6D, (byte)0xA2, (byte)0x92, 0x05, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x18, 0x02, + (byte)0xD4, 0x5B, 0x00, 0x44, 0x09, 0x00, 0x00, 0x00, 0x44, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x64, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x00, (byte)0xE8, 0x00, 0x00, 0x00 }; + + // basal 18.1, bolus 1.7 manual = 1.7 + // All (bg low hi, number Bgs, Sen Lo/Hi, Sens Cal/Data, Basal, Bolus, Carbs, Fodd, Corr, Manual=1.7, food/corr, + // Food+corr, manual bolus=1 + testRecord(data); + + } + + + // @Test + public void decodeDailyTotals515() { + + byte[] data = ByteUtil + .createByteArrayFromHexString("0x6C 0x17 0x93 0x06 0x08 0x00 0x2B 0x00 0x00 0x00 0x00 0x04 0x24 0x03 0x7C 0x54 0x00 0xA8 0x10 0x00 0x00 0x00 0xA8 0x10" + + " 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xA8 0x64 0x03 0x00 0x00"); + + // 0x6C 0x17 0x93 0x06 0x08 0x00 0x2B 0x00 0x00 0x00 0x00 0x04 0x24 0x03 0x7C 0x54 0x00 0xA8 0x10 0x00 0x00 0x00 + // 0xA8 0x10 + // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xA8 0x64 0x03 0x00 0x00 + + // Delivery Stats: BG AVG: Bg Low/Hi=none,Number BGs=0 + // Delivery Stats: INSULIN: Basal 22.30, Bolus=4.20, Catbs = 0g + // Delivery Stats: BOLUS: Food=0.00, Corr=0.00, Manual=4.20 + // Delivery Stats: NUM BOLUS: Food/Corr=0,Food+Corr=0, Manual=3 + + testRecord(data); + + } + + + // @Test + public void decodeDailyTotals523() { + + byte[] data = new byte[] { + 0x6E, (byte)0xB1, (byte)0x92, 0x05, 0x00, (byte)0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, (byte)0x9A, 0x00, + 0x50, 0x34, 0x00, 0x4A, 0x30, 0x00, 0x0B, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0x80, (byte)0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + + // Carbs=11, total=3.850,basal=2.000, bolus=1.850, basal 52%, blus=48%, Manual=0.95, #manual=5, + // Food only=0.9, #Food Only=1,Corr Only =0, #Corr only=0,Food+Corr=0 + + // Delivery Stats: Carbs=11, Total Insulin=3.850, Basal=2.000 + // Delivery Stats: Basal 52,Bolus 1.850, Bolus=48%o + // Delivery Stats: Food only=0.9, Food only#=1, Corr only = 0.0 + // Delivery Stats: #Corr_only=0,Food+Corr=0.000, #Food+Corr=0 + // Delivery Stats: Manual = 0.95, #Manual=5 + + testRecord(data); + + } + + + private void testRecord(byte[] data) { + // byte[] data = new byte[] { 0x07, 0x00, 0x00, 0x05, (byte)0xFA, (byte)0xBF, 0x12 }; + + PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(data[0]); + + PumpHistoryEntry phe = new PumpHistoryEntry(); + phe.setEntryType(entryType); + phe.setData(ByteUtil.getListFromByteArray(data), false); + + System.out.println("EntryType: " + entryType); + + decoder.decodeRecord(phe); + + System.out.println("Record: " + phe); + + } +*/ + @Test @Throws(Exception::class) fun historyProblem_148_amunhateb() { + val pumpHistoryEntries = readAndParseHistoryPage("5A 0F 20 F4 0C 03 15 19 11 00 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1A 11 00 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 01 12 12 00 25 DE 2D 43 15 01 50 50 00 26 EA 2D 43 15 01 4B 4B 00 2C C9 34 43 15 62 00 2F CB 17 03 15 01 33 33 00 16 DE 37 43 15 07 00 00 07 FE 23 95 6D 23 95 0A 08 00 2B 00 00 00 00 07 FE 03 8E 2C 04 70 38 00 00 04 70 38 00 00 00 00 00 00 04 70 64 06 00 00 00 06 08 00 2B 00 00 00 2C A0 2F E3 01 04 15 33 00 2F E7 04 44 15 00 16 03 2F E7 04 44 15 33 28 3B C2 06 44 15 00 16 01 3B C2 06 44 15 08 08 17 DB 0B 44 15 00 26 00 06 26 00 0C 26 00 12 28 00 18 26 00 1E 26 00 24 24 00 2A 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 18 17 DB 0B 44 15 00 26 00 02 26 00 04 26 00 06 24 00 08 24 00 0A 24 00 0C 26 00 0E 26 00 10 26 00 12 28 00 14 28 00 16 28 00 18 26 00 1A 26 00 1C 26 00 1E 26 00 20 26 00 22 26 00 24 24 00 26 24 00 28 24 00 2A 26 00 2C 26 00 2E 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 45 45 00 28 E9 2B 44 15 19 00 00 C1 0D 04 15 1A 00 15 C3 0D 04 15 1A 01 33 C3 0D 04 15 01 28 28 00 07 CC 2E 44 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 2D", + MedtronicDeviceType.Medtronic_522_722) + Assert.assertEquals(20, pumpHistoryEntries.size.toLong()) + } + + @Test @Throws(Exception::class) fun historyProblem_423_duzy78() { + val pumpHistoryEntries = readAndParseHistoryPage("16 00 08 D0 0E 51 15 33 60 0A D0 0E 51 15 00 16 01 0A D0 0E 51 15 33 00 07 DF 0E 51 15 00 16 00 07 DF 0E 51 15 33 6C 09 DF 0E 51 15 00 16 01 09 DF 0E 51 15 33 00 25 ED 0E 51 15 00 16 00 25 ED 0E 51 15 33 2C 27 ED 0E 51 15 00 16 01 27 ED 0E 51 15 33 00 07 F4 0E 51 15 00 16 00 07 F4 0E 51 15 33 00 09 F4 0E 51 15 00 16 01 09 F4 0E 51 15 33 2C 25 D5 0F 51 15 00 16 01 25 D5 0F 51 15 01 3C 3C 00 30 D5 4F 51 15 33 2C 25 F7 0F 51 15 00 16 01 25 F7 0F 51 15 33 00 07 C6 10 51 15 00 16 00 07 C6 10 51 15 33 30 09 C6 10 51 15 00 16 01 09 C6 10 51 15 33 30 25 E8 10 51 15 00 16 01 25 E8 10 51 15 33 30 24 CF 11 51 15 00 16 01 24 CF 11 51 15 33 00 23 E4 11 51 15 00 16 00 23 E4 11 51 15 33 3C 25 E4 11 51 15 00 16 01 25 E4 11 51 15 33 00 23 E8 11 51 15 00 16 00 23 E8 11 51 15 33 4A 25 E8 11 51 15 00 16 01 25 E8 11 51 15 33 00 19 EE 11 51 15 00 16 00 19 EE 11 51 15 33 30 1B EE 11 51 15 00 16 01 1B EE 11 51 15 33 00 23 F2 11 51 15 00 16 00 23 F2 11 51 15 33 3E 25 F2 11 51 15 00 16 01 25 F2 11 51 15 33 00 24 C0 12 51 15 00 16 00 24 C0 12 51 15 33 5E 25 C0 12 51 15 00 16 01 25 C0 12 51 15 33 00 23 CF 12 51 15 00 16 00 23 CF 12 51 15 33 64 25 CF 12 51 15 00 16 01 25 CF 12 51 15 33 00 23 D9 12 51 15 00 16 00 23 D9 12 51 15 33 6A 25 D9 12 51 15 00 16 01 25 D9 12 51 15 33 00 23 E9 12 51 15 00 16 00 23 E9 12 51 15 33 30 25 E9 12 51 15 00 16 01 25 E9 12 51 15 01 16 16 00 10 C2 53 51 15 33 30 24 CF 13 51 15 00 16 01 24 CF 13 51 15 33 30 04 EE 13 51 15 00 16 01 04 EE 13 51 15 01 14 14 00 3B F0 53 51 15 33 00 22 C0 14 51 15 00 16 00 22 C0 14 51 15 33 22 24 C0 14 51 15 00 16 01 24 C0 14 51 15 33 22 03 DF 14 51 15 00 16 01 03 DF 14 51 15 1E 00 37 E1 14 11 15 1F 00 01 EE 14 11 15 33 22 03 C6 15 51 15 00 16 01 03 C6 15 51 15 33 00 20 D9 15 51 15 00 16 00 20 D9 15 51 15 33 34 22 D9 15 51 15 00 16 01 22 D9 15 51 15 39 14 0E DF 35 71 15 83 92 40 01 0B 0B 00 37 E0 55 51 15 33 00 21 E3 15 51 15 00 16 00 21 E3 15 51 15 33 22 22 E3 15 51 15 00 16 01 22 E3 15 51 15 33 00 21 E8 15 51 15 00 16 00 21 E8 15 51 15 33 30 23 E8 15 51 15 00 16 01 23 E8 15 51 15 33 00 20 ED 15 51 15 00 16 00 20 ED 15 51 15 33 22 22 ED 15 51 15 00 16 01 22 ED 15 51 15 33 00 03 F8 15 51 15 00 16 00 03 F8 15 51 15 33 32 05 F8 15 51 15 00 16 01 05 F8 15 51 15 33 00 01 CB 16 51 15 00 16 00 01 CB 16 51 15 33 20 03 CB 16 51 15 00 16 01 03 CB 16 51 15 33 2A 20 ED 16 51 15 00 16 01 20 ED 16 51 15 33 00 02 F8 16 51 15 00 16 00 02 F8 16 51 15 33 2C 04 F8 16 51 15 00 16 01 04 F8 16 51 15 33 00 1F CA 17 51 15 00 16 00 1F CA 17 51 15 33 34 21 CA 17 51 15 00 16 01 21 CA 17 51 15 33 00 1F D4 17 51 15 00 16 00 1F D4 17 51 15 33 38 21 D4 17 51 15 00 16 01 21 D4 17 51 15 33 00 15 EE 17 51 15 00 16 00 15 EE 17 51 15 33 42 17 EE 17 51 15 00 16 01 17 EE 17 51 15 07 00 00 08 0A 31 95 6C 31 95 05 00 A1 A1 A1 01 00 00 08 0A 04 8E 39 03 7C 2B 00 00 03 7C 2B 00 00 00 00 00 00 03 7C 64 07 00 00 00 07 33 00 05 C1 00 52 15 00 16 00 05 C1 00 52 15 33 50 07 C1 00 52 15 00 16 01 07 C1 00 52 15 33 00 01 CB 00 52 15 00 16 00 01 CB 00 52 15 33 26 03 CB 00 52 15 00 16 01 03 CB 00 52 15 33 00 1E DE 00 52 15 00 00 00 8F 0E", + MedtronicDeviceType.Medtronic_515_715) + Assert.assertEquals(131, pumpHistoryEntries.size.toLong()) + } + + @Test @Throws(Exception::class) fun historyProblem_476_OpossumGit() { + val pumpHistoryEntries = readAndParseHistoryPage("08 07 50 05 0D 4D 15 00 18 00 08 14 00 0E 10 00 14 08 00 1E 12 00 26 16 00 2B 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 18 50 05 0D 4D 15 00 18 00 02 18 00 04 18 00 06 18 00 08 14 00 0A 14 00 0C 14 00 0E 10 00 10 10 00 12 10 00 14 08 00 16 08 00 18 08 00 1A 08 00 1C 08 00 1E 14 00 20 14 00 22 14 00 24 14 00 26 16 00 28 16 00 2A 16 00 2C 1C 00 2E 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 04 04 00 44 09 4D 4D 15 33 06 57 15 0D 4D 15 00 16 01 57 15 0D 4D 15 33 00 77 15 0D 4D 15 00 16 00 77 15 0D 4D 15 33 00 5D 16 0D 4D 15 00 16 04 5D 16 0D 4D 15 33 00 61 1A 0D 4D 15 00 16 00 61 1A 0D 4D 15 5D 00 5E 31 0D 0D 15 1A 00 6E 31 0D 0D 15 06 03 04 D2 6E 31 6D 0D 15 0C 03 11 40 00 01 05 64 01 13 40 00 01 05 17 00 0A 41 00 01 05 18 00 40 39 15 0D 15 21 00 53 04 16 0D 15 03 00 00 00 58 57 09 36 0D 15 5D 01 78 0A 16 0D 15 64 01 78 0A 16 8D 15 2C 68 78 0A 16 8D 15 24 3C 78 0A 16 8D 15 63 02 78 0A 16 8D 15 1B 12 78 0A 16 8D 15 65 61 78 0A 16 8D 15 61 00 78 0A 16 8D 15 32 0E 78 0A 16 8D 15 66 00 78 0A 16 8D 15 3C 01 78 0A 16 8D 15 3D 88 32 93 00 00 00 3E 00 00 00 00 00 00 26 01 78 0A 16 8D 15 27 01 B2 08 00 00 00 28 00 00 00 00 00 00 60 00 78 0A 16 4D 15 23 00 78 0A 16 8D 15 5E 01 78 0A 16 8D 15 2D 01 78 0A 16 8D 15 5A 0F 78 0A 16 8D 15 49 13 00 07 12 0A 1E 0B 2A 0A 00 00 00 00 00 00 00 00 00 23 08 27 2C 23 00 00 00 00 00 00 00 00 00 00 00 32 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 49 13 00 07 12 0A 1E 0B 2A 0A 00 00 00 00 00 00 00 00 00 23 08 27 2C 23 00 00 00 00 00 00 00 00 00 00 00 32 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 62 00 78 0A 16 8D 15 5F 51 78 0A 16 8D 15 4F 00 78 0A 16 8D 15 40 01 00 6F 1C 16 1E 00 3C 14 00 1E 3C 1F 15 70 40 01 00 6F 1C 16 1E 00 3C 14 00 1E 3C 1F 15 70 08 18 78 0A 16 8D 15 00 18 00 02 18 00 04 18 00 06 18 00 08 14 00 0A 14 00 0C 14 00 0E 10 00 10 10 00 12 10 00 14 08 00 16 08 00 18 08 00 1A 08 00 1C 08 00 1E 14 00 20 14 00 22 14 00 24 14 00 26 16 00 28 16 00 2A 16 00 2C 1C 00 2E 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D6 06", + MedtronicDeviceType.Medtronic_522_722) + Assert.assertEquals(41, pumpHistoryEntries.size.toLong()) + } + + @Throws(Exception::class) + private fun readAndParseHistoryPage(historyPageString: String, medtronicDeviceType: MedtronicDeviceType): List { + val historyPageData = ByteUtil.createByteArrayFromString(historyPageString) + aapsLogger.debug("History Page Length:" + historyPageData.size) + medtronicUtil!!.medtronicPumpModel = medtronicDeviceType + medtronicUtil!!.isModelSet = true + val historyPage = RawHistoryPage(aapsLogger) + historyPage.appendData(historyPageData) + val pumpHistoryEntries: List = decoder!!.processPageAndCreateRecords(historyPage) + displayHistoryRecords(pumpHistoryEntries) + return pumpHistoryEntries + } + + private fun displayHistoryRecords(pumpHistoryEntries: List) { + aapsLogger.debug("PumpHistoryEntries: " + pumpHistoryEntries.size) + for (pumpHistoryEntry in pumpHistoryEntries) { + aapsLogger.debug(pumpHistoryEntry.toString()) + } + } +} \ No newline at end of file diff --git a/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryUTest.java b/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryUTest.java index af8c7b0d0a..043a2fdd86 100644 --- a/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryUTest.java +++ b/medtronic/src/test/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryUTest.java @@ -16,7 +16,7 @@ public class PumpHistoryEntryUTest { long queryObject = 20191009000000L; PumpHistoryEntry phe = new PumpHistoryEntry(); - phe.atechDateTime = dateObject; + phe.setAtechDateTime(dateObject); Assert.assertTrue(phe.isAfter(queryObject)); } diff --git a/omnipod-eros/build.gradle b/omnipod-eros/build.gradle index 63fcffcef6..e530957c56 100644 --- a/omnipod-eros/build.gradle +++ b/omnipod-eros/build.gradle @@ -16,6 +16,7 @@ android { dependencies { implementation project(':core') + implementation project(':pump-common') implementation project(':omnipod-common') implementation project(':rileylink') } diff --git a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java index 65a3c739ba..a1c5ead10b 100644 --- a/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java +++ b/omnipod-eros/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/eros/OmnipodErosPumpPlugin.java @@ -1,10 +1,5 @@ package info.nightscout.androidaps.plugins.pump.omnipod.eros; -import static info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.OmnipodConstants.BASAL_STEP_DURATION; -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.convertedToAbsolute; -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes; -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.toStringFull; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -34,7 +29,6 @@ import javax.inject.Singleton; import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.interfaces.Profile; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppInitialized; @@ -44,9 +38,10 @@ import info.nightscout.androidaps.interfaces.ActivePlugin; import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.interfaces.Profile; import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.Pump; +import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpPluginBase; import info.nightscout.androidaps.interfaces.PumpSync; import info.nightscout.androidaps.logging.AAPSLogger; @@ -110,6 +105,11 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers; import info.nightscout.androidaps.utils.sharedPreferences.SP; import io.reactivex.disposables.CompositeDisposable; +import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.convertedToAbsolute; +import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes; +import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.toStringFull; +import static info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.OmnipodConstants.BASAL_STEP_DURATION; + /** * Created by andy on 23.04.18. * @@ -651,7 +651,7 @@ public class OmnipodErosPumpPlugin extends PumpPluginBase implements Pump, Riley activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, true); return new PumpEnactResult(getInjector()).success(true).enacted(true).bolusDelivered(0d) - .carbsDelivered(detailedBolusInfo.carbs).comment(info.nightscout.androidaps.core.R.string.common_resultok); + .carbsDelivered(detailedBolusInfo.carbs); } } @@ -1040,17 +1040,17 @@ public class OmnipodErosPumpPlugin extends PumpPluginBase implements Pump, Riley @NonNull @Override public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) { 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.plugins.pump.common.R.string.pump_operation_not_supported_by_pump_driver); } @NonNull @Override public PumpEnactResult cancelExtendedBolus() { 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.plugins.pump.common.R.string.pump_operation_not_supported_by_pump_driver); } @NonNull @Override public PumpEnactResult loadTDDs() { 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.plugins.pump.common.R.string.pump_operation_not_supported_by_pump_driver); } public boolean isUseRileyLinkBatteryLevel() { diff --git a/pump-common/.gitignore b/pump-common/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/pump-common/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/pump-common/build.gradle b/pump-common/build.gradle new file mode 100644 index 0000000000..996daf31dd --- /dev/null +++ b/pump-common/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' +apply plugin: 'com.hiya.jacoco-android' + +apply from: "${project.rootDir}/gradle/android_dependencies.gradle" +apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle" +apply from: "${project.rootDir}/gradle/test_dependencies.gradle" + +android { + defaultConfig { + versionCode 1 + versionName "1.0" + } +} + +dependencies { + implementation project(':core') + //implementation project(':database') + + implementation('com.thoughtworks.xstream:xstream:1.4.7') { + exclude group: 'xmlpull', module: 'xmlpull' + } +} diff --git a/pump-common/consumer-rules.pro b/pump-common/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pump-common/proguard-rules.pro b/pump-common/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/pump-common/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/pump-common/src/main/AndroidManifest.xml b/pump-common/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..d4055a4210 --- /dev/null +++ b/pump-common/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt new file mode 100644 index 0000000000..814e950f03 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt @@ -0,0 +1,384 @@ +package info.nightscout.androidaps.plugins.pump.common + +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.text.format.DateFormat +import com.google.gson.GsonBuilder +import dagger.android.HasAndroidInjector + +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventCustomActionsChanged +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.extensions.toStringFull +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.PumpSync.TemporaryBasalType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.common.ManufacturerType +import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal +import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import org.json.JSONException +import org.json.JSONObject + +/** + * Created by andy on 23.04.18. + */ +// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin) +abstract class PumpPluginAbstract protected constructor( + pluginDescription: PluginDescription?, + pumpType: PumpType, + injector: HasAndroidInjector?, + resourceHelper: ResourceHelper, + aapsLogger: AAPSLogger, + commandQueue: CommandQueueProvider, + var rxBus: RxBusWrapper, + var activePlugin: ActivePlugin, + var sp: SP, + var context: Context, + var fabricPrivacy: FabricPrivacy, + var dateUtil: DateUtil, + var aapsSchedulers: AapsSchedulers, + var pumpSync: PumpSync, + var pumpSyncStorage: info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage +) : PumpPluginBase(pluginDescription!!, injector!!, aapsLogger, resourceHelper, commandQueue), Pump, Constraints, info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncEntriesCreator { + + private val disposable = CompositeDisposable() + + // Pump capabilities + final override var pumpDescription = PumpDescription() + //protected set + + @JvmField protected var serviceConnection: ServiceConnection? = null + @JvmField protected var serviceRunning = false + @JvmField protected var pumpState = PumpDriverState.NotInitialized + @JvmField protected var displayConnectionMessages = false + + var pumpType: PumpType = PumpType.GENERIC_AAPS + get() = field + set(value) { + field = value + pumpDescription.fillFor(value) + } + + protected var gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() + + abstract fun initPumpStatusData() + + override fun onStart() { + super.onStart() + initPumpStatusData() + val intent = Intent(context, serviceClass) + context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE) + serviceRunning = true + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ _ -> context.unbindService(serviceConnection!!) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) } + ) + onStartCustomActions() + } + + override fun onStop() { + aapsLogger.debug(LTag.PUMP, deviceID() + " onStop()") + context.unbindService(serviceConnection!!) + serviceRunning = false + disposable.clear() + super.onStop() + } + + /** + * If we need to run any custom actions in onStart (triggering events, etc) + */ + abstract fun onStartCustomActions() + + /** + * Service class (same one you did serviceConnection for) + * + * @return Class + */ + abstract val serviceClass: Class<*>? + abstract val pumpStatusData: PumpStatus + + override fun isInitialized(): Boolean { + return pumpState.isInitialized() + } + + override fun isSuspended(): Boolean { + return pumpState === PumpDriverState.Suspended + } + + override fun isBusy(): Boolean { + return pumpState === PumpDriverState.Busy + } + + override fun isConnected(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isConnected [PumpPluginAbstract].") + return pumpState.isConnected() + } + + override fun isConnecting(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isConnecting [PumpPluginAbstract].") + return pumpState === PumpDriverState.Connecting + } + + override fun connect(reason: String) { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "connect (reason={}) [PumpPluginAbstract] - default (empty) implementation.$reason") + } + + override fun disconnect(reason: String) { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "disconnect (reason={}) [PumpPluginAbstract] - default (empty) implementation.$reason") + } + + override fun stopConnecting() { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation.") + } + + override fun isHandshakeInProgress(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isHandshakeInProgress [PumpPluginAbstract] - default (empty) implementation.") + return false + } + + override fun finishHandshaking() { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "finishHandshaking [PumpPluginAbstract] - default (empty) implementation.") + } + + // Upload to pump new basal profile + override fun setNewBasalProfile(profile: Profile): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setNewBasalProfile [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun isThisProfileSet(profile: Profile): Boolean { + aapsLogger.debug(LTag.PUMP, "isThisProfileSet [PumpPluginAbstract] - Not implemented.") + return true + } + + override fun lastDataTime(): Long { + aapsLogger.debug(LTag.PUMP, "lastDataTime [PumpPluginAbstract].") + return pumpStatusData.lastConnection + } + + // base basal rate, not temp basal + override val baseBasalRate: Double + get() { + aapsLogger.debug(LTag.PUMP, "getBaseBasalRate [PumpPluginAbstract] - Not implemented.") + return 0.0 + } + + override fun stopBolusDelivering() { + aapsLogger.debug(LTag.PUMP, "stopBolusDelivering [PumpPluginAbstract] - Not implemented.") + } + + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setExtendedBolus [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + // some pumps might set a very short temp close to 100% as cancelling a temp can be noisy + // when the cancel request is requested by the user (forced), the pump should always do a real cancel + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "cancelTempBasal [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun cancelExtendedBolus(): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + // Status to be passed to NS + // public JSONObject getJSONStatus(Profile profile, String profileName) { + // return pumpDriver.getJSONStatus(profile, profileName); + // } + open fun deviceID(): String { + aapsLogger.debug(LTag.PUMP, "deviceID [PumpPluginAbstract] - Not implemented.") + return "FakeDevice" + } + + // Short info for SMS, Wear etc + override val isFakingTempsByExtendedBoluses: Boolean + get() { + aapsLogger.debug(LTag.PUMP, "isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented.") + return false + } + + override fun loadTDDs(): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "loadTDDs [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { + if (pumpStatusData.lastConnection + 60 * 60 * 1000L < System.currentTimeMillis()) { + return JSONObject() + } + val now = System.currentTimeMillis() + val pump = JSONObject() + val battery = JSONObject() + val status = JSONObject() + val extended = JSONObject() + try { + battery.put("percent", pumpStatusData.batteryRemaining) + status.put("status", pumpStatusData.pumpStatusType.status) + extended.put("Version", version) + try { + extended.put("ActiveProfile", profileName) + } catch (ignored: Exception) { + } + val tb = pumpSync.expectedPumpState().temporaryBasal + if (tb != null) { + extended.put("TempBasalAbsoluteRate", tb.convertedToAbsolute(now, profile)) + extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.timestamp)) + extended.put("TempBasalRemaining", tb.plannedRemainingMinutes) + } + val eb = pumpSync.expectedPumpState().extendedBolus + if (eb != null) { + extended.put("ExtendedBolusAbsoluteRate", eb.rate) + extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.timestamp)) + extended.put("ExtendedBolusRemaining", eb.plannedRemainingMinutes) + } + status.put("timestamp", dateUtil.toISOString(dateUtil.now())) + pump.put("battery", battery) + pump.put("status", status) + pump.put("extended", extended) + pump.put("reservoir", pumpStatusData.reservoirRemainingUnits) + pump.put("clock", dateUtil.toISOString(dateUtil.now())) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + return pump + } + + // FIXME i18n, null checks: iob, TDD + override fun shortStatus(veryShort: Boolean): String { + var ret = "" + + if (pumpStatusData.lastConnection == 0L) { + ret += "LastConn: never\n" + } else { + val agoMsec = System.currentTimeMillis() - pumpStatusData.lastConnection + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + ret += "LastConn: $agoMin min ago\n" + } + + if (pumpStatusData.lastBolusTime != null && pumpStatusData.lastBolusTime!!.time != 0L) { + ret += """ + LastBolus: ${to2Decimal(pumpStatusData.lastBolusAmount!!)}U @${DateFormat.format("HH:mm", pumpStatusData.lastBolusTime)} + + """.trimIndent() + } + val activeTemp = pumpSync.expectedPumpState().temporaryBasal + if (activeTemp != null) { + ret += """ + Temp: ${activeTemp.toStringFull(dateUtil)} + + """.trimIndent() + } + val activeExtendedBolus = pumpSync.expectedPumpState().extendedBolus + if (activeExtendedBolus != null) { + ret += """ + Extended: ${activeExtendedBolus.toStringFull(dateUtil)} + + """.trimIndent() + } + // if (!veryShort) { + // ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " + // + pumpStatus.maxDailyTotalUnits + " U\n"; + // } + ret += """ + IOB: ${pumpStatusData.iob}U + + """.trimIndent() + ret += """ + Reserv: ${to0Decimal(pumpStatusData.reservoirRemainingUnits)}U + + """.trimIndent() + ret += """ + Batt: ${pumpStatusData.batteryRemaining} + + """.trimIndent() + return ret + } + + override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { + return try { + if (detailedBolusInfo.insulin == 0.0 && detailedBolusInfo.carbs == 0.0) { + // neither carbs nor bolus requested + aapsLogger.error("deliverTreatment: Invalid input") + PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0).carbsDelivered(0.0) + .comment(R.string.invalidinput) + } else if (detailedBolusInfo.insulin > 0) { + // bolus needed, ask pump to deliver it + deliverBolus(detailedBolusInfo) + } else { + detailedBolusInfo.timestamp = System.currentTimeMillis() + + // no bolus required, carb only treatment + pumpSyncStorage.addCarbs(info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryCarbs(detailedBolusInfo, this)) + + val bolusingEvent = EventOverviewBolusProgress + bolusingEvent.t = EventOverviewBolusProgress.Treatment(0.0, detailedBolusInfo.carbs.toInt(), detailedBolusInfo.bolusType === DetailedBolusInfo.BolusType.SMB) + bolusingEvent.percent = 100 + rxBus.send(bolusingEvent) + aapsLogger.debug(LTag.PUMP, "deliverTreatment: Carb only treatment.") + PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(0.0) + .carbsDelivered(detailedBolusInfo.carbs).comment(R.string.common_resultok) + } + } finally { + triggerUIChange() + } + } + + protected fun refreshCustomActionsList() { + rxBus.send(EventCustomActionsChanged()) + } + + override fun manufacturer(): ManufacturerType { + return pumpType.manufacturer!! + } + + override fun model(): PumpType { + return pumpType + } + + override fun canHandleDST(): Boolean { + return false + } + + protected abstract fun deliverBolus(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult + + protected abstract fun triggerUIChange() + + private fun getOperationNotSupportedWithCustomText(resourceId: Int): PumpEnactResult { + return PumpEnactResult(injector).success(false).enacted(false).comment(resourceId) + } + + init { + pumpDescription.fillFor(pumpType) + this.pumpType = pumpType + } +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt new file mode 100644 index 0000000000..9ecd30bca7 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpStatus.kt @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.plugins.pump.common.data + +import info.nightscout.androidaps.plugins.pump.common.defs.PumpStatusType +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import java.util.* + +/** + * Created by andy on 4/28/18. + */ +abstract class PumpStatus(var pumpType: PumpType) { + + // connection + var lastDataTime: Long = 0 + var lastConnection = 0L + var previousConnection = 0L // here should be stored last connection of previous session (so needs to be + + // read before lastConnection is modified for first time). + // last bolus + var lastBolusTime: Date? = null + var lastBolusAmount: Double? = null + + // other pump settings + var activeProfileName = "0" + var reservoirRemainingUnits = 0.0 + var reservoirFullUnits = 0 + var batteryRemaining = 0 // percent, so 0-100 + var batteryVoltage: Double? = null + + // iob + var iob: String? = null + + // TDD + var dailyTotalUnits: Double? = null + var maxDailyTotalUnits: String? = null + var units: String? = null // Constants.MGDL or Constants.MMOL + var pumpStatusType = PumpStatusType.Running + var basalsByHour: DoubleArray? = null + var tempBasalStart: Date? = null + var tempBasalAmount: Double? = 0.0 + var tempBasalLength: Int? = 0 + var tempBasalEnd: Long? = null + + abstract fun initSettings() + + fun setLastCommunicationToNow() { + lastDataTime = System.currentTimeMillis() + lastConnection = System.currentTimeMillis() + } + + abstract val errorInfo: String? + +} \ No newline at end of file diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt similarity index 91% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt index b41a6bd4c1..803d1a8902 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDeviceState.kt @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.common.defs -import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.plugins.pump.common.R enum class PumpDeviceState(var resourceId: Int) { diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt similarity index 100% rename from medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpDriverState.kt diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt new file mode 100644 index 0000000000..081c76c8db --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt @@ -0,0 +1,53 @@ +package info.nightscout.androidaps.plugins.pump.common.defs + +import info.nightscout.androidaps.plugins.pump.common.R +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * + * Author: Andy {andy.rozman@gmail.com} + */ +enum class PumpHistoryEntryGroup(val resourceId: Int) { + + All(R.string.history_group_all), + Bolus(R.string.history_group_bolus), + Basal(R.string.history_group_basal), + Prime(R.string.history_group_prime), + Configuration(R.string.history_group_configuration), + Alarm(R.string.history_group_alarm), + Glucose(R.string.history_group_glucose), + Notification(R.string.history_group_notification), + Statistic(R.string.history_group_statistic), + Unknown(R.string.history_group_unknown); + + var translated: String? = null + private set + + override fun toString(): String { + return translated!! + } + + companion object { + + private var translatedList: MutableList? = null + + private fun doTranslation(resourceHelper: ResourceHelper) { + translatedList = ArrayList() + for (pumpHistoryEntryGroup in values()) { + pumpHistoryEntryGroup.translated = resourceHelper.gs(pumpHistoryEntryGroup.resourceId) + (translatedList as ArrayList).add(pumpHistoryEntryGroup) + } + } + + @JvmStatic + fun getTranslatedList(resourceHelper: ResourceHelper): List { + if (translatedList == null) doTranslation(resourceHelper) + return translatedList!! + } + } + +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpStatusType.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpStatusType.kt similarity index 100% rename from medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpStatusType.kt rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpStatusType.kt diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/TempBasalPair.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/TempBasalPair.kt similarity index 100% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/TempBasalPair.kt rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/TempBasalPair.kt diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/di/PumpCommonModule.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/di/PumpCommonModule.kt new file mode 100644 index 0000000000..a5c85412a4 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/di/PumpCommonModule.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.pump.common.di + +import dagger.Module +import dagger.Provides +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.plugins.pump.common.sync.PumpSyncStorage +import info.nightscout.androidaps.utils.sharedPreferences.SP +import javax.inject.Singleton + +@Module +@Suppress("unused") +class PumpCommonModule { + + @Provides + @Singleton + fun providesPumpSyncStorage( + pumpSync: PumpSync, + sp: SP, + aapsLogger: AAPSLogger + ): PumpSyncStorage { + return PumpSyncStorage(pumpSync, sp, aapsLogger) + } + +} \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpDbEntry.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpDbEntry.kt new file mode 100644 index 0000000000..e1b734b048 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpDbEntry.kt @@ -0,0 +1,63 @@ +package info.nightscout.androidaps.plugins.pump.common.sync + +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType + +data class PumpDbEntry constructor(var temporaryId: Long, + var date: Long, + var pumpType: PumpType, + var serialNumber: String, + var bolusData: PumpDbEntryBolus? = null, + var tbrData: PumpDbEntryTBR? = null, + var pumpId: Long? = null) { + + constructor(temporaryId: Long, + date: Long, + pumpType: PumpType, + serialNumber: String, + detailedBolusInfo: DetailedBolusInfo) : this(temporaryId, date, pumpType, serialNumber) { + this.bolusData = PumpDbEntryBolus( + detailedBolusInfo.insulin, + detailedBolusInfo.carbs, + detailedBolusInfo.bolusType) + } + + constructor(temporaryId: Long, + date: Long, + pumpType: PumpType, + serialNumber: String, + rate: Double, + isAbsolute: Boolean, + durationInMinutes: Int, + tbrType: PumpSync.TemporaryBasalType) : this(temporaryId, date, pumpType, serialNumber) { + this.tbrData = PumpDbEntryTBR( + rate, + isAbsolute, + durationInMinutes, + tbrType) + } + +} + +data class PumpDbEntryBolus(var insulin: Double, + var carbs: Double, + var bolusType: DetailedBolusInfo.BolusType) + +data class PumpDbEntryCarbs(var date: Long, + var carbs: Double, + var pumpType: PumpType, + var serialNumber: String, + var pumpId: Long? = null) { + + constructor(detailedBolusInfo: DetailedBolusInfo, + creator: PumpSyncEntriesCreator) : this(detailedBolusInfo.timestamp, + detailedBolusInfo.carbs, + creator.model(), + creator.serialNumber()) +} + +data class PumpDbEntryTBR(var rate: Double, + var isAbsolute: Boolean, + var durationInMinutes: Int, + var tbrType: PumpSync.TemporaryBasalType) \ No newline at end of file diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncEntriesCreator.java b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncEntriesCreator.java new file mode 100644 index 0000000000..1ba73df0bb --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncEntriesCreator.java @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.plugins.pump.common.sync; + +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; + +public interface PumpSyncEntriesCreator { + + long generateTempId(Object dataObject); + + PumpType model(); + + String serialNumber(); + +} diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncStorage.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncStorage.kt new file mode 100644 index 0000000000..8084d52cd6 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/sync/PumpSyncStorage.kt @@ -0,0 +1,172 @@ +package info.nightscout.androidaps.plugins.pump.common.sync + +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.thoughtworks.xstream.XStream +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.interfaces.PumpSync +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.utils.sharedPreferences.SP +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * This class is intended for Pump Drivers that use temporaryId and need way to pair records + */ +@Singleton +class PumpSyncStorage @Inject constructor( + val pumpSync: PumpSync, + val sp: SP, + val aapsLogger: AAPSLogger +) { + + val pumpSyncStorageKey: String = "pump_sync_storage_xstream" + var pumpSyncStorage: MutableMap> = mutableMapOf() + var TBR: String = "TBR" + var BOLUS: String = "BOLUS" + var storageInitialized: Boolean = false + var gson: Gson = GsonBuilder().create() + var xstream: XStream = XStream() + + init { + initStorage() + } + + fun initStorage() { + if (storageInitialized) + return + + var loaded = false + + if (sp.contains(pumpSyncStorageKey)) { + val jsonData: String = sp.getString(pumpSyncStorageKey, ""); + + if (!jsonData.isBlank()) { + pumpSyncStorage = xstream.fromXML(jsonData, MutableMap::class.java) as MutableMap> + + aapsLogger.debug(String.format("Loading Pump Sync Storage: boluses=%d, tbrs=%d.", pumpSyncStorage[BOLUS]!!.size, pumpSyncStorage[TBR]!!.size)) + loaded = true + } + } + + if (!loaded) { + pumpSyncStorage[BOLUS] = mutableListOf() + pumpSyncStorage[TBR] = mutableListOf() + } + } + + fun saveStorage() { + if (!isStorageEmpty()) { + sp.putString(pumpSyncStorageKey, xstream.toXML(pumpSyncStorage)) + aapsLogger.debug(String.format("Saving Pump Sync Storage: boluses=%d, tbrs=%d.", pumpSyncStorage[BOLUS]!!.size, pumpSyncStorage[TBR]!!.size)) + } + } + + fun isStorageEmpty(): Boolean { + return pumpSyncStorage[BOLUS]!!.isEmpty() && pumpSyncStorage[TBR]!!.isEmpty() + } + + fun getBoluses(): MutableList { + return pumpSyncStorage[BOLUS]!!; + } + + fun getTBRs(): MutableList { + return pumpSyncStorage[TBR]!!; + } + + fun addBolusWithTempId(detailedBolusInfo: DetailedBolusInfo, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean { + val temporaryId = creator.generateTempId(detailedBolusInfo.timestamp) + val result = pumpSync.addBolusWithTempId( + detailedBolusInfo.timestamp, + detailedBolusInfo.insulin, + temporaryId, + detailedBolusInfo.bolusType, + creator.model(), + creator.serialNumber()) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "addBolusWithTempId [date=%d, temporaryId=%d, insulin=%.2f, type=%s, pumpSerial=%s] - Result: %b", + detailedBolusInfo.timestamp, temporaryId, detailedBolusInfo.insulin, detailedBolusInfo.bolusType, + creator.serialNumber(), result)) + + if (detailedBolusInfo.carbs > 0.0) { + addCarbs(PumpDbEntryCarbs(detailedBolusInfo, creator)) + } + + if (result && writeToInternalHistory) { + val innerList: MutableList = pumpSyncStorage[BOLUS]!! + + innerList.add(PumpDbEntry(temporaryId, detailedBolusInfo.timestamp, creator.model(), creator.serialNumber(), detailedBolusInfo)) + pumpSyncStorage[BOLUS] = innerList + saveStorage() + } + return result + } + + fun addCarbs(carbsDto: PumpDbEntryCarbs) { + val result = pumpSync.syncCarbsWithTimestamp( + carbsDto.date, + carbsDto.carbs, + null, + carbsDto.pumpType, + carbsDto.serialNumber) + + aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncCarbsWithTimestamp [date=%d, carbs=%.2f, pumpSerial=%s] - Result: %b", + carbsDto.date, carbsDto.carbs, carbsDto.serialNumber, result)) + } + + fun addTemporaryBasalRateWithTempId(temporaryBasal: PumpDbEntryTBR, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean { + val timenow: Long = System.currentTimeMillis() + val temporaryId = creator.generateTempId(timenow) + + val response = pumpSync.addTemporaryBasalWithTempId( + timenow, + temporaryBasal.rate, + (temporaryBasal.durationInMinutes * 60L * 1000L), + temporaryBasal.isAbsolute, + temporaryId, + temporaryBasal.tbrType, + creator.model(), + creator.serialNumber()) + + if (response && writeToInternalHistory) { + val innerList: MutableList = pumpSyncStorage[TBR]!! + + innerList.add(PumpDbEntry(temporaryId, timenow, creator.model(), creator.serialNumber(), null, temporaryBasal)) + pumpSyncStorage[BOLUS] = innerList + saveStorage() + } + + return response; + } + + fun removeBolusWithTemporaryId(temporaryId: Long) { + val bolusList = removeTemporaryId(temporaryId, pumpSyncStorage[BOLUS]!!) + pumpSyncStorage[BOLUS] = bolusList + saveStorage() + } + + fun removeTemporaryBasalWithTemporaryId(temporaryId: Long) { + val tbrList = removeTemporaryId(temporaryId, pumpSyncStorage[TBR]!!) + pumpSyncStorage[TBR] = tbrList + saveStorage() + } + + private fun removeTemporaryId(temporaryId: Long, list: MutableList): MutableList { + var dbEntry: PumpDbEntry? = null + + for (pumpDbEntry in list) { + if (pumpDbEntry.temporaryId == temporaryId) { + dbEntry = pumpDbEntry + } + } + + if (dbEntry != null) { + list.remove(dbEntry) + } + + return list + } + +} \ No newline at end of file diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java similarity index 83% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java index 6273ff5203..ac4f6d24af 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java @@ -29,9 +29,13 @@ public class ByteUtil { return (b < 0) ? b + 256 : b; } + public static int asUINT8(Integer b) { + return (b < 0) ? b + 256 : b; + } + public static byte[] getBytesFromInt16(int value) { byte[] array = getBytesFromInt(value); - return new byte[] {array[2], array[3]}; + return new byte[]{array[2], array[3]}; } public static byte[] getBytesFromInt(int value) { @@ -182,22 +186,6 @@ public class ByteUtil { } - // public static byte[] fromByteList(List byteArray) { - // byte[] rval = new byte[byteArray.size()]; - // for (int i = 0; i < byteArray.size(); i++) { - // rval[i] = byteArray.get(i); - // } - // return rval; - // } - - // public static List toByteList(byte[] data) { - // ArrayList rval = new ArrayList<>(data.length); - // for (int i = 0; i < data.length; i++) { - // rval.add(i, new Byte(data[i])); - // } - // return rval; - // } - public static List getListFromByteArray(byte[] array) { List listOut = new ArrayList(); @@ -283,15 +271,62 @@ public class ByteUtil { } + /** + * Converts 4 (or less) ints into int. (Shorts are objects, so you can send null if you have less parameters) + * + * @param b1 short 1 + * @param b2 short 2 + * @param b3 short 3 + * @param b4 short 4 + * @param flag Conversion Flag (Big Endian, Little endian) + * @return int value + */ + public static int toInt(Byte b1, Byte b2, Byte b3, Byte b4, BitConversion flag) { + switch (flag) { + case LITTLE_ENDIAN: { + if (b4 != null) { + return (b4 & 0xff) << 24 | (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff; + } else if (b3 != null) { + return (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff; + } else if (b2 != null) { + return (b2 & 0xff) << 8 | b1 & 0xff; + } else { + return b1 & 0xff; + } + } + + default: + case BIG_ENDIAN: { + if (b4 != null) { + return (b1 & 0xff) << 24 | (b2 & 0xff) << 16 | (b3 & 0xff) << 8 | b4 & 0xff; + } else if (b3 != null) { + return (b1 & 0xff) << 16 | (b2 & 0xff) << 8 | b3 & 0xff; + } else if (b2 != null) { + return (b1 & 0xff) << 8 | b2 & 0xff; + } else { + return b1 & 0xff; + } + } + } + } + + public static int toInt(int b1, int b2) { return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN); } + public static int toInt(Byte b1, Byte b2) { + return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN); + } + public static int toInt(int b1, int b2, int b3) { return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN); } + public static int toInt(Byte b1, Byte b2, Byte b3) { + return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN); + } public static int toInt(int b1, int b2, BitConversion flag) { return toInt(b1, b2, null, null, flag); @@ -329,6 +364,25 @@ public class ByteUtil { } + public static String getCorrectHexValue(byte inp) { + String hx = Integer.toHexString((char) inp); + + if (hx.length() == 0) + return "00"; + else if (hx.length() == 1) + return "0" + hx; + else if (hx.length() == 2) + return hx; + else if (hx.length() == 4) + return hx.substring(2); + else { + System.out.println("Hex Error: " + inp); + } + + return null; + } + + public static String getHex(byte[] abyte0) { return abyte0 != null ? getHex(abyte0, abyte0.length) : null; } diff --git a/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.kt b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.kt new file mode 100644 index 0000000000..6559d80f80 --- /dev/null +++ b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.kt @@ -0,0 +1,81 @@ +package info.nightscout.androidaps.plugins.pump.common.utils + +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.Profile.ProfileValue +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import java.util.* + +object ProfileUtil { + + fun getProfileDisplayable(profile: Profile, pumpType: PumpType): String { + val stringBuilder = StringBuilder() + for (basalValue in profile.getBasalValues()) { + val basalValueValue = pumpType.determineCorrectBasalSize(basalValue.value) + val hour = basalValue.timeAsSeconds / (60 * 60) + stringBuilder.append((if (hour < 10) "0" else "") + hour + ":00") + stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)) + stringBuilder.append(", ") + } + return if (stringBuilder.length > 3) stringBuilder.substring(0, stringBuilder.length - 2) else stringBuilder.toString() + } + + @JvmStatic + fun getBasalProfilesDisplayable(profiles: Array, pumpType: PumpType): String { + val stringBuilder = StringBuilder() + for (basalValue in profiles) { + val basalValueValue = pumpType.determineCorrectBasalSize(basalValue.value) + val hour = basalValue.timeAsSeconds / (60 * 60) + stringBuilder.append((if (hour < 10) "0" else "") + hour + ":00") + stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)) + stringBuilder.append(", ") + } + return if (stringBuilder.length > 3) stringBuilder.substring(0, stringBuilder.length - 2) else stringBuilder.toString() + } + + @JvmStatic + fun getBasalProfilesDisplayableAsStringOfArray(profile: Profile, pumpType: PumpType): String? { + val stringBuilder = java.lang.StringBuilder() + // for (basalValue in profiles) { + // val basalValueValue = pumpType.determineCorrectBasalSize(basalValue.value) + // val hour = basalValue.timeAsSeconds / (60 * 60) + // stringBuilder.append((if (hour < 10) "0" else "") + hour + ":00") + // stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)) + // stringBuilder.append(", ") + // } + // return if (stringBuilder.length > 3) stringBuilder.substring(0, stringBuilder.length - 2) else stringBuilder.toString() + + var entriesCopy = profile.getBasalValues() + + for (i in entriesCopy.indices) { + val current = entriesCopy[i] + // var currentTime = if (current.startTime_raw % 2 == 0) current.startTime_raw.toInt() else current.startTime_raw - 1 + // currentTime = currentTime * 30 / 60 + val currentTime = current.timeAsSeconds / (60 * 60) + + var lastHour: Int + lastHour = if (i + 1 == entriesCopy.size) { + 24 + } else { + val basalProfileEntry = entriesCopy[i + 1] + //val rawTime = if (basalProfileEntry.startTime_raw % 2 == 0) basalProfileEntry.startTime_raw.toInt() else basalProfileEntry.startTime_raw - 1 + //rawTime * 30 / 60 + basalProfileEntry.timeAsSeconds / (60 * 60) + } + + // System.out.println("Current time: " + currentTime + " Next Time: " + lastHour); + for (j in currentTime until lastHour) { + // if (pumpType == null) + // basalByHour[j] = current.rate + // else + //basalByHour[j] = pumpType.determineCorrectBasalSize(current.value) + + stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", pumpType.determineCorrectBasalSize(current.value))) + stringBuilder.append(" ") + } + } + + return stringBuilder.toString().trim() + + } + +} \ No newline at end of file diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java similarity index 100% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java b/pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java similarity index 100% rename from rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java rename to pump-common/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java diff --git a/pump-common/src/main/res/values/strings.xml b/pump-common/src/main/res/values/strings.xml new file mode 100644 index 0000000000..cdbe2ba8bf --- /dev/null +++ b/pump-common/src/main/res/values/strings.xml @@ -0,0 +1,32 @@ + + + + + Operation not supported by pump and/or driver. + Operation not YET supported by pump. + OK + + + Never contacted + Waking up + Error with communication + Timeout on communication + Pump unreachable + Invalid configuration + Active + Sleeping + + + Basals + Configurations + Notifications + Statistics + Unknowns + All + Boluses + Prime + Alarms + Glucose + + + \ No newline at end of file diff --git a/rileylink/build.gradle b/rileylink/build.gradle index 1cae2aa0f9..96f2bfa148 100644 --- a/rileylink/build.gradle +++ b/rileylink/build.gradle @@ -16,4 +16,5 @@ android { dependencies { implementation project(':core') + implementation project(':pump-common') } diff --git a/rileylink/src/main/AndroidManifest.xml b/rileylink/src/main/AndroidManifest.xml index 3236de84bd..79aa441d6a 100644 --- a/rileylink/src/main/AndroidManifest.xml +++ b/rileylink/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="info.nightscout.androidaps.plugins.pump.common.hw.rileylink"> @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.java deleted file mode 100644 index 70a484d5d6..0000000000 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.java +++ /dev/null @@ -1,66 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common.defs; - -import java.util.ArrayList; -import java.util.List; - - -import info.nightscout.androidaps.core.R; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - - -/** - * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes - * management and modified/extended for AAPS. - *

- * Author: Andy {andy.rozman@gmail.com} - */ - -public enum PumpHistoryEntryGroup { - - All(R.string.history_group_all), - Bolus(R.string.history_group_bolus), - Basal(R.string.history_group_basal), - Prime(R.string.history_group_prime), - Configuration(R.string.history_group_configuration), - Alarm(R.string.history_group_alarm), - Glucose(R.string.history_group_glucose), - Notification(R.string.history_group_notification), - Statistic(R.string.history_group_statistic), - Unknown(R.string.history_group_unknown), - ; - - private final int resourceId; - private String translated; - - private static List translatedList; - - PumpHistoryEntryGroup(int resourceId) { - this.resourceId = resourceId; - } - - private static void doTranslation(ResourceHelper resourceHelper) { - translatedList = new ArrayList<>(); - - for (PumpHistoryEntryGroup pumpHistoryEntryGroup : values()) { - pumpHistoryEntryGroup.translated = resourceHelper.gs(pumpHistoryEntryGroup.resourceId); - translatedList.add(pumpHistoryEntryGroup); - } - } - - public static List getTranslatedList(ResourceHelper resourceHelper) { - if (translatedList == null) doTranslation(resourceHelper); - return translatedList; - } - - public int getResourceId() { - return resourceId; - } - - public String getTranslated() { - return translated; - } - - public String toString() { - return this.translated; - } -} diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEConfigActivity.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEConfigActivity.java index 3f2482c2cc..227b642d8c 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEConfigActivity.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEConfigActivity.java @@ -35,8 +35,8 @@ import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.interfaces.ActivePlugin; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.pump.common.R; import info.nightscout.androidaps.plugins.pump.common.ble.BlePreCheck; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java index 2d6c93a0a9..da071355da 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java @@ -1,8 +1,6 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink; -import info.nightscout.androidaps.plugins.pump.common.R; - /** * Created by andy on 16/05/2018. */ diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java index 9d8c976363..a360c13d23 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java @@ -16,7 +16,7 @@ import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.pump.common.R; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.RileyLinkCommand; diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java index 663ed099e5..64d6950020 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java @@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; import java.util.HashMap; import java.util.Map; -import info.nightscout.androidaps.plugins.pump.common.R; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.utils.resources.ResourceHelper; public enum RileyLinkEncodingType { @@ -21,11 +21,11 @@ public enum RileyLinkEncodingType { private static Map encodingTypeMap; RileyLinkEncodingType(int value) { - this.value = (byte)value; + this.value = (byte) value; } RileyLinkEncodingType(int value, Integer resourceId) { - this.value = (byte)value; + this.value = (byte) value; this.resourceId = resourceId; } @@ -33,7 +33,7 @@ public enum RileyLinkEncodingType { encodingTypeMap = new HashMap<>(); for (RileyLinkEncodingType encType : values()) { - if (encType.resourceId!=null) { + if (encType.resourceId != null) { encodingTypeMap.put(resourceHelper.gs(encType.resourceId), encType); } } diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java index 0334c8d54e..0a72d9a512 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; -import info.nightscout.androidaps.plugins.pump.common.R; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; /** * Created by andy on 14/05/2018. @@ -43,8 +43,8 @@ public enum RileyLinkError { if (this.resourceIdPod != null) { return targetDevice == RileyLinkTargetDevice.MedtronicPump ? // - this.resourceId - : this.resourceIdPod; + this.resourceId + : this.resourceIdPod; } else { return this.resourceId; } diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java index 97ce0c6ce1..3a6bee96b6 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; -import info.nightscout.androidaps.plugins.pump.common.R; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; /** * Created by andy on 14/05/2018. @@ -13,21 +13,21 @@ public enum RileyLinkServiceState { // Bluetooth BluetoothInitializing(R.string.rileylink_state_bt_init), // (S) init BT (if error no BT interface -> Disabled, BT - // not enabled -> BluetoothError) + // not enabled -> BluetoothError) // BluetoothNotAvailable, // (E) BT not available, would happen only if device has no BT BluetoothError(R.string.rileylink_state_bt_error), // (E) if BT gets disabled ( -> EnableBluetooth) BluetoothReady(R.string.rileylink_state_bt_ready), // (OK) // RileyLink RileyLinkInitializing(R.string.rileylink_state_rl_init), // (S) start Gatt discovery (OK -> RileyLinkReady, Error -> - // BluetoothEnabled) ?? + // BluetoothEnabled) ?? RileyLinkError(R.string.rileylink_state_rl_error), // (E) RileyLinkReady(R.string.rileylink_state_rl_ready), // (OK) if tunning was already done we go to PumpConnectorReady // Tunning TuneUpDevice(R.string.rileylink_state_pc_tune_up), // (S) PumpConnectorError(R.string.rileylink_state_pc_error), // either TuneUp Error or pump couldn't not be contacted - // error + // error PumpConnectorReady(R.string.rileylink_state_connected), // (OK) RileyLink Ready for Pump Communication // Initializing, // get all parameters required for connection (if not possible -> Disabled, if sucessful -> @@ -58,17 +58,17 @@ public enum RileyLinkServiceState { public boolean isConnecting() { return (this == RileyLinkServiceState.BluetoothInitializing || // - // this == RileyLinkServiceState.BluetoothError || // - this == RileyLinkServiceState.BluetoothReady || // - this == RileyLinkServiceState.RileyLinkInitializing || // - this == RileyLinkReady - // this == RileyLinkServiceState.RileyLinkBLEError + // this == RileyLinkServiceState.BluetoothError || // + this == RileyLinkServiceState.BluetoothReady || // + this == RileyLinkServiceState.RileyLinkInitializing || // + this == RileyLinkReady + // this == RileyLinkServiceState.RileyLinkBLEError ); } public boolean isError() { return (this == RileyLinkServiceState.BluetoothError || // - // this == RileyLinkServiceState.PumpConnectorError || // - this == RileyLinkServiceState.RileyLinkError); + // this == RileyLinkServiceState.PumpConnectorError || // + this == RileyLinkServiceState.RileyLinkError); } } diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java index 1e560d0801..aff01e36a0 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; -import info.nightscout.androidaps.plugins.pump.common.R; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; /** * Created by andy on 5/19/18. diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java index 882f7a84ca..beff5bb026 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; @@ -10,16 +11,14 @@ import androidx.viewpager.widget.ViewPager; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.tabs.TabLayout; -import androidx.annotation.NonNull; - import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; -import info.nightscout.androidaps.plugins.pump.common.R; import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.utils.resources.ResourceHelper; public class RileyLinkStatusActivity extends NoSplashAppCompatActivity { diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java index 3faff82981..8bc967bd9f 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java @@ -15,14 +15,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import info.nightscout.androidaps.plugins.pump.common.R; import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.CommandValueDefinition; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; -//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.CommandValueDefinition; -//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; -//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; -//import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; + /** * Created by andy on 5/19/18. @@ -140,7 +137,7 @@ public class RileyLinkStatusDevice extends Fragment implements RefreshableInterf viewHolder.itemValue = view.findViewById(R.id.rileylink_device_action); view.setTag(viewHolder); } else { - viewHolder = (RileyLinkStatusDevice.ViewHolder)view.getTag(); + viewHolder = (RileyLinkStatusDevice.ViewHolder) view.getTag(); } // Z // RLHistoryItem item = historyItemList.get(i); diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneralFragment.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneralFragment.java index 2ae48e3f5a..7ab6625951 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneralFragment.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneralFragment.java @@ -15,8 +15,8 @@ import javax.inject.Inject; import dagger.android.support.DaggerFragment; import info.nightscout.androidaps.interfaces.ActivePlugin; import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.plugins.pump.common.R; import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpDevice; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpInfo; diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistoryFragment.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistoryFragment.java index 452c99f905..7ae38c4d58 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistoryFragment.java +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistoryFragment.java @@ -6,11 +6,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.annotation.NonNull; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -18,9 +17,9 @@ import java.util.List; import javax.inject.Inject; import dagger.android.support.DaggerFragment; -import info.nightscout.androidaps.plugins.pump.common.R; import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState; import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.R; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; import info.nightscout.androidaps.utils.DateUtil; diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.java b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.java deleted file mode 100644 index badd401083..0000000000 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ProfileUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common.utils; - -import java.util.Locale; - -import info.nightscout.androidaps.interfaces.Profile; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; - -public class ProfileUtil { - - - public static String getProfileDisplayable(Profile profile, PumpType pumpType) { - - StringBuilder stringBuilder = new StringBuilder(); - - for (Profile.ProfileValue basalValue : profile.getBasalValues()) { - - double basalValueValue = pumpType.determineCorrectBasalSize(basalValue.getValue()); - - int hour = basalValue.getTimeAsSeconds() / (60 * 60); - - stringBuilder.append((hour < 10 ? "0" : "") + hour + ":00"); - - stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)); - stringBuilder.append(", "); - } - if (stringBuilder.length() > 3) - return stringBuilder.substring(0, stringBuilder.length() - 2); - else - return stringBuilder.toString(); - } - - public static String getBasalProfilesDisplayable(Profile.ProfileValue[] profiles, PumpType pumpType) { - - StringBuilder stringBuilder = new StringBuilder(); - - for (Profile.ProfileValue basalValue : profiles) { - - double basalValueValue = pumpType.determineCorrectBasalSize(basalValue.getValue()); - - int hour = basalValue.getTimeAsSeconds() / (60 * 60); - - stringBuilder.append((hour < 10 ? "0" : "") + hour + ":00"); - - stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)); - stringBuilder.append(", "); - } - if (stringBuilder.length() > 3) - return stringBuilder.substring(0, stringBuilder.length() - 2); - else - return stringBuilder.toString(); - } - - -} diff --git a/rileylink/src/main/res/layout/rileylink_status_device.xml b/rileylink/src/main/res/layout/rileylink_status_device.xml index edf3cbdd7e..f56c4b0d6d 100644 --- a/rileylink/src/main/res/layout/rileylink_status_device.xml +++ b/rileylink/src/main/res/layout/rileylink_status_device.xml @@ -2,7 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusDevice"> + tools:context=".plugins.pump.medtronic.dialog.RileyLinkStatusDevice">