- merges of bug fixes from RileyLinkAAPS

This commit is contained in:
Andy Rozman 2018-08-12 16:30:32 +01:00
parent c9d35696cf
commit 6a73937b2a
27 changed files with 1124 additions and 189 deletions

View file

@ -31,7 +31,7 @@ import info.nightscout.utils.SP;
public abstract class RileyLinkCommunicationManager {
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkCommunicationManager.class);
private static final int SCAN_TIMEOUT = 1500;
protected final RFSpy rfspy;
protected final Context context;
protected int receiverDeviceAwakeForMinutes = 1; // override this in constructor of specific implementation
@ -41,16 +41,13 @@ public abstract class RileyLinkCommunicationManager {
protected RileyLinkServiceData rileyLinkServiceData;
protected RileyLinkTargetFrequency targetFrequency;
private double[] scanFrequencies;
// internal flag
private boolean showPumpMessages = true;
private int timeoutCount = 0;
private long nextWakeUpRequired = 0L;
// protected PumpMessage sendAndListen(RLMessage msg) {
// return sendAndListen(msg, 4000); // 2000
// }
public RileyLinkCommunicationManager(Context context, RFSpy rfspy, RileyLinkTargetFrequency targetFrequency) {
this.context = context;
this.rfspy = rfspy;
@ -95,7 +92,6 @@ public abstract class RileyLinkCommunicationManager {
// RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_quickTune);
}
}
}
if (showPumpMessages) {
@ -212,7 +208,7 @@ public abstract class RileyLinkCommunicationManager {
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0,
(byte)0, (byte)0, 1500, (byte)0);
(byte)0, (byte)0, SCAN_TIMEOUT, (byte)0);
if (resp.wasTimeout()) {
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
} else if (resp.looksLikeRadioPacket()) {
@ -266,7 +262,7 @@ public abstract class RileyLinkCommunicationManager {
// RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData);
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
RadioPacket pkt = new RadioPacket(pumpMsgContent);
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte)0, (byte)0, (byte)0, (byte)0, 1500, (byte)0);
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte)0, (byte)0, (byte)0, (byte)0, SCAN_TIMEOUT, (byte)0);
if (resp.wasTimeout()) {
LOG.warn("tune_tryFrequency: no pump response at frequency {}", freqMHz);
} else if (resp.looksLikeRadioPacket()) {

View file

@ -18,6 +18,7 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.defs.RXFil
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.defs.RileyLinkTargetFrequency;
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.BLECommOperationResult;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ThreadUtil;
@ -107,6 +108,9 @@ public class RFSpy {
// prepend length, and send it.
byte[] prepended = ByteUtil.concat(new byte[] { (byte)(bytes.length) }, bytes);
//LOG.debug("writeToData (command={},raw={})", command.name(), HexDump.toHexStringDisplayable(prepended));
BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristic_blocking(radioServiceUUID, radioDataUUID,
prepended);
if (writeCheck.resultCode != BLECommOperationResult.RESULT_SUCCESS) {

View file

@ -17,7 +17,6 @@ public enum RileyLinkTargetDevice {
RileyLinkTargetDevice(int resourceId, boolean tuneUpEnabled) {
this.resourceId = resourceId;
this.tuneUpEnabled = tuneUpEnabled;
}

View file

@ -32,8 +32,6 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTaskExecutor;
import info.nightscout.utils.SP;
//import static info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.;
/**
* Created by andy on 5/6/18.
* Split from original file and renamed.
@ -61,6 +59,7 @@ public abstract class RileyLinkService extends Service {
this.context = context;
RileyLinkUtil.setContext(this.context);
determineRileyLinkTargetFrequency();
RileyLinkUtil.setRileyLinkService(this);
RileyLinkUtil.setRileyLinkTargetFrequency(rileyLinkTargetFrequency);
initRileyLinkServiceData();
}

View file

@ -72,6 +72,14 @@ public class ByteUtil {
}
public static byte[] substring(byte[] a, int start) {
int len = a.length - start;
byte[] rval = new byte[len];
System.arraycopy(a, start, rval, 0, len);
return rval;
}
public static String shortHexString(byte[] ra) {
String rval = "";
if (ra == null) {
@ -236,6 +244,102 @@ public class ByteUtil {
return k;
}
/**
* Gets the correct hex value.
*
* @param inp the inp
* @return the correct hex value
*/
public static String getCorrectHexValue(int 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 byte[] getByteArrayFromList(List<Byte> list) {
byte[] out = new byte[list.size()];
for (int i = 0; i < list.size(); i++) {
out[i] = list.get(i);
}
return out;
}
public static String getHex(byte abyte0[]) {
return abyte0 != null ? getHex(abyte0, abyte0.length) : null;
}
public static String getHex(byte abyte0[], int i) {
StringBuffer stringbuffer = new StringBuffer();
if (abyte0 != null) {
i = Math.min(i, abyte0.length);
for (int j = 0; j < i; j++) {
stringbuffer.append(getHex(abyte0[j]));
if (j < i - 1) {
stringbuffer.append(" ");
}
}
}
return new String(stringbuffer);
}
public static String getHex(byte byte0) {
String s = byte0 != -1 ? "0x" : "";
return s + getHexCompact(byte0);
}
public static String getHexCompact(byte byte0) {
int i = byte0 != -1 ? convertUnsignedByteToInt(byte0) : (int)byte0;
return getHexCompact(i);
}
public static int convertUnsignedByteToInt(byte data) {
return data & 0xff;
}
// public String getHexCompact(int i) {
// long l = i != -1 ? convertUnsignedIntToLong(i) : i;
// return getHexCompact(l);
// }
public static String getHexCompact(int l) {
String s = Long.toHexString(l).toUpperCase();
String s1 = isOdd(s.length()) ? "0" : "";
return l != -1L ? s1 + s : "-1";
}
public static boolean isEven(int i) {
return i % 2 == 0;
}
public static boolean isOdd(int i) {
return !isEven(i);
}
public enum BitConversion {
LITTLE_ENDIAN, // 20 0 0 0 = reverse
BIG_ENDIAN // 0 0 0 20 = normal - java

View file

@ -80,4 +80,19 @@ public class StringUtil {
public static String toDateTimeString(LocalDateTime localDateTime) {
return localDateTime.toString("dd.MM.yyyy HH:mm:ss");
}
public static String getStringInLength(String value, int length) {
StringBuilder val = new StringBuilder(value);
if (val.length() > length) {
return val.substring(0, length);
}
for (int i = val.length(); i < length; i++) {
val.append(" ");
}
return val.toString();
}
}

View file

@ -19,13 +19,12 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.defs.Riley
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.Page;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.Record;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.ButtonPressCarelinkMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.CarelinkLongMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.CarelinkShortMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.GetHistoryPageCarelinkMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.MedtronicConverter;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.MessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PacketType;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PumpAckMessageBody;
@ -592,14 +591,111 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
public BasalProfile getBasalProfile() {
public BasalProfile getBasalProfile_Old() {
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBasalProfileSTD);
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBasalProfileA);
return responseObject == null ? null : (BasalProfile)responseObject;
}
public BasalProfile getBasalProfile() {
// wakeUp
wakeUp(receiverDeviceAwakeForMinutes, false);
MedtronicUtil.setPumpDeviceState(PumpDeviceState.Active);
MedtronicCommandType commandType = MedtronicCommandType.GetBasalProfileSTD;
for (int retries = 0; retries <= MAX_COMMAND_RETRIES; retries++) {
// create message
PumpMessage msg;
// if (bodyData == null)
msg = makePumpMessage(commandType);
// send and wait for response
PumpMessage response = sendAndListen(msg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries));
// LOG.debug("1st Response: " + HexDump.toHexStringDisplayable(response.getRawContent()));
// LOG.debug("1st Response: " + HexDump.toHexStringDisplayable(response.getMessageBody().getTxData()));
String check = checkResponseContent(response, commandType.commandDescription, commandType.expectedLength);
byte[] data = null;
int runs = 1;
if (check == null) {
data = response.getRawContent();
PumpMessage ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, new PumpAckMessageBody());
while (checkIfWeHaveMoreData(commandType, response, data)) {
runs++;
PumpMessage response2 = sendAndListen(ackMsg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries));
// LOG.debug("{} Response: {}", runs, HexDump.toHexStringDisplayable(response2.getRawContent()));
// LOG.debug("{} Response: {}", runs,
// HexDump.toHexStringDisplayable(response2.getMessageBody().getTxData()));
String check2 = checkResponseContent(response2, commandType.commandDescription,
commandType.expectedLength);
if (check2 == null) {
data = ByteUtil.concat(data, ByteUtil.substring(response2.getMessageBody().getTxData(), 1));
} else {
this.errorMessage = check;
}
}
} else {
errorMessage = check;
}
Object dataResponse = medtronicConverter.convertResponse(commandType, data);
LOG.debug("Converted response for {} is {}.", commandType.name(), dataResponse);
MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping);
return (BasalProfile)dataResponse;
}
return null;
}
private boolean checkIfWeHaveMoreData(MedtronicCommandType commandType, PumpMessage response, byte[] data) {
if (commandType == MedtronicCommandType.GetBasalProfileSTD || //
commandType == MedtronicCommandType.GetBasalProfileA || //
commandType == MedtronicCommandType.GetBasalProfileB) {
byte[] responseRaw = response.getRawContent();
int last = responseRaw.length - 1;
LOG.debug("Length: " + data.length);
if (data.length >= BasalProfile.MAX_RAW_DATA_SIZE) {
return false;
}
return !(responseRaw[last] == 0x00 && responseRaw[last - 1] == 0x00 && responseRaw[last - 2] == 0x00);
}
return false;
}
public LocalDateTime getPumpTime() {
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.RealTimeClock);
@ -638,7 +734,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
LOG.debug("Set Bolus: Body - {}", HexDump.toHexStringDisplayable(body));
PumpMessage msg = makePumpMessage(MedtronicCommandType.SetBolus, //
new CarelinkLongMessageBody(ByteUtil.concat((byte)body.length, body)));
new CarelinkLongMessageBody(body));
PumpMessage pumpMessage = runCommandWithArgs(msg);
@ -676,7 +772,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
// FIXME:
public BatteryStatusDTO getRemainingBattery() {
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBatteryStatus);

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.message;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm;
import java.util.HashMap;
import java.util.Map;
@ -56,7 +56,9 @@ public class MedtronicConverter {
return decodeBatteryStatus(rawContent);
}
case GetBasalProfileSTD: {
case GetBasalProfileSTD:
case GetBasalProfileA:
case GetBasalProfileB: {
return new BasalProfile(rawContent);
}
@ -114,12 +116,6 @@ public class MedtronicConverter {
if ((i != 0) && (time_x == 0)) {
break;
}
// String value = i18nControl.getMessage("CFG_BASE_FROM") + "=" + atd.getTimeString() + ", "
// + i18nControl.getMessage("CFG_BASE_AMOUNT") + "=" + vald;
// writeSetting(key, value, value, PumpConfigurationGroup.Basal);
}
return basalProfile;
@ -234,6 +230,7 @@ public class MedtronicConverter {
addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h",
PumpConfigurationGroup.General, map);
addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] != 0 ? 50 : 100), PumpConfigurationGroup.Insulin, map);
LOG.debug("Insulin concentration: " + rd[9]);
addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map);
if (rd[10] == 1) {
@ -319,7 +316,7 @@ public class MedtronicConverter {
public float getStrokesPerUnit(boolean isBasal) {
return isBasal ? 40.0f : 10.0f;
return isBasal ? 40.0f : pumpModel.getBolusStrokes();
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history;
import java.util.ArrayList;
import java.util.HashMap;
@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
/**
@ -118,7 +117,8 @@ public abstract class MedtronicHistoryDecoder {
}
protected void addToStatistics(MedtronicHistoryEntry pumpHistoryEntry, RecordDecodeStatus status, Integer opCode) {
protected void addToStatistics(MedtronicHistoryEntryInterface pumpHistoryEntry, RecordDecodeStatus status,
Integer opCode) {
if (!statisticsEnabled)
return;
@ -129,8 +129,8 @@ public abstract class MedtronicHistoryDecoder {
return;
}
if (!mapStatistics.get(status).containsKey(pumpHistoryEntry.getEntryType().name())) {
mapStatistics.get(status).put(pumpHistoryEntry.getEntryType().name(), "");
if (!mapStatistics.get(status).containsKey(pumpHistoryEntry.getEntryTypeName())) {
mapStatistics.get(status).put(pumpHistoryEntry.getEntryTypeName(), "");
}
}

View file

@ -1,12 +1,13 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDateTime;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.PumpTimeStampedRecord;
/**
* Application: GGC - GNU Gluco Control
@ -29,7 +30,7 @@ import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.PumpTimeStamped
* Author: Andy {andy@atech-software.com}
*/
public abstract class MedtronicHistoryEntry {
public abstract class MedtronicHistoryEntry implements MedtronicHistoryEntryInterface {
protected List<Byte> rawData;
@ -40,7 +41,9 @@ public abstract class MedtronicHistoryEntry {
protected byte[] body;
protected LocalDateTime dateTime;
protected PumpTimeStampedRecord historyEntryDetails;
// protected PumpTimeStampedRecord historyEntryDetails;
private Map<String, Object> decodedData;
public void setData(List<Byte> listRawData, boolean doNotProcess) {
@ -77,6 +80,32 @@ public abstract class MedtronicHistoryEntry {
}
public String getDecodedData() {
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 boolean showRaw() {
return getEntryTypeName().equals("EndResultTotals");
}
public int getHeadLength() {
return sizes[0];
}
@ -97,7 +126,9 @@ public abstract class MedtronicHistoryEntry {
StringBuilder sb = new StringBuilder();
sb.append(getToStringStart());
sb.append(", DT: " + ((this.dateTime == null) ? "x" : StringUtil.toDateTimeString(this.dateTime)));
sb.append(", DT: "
+ StringUtil.getStringInLength((this.dateTime == null) ? "x" : StringUtil.toDateTimeString(this.dateTime),
19));
sb.append(", length=");
sb.append(getHeadLength());
sb.append(",");
@ -108,6 +139,17 @@ public abstract class MedtronicHistoryEntry {
sb.append((getHeadLength() + getDateTimeLength() + getBodyLength()));
sb.append(")");
boolean hasData = hasData();
if (hasData) {
sb.append(", data=" + getDecodedData());
}
if (hasData && !showRaw()) {
sb.append("]");
return sb.toString();
}
if (head != null) {
sb.append(", head=");
sb.append(HexDump.toHexStringDisplayable(this.head));
@ -127,10 +169,10 @@ public abstract class MedtronicHistoryEntry {
sb.append(HexDump.toHexStringDisplayable(this.rawData));
sb.append("]");
sb.append(" DT: ");
sb.append(this.dateTime == null ? " - " : this.dateTime.toString("dd.MM.yyyy HH:mm:ss"));
// sb.append(" DT: ");
// sb.append(this.dateTime == null ? " - " : this.dateTime.toString("dd.MM.yyyy HH:mm:ss"));
sb.append(" Ext: ");
// sb.append(" Ext: ");
return sb.toString();
}
@ -192,6 +234,14 @@ public abstract class MedtronicHistoryEntry {
}
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. ";
@ -200,8 +250,7 @@ public abstract class MedtronicHistoryEntry {
}
}
// if we extend to CGMS this need to be changed back
public abstract PumpHistoryEntryType getEntryType();
// public abstract PumpHistoryEntryType getEntryType();
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.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

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history;
import java.util.Arrays;

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history;
/**
* Application: GGC - GNU Gluco Control

View file

@ -0,0 +1,72 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.cgms;
import java.util.List;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
/**
* Created by andy on 27.03.15.
*/
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;
}
@Override
public String getToStringStart() {
return "CGMSHistoryEntry [type=" + entryType.name() + " [" + getOpCode() + ", 0x"
+ ByteUtil.getCorrectHexValue(getOpCode()) + "]";
}
}

View file

@ -0,0 +1,167 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.cgms;
import java.util.HashMap;
import java.util.Map;
/**
* Application: GGC - GNU Gluco Control
* Plug-in: GGC PlugIn Base (base class for all plugins)
* <p>
* See AUTHORS for copyright information.
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* <p>
* Filename: PumpHistoryEntryType Description: Pump History Entry Type.
* <p>
* Data is from several sources, so in comments there are "versions". Version: v1 - default doc fromc decoding-carelink
* v2 - Andy testing (?)
* <p>
* Author: Andy {andy@atech-software.com}
*/
public enum CGMSHistoryEntryType {
None(0, "None", 1, 0, 0, DateType.None), //
DataEnd(0x01, "DataEnd", 1, 0, 0, DateType.None), //
SensorWeakSignal(0x02, "SensorWeakSignal", 1, 0, 0, DateType.PreviousTimeStamp), //
SensorCal(0x03, "SensorCal", 1, 0, 1, DateType.PreviousTimeStamp), //
SensorTimestamp(0x08, "SensorTimestamp", 1, 4, 0, DateType.MinuteSpecific), //
BatteryChange(0x0a, "BatteryChange',packet_size=4", 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), //
// # 0x10: '10-Something',packet_size=7,date_type='minSpecific',op='0x10'),
Something10(0x10, "10-Something", 1, 4, 0, DateType.MinuteSpecific), //
Something19(0x13, "19-Something", 1, 0, 0, DateType.PreviousTimeStamp),
// V2
Something05(0x05, "05-Something", 1, 0, 0, DateType.PreviousTimeStamp),
GlucoseSensorData(0xFF, "GlucoseSensorData", 1, 0, 0, DateType.PreviousTimeStamp);
;
private static Map<Integer, CGMSHistoryEntryType> opCodeMap = new HashMap<Integer, CGMSHistoryEntryType>();
static {
for (CGMSHistoryEntryType type : values()) {
opCodeMap.put(type.opCode, type);
}
}
public boolean schemaSet = false;
private int opCode;
private String description;
private int headLength;
private int dateLength;
private int bodyLength;
private int totalLength;
private 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,361 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.cgms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryDecoder;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RecordDecodeStatus;
/**
* Application: GGC - GNU Gluco Control
* Plug-in: GGC PlugIn Base (base class for all plugins)
* <p>
* See AUTHORS for copyright information.
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* <p>
* Filename: MinimedCGMSHistoryDecoder Description: Decoder for CGMS history data. For now we support just data from
* GlucoseHistory command. ISIGHistory and VCntrHistory are IGNORED for now.
* <p>
* Author: Andy {andy@atech-software.com}
*/
public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
private static final Logger LOG = LoggerFactory.getLogger(MedtronicCGMSHistoryDecoder.class);
// CGMSValuesWriter cgmsValuesWriter = null;
public MedtronicCGMSHistoryDecoder() {
}
public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn) {
CGMSHistoryEntry precord = (CGMSHistoryEntry)entryIn;
try {
return decodeRecord(precord, false);
} catch (Exception ex) {
LOG.error(" Error decoding: type={}, ex={}", precord.getEntryType().name(), ex.getMessage(), ex);
return RecordDecodeStatus.Error;
}
}
public RecordDecodeStatus decodeRecord(CGMSHistoryEntry entry, boolean x) {
// FIXME
// TODO
// CGMSHistoryEntry entry = (CGMSHistoryEntry) entryIn;
if (entry.getDateTimeLength() > 0) {
LocalDateTime dt = parseDate(entry);
System.out.println("DT: " + dt);
}
// LOG.debug("decodeRecord: type={}", entry.getEntryType());
switch ((CGMSHistoryEntryType)entry.getEntryType()) {
case SensorWeakSignal:
break;
case SensorCal:
break;
case SensorTimestamp:
break;
case BatteryChange:
break;
case SensorStatus:
break;
case DateTimeChange:
break;
case SensorSync:
break;
case CalBGForGH:
break;
case SensorCalFactor:
LOG.debug("{}", entry.toString());
break;
case Something10:
break;
case Something19:
break;
case None:
case DataEnd:
break;
}
return RecordDecodeStatus.NotSupported;
}
@Override
public void postProcess() {
}
@Override
public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) throws Exception {
List<Byte> dataClear = checkPage(page);
return createRecords(dataClear);
}
private List<? extends MedtronicHistoryEntry> createRecords(List<Byte> dataClearInput) {
// List<MinimedHistoryEntry> listRecords = new
// ArrayList<MinimedHistoryEntry>();
LOG.debug("createRecords not implemented... WIP");
// return listRecords;
List<Byte> dataClear = reverseList(dataClearInput);
System.out.println("Reversed:" + ByteUtil.getHex(ByteUtil.getByteArrayFromList(dataClear)));
prepareStatistics();
int counter = 0;
int record = 0;
List<MedtronicHistoryEntry> outList = new ArrayList<MedtronicHistoryEntry>();
// 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);
// System.out.println("Unknown code: " + opCode);
CGMSHistoryEntry pe = new CGMSHistoryEntry();
pe.setEntryType(CGMSHistoryEntryType.None);
pe.setOpCode(opCode);
// List<Byte> listRawData = new ArrayList<Byte>();
// listRawData.add((byte) opCode);
// pe.setOpCode(opCode);
pe.setData(Arrays.asList((byte)opCode), false);
// System.out.println("Record: " + pe);
outList.add(pe);
} else {
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);
// List<Byte> listRawData = new ArrayList<Byte>();
// listRawData.add((byte) opCode);
// pe.setOpCode(opCode);
pe.setData(Arrays.asList((byte)opCode), false);
// System.out.println("Record: " + pe);
outList.add(pe);
}
} while (counter < dataClear.size());
int i = outList.size() - 1;
for (; i >= 0; i--) {
// CGMSHistoryEntryType type = (CGMSHistoryEntryType) outList.get(i).getDateLength();
if (outList.get(i).getDateLength() > 0) {
System.out.println("Recordccc2: " + outList.get(i));
decodeRecord(outList.get(i));
// 2015-04-11T14:02:00
break;
}
// System.out.println("Record2: " + outList.get(i));
}
// System.exit(1);
// FIXME
for (; i >= 0; i--) {
// System.out.println("Record2: " + outList.get(i));
CGMSHistoryEntry che = (CGMSHistoryEntry)outList.get(i);
parseDate(che);
System.out.println("Record2: " + outList.get(i));
}
return outList;
}
private List<Byte> reverseList(List<Byte> dataClearInput) {
List<Byte> outList = new ArrayList<Byte>();
for (int i = dataClearInput.size() - 1; i > 0; i--) {
outList.add(dataClearInput.get(i));
}
return outList;
}
private int parseMinutes(int one) {
// minute = (one & 0b111111 )
// return minute
// int yourInteger = Integer.parseInt("100100101", 2);
return (one & Integer.parseInt("0111111", 2));
}
private int parseHours(int one) {
// def parse_hours (one):
// return (one & 0x1F )
return (one & 0x1F);
}
private int parseDay(int one) {
// def parse_day (one):
// return one & 0x1F
return one & 0x1F;
}
private int parseMonths(int first_byte, int second_byte) {
// def parse_months (first_byte, second_byte):
// first_two_bits = first_byte >> 6
// second_two_bits = second_byte >> 6
// return (first_two_bits << 2) + second_two_bits
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) {
// def parse_years_lax(year):
// """
// simple mask plus correction
// """
// y = (year & Mask.year) + 2000
// return y
return (year & 0x0F) + 2000;
}
private LocalDateTime parseDate(CGMSHistoryEntry entry) {
if (entry.getEntryType().hasDate())
return null;
byte data[] = entry.getDatetime();
// def parse_date (data, unmask=False, strict=False,
// minute_specific=False):
// """
// Some dates are formatted/stored down to the second (Sensor
// CalBGForPH) while
// others are stored down to the minute (CGM SensorTimestamp dates).
// """
// data = data[:]
// seconds = 0
// minutes = 0
// hours = 0
//
// year = times.parse_years(data[0])
// day = parse_day(data[1])
// minutes = parse_minutes(data[2])
//
// hours = parse_hours(data[3])2015parse
//
// month = parse_months(data[3], data[2])
// 2015-04-11T14:02:00
// date is reversed
if (entry.getEntryType().getDateType() == CGMSHistoryEntryType.DateType.MinuteSpecific) {
LocalDateTime date = new LocalDateTime(parseDay(data[2]), parseMonths(data[0], data[1]),
parseYear(data[3]), parseHours(data[0]), parseMinutes(data[1]), 0);
// ATechDate date = new ATechDate(parseDay(data[0]),
// parseMonths(data[2], data[1]), parseYear(data[2]),
// parseHours(data[2]), parseMinutes(data[1]), 0,
// ATechDateType.DateAndTimeSec);
entry.setLocalDateTime(date);
return date;
} 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;
}
@Override
protected void runPostDecodeTasks() {
this.showStatistics();
}
}

View file

@ -1,7 +1,9 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
@ -11,10 +13,14 @@ import android.util.Log;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BasalProfileEntry;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryDecoder;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RecordDecodeStatus;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BolusDTO;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BolusWizardDTO;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.TempBasalPair;
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicDeviceType;
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.PumpBolusType;
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
@ -43,16 +49,16 @@ import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
private static final Logger LOG = LoggerFactory.getLogger(MedtronicPumpHistoryDecoder.class);
private static final String TAG = "MdtPumpHistoryDecoder";
private static final String TAG = "MdtPump";
// PumpValuesWriter pumpValuesWriter = null;
// DataAccessPlugInBase dataAccess = DataAccessPump.getInstance();
BolusDTO bolusEntry;
PumpHistoryEntry pumpHistoryEntry4BolusEntry;
Map<String, BolusDTO> bolusHistory = new HashMap<>();
// Temporary records for processing
private PumpHistoryEntry tbrPreviousRecord;
private PumpHistoryEntry changeTimeRecord;
private MedtronicDeviceType deviceType;
public MedtronicPumpHistoryDecoder() {
@ -72,11 +78,17 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
int counter = 0;
int record = 0;
boolean incompletePacket = false;
deviceType = MedtronicUtil.getMedtronicPumpModel();
List<MedtronicHistoryEntry> outList = new ArrayList<MedtronicHistoryEntry>();
String skipped = null;
int elementStart = 0;
if (dataClear.size() == 0) {
Log.e(TAG, "Empty page.");
// return;
}
do {
int opCode = dataClear.get(counter);
boolean special = false;
@ -237,19 +249,6 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
}
// private void decodeCalBGForPH(PumpHistoryEntry entry) {
// int high = (entry.getDatetime()[4] & 0x80) >> 7;
// int bg = bitUtils.toInt(high, getUnsignedInt(entry.getHead()[0]));
//
// writeData(PumpBaseType.AdditionalData, PumpAdditionalDataType.BloodGlucose, "" + bg, entry.getATechDate());
// }
// masks = [ ( 0x80, 7), (0x40, 6), (0x20, 5), (0x10, 4) ]
// nibbles = [ ]
// for mask, shift in masks:
// nibbles.append( ( (year & mask) >> shift ) )
// return nibbles
public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn, boolean x) {
// FIXME
// TODO
@ -454,6 +453,19 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
}
// private void decodeCalBGForPH(PumpHistoryEntry entry) {
// int high = (entry.getDatetime()[4] & 0x80) >> 7;
// int bg = bitUtils.toInt(high, getUnsignedInt(entry.getHead()[0]));
//
// writeData(PumpBaseType.AdditionalData, PumpAdditionalDataType.BloodGlucose, "" + bg, entry.getATechDate());
// }
// masks = [ ( 0x80, 7), (0x40, 6), (0x20, 5), (0x10, 4) ]
// nibbles = [ ]
// for mask, shift in masks:
// nibbles.append( ( (year & mask) >> shift ) )
// return nibbles
// FIXME
private void decodeChangeTime(PumpHistoryEntry entry) {
if (changeTimeRecord == null)
@ -475,9 +487,12 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
}
// FIXME
// FIXME 554 ?
private void decodeEndResultTotals(PumpHistoryEntry entry) {
float totals = bitUtils.toInt(entry.getHead()[2], entry.getHead()[3]) * 0.025f;
float totals = bitUtils.toInt((int)entry.getHead()[0], (int)entry.getHead()[1], (int)entry.getHead()[2],
(int)entry.getHead()[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025f;
entry.addDecodedData("Totals", totals);
// this.writeData(PumpBaseType.Report, PumpReport.InsulinTotalDay, getFormattedFloat(totals, 2),
// entry.getATechDate());
@ -504,6 +519,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
} else {
// writeData(PumpBaseType.Basal, PumpBasalType.ValueChange, getFormattedFloat(rate, 3),
// entry.getATechDate());
entry.addDecodedData("Value", getFormattedFloat(rate, 3));
return RecordDecodeStatus.OK;
}
@ -549,7 +565,9 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
dto.correctionEstimate = (body[7] + (body[5] & 0x0F)) / 10.0f;
}
entry.setHistoryEntryDetails(dto);
dto.localDateTime = entry.getLocalDateTime();
entry.addDecodedData("Object", dto);
// entry.setHistoryEntryDetails(dto);
// this.writeData(PumpBaseType.Event, PumpEventType.BolusWizard, dto.getValue(), entry.getATechDate());
@ -569,6 +587,14 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
float amount = bitUtils.toInt(entry.getHead()[2], entry.getHead()[3]) / 10.0f;
float fixed = bitUtils.toInt(entry.getHead()[0], entry.getHead()[1]) / 10.0f;
entry.addDecodedData("Amount", amount);
entry.addDecodedData("FixedAmount", fixed);
// amount = (double) (asUINT8(data[4]) << 2) / 40.0;
// programmedAmount = (double) (asUINT8(data[2]) << 2) / 40.0;
// primeType = programmedAmount == 0 ? "manual" : "fixed";
// return true;
// this.writeData(PumpBaseType.Event, PumpEventType.PrimeInfusionSet, fixed > 0 ? getFormattedFloat(fixed, 1) :
// getFormattedFloat(amount, 1), entry.getATechDate());
}
@ -576,9 +602,9 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
@Override
public void postProcess() {
if (bolusEntry != null) {
writeBolus(pumpHistoryEntry4BolusEntry, bolusEntry);
}
// if (bolusEntry != null) {
// writeBolus(pumpHistoryEntry4BolusEntry, bolusEntry);
// }
}
@ -609,42 +635,30 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
: PumpBolusType.Normal);
bolus.setLocalDateTime(entry.getLocalDateTime());
if (bolusEntry != null) {
if (bolus.getBolusType() == PumpBolusType.Extended) {
if (bolusEntry.getLocalDateTime().equals(bolus.getLocalDateTime())) {
bolus.setImmediateAmount(bolusEntry.getDeliveredAmount());
bolus.setBolusType(PumpBolusType.Multiwave);
String dateTime = StringUtil.toDateTimeString(entry.getLocalDateTime());
writeBolus(entry, bolus);
} else {
// FIXME ths might not be correct
writeBolus(entry, bolusEntry);
}
} else {
writeBolus(entry, bolusEntry);
if (bolus.getBolusType() == PumpBolusType.Extended) {
// we check if we have coresponding normal entry
if (bolusHistory.containsKey(dateTime)) {
BolusDTO bolusDTO = bolusHistory.get(dateTime);
bolusDTO.setImmediateAmount(bolus.getDeliveredAmount());
bolusDTO.setBolusType(PumpBolusType.Multiwave);
return;
}
}
bolusEntry = bolus;
pumpHistoryEntry4BolusEntry = entry;
entry.addDecodedData("Object", bolus);
bolusHistory.put(dateTime, bolus);
}
private void resetBolusEntry() {
bolusEntry = null;
pumpHistoryEntry4BolusEntry = null;
}
private void writeBolus(PumpHistoryEntry pumpHistoryEntry, BolusDTO bolus) {
// writeData(PumpBaseType.Bolus, bolus.getBolusType(), bolus.getValue(), bolus.getATechDate());
pumpHistoryEntry.setHistoryEntryDetails(bolus);
resetBolusEntry();
}
// FIXME
// FIXME new pumps have single record (I think)
private void decodeTempBasal(PumpHistoryEntry entry) {
if (this.tbrPreviousRecord == null) {
// LOG.debug(this.tbrPreviousRecord.toString());
this.tbrPreviousRecord = entry;
@ -668,21 +682,24 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
// LOG.debug("Rate: " + tbrRate.toString());
// LOG.debug("Durration: " + tbrDuration.toString());
BasalProfileEntry basalProfileEntry = new BasalProfileEntry(tbrRate.getHead()[0], tbrDuration.getHead()[0]);
// System.out.println(
// "TBR: amount=" + tbr.getAmount() + ", duration=" + tbr.getDuration()
// + " min. Packed: " + tbr.getValue());
// FIXME set Unit
// FIXME AAPS
// if (tbr.getDuration() > 0) {
// writeData(PumpBaseType.Basal, PumpBasalType.TemporaryBasalRate, tbr.getValue(), entry.getATechDate());
// if ((asUINT8(data[7]) >> 3) == 0) {
// mIsPercent = false;
// tbrRate = (double) (asUINT8(tbrRate.getRawData().get(1)) / 40.0;
// } else {
// writeData(PumpBaseType.Basal, PumpBasalType.TemporaryBasalRateCanceled, "", entry.getATechDate());
// mIsPercent = true;
// basalRate = asUINT8(data[1]);
// }
// FIXME
TempBasalPair tbr = new TempBasalPair(tbrRate.getHead()[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);
tbrPreviousRecord = null;
}

View file

@ -1,7 +1,8 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.PumpTimeStampedRecord;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
/**
* Application: GGC - GNU Gluco Control
@ -62,20 +63,20 @@ public class PumpHistoryEntry extends MedtronicHistoryEntry {
@Override
public String getToStringStart() {
return "PumpHistoryRecord [type=" + entryType.name() + " [" + getOpCode() + ", 0x"
return "PumpHistoryRecord [type=" + StringUtil.getStringInLength(entryType.name(), 20) + " ["
+ StringUtil.getStringInLength("" + getOpCode(), 3) + ", 0x"
+ HexDump.getCorrectHexValue((byte)getOpCode()) + "]";
}
public PumpTimeStampedRecord getHistoryEntryDetails() {
return historyEntryDetails;
}
public void setHistoryEntryDetails(PumpTimeStampedRecord historyEntryDetails) {
this.historyEntryDetails = historyEntryDetails;
}
// public PumpTimeStampedRecord getHistoryEntryDetails() {
// return historyEntryDetails;
// }
//
//
// public void setHistoryEntryDetails(PumpTimeStampedRecord historyEntryDetails) {
// this.historyEntryDetails = historyEntryDetails;
// }
public int getOffset() {
return offset;
@ -85,4 +86,16 @@ public class PumpHistoryEntry extends MedtronicHistoryEntry {
public void setOffset(int offset) {
this.offset = offset;
}
@Override
public String getEntryTypeName() {
return this.entryType.name();
}
@Override
public int getDateLength() {
return this.entryType.getDateLength();
}
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history2;
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump;
import java.util.ArrayList;
import java.util.HashMap;
@ -71,7 +71,7 @@ public enum PumpHistoryEntryType // implements CodeEnum
ChangeChildBlockEnable(0x23, "ChangeChildBlockEnable"), // 8?
ChangeMaxBolus(0x24), // 8?
EventUnknown_MM522_0x25(0x25), // 8?
ToggleRemote(0x26, "EnableDisableRemote", 2, 5, 0), // 2, 5, 14
ToggleRemote(0x26, "EnableDisableRemote", 2, 5, 14), // 2, 5, 14 V6:2,5,14
ChangeRemoteId(0x27, "ChangeRemoteID"), // ??
ChangeMaxBasal(0x2c), //
@ -88,7 +88,7 @@ public enum PumpHistoryEntryType // implements CodeEnum
EventUnknown_MM512_0x38(0x38), //
EventUnknown_MM512_0x39(0x39), //
EventUnknown_MM512_0x3b(0x3b), //
ChangeParadigmLinkID(0x3c), // V3 ?
ChangeParadigmLinkID(0x3c, 2, 5, 14), // V3 ? V6: 2,5,14
BGReceived(0x3f, "BGReceived", 2, 5, 3), // Ian3F
JournalEntryMealMarker(0x40, 2, 5, 2), //
@ -232,7 +232,12 @@ public enum PumpHistoryEntryType // implements CodeEnum
static void setSpecialRulesForEntryTypes() {
EndResultTotals.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 3));
Bolus.addSpecialRuleHead(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 8));
BolusWizardChange.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_522andHigher, 143));
// BolusWizardChange.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_522andHigher, 143));
BolusWizardChange.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 143)); // V5:
// 522
// has
// old
// form
BolusWizardBolusEstimate.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 15));
BolusReminder.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 2));
}
@ -260,6 +265,11 @@ public enum PumpHistoryEntryType // implements CodeEnum
// }
//
public static boolean isRelevantEntry() {
return true;
}
public int getCode() {
return this.opCode;
}
@ -340,6 +350,8 @@ public enum PumpHistoryEntryType // implements CodeEnum
}
// byte[] dh = { 2, 3 };
private int determineSizeByRule(int defaultValue, List<SpecialRule> rules) {
int size = defaultValue;
@ -353,8 +365,6 @@ public enum PumpHistoryEntryType // implements CodeEnum
return size;
}
// byte[] dh = { 2, 3 };
enum DateFormat {
None(0), //
LongDate(5), //

View file

@ -26,7 +26,7 @@ import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
*/
public class BasalProfile {
protected static final int MAX_RAW_DATA_SIZE = (48 * 3) + 1;
public static final int MAX_RAW_DATA_SIZE = (48 * 3) + 1;
// private static final String TAG = "BasalProfile";
private static final Logger LOG = LoggerFactory.getLogger(BasalProfile.class);
private static final boolean DEBUG_BASALPROFILE = false;
@ -50,35 +50,6 @@ public class BasalProfile {
}
public static void testParser() {
byte[] testData = new byte[] {
32, 0, 0, 38, 0, 13, 44, 0, 19, 38, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/*
* from decocare:
* _test_schedule = {'total': 22.50, 'schedule': [
* { 'start': '12:00A', 'rate': 0.80 },
* { 'start': '6:30A', 'rate': 0.95 },
* { 'start': '9:30A', 'rate': 1.10 },
* { 'start': '2:00P', 'rate': 0.95 },
* ]}
*/
BasalProfile profile = new BasalProfile();
profile.setRawData(testData);
List<BasalProfileEntry> entries = profile.getEntries();
if (entries.isEmpty()) {
LOG.error("testParser: failed");
} else {
for (int i = 0; i < entries.size(); i++) {
BasalProfileEntry e = entries.get(i);
LOG.debug(String.format("testParser entry #%d: rate: %.2f, start %d:%d", i, e.rate,
e.startTime.getHourOfDay(), e.startTime.getMinuteOfHour()));
}
}
}
public void init() {
mRawData = new byte[MAX_RAW_DATA_SIZE];
mRawData[0] = 0;
@ -108,6 +79,7 @@ public class BasalProfile {
for (int i = 0; i < entries.size(); i++) {
BasalProfileEntry entry = entries.get(i);
String startString = entry.startTime.toString("HH:mm");
// this doesn't work
LOG.debug(String.format("Entry %d, rate=%.3f (0x%02X), start=%s (0x%02X)", i + 1, entry.rate,
entry.rate_raw, startString, entry.startTime_raw));
@ -122,8 +94,7 @@ public class BasalProfile {
BasalProfileEntry entry = entries.get(i);
String startString = entry.startTime.toString("HH:mm");
sb.append(String.format("Entry %d, rate=%.3f (0x%02X), start=%s (0x%02X)\n", i + 1, entry.rate,
entry.rate_raw, startString, entry.startTime_raw));
sb.append(String.format("Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString));
}
return sb.toString();
@ -187,22 +158,19 @@ public class BasalProfile {
LOG.warn("Raw Data is empty.");
return entries; // an empty list
}
int i = 0;
boolean done = false;
int r, st;
while (!done) {
for (int i = 0; i < mRawData.length - 2; i += 3) {
if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0))
break;
r = MedtronicUtil.makeUnsignedShort(mRawData[i + 1], mRawData[i]); // readUnsignedByte(mRawData[i]);
// What is mRawData[i+1]? Not used in decocare.
st = readUnsignedByte(mRawData[i + 2]);
entries.add(new BasalProfileEntry(r, st));
i = i + 3;
if (i >= MAX_RAW_DATA_SIZE) {
done = true;
} else if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0)) {
done = true;
}
}
return entries;
}
@ -228,16 +196,8 @@ public class BasalProfile {
byte[] strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true);
// TODO check if this is correct
outData.add(profileEntry.rate_raw[0]);
outData.add(profileEntry.rate_raw[1]);
// int time = profileEntry.startTime.getHourOfDay();
// if (profileEntry.startTime.getMinuteOfHour() == 30) {
// time++;
// }
outData.add(profileEntry.startTime_raw);
}

View file

@ -133,4 +133,10 @@ public class BolusDTO extends PumpTimeStampedRecord {
}
@Override
public String toString() {
return "BolusDTO [type=" + bolusType.name() + ", " + getValue() + "]";
}
}

View file

@ -20,4 +20,10 @@ public class PumpSettingDTO {
this.configurationGroup = configurationGroup;
}
@Override
public String toString() {
return "PumpSettingDTO [key=" + key + ",value=" + value + ",group=" + configurationGroup.name() + "]";
}
}

View file

@ -26,6 +26,23 @@ public class TempBasalPair {
}
/**
* This constructor is for use with PumpHistoryDecoder
*
* @param rateByte
* @param startTimeByte
* @param isPercent
*/
public TempBasalPair(byte rateByte, int startTimeByte, boolean isPercent) {
if (isPercent)
this.insulinRate = rateByte;
else
this.insulinRate = rateByte * 0.025;
this.durationMinutes = startTimeByte * 30;
this.isPercent = isPercent;
}
public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) {
this.insulinRate = insulinRate;
this.isPercent = isPercent;
@ -90,8 +107,7 @@ public class TempBasalPair {
list.add((byte)5);
byte[] insulinRate = MedtronicUtil.getBasalStrokes(this.insulinRate, true);
byte[] timeMin = MedtronicUtil.getByteArrayFromUnsignedShort(
MedtronicUtil.getIntervalFromMinutes(durationMinutes), true);
byte timeMin = (byte)MedtronicUtil.getIntervalFromMinutes(durationMinutes);
// list.add((byte) 0); // ?
@ -100,19 +116,19 @@ public class TempBasalPair {
if (insulinRate.length == 1)
list.add((byte)0x00);
else
list.add(insulinRate[1]);
list.add(insulinRate[0]);
list.add(insulinRate[0]);
list.add(insulinRate[1]);
// list.add((byte) 0); // percent amount
list.add(timeMin[0]); // 3 (time) - OK
list.add(timeMin); // 3 (time) - OK
if (insulinRate.length == 1)
list.add((byte)0x00);
else
list.add(insulinRate[1]);
list.add(insulinRate[0]);
list.add(insulinRate[0]);
list.add(insulinRate[1]);
return MedtronicUtil.createByteArray(list);
}

View file

@ -73,7 +73,7 @@ public enum MedtronicDeviceType {
MedtronicConverterType pumpConverter;
MedtronicConverterType cgmsConverter;
private String pumpModel = "";
private String pumpModel;
// String smallReservoirPump;
// String bigReservoirPump;
@ -144,6 +144,11 @@ public enum MedtronicDeviceType {
}
public boolean isLargerFormat() {
return isSameDevice(this, Medtronic_523andHigher);
}
public int getBolusStrokes() {
return (isLargerFormat(this)) ? 40 : 10;
}

View file

@ -62,8 +62,6 @@ public class MedtronicPumpStatus extends PumpStatus {
boolean rileyLinkAddressChanged = false;
private String[] frequencies;
private boolean isFrequencyUS = false;
// fixme
private Map<String, PumpType> medtronicPumpMap = null;
private Map<String, MedtronicDeviceType> medtronicDeviceTypeMap = null;

View file

@ -20,10 +20,10 @@ public class MedtronicConst {
public class Statistics {
public static final String StatsPrefix = "medtronic_";
public static final String FirstPumpStart = Prefix + "first_pump_use";
public static final String LastGoodPumpCommunicationTime = Prefix + "lastGoodPumpCommunicationTime";
public static final String LastGoodPumpFrequency = Prefix + "LastGoodPumpFrequency";
static final String StatsPrefix = "medtronic_";
public static final String TBRsSet = StatsPrefix + "tbrs_set";
public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered";
public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered";

View file

@ -68,9 +68,9 @@ public class MedtronicUtil extends RileyLinkUtil {
byte lowByte = (byte)(shortValue & 0xFF);
if (highByte > 0) {
return createByteArray(lowByte, highByte);
return createByteArray(highByte, lowByte);
} else {
return returnFixedSize ? createByteArray(lowByte, highByte) : createByteArray(lowByte);
return returnFixedSize ? createByteArray(highByte, lowByte) : createByteArray(lowByte);
}
}
@ -119,7 +119,34 @@ public class MedtronicUtil extends RileyLinkUtil {
public static byte[] getBolusStrokes(double amount) {
return getStrokes(amount, medtronicPumpModel.getBolusStrokes(), false);
int strokesPerUnit = medtronicPumpModel.getBolusStrokes();
int length;
int scrollRate;
if (strokesPerUnit >= 40) {
length = 2;
// 40-stroke pumps scroll faster for higher unit values
if (amount > 10)
scrollRate = 4;
else if (amount > 1)
scrollRate = 2;
else
scrollRate = 1;
} else {
length = 1;
scrollRate = 1;
}
int strokes = (int)(amount * ((strokesPerUnit * 1.0d) / (scrollRate * 1.0d))) * scrollRate;
byte[] body = ByteUtil.fromHexString(String.format("%02x%0" + (2 * length) + "x", length, strokes));
return body;
}
@ -307,4 +334,5 @@ public class MedtronicUtil extends RileyLinkUtil {
public static void setSettings(Map<String, PumpSettingDTO> settings) {
MedtronicUtil.settings = settings;
}
}