Merge pull request #531 from andyrozman/meallink-mdt
Medtronic driver changes - Kotlin and PumpSync
This commit is contained in:
commit
66a70fcd22
174 changed files with 9872 additions and 13226 deletions
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -72,21 +72,38 @@ 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
|
||||
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
|
||||
|
||||
@Binds
|
||||
fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
|
||||
|
||||
@Binds
|
||||
fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder
|
||||
|
||||
@Binds
|
||||
fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
|
||||
|
||||
@Binds
|
||||
fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
|
||||
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface
|
||||
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
|
||||
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
|
||||
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
|
||||
|
||||
@Binds
|
||||
fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
|
||||
|
||||
@Binds
|
||||
fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
|
||||
|
||||
@Binds
|
||||
fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
|
||||
@Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync
|
||||
|
||||
}
|
||||
|
|
|
@ -184,4 +184,4 @@ class PluginStore @Inject constructor(
|
|||
|
||||
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package info.nightscout.androidaps.db
|
||||
|
||||
interface DbObjectBase {
|
||||
|
||||
val date: Long
|
||||
val pumpId: Long
|
||||
}
|
|
@ -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<OmnipodHistoryRecord> {
|
||||
public class OmnipodHistoryRecord implements Comparable<OmnipodHistoryRecord> {
|
||||
|
||||
@DatabaseField(id = true)
|
||||
public long date;
|
||||
|
@ -42,7 +42,6 @@ public class OmnipodHistoryRecord implements DbObjectBase, Comparable<OmnipodHis
|
|||
generatePumpId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDate() {
|
||||
return this.date;
|
||||
}
|
||||
|
@ -91,7 +90,6 @@ public class OmnipodHistoryRecord implements DbObjectBase, Comparable<OmnipodHis
|
|||
this.successConfirmed = successConfirmed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPumpId() {
|
||||
return pumpId;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
|||
|
||||
@Deprecated
|
||||
@DatabaseTable(tableName = "TemporaryBasals")
|
||||
public class TemporaryBasal implements Interval, DbObjectBase {
|
||||
public class TemporaryBasal implements Interval {
|
||||
|
||||
@Inject public AAPSLogger aapsLogger;
|
||||
@Inject public ProfileFunction profileFunction;
|
||||
|
@ -360,12 +360,10 @@ public class TemporaryBasal implements Interval, DbObjectBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDate() {
|
||||
return this.date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPumpId() {
|
||||
return this.pumpId;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ import javax.inject.Inject;
|
|||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.core.R;
|
||||
import info.nightscout.androidaps.interfaces.Profile;
|
||||
import info.nightscout.androidaps.database.entities.Bolus;
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin;
|
||||
import info.nightscout.androidaps.interfaces.Insulin;
|
||||
import info.nightscout.androidaps.interfaces.Profile;
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction;
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||
|
@ -33,7 +33,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
|||
|
||||
@Deprecated
|
||||
@DatabaseTable(tableName = Treatment.TABLE_TREATMENTS)
|
||||
public class Treatment implements DataPointWithLabelInterface, DbObjectBase {
|
||||
public class Treatment implements DataPointWithLabelInterface {
|
||||
@Inject public DefaultValueHelper defaultValueHelper;
|
||||
@Inject public ResourceHelper resourceHelper;
|
||||
@Inject public ProfileFunction profileFunction;
|
||||
|
@ -270,12 +270,10 @@ public class Treatment implements DataPointWithLabelInterface, DbObjectBase {
|
|||
|
||||
// ----------------- DataPointInterface end --------------------
|
||||
|
||||
@Override
|
||||
public long getDate() {
|
||||
return this.date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPumpId() {
|
||||
return this.pumpId;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.general.actions.defs
|
||||
|
||||
interface CustomActionType {
|
||||
val key: String?
|
||||
fun getKey(): String
|
||||
}
|
|
@ -320,32 +320,6 @@
|
|||
<string name="timedetection">Time detection</string>
|
||||
<string name="format_hour_minute">%1$dh %2$dm</string>
|
||||
|
||||
<!-- PumoCommon - Pump Abstract -->
|
||||
<string name="pump_operation_not_supported_by_pump_driver">Operation not supported by pump and/or driver.</string>
|
||||
<string name="pump_operation_not_yet_supported_by_pump">Operation not YET supported by pump.</string>
|
||||
<string name="common_resultok">OK</string>
|
||||
|
||||
<!-- PumoCommon - Pump Status -->
|
||||
<string name="pump_status_never_contacted">Never contacted</string>
|
||||
<string name="pump_status_waking_up">Waking up</string>
|
||||
<string name="pump_status_error_comm">Error with communication</string>
|
||||
<string name="pump_status_timeout_comm">Timeout on communication</string>
|
||||
<string name="pump_status_pump_unreachable">Pump unreachable</string>
|
||||
<string name="pump_status_invalid_config">Invalid configuration</string>
|
||||
<string name="pump_status_active">Active</string>
|
||||
<string name="pump_status_sleeping">Sleeping</string>
|
||||
|
||||
<!-- PumpCommon - History Group -->
|
||||
<string name="history_group_basal">Basals</string>
|
||||
<string name="history_group_configuration">Configurations</string>
|
||||
<string name="history_group_notification">Notifications</string>
|
||||
<string name="history_group_statistic">Statistics</string>
|
||||
<string name="history_group_unknown">Unknowns</string>
|
||||
<string name="history_group_all">All</string>
|
||||
<string name="history_group_bolus">Boluses</string>
|
||||
<string name="history_group_prime">Prime</string>
|
||||
<string name="history_group_alarm">Alarms</string>
|
||||
<string name="history_group_glucose">Glucose</string>
|
||||
<string name="mute5min">Mute for 5 minutes</string>
|
||||
|
||||
<!-- Maintenance -->
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -16,5 +16,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation project(':core')
|
||||
implementation project(':pump-common')
|
||||
implementation project(':rileylink')
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
* <p>
|
||||
* 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<PumpMessage> {
|
||||
|
||||
@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<List<Byte>> 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<Byte> 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<PumpHistoryEntry> 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<String, PumpSettingDTO> getPumpSettings() {
|
||||
|
||||
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.getSettings(medtronicUtil
|
||||
.getMedtronicPumpModel()));
|
||||
|
||||
return responseObject == null ? null : (Map<String, PumpSettingDTO>) 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<List<Byte>> 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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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<PumpMessage?>() {
|
||||
|
||||
@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<List<Byte>>): 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 <reified T> 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<String, PumpSettingDTO>? {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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<String, PumpSettingDTO> decodeSettingsLoop(byte[] rd) {
|
||||
|
||||
Map<String, PumpSettingDTO> 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<String, PumpSettingDTO> decodeSettings512(byte[] rd) {
|
||||
|
||||
Map<String, PumpSettingDTO> 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<String, PumpSettingDTO> map) {
|
||||
map.put(key, new PumpSettingDTO(key, value, group));
|
||||
}
|
||||
|
||||
|
||||
public Map<String, PumpSettingDTO> decodeSettings(byte[] rd) {
|
||||
Map<String, PumpSettingDTO> 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<String, PumpSettingDTO> 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));
|
||||
}
|
||||
}
|
|
@ -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<String, PumpSettingDTO> {
|
||||
val map: MutableMap<String, PumpSettingDTO> = 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<String, PumpSettingDTO> {
|
||||
val map: MutableMap<String, PumpSettingDTO> = 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<String, PumpSettingDTO>) {
|
||||
map[key] = PumpSettingDTO(key, value, group)
|
||||
}
|
||||
|
||||
fun decodeSettings(rd: ByteArray): Map<String, PumpSettingDTO> {
|
||||
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<String, PumpSettingDTO>) {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* Author: Andy {andy.rozman@gmail.com}
|
||||
*/
|
||||
|
||||
public abstract class MedtronicHistoryDecoder<T extends MedtronicHistoryEntry> implements MedtronicHistoryDecoderInterface<T> {
|
||||
|
||||
@Inject protected AAPSLogger aapsLogger;
|
||||
@Inject protected MedtronicUtil medtronicUtil;
|
||||
|
||||
protected ByteUtil bitUtils;
|
||||
|
||||
// STATISTICS (remove at later time or not)
|
||||
protected boolean statisticsEnabled = true;
|
||||
protected Map<Integer, Integer> unknownOpCodes;
|
||||
protected Map<RecordDecodeStatus, Map<String, String>> mapStatistics;
|
||||
|
||||
|
||||
public MedtronicHistoryDecoder() {
|
||||
}
|
||||
|
||||
|
||||
// public abstract <E extends MedtronicHistoryEntry> Class<E> 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<Byte> checkPage(RawHistoryPage page, boolean partial) throws RuntimeException {
|
||||
List<Byte> byteList = new ArrayList<Byte>();
|
||||
|
||||
// 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<T> 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<RecordDecodeStatus, Map<String, String>> entry : mapStatistics.entrySet()) {
|
||||
sb = new StringBuilder();
|
||||
|
||||
if (entry.getKey() != RecordDecodeStatus.OK) {
|
||||
if (entry.getValue().size() == 0)
|
||||
continue;
|
||||
|
||||
for (Map.Entry<String, String> 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<T> processPageAndCreateRecords(RawHistoryPage rawHistoryPage, boolean partial) {
|
||||
List<Byte> dataClear = checkPage(rawHistoryPage, partial);
|
||||
List<T> records = createRecords(dataClear);
|
||||
|
||||
for (T record : records) {
|
||||
decodeRecord(record);
|
||||
}
|
||||
|
||||
runPostDecodeTasks();
|
||||
|
||||
return records;
|
||||
}
|
||||
}
|
|
@ -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<T : MedtronicHistoryEntry?>(var aapsLogger: AAPSLogger,
|
||||
var medtronicUtil: MedtronicUtil,
|
||||
var bitUtils: ByteUtil) : MedtronicHistoryDecoderInterface<T> {
|
||||
|
||||
// STATISTICS (remove at later time or not)
|
||||
protected var statisticsEnabled = true
|
||||
protected var unknownOpCodes: MutableMap<Int, Int?> = mutableMapOf()
|
||||
protected var mapStatistics: MutableMap<RecordDecodeStatus, MutableMap<String, String>> = 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<Byte> {
|
||||
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<T> {
|
||||
val dataClear = checkPage(rawHistoryPage)
|
||||
val records: MutableList<T> = 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<RecordDecodeStatus, MutableMap<String, String>>)[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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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<T> {
|
||||
|
||||
RecordDecodeStatus decodeRecord(T record);
|
||||
|
||||
List<T> createRecords(List<Byte> dataClear);
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history
|
||||
|
||||
/**
|
||||
* Created by andy on 3/10/19.
|
||||
*/
|
||||
interface MedtronicHistoryDecoderInterface<T> {
|
||||
|
||||
fun decodeRecord(record: T): RecordDecodeStatus?
|
||||
fun createRecords(dataClearInput: MutableList<Byte>): MutableList<T>
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* Author: Andy {andy.rozman@gmail.com}
|
||||
*/
|
||||
|
||||
public abstract class MedtronicHistoryEntry implements MedtronicHistoryEntryInterface {
|
||||
|
||||
protected List<Byte> 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<String, Object> 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<Byte> 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<String, Object> 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<Byte> 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<Byte> 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();
|
||||
|
||||
}
|
|
@ -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<Byte>
|
||||
|
||||
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<String, Any> = 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<Byte>, 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();
|
||||
}
|
|
@ -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<Byte> listRawData, boolean doNotProcess);
|
||||
|
||||
int getDateLength();
|
||||
|
||||
}
|
|
@ -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<Byte>, doNotProcess: Boolean)
|
||||
val dateLength: Int
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 ");
|
||||
|
||||
}
|
|
@ -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<Byte> 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)));
|
||||
|
||||
}
|
||||
}
|
|
@ -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<Byte>, 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)))
|
||||
}
|
||||
}
|
|
@ -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<Integer, CGMSHistoryEntryType> 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 //
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Int, CGMSHistoryEntryType> = 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
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* Author: Andy {andy.rozman@gmail.com}
|
||||
*/
|
||||
|
||||
public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder<CGMSHistoryEntry> {
|
||||
|
||||
//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<CGMSHistoryEntry> createRecords(List<Byte> dataClearInput) {
|
||||
|
||||
List<Byte> dataClear = reverseList(dataClearInput, Byte.class);
|
||||
|
||||
prepareStatistics();
|
||||
|
||||
int counter = 0;
|
||||
|
||||
List<CGMSHistoryEntry> outList = new ArrayList<CGMSHistoryEntry>();
|
||||
|
||||
// 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<Byte> listRawData = new ArrayList<Byte>();
|
||||
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<CGMSHistoryEntry> 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 <E> List<E> reverseList(List<E> dataClearInput, Class<E> clazz) {
|
||||
|
||||
List<E> outList = new ArrayList<E>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<CGMSHistoryEntry>(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<Byte>): MutableList<CGMSHistoryEntry> {
|
||||
dataClearInput.reverse()
|
||||
val dataClear = dataClearInput //reverseList(dataClearInput, Byte::class.java)
|
||||
prepareStatistics()
|
||||
var counter = 0
|
||||
val outList: MutableList<CGMSHistoryEntry> = 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<Byte> = 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 <E> reverseList(dataClearInput: List<E>, clazz: Class<E>): List<E> {
|
||||
// val outList: MutableList<E> = 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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* Author: Andy {andy.rozman@gmail.com}
|
||||
*/
|
||||
|
||||
@Singleton
|
||||
public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder<PumpHistoryEntry> {
|
||||
|
||||
private PumpHistoryEntry tbrPreviousRecord;
|
||||
private PumpHistoryEntry changeTimeRecord;
|
||||
|
||||
@Inject
|
||||
public MedtronicPumpHistoryDecoder(
|
||||
AAPSLogger aapsLogger,
|
||||
MedtronicUtil medtronicUtil
|
||||
) {
|
||||
super.aapsLogger = aapsLogger;
|
||||
this.medtronicUtil = medtronicUtil;
|
||||
}
|
||||
|
||||
|
||||
public List<PumpHistoryEntry> createRecords(List<Byte> dataClear) {
|
||||
prepareStatistics();
|
||||
|
||||
int counter = 0;
|
||||
int record = 0;
|
||||
boolean incompletePacket;
|
||||
|
||||
List<PumpHistoryEntry> 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<Byte> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<PumpHistoryEntry>(aapsLogger, medtronicUtil, bitUtils) {
|
||||
|
||||
//private var tbrPreviousRecord: PumpHistoryEntry? = null
|
||||
private var changeTimeRecord: PumpHistoryEntry? = null
|
||||
|
||||
override fun createRecords(dataClearInput: MutableList<Byte>): MutableList<PumpHistoryEntry> {
|
||||
prepareStatistics()
|
||||
var counter = 0
|
||||
var record = 0
|
||||
var incompletePacket: Boolean
|
||||
val outList: MutableList<PumpHistoryEntry> = 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<Byte> = 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* 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<PumpHistoryEntry> {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<PumpHistoryEntry> {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* 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<Integer, PumpHistoryEntryType> 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<SpecialRule> specialRulesHead;
|
||||
private List<SpecialRule> 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<SpecialRule> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Byte, PumpHistoryEntryType?> = 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<SpecialRule>? = null
|
||||
private var specialRulesBody: MutableList<SpecialRule>? = 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<SpecialRule>): 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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
* <p>
|
||||
* 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<PumpHistoryEntry> unprocessedEntries;
|
||||
public List<PumpHistoryEntry> 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<PumpHistoryEntry> 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<PumpHistoryEntry> getValidEntries() {
|
||||
return validEntries;
|
||||
}
|
||||
|
||||
enum SearchType {
|
||||
None, //
|
||||
LastEntry, //
|
||||
Date
|
||||
}
|
||||
}
|
|
@ -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<PumpHistoryEntry> = ArrayList()
|
||||
var validEntries: MutableList<PumpHistoryEntry> = ArrayList()
|
||||
|
||||
fun addHistoryEntries(entries: List<PumpHistoryEntry> /*, 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()
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
// }
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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<Byte, PacketType> 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;
|
||||
}
|
||||
}
|
|
@ -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<Byte, PacketType> = 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()
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<Any>?): 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
|
||||
|
||||
}
|
|
@ -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<String, PumpSettingDTO> settings = (Map<String, PumpSettingDTO>) 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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<String, PumpSettingDTO>
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Any?>? = 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<Any>?) {
|
||||
this.injector = injector
|
||||
this.injector.androidInjector().inject(this)
|
||||
this.commandType = commandType
|
||||
this.parameters = parameters //as Array<Any>
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<BasalProfileEntry> 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<BasalProfileEntry> 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<BasalProfileEntry> 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<BasalProfileEntry> 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<BasalProfileEntry> 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<BasalProfileEntry> getEntries() {
|
||||
List<BasalProfileEntry> 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<Byte> 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<BasalProfileEntry> 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;
|
||||
}
|
||||
}
|
|
@ -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<BasalProfileEntry>? = 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<BasalProfileEntry> {
|
||||
val entries: MutableList<BasalProfileEntry> = 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<Byte> = 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<BasalProfileEntry>? = 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
* <p>
|
||||
* See AUTHORS for copyright information.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Filename: BolusDTO Description: Bolus DTO
|
||||
* <p>
|
||||
* 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() + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]"
|
||||
}
|
||||
}
|
|
@ -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() + "]";
|
||||
}
|
||||
}
|
|
@ -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 + "]"
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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() + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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 + "]"
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
* <p>
|
||||
* 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<Byte> 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() + "]";
|
||||
}
|
||||
}
|
|
@ -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<Byte> = 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 + "]")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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, //
|
||||
;
|
||||
|
||||
}
|
|
@ -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
|
||||
//
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 //
|
||||
);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue