- kotlin changes
- disabled problem in Fragment for now
This commit is contained in:
parent
0d1ff8dd41
commit
bb657a2498
|
@ -311,13 +311,21 @@ class MedtronicFragment : DaggerFragment() {
|
|||
binding.lastBolus.text = ""
|
||||
}
|
||||
|
||||
val pumpState = pumpSync.expectedPumpState()
|
||||
// base basal rate
|
||||
binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") "
|
||||
+ resourceHelper.gs(R.string.pump_basebasalrate, medtronicPumpPlugin.baseBasalRate))
|
||||
if (true) {
|
||||
// base basal rate
|
||||
binding.baseBasalRate.text = ("(" + medtronicPumpStatus.activeProfileName + ") "
|
||||
+ 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
|
||||
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()) {
|
||||
|
||||
if (profileEntry.rate > medtronicPumpStatus.maxBasal) {
|
||||
stringBuilder.append(profileEntry.startTime.toString("HH:mm"));
|
||||
if (profileEntry.getRate() > medtronicPumpStatus.maxBasal) {
|
||||
stringBuilder.append(profileEntry.getStartTime().toString("HH:mm"));
|
||||
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 org.joda.time.LocalDateTime
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
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
|
||||
|
@ -264,6 +266,6 @@ class MedtronicCGMSHistoryDecoder : MedtronicHistoryDecoder<CGMSHistoryEntry>()
|
|||
}
|
||||
|
||||
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,
|
||||
MinimedCommandParameterType parameterType, byte[] cmd_params) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
MedtronicCommandType(int code, String description, MedtronicDeviceType devices, //
|
||||
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() {
|
||||
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() {
|
||||
return name();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue