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 92890b8e0f3577c2a6f4966b2f002da4b65a57ff..cf16a178a2ba8d5b6d8b4da4b13d1138f1391d4d 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 766a8a6145fd6a2bea03d1ef6d7146fdf97b951e..ca2629e4dba044519a7e4fba2bcbe1c2be007b2d 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 804ce6d2d75b17522bd6ef046229e33397104d65..d43c2c8b3aebc2cc903c7ebee783dc509a74812e 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 544caaf5098ec6b5f46f5303099b2cc92b026297..5121641ef319c7de5437cb3efc7dca238a71becd 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 0000000000000000000000000000000000000000..39a75d6daa6b8821d957eb13b0389a91189a558f --- /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 24238af71c62fcb961b147a8c1dd4fe7ac2789ee..b560cfe56f4691ea64827602865dcb5d6f3fe3ce 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 549ee889560c0c04b62bf54824b202501f70f672..89adf9980d64a024c87144334b4d2d47032c7f2c 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 9839f752f66b52f66837536f6928a1248d5077e2..b5edb78c6158b8c0a3414f25d65e5c9f77dfe864 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 12f9b9575057a497020176d2f00254cc9a934faf..4cbf4153ca38c8a20cf2254c81c51bd74d018ab0 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 36d39a48205ed0a53d047efc1b91879539320121..1c8d92aea954fc08f072d89d3f2111e7310a2f4e 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 1d669f67b84cd9118e11c91966db05ff95f8738c..0000000000000000000000000000000000000000 --- 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 390380d6b4d5bb88a4683a2c98e35d049ef71b95..036c185f290caa5a8e0fbbe7ca37a51b83d92f5e 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 24d6aa8b59000418060a7d4f44e30b2637716d92..3dd5141f561ba34948cb46956ec6555fd1f916f4 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 1400a2699299fd903315fbff494cbbf41f0d8aca..fec7c00038cfa9368ee6340ecff94ce5e5bf7311 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 fc7c4dd6f46e9a8a5788e79c7628027ccde16951..9db6c036bc3dd253b191715caa595a02570f645c 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 8557561507042e5a23c857a9c5bca483334d7472..dcc926cc16b7166eb54f4a3b0ab30538d9591675 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 38f4ffb75dd967fff04f05782ea22d428540510f..b061ebd1f08683f49d0f834338fbef0179856171 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 a00715319d580be7e5ab90d8cb00af3ccc2ba802..d341f775bc8fcb75ff944611dcc3440ed53e402a 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 43132b661edfbcb0e2e3f2d66c5b39147cdb5646..daef88a0d8824f35dc9dec5dc391b1906ecb040c 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 0c507a6ae06e91133a3188896ed8e41aa46be7fd..9532cb631c1584cb63df043a54737ae6bc0ce429 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 151348e08ba528e1a0fcbe751db5bbcf81d8e39e..b791a296a21f9beb6fb6e24e0c3caeaae904423d 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 c773fa1034cacad0cd1fc3ff355f5969a0e88aae..00343c2cf6930dc70d8cc86d421dc9f87ce83707 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 6f1ca90a46d23986267e29670a2d805bc7dd981e..38673913051f2cad684389630d402d2eb919a506 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 dc2a97bc5538016a753e646acce0499fb04f36db..a15ffa5f319d7c8f0f42804c40a69d77c9462bf2 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 814f29de870282cf6b54da5c6619dbbadc4f6864..2451ecb361b532fcc4a4d18e94a81e8dbd0d59e7 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 ab95d50f6d346d506d3a6f03a992e53cfaa00804..d73f3e6cdbdda5e2ec8d578fcd6391305c2e4235 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 b542315ff5d471d0dee874e0a08da76ebffe2f75..bcacc3428e19d027a840c7260aa1c3ac8a007912 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;