- 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;
|
||||
|
||||
case DailyTotals515:
|
||||
decodeDailyTotals515(entry.getBody());
|
||||
decodeDailyTotals515(entry.body);
|
||||
break;
|
||||
|
||||
case DailyTotals522:
|
||||
decodeDailyTotals522(entry.getBody());
|
||||
decodeDailyTotals522(entry.body);
|
||||
break;
|
||||
|
||||
case DailyTotals523:
|
||||
decodeDailyTotals523(entry.getBody());
|
||||
decodeDailyTotals523(entry.body);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -96,18 +96,18 @@ public class DailyTotalsDTO {
|
|||
private void setDisplayable() {
|
||||
|
||||
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 {
|
||||
this.entry.setDisplayableValue("Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2)
|
||||
+ ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2));
|
||||
this.entry.displayableValue = "Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2)
|
||||
+ ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void decodeEndResultsTotals(PumpHistoryEntry entry) {
|
||||
double totals = ByteUtil.toInt((int) entry.getHead()[0], (int) entry.getHead()[1], (int) entry.getHead()[2],
|
||||
(int) entry.getHead()[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d;
|
||||
double totals = ByteUtil.toInt((int) entry.head[0], (int) entry.head[1], (int) entry.head[2],
|
||||
(int) entry.head[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d;
|
||||
|
||||
this.insulinTotal = totals;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public class MedtronicHistoryActivity extends NoSplashAppCompatActivity {
|
|||
this.filteredHistoryList.addAll(list);
|
||||
} else {
|
||||
for (PumpHistoryEntry pumpHistoryEntry : list) {
|
||||
if (pumpHistoryEntry.getEntryType().getGroup() == group) {
|
||||
if (pumpHistoryEntry.getEntryType().group == group) {
|
||||
this.filteredHistoryList.add(pumpHistoryEntry);
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ public class MedtronicHistoryActivity extends NoSplashAppCompatActivity {
|
|||
if (record != null) {
|
||||
holder.timeView.setText(record.getDateTimeString());
|
||||
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;
|
||||
}
|
||||
|
||||
public static int asUINT8(Integer b) {
|
||||
return (b < 0) ? b + 256 : b;
|
||||
}
|
||||
|
||||
public static byte[] getBytesFromInt16(int value) {
|
||||
byte[] array = getBytesFromInt(value);
|
||||
return new byte[] {array[2], array[3]};
|
||||
return new byte[]{array[2], array[3]};
|
||||
}
|
||||
|
||||
public static byte[] getBytesFromInt(int value) {
|
||||
|
@ -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) {
|
||||
return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
public static int toInt(Byte b1, Byte b2) {
|
||||
return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN);
|
||||
}
|
||||
|
||||
|
||||
public static int toInt(int b1, int b2, int b3) {
|
||||
return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN);
|
||||
|
@ -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) {
|
||||
return abyte0 != null ? getHex(abyte0, abyte0.length) : null;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue