diff --git a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java index d23d1f81b1677bc1e37dd66db8c38f31b9f6038f..1eab92da74ecea4548ab9afdf0aed8f19839a223 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCNLReader.java @@ -29,6 +29,8 @@ import info.nightscout.android.medtronic.message.EncryptionException; import info.nightscout.android.medtronic.message.EndEHSMMessage; import info.nightscout.android.medtronic.message.MedtronicMessage; import info.nightscout.android.medtronic.message.MessageUtils; +import info.nightscout.android.medtronic.message.PumpBasalPatternRequestMessage; +import info.nightscout.android.medtronic.message.PumpBasalPatternResponseMessage; import info.nightscout.android.medtronic.message.PumpStatusRequestMessage; import info.nightscout.android.medtronic.message.PumpStatusResponseMessage; import info.nightscout.android.medtronic.message.PumpTimeRequestMessage; @@ -365,10 +367,55 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { statusBuffer.order(ByteOrder.BIG_ENDIAN); statusBuffer.put(response.encode(), 0x39, 96); - // Read the data into the record + // Status Flags + pumpRecord.setSuspended((statusBuffer.get(0x03) & 0x01) != 0x00); + pumpRecord.setBolusing((statusBuffer.get(0x03) & 0x02) != 0x00); + pumpRecord.setDeliveringInsulin((statusBuffer.get(0x03) & 0x10) != 0x00); + pumpRecord.setTempBasalActive((statusBuffer.get(0x03) & 0x20) != 0x00); + pumpRecord.setCgmActive((statusBuffer.get(0x03) & 0x40) != 0x00); + + // Active basal pattern + pumpRecord.setActiveBasalPattern(statusBuffer.get(0x1a)); + + // Normal basal rate + long rawNormalBasal = statusBuffer.getInt(0x1b); + pumpRecord.setBasalRate(new BigDecimal(rawNormalBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue()); + + // Temp basal rate + // TODO - need to figure this one out + //long rawTempBasal = statusBuffer.getShort(0x21) & 0x0000ffff; + //pumpRecord.setTempBasalRate(new BigDecimal(rawTempBasal / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue()); + + // Temp basal percentage + pumpRecord.setTempBasalPercentage(statusBuffer.get(0x23)); + + // Temp basal minutes remaining + pumpRecord.setTempBasalMinutesRemaining((short) (statusBuffer.getShort(0x24) & 0x0000ffff)); + + // Units of insulin delivered as basal today + // TODO - is this basal? Do we have a total Units delivered elsewhere? + pumpRecord.setBasalUnitsDeliveredToday(statusBuffer.getInt(0x26)); + + // Pump battery percentage + pumpRecord.setBatteryPercentage((statusBuffer.get(0x2a))); + + // Reservoir amount + long rawReservoirAmount = statusBuffer.getInt(0x2b); + pumpRecord.setReservoirAmount(new BigDecimal(rawReservoirAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue()); + + // Amount of insulin left in pump (in minutes) + byte insulinHours = statusBuffer.get(0x2f); + byte insulinMinutes = statusBuffer.get(0x30); + pumpRecord.setMinutesOfInsulinRemaining((short) ((insulinHours * 60) + insulinMinutes)); + + // Active insulin long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff; pumpRecord.setActiveInsulin(new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue()); + + // CGM SGV pumpRecord.setSgv(statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading + + // SGV Date long rtc; long offset; if ((pumpRecord.getSgv() & 0x200) == 0x200) { @@ -382,16 +429,49 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { offset = statusBuffer.getInt(0x3b); pumpRecord.setCgmTrend(fromMessageByte(statusBuffer.get(0x40))); } + // TODO - this should go in the sgvDate, and eventDate should be the time of this poll. pumpRecord.setEventDate(new Date(MessageUtils.decodeDateTime(rtc, offset).getTime() - pumpTimeOffset)); + + // Predictive low suspend + // TODO - there is more status info in this byte other than just a boolean yes/no + pumpRecord.setLowSuspendActive(statusBuffer.get(0x3f) != 0); + + // Recent Bolus Wizard BGL pumpRecord.setRecentBolusWizard(statusBuffer.get(0x48) != 0); - pumpRecord.setBolusWizardBGL(statusBuffer.getShort(0x49)); // In mg/DL - long rawReservoirAmount = statusBuffer.getInt(0x2b); - pumpRecord.setReservoirAmount(new BigDecimal(rawReservoirAmount / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue()); - pumpRecord.setBatteryPercentage((statusBuffer.get(0x2a))); + pumpRecord.setBolusWizardBGL(statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL Log.d(TAG, "Finished getPumpStatus"); } + public void getBasalPatterns() throws EncryptionException, IOException, ChecksumException, TimeoutException { + Log.d(TAG, "Begin getBasalPatterns"); + // FIXME - throw if not in EHSM mode (add a state machine) + + new PumpBasalPatternRequestMessage(mPumpSession).send(this); + // Read the 0x81 + readMessage(); + + // Read the 0x80 + ContourNextLinkMessage response = PumpBasalPatternResponseMessage.fromBytes(mPumpSession, readMessage()); + + // TODO - determine message validity + /* + if (response.encode().length < (61 + 8)) { + // Invalid message. + // TODO - deal with this more elegantly + Log.e(TAG, "Invalid message received for getBasalPatterns"); + return; + } + */ + + // FIXME - this needs to go into PumpBasalPatternResponseMessage + ByteBuffer basalRatesBuffer = ByteBuffer.allocate(96); + basalRatesBuffer.order(ByteOrder.BIG_ENDIAN); + basalRatesBuffer.put(response.encode(), 0x39, 96); + + Log.d(TAG, "Finished getBasalPatterns"); + } + public void endEHSMSession() throws EncryptionException, IOException, TimeoutException { Log.d(TAG, "Begin endEHSMSession"); new EndEHSMMessage(mPumpSession).send(this); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java index aa8fe1c13346722d2ea1e18a7350e013c5a5365f..496a85bacbf51966917c3c7463feed8a2d99ab43 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java @@ -13,13 +13,31 @@ public class PumpStatusEvent extends RealmObject { private Date eventDate; // The actual time of the event (assume the capture device eventDate/time is accurate) private Date pumpDate; // The eventDate/time on the pump at the time of the event private String deviceName; - private int sgv; - private String cgmTrend; - private float activeInsulin; + + // Data from the Medtronic Pump Status message + private boolean suspended; + private boolean bolusing; + private boolean deliveringInsulin; + private boolean tempBasalActive; + private boolean cgmActive; + private byte activeBasalPattern; + private float basalRate; + private float tempBasalRate; + private byte tempBasalPercentage; + private short tempBasalMinutesRemaining; + private float basalUnitsDeliveredToday; private short batteryPercentage; private float reservoirAmount; + private short minutesOfInsulinRemaining; // 25h == "more than 1 day" + private float activeInsulin; + private int sgv; + private Date sgvDate; + private boolean lowSuspendActive; + private String cgmTrend; + private boolean recentBolusWizard; // Whether a bolus wizard has been run recently private int bolusWizardBGL; // in mg/dL. 0 means no recent bolus wizard reading. + @Index private boolean uploaded = false; @@ -63,6 +81,10 @@ public class PumpStatusEvent extends RealmObject { this.cgmTrend = cgmTrend.name(); } + public void setCgmTrend(String cgmTrend) { + this.cgmTrend = cgmTrend; + } + public float getActiveInsulin() { return activeInsulin; } @@ -91,10 +113,6 @@ public class PumpStatusEvent extends RealmObject { return recentBolusWizard; } - public void setRecentBolusWizard(boolean recentBolusWizard) { - this.recentBolusWizard = recentBolusWizard; - } - public int getBolusWizardBGL() { return bolusWizardBGL; } @@ -111,6 +129,126 @@ public class PumpStatusEvent extends RealmObject { this.uploaded = uploaded; } + public boolean isSuspended() { + return suspended; + } + + public void setSuspended(boolean suspended) { + this.suspended = suspended; + } + + public boolean isBolusing() { + return bolusing; + } + + public void setBolusing(boolean bolusing) { + this.bolusing = bolusing; + } + + public boolean isDeliveringInsulin() { + return deliveringInsulin; + } + + public void setDeliveringInsulin(boolean deliveringInsulin) { + this.deliveringInsulin = deliveringInsulin; + } + + public boolean isTempBasalActive() { + return tempBasalActive; + } + + public void setTempBasalActive(boolean tempBasalActive) { + this.tempBasalActive = tempBasalActive; + } + + public boolean isCgmActive() { + return cgmActive; + } + + public void setCgmActive(boolean cgmActive) { + this.cgmActive = cgmActive; + } + + public byte getActiveBasalPattern() { + return activeBasalPattern; + } + + public void setActiveBasalPattern(byte activeBasalPattern) { + this.activeBasalPattern = activeBasalPattern; + } + + public float getBasalRate() { + return basalRate; + } + + public void setBasalRate(float basalRate) { + this.basalRate = basalRate; + } + + public float getTempBasalRate() { + return tempBasalRate; + } + + public void setTempBasalRate(float tempBasalRate) { + this.tempBasalRate = tempBasalRate; + } + + public byte getTempBasalPercentage() { + return tempBasalPercentage; + } + + public void setTempBasalPercentage(byte tempBasalPercentage) { + this.tempBasalPercentage = tempBasalPercentage; + } + + public short getTempBasalMinutesRemaining() { + return tempBasalMinutesRemaining; + } + + public void setTempBasalMinutesRemaining(short tempBasalMinutesRemaining) { + this.tempBasalMinutesRemaining = tempBasalMinutesRemaining; + } + + public float getBasalUnitsDeliveredToday() { + return basalUnitsDeliveredToday; + } + + public void setBasalUnitsDeliveredToday(float basalUnitsDeliveredToday) { + this.basalUnitsDeliveredToday = basalUnitsDeliveredToday; + } + + public short getMinutesOfInsulinRemaining() { + return minutesOfInsulinRemaining; + } + + public void setMinutesOfInsulinRemaining(short minutesOfInsulinRemaining) { + this.minutesOfInsulinRemaining = minutesOfInsulinRemaining; + } + + public Date getSgvDate() { + return sgvDate; + } + + public void setSgvDate(Date sgvDate) { + this.sgvDate = sgvDate; + } + + public boolean isLowSuspendActive() { + return lowSuspendActive; + } + + public void setLowSuspendActive(boolean lowSuspendActive) { + this.lowSuspendActive = lowSuspendActive; + } + + public boolean isRecentBolusWizard() { + return recentBolusWizard; + } + + public void setRecentBolusWizard(boolean recentBolusWizard) { + this.recentBolusWizard = recentBolusWizard; + } + public enum CGM_TREND { NONE, DOUBLE_UP, diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java index d58721a16a1546ed422ebd7d7363d17760d93b54..ee134cb0fab61eadf800894443fc6afe092cf6ab 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadIntentService.java @@ -169,7 +169,7 @@ public class NightscoutUploadIntentService extends IntentService { uploadToNightscout(new URL(baseURL + "/devicestatus"), secret, devicestatusBody); // Yay! We uploaded. Tell Realm - // TODO - check the upload succeeded! + // FIXME - check the upload succeeded! mRealm.beginTransaction(); for (PumpStatusEvent updateRecord : records) {