- kotlinizing mdt

This commit is contained in:
Andy Rozman 2021-04-16 17:11:00 +01:00
parent 1e29239bde
commit 769ab218d4
32 changed files with 2590 additions and 2872 deletions

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -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>
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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
}

View file

@ -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());
}
}

View file

@ -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")
}
}

View file

@ -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;
}
}

View file

@ -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 ");
}

View file

@ -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)));
}
}

View file

@ -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)))
}
}

View file

@ -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 //
}
}

View file

@ -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
}
}

View file

@ -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();
}
}

View file

@ -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)
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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
}
}
}

View file

@ -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;
}