From 7d3fd839f542ed6ef6fa248cea429014e35286fe Mon Sep 17 00:00:00 2001 From: Volker Richert <v.richert@addmore.de> Date: Fri, 16 Dec 2016 12:39:18 +0100 Subject: [PATCH] refactoring finished (untested) --- .../info/nightscout/android/USB/USBPower.java | 4 +- .../android/medtronic/MedtronicCnlReader.java | 310 ++---------------- .../medtronic/message/BeginEHSMMessage.java | 9 +- ...va => ChannelNegotiateRequestMessage.java} | 23 +- .../ChannelNegotiateResponseMessage.java | 31 ++ .../ContourNextLinkBinaryRequestMessage.java | 1 - .../ContourNextLinkBinaryResponseMessage.java | 4 - .../ContourNextLinkCommandMessage.java | 1 - .../ContourNextLinkCommandResponse.java | 2 - .../message/ContourNextLinkMessage.java | 4 - .../ContourNextLinkMessageHandler.java | 11 - .../ContourNextLinkRequestMessage.java | 8 - .../ContourNextLinkResponseMessage.java | 8 - .../DeviceInfoResponseCommandMessage.java | 4 - .../medtronic/message/EndEHSMMessage.java | 9 +- .../message/MedtronicResponseMessage.java | 4 +- .../OpenConnectionResponseMessage.java | 3 - .../PumpBasalPatternRequestMessage.java | 12 +- .../PumpBasalPatternResponseMessage.java | 23 +- .../message/PumpStatusRequestMessage.java | 13 +- .../message/PumpStatusResponseMessage.java | 180 +++++++++- .../message/PumpTimeRequestMessage.java | 12 +- .../message/PumpTimeResponseMessage.java | 32 +- .../message/ReadInfoRequestMessage.java | 3 +- .../service/MedtronicCnlIntentService.java | 12 +- .../model/medtronicNg/PumpStatusEvent.java | 23 +- .../NightscoutUploadIntentService.java | 2 - 27 files changed, 375 insertions(+), 373 deletions(-) rename app/src/main/java/info/nightscout/android/medtronic/message/{ChannelNegotiateMessage.java => ChannelNegotiateRequestMessage.java} (50%) create mode 100644 app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java delete mode 100644 app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java diff --git a/app/src/main/java/info/nightscout/android/USB/USBPower.java b/app/src/main/java/info/nightscout/android/USB/USBPower.java index 92890b8..cf16a17 100644 --- a/app/src/main/java/info/nightscout/android/USB/USBPower.java +++ b/app/src/main/java/info/nightscout/android/USB/USBPower.java @@ -1,9 +1,9 @@ package info.nightscout.android.USB; -import java.io.DataOutputStream; - import android.util.Log; +import java.io.DataOutputStream; + public class USBPower { private static final String TAG = "USBPower"; 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 766a8a6..ca2629e 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MedtronicCnlReader.java @@ -4,11 +4,7 @@ import android.util.Log; import org.apache.commons.lang3.ArrayUtils; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; @@ -18,37 +14,33 @@ import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.message.BeginEHSMMessage; -import info.nightscout.android.medtronic.message.ChannelNegotiateMessage; +import info.nightscout.android.medtronic.message.ChannelNegotiateRequestMessage; +import info.nightscout.android.medtronic.message.ChannelNegotiateResponseMessage; import info.nightscout.android.medtronic.message.ChecksumException; -import info.nightscout.android.medtronic.message.ContourNextLinkBinaryMessage; +import info.nightscout.android.medtronic.message.CloseConnectionRequestMessage; import info.nightscout.android.medtronic.message.ContourNextLinkCommandMessage; -import info.nightscout.android.medtronic.message.ContourNextLinkMessage; -import info.nightscout.android.medtronic.message.ContourNextLinkMessageHandler; +import info.nightscout.android.medtronic.message.DeviceInfoRequestCommandMessage; import info.nightscout.android.medtronic.message.DeviceInfoResponseCommandMessage; 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.OpenConnectionRequestMessage; import info.nightscout.android.medtronic.message.PumpBasalPatternRequestMessage; import info.nightscout.android.medtronic.message.PumpBasalPatternResponseMessage; -import info.nightscout.android.medtronic.message.ReadInfoRequestMessage; import info.nightscout.android.medtronic.message.PumpStatusRequestMessage; import info.nightscout.android.medtronic.message.PumpStatusResponseMessage; import info.nightscout.android.medtronic.message.PumpTimeRequestMessage; import info.nightscout.android.medtronic.message.PumpTimeResponseMessage; +import info.nightscout.android.medtronic.message.ReadInfoRequestMessage; import info.nightscout.android.medtronic.message.ReadInfoResponseMessage; import info.nightscout.android.medtronic.message.RequestLinkKeyRequestMessage; import info.nightscout.android.medtronic.message.RequestLinkKeyResponseMessage; -import info.nightscout.android.medtronic.message.DeviceInfoRequestCommandMessage; import info.nightscout.android.medtronic.message.UnexpectedMessageException; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; -import info.nightscout.android.utils.HexDump; /** * Created by lgoedhart on 24/03/2016. */ -public class MedtronicCnlReader implements ContourNextLinkMessageHandler { - +public class MedtronicCnlReader { private static final String TAG = MedtronicCnlReader.class.getSimpleName(); private static final int USB_BLOCKSIZE = 64; @@ -59,34 +51,12 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { private UsbHidDriver mDevice; private MedtronicCnlSession mPumpSession = new MedtronicCnlSession(); - private String mStickSerial = null; public MedtronicCnlReader(UsbHidDriver device) { mDevice = device; } - private static PumpStatusEvent.CGM_TREND fromMessageByte(byte messageByte) { - switch (messageByte) { - case (byte) 0x60: - return PumpStatusEvent.CGM_TREND.FLAT; - case (byte) 0xc0: - return PumpStatusEvent.CGM_TREND.DOUBLE_UP; - case (byte) 0xa0: - return PumpStatusEvent.CGM_TREND.SINGLE_UP; - case (byte) 0x80: - return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_UP; - case (byte) 0x40: - return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_DOWN; - case (byte) 0x20: - return PumpStatusEvent.CGM_TREND.SINGLE_DOWN; - case (byte) 0x00: - return PumpStatusEvent.CGM_TREND.DOUBLE_DOWN; - default: - return PumpStatusEvent.CGM_TREND.NOT_COMPUTABLE; - } - } - public String getStickSerial() { return mStickSerial; } @@ -95,75 +65,6 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { return mPumpSession; } - public byte[] readMessage() throws IOException, TimeoutException { - ByteArrayOutputStream responseMessage = new ByteArrayOutputStream(); - - byte[] responseBuffer = new byte[USB_BLOCKSIZE]; - int bytesRead; - int messageSize = 0; - - do { - bytesRead = mDevice.read(responseBuffer, READ_TIMEOUT_MS); - - if (bytesRead == -1) { - throw new TimeoutException("Timeout waiting for response from pump"); - } else if (bytesRead > 0) { - // Validate the header - ByteBuffer header = ByteBuffer.allocate(3); - header.put(responseBuffer, 0, 3); - String headerString = new String(header.array()); - if (!headerString.equals(BAYER_USB_HEADER)) { - throw new IOException("Unexpected header received"); - } - messageSize = responseBuffer[3]; - responseMessage.write(responseBuffer, 4, messageSize); - } else { - Log.w(TAG, "readMessage: got a zero-sized response."); - } - } while (bytesRead > 0 && messageSize == 60); - - String responseString = HexDump.dumpHexString(responseMessage.toByteArray()); - Log.d(TAG, "READ: " + responseString); - - return responseMessage.toByteArray(); - } - - @Override - public void sendMessage(ContourNextLinkMessage message) throws IOException { - sendMessage(message.encode()); - if (message instanceof ContourNextLinkBinaryMessage) { - mPumpSession.incrBayerSequenceNumber(); - } - - if (message instanceof MedtronicMessage) { - mPumpSession.incrMedtronicSequenceNumber(); - } - } - - @Override - public ContourNextLinkMessage receiveMessage() { - return null; - } - - public void sendMessage(byte[] message) throws IOException { - - int pos = 0; - - while (message.length > pos) { - ByteBuffer outputBuffer = ByteBuffer.allocate(USB_BLOCKSIZE); - int sendLength = (pos + 60 > message.length) ? message.length - pos : 60; - outputBuffer.put(BAYER_USB_HEADER.getBytes()); - outputBuffer.put((byte) sendLength); - outputBuffer.put(message, pos, sendLength); - - mDevice.write(outputBuffer.array(), 200); - pos += sendLength; - - String outputString = HexDump.dumpHexString(outputBuffer.array()); - Log.d(TAG, "WRITE: " + outputString); - } - } - public void requestDeviceInfo() throws IOException, TimeoutException, UnexpectedMessageException, ChecksumException, EncryptionException { DeviceInfoResponseCommandMessage response = new DeviceInfoRequestCommandMessage(mPumpSession).send(mDevice); @@ -172,7 +73,6 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { mStickSerial = response.getSerial(); } - public void enterControlMode() throws IOException, TimeoutException, UnexpectedMessageException, ChecksumException, EncryptionException { boolean doRetry; @@ -185,7 +85,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { .send(mDevice).checkControlMessage(ContourNextLinkCommandMessage.ASCII.ACK); } catch (UnexpectedMessageException e2) { try { - new ContourNextLinkCommandMessage(ContourNextLinkCommandMessage.ASCII.EOT).send(this); + new ContourNextLinkCommandMessage(ContourNextLinkCommandMessage.ASCII.EOT).send(mDevice); } catch (IOException e) {} finally { doRetry = true; @@ -205,11 +105,9 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { Log.d(TAG, "Finished enterPasshtroughMode"); } - public void openConnection() throws IOException, TimeoutException, NoSuchAlgorithmException, ChecksumException { + public void openConnection() throws IOException, TimeoutException, NoSuchAlgorithmException, ChecksumException, EncryptionException { Log.d(TAG, "Begin openConnection"); - new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.OPEN_CONNECTION, mPumpSession, mPumpSession.getHMAC()).send(this); - // FIXME - We need to care what the response message is - wrong MAC and all that - readMessage(); + new OpenConnectionRequestMessage(mPumpSession, mPumpSession.getHMAC()).send(mDevice); Log.d(TAG, "Finished openConnection"); } @@ -234,7 +132,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { Log.d(TAG, String.format("Finished requestLinkKey. linkKey = '%s'", this.getPumpSession().getKey())); } - public byte negotiateChannel(byte lastRadioChannel) throws IOException, ChecksumException, TimeoutException { + public byte negotiateChannel(byte lastRadioChannel) throws IOException, ChecksumException, TimeoutException, EncryptionException { ArrayList<Byte> radioChannels = new ArrayList<>(Arrays.asList(ArrayUtils.toObject(RADIO_CHANNELS))); if (lastRadioChannel != 0x00) { @@ -250,26 +148,12 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { for (byte channel : radioChannels) { Log.d(TAG, String.format("negotiateChannel: trying channel '%d'...", channel)); mPumpSession.setRadioChannel(channel); - new ChannelNegotiateMessage(mPumpSession).send(this); - - // Don't care what the 0x81 response message is at this stage - Log.d(TAG, "negotiateChannel: Reading 0x81 message"); - readMessage(); - // The 0x80 message - Log.d(TAG, "negotiateChannel: Reading 0x80 message"); - ContourNextLinkMessage response = ContourNextLinkBinaryMessage.fromBytes(readMessage()); - byte[] responseBytes = response.encode(); - - Log.d(TAG, "negotiateChannel: Check response length"); - if (responseBytes.length > 46) { - // Looks promising, let's check the last byte of the payload to make sure - if (responseBytes[76] == mPumpSession.getRadioChannel()) { - break; - } else { - throw new IOException(String.format(Locale.getDefault(), "Expected to get a message for channel %d. Got %d", mPumpSession.getRadioChannel(), responseBytes[76])); - } + ChannelNegotiateResponseMessage response = new ChannelNegotiateRequestMessage(mPumpSession).send(mDevice); + + if (response.getRadioChannel() == mPumpSession.getRadioChannel()) { + break; } else { - mPumpSession.setRadioChannel((byte) 0); + throw new IOException(String.format(Locale.getDefault(), "Expected to get a message for channel %d. Got %d", mPumpSession.getRadioChannel(), response.getRadioChannel())); } } @@ -279,9 +163,7 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { public void beginEHSMSession() throws EncryptionException, IOException, TimeoutException, ChecksumException { Log.d(TAG, "Begin beginEHSMSession"); - new BeginEHSMMessage(mPumpSession).send(this); - // The Begin EHSM Session only has an 0x81 response - readMessage(); + new BeginEHSMMessage(mPumpSession).send(mDevice); Log.d(TAG, "Finished beginEHSMSession"); } @@ -289,172 +171,42 @@ public class MedtronicCnlReader implements ContourNextLinkMessageHandler { Log.d(TAG, "Begin getPumpTime"); // FIXME - throw if not in EHSM mode (add a state machine) - new PumpTimeRequestMessage(mPumpSession).send(this); - // Read the 0x81 - readMessage(); - - // Read the 0x80 - ContourNextLinkMessage response = PumpTimeResponseMessage.fromBytes(mPumpSession, readMessage()); - - if (response.encode().length < (61 + 8)) { - // Invalid message. Return an invalid date. - // TODO - deal with this more elegantly - Log.e(TAG, "Invalid message received for getPumpTime"); - return new Date(); - } - - // FIXME - this needs to go into PumpTimeResponseMessage - ByteBuffer dateBuffer = ByteBuffer.allocate(8); - dateBuffer.order(ByteOrder.BIG_ENDIAN); - dateBuffer.put(response.encode(), 0x3d, 8); - long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL; - long offset = dateBuffer.getInt(4); + PumpTimeResponseMessage response = new PumpTimeRequestMessage(mPumpSession).send(mDevice); - Log.d(TAG, "Finished getPumpTime with date " + MessageUtils.decodeDateTime(rtc, offset)); - return MessageUtils.decodeDateTime(rtc, offset); + Log.d(TAG, "Finished getPumpTime with date " + response.getPumpTime()); + return response.getPumpTime(); } - public void getPumpStatus(PumpStatusEvent pumpRecord, long pumpTimeOffset) throws IOException, EncryptionException, ChecksumException, TimeoutException { - Log.d(TAG, "Begin getPumpStatus"); - // FIXME - throw if not in EHSM mode (add a state machine) - - new PumpStatusRequestMessage(mPumpSession).send(this); - // Read the 0x81 - readMessage(); - - // Read the 0x80 - ContourNextLinkMessage response = PumpStatusResponseMessage.fromBytes(mPumpSession, readMessage()); + public PumpStatusEvent updatePumpStatus(PumpStatusEvent pumpRecord) throws IOException, EncryptionException, ChecksumException, TimeoutException { + Log.d(TAG, "Begin updatePumpStatus"); - if (response.encode().length < (57 + 96)) { - // Invalid message. Don't try and parse it - // TODO - deal with this more elegantly - Log.e(TAG, "Invalid message received for getPumpStatus"); - return; - } - - // FIXME - this needs to go into PumpStatusResponseMessage - ByteBuffer statusBuffer = ByteBuffer.allocate(96); - statusBuffer.order(ByteOrder.BIG_ENDIAN); - statusBuffer.put(response.encode(), 0x39, 96); - - // 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) { - // Sensor error. Let's reset. FIXME - solve this more elegantly later - pumpRecord.setSgv(0); - rtc = 0; - offset = 0; - pumpRecord.setCgmTrend(PumpStatusEvent.CGM_TREND.NOT_SET); - } else { - rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL; - 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); + // FIXME - throw if not in EHSM mode (add a state machine) + PumpStatusResponseMessage response = new PumpStatusRequestMessage(mPumpSession).send(mDevice); + response.updatePumpRecord(pumpRecord); - // Recent Bolus Wizard BGL - pumpRecord.setRecentBolusWizard(statusBuffer.get(0x48) != 0); - pumpRecord.setBolusWizardBGL(statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL + Log.d(TAG, "Finished updatePumpStatus"); - Log.d(TAG, "Finished getPumpStatus"); + return pumpRecord; } 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); + PumpBasalPatternResponseMessage response = new PumpBasalPatternRequestMessage(mPumpSession).send(mDevice); Log.d(TAG, "Finished getBasalPatterns"); } public void endEHSMSession() throws EncryptionException, IOException, TimeoutException, ChecksumException { Log.d(TAG, "Begin endEHSMSession"); - new EndEHSMMessage(mPumpSession).send(this); - // The End EHSM Session only has an 0x81 response - readMessage(); + new EndEHSMMessage(mPumpSession).send(mDevice); Log.d(TAG, "Finished endEHSMSession"); } - public void closeConnection() throws IOException, TimeoutException, ChecksumException { + public void closeConnection() throws IOException, TimeoutException, ChecksumException, EncryptionException, NoSuchAlgorithmException { Log.d(TAG, "Begin closeConnection"); - new ContourNextLinkBinaryMessage(ContourNextLinkBinaryMessage.CommandType.CLOSE_CONNECTION, mPumpSession, null).send(this); - // FIXME - We need to care what the response message is - wrong MAC and all that - readMessage(); + new CloseConnectionRequestMessage(mPumpSession, mPumpSession.getHMAC()).send(mDevice); Log.d(TAG, "Finished closeConnection"); } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java index 804ce6d..d43c2c8 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/BeginEHSMMessage.java @@ -1,6 +1,7 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; @@ -18,8 +19,10 @@ public class BeginEHSMMessage extends MedtronicRequestMessage { return new byte[] { 0x00 }; } - protected void sendMessage(UsbHidDriver mDevice) throws IOException { - super.sendMessage(mDevice); - mPumpSession.incrMedtronicSequenceNumber(); + public void send(UsbHidDriver mDevice) throws IOException, TimeoutException { + sendMessage(mDevice); + + // The Begin EHSM Session only has an 0x81 response + readMessage(mDevice); } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java similarity index 50% rename from app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java rename to app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java index 544caaf..5121641 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateRequestMessage.java @@ -1,18 +1,37 @@ package info.nightscout.android.medtronic.message; +import android.util.Log; + +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.concurrent.TimeoutException; +import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; /** * Created by lgoedhart on 26/03/2016. */ -public class ChannelNegotiateMessage extends MedtronicRequestMessage { - public ChannelNegotiateMessage(MedtronicCnlSession pumpSession) throws ChecksumException { +public class ChannelNegotiateRequestMessage extends MedtronicRequestMessage { + private static final String TAG = ChannelNegotiateRequestMessage.class.getSimpleName(); + + public ChannelNegotiateRequestMessage(MedtronicCnlSession pumpSession) throws ChecksumException { super(CommandType.SEND_MESSAGE, CommandAction.CHANNEL_NEGOTIATE, pumpSession, buildPayload(pumpSession)); } + public ChannelNegotiateResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException { + + // Don't care what the 0x81 response message is at this stage + Log.d(TAG, "negotiateChannel: Reading 0x81 message"); + readMessage(mDevice); + // The 0x80 message + Log.d(TAG, "negotiateChannel: Reading 0x80 message"); + ChannelNegotiateResponseMessage response = new ChannelNegotiateResponseMessage(mPumpSession, readMessage(mDevice)); + + return response; + } + protected static byte[] buildPayload( MedtronicCnlSession pumpSession ) { ByteBuffer payload = ByteBuffer.allocate(26); payload.order(ByteOrder.LITTLE_ENDIAN); diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java new file mode 100644 index 0000000..39a75d6 --- /dev/null +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ChannelNegotiateResponseMessage.java @@ -0,0 +1,31 @@ +package info.nightscout.android.medtronic.message; + +import android.util.Log; + +import info.nightscout.android.medtronic.MedtronicCnlSession; + +/** + * Created by lgoedhart on 27/03/2016. + */ +public class ChannelNegotiateResponseMessage extends MedtronicResponseMessage { + private static final String TAG = ChannelNegotiateResponseMessage.class.getSimpleName(); + + private byte radioChannel = 0; + + protected ChannelNegotiateResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException { + super(pumpSession, payload); + + byte[] responseBytes = this.encode(); + + Log.d(TAG, "negotiateChannel: Check response length"); + if (responseBytes.length > 46) { + radioChannel = responseBytes[76]; + } else { + radioChannel = ((byte) 0); + } + } + + public byte getRadioChannel() { + return radioChannel; + } +} diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java index 24238af..b560cfe 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryRequestMessage.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Locale; -import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java index 549ee88..89adf99 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkBinaryResponseMessage.java @@ -1,9 +1,5 @@ package info.nightscout.android.medtronic.message; -import java.io.IOException; -import java.util.Locale; -import java.util.concurrent.TimeoutException; - /** * Created by lgoedhart on 26/03/2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java index 9839f75..b5edb78 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandMessage.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; -import info.nightscout.android.medtronic.MedtronicCnlSession; /** * Created by lgoedhart on 26/03/2016. diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java index 12f9b95..4cbf415 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkCommandResponse.java @@ -1,7 +1,5 @@ package info.nightscout.android.medtronic.message; -import info.nightscout.android.medtronic.MedtronicCnlSession; - /** * Created by volker on 10.12.2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java index 36d39a4..1c8d92a 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java @@ -5,15 +5,11 @@ import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Locale; import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; -import info.nightscout.android.medtronic.MedtronicCnlSession; import info.nightscout.android.utils.HexDump; -import static android.R.id.message; - /** * Created by lgoedhart on 26/03/2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java deleted file mode 100644 index 1d669f6..0000000 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessageHandler.java +++ /dev/null @@ -1,11 +0,0 @@ -package info.nightscout.android.medtronic.message; - -import java.io.IOException; - -/** - * Created by lgoedhart on 26/03/2016. - */ -public interface ContourNextLinkMessageHandler { - void sendMessage( ContourNextLinkMessage message ) throws IOException; - ContourNextLinkMessage receiveMessage(); -} diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java index 390380d..036c185 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkRequestMessage.java @@ -1,14 +1,6 @@ package info.nightscout.android.medtronic.message; -import java.io.IOException; -import java.util.Locale; -import java.util.concurrent.TimeoutException; - -import info.nightscout.android.medtronic.MedtronicCnlSession; -import info.nightscout.android.medtronic.message.ContourNextLinkMessage; -import info.nightscout.android.medtronic.message.UnexpectedMessageException; - /** * Created by volker on 12.12.2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java index 24d6aa8..3dd5141 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkResponseMessage.java @@ -1,17 +1,9 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.Locale; import java.util.concurrent.TimeoutException; -import info.nightscout.android.USB.UsbHidDriver; -import info.nightscout.android.medtronic.MedtronicCnlSession; -import info.nightscout.android.medtronic.message.ChecksumException; -import info.nightscout.android.medtronic.message.ContourNextLinkMessage; -import info.nightscout.android.medtronic.message.UnexpectedMessageException; - /** * Created by lgoedhart on 26/03/2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java index 1400a26..fec7c00 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/DeviceInfoResponseCommandMessage.java @@ -6,10 +6,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import info.nightscout.android.medtronic.MedtronicCnlSession; -import info.nightscout.android.medtronic.message.ChecksumException; -import info.nightscout.android.medtronic.message.EncryptionException; -import info.nightscout.android.medtronic.message.MedtronicResponseMessage; -import info.nightscout.android.medtronic.message.UnexpectedMessageException; /** * Created by lgoedhart on 10/05/2016. diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java index fc7c4dd..9db6c03 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/EndEHSMMessage.java @@ -1,6 +1,7 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; @@ -18,8 +19,10 @@ public class EndEHSMMessage extends MedtronicRequestMessage { return new byte[] { 0x01 }; } - protected void sendMessage(UsbHidDriver mDevice) throws IOException { - super.sendMessage(mDevice); - mPumpSession.incrMedtronicSequenceNumber(); + public void send(UsbHidDriver mDevice) throws IOException, TimeoutException { + sendMessage(mDevice); + + // The End EHSM Session only has an 0x81 response + readMessage(mDevice); } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java index 8557561..dcc926c 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/MedtronicResponseMessage.java @@ -1,13 +1,13 @@ package info.nightscout.android.medtronic.message; -import info.nightscout.android.medtronic.MedtronicCnlSession; - import java.nio.ByteBuffer; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import info.nightscout.android.medtronic.MedtronicCnlSession; + /** * Created by lgoedhart on 26/03/2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java index 38f4ffb..b061ebd 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/OpenConnectionResponseMessage.java @@ -1,8 +1,5 @@ package info.nightscout.android.medtronic.message; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - import info.nightscout.android.medtronic.MedtronicCnlSession; /** diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java index a007153..d341f77 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternRequestMessage.java @@ -1,6 +1,7 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; @@ -13,8 +14,13 @@ public class PumpBasalPatternRequestMessage extends MedtronicRequestMessage { super(SendMessageType.READ_BASAL_PATTERN_REQUEST, pumpSession, null); } - protected void sendMessage(UsbHidDriver mDevice) throws IOException { - super.sendMessage(mDevice); - mPumpSession.incrMedtronicSequenceNumber(); + public PumpBasalPatternResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException { + // Read the 0x81 + readMessage(mDevice); + + // Read the 0x80 + PumpBasalPatternResponseMessage response = new PumpBasalPatternResponseMessage(mPumpSession, readMessage(mDevice)); + + return response; } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java index 43132b6..daef88a 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpBasalPatternResponseMessage.java @@ -1,5 +1,8 @@ package info.nightscout.android.medtronic.message; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + import info.nightscout.android.medtronic.MedtronicCnlSession; /** @@ -8,14 +11,22 @@ import info.nightscout.android.medtronic.MedtronicCnlSession; public class PumpBasalPatternResponseMessage extends MedtronicResponseMessage { protected PumpBasalPatternResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException { super(pumpSession, payload); - } - public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException { - // TODO - turn this into a factory - ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes); + // 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; + } + */ - // TODO - Validate the MessageType + // FIXME - this needs to go into PumpBasalPatternResponseMessage + ByteBuffer basalRatesBuffer = ByteBuffer.allocate(96); + basalRatesBuffer.order(ByteOrder.BIG_ENDIAN); + basalRatesBuffer.put(this.encode(), 0x39, 96); - return message; } + } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java index 0c507a6..9532cb6 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusRequestMessage.java @@ -1,6 +1,7 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; @@ -13,8 +14,14 @@ public class PumpStatusRequestMessage extends MedtronicRequestMessage { super(SendMessageType.READ_PUMP_STATUS_REQUEST, pumpSession, null); } - protected void sendMessage(UsbHidDriver mDevice) throws IOException { - super.sendMessage(mDevice); - mPumpSession.incrMedtronicSequenceNumber(); + public PumpStatusResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException { + sendMessage(mDevice); + + // Read the 0x81 + readMessage(mDevice); + + PumpStatusResponseMessage response = new PumpStatusResponseMessage(mPumpSession, readMessage(mDevice)); + + return response; } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java index 151348e..b791a29 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java @@ -1,21 +1,191 @@ package info.nightscout.android.medtronic.message; +import android.util.Log; + +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Date; + import info.nightscout.android.medtronic.MedtronicCnlSession; +import info.nightscout.android.model.medtronicNg.PumpStatusEvent; /** * Created by lgoedhart on 27/03/2016. */ public class PumpStatusResponseMessage extends MedtronicResponseMessage { + private static final String TAG = PumpStatusResponseMessage.class.getSimpleName(); + + // 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 PumpStatusEvent.CGM_TREND 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. + + private long rtc; + private long offset; + protected PumpStatusResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException { super(pumpSession, payload); + + if (this.encode().length < (57 + 96)) { + // Invalid message. Don't try and parse it + // TODO - deal with this more elegantly + Log.e(TAG, "Invalid message received for updatePumpStatus"); + return; + } + + // FIXME - this needs to go into PumpStatusResponseMessage + ByteBuffer statusBuffer = ByteBuffer.allocate(96); + statusBuffer.order(ByteOrder.BIG_ENDIAN); + statusBuffer.put(this.encode(), 0x39, 96); + + // Status Flags + suspended = ((statusBuffer.get(0x03) & 0x01) != 0x00); + bolusing = ((statusBuffer.get(0x03) & 0x02) != 0x00); + deliveringInsulin = ((statusBuffer.get(0x03) & 0x10) != 0x00); + tempBasalActive = ((statusBuffer.get(0x03) & 0x20) != 0x00); + cgmActive = ((statusBuffer.get(0x03) & 0x40) != 0x00); + + // Active basal pattern + activeBasalPattern = (statusBuffer.get(0x1a)); + + // Normal basal rate + long rawNormalBasal = statusBuffer.getInt(0x1b); + basalRate = (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 + tempBasalPercentage = (statusBuffer.get(0x23)); + + // Temp basal minutes remaining + tempBasalMinutesRemaining = ((short) (statusBuffer.getShort(0x24) & 0x0000ffff)); + + // Units of insulin delivered as basal today + // TODO - is this basal? Do we have a total Units delivered elsewhere? + basalUnitsDeliveredToday = (statusBuffer.getInt(0x26)); + + // Pump battery percentage + batteryPercentage = ((statusBuffer.get(0x2a))); + + // Reservoir amount + long rawReservoirAmount = statusBuffer.getInt(0x2b); + reservoirAmount = (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); + minutesOfInsulinRemaining = ((short) ((insulinHours * 60) + insulinMinutes)); + + // Active insulin + long rawActiveInsulin = statusBuffer.getShort(0x33) & 0x0000ffff; + activeInsulin = new BigDecimal(rawActiveInsulin / 10000f).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue(); + ; + + // CGM SGV + sgv = (statusBuffer.getShort(0x35) & 0x0000ffff); // In mg/DL. 0 means no CGM reading + + // SGV Date + + if ((sgv & 0x200) == 0x200) { + // Sensor error. Let's reset. FIXME - solve this more elegantly later + sgv = 0; + rtc = 0; + offset = 0; + cgmTrend = (PumpStatusEvent.CGM_TREND.NOT_SET); + } else { + rtc = statusBuffer.getInt(0x37) & 0x00000000ffffffffL; + offset = statusBuffer.getInt(0x3b); + cgmTrend = (PumpStatusEvent.CGM_TREND.fromMessageByte(statusBuffer.get(0x40))); + } + + // TODO - this should go in the sgvDate, and eventDate should be the time of this poll. + sgvDate = MessageUtils.decodeDateTime(rtc, offset); + + // Predictive low suspend + // TODO - there is more status info in this byte other than just a boolean yes/no + lowSuspendActive = (statusBuffer.get(0x3f) != 0); + + // Recent Bolus Wizard BGL + recentBolusWizard = (statusBuffer.get(0x48) != 0); + bolusWizardBGL = (statusBuffer.getShort(0x49) & 0x0000ffff); // In mg/DL } - public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException { - // TODO - turn this into a factory - ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes); + /** + * update pumpRecord with data read from pump + * + * @param pumpRecord + */ + public void updatePumpRecord(PumpStatusEvent pumpRecord) { + // Status Flags + pumpRecord.setSuspended(suspended); + pumpRecord.setBolusing(bolusing); + pumpRecord.setDeliveringInsulin(deliveringInsulin); + pumpRecord.setTempBasalActive(tempBasalActive); + pumpRecord.setCgmActive(cgmActive); + + // Active basal pattern + pumpRecord.setActiveBasalPattern(activeBasalPattern); + + // Normal basal rate + pumpRecord.setBasalRate(basalRate); + + // Temp basal percentage + pumpRecord.setTempBasalPercentage(tempBasalPercentage); + + // Temp basal minutes remaining + pumpRecord.setTempBasalMinutesRemaining(tempBasalMinutesRemaining); + + // Units of insulin delivered as basal today + pumpRecord.setBasalUnitsDeliveredToday(basalUnitsDeliveredToday); + + // Pump battery percentage + pumpRecord.setBatteryPercentage(batteryPercentage); + + // Reservoir amount + pumpRecord.setReservoirAmount(reservoirAmount); + + // Amount of insulin left in pump (in minutes) + pumpRecord.setMinutesOfInsulinRemaining(minutesOfInsulinRemaining); + + // Active insulin + pumpRecord.setActiveInsulin(activeInsulin); + + // CGM SGV + pumpRecord.setSgv(sgv); + + // SGV Date + pumpRecord.setCgmTrend(cgmTrend); + pumpRecord.setEventDate(new Date(sgvDate.getTime() - pumpRecord.getPumpTimeOffset())); - // TODO - Validate the MessageType + // Predictive low suspend + // TODO - there is more status info in this byte other than just a boolean yes/no + pumpRecord.setLowSuspendActive(lowSuspendActive); - return message; + // Recent Bolus Wizard BGL + pumpRecord.setRecentBolusWizard(recentBolusWizard); + pumpRecord.setBolusWizardBGL(bolusWizardBGL); // In mg/DL } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java index c773fa1..00343c2 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeRequestMessage.java @@ -1,6 +1,7 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; @@ -13,8 +14,13 @@ public class PumpTimeRequestMessage extends MedtronicRequestMessage { super(SendMessageType.TIME_REQUEST, pumpSession, null); } - protected void sendMessage(UsbHidDriver mDevice) throws IOException { - super.sendMessage(mDevice); - mPumpSession.incrMedtronicSequenceNumber(); + public PumpTimeResponseMessage send(UsbHidDriver mDevice) throws IOException, TimeoutException, ChecksumException, EncryptionException { + // Read the 0x81 + readMessage(mDevice); + + // Read the 0x80 + PumpTimeResponseMessage response = new PumpTimeResponseMessage(mPumpSession, readMessage(mDevice)); + + return response; } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java index 6f1ca90..3867391 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpTimeResponseMessage.java @@ -1,21 +1,41 @@ package info.nightscout.android.medtronic.message; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Date; + import info.nightscout.android.medtronic.MedtronicCnlSession; /** * Created by lgoedhart on 27/03/2016. */ public class PumpTimeResponseMessage extends MedtronicResponseMessage { + private static final String TAG = PumpTimeResponseMessage.class.getSimpleName(); + + private Date pumpTime; + protected PumpTimeResponseMessage(MedtronicCnlSession pumpSession, byte[] payload) throws EncryptionException, ChecksumException { super(pumpSession, payload); - } - public static ContourNextLinkMessage fromBytes(MedtronicCnlSession pumpSession, byte[] bytes) throws ChecksumException, EncryptionException { - // TODO - turn this into a factory - ContourNextLinkMessage message = MedtronicResponseMessage.fromBytes(pumpSession, bytes); + if (this.encode().length < (61 + 8)) { + // Invalid message. Return an invalid date. + // TODO - deal with this more elegantly + Log.e(TAG, "Invalid message received for getPumpTime"); + pumpTime = new Date(); + } - // TODO - Validate the MessageType + // FIXME - this needs to go into PumpTimeResponseMessage + ByteBuffer dateBuffer = ByteBuffer.allocate(8); + dateBuffer.order(ByteOrder.BIG_ENDIAN); + dateBuffer.put(this.encode(), 0x3d, 8); + long rtc = dateBuffer.getInt(0) & 0x00000000ffffffffL; + long offset = dateBuffer.getInt(4); + pumpTime = MessageUtils.decodeDateTime(rtc, offset); + } - return message; + public Date getPumpTime() { + return pumpTime; } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java index dc2a97b..a15ffa5 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ReadInfoRequestMessage.java @@ -1,12 +1,11 @@ package info.nightscout.android.medtronic.message; import java.io.IOException; +import java.util.concurrent.TimeoutException; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MedtronicCnlSession; -import java.util.concurrent.TimeoutException; - /** * Created by volker on 10.12.2016. */ diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java index 814f29d..2451ecb 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java @@ -235,7 +235,7 @@ public class MedtronicCnlIntentService extends IntentService { // TODO - send ACTION to MainActivity to show offset between pump and uploader. pumpRecord.setPumpTimeOffset(pumpOffset); pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset)); - cnlReader.getPumpStatus(pumpRecord, pumpOffset); + cnlReader.updatePumpStatus(pumpRecord); activePump.getPumpHistory().add(pumpRecord); cnlReader.endEHSMSession(); @@ -270,10 +270,12 @@ public class MedtronicCnlIntentService extends IntentService { Log.e(TAG, "Could not determine CNL HMAC", e); sendStatus("Error connecting to Contour Next Link: Hashing error."); } finally { - //TODO : 05.11.2016 has the close to be here? - cnlReader.closeConnection(); - cnlReader.endPassthroughMode(); - cnlReader.endControlMode(); + try { + cnlReader.closeConnection(); + cnlReader.endPassthroughMode(); + cnlReader.endControlMode(); + } catch (NoSuchAlgorithmException e) {} + } } catch (IOException e) { Log.e(TAG, "Error connecting to Contour Next Link.", e); 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 ab95d50..d73f3e6 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 @@ -272,6 +272,27 @@ public class PumpStatusEvent extends RealmObject { DOUBLE_DOWN, NOT_COMPUTABLE, RATE_OUT_OF_RANGE, - NOT_SET + NOT_SET; + + public static CGM_TREND fromMessageByte(byte messageByte) { + switch (messageByte) { + case (byte) 0x60: + return PumpStatusEvent.CGM_TREND.FLAT; + case (byte) 0xc0: + return PumpStatusEvent.CGM_TREND.DOUBLE_UP; + case (byte) 0xa0: + return PumpStatusEvent.CGM_TREND.SINGLE_UP; + case (byte) 0x80: + return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_UP; + case (byte) 0x40: + return PumpStatusEvent.CGM_TREND.FOURTY_FIVE_DOWN; + case (byte) 0x20: + return PumpStatusEvent.CGM_TREND.SINGLE_DOWN; + case (byte) 0x00: + return PumpStatusEvent.CGM_TREND.DOUBLE_DOWN; + default: + return PumpStatusEvent.CGM_TREND.NOT_COMPUTABLE; + } + } } } 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 b542315..bcacc34 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 @@ -10,8 +10,6 @@ import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; -- GitLab