- kotlinizing mdt
This commit is contained in:
parent
1e29239bde
commit
769ab218d4
32 changed files with 2590 additions and 2872 deletions
|
@ -0,0 +1,535 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> meallink
|
||||||
|
import dagger.android.HasAndroidInjector;
|
||||||
|
import info.nightscout.androidaps.core.R;
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||||
|
import info.nightscout.androidaps.data.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.ActivePluginProvider;
|
||||||
|
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
|
||||||
|
import info.nightscout.androidaps.interfaces.ConstraintsInterface;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
|
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.PumpDbEntry;
|
||||||
|
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 PumpInterface, ConstraintsInterface {
|
||||||
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
|
|
||||||
|
protected HasAndroidInjector injector;
|
||||||
|
protected AAPSLogger aapsLogger;
|
||||||
|
protected RxBusWrapper rxBus;
|
||||||
|
protected ActivePluginProvider 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;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> meallink
|
||||||
|
|
||||||
|
protected PumpPluginAbstract(
|
||||||
|
PluginDescription pluginDescription,
|
||||||
|
PumpType pumpType,
|
||||||
|
HasAndroidInjector injector,
|
||||||
|
ResourceHelper resourceHelper,
|
||||||
|
AAPSLogger aapsLogger,
|
||||||
|
CommandQueueProvider commandQueue,
|
||||||
|
RxBusWrapper rxBus,
|
||||||
|
ActivePluginProvider 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.setPumpDescription(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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// TODO fix
|
||||||
|
TemporaryBasal tb = activePlugin.getActiveTreatments().getTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
=======
|
||||||
|
PumpSync.PumpState.TemporaryBasal tb = pumpSync.expectedPumpState().getTemporaryBasal();
|
||||||
|
>>>>>>> meallink
|
||||||
|
if (tb != null) {
|
||||||
|
extended.put("TempBasalAbsoluteRate", convertedToAbsolute(tb, now, profile));
|
||||||
|
extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.getTimestamp()));
|
||||||
|
extended.put("TempBasalRemaining", getPlannedRemainingMinutes(tb));
|
||||||
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// TODO fix
|
||||||
|
ExtendedBolus eb = activePlugin.getActiveTreatments().getExtendedBolusFromHistory(System.currentTimeMillis());
|
||||||
|
=======
|
||||||
|
PumpSync.PumpState.ExtendedBolus eb = pumpSync.expectedPumpState().getExtendedBolus();
|
||||||
|
>>>>>>> meallink
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// TODO fix
|
||||||
|
TemporaryBasal activeTemp = activePlugin.getActiveTreatments().getRealTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
=======
|
||||||
|
PumpSync.PumpState.TemporaryBasal activeTemp = pumpSync.expectedPumpState().getTemporaryBasal();
|
||||||
|
>>>>>>> meallink
|
||||||
|
if (activeTemp != null) {
|
||||||
|
ret += "Temp: " + PumpStateExtensionKt.toStringFull(activeTemp, dateUtil) + "\n";
|
||||||
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
// TODO fix
|
||||||
|
ExtendedBolus activeExtendedBolus = activePlugin.getActiveTreatments().getExtendedBolusFromHistory(
|
||||||
|
System.currentTimeMillis());
|
||||||
|
=======
|
||||||
|
PumpSync.PumpState.ExtendedBolus activeExtendedBolus = pumpSync.expectedPumpState().getExtendedBolus();
|
||||||
|
>>>>>>> meallink
|
||||||
|
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)");
|
||||||
|
|
||||||
|
// TODO fix
|
||||||
|
// 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.setPumpDescription(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PumpSync
|
||||||
|
|
||||||
|
Map<Long, PumpDbEntry> driverHistory = new HashMap<>();
|
||||||
|
|
||||||
|
public abstract long generateTempId(long timeMillis);
|
||||||
|
|
||||||
|
public boolean addBolusWithTempId(DetailedBolusInfo detailedBolusInfo, boolean writeToInternalHistory) {
|
||||||
|
long temporaryId = generateTempId(detailedBolusInfo.timestamp);
|
||||||
|
boolean response = pumpSync.addBolusWithTempId(detailedBolusInfo.timestamp, detailedBolusInfo.insulin,
|
||||||
|
generateTempId(detailedBolusInfo.timestamp), detailedBolusInfo.getBolusType(),
|
||||||
|
getPumpType(), serialNumber());
|
||||||
|
|
||||||
|
if (response && writeToInternalHistory) {
|
||||||
|
driverHistory.put(temporaryId, new PumpDbEntry(temporaryId, model(), serialNumber(), detailedBolusInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTemporaryId(long temporaryId) {
|
||||||
|
driverHistory.remove(temporaryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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,132 @@
|
||||||
|
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
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.jvm.Throws
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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?> : MedtronicHistoryDecoderInterface<T> {
|
||||||
|
|
||||||
|
@JvmField @Inject
|
||||||
|
var aapsLogger: AAPSLogger? = null
|
||||||
|
|
||||||
|
@JvmField @Inject
|
||||||
|
var medtronicUtil: MedtronicUtil? = null
|
||||||
|
protected var bitUtils: ByteUtil? = null
|
||||||
|
|
||||||
|
// STATISTICS (remove at later time or not)
|
||||||
|
protected var statisticsEnabled = true
|
||||||
|
@JvmField protected var unknownOpCodes: MutableMap<Int, Int?>? = null
|
||||||
|
protected var mapStatistics: MutableMap<RecordDecodeStatus, MutableMap<String, String>>? = null
|
||||||
|
|
||||||
|
// public abstract <E extends MedtronicHistoryEntry> Class<E> getHistoryEntryClass();
|
||||||
|
// public abstract RecordDecodeStatus decodeRecord(T record);
|
||||||
|
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, partial: Boolean): List<Byte> {
|
||||||
|
val byteList: List<Byte> = ArrayList()
|
||||||
|
|
||||||
|
if (medtronicUtil!!.medtronicPumpModel == null) {
|
||||||
|
aapsLogger!!.error(LTag.PUMPCOMM, "Device Type is not defined.")
|
||||||
|
return byteList
|
||||||
|
}
|
||||||
|
return if (page.data.size != 1024) {
|
||||||
|
ByteUtil.getListFromByteArray(page.data)
|
||||||
|
} else if (page.isChecksumOK) {
|
||||||
|
ByteUtil.getListFromByteArray(page.onlyData)
|
||||||
|
} else {
|
||||||
|
byteList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processPageAndCreateRecords(rawHistoryPage: RawHistoryPage): List<T?>? {
|
||||||
|
return processPageAndCreateRecords(rawHistoryPage, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun prepareStatistics() {
|
||||||
|
if (!statisticsEnabled) return
|
||||||
|
unknownOpCodes = HashMap()
|
||||||
|
mapStatistics = HashMap()
|
||||||
|
for (stat in RecordDecodeStatus.values()) {
|
||||||
|
(mapStatistics as HashMap<RecordDecodeStatus, MutableMap<String, String>>)[stat] = HashMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, String.format(Locale.ENGLISH, " %s%s - %d. Elements: %s", key.name, spaces, value.size, sb.toString()))
|
||||||
|
} else {
|
||||||
|
aapsLogger!!.info(LTag.PUMPCOMM, String.format(Locale.ENGLISH, " %s - %d", 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processPageAndCreateRecords(rawHistoryPage: RawHistoryPage, partial: Boolean): List<T> {
|
||||||
|
val dataClear = checkPage(rawHistoryPage, partial)
|
||||||
|
val records: List<T> = createRecords(dataClear)
|
||||||
|
for (record in records!!) {
|
||||||
|
decodeRecord(record)
|
||||||
|
}
|
||||||
|
runPostDecodeTasks()
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(dataClear: List<Byte>): List<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,222 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
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
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
abstract class MedtronicHistoryEntry : MedtronicHistoryEntryInterface {
|
||||||
|
|
||||||
|
@JvmField var rawData: List<Byte>? = null
|
||||||
|
@JvmField protected var sizes = IntArray(3)
|
||||||
|
var head: ByteArray? = null
|
||||||
|
var datetime: ByteArray? = null
|
||||||
|
var body: ByteArray? = null
|
||||||
|
|
||||||
|
// protected LocalDateTime dateTime;
|
||||||
|
@JvmField var id: Long = 0
|
||||||
|
|
||||||
|
@JvmField @Expose
|
||||||
|
var DT: String? = null
|
||||||
|
|
||||||
|
@JvmField @Expose
|
||||||
|
var atechDateTime: Long? = null
|
||||||
|
|
||||||
|
@Expose
|
||||||
|
protected var decodedData: MutableMap<String, Any?>? = null
|
||||||
|
var phoneDateTime // time on phone
|
||||||
|
: Long = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pump id that will be used with AAPS object (time * 1000 + historyType (max is FF = 255)
|
||||||
|
*/
|
||||||
|
protected open var pumpId: Long? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 //= linkedObject
|
||||||
|
set(value) {
|
||||||
|
linked = true
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// fun setLinkedObject(linkedObject: Any?) {
|
||||||
|
// linked = true
|
||||||
|
// this.linkedObject = linkedObject
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun setData(listRawData: List<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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bodyLength > 0) {
|
||||||
|
body = ByteArray(bodyLength)
|
||||||
|
var i = headLength + dateTimeLength
|
||||||
|
var j = 0
|
||||||
|
while (j < bodyLength) {
|
||||||
|
body!![j] = listRawData!![i]!!
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val dateTimeString: String
|
||||||
|
get() = if (DT == null) "Unknown" else DT!!
|
||||||
|
|
||||||
|
val decodedDataAsString: String
|
||||||
|
get() = if (decodedData == null) if (isNoDataEntry) "No data" else "" else decodedData.toString()
|
||||||
|
|
||||||
|
fun hasData(): Boolean {
|
||||||
|
return decodedData != null || isNoDataEntry || entryTypeName == "UnabsorbedInsulin"
|
||||||
|
}
|
||||||
|
|
||||||
|
val isNoDataEntry: Boolean
|
||||||
|
get() = sizes[0] == 2 && sizes[1] == 5 && sizes[2] == 0
|
||||||
|
|
||||||
|
// fun getDecodedData(): Map<String, Any?>? {
|
||||||
|
// return decodedData
|
||||||
|
// }
|
||||||
|
|
||||||
|
fun getDecodedDataEntry(key: String?): Any? {
|
||||||
|
return if (decodedData != null) 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 != null) {
|
||||||
|
sb.append(", head=")
|
||||||
|
sb.append(ByteUtil.shortHexString(head))
|
||||||
|
}
|
||||||
|
if (datetime != null) {
|
||||||
|
sb.append(", datetime=")
|
||||||
|
sb.append(ByteUtil.shortHexString(datetime))
|
||||||
|
}
|
||||||
|
if (body != null) {
|
||||||
|
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 setAtechDateTime(dt: Long) {
|
||||||
|
atechDateTime = dt
|
||||||
|
DT = DateTimeUtil.toString(atechDateTime!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDecodedData(key: String, value: Any?) {
|
||||||
|
if (decodedData == null) decodedData = HashMap()
|
||||||
|
decodedData!![key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toShortString(): String {
|
||||||
|
return if (head == null) {
|
||||||
|
"Unidentified record. "
|
||||||
|
} else {
|
||||||
|
"HistoryRecord: head=[" + ByteUtil.shortHexString(head) + "]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containsDecodedData(key: String?): Boolean {
|
||||||
|
return if (decodedData == null) false else 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: List<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,55 @@
|
||||||
|
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? = null
|
||||||
|
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 setData(listRawData: List<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) {
|
||||||
|
setAtechDateTime(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,62 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val opCodeMap: MutableMap<Int, CGMSHistoryEntryType?> = HashMap()
|
||||||
|
@JvmStatic fun getByCode(opCode: Int): CGMSHistoryEntryType? {
|
||||||
|
return if (opCodeMap.containsKey(opCode)) {
|
||||||
|
opCodeMap[opCode]
|
||||||
|
} else None
|
||||||
|
}
|
||||||
|
|
||||||
|
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,269 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper.Companion.getLogger
|
||||||
|
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
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms.CGMSHistoryEntryType.Companion.getByCode
|
||||||
|
import okhttp3.internal.and
|
||||||
|
import org.joda.time.LocalDateTime
|
||||||
|
import org.slf4j.Logger
|
||||||
|
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 : MedtronicHistoryDecoder<CGMSHistoryEntry>() {
|
||||||
|
|
||||||
|
override fun decodeRecord(record: CGMSHistoryEntry): RecordDecodeStatus? {
|
||||||
|
return try {
|
||||||
|
decodeRecord(record, false)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
LOG.error(" Error decoding: type={}, ex={}", record.entryType!!.name, ex.message, ex)
|
||||||
|
RecordDecodeStatus.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeRecord(entry: CGMSHistoryEntry, ignore: Boolean): 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.None -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RecordDecodeStatus.NotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun postProcess() {}
|
||||||
|
override fun createRecords(dataClearInput: List<Byte>): List<CGMSHistoryEntry> {
|
||||||
|
val dataClear = reverseList(dataClearInput, Byte::class.java)
|
||||||
|
prepareStatistics()
|
||||||
|
var counter = 0
|
||||||
|
val outList: MutableList<CGMSHistoryEntry> = ArrayList()
|
||||||
|
|
||||||
|
// 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
|
||||||
|
LOG.warn("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)
|
||||||
|
val reversedOutList = 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(timeStamp!!)
|
||||||
|
getIndex = 0
|
||||||
|
} else if (entry.entryType === 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 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.setAtechDateTime(atechDateTime)
|
||||||
|
atechDateTime
|
||||||
|
} else if (entry.entryType!!.dateType === CGMSHistoryEntryType.DateType.SecondSpecific) {
|
||||||
|
LOG.warn("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()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOG: Logger = getLogger(LTag.PUMPCOMM)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,502 @@
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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?
|
||||||
|
) : MedtronicHistoryDecoder<PumpHistoryEntry>() {
|
||||||
|
|
||||||
|
private var tbrPreviousRecord: PumpHistoryEntry? = null
|
||||||
|
private var changeTimeRecord: PumpHistoryEntry? = null
|
||||||
|
override fun createRecords(dataClear: List<Byte>): List<PumpHistoryEntry> {
|
||||||
|
prepareStatistics()
|
||||||
|
var counter = 0
|
||||||
|
var record = 0
|
||||||
|
var incompletePacket: Boolean
|
||||||
|
val outList: MutableList<PumpHistoryEntry> = ArrayList()
|
||||||
|
var skipped: String? = null
|
||||||
|
if (dataClear!!.size == 0) {
|
||||||
|
aapsLogger!!.error(LTag.PUMPBTCOMM, "Empty page.")
|
||||||
|
return outList
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
val opCode: Int = dataClear[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!!)
|
||||||
|
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 = dataClear[counter]!!.toInt()
|
||||||
|
listRawData.add(elements.toByte())
|
||||||
|
counter++
|
||||||
|
val els = getUnsignedInt(elements)
|
||||||
|
for (k in 0 until els - 2) {
|
||||||
|
if (counter < 1022) {
|
||||||
|
listRawData.add(dataClear[counter])
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
special = true
|
||||||
|
} else {
|
||||||
|
for (j in 0 until entryType.getTotalLength(medtronicUtil!!.medtronicPumpModel) - 1) {
|
||||||
|
try {
|
||||||
|
listRawData.add(dataClear[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 as List<Byte>, 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 < dataClear.size)
|
||||||
|
return outList
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeRecord(record: PumpHistoryEntry): RecordDecodeStatus? {
|
||||||
|
return try {
|
||||||
|
decodeRecord(record, false)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger!!.error(LTag.PUMPBTCOMM, String.format(Locale.ENGLISH, " Error decoding: type=%s, ex=%s", record.entryType!!.name, ex.message, ex))
|
||||||
|
RecordDecodeStatus.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeRecord(entry: PumpHistoryEntry, x: Boolean): 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 -> {
|
||||||
|
// 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")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return RecordDecodeStatus.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
// LOG.debug("decodeBasalProfile: {}", entry);
|
||||||
|
val basalProfile = BasalProfile(aapsLogger)
|
||||||
|
basalProfile.setRawDataFromHistory(entry.body)
|
||||||
|
|
||||||
|
// LOG.debug("decodeBasalProfile BasalProfile: {}", basalProfile);
|
||||||
|
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) {
|
||||||
|
// this.writeData(PumpBaseType.Event, entry.getHead()[0] == 0 ? PumpEventType.BatteryRemoved :
|
||||||
|
// PumpEventType.BatteryReplaced, entry.getATechDate());
|
||||||
|
entry.displayableValue = if (entry.head!![0] == 0.toByte()) "Battery Removed" else "Battery Replaced"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeBasalProfileStart(entry: PumpHistoryEntry): RecordDecodeStatus {
|
||||||
|
val body = entry.body
|
||||||
|
// int bodyOffset = headerSize + timestampSize;
|
||||||
|
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 as IntArray
|
||||||
|
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 shl 6) + body[0]
|
||||||
|
dto.bloodGlucose = (body[1] and 0x03 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] as Int
|
||||||
|
dto.bgTargetHigh = body[14] as Int
|
||||||
|
dto.correctionEstimate = ((body[9] and 0x38 shl 5) + body[6]) / bolusStrokes
|
||||||
|
dto.foodEstimate = ((body[7] shl 8) + body[8]) / bolusStrokes
|
||||||
|
dto.unabsorbedInsulin = ((body[10] shl 8) + body[11]) / bolusStrokes
|
||||||
|
dto.bolusTotal = ((body[12] shl 8) + body[13]) / bolusStrokes
|
||||||
|
} else {
|
||||||
|
dto.bloodGlucose = body.get(1) and 0x0F shl 8 or entry.head!!.get(0).toInt()
|
||||||
|
dto.carbs = body.get(0) as Int
|
||||||
|
dto.carbRatio = body.get(2).toFloat()
|
||||||
|
dto.insulinSensitivity = body.get(3).toFloat()
|
||||||
|
dto.bgTargetLow = body.get(4) as Int
|
||||||
|
dto.bgTargetHigh = body.get(12) as Int
|
||||||
|
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 != null && 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 as IntArray
|
||||||
|
val dto = BolusWizardDTO()
|
||||||
|
val bolusStrokes = 10.0f
|
||||||
|
dto.bloodGlucose = body.get(1) and 0x03 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)
|
||||||
|
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 != null && 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 as IntArray
|
||||||
|
if (MedtronicDeviceType.isSameDevice(medtronicUtil!!.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) {
|
||||||
|
bolus.requestedAmount = ByteUtil.toInt(data.get(0), data.get(1)) / 40.0
|
||||||
|
bolus.deliveredAmount = ByteUtil.toInt(data.get(2), data.get(3)) / 40.0
|
||||||
|
bolus.insulinOnBoard = ByteUtil.toInt(data.get(4), data.get(5)) / 40.0
|
||||||
|
bolus.duration = data.get(6) * 30
|
||||||
|
} else {
|
||||||
|
bolus.requestedAmount = ByteUtil.asUINT8(data.get(0)) / 10.0
|
||||||
|
bolus.deliveredAmount = ByteUtil.asUINT8(data.get(1)) / 10.0
|
||||||
|
bolus.duration = ByteUtil.asUINT8(data.get(2)) * 30
|
||||||
|
}
|
||||||
|
bolus.bolusType = if (bolus.duration != null && bolus.duration > 0) PumpBolusType.Extended else PumpBolusType.Normal
|
||||||
|
bolus.setAtechDateTime(entry.atechDateTime!!)
|
||||||
|
entry.addDecodedData("Object", bolus)
|
||||||
|
entry.displayableValue = bolus.displayableValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// private fun decodeTempBasal(entry: PumpHistoryEntry) {
|
||||||
|
// if (tbrPreviousRecord == null) {
|
||||||
|
// // LOG.debug(this.tbrPreviousRecord.toString());
|
||||||
|
// tbrPreviousRecord = entry
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// decodeTempBasal(tbrPreviousRecord, entry)
|
||||||
|
// tbrPreviousRecord = null
|
||||||
|
// }
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempBasalPair tbr = new TempBasalPair(
|
||||||
|
// tbrRate.getHead()[0],
|
||||||
|
// tbrDuration.getHead()[0],
|
||||||
|
// (ByteUtil.asUINT8(tbrRate.getDatetime()[4]) >> 3) == 0);
|
||||||
|
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)
|
||||||
|
|
||||||
|
// System.out.println("TBR: amount=" + tbr.getInsulinRate() + ", duration=" + tbr.getDurationMinutes()
|
||||||
|
// // + " min. Packed: " + tbr.getValue()
|
||||||
|
// );
|
||||||
|
entry.addDecodedData("Object", tbr)
|
||||||
|
entry.displayableValue = tbr.description
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeDateTime(entry: PumpHistoryEntry) {
|
||||||
|
val dt = entry.datetime as IntArray
|
||||||
|
if (dt == null) {
|
||||||
|
aapsLogger!!.warn(LTag.PUMPBTCOMM, "DateTime not set.")
|
||||||
|
}
|
||||||
|
if (entry.dateTimeLength == 5) {
|
||||||
|
val seconds: Int = (dt!!.get(0) and 0x3F).toInt()
|
||||||
|
val minutes: Int = (dt!!.get(1) and 0x3F).toInt()
|
||||||
|
val hour: Int = (dt.get(2) and 0x1F).toInt()
|
||||||
|
val month: Int = (dt.get(0) shr 4 and 0x0c) + (dt.get(1) shr 6 and 0x03)
|
||||||
|
// ((dt[0] & 0xC0) >> 6) | ((dt[1] & 0xC0) >> 4);
|
||||||
|
val dayOfMonth: Int = dt.get(3) and 0x1F
|
||||||
|
val year = fix2DigitYear(dt.get(4) and 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.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
|
||||||
|
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.setAtechDateTime(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 year = year
|
||||||
|
year += if (year > 90) {
|
||||||
|
1900
|
||||||
|
} else {
|
||||||
|
2000
|
||||||
|
}
|
||||||
|
return year
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun getFormattedValue(value: Float, decimals: Int): String {
|
||||||
|
return String.format(Locale.ENGLISH, "%." + decimals + "f", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
super.aapsLogger = aapsLogger
|
||||||
|
this.medtronicUtil = medtronicUtil
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,117 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
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.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? = null
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// // override fun getOpCode(): Int {
|
||||||
|
// // return
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// fun setOpCode(opCode: Int?) {
|
||||||
|
// this.opCode = opCode
|
||||||
|
// }
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
var displayableValue = ""
|
||||||
|
|
||||||
|
fun setEntryType(medtronicDeviceType: MedtronicDeviceType?, entryType: PumpHistoryEntryType) {
|
||||||
|
this.entryType = entryType
|
||||||
|
sizes[0] = entryType.getHeadLength(medtronicDeviceType)
|
||||||
|
sizes[1] = entryType.dateLength
|
||||||
|
sizes[2] = entryType.getBodyLength(medtronicDeviceType)
|
||||||
|
if (this.entryType != null && atechDateTime != null) setPumpId()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setPumpId() {
|
||||||
|
pumpId = entryType!!.code + atechDateTime!! * 1000L
|
||||||
|
}
|
||||||
|
|
||||||
|
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(o: Any?): Boolean {
|
||||||
|
if (this === o) return true
|
||||||
|
if (o !is PumpHistoryEntry) return false
|
||||||
|
val that = o
|
||||||
|
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 == null) {
|
||||||
|
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? = null
|
||||||
|
get() {
|
||||||
|
setPumpId()
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(pumpId) {
|
||||||
|
super.pumpId = pumpId
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
||||||
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,323 @@
|
||||||
|
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
|
||||||
|
@JvmOverloads constructor(opCode: Byte, name: String?, group: PumpHistoryEntryGroup, head: Int = 2, date: Int = 5, body: 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), // 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(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))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun getByCode(opCode: Byte): PumpHistoryEntryType? {
|
||||||
|
return if (opCodeMap.containsKey(opCode)) {
|
||||||
|
opCodeMap[opCode]
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val code: Byte
|
||||||
|
private val description: String?
|
||||||
|
private val headLength: Int
|
||||||
|
val dateLength: Int
|
||||||
|
|
||||||
|
// private MinimedDeviceType deviceType;
|
||||||
|
private val bodyLength: Int
|
||||||
|
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
|
||||||
|
val group: PumpHistoryEntryGroup
|
||||||
|
|
||||||
|
private constructor(opCode: Byte, group: PumpHistoryEntryGroup) : this(opCode, null, group, 2, 5, 0) {}
|
||||||
|
private constructor(opCode: Byte, group: PumpHistoryEntryGroup, head: Int, date: Int, body: Int) : this(opCode, null, group, head, date, body) {}
|
||||||
|
|
||||||
|
fun getTotalLength(medtronicDeviceType: MedtronicDeviceType?): Int {
|
||||||
|
return if (hasSpecialRules()) {
|
||||||
|
getHeadLength(medtronicDeviceType) + getBodyLength(medtronicDeviceType) + dateLength
|
||||||
|
} else {
|
||||||
|
totalLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasSpecialRules(): Boolean {
|
||||||
|
return hasSpecialRules
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSpecialRuleHead(rule: SpecialRule) {
|
||||||
|
if (isEmpty(specialRulesHead)) {
|
||||||
|
specialRulesHead = ArrayList()
|
||||||
|
}
|
||||||
|
specialRulesHead!!.add(rule)
|
||||||
|
hasSpecialRules = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSpecialRuleBody(rule: SpecialRule) {
|
||||||
|
if (isEmpty(specialRulesBody)) {
|
||||||
|
specialRulesBody = ArrayList()
|
||||||
|
}
|
||||||
|
specialRulesBody!!.add(rule)
|
||||||
|
hasSpecialRules = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDescription(): String {
|
||||||
|
return description ?: name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHeadLength(medtronicDeviceType: MedtronicDeviceType?): Int {
|
||||||
|
return if (hasSpecialRules) {
|
||||||
|
if (isNotEmpty(specialRulesHead)) {
|
||||||
|
determineSizeByRule(medtronicDeviceType, headLength, specialRulesHead)
|
||||||
|
} else {
|
||||||
|
headLength
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
headLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBodyLength(medtronicDeviceType: MedtronicDeviceType?): Int {
|
||||||
|
return if (hasSpecialRules) {
|
||||||
|
if (isNotEmpty(specialRulesBody)) {
|
||||||
|
determineSizeByRule(medtronicDeviceType, bodyLength, specialRulesBody)
|
||||||
|
} else {
|
||||||
|
bodyLength
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bodyLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isNotEmpty(list: List<*>?): Boolean {
|
||||||
|
return list != null && !list.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isEmpty(list: List<*>?): Boolean {
|
||||||
|
return list == null || list.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
this.code = opCode //as Byte.toInt()
|
||||||
|
description = name
|
||||||
|
headLength = head
|
||||||
|
dateLength = date
|
||||||
|
bodyLength = body
|
||||||
|
totalLength = head + date + body
|
||||||
|
this.group = group
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,144 @@
|
||||||
|
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.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
@JvmField var unprocessedEntries: List<PumpHistoryEntry?>? = null
|
||||||
|
@JvmField var validEntries: MutableList<PumpHistoryEntry?>?
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchType.Date -> {
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, "PE. Date search: Search date: " + searchDate)
|
||||||
|
for (unprocessedEntry in unprocessedEntries!!) {
|
||||||
|
if (unprocessedEntry!!.atechDateTime == null || 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=" + (if (unprocessedEntries != null) "" + unprocessedEntries!!.size else "0") + //
|
||||||
|
", valid=" + (if (validEntries != null) "" + validEntries!!.size else "0") + //
|
||||||
|
", searchEntry=" + searchEntry + //
|
||||||
|
", searchDate=" + searchDate + //
|
||||||
|
", searchType=" + searchType + //
|
||||||
|
", searchFinished=" + isSearchFinished + //
|
||||||
|
"]"
|
||||||
|
}// PumpHistoryEntry pumpHistoryEntry = this.validEntries.get(0);
|
||||||
|
//
|
||||||
|
// if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.EndResultTotals)
|
||||||
|
// return pumpHistoryEntry;
|
||||||
|
// else
|
||||||
|
// return this.validEntries.get(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return latest entry (entry with highest date time)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
val latestEntry: PumpHistoryEntry?
|
||||||
|
get() = if (validEntries == null || validEntries!!.size == 0) null else {
|
||||||
|
validEntries!![0]
|
||||||
|
// PumpHistoryEntry pumpHistoryEntry = this.validEntries.get(0);
|
||||||
|
//
|
||||||
|
// if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.EndResultTotals)
|
||||||
|
// return pumpHistoryEntry;
|
||||||
|
// else
|
||||||
|
// return this.validEntries.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
val isSearchRequired: Boolean
|
||||||
|
get() = searchType != SearchType.None
|
||||||
|
|
||||||
|
fun getValidEntries(): List<PumpHistoryEntry?>? {
|
||||||
|
return validEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,15 +74,15 @@ public class DailyTotalsDTO {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DailyTotals515:
|
case DailyTotals515:
|
||||||
decodeDailyTotals515(entry.getBody());
|
decodeDailyTotals515(entry.body);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DailyTotals522:
|
case DailyTotals522:
|
||||||
decodeDailyTotals522(entry.getBody());
|
decodeDailyTotals522(entry.body);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DailyTotals523:
|
case DailyTotals523:
|
||||||
decodeDailyTotals523(entry.getBody());
|
decodeDailyTotals523(entry.body);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -96,18 +96,18 @@ public class DailyTotalsDTO {
|
||||||
private void setDisplayable() {
|
private void setDisplayable() {
|
||||||
|
|
||||||
if (this.insulinBasal == null) {
|
if (this.insulinBasal == null) {
|
||||||
this.entry.setDisplayableValue("Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2));
|
this.entry.displayableValue = "Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2);
|
||||||
} else {
|
} else {
|
||||||
this.entry.setDisplayableValue("Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2)
|
this.entry.displayableValue = "Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2)
|
||||||
+ ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2));
|
+ ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void decodeEndResultsTotals(PumpHistoryEntry entry) {
|
private void decodeEndResultsTotals(PumpHistoryEntry entry) {
|
||||||
double totals = ByteUtil.toInt((int) entry.getHead()[0], (int) entry.getHead()[1], (int) entry.getHead()[2],
|
double totals = ByteUtil.toInt((int) entry.head[0], (int) entry.head[1], (int) entry.head[2],
|
||||||
(int) entry.getHead()[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d;
|
(int) entry.head[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d;
|
||||||
|
|
||||||
this.insulinTotal = totals;
|
this.insulinTotal = totals;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class MedtronicHistoryActivity extends NoSplashAppCompatActivity {
|
||||||
this.filteredHistoryList.addAll(list);
|
this.filteredHistoryList.addAll(list);
|
||||||
} else {
|
} else {
|
||||||
for (PumpHistoryEntry pumpHistoryEntry : list) {
|
for (PumpHistoryEntry pumpHistoryEntry : list) {
|
||||||
if (pumpHistoryEntry.getEntryType().getGroup() == group) {
|
if (pumpHistoryEntry.getEntryType().group == group) {
|
||||||
this.filteredHistoryList.add(pumpHistoryEntry);
|
this.filteredHistoryList.add(pumpHistoryEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ public class MedtronicHistoryActivity extends NoSplashAppCompatActivity {
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
holder.timeView.setText(record.getDateTimeString());
|
holder.timeView.setText(record.getDateTimeString());
|
||||||
holder.typeView.setText(record.getEntryType().getDescription());
|
holder.typeView.setText(record.getEntryType().getDescription());
|
||||||
holder.valueView.setText(record.getDisplayableValue());
|
holder.valueView.setText(record.displayableValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.common.defs;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.core.R;
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
|
||||||
* management and modified/extended for AAPS.
|
|
||||||
* <p>
|
|
||||||
* Author: Andy {andy.rozman@gmail.com}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public enum PumpHistoryEntryGroup {
|
|
||||||
|
|
||||||
All(R.string.history_group_all),
|
|
||||||
Bolus(R.string.history_group_bolus),
|
|
||||||
Basal(R.string.history_group_basal),
|
|
||||||
Prime(R.string.history_group_prime),
|
|
||||||
Configuration(R.string.history_group_configuration),
|
|
||||||
Alarm(R.string.history_group_alarm),
|
|
||||||
Glucose(R.string.history_group_glucose),
|
|
||||||
Notification(R.string.history_group_notification),
|
|
||||||
Statistic(R.string.history_group_statistic),
|
|
||||||
Unknown(R.string.history_group_unknown),
|
|
||||||
;
|
|
||||||
|
|
||||||
private final int resourceId;
|
|
||||||
private String translated;
|
|
||||||
|
|
||||||
private static List<PumpHistoryEntryGroup> translatedList;
|
|
||||||
|
|
||||||
PumpHistoryEntryGroup(int resourceId) {
|
|
||||||
this.resourceId = resourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doTranslation(ResourceHelper resourceHelper) {
|
|
||||||
translatedList = new ArrayList<>();
|
|
||||||
|
|
||||||
for (PumpHistoryEntryGroup pumpHistoryEntryGroup : values()) {
|
|
||||||
pumpHistoryEntryGroup.translated = resourceHelper.gs(pumpHistoryEntryGroup.resourceId);
|
|
||||||
translatedList.add(pumpHistoryEntryGroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<PumpHistoryEntryGroup> getTranslatedList(ResourceHelper resourceHelper) {
|
|
||||||
if (translatedList == null) doTranslation(resourceHelper);
|
|
||||||
return translatedList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getResourceId() {
|
|
||||||
return resourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTranslated() {
|
|
||||||
return translated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return this.translated;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.core.R
|
||||||
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
||||||
|
* management and modified/extended for AAPS.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Author: Andy {andy.rozman@gmail.com}
|
||||||
|
*/
|
||||||
|
enum class PumpHistoryEntryGroup(val resourceId: Int) {
|
||||||
|
|
||||||
|
All(R.string.history_group_all),
|
||||||
|
Bolus(R.string.history_group_bolus),
|
||||||
|
Basal(R.string.history_group_basal),
|
||||||
|
Prime(R.string.history_group_prime),
|
||||||
|
Configuration(R.string.history_group_configuration),
|
||||||
|
Alarm(R.string.history_group_alarm),
|
||||||
|
Glucose(R.string.history_group_glucose),
|
||||||
|
Notification(R.string.history_group_notification),
|
||||||
|
Statistic(R.string.history_group_statistic),
|
||||||
|
Unknown(R.string.history_group_unknown);
|
||||||
|
|
||||||
|
var translated: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return translated!!
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var translatedList: MutableList<PumpHistoryEntryGroup>? = null
|
||||||
|
private fun doTranslation(resourceHelper: ResourceHelper) {
|
||||||
|
translatedList = ArrayList()
|
||||||
|
for (pumpHistoryEntryGroup in values()) {
|
||||||
|
pumpHistoryEntryGroup.translated = resourceHelper.gs(pumpHistoryEntryGroup.resourceId)
|
||||||
|
(translatedList as ArrayList<PumpHistoryEntryGroup>).add(pumpHistoryEntryGroup)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun getTranslatedList(resourceHelper: ResourceHelper): List<PumpHistoryEntryGroup>? {
|
||||||
|
if (translatedList == null) doTranslation(resourceHelper)
|
||||||
|
return translatedList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -29,9 +29,13 @@ public class ByteUtil {
|
||||||
return (b < 0) ? b + 256 : b;
|
return (b < 0) ? b + 256 : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int asUINT8(Integer b) {
|
||||||
|
return (b < 0) ? b + 256 : b;
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] getBytesFromInt16(int value) {
|
public static byte[] getBytesFromInt16(int value) {
|
||||||
byte[] array = getBytesFromInt(value);
|
byte[] array = getBytesFromInt(value);
|
||||||
return new byte[] {array[2], array[3]};
|
return new byte[]{array[2], array[3]};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getBytesFromInt(int value) {
|
public static byte[] getBytesFromInt(int value) {
|
||||||
|
@ -283,10 +287,54 @@ public class ByteUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts 4 (or less) ints into int. (Shorts are objects, so you can send null if you have less parameters)
|
||||||
|
*
|
||||||
|
* @param b1 short 1
|
||||||
|
* @param b2 short 2
|
||||||
|
* @param b3 short 3
|
||||||
|
* @param b4 short 4
|
||||||
|
* @param flag Conversion Flag (Big Endian, Little endian)
|
||||||
|
* @return int value
|
||||||
|
*/
|
||||||
|
public static int toInt(Byte b1, Byte b2, Byte b3, Byte b4, BitConversion flag) {
|
||||||
|
switch (flag) {
|
||||||
|
case LITTLE_ENDIAN: {
|
||||||
|
if (b4 != null) {
|
||||||
|
return (b4 & 0xff) << 24 | (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff;
|
||||||
|
} else if (b3 != null) {
|
||||||
|
return (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff;
|
||||||
|
} else if (b2 != null) {
|
||||||
|
return (b2 & 0xff) << 8 | b1 & 0xff;
|
||||||
|
} else {
|
||||||
|
return b1 & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
case BIG_ENDIAN: {
|
||||||
|
if (b4 != null) {
|
||||||
|
return (b1 & 0xff) << 24 | (b2 & 0xff) << 16 | (b3 & 0xff) << 8 | b4 & 0xff;
|
||||||
|
} else if (b3 != null) {
|
||||||
|
return (b1 & 0xff) << 16 | (b2 & 0xff) << 8 | b3 & 0xff;
|
||||||
|
} else if (b2 != null) {
|
||||||
|
return (b1 & 0xff) << 8 | b2 & 0xff;
|
||||||
|
} else {
|
||||||
|
return b1 & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int toInt(int b1, int b2) {
|
public static int toInt(int b1, int b2) {
|
||||||
return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN);
|
return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int toInt(Byte b1, Byte b2) {
|
||||||
|
return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static int toInt(int b1, int b2, int b3) {
|
public static int toInt(int b1, int b2, int b3) {
|
||||||
return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN);
|
return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN);
|
||||||
|
@ -329,6 +377,25 @@ public class ByteUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getCorrectHexValue(byte inp) {
|
||||||
|
String hx = Integer.toHexString((char) inp);
|
||||||
|
|
||||||
|
if (hx.length() == 0)
|
||||||
|
return "00";
|
||||||
|
else if (hx.length() == 1)
|
||||||
|
return "0" + hx;
|
||||||
|
else if (hx.length() == 2)
|
||||||
|
return hx;
|
||||||
|
else if (hx.length() == 4)
|
||||||
|
return hx.substring(2);
|
||||||
|
else {
|
||||||
|
System.out.println("Hex Error: " + inp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getHex(byte[] abyte0) {
|
public static String getHex(byte[] abyte0) {
|
||||||
return abyte0 != null ? getHex(abyte0, abyte0.length) : null;
|
return abyte0 != null ? getHex(abyte0, abyte0.length) : null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue