- kotlin changes
- disabled problem in Fragment for now
This commit is contained in:
parent
0d1ff8dd41
commit
bb657a2498
10 changed files with 723 additions and 979 deletions
|
@ -311,13 +311,21 @@ class MedtronicFragment : DaggerFragment() {
|
||||||
binding.lastBolus.text = ""
|
binding.lastBolus.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
val pumpState = pumpSync.expectedPumpState()
|
if (true) {
|
||||||
// base basal rate
|
// base basal rate
|
||||||
binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") "
|
binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") "
|
||||||
+ resourceHelper.gs(R.string.pump_basebasalrate, medtronicPumpPlugin.baseBasalRate))
|
+ resourceHelper.gs(R.string.pump_basebasalrate, medtronicPumpPlugin.baseBasalRate))
|
||||||
|
|
||||||
binding.tempBasal.text = pumpState.temporaryBasal?.toStringFull(dateUtil)
|
binding.tempBasal.text = "??"
|
||||||
?: ""
|
} else {
|
||||||
|
val pumpState = pumpSync.expectedPumpState()
|
||||||
|
// base basal rate
|
||||||
|
binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") "
|
||||||
|
+ resourceHelper.gs(R.string.pump_basebasalrate, medtronicPumpPlugin.baseBasalRate))
|
||||||
|
|
||||||
|
binding.tempBasal.text = pumpState.temporaryBasal?.toStringFull(dateUtil)
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
|
||||||
// battery
|
// battery
|
||||||
if (medtronicPumpStatus.batteryType == BatteryType.None || medtronicPumpStatus.batteryVoltage == null) {
|
if (medtronicPumpStatus.batteryType == BatteryType.None || medtronicPumpStatus.batteryVoltage == null) {
|
||||||
|
|
|
@ -1499,10 +1499,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements Pump, Ril
|
||||||
|
|
||||||
for (BasalProfileEntry profileEntry : basalProfile.getEntries()) {
|
for (BasalProfileEntry profileEntry : basalProfile.getEntries()) {
|
||||||
|
|
||||||
if (profileEntry.rate > medtronicPumpStatus.maxBasal) {
|
if (profileEntry.getRate() > medtronicPumpStatus.maxBasal) {
|
||||||
stringBuilder.append(profileEntry.startTime.toString("HH:mm"));
|
stringBuilder.append(profileEntry.getStartTime().toString("HH:mm"));
|
||||||
stringBuilder.append("=");
|
stringBuilder.append("=");
|
||||||
stringBuilder.append(profileEntry.rate);
|
stringBuilder.append(profileEntry.getRate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,444 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.medtronic.comm;
|
|
||||||
|
|
||||||
import org.joda.time.IllegalFieldValueException;
|
|
||||||
import org.joda.time.LocalDateTime;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
|
||||||
import info.nightscout.androidaps.logging.LTag;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by andy on 5/9/18.
|
|
||||||
* High level decoder for data returned through MedtroniUIComm
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class MedtronicConverter {
|
|
||||||
|
|
||||||
private final AAPSLogger aapsLogger;
|
|
||||||
private final MedtronicUtil medtronicUtil;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public MedtronicConverter(
|
|
||||||
AAPSLogger aapsLogger,
|
|
||||||
MedtronicUtil medtronicUtil
|
|
||||||
) {
|
|
||||||
this.aapsLogger = aapsLogger;
|
|
||||||
this.medtronicUtil = medtronicUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object convertResponse(PumpType pumpType, MedtronicCommandType commandType, byte[] rawContent) {
|
|
||||||
|
|
||||||
if ((rawContent == null || rawContent.length < 1) && commandType != MedtronicCommandType.PumpModel) {
|
|
||||||
aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Content is empty or too short, no data to convert (type=%s,isNull=%b,length=%s)",
|
|
||||||
commandType.name(), rawContent == null, rawContent == null ? "-" : rawContent.length));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "Raw response before convert: " + ByteUtil.shortHexString(rawContent));
|
|
||||||
|
|
||||||
switch (commandType) {
|
|
||||||
|
|
||||||
case PumpModel: {
|
|
||||||
return decodeModel(rawContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
case GetRealTimeClock: {
|
|
||||||
return decodeTime(rawContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
case GetRemainingInsulin: {
|
|
||||||
return decodeRemainingInsulin(rawContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
case GetBatteryStatus: {
|
|
||||||
return decodeBatteryStatus(rawContent); // 1
|
|
||||||
}
|
|
||||||
|
|
||||||
case GetBasalProfileSTD:
|
|
||||||
case GetBasalProfileA:
|
|
||||||
case GetBasalProfileB: {
|
|
||||||
return decodeBasalProfile(pumpType, rawContent);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case ReadTemporaryBasal: {
|
|
||||||
return new TempBasalPair(aapsLogger, rawContent); // 5
|
|
||||||
}
|
|
||||||
|
|
||||||
case Settings_512: {
|
|
||||||
return decodeSettingsLoop(rawContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Settings: {
|
|
||||||
return decodeSettingsLoop(rawContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
case SetBolus: {
|
|
||||||
return rawContent; // 1
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
throw new RuntimeException("Unsupported command Type: " + commandType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private BasalProfile decodeBasalProfile(PumpType pumpType, byte[] rawContent) {
|
|
||||||
|
|
||||||
BasalProfile basalProfile = new BasalProfile(aapsLogger, rawContent);
|
|
||||||
|
|
||||||
return basalProfile.verify(pumpType) ? basalProfile : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private MedtronicDeviceType decodeModel(byte[] rawContent) {
|
|
||||||
|
|
||||||
if ((rawContent == null || rawContent.length < 4)) {
|
|
||||||
aapsLogger.warn(LTag.PUMPCOMM, "Error reading PumpModel, returning Unknown_Device");
|
|
||||||
return MedtronicDeviceType.Unknown_Device;
|
|
||||||
}
|
|
||||||
|
|
||||||
String rawModel = StringUtil.fromBytes(ByteUtil.substring(rawContent, 1, 3));
|
|
||||||
MedtronicDeviceType pumpModel = MedtronicDeviceType.getByDescription(rawModel);
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "PumpModel: [raw=%s, resolved=%s]", rawModel, pumpModel.name()));
|
|
||||||
|
|
||||||
if (pumpModel != MedtronicDeviceType.Unknown_Device) {
|
|
||||||
if (!medtronicUtil.isModelSet()) {
|
|
||||||
medtronicUtil.setMedtronicPumpModel(pumpModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pumpModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private BatteryStatusDTO decodeBatteryStatus(byte[] rawData) {
|
|
||||||
// 00 7C 00 00
|
|
||||||
|
|
||||||
BatteryStatusDTO batteryStatus = new BatteryStatusDTO();
|
|
||||||
|
|
||||||
int status = rawData[0];
|
|
||||||
|
|
||||||
if (status == 0) {
|
|
||||||
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Normal;
|
|
||||||
} else if (status == 1) {
|
|
||||||
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Low;
|
|
||||||
} else if (status == 2) {
|
|
||||||
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawData.length > 1) {
|
|
||||||
|
|
||||||
Double d = null;
|
|
||||||
|
|
||||||
// if response in 3 bytes then we add additional information
|
|
||||||
if (rawData.length == 2) {
|
|
||||||
d = (rawData[1] * 1.0d) / 100.0d;
|
|
||||||
} else {
|
|
||||||
d = (ByteUtil.toInt(rawData[1], rawData[2]) * 1.0d) / 100.0d;
|
|
||||||
}
|
|
||||||
|
|
||||||
batteryStatus.voltage = d;
|
|
||||||
batteryStatus.extendedDataReceived = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return batteryStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Float decodeRemainingInsulin(byte[] rawData) {
|
|
||||||
int startIdx = 0;
|
|
||||||
|
|
||||||
MedtronicDeviceType pumpModel = medtronicUtil.getMedtronicPumpModel();
|
|
||||||
|
|
||||||
int strokes = pumpModel == null ? 10 : pumpModel.getBolusStrokes();
|
|
||||||
|
|
||||||
if (strokes == 40) {
|
|
||||||
startIdx = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int reqLength = startIdx + 1;
|
|
||||||
float value = 0;
|
|
||||||
|
|
||||||
if (reqLength >= rawData.length) {
|
|
||||||
value = rawData[startIdx] / (1.0f * strokes);
|
|
||||||
} else {
|
|
||||||
value = ByteUtil.toInt(rawData[startIdx], rawData[startIdx + 1]) / (1.0f * strokes);
|
|
||||||
}
|
|
||||||
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "Remaining insulin: " + value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private LocalDateTime decodeTime(byte[] rawContent) {
|
|
||||||
|
|
||||||
int hours = ByteUtil.asUINT8(rawContent[0]);
|
|
||||||
int minutes = ByteUtil.asUINT8(rawContent[1]);
|
|
||||||
int seconds = ByteUtil.asUINT8(rawContent[2]);
|
|
||||||
int year = (ByteUtil.asUINT8(rawContent[4]) & 0x3f) + 1984;
|
|
||||||
int month = ByteUtil.asUINT8(rawContent[5]);
|
|
||||||
int day = ByteUtil.asUINT8(rawContent[6]);
|
|
||||||
try {
|
|
||||||
LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds);
|
|
||||||
return pumpTime;
|
|
||||||
} catch (IllegalFieldValueException e) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM,
|
|
||||||
String.format(Locale.ENGLISH, "decodeTime: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d",
|
|
||||||
year, month, day, hours, minutes, seconds));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Map<String, PumpSettingDTO> decodeSettingsLoop(byte[] rd) {
|
|
||||||
|
|
||||||
Map<String, PumpSettingDTO> map = new HashMap<>();
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map);
|
|
||||||
addSettingToMap(
|
|
||||||
"PCFG_MAX_BASAL",
|
|
||||||
""
|
|
||||||
+ decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()],
|
|
||||||
rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map);
|
|
||||||
addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h",
|
|
||||||
PumpConfigurationGroup.General, map);
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
if (rd[10] == 1) {
|
|
||||||
String patt;
|
|
||||||
switch (rd[11]) {
|
|
||||||
case 0:
|
|
||||||
patt = "STD";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
patt = "A";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
patt = "B";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
patt = "???";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", "STD", PumpConfigurationGroup.Basal, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Map<String, PumpSettingDTO> decodeSettings512(byte[] rd) {
|
|
||||||
|
|
||||||
Map<String, PumpSettingDTO> map = new HashMap<>();
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_AUTOOFF_TIMEOUT", "" + rd[0], PumpConfigurationGroup.General, map);
|
|
||||||
|
|
||||||
if (rd[1] == 4) {
|
|
||||||
addSettingToMap("PCFG_ALARM_MODE", "Silent", PumpConfigurationGroup.Sound, map);
|
|
||||||
} else {
|
|
||||||
addSettingToMap("PCFG_ALARM_MODE", "Normal", PumpConfigurationGroup.Sound, map);
|
|
||||||
addSettingToMap("PCFG_ALARM_BEEP_VOLUME", "" + rd[1], PumpConfigurationGroup.Sound, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_AUDIO_BOLUS_ENABLED", parseResultEnable(rd[2]), PumpConfigurationGroup.Bolus, map);
|
|
||||||
|
|
||||||
if (rd[2] == 1) {
|
|
||||||
addSettingToMap("PCFG_AUDIO_BOLUS_STEP_SIZE", "" + decodeBolusInsulin(ByteUtil.asUINT8(rd[3])),
|
|
||||||
PumpConfigurationGroup.Bolus, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_VARIABLE_BOLUS_ENABLED", parseResultEnable(rd[4]), PumpConfigurationGroup.Bolus, map);
|
|
||||||
addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map);
|
|
||||||
addSettingToMap(
|
|
||||||
"PCFG_MAX_BASAL",
|
|
||||||
""
|
|
||||||
+ decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()],
|
|
||||||
rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map);
|
|
||||||
addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h",
|
|
||||||
PumpConfigurationGroup.General, map);
|
|
||||||
|
|
||||||
if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) {
|
|
||||||
addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] == 0 ? 50 : 100), PumpConfigurationGroup.Insulin,
|
|
||||||
map);
|
|
||||||
// LOG.debug("Insulin concentration: " + rd[9]);
|
|
||||||
} else {
|
|
||||||
addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] != 0 ? 50 : 100), PumpConfigurationGroup.Insulin,
|
|
||||||
map);
|
|
||||||
// LOG.debug("Insulin concentration: " + rd[9]);
|
|
||||||
}
|
|
||||||
addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
if (rd[10] == 1) {
|
|
||||||
String patt;
|
|
||||||
switch (rd[11]) {
|
|
||||||
case 0:
|
|
||||||
patt = "STD";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
patt = "A";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
patt = "B";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
patt = "???";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("CFG_MM_RF_ENABLED", parseResultEnable(rd[12]), PumpConfigurationGroup.General, map);
|
|
||||||
addSettingToMap("CFG_MM_BLOCK_ENABLED", parseResultEnable(rd[13]), PumpConfigurationGroup.General, map);
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map);
|
|
||||||
|
|
||||||
if (rd[14] == 1) {
|
|
||||||
addSettingToMap("PCFG_TEMP_BASAL_PERCENT", "" + rd[15], PumpConfigurationGroup.Basal, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("CFG_PARADIGM_LINK_ENABLE", parseResultEnable(rd[16]), PumpConfigurationGroup.General, map);
|
|
||||||
|
|
||||||
decodeInsulinActionSetting(rd, map);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void addSettingToMap(String key, String value, PumpConfigurationGroup group, Map<String, PumpSettingDTO> map) {
|
|
||||||
map.put(key, new PumpSettingDTO(key, value, group));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Map<String, PumpSettingDTO> decodeSettings(byte[] rd) {
|
|
||||||
Map<String, PumpSettingDTO> map = decodeSettings512(rd);
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_MM_RESERVOIR_WARNING_TYPE_TIME", rd[18] != 0 ? "PCFG_MM_RESERVOIR_WARNING_TYPE_TIME"
|
|
||||||
: "PCFG_MM_RESERVOIR_WARNING_TYPE_UNITS", PumpConfigurationGroup.Other, map);
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_MM_SRESERVOIR_WARNING_POINT", "" + ByteUtil.asUINT8(rd[19]),
|
|
||||||
PumpConfigurationGroup.Other, map);
|
|
||||||
|
|
||||||
addSettingToMap("CFG_MM_KEYPAD_LOCKED", parseResultEnable(rd[20]), PumpConfigurationGroup.Other, map);
|
|
||||||
|
|
||||||
if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher)) {
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_BOLUS_SCROLL_STEP_SIZE", "" + rd[21], PumpConfigurationGroup.Bolus, map);
|
|
||||||
addSettingToMap("PCFG_CAPTURE_EVENT_ENABLE", parseResultEnable(rd[22]), PumpConfigurationGroup.Other, map);
|
|
||||||
addSettingToMap("PCFG_OTHER_DEVICE_ENABLE", parseResultEnable(rd[23]), PumpConfigurationGroup.Other, map);
|
|
||||||
addSettingToMap("PCFG_OTHER_DEVICE_PAIRED_STATE", parseResultEnable(rd[24]), PumpConfigurationGroup.Other,
|
|
||||||
map);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String parseResultEnable(int i) {
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
return "No";
|
|
||||||
case 1:
|
|
||||||
return "Yes";
|
|
||||||
default:
|
|
||||||
return "???";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private float getStrokesPerUnit(boolean isBasal) {
|
|
||||||
return isBasal ? 40.0f : 10; // pumpModel.getBolusStrokes();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 512
|
|
||||||
private void decodeInsulinActionSetting(byte[] ai, Map<String, PumpSettingDTO> map) {
|
|
||||||
if (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_512_712)) {
|
|
||||||
addSettingToMap("PCFG_INSULIN_ACTION_TYPE", (ai[17] != 0 ? "Regular" : "Fast"),
|
|
||||||
PumpConfigurationGroup.Insulin, map);
|
|
||||||
} else {
|
|
||||||
int i = ai[17];
|
|
||||||
String s;
|
|
||||||
|
|
||||||
if ((i == 0) || (i == 1)) {
|
|
||||||
s = ai[17] != 0 ? "Regular" : "Fast";
|
|
||||||
} else {
|
|
||||||
if (i == 15)
|
|
||||||
s = "Unset";
|
|
||||||
else
|
|
||||||
s = "Curve: " + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
addSettingToMap("PCFG_INSULIN_ACTION_TYPE", s, PumpConfigurationGroup.Insulin, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private double decodeBasalInsulin(int i) {
|
|
||||||
return (double) i / (double) getStrokesPerUnit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private double decodeBolusInsulin(int i) {
|
|
||||||
|
|
||||||
return (double) i / (double) getStrokesPerUnit(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int getSettingIndexMaxBasal() {
|
|
||||||
return is523orHigher() ? 7 : 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int getSettingIndexTimeDisplayFormat() {
|
|
||||||
return is523orHigher() ? 9 : 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private double decodeMaxBolus(byte[] ai) {
|
|
||||||
return is523orHigher() ? decodeBolusInsulin(ByteUtil.toInt(ai[5], ai[6])) : decodeBolusInsulin(ByteUtil
|
|
||||||
.asUINT8(ai[5]));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean is523orHigher() {
|
|
||||||
return (MedtronicDeviceType.isSameDevice(medtronicUtil.getMedtronicPumpModel(), MedtronicDeviceType.Medtronic_523andHigher));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.comm
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
||||||
|
import org.joda.time.IllegalFieldValueException
|
||||||
|
import org.joda.time.LocalDateTime
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/9/18.
|
||||||
|
* High level decoder for data returned through MedtroniUIComm
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class MedtronicConverter @Inject constructor(
|
||||||
|
private val aapsLogger: AAPSLogger,
|
||||||
|
private val medtronicUtil: MedtronicUtil
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun convertResponse(pumpType: PumpType, commandType: MedtronicCommandType, rawContent: ByteArray?): Any? {
|
||||||
|
if ((rawContent == null || rawContent.size < 1) && commandType != MedtronicCommandType.PumpModel) {
|
||||||
|
aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Content is empty or too short, no data to convert (type=%s,isNull=%b,length=%s)",
|
||||||
|
commandType.name, rawContent == null, rawContent?.size ?: "-"))
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, "Raw response before convert: " + ByteUtil.shortHexString(rawContent))
|
||||||
|
return when (commandType) {
|
||||||
|
MedtronicCommandType.PumpModel -> {
|
||||||
|
decodeModel(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.GetRealTimeClock -> {
|
||||||
|
decodeTime(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.GetRemainingInsulin -> {
|
||||||
|
decodeRemainingInsulin(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.GetBatteryStatus -> {
|
||||||
|
decodeBatteryStatus(rawContent) // 1
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.GetBasalProfileSTD, MedtronicCommandType.GetBasalProfileA, MedtronicCommandType.GetBasalProfileB -> {
|
||||||
|
decodeBasalProfile(pumpType, rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.ReadTemporaryBasal -> {
|
||||||
|
TempBasalPair(aapsLogger, rawContent) // 5
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.Settings_512 -> {
|
||||||
|
decodeSettingsLoop(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.Settings -> {
|
||||||
|
decodeSettingsLoop(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicCommandType.SetBolus -> {
|
||||||
|
rawContent // 1
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
throw RuntimeException("Unsupported command Type: $commandType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeBasalProfile(pumpType: PumpType, rawContent: ByteArray?): BasalProfile? {
|
||||||
|
val basalProfile = BasalProfile(aapsLogger, rawContent!!)
|
||||||
|
return if (basalProfile.verify(pumpType)) basalProfile else null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeModel(rawContent: ByteArray?): MedtronicDeviceType {
|
||||||
|
if (rawContent == null || rawContent.size < 4) {
|
||||||
|
aapsLogger.warn(LTag.PUMPCOMM, "Error reading PumpModel, returning Unknown_Device")
|
||||||
|
return MedtronicDeviceType.Unknown_Device
|
||||||
|
}
|
||||||
|
val rawModel = StringUtil.fromBytes(ByteUtil.substring(rawContent, 1, 3))
|
||||||
|
val pumpModel = MedtronicDeviceType.getByDescription(rawModel)
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "PumpModel: [raw=%s, resolved=%s]", rawModel, pumpModel.name))
|
||||||
|
if (pumpModel != MedtronicDeviceType.Unknown_Device) {
|
||||||
|
if (!medtronicUtil.isModelSet) {
|
||||||
|
medtronicUtil.medtronicPumpModel = pumpModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pumpModel
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeBatteryStatus(rawData: ByteArray?): BatteryStatusDTO {
|
||||||
|
// 00 7C 00 00
|
||||||
|
val batteryStatus = BatteryStatusDTO()
|
||||||
|
val status = rawData!![0].toInt()
|
||||||
|
if (status == 0) {
|
||||||
|
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Normal
|
||||||
|
} else if (status == 1) {
|
||||||
|
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Low
|
||||||
|
} else if (status == 2) {
|
||||||
|
batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Unknown
|
||||||
|
}
|
||||||
|
if (rawData.size > 1) {
|
||||||
|
var d: Double? = null
|
||||||
|
|
||||||
|
// if response in 3 bytes then we add additional information
|
||||||
|
d = if (rawData.size == 2) {
|
||||||
|
rawData[1] * 1.0 / 100.0
|
||||||
|
} else {
|
||||||
|
ByteUtil.toInt(rawData[1], rawData[2]) * 1.0 / 100.0
|
||||||
|
}
|
||||||
|
batteryStatus.voltage = d
|
||||||
|
batteryStatus.extendedDataReceived = true
|
||||||
|
}
|
||||||
|
return batteryStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeRemainingInsulin(rawData: ByteArray?): Float {
|
||||||
|
var startIdx = 0
|
||||||
|
val pumpModel = medtronicUtil.medtronicPumpModel
|
||||||
|
val strokes = pumpModel?.bolusStrokes ?: 10
|
||||||
|
if (strokes == 40) {
|
||||||
|
startIdx = 2
|
||||||
|
}
|
||||||
|
val reqLength = startIdx + 1
|
||||||
|
var value = 0f
|
||||||
|
value = if (reqLength >= rawData!!.size) {
|
||||||
|
rawData[startIdx] / (1.0f * strokes)
|
||||||
|
} else {
|
||||||
|
ByteUtil.toInt(rawData[startIdx], rawData[startIdx + 1]) / (1.0f * strokes)
|
||||||
|
}
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, "Remaining insulin: $value")
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeTime(rawContent: ByteArray?): LocalDateTime? {
|
||||||
|
val hours = ByteUtil.asUINT8(rawContent!![0])
|
||||||
|
val minutes = ByteUtil.asUINT8(rawContent[1])
|
||||||
|
val seconds = ByteUtil.asUINT8(rawContent[2])
|
||||||
|
val year = (ByteUtil.asUINT8(rawContent[4]) and 0x3f) + 1984
|
||||||
|
val month = ByteUtil.asUINT8(rawContent[5])
|
||||||
|
val day = ByteUtil.asUINT8(rawContent[6])
|
||||||
|
return try {
|
||||||
|
LocalDateTime(year, month, day, hours, minutes, seconds)
|
||||||
|
} catch (e: IllegalFieldValueException) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "decodeTime: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d",
|
||||||
|
year, month, day, hours, minutes, seconds))
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeSettingsLoop(rd: ByteArray?): Map<String, PumpSettingDTO> {
|
||||||
|
val map: MutableMap<String, PumpSettingDTO> = HashMap()
|
||||||
|
addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map)
|
||||||
|
addSettingToMap(
|
||||||
|
"PCFG_MAX_BASAL", ""
|
||||||
|
+ decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd!![settingIndexMaxBasal].toInt(),
|
||||||
|
rd[settingIndexMaxBasal + 1].toInt())), PumpConfigurationGroup.Basal, map)
|
||||||
|
addSettingToMap("CFG_BASE_CLOCK_MODE", if (rd[settingIndexTimeDisplayFormat].toInt() == 0) "12h" else "24h",
|
||||||
|
PumpConfigurationGroup.General, map)
|
||||||
|
addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10].toInt()), PumpConfigurationGroup.Basal, map)
|
||||||
|
if (rd[10].toInt() == 1) {
|
||||||
|
val patt: String
|
||||||
|
patt = when (rd[11].toInt()) {
|
||||||
|
0 -> "STD"
|
||||||
|
1 -> "A"
|
||||||
|
2 -> "B"
|
||||||
|
else -> "???"
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map)
|
||||||
|
} else {
|
||||||
|
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", "STD", PumpConfigurationGroup.Basal, map)
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_TEMP_BASAL_TYPE", if (rd[14].toInt() != 0) "Percent" else "Units", PumpConfigurationGroup.Basal, map)
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeSettings512(rd: ByteArray): MutableMap<String, PumpSettingDTO> {
|
||||||
|
val map: MutableMap<String, PumpSettingDTO> = HashMap()
|
||||||
|
addSettingToMap("PCFG_AUTOOFF_TIMEOUT", "" + rd[0], PumpConfigurationGroup.General, map)
|
||||||
|
if (rd[1].toInt() == 4) {
|
||||||
|
addSettingToMap("PCFG_ALARM_MODE", "Silent", PumpConfigurationGroup.Sound, map)
|
||||||
|
} else {
|
||||||
|
addSettingToMap("PCFG_ALARM_MODE", "Normal", PumpConfigurationGroup.Sound, map)
|
||||||
|
addSettingToMap("PCFG_ALARM_BEEP_VOLUME", "" + rd[1], PumpConfigurationGroup.Sound, map)
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_AUDIO_BOLUS_ENABLED", parseResultEnable(rd[2].toInt()), PumpConfigurationGroup.Bolus, map)
|
||||||
|
if (rd[2].toInt() == 1) {
|
||||||
|
addSettingToMap("PCFG_AUDIO_BOLUS_STEP_SIZE", "" + decodeBolusInsulin(ByteUtil.asUINT8(rd[3])),
|
||||||
|
PumpConfigurationGroup.Bolus, map)
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_VARIABLE_BOLUS_ENABLED", parseResultEnable(rd[4].toInt()), PumpConfigurationGroup.Bolus, map)
|
||||||
|
addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map)
|
||||||
|
addSettingToMap(
|
||||||
|
"PCFG_MAX_BASAL", ""
|
||||||
|
+ decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[settingIndexMaxBasal].toInt(),
|
||||||
|
rd[settingIndexMaxBasal + 1].toInt())), PumpConfigurationGroup.Basal, map)
|
||||||
|
addSettingToMap("CFG_BASE_CLOCK_MODE", if (rd[settingIndexTimeDisplayFormat].toInt() == 0) "12h" else "24h",
|
||||||
|
PumpConfigurationGroup.General, map)
|
||||||
|
if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) {
|
||||||
|
addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + if (rd[9].toInt() == 0) 50 else 100, PumpConfigurationGroup.Insulin,
|
||||||
|
map)
|
||||||
|
// LOG.debug("Insulin concentration: " + rd[9]);
|
||||||
|
} else {
|
||||||
|
addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + if (rd[9].toInt() != 0) 50 else 100, PumpConfigurationGroup.Insulin,
|
||||||
|
map)
|
||||||
|
// LOG.debug("Insulin concentration: " + rd[9]);
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10].toInt()), PumpConfigurationGroup.Basal, map)
|
||||||
|
if (rd[10].toInt() == 1) {
|
||||||
|
val patt: String
|
||||||
|
patt = when (rd[11].toInt()) {
|
||||||
|
0 -> "STD"
|
||||||
|
1 -> "A"
|
||||||
|
2 -> "B"
|
||||||
|
else -> "???"
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map)
|
||||||
|
}
|
||||||
|
addSettingToMap("CFG_MM_RF_ENABLED", parseResultEnable(rd[12].toInt()), PumpConfigurationGroup.General, map)
|
||||||
|
addSettingToMap("CFG_MM_BLOCK_ENABLED", parseResultEnable(rd[13].toInt()), PumpConfigurationGroup.General, map)
|
||||||
|
addSettingToMap("PCFG_TEMP_BASAL_TYPE", if (rd[14].toInt() != 0) "Percent" else "Units", PumpConfigurationGroup.Basal, map)
|
||||||
|
if (rd[14].toInt() == 1) {
|
||||||
|
addSettingToMap("PCFG_TEMP_BASAL_PERCENT", "" + rd[15], PumpConfigurationGroup.Basal, map)
|
||||||
|
}
|
||||||
|
addSettingToMap("CFG_PARADIGM_LINK_ENABLE", parseResultEnable(rd[16].toInt()), PumpConfigurationGroup.General, map)
|
||||||
|
decodeInsulinActionSetting(rd, map)
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSettingToMap(key: String, value: String, group: PumpConfigurationGroup, map: MutableMap<String, PumpSettingDTO>) {
|
||||||
|
map[key] = PumpSettingDTO(key, value, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeSettings(rd: ByteArray): Map<String, PumpSettingDTO> {
|
||||||
|
val map = decodeSettings512(rd)
|
||||||
|
addSettingToMap("PCFG_MM_RESERVOIR_WARNING_TYPE_TIME", if (rd[18].toInt() != 0) "PCFG_MM_RESERVOIR_WARNING_TYPE_TIME" else "PCFG_MM_RESERVOIR_WARNING_TYPE_UNITS", PumpConfigurationGroup.Other, map)
|
||||||
|
addSettingToMap("PCFG_MM_SRESERVOIR_WARNING_POINT", "" + ByteUtil.asUINT8(rd[19]),
|
||||||
|
PumpConfigurationGroup.Other, map)
|
||||||
|
addSettingToMap("CFG_MM_KEYPAD_LOCKED", parseResultEnable(rd[20].toInt()), PumpConfigurationGroup.Other, map)
|
||||||
|
if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)) {
|
||||||
|
addSettingToMap("PCFG_BOLUS_SCROLL_STEP_SIZE", "" + rd[21], PumpConfigurationGroup.Bolus, map)
|
||||||
|
addSettingToMap("PCFG_CAPTURE_EVENT_ENABLE", parseResultEnable(rd[22].toInt()), PumpConfigurationGroup.Other, map)
|
||||||
|
addSettingToMap("PCFG_OTHER_DEVICE_ENABLE", parseResultEnable(rd[23].toInt()), PumpConfigurationGroup.Other, map)
|
||||||
|
addSettingToMap("PCFG_OTHER_DEVICE_PAIRED_STATE", parseResultEnable(rd[24].toInt()), PumpConfigurationGroup.Other,
|
||||||
|
map)
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseResultEnable(i: Int): String {
|
||||||
|
return when (i) {
|
||||||
|
0 -> "No"
|
||||||
|
1 -> "Yes"
|
||||||
|
else -> "???"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStrokesPerUnit(isBasal: Boolean): Float {
|
||||||
|
return if (isBasal) 40.0f else 10.0f // pumpModel.getBolusStrokes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 512
|
||||||
|
private fun decodeInsulinActionSetting(ai: ByteArray, map: MutableMap<String, PumpSettingDTO>) {
|
||||||
|
if (MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_512_712)) {
|
||||||
|
addSettingToMap("PCFG_INSULIN_ACTION_TYPE", if (ai[17].toInt() != 0) "Regular" else "Fast",
|
||||||
|
PumpConfigurationGroup.Insulin, map)
|
||||||
|
} else {
|
||||||
|
val i = ai[17].toInt()
|
||||||
|
val s: String
|
||||||
|
s = if (i == 0 || i == 1) {
|
||||||
|
if (ai[17].toInt() != 0) "Regular" else "Fast"
|
||||||
|
} else {
|
||||||
|
if (i == 15) "Unset" else "Curve: $i"
|
||||||
|
}
|
||||||
|
addSettingToMap("PCFG_INSULIN_ACTION_TYPE", s, PumpConfigurationGroup.Insulin, map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeBasalInsulin(i: Int): Double {
|
||||||
|
return i.toDouble() / getStrokesPerUnit(true).toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeBolusInsulin(i: Int): Double {
|
||||||
|
return i.toDouble() / getStrokesPerUnit(false).toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val settingIndexMaxBasal: Int
|
||||||
|
private get() = if (is523orHigher()) 7 else 6
|
||||||
|
|
||||||
|
private val settingIndexTimeDisplayFormat: Int
|
||||||
|
private get() = if (is523orHigher()) 9 else 8
|
||||||
|
|
||||||
|
private fun decodeMaxBolus(ai: ByteArray?): Double {
|
||||||
|
return if (is523orHigher()) decodeBolusInsulin(ByteUtil.toInt(ai!![5], ai[6])) else decodeBolusInsulin(ByteUtil
|
||||||
|
.asUINT8(ai!![5]))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun is523orHigher(): Boolean {
|
||||||
|
return MedtronicDeviceType.isSameDevice(medtronicUtil.medtronicPumpModel, MedtronicDeviceType.Medtronic_523andHigher)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,7 +10,9 @@ import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms.CGMSH
|
||||||
import okhttp3.internal.and
|
import okhttp3.internal.and
|
||||||
import org.joda.time.LocalDateTime
|
import org.joda.time.LocalDateTime
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import org.slf4j.LoggerFactory.getLogger as getLogger1
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
* This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes
|
||||||
|
@ -264,6 +266,6 @@ class MedtronicCGMSHistoryDecoder : MedtronicHistoryDecoder<CGMSHistoryEntry>()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG: Logger = getLogger(LTag.PUMPCOMM)
|
private val LOG: Logger = getLogger1("MedtronicCGMSHistoryDecoder")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,384 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.medtronic.data.dto;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose;
|
|
||||||
|
|
||||||
import org.joda.time.Instant;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
|
||||||
import info.nightscout.androidaps.logging.LTag;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by geoff on 6/1/15.
|
|
||||||
* <p>
|
|
||||||
* There are three basal profiles stored on the pump. (722 only?) They are all parsed the same, the user just has 3 to
|
|
||||||
* choose from: Standard, A, and B
|
|
||||||
* <p>
|
|
||||||
* The byte array is 48 times three byte entries long, plus a zero? If the profile is completely empty, it should have
|
|
||||||
* one entry: [0,0,0x3F]. The first entry of [0,0,0] marks the end of the used entries.
|
|
||||||
* <p>
|
|
||||||
* Each entry is assumed to span from the specified start time to the start time of the next entry, or to midnight if
|
|
||||||
* there are no more entries.
|
|
||||||
* <p>
|
|
||||||
* Individual entries are of the form [r,z,m] where r is the rate (in 0.025 U increments) z is zero (?) m is the start
|
|
||||||
* time-of-day for the basal rate period (in 30 minute increments)
|
|
||||||
*/
|
|
||||||
public class BasalProfile {
|
|
||||||
|
|
||||||
private final AAPSLogger aapsLogger;
|
|
||||||
|
|
||||||
public static final int MAX_RAW_DATA_SIZE = (48 * 3) + 1;
|
|
||||||
private static final boolean DEBUG_BASALPROFILE = false;
|
|
||||||
@Expose
|
|
||||||
private byte[] mRawData; // store as byte array to make transport (via parcel) easier
|
|
||||||
private List<BasalProfileEntry> listEntries;
|
|
||||||
|
|
||||||
|
|
||||||
public BasalProfile(AAPSLogger aapsLogger) {
|
|
||||||
this.aapsLogger = aapsLogger;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public BasalProfile(AAPSLogger aapsLogger, byte[] data) {
|
|
||||||
this.aapsLogger = aapsLogger;
|
|
||||||
setRawData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// this asUINT8 should be combined with Record.asUINT8, and placed in a new util class.
|
|
||||||
private static int readUnsignedByte(byte b) {
|
|
||||||
return (b < 0) ? b + 256 : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void init() {
|
|
||||||
mRawData = new byte[MAX_RAW_DATA_SIZE];
|
|
||||||
mRawData[0] = 0;
|
|
||||||
mRawData[1] = 0;
|
|
||||||
mRawData[2] = 0x3f;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean setRawData(byte[] data) {
|
|
||||||
if (data == null) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have just one entry through all day it looks like just length 1
|
|
||||||
if (data.length == 1) {
|
|
||||||
data = MedtronicUtil.createByteArray(data[0], (byte) 0, (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length == MAX_RAW_DATA_SIZE) {
|
|
||||||
mRawData = data;
|
|
||||||
} else {
|
|
||||||
int len = Math.min(MAX_RAW_DATA_SIZE, data.length);
|
|
||||||
mRawData = new byte[MAX_RAW_DATA_SIZE];
|
|
||||||
System.arraycopy(data, 0, mRawData, 0, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean setRawDataFromHistory(byte[] data) {
|
|
||||||
if (data == null) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mRawData = new byte[MAX_RAW_DATA_SIZE];
|
|
||||||
int item = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < data.length - 2; i += 3) {
|
|
||||||
|
|
||||||
if ((data[i] == 0) && (data[i + 1] == 0) && (data[i + 2] == 0)) {
|
|
||||||
mRawData[i] = 0;
|
|
||||||
mRawData[i + 1] = 0;
|
|
||||||
mRawData[i + 2] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mRawData[i] = data[i + 1];
|
|
||||||
mRawData[i + 1] = data[i + 2];
|
|
||||||
mRawData[i + 2] = data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void dumpBasalProfile() {
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "Basal Profile entries:");
|
|
||||||
List<BasalProfileEntry> entries = getEntries();
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
|
||||||
BasalProfileEntry entry = entries.get(i);
|
|
||||||
String startString = entry.startTime.toString("HH:mm");
|
|
||||||
// this doesn't work
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Entry %d, rate=%.3f (%s), start=%s (0x%02X)", i + 1, entry.rate,
|
|
||||||
ByteUtil.getHex(entry.rate_raw), startString, entry.startTime_raw));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getBasalProfileAsString() {
|
|
||||||
StringBuffer sb = new StringBuffer("Basal Profile entries:\n");
|
|
||||||
List<BasalProfileEntry> entries = getEntries();
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
|
||||||
BasalProfileEntry entry = entries.get(i);
|
|
||||||
String startString = entry.startTime.toString("HH:mm");
|
|
||||||
|
|
||||||
sb.append(String.format(Locale.ENGLISH, "Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String basalProfileToStringError() {
|
|
||||||
return "Basal Profile [rawData=" + ByteUtil.shortHexString(this.getRawData()) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String basalProfileToString() {
|
|
||||||
StringBuffer sb = new StringBuffer("Basal Profile [");
|
|
||||||
List<BasalProfileEntry> entries = getEntries();
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
|
||||||
BasalProfileEntry entry = entries.get(i);
|
|
||||||
String startString = entry.startTime.toString("HH:mm");
|
|
||||||
|
|
||||||
sb.append(String.format(Locale.ENGLISH, "%s=%.3f, ", startString, entry.rate));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append("]");
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: this function must be expanded to include changes in which profile is in use.
|
|
||||||
// and changes to the profiles themselves.
|
|
||||||
public BasalProfileEntry getEntryForTime(Instant when) {
|
|
||||||
BasalProfileEntry rval = new BasalProfileEntry();
|
|
||||||
List<BasalProfileEntry> entries = getEntries();
|
|
||||||
if (entries.size() == 0) {
|
|
||||||
aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): table is empty",
|
|
||||||
when.toDateTime().toLocalTime().toString("HH:mm")));
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
// Log.w(TAG,"Assuming first entry");
|
|
||||||
rval = entries.get(0);
|
|
||||||
if (entries.size() == 1) {
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "getEntryForTime: Only one entry in profile");
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
int localMillis = when.toDateTime().toLocalTime().getMillisOfDay();
|
|
||||||
boolean done = false;
|
|
||||||
int i = 1;
|
|
||||||
while (!done) {
|
|
||||||
BasalProfileEntry entry = entries.get(i);
|
|
||||||
if (DEBUG_BASALPROFILE) {
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Comparing 'now'=%s to entry 'start time'=%s", when.toDateTime().toLocalTime()
|
|
||||||
.toString("HH:mm"), entry.startTime.toString("HH:mm")));
|
|
||||||
}
|
|
||||||
if (localMillis >= entry.startTime.getMillisOfDay()) {
|
|
||||||
rval = entry;
|
|
||||||
if (DEBUG_BASALPROFILE)
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "Accepted Entry");
|
|
||||||
} else {
|
|
||||||
// entry at i has later start time, keep older entry
|
|
||||||
if (DEBUG_BASALPROFILE)
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, "Rejected Entry");
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
if (i >= entries.size()) {
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (DEBUG_BASALPROFILE) {
|
|
||||||
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): Returning entry: rate=%.3f (%s), start=%s (%d)", when
|
|
||||||
.toDateTime().toLocalTime().toString("HH:mm"), rval.rate, ByteUtil.getHex(rval.rate_raw),
|
|
||||||
rval.startTime.toString("HH:mm"), rval.startTime_raw));
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public List<BasalProfileEntry> getEntries() {
|
|
||||||
List<BasalProfileEntry> entries = new ArrayList<>();
|
|
||||||
|
|
||||||
if (mRawData == null || mRawData[2] == 0x3f) {
|
|
||||||
aapsLogger.warn(LTag.PUMPCOMM, "Raw Data is empty.");
|
|
||||||
return entries; // an empty list
|
|
||||||
}
|
|
||||||
int r, st;
|
|
||||||
|
|
||||||
for (int i = 0; i < mRawData.length - 2; i += 3) {
|
|
||||||
|
|
||||||
if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0x3f))
|
|
||||||
break;
|
|
||||||
|
|
||||||
r = MedtronicUtil.makeUnsignedShort(mRawData[i + 1], mRawData[i]); // readUnsignedByte(mRawData[i]);
|
|
||||||
st = readUnsignedByte(mRawData[i + 2]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
entries.add(new BasalProfileEntry(aapsLogger, r, st));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, "Error decoding basal profile from bytes: " + ByteUtil.shortHexString(mRawData));
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used to prepare new profile
|
|
||||||
*
|
|
||||||
* @param entry
|
|
||||||
*/
|
|
||||||
public void addEntry(BasalProfileEntry entry) {
|
|
||||||
if (listEntries == null)
|
|
||||||
listEntries = new ArrayList<>();
|
|
||||||
|
|
||||||
listEntries.add(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void generateRawDataFromEntries() {
|
|
||||||
|
|
||||||
List<Byte> outData = new ArrayList<>();
|
|
||||||
|
|
||||||
for (BasalProfileEntry profileEntry : listEntries) {
|
|
||||||
|
|
||||||
byte[] strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true);
|
|
||||||
|
|
||||||
outData.add(profileEntry.rate_raw[0]);
|
|
||||||
outData.add(profileEntry.rate_raw[1]);
|
|
||||||
outData.add(profileEntry.startTime_raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setRawData(MedtronicUtil.createByteArray(outData));
|
|
||||||
|
|
||||||
// return this.mRawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Double[] getProfilesByHour(PumpType pumpType) {
|
|
||||||
|
|
||||||
List<BasalProfileEntry> entries = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
entries = getEntries();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, "=============================================================================");
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, " Error generating entries. Ex.: " + ex, ex);
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, " rawBasalValues: " + ByteUtil.shortHexString(this.getRawData()));
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM, "=============================================================================");
|
|
||||||
|
|
||||||
//FabricUtil.createEvent("MedtronicBasalProfileGetByHourError", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries == null || entries.size() == 0) {
|
|
||||||
Double[] basalByHour = new Double[24];
|
|
||||||
|
|
||||||
for (int i = 0; i < 24; i++) {
|
|
||||||
basalByHour[i] = 0.0d;
|
|
||||||
}
|
|
||||||
|
|
||||||
return basalByHour;
|
|
||||||
}
|
|
||||||
|
|
||||||
Double[] basalByHour = new Double[24];
|
|
||||||
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
|
||||||
BasalProfileEntry current = entries.get(i);
|
|
||||||
|
|
||||||
int currentTime = (current.startTime_raw % 2 == 0) ? current.startTime_raw : current.startTime_raw - 1;
|
|
||||||
|
|
||||||
currentTime = (currentTime * 30) / 60;
|
|
||||||
|
|
||||||
int lastHour;
|
|
||||||
if ((i + 1) == entries.size()) {
|
|
||||||
lastHour = 24;
|
|
||||||
} else {
|
|
||||||
BasalProfileEntry basalProfileEntry = entries.get(i + 1);
|
|
||||||
|
|
||||||
int rawTime = (basalProfileEntry.startTime_raw % 2 == 0) ? basalProfileEntry.startTime_raw
|
|
||||||
: basalProfileEntry.startTime_raw - 1;
|
|
||||||
|
|
||||||
lastHour = (rawTime * 30) / 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
// System.out.println("Current time: " + currentTime + " Next Time: " + lastHour);
|
|
||||||
|
|
||||||
for (int j = currentTime; j < lastHour; j++) {
|
|
||||||
if (pumpType == null)
|
|
||||||
basalByHour[j] = current.rate;
|
|
||||||
else
|
|
||||||
basalByHour[j] = pumpType.determineCorrectBasalSize(current.rate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return basalByHour;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static String getProfilesByHourToString(Double[] data) {
|
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
for (Double value : data) {
|
|
||||||
stringBuilder.append(String.format("%.3f", value));
|
|
||||||
stringBuilder.append(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.toString();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] getRawData() {
|
|
||||||
return this.mRawData;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@NonNull public String toString() {
|
|
||||||
return basalProfileToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean verify(PumpType pumpType) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
getEntries();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Double[] profilesByHour = getProfilesByHour(pumpType);
|
|
||||||
|
|
||||||
for (Double aDouble : profilesByHour) {
|
|
||||||
if (aDouble > 35.0d)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.data.dto
|
||||||
|
|
||||||
|
import com.google.gson.annotations.Expose
|
||||||
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
||||||
|
import org.joda.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/1/15.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* There are three basal profiles stored on the pump. (722 only?) They are all parsed the same, the user just has 3 to
|
||||||
|
* choose from: Standard, A, and B
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The byte array is 48 times three byte entries long, plus a zero? If the profile is completely empty, it should have
|
||||||
|
* one entry: [0,0,0x3F]. The first entry of [0,0,0] marks the end of the used entries.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Each entry is assumed to span from the specified start time to the start time of the next entry, or to midnight if
|
||||||
|
* there are no more entries.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Individual entries are of the form [r,z,m] where r is the rate (in 0.025 U increments) z is zero (?) m is the start
|
||||||
|
* time-of-day for the basal rate period (in 30 minute increments)
|
||||||
|
*/
|
||||||
|
class BasalProfile {
|
||||||
|
|
||||||
|
private val aapsLogger: AAPSLogger
|
||||||
|
|
||||||
|
@Expose var rawData : ByteArray? = null // store as byte array to make transport (via parcel) easier
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var listEntries: MutableList<BasalProfileEntry>? = null
|
||||||
|
|
||||||
|
constructor(aapsLogger: AAPSLogger) {
|
||||||
|
this.aapsLogger = aapsLogger
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(aapsLogger: AAPSLogger, data: ByteArray) {
|
||||||
|
this.aapsLogger = aapsLogger
|
||||||
|
setRawData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
rawData = ByteArray(MAX_RAW_DATA_SIZE)
|
||||||
|
rawData!![0] = 0
|
||||||
|
rawData!![1] = 0
|
||||||
|
rawData!![2] = 0x3f
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setRawData(data: ByteArray): Boolean {
|
||||||
|
var data: ByteArray? = data
|
||||||
|
if (data == null) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have just one entry through all day it looks like just length 1
|
||||||
|
if (data.size == 1) {
|
||||||
|
data = MedtronicUtil.createByteArray(data[0], 0.toByte(), 0.toByte())
|
||||||
|
}
|
||||||
|
if (data!!.size == MAX_RAW_DATA_SIZE) {
|
||||||
|
rawData = data
|
||||||
|
} else {
|
||||||
|
val len = Math.min(MAX_RAW_DATA_SIZE, data.size)
|
||||||
|
rawData = ByteArray(MAX_RAW_DATA_SIZE)
|
||||||
|
System.arraycopy(data, 0, rawData, 0, len)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRawDataFromHistory(data: ByteArray?): Boolean {
|
||||||
|
if (data == null) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
rawData = ByteArray(MAX_RAW_DATA_SIZE)
|
||||||
|
val item = 0
|
||||||
|
var i = 0
|
||||||
|
while (i < data.size - 2) {
|
||||||
|
if (data[i] == 0.toByte() && data[i + 1] == 0.toByte() && data[i + 2] == 0.toByte()) {
|
||||||
|
rawData!![i] = 0
|
||||||
|
rawData!![i + 1] = 0
|
||||||
|
rawData!![i + 2] = 0
|
||||||
|
}
|
||||||
|
rawData!![i] = data[i + 1]
|
||||||
|
rawData!![i + 1] = data[i + 2]
|
||||||
|
rawData!![i + 2] = data[i]
|
||||||
|
i += 3
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dumpBasalProfile() {
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, "Basal Profile entries:")
|
||||||
|
val entries = entries
|
||||||
|
for (i in entries.indices) {
|
||||||
|
val entry = entries[i]
|
||||||
|
val startString = entry.startTime!!.toString("HH:mm")
|
||||||
|
// this doesn't work
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Entry %d, rate=%.3f (%s), start=%s (0x%02X)", i + 1, entry.rate,
|
||||||
|
ByteUtil.getHex(entry.rate_raw), startString, entry.startTime_raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val basalProfileAsString: String
|
||||||
|
get() {
|
||||||
|
val sb = StringBuffer("Basal Profile entries:\n")
|
||||||
|
val entries = entries
|
||||||
|
for (i in entries.indices) {
|
||||||
|
val entry = entries[i]
|
||||||
|
val startString = entry.startTime!!.toString("HH:mm")
|
||||||
|
sb.append(String.format(Locale.ENGLISH, "Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString))
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun basalProfileToStringError(): String {
|
||||||
|
return "Basal Profile [rawData=" + ByteUtil.shortHexString(rawData) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun basalProfileToString(): String {
|
||||||
|
val sb = StringBuffer("Basal Profile [")
|
||||||
|
val entries = entries
|
||||||
|
for (i in entries.indices) {
|
||||||
|
val entry = entries[i]
|
||||||
|
val startString = entry.startTime!!.toString("HH:mm")
|
||||||
|
sb.append(String.format(Locale.ENGLISH, "%s=%.3f, ", startString, entry.rate))
|
||||||
|
}
|
||||||
|
sb.append("]")
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this function must be expanded to include changes in which profile is in use.
|
||||||
|
// and changes to the profiles themselves.
|
||||||
|
fun getEntryForTime(`when`: Instant): BasalProfileEntry {
|
||||||
|
var rval = BasalProfileEntry()
|
||||||
|
val entries = entries
|
||||||
|
if (entries.size == 0) {
|
||||||
|
aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): table is empty",
|
||||||
|
`when`.toDateTime().toLocalTime().toString("HH:mm")))
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
// Log.w(TAG,"Assuming first entry");
|
||||||
|
rval = entries[0]
|
||||||
|
if (entries.size == 1) {
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, "getEntryForTime: Only one entry in profile")
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
val localMillis = `when`.toDateTime().toLocalTime().millisOfDay
|
||||||
|
var done = false
|
||||||
|
var i = 1
|
||||||
|
while (!done) {
|
||||||
|
val entry = entries[i]
|
||||||
|
if (DEBUG_BASALPROFILE) {
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Comparing 'now'=%s to entry 'start time'=%s", `when`.toDateTime().toLocalTime()
|
||||||
|
.toString("HH:mm"), entry.startTime!!.toString("HH:mm")))
|
||||||
|
}
|
||||||
|
if (localMillis >= entry.startTime!!.millisOfDay) {
|
||||||
|
rval = entry
|
||||||
|
if (DEBUG_BASALPROFILE) aapsLogger.debug(LTag.PUMPCOMM, "Accepted Entry")
|
||||||
|
} else {
|
||||||
|
// entry at i has later start time, keep older entry
|
||||||
|
if (DEBUG_BASALPROFILE) aapsLogger.debug(LTag.PUMPCOMM, "Rejected Entry")
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if (i >= entries.size) {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DEBUG_BASALPROFILE) {
|
||||||
|
aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "getEntryForTime(%s): Returning entry: rate=%.3f (%s), start=%s (%d)", `when`
|
||||||
|
.toDateTime().toLocalTime().toString("HH:mm"), rval.rate, ByteUtil.getHex(rval.rate_raw),
|
||||||
|
rval.startTime!!.toString("HH:mm"), rval.startTime_raw))
|
||||||
|
}
|
||||||
|
return rval
|
||||||
|
}// readUnsignedByte(mRawData[i]);
|
||||||
|
|
||||||
|
// an empty list
|
||||||
|
val entries: List<BasalProfileEntry>
|
||||||
|
get() {
|
||||||
|
val entries: MutableList<BasalProfileEntry> = ArrayList()
|
||||||
|
if (rawData == null || rawData!![2] == 0x3f.toByte()) {
|
||||||
|
aapsLogger.warn(LTag.PUMPCOMM, "Raw Data is empty.")
|
||||||
|
return entries // an empty list
|
||||||
|
}
|
||||||
|
var r: Int
|
||||||
|
var st: Int
|
||||||
|
var i = 0
|
||||||
|
while (i < rawData!!.size - 2) {
|
||||||
|
if (rawData!![i] == 0.toByte() && rawData!![i + 1] == 0.toByte() && rawData!![i + 2] == 0.toByte()) break
|
||||||
|
if (rawData!![i] == 0.toByte() && rawData!![i + 1] == 0.toByte() && rawData!![i + 2] == 0x3f.toByte()) break
|
||||||
|
r = MedtronicUtil.makeUnsignedShort(rawData!![i + 1].toInt(), rawData!![i].toInt()) // readUnsignedByte(mRawData[i]);
|
||||||
|
st = readUnsignedByte(rawData!![i + 2])
|
||||||
|
try {
|
||||||
|
entries.add(BasalProfileEntry(aapsLogger, r, st))
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, "Error decoding basal profile from bytes: " + ByteUtil.shortHexString(rawData))
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
i += 3
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to prepare new profile
|
||||||
|
*
|
||||||
|
* @param entry
|
||||||
|
*/
|
||||||
|
fun addEntry(entry: BasalProfileEntry) {
|
||||||
|
if (listEntries == null) listEntries = ArrayList()
|
||||||
|
listEntries!!.add(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateRawDataFromEntries() {
|
||||||
|
val outData: MutableList<Byte> = ArrayList()
|
||||||
|
for (profileEntry in listEntries!!) {
|
||||||
|
val strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true)
|
||||||
|
outData.add(profileEntry.rate_raw[0])
|
||||||
|
outData.add(profileEntry.rate_raw[1])
|
||||||
|
outData.add(profileEntry.startTime_raw)
|
||||||
|
}
|
||||||
|
setRawData(MedtronicUtil.createByteArray(outData))
|
||||||
|
|
||||||
|
// return this.mRawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProfilesByHour(pumpType: PumpType?): Array<Double?> {
|
||||||
|
var entries: List<BasalProfileEntry>? = null
|
||||||
|
try {
|
||||||
|
entries = entries
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, "=============================================================================")
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, " Error generating entries. Ex.: $ex", ex)
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, " rawBasalValues: " + ByteUtil.shortHexString(rawData))
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, "=============================================================================")
|
||||||
|
|
||||||
|
//FabricUtil.createEvent("MedtronicBasalProfileGetByHourError", null);
|
||||||
|
}
|
||||||
|
if (entries == null || entries.size == 0) {
|
||||||
|
val basalByHour = arrayOfNulls<Double>(24)
|
||||||
|
for (i in 0..23) {
|
||||||
|
basalByHour[i] = 0.0
|
||||||
|
}
|
||||||
|
return basalByHour
|
||||||
|
}
|
||||||
|
val basalByHour = arrayOfNulls<Double>(24)
|
||||||
|
for (i in entries.indices) {
|
||||||
|
val current = entries[i]
|
||||||
|
var currentTime = if (current.startTime_raw % 2 == 0) current.startTime_raw.toInt() else current.startTime_raw - 1
|
||||||
|
currentTime = currentTime * 30 / 60
|
||||||
|
var lastHour: Int
|
||||||
|
lastHour = if (i + 1 == entries.size) {
|
||||||
|
24
|
||||||
|
} else {
|
||||||
|
val basalProfileEntry = entries[i + 1]
|
||||||
|
val rawTime = if (basalProfileEntry.startTime_raw % 2 == 0) basalProfileEntry.startTime_raw.toInt() else basalProfileEntry.startTime_raw - 1
|
||||||
|
rawTime * 30 / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
// System.out.println("Current time: " + currentTime + " Next Time: " + lastHour);
|
||||||
|
for (j in currentTime until lastHour) {
|
||||||
|
if (pumpType == null) basalByHour[j] = current.rate else basalByHour[j] = pumpType.determineCorrectBasalSize(current.rate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return basalByHour
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return basalProfileToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verify(pumpType: PumpType?): Boolean {
|
||||||
|
try {
|
||||||
|
entries
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val profilesByHour = getProfilesByHour(pumpType)
|
||||||
|
for (aDouble in profilesByHour) {
|
||||||
|
if (aDouble!! > 35.0) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MAX_RAW_DATA_SIZE = 48 * 3 + 1
|
||||||
|
private const val DEBUG_BASALPROFILE = false
|
||||||
|
|
||||||
|
// this asUINT8 should be combined with Record.asUINT8, and placed in a new util class.
|
||||||
|
private fun readUnsignedByte(b: Byte): Int {
|
||||||
|
return if (b < 0) b + 256 else b.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getProfilesByHourToString(data: Array<Double?>): String {
|
||||||
|
val stringBuilder = StringBuilder()
|
||||||
|
for (value in data) {
|
||||||
|
stringBuilder.append(String.format("%.3f", value))
|
||||||
|
stringBuilder.append(" ")
|
||||||
|
}
|
||||||
|
return stringBuilder.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,81 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.medtronic.data.dto;
|
|
||||||
|
|
||||||
import org.joda.time.LocalTime;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
|
||||||
import info.nightscout.androidaps.logging.LTag;
|
|
||||||
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by geoff on 6/1/15.
|
|
||||||
* This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile
|
|
||||||
* - fixed rate is not one bit but two
|
|
||||||
*/
|
|
||||||
public class BasalProfileEntry {
|
|
||||||
|
|
||||||
byte[] rate_raw;
|
|
||||||
public double rate;
|
|
||||||
byte startTime_raw;
|
|
||||||
public LocalTime startTime; // Just a "time of day"
|
|
||||||
|
|
||||||
public BasalProfileEntry() {
|
|
||||||
rate = -9.999E6;
|
|
||||||
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(0xFF, true);
|
|
||||||
startTime = new LocalTime(0);
|
|
||||||
startTime_raw = (byte) 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasalProfileEntry(double rate, int hour, int minutes) {
|
|
||||||
byte[] data = MedtronicUtil.getBasalStrokes(rate, true);
|
|
||||||
|
|
||||||
rate_raw = new byte[2];
|
|
||||||
rate_raw[0] = data[1];
|
|
||||||
rate_raw[1] = data[0];
|
|
||||||
|
|
||||||
int interval = hour * 2;
|
|
||||||
|
|
||||||
if (minutes == 30) {
|
|
||||||
interval++;
|
|
||||||
}
|
|
||||||
|
|
||||||
startTime_raw = (byte) interval;
|
|
||||||
startTime = new LocalTime(hour, minutes == 30 ? 30 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BasalProfileEntry(AAPSLogger aapsLogger, int rateStrokes, int startTimeInterval) {
|
|
||||||
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
|
||||||
// startTimeByte is time-of-day, in 30 minute increments
|
|
||||||
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateStrokes, true);
|
|
||||||
rate = rateStrokes * 0.025;
|
|
||||||
startTime_raw = (byte) startTimeInterval;
|
|
||||||
|
|
||||||
try {
|
|
||||||
startTime = new LocalTime(startTimeInterval / 2, (startTimeInterval % 2) * 30);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
aapsLogger.error(LTag.PUMPCOMM,
|
|
||||||
String.format(Locale.ENGLISH, "Error creating BasalProfileEntry: startTimeInterval=%d, startTime_raw=%d, hours=%d, rateStrokes=%d",
|
|
||||||
startTimeInterval, startTime_raw, startTimeInterval / 2, rateStrokes));
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
BasalProfileEntry(byte rateByte, int startTimeByte) {
|
|
||||||
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
|
||||||
// startTimeByte is time-of-day, in 30 minute increments
|
|
||||||
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateByte, true);
|
|
||||||
rate = rateByte * 0.025;
|
|
||||||
startTime_raw = (byte) startTimeByte;
|
|
||||||
startTime = new LocalTime(startTimeByte / 2, (startTimeByte % 2) * 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStartTime(LocalTime localTime) {
|
|
||||||
this.startTime = localTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRate(double rate) {
|
|
||||||
this.rate = rate;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.medtronic.data.dto
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil
|
||||||
|
import org.joda.time.LocalTime
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/1/15.
|
||||||
|
* This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile
|
||||||
|
* - fixed rate is not one bit but two
|
||||||
|
*/
|
||||||
|
class BasalProfileEntry {
|
||||||
|
|
||||||
|
var rate_raw: ByteArray
|
||||||
|
var rate = 0.0
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var startTime_raw: Byte
|
||||||
|
var startTime : LocalTime? = null // Just a "time of day"
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
rate = -9.999E6
|
||||||
|
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(0xFF, true)
|
||||||
|
startTime = LocalTime(0)
|
||||||
|
startTime_raw = 0xFF.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(rate: Double, hour: Int, minutes: Int) {
|
||||||
|
val data = MedtronicUtil.getBasalStrokes(rate, true)
|
||||||
|
rate_raw = ByteArray(2)
|
||||||
|
rate_raw[0] = data[1]
|
||||||
|
rate_raw[1] = data[0]
|
||||||
|
var interval = hour * 2
|
||||||
|
if (minutes == 30) {
|
||||||
|
interval++
|
||||||
|
}
|
||||||
|
startTime_raw = interval.toByte()
|
||||||
|
startTime = LocalTime(hour, if (minutes == 30) 30 else 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(aapsLogger: AAPSLogger, rateStrokes: Int, startTimeInterval: Int) {
|
||||||
|
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
||||||
|
// startTimeByte is time-of-day, in 30 minute increments
|
||||||
|
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateStrokes, true)
|
||||||
|
rate = rateStrokes * 0.025
|
||||||
|
startTime_raw = startTimeInterval.toByte()
|
||||||
|
startTime = try {
|
||||||
|
LocalTime(startTimeInterval / 2, startTimeInterval % 2 * 30)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
aapsLogger.error(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error creating BasalProfileEntry: startTimeInterval=%d, startTime_raw=%d, hours=%d, rateStrokes=%d",
|
||||||
|
startTimeInterval, startTime_raw, startTimeInterval / 2, rateStrokes))
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(rateByte: Byte, startTimeByte: Int) {
|
||||||
|
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
||||||
|
// startTimeByte is time-of-day, in 30 minute increments
|
||||||
|
rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateByte.toInt(), true)
|
||||||
|
rate = rateByte * 0.025
|
||||||
|
startTime_raw = startTimeByte.toByte()
|
||||||
|
startTime = LocalTime(startTimeByte / 2, startTimeByte % 2 * 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -215,32 +215,6 @@ public enum MedtronicCommandType implements Serializable // , MinimedCommandType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
|
||||||
// MinimedCommandParameterType parameterType) {
|
|
||||||
// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, 0);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
|
||||||
// MinimedCommandParameterType parameterType, int expectedLength) {
|
|
||||||
// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, expectedLength);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
|
||||||
// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) {
|
|
||||||
// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType, 0);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
|
||||||
// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType,
|
|
||||||
// int expectedLength) {
|
|
||||||
// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType,
|
|
||||||
// expectedLength);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
||||||
MinimedCommandParameterType parameterType, byte[] cmd_params) {
|
MinimedCommandParameterType parameterType, byte[] cmd_params) {
|
||||||
this(code, description, devices, parameterType, 0, 1, 0, 0, 11, 0);
|
this(code, description, devices, parameterType, 0, 1, 0, 0, 11, 0);
|
||||||
|
@ -256,14 +230,6 @@ public enum MedtronicCommandType implements Serializable // , MinimedCommandType
|
||||||
this(code, description, devices, parameterType, 64, 1, 0, null);
|
this(code, description, devices, parameterType, 64, 1, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NEW
|
|
||||||
MedtronicCommandType(int code, String description, MedtronicDeviceType devices,
|
|
||||||
MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) {
|
|
||||||
this(code, description, devices, parameterType, recordLength, maxRecords, 0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NEW
|
// NEW
|
||||||
MedtronicCommandType(int code, String description, MedtronicDeviceType devices, //
|
MedtronicCommandType(int code, String description, MedtronicDeviceType devices, //
|
||||||
MinimedCommandParameterType parameterType, int expectedLength) {
|
MinimedCommandParameterType parameterType, int expectedLength) {
|
||||||
|
@ -390,22 +356,6 @@ public enum MedtronicCommandType implements Serializable // , MinimedCommandType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean canReturnData() {
|
|
||||||
System.out.println("CanReturnData: ]id=" + this.name() + "max=" + this.maxRecords + "recLen=" + recordLength);
|
|
||||||
return (this.maxRecords * this.recordLength) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public int getRecordLength() {
|
|
||||||
return recordLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public int getMaxRecords() {
|
|
||||||
return maxRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte getCommandCode() {
|
public byte getCommandCode() {
|
||||||
return commandCode;
|
return commandCode;
|
||||||
}
|
}
|
||||||
|
@ -420,16 +370,6 @@ public enum MedtronicCommandType implements Serializable // , MinimedCommandType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public byte[] getCommandParameters() {
|
|
||||||
return commandParameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean hasCommandParameters() {
|
|
||||||
return (getCommandParametersCount() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name();
|
return name();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue