- copied RL code
- copied Medtronic code - added BLE Scan - added RL Stats activity - added RL configuration display
This commit is contained in:
parent
4aefea0b28
commit
e8f860be8b
|
@ -214,6 +214,8 @@ dependencies {
|
||||||
implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
|
implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
|
||||||
implementation(name: "sightparser-release", ext: "aar")
|
implementation(name: "sightparser-release", ext: "aar")
|
||||||
|
|
||||||
|
implementation "com.android.support:support-core-utils:${supportLibraryVersion}"
|
||||||
|
|
||||||
implementation("com.google.android:flexbox:0.3.0") {
|
implementation("com.google.android:flexbox:0.3.0") {
|
||||||
exclude group: "com.android.support"
|
exclude group: "com.android.support"
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,23 +53,33 @@
|
||||||
<activity android:name=".plugins.Overview.activities.QuickWizardListActivity">
|
<activity android:name=".plugins.Overview.activities.QuickWizardListActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.nightscout.androidaps.plugins.Overview.activities.QuickWizardListActivity" />
|
<action android:name="info.nightscout.androidaps.plugins.Overview.activities.QuickWizardListActivity" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".plugins.PumpDanaRS.activities.BLEScanActivity">
|
<activity android:name=".plugins.PumpDanaRS.activities.BLEScanActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.nightscout.androidaps.plugins.PumpDanaRS.activities.BLEScanActivity" />
|
<action android:name="info.nightscout.androidaps.plugins.PumpDanaRS.activities.BLEScanActivity" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".plugins.PumpDanaRS.activities.PairingHelperActivity" />
|
<activity android:name=".plugins.PumpDanaRS.activities.PairingHelperActivity" />
|
||||||
<activity android:name=".HistoryBrowseActivity" />
|
<activity android:name=".HistoryBrowseActivity" />
|
||||||
|
<activity android:name=".plugins.PumpCommon.dialog.RileyLinkBLEScanActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".receivers.DataReceiver"
|
android:name=".receivers.DataReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
||||||
<!-- Receive new SMS messages -->
|
<!-- Receive new SMS messages -->
|
||||||
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||||
<!-- Receiver from xDrip -->
|
<!-- Receiver from xDrip -->
|
||||||
|
@ -140,7 +150,7 @@
|
||||||
<service
|
<service
|
||||||
android:name=".plugins.PumpDanaRS.services.DanaRSService"
|
android:name=".plugins.PumpDanaRS.services.DanaRSService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"></service>
|
android:exported="true" />
|
||||||
<service
|
<service
|
||||||
android:name=".plugins.Wear.wearintegration.WatchUpdaterService"
|
android:name=".plugins.Wear.wearintegration.WatchUpdaterService"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
@ -158,12 +168,18 @@
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
<service
|
<service
|
||||||
android:name=".plugins.Overview.notifications.DismissNotificationService"
|
android:name=".plugins.Overview.notifications.DismissNotificationService"
|
||||||
android:exported="false"></service>
|
android:exported="false" />
|
||||||
|
<service android:name=".plugins.PumpMedtronic.service.RileyLinkMedtronicService" />
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.fabric.ApiKey"
|
android:name="io.fabric.ApiKey"
|
||||||
android:value="59d462666c664c57b29e1d79ea123e01f8057cfa" />
|
android:value="59d462666c664c57b29e1d79ea123e01f8057cfa" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".plugins.PumpCommon.dialog.RileylinkSettingsActivity"
|
||||||
|
android:label="@string/title_activity_rileylink_settings"
|
||||||
|
android:theme="@style/AppTheme"></activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -2,7 +2,6 @@ package com.gxwtech.roundtrip2.RoundtripService.RileyLink;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.gxwtech.roundtrip2.RT2Const;
|
import com.gxwtech.roundtrip2.RT2Const;
|
||||||
|
@ -24,7 +23,6 @@ import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpData.Page;
|
||||||
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpData.records.Record;
|
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpData.records.Record;
|
||||||
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpMessage;
|
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpMessage;
|
||||||
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpModel;
|
import com.gxwtech.roundtrip2.RoundtripService.medtronic.PumpModel;
|
||||||
import com.gxwtech.roundtrip2.ServiceData.PumpStatusResult;
|
|
||||||
import com.gxwtech.roundtrip2.ServiceData.ReadPumpClockResult;
|
import com.gxwtech.roundtrip2.ServiceData.ReadPumpClockResult;
|
||||||
import com.gxwtech.roundtrip2.ServiceData.ServiceResult;
|
import com.gxwtech.roundtrip2.ServiceData.ServiceResult;
|
||||||
import com.gxwtech.roundtrip2.util.ByteUtil;
|
import com.gxwtech.roundtrip2.util.ByteUtil;
|
||||||
|
@ -53,6 +51,7 @@ public class PumpManager {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
private Instant lastGoodPumpCommunicationTime = new Instant(0);
|
private Instant lastGoodPumpCommunicationTime = new Instant(0);
|
||||||
|
|
||||||
public PumpManager(Context context, RFSpy rfspy, byte[] pumpID) {
|
public PumpManager(Context context, RFSpy rfspy, byte[] pumpID) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.rfspy = rfspy;
|
this.rfspy = rfspy;
|
||||||
|
@ -62,36 +61,36 @@ public class PumpManager {
|
||||||
|
|
||||||
private PumpMessage runCommandWithArgs(PumpMessage msg) {
|
private PumpMessage runCommandWithArgs(PumpMessage msg) {
|
||||||
PumpMessage rval;
|
PumpMessage rval;
|
||||||
PumpMessage shortMessage = makePumpMessage(msg.messageType,new CarelinkShortMessageBody(new byte[]{0}));
|
PumpMessage shortMessage = makePumpMessage(msg.messageType, new CarelinkShortMessageBody(new byte[]{0}));
|
||||||
// look for ack from short message
|
// look for ack from short message
|
||||||
PumpMessage shortResponse = sendAndListen(shortMessage);
|
PumpMessage shortResponse = sendAndListen(shortMessage);
|
||||||
if (shortResponse.messageType.mtype == MessageType.PumpAck) {
|
if (shortResponse.messageType.mtype == MessageType.PumpAck) {
|
||||||
rval = sendAndListen(msg);
|
rval = sendAndListen(msg);
|
||||||
return rval;
|
return rval;
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG,"runCommandWithArgs: Pump did not ack Attention packet");
|
Log.e(TAG, "runCommandWithArgs: Pump did not ack Attention packet");
|
||||||
}
|
}
|
||||||
return new PumpMessage();
|
return new PumpMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PumpMessage sendAndListen(PumpMessage msg) {
|
protected PumpMessage sendAndListen(PumpMessage msg) {
|
||||||
return sendAndListen(msg,2000);
|
return sendAndListen(msg, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All pump communications go through this function.
|
// All pump communications go through this function.
|
||||||
protected PumpMessage sendAndListen(PumpMessage msg, int timeout_ms) {
|
protected PumpMessage sendAndListen(PumpMessage msg, int timeout_ms) {
|
||||||
boolean showPumpMessages = true;
|
boolean showPumpMessages = true;
|
||||||
if (showPumpMessages) {
|
if (showPumpMessages) {
|
||||||
Log.i(TAG,"Sent:"+ByteUtil.shortHexString(msg.getTxData()));
|
Log.i(TAG, "Sent:" + ByteUtil.shortHexString(msg.getTxData()));
|
||||||
}
|
}
|
||||||
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()),timeout_ms);
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), timeout_ms);
|
||||||
PumpMessage rval = new PumpMessage(resp.getRadioResponse().getPayload());
|
PumpMessage rval = new PumpMessage(resp.getRadioResponse().getPayload());
|
||||||
if (rval.isValid()) {
|
if (rval.isValid()) {
|
||||||
// Mark this as the last time we heard from the pump.
|
// Mark this as the last time we heard from the pump.
|
||||||
rememberLastGoodPumpCommunicationTime();
|
rememberLastGoodPumpCommunicationTime();
|
||||||
}
|
}
|
||||||
if (showPumpMessages) {
|
if (showPumpMessages) {
|
||||||
Log.i(TAG,"Received:"+ByteUtil.shortHexString(resp.getRadioResponse().getPayload()));
|
Log.i(TAG, "Received:" + ByteUtil.shortHexString(resp.getRadioResponse().getPayload()));
|
||||||
}
|
}
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +104,7 @@ public class PumpManager {
|
||||||
PumpMessage firstResponse = runCommandWithArgs(getHistoryMsg);
|
PumpMessage firstResponse = runCommandWithArgs(getHistoryMsg);
|
||||||
//Log.i(TAG,"getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents()));
|
//Log.i(TAG,"getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents()));
|
||||||
|
|
||||||
PumpMessage ackMsg = makePumpMessage(MessageType.PumpAck,new PumpAckMessageBody());
|
PumpMessage ackMsg = makePumpMessage(MessageType.PumpAck, new PumpAckMessageBody());
|
||||||
GetHistoryPageCarelinkMessageBody currentResponse = new GetHistoryPageCarelinkMessageBody(firstResponse.getMessageBody().getTxData());
|
GetHistoryPageCarelinkMessageBody currentResponse = new GetHistoryPageCarelinkMessageBody(firstResponse.getMessageBody().getTxData());
|
||||||
int expectedFrameNum = 1;
|
int expectedFrameNum = 1;
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
|
@ -117,13 +116,13 @@ public class PumpManager {
|
||||||
if ((frameData != null) && (frameData.length > 0) && currentResponse.getFrameNumber() == expectedFrameNum) {
|
if ((frameData != null) && (frameData.length > 0) && currentResponse.getFrameNumber() == expectedFrameNum) {
|
||||||
// success! got a frame.
|
// success! got a frame.
|
||||||
if (frameData.length != 64) {
|
if (frameData.length != 64) {
|
||||||
Log.w(TAG,"Expected frame of length 64, got frame of length " + frameData.length);
|
Log.w(TAG, "Expected frame of length 64, got frame of length " + frameData.length);
|
||||||
// but append it anyway?
|
// but append it anyway?
|
||||||
}
|
}
|
||||||
// handle successful frame data
|
// handle successful frame data
|
||||||
rval.appendData(currentResponse.getFrameData());
|
rval.appendData(currentResponse.getFrameData());
|
||||||
RoundtripService.getInstance().announceProgress(((100/16) * currentResponse.getFrameNumber()+1));
|
RoundtripService.getInstance().announceProgress(((100 / 16) * currentResponse.getFrameNumber() + 1));
|
||||||
Log.i(TAG,"getPumpHistoryPage: Got frame "+currentResponse.getFrameNumber());
|
Log.i(TAG, "getPumpHistoryPage: Got frame " + currentResponse.getFrameNumber());
|
||||||
// Do we need to ask for the next frame?
|
// Do we need to ask for the next frame?
|
||||||
if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722
|
if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722
|
||||||
expectedFrameNum++;
|
expectedFrameNum++;
|
||||||
|
@ -132,7 +131,7 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (frameData == null) {
|
if (frameData == null) {
|
||||||
Log.e(TAG,"null frame data, retrying");
|
Log.e(TAG, "null frame data, retrying");
|
||||||
} else if (currentResponse.getFrameNumber() != expectedFrameNum) {
|
} else if (currentResponse.getFrameNumber() != expectedFrameNum) {
|
||||||
Log.w(TAG, String.format("Expected frame number %d, received %d (retrying)", expectedFrameNum, currentResponse.getFrameNumber()));
|
Log.w(TAG, String.format("Expected frame number %d, received %d (retrying)", expectedFrameNum, currentResponse.getFrameNumber()));
|
||||||
} else if (frameData.length == 0) {
|
} else if (frameData.length == 0) {
|
||||||
|
@ -140,7 +139,7 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
failures++;
|
failures++;
|
||||||
if (failures == 6) {
|
if (failures == 6) {
|
||||||
Log.e(TAG,String.format("6 failures in attempting to download frame %d of page %d, giving up.",expectedFrameNum,pageNumber));
|
Log.e(TAG, String.format("6 failures in attempting to download frame %d of page %d, giving up.", expectedFrameNum, pageNumber));
|
||||||
done = true; // failure completion.
|
done = true; // failure completion.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,10 +150,10 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rval.getLength() != 1024) {
|
if (rval.getLength() != 1024) {
|
||||||
Log.w(TAG,"getPumpHistoryPage: short page. Expected length of 1024, found length of "+rval.getLength());
|
Log.w(TAG, "getPumpHistoryPage: short page. Expected length of 1024, found length of " + rval.getLength());
|
||||||
}
|
}
|
||||||
if (rval.isChecksumOK() == false) {
|
if (rval.isChecksumOK() == false) {
|
||||||
Log.e(TAG,"getPumpHistoryPage: checksum is wrong");
|
Log.e(TAG, "getPumpHistoryPage: checksum is wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
rval.dumpToDebug();
|
rval.dumpToDebug();
|
||||||
|
@ -203,7 +202,7 @@ public class PumpManager {
|
||||||
LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds);
|
LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds);
|
||||||
return pumpTime;
|
return pumpTime;
|
||||||
} catch (IllegalFieldValueException e) {
|
} catch (IllegalFieldValueException e) {
|
||||||
Log.e(TAG,String.format("parsePumpRTCBytes: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d",year,month,day,hours,minutes,seconds));
|
Log.e(TAG, String.format("parsePumpRTCBytes: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d", year, month, day, hours, minutes, seconds));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +211,7 @@ public class PumpManager {
|
||||||
ReadPumpClockResult rval = new ReadPumpClockResult();
|
ReadPumpClockResult rval = new ReadPumpClockResult();
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
PumpMessage getRTCMsg = makePumpMessage(new MessageType(MessageType.CMD_M_READ_RTC), new CarelinkShortMessageBody(new byte[]{0}));
|
PumpMessage getRTCMsg = makePumpMessage(new MessageType(MessageType.CMD_M_READ_RTC), new CarelinkShortMessageBody(new byte[]{0}));
|
||||||
Log.i(TAG,"getPumpRTC: " + ByteUtil.shortHexString(getRTCMsg.getTxData()));
|
Log.i(TAG, "getPumpRTC: " + ByteUtil.shortHexString(getRTCMsg.getTxData()));
|
||||||
PumpMessage response = sendAndListen(getRTCMsg);
|
PumpMessage response = sendAndListen(getRTCMsg);
|
||||||
if (response.isValid()) {
|
if (response.isValid()) {
|
||||||
byte[] receivedData = response.getContents();
|
byte[] receivedData = response.getContents();
|
||||||
|
@ -240,19 +239,19 @@ public class PumpManager {
|
||||||
public PumpModel getPumpModel() {
|
public PumpModel getPumpModel() {
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel), new GetPumpModelCarelinkMessageBody());
|
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel), new GetPumpModelCarelinkMessageBody());
|
||||||
Log.i(TAG,"getPumpModel: " + ByteUtil.shortHexString(msg.getTxData()));
|
Log.i(TAG, "getPumpModel: " + ByteUtil.shortHexString(msg.getTxData()));
|
||||||
PumpMessage response = sendAndListen(msg);
|
PumpMessage response = sendAndListen(msg);
|
||||||
Log.i(TAG,"getPumpModel response: " + ByteUtil.shortHexString(response.getContents()));
|
Log.i(TAG, "getPumpModel response: " + ByteUtil.shortHexString(response.getContents()));
|
||||||
byte[] contents = response.getContents();
|
byte[] contents = response.getContents();
|
||||||
PumpModel rval = PumpModel.UNSET;
|
PumpModel rval = PumpModel.UNSET;
|
||||||
if (contents != null) {
|
if (contents != null) {
|
||||||
if (contents.length >= 7) {
|
if (contents.length >= 7) {
|
||||||
rval = PumpModel.fromString(StringUtil.fromBytes(ByteUtil.substring(contents,3,3)));
|
rval = PumpModel.fromString(StringUtil.fromBytes(ByteUtil.substring(contents, 3, 3)));
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,"getPumpModel: Cannot return pump model number: data is too short.");
|
Log.w(TAG, "getPumpModel: Cannot return pump model number: data is too short.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,"getPumpModel: Cannot return pump model number: null response");
|
Log.w(TAG, "getPumpModel: Cannot return pump model number: null response");
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
|
@ -261,7 +260,7 @@ public class PumpManager {
|
||||||
|
|
||||||
public ISFTable getPumpISFProfile() {
|
public ISFTable getPumpISFProfile() {
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
PumpMessage getISFProfileMessage = makePumpMessage(new MessageType(MessageType.GetISFProfile),new CarelinkShortMessageBody());
|
PumpMessage getISFProfileMessage = makePumpMessage(new MessageType(MessageType.GetISFProfile), new CarelinkShortMessageBody());
|
||||||
PumpMessage resp = sendAndListen(getISFProfileMessage);
|
PumpMessage resp = sendAndListen(getISFProfileMessage);
|
||||||
ISFTable table = new ISFTable();
|
ISFTable table = new ISFTable();
|
||||||
table.parseFrom(resp.getContents());
|
table.parseFrom(resp.getContents());
|
||||||
|
@ -270,7 +269,7 @@ public class PumpManager {
|
||||||
|
|
||||||
public PumpMessage getBolusWizardCarbProfile() {
|
public PumpMessage getBolusWizardCarbProfile() {
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
PumpMessage getCarbProfileMessage = makePumpMessage(new MessageType(MessageType.CMD_M_READ_CARB_RATIOS),new CarelinkShortMessageBody());
|
PumpMessage getCarbProfileMessage = makePumpMessage(new MessageType(MessageType.CMD_M_READ_CARB_RATIOS), new CarelinkShortMessageBody());
|
||||||
PumpMessage resp = sendAndListen(getCarbProfileMessage);
|
PumpMessage resp = sendAndListen(getCarbProfileMessage);
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
@ -280,19 +279,19 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hunt() {
|
public void hunt() {
|
||||||
tryoutPacket(new byte[] {MessageType.CMD_M_READ_PUMP_STATUS,0});
|
tryoutPacket(new byte[]{MessageType.CMD_M_READ_PUMP_STATUS, 0});
|
||||||
tryoutPacket(new byte[] {MessageType.CMD_M_READ_FIRMWARE_VER,0});
|
tryoutPacket(new byte[]{MessageType.CMD_M_READ_FIRMWARE_VER, 0});
|
||||||
tryoutPacket(new byte[] {MessageType.CMD_M_READ_INSULIN_REMAINING,0});
|
tryoutPacket(new byte[]{MessageType.CMD_M_READ_INSULIN_REMAINING, 0});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// See ButtonPressCarelinkMessageBody
|
// See ButtonPressCarelinkMessageBody
|
||||||
public void pressButton(int which) {
|
public void pressButton(int which) {
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
PumpMessage pressButtonMessage = makePumpMessage(new MessageType(MessageType.CMD_M_KEYPAD_PUSH),new ButtonPressCarelinkMessageBody(which));
|
PumpMessage pressButtonMessage = makePumpMessage(new MessageType(MessageType.CMD_M_KEYPAD_PUSH), new ButtonPressCarelinkMessageBody(which));
|
||||||
PumpMessage resp = sendAndListen(pressButtonMessage);
|
PumpMessage resp = sendAndListen(pressButtonMessage);
|
||||||
if (resp.messageType.mtype != MessageType.PumpAck) {
|
if (resp.messageType.mtype != MessageType.PumpAck) {
|
||||||
Log.e(TAG,"Pump did not ack button press.");
|
Log.e(TAG, "Pump did not ack button press.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,16 +300,16 @@ public class PumpManager {
|
||||||
// **** FIXME: this wakeup doesn't seem to work well... must revisit
|
// **** FIXME: this wakeup doesn't seem to work well... must revisit
|
||||||
pumpAwakeForMinutes = duration_minutes;
|
pumpAwakeForMinutes = duration_minutes;
|
||||||
Instant lastGood = getLastGoodPumpCommunicationTime();
|
Instant lastGood = getLastGoodPumpCommunicationTime();
|
||||||
// Instant lastGoodPlus = lastGood.plus(new Duration(pumpAwakeForMinutes * 60 * 1000));
|
// Instant lastGoodPlus = lastGood.plus(new Duration(receiverDeviceAwakeForMinutes * 60 * 1000));
|
||||||
Instant lastGoodPlus = lastGood.plus(new Duration(1 * 60 * 1000));
|
Instant lastGoodPlus = lastGood.plus(new Duration(1 * 60 * 1000));
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
if (now.compareTo(lastGoodPlus) > 0) {
|
if (now.compareTo(lastGoodPlus) > 0) {
|
||||||
Log.i(TAG,"Waking pump...");
|
Log.i(TAG, "Waking pump...");
|
||||||
PumpMessage msg = makePumpMessage(new MessageType(MessageType.PowerOn), new CarelinkShortMessageBody(new byte[]{(byte) duration_minutes}));
|
PumpMessage msg = makePumpMessage(new MessageType(MessageType.PowerOn), new CarelinkShortMessageBody(new byte[]{(byte) duration_minutes}));
|
||||||
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), (byte) 0, (byte) 200, (byte) 0, (byte) 0, 15000, (byte) 0);
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), (byte) 0, (byte) 200, (byte) 0, (byte) 0, 15000, (byte) 0);
|
||||||
Log.i(TAG, "wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
Log.i(TAG, "wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
} else {
|
} else {
|
||||||
Log.v(TAG,"Last pump communication was recent, not waking pump.");
|
Log.v(TAG, "Last pump communication was recent, not waking pump.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,18 +323,18 @@ public class PumpManager {
|
||||||
|
|
||||||
private int tune_tryFrequency(double freqMHz) {
|
private int tune_tryFrequency(double freqMHz) {
|
||||||
rfspy.setBaseFrequency(freqMHz);
|
rfspy.setBaseFrequency(freqMHz);
|
||||||
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel),new GetPumpModelCarelinkMessageBody());
|
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel), new GetPumpModelCarelinkMessageBody());
|
||||||
RadioPacket pkt = new RadioPacket(msg.getTxData());
|
RadioPacket pkt = new RadioPacket(msg.getTxData());
|
||||||
RFSpyResponse resp = rfspy.transmitThenReceive(pkt,(byte)0,(byte)0,(byte)0,(byte)0,rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS,(byte)0);
|
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS, (byte) 0);
|
||||||
if (resp.wasTimeout()) {
|
if (resp.wasTimeout()) {
|
||||||
Log.w(TAG,String.format("tune_tryFrequency: no pump response at frequency %.2f",freqMHz));
|
Log.w(TAG, String.format("tune_tryFrequency: no pump response at frequency %.2f", freqMHz));
|
||||||
} else if (resp.looksLikeRadioPacket()) {
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
RadioResponse radioResponse = new RadioResponse(resp.getRaw());
|
RadioResponse radioResponse = new RadioResponse(resp.getRaw());
|
||||||
if (radioResponse.isValid()) {
|
if (radioResponse.isValid()) {
|
||||||
Log.w(TAG,String.format("tune_tryFrequency: saw response level %d at frequency %.2f",radioResponse.rssi,freqMHz));
|
Log.w(TAG, String.format("tune_tryFrequency: saw response level %d at frequency %.2f", radioResponse.rssi, freqMHz));
|
||||||
return radioResponse.rssi;
|
return radioResponse.rssi;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,"tune_tryFrequency: invalid radio response:"+ByteUtil.shortHexString(radioResponse.getPayload()));
|
Log.w(TAG, "tune_tryFrequency: invalid radio response:" + ByteUtil.shortHexString(radioResponse.getPayload()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -351,7 +350,7 @@ public class PumpManager {
|
||||||
// Try again at larger step size
|
// Try again at larger step size
|
||||||
stepsize += 0.05;
|
stepsize += 0.05;
|
||||||
} else {
|
} else {
|
||||||
if ((int)(evenBetterFrequency * 100) == (int)(betterFrequency * 100)) {
|
if ((int) (evenBetterFrequency * 100) == (int) (betterFrequency * 100)) {
|
||||||
// value did not change, so we're done.
|
// value did not change, so we're done.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -360,7 +359,7 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
if (betterFrequency == 0.0) {
|
if (betterFrequency == 0.0) {
|
||||||
// we've failed... caller should try a full scan for pump
|
// we've failed... caller should try a full scan for pump
|
||||||
Log.e(TAG,"quickTuneForPump: failed to find pump");
|
Log.e(TAG, "quickTuneForPump: failed to find pump");
|
||||||
} else {
|
} else {
|
||||||
rfspy.setBaseFrequency(betterFrequency);
|
rfspy.setBaseFrequency(betterFrequency);
|
||||||
if (betterFrequency != startFrequencyMHz) {
|
if (betterFrequency != startFrequencyMHz) {
|
||||||
|
@ -373,7 +372,7 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private double quickTunePumpStep(double startFrequencyMHz, double stepSizeMHz) {
|
private double quickTunePumpStep(double startFrequencyMHz, double stepSizeMHz) {
|
||||||
Log.i(TAG,"Doing quick radio tune for pump ID " + pumpID);
|
Log.i(TAG, "Doing quick radio tune for pump ID " + pumpID);
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
int startRssi = tune_tryFrequency(startFrequencyMHz);
|
int startRssi = tune_tryFrequency(startFrequencyMHz);
|
||||||
double lowerFrequency = startFrequencyMHz - stepSizeMHz;
|
double lowerFrequency = startFrequencyMHz - stepSizeMHz;
|
||||||
|
@ -395,19 +394,19 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private double scanForPump(double[] frequencies) {
|
private double scanForPump(double[] frequencies) {
|
||||||
Log.i(TAG,"Scanning for pump ID " + pumpID);
|
Log.i(TAG, "Scanning for pump ID " + pumpID);
|
||||||
wakeup(pumpAwakeForMinutes);
|
wakeup(pumpAwakeForMinutes);
|
||||||
FrequencyScanResults results = new FrequencyScanResults();
|
FrequencyScanResults results = new FrequencyScanResults();
|
||||||
|
|
||||||
for (int i=0; i<frequencies.length; i++) {
|
for (int i = 0; i < frequencies.length; i++) {
|
||||||
int tries = 3;
|
int tries = 3;
|
||||||
FrequencyTrial trial = new FrequencyTrial();
|
FrequencyTrial trial = new FrequencyTrial();
|
||||||
trial.frequencyMHz = frequencies[i];
|
trial.frequencyMHz = frequencies[i];
|
||||||
rfspy.setBaseFrequency(frequencies[i]);
|
rfspy.setBaseFrequency(frequencies[i]);
|
||||||
int sumRSSI = 0;
|
int sumRSSI = 0;
|
||||||
for (int j = 0; j<tries; j++) {
|
for (int j = 0; j < tries; j++) {
|
||||||
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel), new GetPumpModelCarelinkMessageBody());
|
PumpMessage msg = makePumpMessage(new MessageType(MessageType.GetPumpModel), new GetPumpModelCarelinkMessageBody());
|
||||||
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()),(byte) 0, (byte) 0, (byte) 0, (byte) 0, rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS, (byte) 0);
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), (byte) 0, (byte) 0, (byte) 0, (byte) 0, rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS, (byte) 0);
|
||||||
if (resp.wasTimeout()) {
|
if (resp.wasTimeout()) {
|
||||||
Log.e(TAG, String.format("scanForPump: Failed to find pump at frequency %.2f", frequencies[i]));
|
Log.e(TAG, String.format("scanForPump: Failed to find pump at frequency %.2f", frequencies[i]));
|
||||||
} else if (resp.looksLikeRadioPacket()) {
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
@ -416,7 +415,7 @@ public class PumpManager {
|
||||||
sumRSSI += radioResponse.rssi;
|
sumRSSI += radioResponse.rssi;
|
||||||
trial.successes++;
|
trial.successes++;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,"Failed to parse radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
Log.w(TAG, "Failed to parse radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "scanForPump: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
Log.e(TAG, "scanForPump: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
@ -424,68 +423,68 @@ public class PumpManager {
|
||||||
trial.tries++;
|
trial.tries++;
|
||||||
}
|
}
|
||||||
sumRSSI += -99.0 * (trial.tries - trial.successes);
|
sumRSSI += -99.0 * (trial.tries - trial.successes);
|
||||||
trial.averageRSSI = (double)(sumRSSI) / (double)(trial.tries);
|
trial.averageRSSI = (double) (sumRSSI) / (double) (trial.tries);
|
||||||
results.trials.add(trial);
|
results.trials.add(trial);
|
||||||
}
|
}
|
||||||
results.sort(); // sorts in ascending order
|
results.sort(); // sorts in ascending order
|
||||||
Log.d(TAG,"Sorted scan results:");
|
Log.d(TAG, "Sorted scan results:");
|
||||||
for (int k=0; k<results.trials.size(); k++) {
|
for (int k = 0; k < results.trials.size(); k++) {
|
||||||
FrequencyTrial one = results.trials.get(k);
|
FrequencyTrial one = results.trials.get(k);
|
||||||
Log.d(TAG,String.format("Scan Result[%d]: Freq=%.2f, avg RSSI = %f",k,one.frequencyMHz, one.averageRSSI));
|
Log.d(TAG, String.format("Scan Result[%d]: Freq=%.2f, avg RSSI = %f", k, one.frequencyMHz, one.averageRSSI));
|
||||||
}
|
}
|
||||||
FrequencyTrial bestTrial = results.trials.get(results.trials.size()-1);
|
FrequencyTrial bestTrial = results.trials.get(results.trials.size() - 1);
|
||||||
results.bestFrequencyMHz = bestTrial.frequencyMHz;
|
results.bestFrequencyMHz = bestTrial.frequencyMHz;
|
||||||
if (bestTrial.successes > 0) {
|
if (bestTrial.successes > 0) {
|
||||||
rfspy.setBaseFrequency(results.bestFrequencyMHz);
|
rfspy.setBaseFrequency(results.bestFrequencyMHz);
|
||||||
return results.bestFrequencyMHz;
|
return results.bestFrequencyMHz;
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG,"No pump response during scan.");
|
Log.e(TAG, "No pump response during scan.");
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PumpMessage makePumpMessage(MessageType messageType, MessageBody messageBody) {
|
private PumpMessage makePumpMessage(MessageType messageType, MessageBody messageBody) {
|
||||||
PumpMessage msg = new PumpMessage();
|
PumpMessage msg = new PumpMessage();
|
||||||
msg.init(new PacketType(PacketType.Carelink),pumpID,messageType,messageBody);
|
msg.init(new PacketType(PacketType.Carelink), pumpID, messageType, messageBody);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PumpMessage makePumpMessage(byte msgType, MessageBody body) {
|
private PumpMessage makePumpMessage(byte msgType, MessageBody body) {
|
||||||
return makePumpMessage(new MessageType(msgType),body);
|
return makePumpMessage(new MessageType(msgType), body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PumpMessage makePumpMessage(byte[] typeAndBody) {
|
private PumpMessage makePumpMessage(byte[] typeAndBody) {
|
||||||
PumpMessage msg = new PumpMessage();
|
PumpMessage msg = new PumpMessage();
|
||||||
msg.init(ByteUtil.concat(ByteUtil.concat(new byte[]{(byte)0xa7},pumpID),typeAndBody));
|
msg.init(ByteUtil.concat(ByteUtil.concat(new byte[]{(byte) 0xa7}, pumpID), typeAndBody));
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rememberLastGoodPumpCommunicationTime() {
|
private void rememberLastGoodPumpCommunicationTime() {
|
||||||
lastGoodPumpCommunicationTime = Instant.now();
|
lastGoodPumpCommunicationTime = Instant.now();
|
||||||
SharedPreferences.Editor ed = prefs.edit();
|
SharedPreferences.Editor ed = prefs.edit();
|
||||||
ed.putLong("lastGoodPumpCommunicationTime",lastGoodPumpCommunicationTime.getMillis());
|
ed.putLong("lastGoodReceiverCommunicationTime", lastGoodPumpCommunicationTime.getMillis());
|
||||||
ed.commit();
|
ed.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Instant getLastGoodPumpCommunicationTime() {
|
private Instant getLastGoodPumpCommunicationTime() {
|
||||||
// If we have a value of zero, we need to load from prefs.
|
// If we have a value of zero, we need to load from prefs.
|
||||||
if (lastGoodPumpCommunicationTime.getMillis() == new Instant(0).getMillis()) {
|
if (lastGoodPumpCommunicationTime.getMillis() == new Instant(0).getMillis()) {
|
||||||
lastGoodPumpCommunicationTime = new Instant(prefs.getLong("lastGoodPumpCommunicationTime",0));
|
lastGoodPumpCommunicationTime = new Instant(prefs.getLong("lastGoodReceiverCommunicationTime", 0));
|
||||||
// Might still be zero, but that's fine.
|
// Might still be zero, but that's fine.
|
||||||
}
|
}
|
||||||
double minutesAgo = (Instant.now().getMillis() - lastGoodPumpCommunicationTime.getMillis()) / (1000.0 * 60.0);
|
double minutesAgo = (Instant.now().getMillis() - lastGoodPumpCommunicationTime.getMillis()) / (1000.0 * 60.0);
|
||||||
Log.v(TAG,"Last good pump communication was " + minutesAgo + " minutes ago.");
|
Log.v(TAG, "Last good pump communication was " + minutesAgo + " minutes ago.");
|
||||||
return lastGoodPumpCommunicationTime;
|
return lastGoodPumpCommunicationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PumpMessage getRemainingBattery() {
|
public PumpMessage getRemainingBattery() {
|
||||||
PumpMessage getBatteryMsg = makePumpMessage(new MessageType(MessageType.GetBattery),new CarelinkShortMessageBody());
|
PumpMessage getBatteryMsg = makePumpMessage(new MessageType(MessageType.GetBattery), new CarelinkShortMessageBody());
|
||||||
PumpMessage resp = sendAndListen(getBatteryMsg);
|
PumpMessage resp = sendAndListen(getBatteryMsg);
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PumpMessage getRemainingInsulin() {
|
public PumpMessage getRemainingInsulin() {
|
||||||
PumpMessage msg = makePumpMessage(new MessageType(MessageType.CMD_M_READ_INSULIN_REMAINING),new CarelinkShortMessageBody());
|
PumpMessage msg = makePumpMessage(new MessageType(MessageType.CMD_M_READ_INSULIN_REMAINING), new CarelinkShortMessageBody());
|
||||||
PumpMessage resp = sendAndListen(msg);
|
PumpMessage resp = sendAndListen(msg);
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
@ -498,7 +497,9 @@ public class PumpManager {
|
||||||
|
|
||||||
PumpManagerStatus pumpManagerStatus = new PumpManagerStatus();
|
PumpManagerStatus pumpManagerStatus = new PumpManagerStatus();
|
||||||
|
|
||||||
public PumpManagerStatus getPumpManagerStatus() { return pumpManagerStatus; }
|
public PumpManagerStatus getPumpManagerStatus() {
|
||||||
|
return pumpManagerStatus;
|
||||||
|
}
|
||||||
|
|
||||||
public void updatePumpManagerStatus() {
|
public void updatePumpManagerStatus() {
|
||||||
PumpMessage resp = getRemainingBattery();
|
PumpMessage resp = getRemainingBattery();
|
||||||
|
@ -555,44 +556,44 @@ public class PumpManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPageDecode() {
|
public void testPageDecode() {
|
||||||
byte[] raw = new byte[] {(byte)0x6D, (byte)0x62, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
byte[] raw = new byte[]{(byte) 0x6D, (byte) 0x62, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x63, (byte)0x10, (byte)0x6D, (byte)0x63, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x63, (byte) 0x10, (byte) 0x6D, (byte) 0x63, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
|
||||||
(byte)0x01, (byte)0x01, (byte)0x00, (byte)0x5A, (byte)0xA5, (byte)0x49, (byte)0x04, (byte)0x10, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x00, (byte)0x6D, (byte)0xA5, (byte)0x49, (byte)0x04, (byte)0x10, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x64, (byte)0x10, (byte)0x6D, (byte)0x64, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00,
|
(byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x5A, (byte) 0xA5, (byte) 0x49, (byte) 0x04, (byte) 0x10, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x6D, (byte) 0xA5, (byte) 0x49, (byte) 0x04, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x10, (byte) 0x6D, (byte) 0x64, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x64, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x64, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x64, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x0C, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x0C, (byte) 0x00,
|
||||||
(byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x64, (byte)0x01, (byte)0x75, (byte)0x94, (byte)0x0D, (byte)0x05, (byte)0x10, (byte)0x64, (byte)0x01, (byte)0x44, (byte)0x95, (byte)0x0D, (byte)0x05, (byte)0x10, (byte)0x17, (byte)0x00, (byte)0x4E, (byte)0x95, (byte)0x0D, (byte)0x05, (byte)0x10, (byte)0x18, (byte)0x00, (byte)0x40, (byte)0x95, (byte)0x0D, (byte)0x05, (byte)0x10,
|
(byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x64, (byte) 0x01, (byte) 0x75, (byte) 0x94, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x64, (byte) 0x01, (byte) 0x44, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x17, (byte) 0x00, (byte) 0x4E, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x18, (byte) 0x00, (byte) 0x40, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10,
|
||||||
(byte)0x19, (byte)0x00, (byte)0x40, (byte)0x81, (byte)0x15, (byte)0x05, (byte)0x10, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x65, (byte)0x10, (byte)0x6D, (byte)0x65, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x19, (byte) 0x00, (byte) 0x40, (byte) 0x81, (byte) 0x15, (byte) 0x05, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x65, (byte) 0x10, (byte) 0x6D, (byte) 0x65, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x1A, (byte)0x00, (byte)0x47, (byte)0x82, (byte)0x09, (byte)0x06,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x1A, (byte) 0x00, (byte) 0x47, (byte) 0x82, (byte) 0x09, (byte) 0x06,
|
||||||
(byte)0x10, (byte)0x1A, (byte)0x01, (byte)0x5C, (byte)0x82, (byte)0x09, (byte)0x06, (byte)0x10, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x66, (byte)0x10, (byte)0x6D, (byte)0x66, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x10, (byte) 0x1A, (byte) 0x01, (byte) 0x5C, (byte) 0x82, (byte) 0x09, (byte) 0x06, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x66, (byte) 0x10, (byte) 0x6D, (byte) 0x66, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x67, (byte)0x10, (byte)0x6D, (byte)0x67, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x67, (byte) 0x10, (byte) 0x6D, (byte) 0x67, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x68, (byte)0x10, (byte)0x6D, (byte)0x68, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x68, (byte) 0x10, (byte) 0x6D, (byte) 0x68, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x69, (byte)0x10, (byte)0x6D, (byte)0x69, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x69, (byte) 0x10, (byte) 0x6D, (byte) 0x69, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6A, (byte)0x10, (byte)0x6D, (byte)0x6A, (byte)0x10, (byte)0x05, (byte)0x0C,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A, (byte) 0x10, (byte) 0x6D, (byte) 0x6A, (byte) 0x10, (byte) 0x05, (byte) 0x0C,
|
||||||
(byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6B, (byte)0x10, (byte)0x6D, (byte)0x6B, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6B, (byte) 0x10, (byte) 0x6D, (byte) 0x6B, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6C,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6C,
|
||||||
(byte)0x10, (byte)0x6D, (byte)0x6C, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x10, (byte) 0x6D, (byte) 0x6C, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6D, (byte)0x10, (byte)0x6D, (byte)0x6D, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6D, (byte) 0x10, (byte) 0x6D, (byte) 0x6D, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6E, (byte)0x10, (byte)0x6D, (byte)0x6E, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6E, (byte) 0x10, (byte) 0x6D, (byte) 0x6E, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6F, (byte)0x10, (byte)0x6D, (byte)0x6F, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6F, (byte) 0x10, (byte) 0x6D, (byte) 0x6F, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00,
|
||||||
(byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x19, (byte)0x00, (byte)0x40, (byte)0x81, (byte)0x03, (byte)0x10, (byte)0x10, (byte)0x1A, (byte)0x00, (byte)0x68, (byte)0x96, (byte)0x0A, (byte)0x10, (byte)0x10, (byte)0x1A, (byte)0x01, (byte)0x40, (byte)0x97, (byte)0x0A, (byte)0x10, (byte)0x10, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x19, (byte) 0x00, (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x10, (byte) 0x10, (byte) 0x1A, (byte) 0x00, (byte) 0x68, (byte) 0x96, (byte) 0x0A, (byte) 0x10, (byte) 0x10, (byte) 0x1A, (byte) 0x01, (byte) 0x40, (byte) 0x97, (byte) 0x0A, (byte) 0x10, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x70, (byte)0x10, (byte)0x6D, (byte)0x70, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x70, (byte) 0x10, (byte) 0x6D, (byte) 0x70, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x71, (byte)0x10, (byte)0x6D, (byte)0x71, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x71, (byte) 0x10, (byte) 0x6D, (byte) 0x71, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x72, (byte)0x10, (byte)0x6D, (byte)0x72, (byte)0x10, (byte)0x05, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x72, (byte) 0x10, (byte) 0x6D, (byte) 0x72, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x73, (byte)0x10, (byte)0x6D, (byte)0x73, (byte)0x10, (byte)0x05, (byte)0x0C,
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x73, (byte) 0x10, (byte) 0x6D, (byte) 0x73, (byte) 0x10, (byte) 0x05, (byte) 0x0C,
|
||||||
(byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
(byte)0x00, (byte)0x0C, (byte)0x00, (byte)0xE8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x74, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x2C, (byte)0x79,
|
(byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x74, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x2C, (byte) 0x79,
|
||||||
};
|
};
|
||||||
Page page = new Page();
|
Page page = new Page();
|
||||||
page.parseFrom(raw,PumpModel.MM522);
|
page.parseFrom(raw, PumpModel.MM522);
|
||||||
page.parseByDates(raw,PumpModel.MM522);
|
page.parseByDates(raw, PumpModel.MM522);
|
||||||
page.parsePicky(raw,PumpModel.MM522);
|
page.parsePicky(raw, PumpModel.MM522);
|
||||||
Log.i(TAG,"testPageDecode: done");
|
Log.i(TAG, "testPageDecode: done");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package info.nightscout.androidaps.plugins.PumpCommon;
|
package info.nightscout.androidaps.plugins.PumpCommon;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -17,6 +22,7 @@ import info.nightscout.androidaps.data.ProfileStore;
|
||||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
import info.nightscout.androidaps.db.ExtendedBolus;
|
import info.nightscout.androidaps.db.ExtendedBolus;
|
||||||
import info.nightscout.androidaps.db.TemporaryBasal;
|
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||||
|
import info.nightscout.androidaps.events.EventAppExit;
|
||||||
import info.nightscout.androidaps.interfaces.ConstraintsInterface;
|
import info.nightscout.androidaps.interfaces.ConstraintsInterface;
|
||||||
import info.nightscout.androidaps.interfaces.PluginBase;
|
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||||
|
@ -24,41 +30,68 @@ import info.nightscout.androidaps.interfaces.PluginType;
|
||||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.driver.PumpDriverInterface;
|
import info.nightscout.androidaps.plugins.PumpCommon.driver.PumpDriverInterface;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.PumpUtil;
|
||||||
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
|
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
|
import info.nightscout.utils.DecimalFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by andy on 23.04.18.
|
* Created by andy on 23.04.18.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin)
|
||||||
|
|
||||||
|
|
||||||
public abstract class PumpPluginAbstract extends PluginBase implements PumpInterface, ConstraintsInterface {
|
public abstract class PumpPluginAbstract extends PluginBase implements PumpInterface, ConstraintsInterface {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PumpPluginAbstract.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PumpPluginAbstract.class);
|
||||||
protected boolean pumpServiceRunning = false;
|
//protected boolean pumpServiceRunning = false;
|
||||||
|
|
||||||
|
protected PumpDescription pumpDescription = new PumpDescription();
|
||||||
|
protected PumpStatus pumpStatusData;
|
||||||
|
|
||||||
protected static PumpPluginAbstract plugin = null;
|
|
||||||
protected PumpDriverInterface pumpDriver;
|
protected PumpDriverInterface pumpDriver;
|
||||||
protected PumpStatus pumpStatus;
|
protected PumpStatus pumpStatus;
|
||||||
protected String internalName;
|
protected String internalName;
|
||||||
|
|
||||||
|
protected ServiceConnection serviceConnection = null;
|
||||||
|
|
||||||
|
|
||||||
protected PumpPluginAbstract(PumpDriverInterface pumpDriverInterface, //
|
protected PumpPluginAbstract(PumpDriverInterface pumpDriverInterface, //
|
||||||
String internalName, //
|
String internalName, //
|
||||||
String fragmentClassName, //
|
String fragmentClassName, //
|
||||||
int pluginName, //
|
int pluginName, //
|
||||||
int pluginShortName) {
|
int pluginShortName, //
|
||||||
super(new PluginDescription()
|
PumpType pumpType) {
|
||||||
.mainType(PluginType.PUMP)
|
this(pumpDriverInterface, //
|
||||||
.fragmentClass(fragmentClassName)
|
internalName, //
|
||||||
.pluginName(pluginName)
|
new PluginDescription() //
|
||||||
.shortName(pluginShortName)
|
.mainType(PluginType.PUMP) //
|
||||||
|
.fragmentClass(fragmentClassName) //
|
||||||
|
.pluginName(pluginName) //
|
||||||
|
.shortName(pluginShortName), //
|
||||||
|
pumpType //
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpPluginAbstract(PumpDriverInterface pumpDriverInterface, //
|
||||||
|
String internalName, //
|
||||||
|
PluginDescription pluginDescription,
|
||||||
|
PumpType pumpType //
|
||||||
|
) {
|
||||||
|
super(pluginDescription);
|
||||||
|
|
||||||
this.pumpDriver = pumpDriverInterface;
|
this.pumpDriver = pumpDriverInterface;
|
||||||
this.pumpStatus = this.pumpDriver.getPumpStatusData();
|
|
||||||
this.internalName = internalName;
|
this.internalName = internalName;
|
||||||
|
|
||||||
|
initPumpStatusData();
|
||||||
|
|
||||||
|
PumpUtil.setPumpDescription(getPumpDescription(), pumpType);
|
||||||
|
|
||||||
|
this.pumpDriver.initDriver(this.pumpStatus, this.pumpDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,9 +99,49 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
|
||||||
return this.internalName;
|
return this.internalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void startPumpService();
|
|
||||||
|
|
||||||
protected abstract void stopPumpService();
|
public abstract void initPumpStatusData();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
Context context = MainApp.instance().getApplicationContext();
|
||||||
|
Intent intent = new Intent(context, getServiceClass());
|
||||||
|
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
|
||||||
|
MainApp.bus().register(this);
|
||||||
|
onStartCustomActions();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
Context context = MainApp.instance().getApplicationContext();
|
||||||
|
context.unbindService(serviceConnection);
|
||||||
|
|
||||||
|
MainApp.bus().unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we need to run any custom actions in onStart (triggering events, etc)
|
||||||
|
*/
|
||||||
|
public abstract void onStartCustomActions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service class (same one you did serviceConnection for)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Class getServiceClass();
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedParameters")
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusEvent(final EventAppExit e) {
|
||||||
|
MainApp.instance().getApplicationContext().unbindService(serviceConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public PumpStatus getPumpStatusData() {
|
public PumpStatus getPumpStatusData() {
|
||||||
|
@ -193,17 +266,12 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
|
||||||
|
|
||||||
|
|
||||||
public PumpDescription getPumpDescription() {
|
public PumpDescription getPumpDescription() {
|
||||||
return pumpDriver.getPumpDescription();
|
return pumpDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Short info for SMS, Wear etc
|
// Short info for SMS, Wear etc
|
||||||
|
|
||||||
|
|
||||||
public String shortStatus(boolean veryShort) {
|
|
||||||
return pumpDriver.shortStatus(veryShort);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isFakingTempsByExtendedBoluses() {
|
public boolean isFakingTempsByExtendedBoluses() {
|
||||||
return pumpDriver.isInitialized();
|
return pumpDriver.isInitialized();
|
||||||
}
|
}
|
||||||
|
@ -274,9 +342,6 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSONObject getJSONStatus(Profile profile, String profileName) {
|
public JSONObject getJSONStatus(Profile profile, String profileName) {
|
||||||
//if (!SP.getBoolean("virtualpump_uploadstatus", false)) {
|
|
||||||
// return null;
|
|
||||||
//}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if ((pumpStatus.lastConnection + 5 * 60 * 1000L) < System.currentTimeMillis()) {
|
if ((pumpStatus.lastConnection + 5 * 60 * 1000L) < System.currentTimeMillis()) {
|
||||||
|
@ -323,6 +388,36 @@ public abstract class PumpPluginAbstract extends PluginBase implements PumpInter
|
||||||
return pump;
|
return pump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME i18n, null checks: iob, TDD
|
||||||
|
@Override
|
||||||
|
public String shortStatus(boolean veryShort) {
|
||||||
|
String ret = "";
|
||||||
|
if (pumpStatus.lastConnection != 0) {
|
||||||
|
Long agoMsec = System.currentTimeMillis() - pumpStatus.lastConnection;
|
||||||
|
int agoMin = (int) (agoMsec / 60d / 1000d);
|
||||||
|
ret += "LastConn: " + agoMin + " min ago\n";
|
||||||
|
}
|
||||||
|
if (pumpStatus.lastBolusTime.getTime() != 0) {
|
||||||
|
ret += "LastBolus: " + DecimalFormatter.to2Decimal(pumpStatus.lastBolusAmount) + "U @" + //
|
||||||
|
android.text.format.DateFormat.format("HH:mm", pumpStatus.lastBolusTime) + "\n";
|
||||||
|
}
|
||||||
|
TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
if (activeTemp != null) {
|
||||||
|
ret += "Temp: " + activeTemp.toStringFull() + "\n";
|
||||||
|
}
|
||||||
|
ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis());
|
||||||
|
if (activeExtendedBolus != null) {
|
||||||
|
ret += "Extended: " + activeExtendedBolus.toString() + "\n";
|
||||||
|
}
|
||||||
|
if (!veryShort) {
|
||||||
|
ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " + pumpStatus.maxDailyTotalUnits + " U\n";
|
||||||
|
}
|
||||||
|
ret += "IOB: " + pumpStatus.iob + "U\n";
|
||||||
|
ret += "Reserv: " + DecimalFormatter.to0Decimal(pumpStatus.reservoirRemainingUnits) + "U\n";
|
||||||
|
ret += "Batt: " + pumpStatus.batteryRemaining + "\n";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Profile interface
|
// Profile interface
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Date;
|
||||||
import info.nightscout.androidaps.data.ProfileStore;
|
import info.nightscout.androidaps.data.ProfileStore;
|
||||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpStatusType;
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpStatusType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by andy on 4/28/18.
|
* Created by andy on 4/28/18.
|
||||||
|
@ -12,17 +13,34 @@ import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpStatusType;
|
||||||
|
|
||||||
public abstract class PumpStatus {
|
public abstract class PumpStatus {
|
||||||
|
|
||||||
|
// connection
|
||||||
public Date lastDataTime;
|
public Date lastDataTime;
|
||||||
public long lastConnection = 0L;
|
public long lastConnection = 0L;
|
||||||
|
|
||||||
|
// last bolus
|
||||||
public Date lastBolusTime;
|
public Date lastBolusTime;
|
||||||
|
public double lastBolusAmount;
|
||||||
|
|
||||||
|
// other pump settings
|
||||||
public String activeProfileName = "0";
|
public String activeProfileName = "0";
|
||||||
public double reservoirRemainingUnits = 0d;
|
public double reservoirRemainingUnits = 0d;
|
||||||
public String reservoirFullUnits = "???";
|
public String reservoirFullUnits = "???";
|
||||||
public int batteryRemaining = 0; // percent, so 0-100
|
public int batteryRemaining = 0; // percent, so 0-100
|
||||||
public String iob = "0";
|
|
||||||
|
// iob
|
||||||
|
public String iob = null;
|
||||||
|
|
||||||
|
// TDD
|
||||||
|
public Double dailyTotalUnits;
|
||||||
|
public String maxDailyTotalUnits;
|
||||||
|
|
||||||
|
|
||||||
protected PumpDescription pumpDescription;
|
protected PumpDescription pumpDescription;
|
||||||
public boolean validBasalRateProfileSelectedOnPump = true;
|
public boolean validBasalRateProfileSelectedOnPump = true;
|
||||||
|
|
||||||
|
public PumpType pumpType = PumpType.GenericAAPS;
|
||||||
|
|
||||||
|
|
||||||
public ProfileStore profileStore;
|
public ProfileStore profileStore;
|
||||||
public String units; // Constants.MGDL or Constants.MMOL
|
public String units; // Constants.MGDL or Constants.MMOL
|
||||||
public PumpStatusType pumpStatusType = PumpStatusType.Running;
|
public PumpStatusType pumpStatusType = PumpStatusType.Running;
|
||||||
|
@ -56,4 +74,12 @@ public abstract class PumpStatus {
|
||||||
|
|
||||||
public abstract void refreshConfiguration();
|
public abstract void refreshConfiguration();
|
||||||
|
|
||||||
|
|
||||||
|
public PumpType getPumpType() {
|
||||||
|
return pumpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPumpType(PumpType pumpType) {
|
||||||
|
this.pumpType = pumpType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,26 @@ public enum PumpCapability {
|
||||||
// grouped
|
// grouped
|
||||||
VirtualPump(Bolus, ExtendedBolus, TBR, BasalProfileSet, StoreCarbInfo), //
|
VirtualPump(Bolus, ExtendedBolus, TBR, BasalProfileSet, StoreCarbInfo), //
|
||||||
|
|
||||||
|
|
||||||
Bolus_TBR_Basal_Refill_Carb(Bolus, TBR, BasalProfileSet, Refill, StoreCarbInfo), //
|
Bolus_TBR_Basal_Refill_Carb(Bolus, TBR, BasalProfileSet, Refill, StoreCarbInfo), //
|
||||||
Bolus_Extended_TBR_Basal_Carb(Bolus, ExtendedBolus, TBR, BasalProfileSet, StoreCarbInfo), //
|
Bolus_Extended_TBR_Basal_Carb(Bolus, ExtendedBolus, TBR, BasalProfileSet, StoreCarbInfo), //
|
||||||
Bolus_Extended_TBR_Basal_Refill_Carb(Bolus, ExtendedBolus, TBR, BasalProfileSet, Refill, StoreCarbInfo), //
|
Bolus_Extended_TBR_Basal_Refill_Carb(Bolus, ExtendedBolus, TBR, BasalProfileSet, Refill, StoreCarbInfo), //
|
||||||
|
|
||||||
;
|
None;
|
||||||
|
|
||||||
PumpCapability[] children;
|
PumpCapability[] children;
|
||||||
|
|
||||||
|
|
||||||
PumpCapability()
|
PumpCapability() {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PumpCapability(PumpCapability...children)
|
PumpCapability(PumpCapability... children) {
|
||||||
{
|
|
||||||
this.children = children;
|
this.children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasCapability(PumpCapability capability)
|
public boolean hasCapability(PumpCapability capability) {
|
||||||
{
|
|
||||||
// we can only check presense of simple capabilities
|
// we can only check presense of simple capabilities
|
||||||
if (capability.children != null)
|
if (capability.children != null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -45,19 +43,16 @@ public enum PumpCapability {
|
||||||
if (this == capability)
|
if (this == capability)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (this.children!=null)
|
if (this.children != null) {
|
||||||
{
|
|
||||||
for (PumpCapability child : children) {
|
for (PumpCapability child : children) {
|
||||||
if (child == capability)
|
if (child == capability)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,68 +4,72 @@ package info.nightscout.androidaps.plugins.PumpCommon.defs;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.data.DoseSettings;
|
import info.nightscout.androidaps.plugins.PumpCommon.data.DoseSettings;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by andy on 02/05/2018.
|
* Created by andy on 02/05/2018.
|
||||||
*
|
* <p>
|
||||||
* Most of this defintions is intended for VirtualPump only, but they can be used by other plugins.
|
* Most of this defintions is intended for VirtualPump only, but they can be used by other plugins.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public enum PumpType {
|
public enum PumpType {
|
||||||
|
|
||||||
GenericAAPS("Generic AAPS", 0.1f, null, //
|
Unknown("Unknown Pump", 0.1f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10,30, 24*60, 0f, 500f), //
|
new DoseSettings(10, 30, 24 * 60, 0f, 200f), //
|
||||||
|
0.01f, 0.01f, null, PumpCapability.None),
|
||||||
|
|
||||||
|
GenericAAPS("Generic AAPS", 0.1f, null, //
|
||||||
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
|
PumpTempBasalType.Percent, //
|
||||||
|
new DoseSettings(10, 30, 24 * 60, 0f, 500f), //
|
||||||
0.01f, 0.01f, null, PumpCapability.VirtualPump), //
|
0.01f, 0.01f, null, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
// Cellnovo
|
// Cellnovo
|
||||||
|
|
||||||
Cellnovo1("Cellnovo", 0.05f, null, //
|
Cellnovo1("Cellnovo", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 24*60, 1f, null),
|
new DoseSettings(0.05f, 30, 24 * 60, 1f, null),
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(5,30, 24*60, 0f, 200f), //
|
new DoseSettings(5, 30, 24 * 60, 0f, 200f), //
|
||||||
0.05f, 0.05f, null, PumpCapability.VirtualPump), //
|
0.05f, 0.05f, null, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
// Accu-Chek
|
// Accu-Chek
|
||||||
|
|
||||||
AccuChekCombo("Accu-Chek Combo", 0.1f, null, //
|
AccuChekCombo("Accu-Chek Combo", 0.1f, null, //
|
||||||
new DoseSettings(0.1f, 15, 12*60, 0.1f), //
|
new DoseSettings(0.1f, 15, 12 * 60, 0.1f), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 12*60,0f, 500f), //
|
new DoseSettings(10, 15, 12 * 60, 0f, 500f), //
|
||||||
0.01f, 0.1f, DoseStepSize.ComboBasal, PumpCapability.Bolus_TBR_Basal_Refill_Carb), //
|
0.01f, 0.1f, DoseStepSize.ComboBasal, PumpCapability.Bolus_TBR_Basal_Refill_Carb), //
|
||||||
|
|
||||||
AccuChekSpirit("Accu-Chek Spirit", 0.1f, null, //
|
AccuChekSpirit("Accu-Chek Spirit", 0.1f, null, //
|
||||||
new DoseSettings(0.1f, 15, 12*60, 0.1f), //
|
new DoseSettings(0.1f, 15, 12 * 60, 0.1f), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 12*60,0f, 500f), //
|
new DoseSettings(10, 15, 12 * 60, 0f, 500f), //
|
||||||
0.01f, 0.1f, null, PumpCapability.VirtualPump), //
|
0.01f, 0.1f, null, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
|
|
||||||
// Animas
|
// Animas
|
||||||
AnimasVibe("Animas Vibe", 0.05f, null, // AnimasBolus?
|
AnimasVibe("Animas Vibe", 0.05f, null, // AnimasBolus?
|
||||||
new DoseSettings(0.05f, 30, 12*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 12 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10, 30, 24*60, 0f, 200f), //
|
new DoseSettings(10, 30, 24 * 60, 0f, 200f), //
|
||||||
0.025f, 5f, 0f, null, PumpCapability.VirtualPump), //
|
0.025f, 5f, 0f, null, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
AnimasPing("Animas Ping", AnimasVibe),
|
AnimasPing("Animas Ping", AnimasVibe),
|
||||||
|
|
||||||
// Dana
|
// Dana
|
||||||
DanaR("DanaR", 0.05f, null, //
|
DanaR("DanaR", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10f, 60, 24*60, 0f, 200f), //
|
new DoseSettings(10f, 60, 24 * 60, 0f, 200f), //
|
||||||
0.04f, 0.01f, null, PumpCapability.Bolus_Extended_TBR_Basal_Refill_Carb),
|
0.04f, 0.01f, null, PumpCapability.Bolus_Extended_TBR_Basal_Refill_Carb),
|
||||||
|
|
||||||
DanaRKorean("DanaR Korean", 0.05f, null, //
|
DanaRKorean("DanaR Korean", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10f, 60, 24*60, 0f, 200f), //
|
new DoseSettings(10f, 60, 24 * 60, 0f, 200f), //
|
||||||
0.1f, 0.01f, null, PumpCapability.Bolus_Extended_TBR_Basal_Refill_Carb),
|
0.1f, 0.01f, null, PumpCapability.Bolus_Extended_TBR_Basal_Refill_Carb),
|
||||||
|
|
||||||
DanaRS("DanaRS", DanaR),
|
DanaRS("DanaRS", DanaR),
|
||||||
|
@ -74,16 +78,16 @@ public enum PumpType {
|
||||||
|
|
||||||
// Insulet
|
// Insulet
|
||||||
Insulet_Omnipod("Insulet Omnipod", 0.05f, null, //
|
Insulet_Omnipod("Insulet Omnipod", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05f, 30, 12*60, 0f, 5.0f), // cannot exceed max basal rate 30u/hr
|
new DoseSettings(0.05f, 30, 12 * 60, 0f, 5.0f), // cannot exceed max basal rate 30u/hr
|
||||||
0.05f, 0.05f, null, PumpCapability.VirtualPump),
|
0.05f, 0.05f, null, PumpCapability.VirtualPump),
|
||||||
|
|
||||||
// Medtronic
|
// Medtronic
|
||||||
Minimed_512_712("Medtronic 512/712", 0.05f, null, //
|
Minimed_512_712("Medtronic 512/712", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05f, 30, 24*60, 0f, 35f), //
|
new DoseSettings(0.05f, 30, 24 * 60, 0f, 35f), //
|
||||||
0.05f, 0.05f, null, PumpCapability.VirtualPump), // TODO
|
0.05f, 0.05f, null, PumpCapability.VirtualPump), // TODO
|
||||||
|
|
||||||
Minimed_515_715("Medtronic 515/715", Minimed_512_712),
|
Minimed_515_715("Medtronic 515/715", Minimed_512_712),
|
||||||
|
@ -91,24 +95,24 @@ public enum PumpType {
|
||||||
Minimed_523_723("Medtronic 523/723", Minimed_512_712),
|
Minimed_523_723("Medtronic 523/723", Minimed_512_712),
|
||||||
|
|
||||||
Minimed_553_753_Revel("Medtronic 553/753 (Revel)", 0.05f, null, //
|
Minimed_553_753_Revel("Medtronic 553/753 (Revel)", 0.05f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05f, 30, 24*60, 0f, 35f), //
|
new DoseSettings(0.05f, 30, 24 * 60, 0f, 35f), //
|
||||||
0.025f, 0.025f, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPump), //
|
0.025f, 0.025f, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
Minimed_554_754_Veo("Medtronic 554/754 (Veo)", Minimed_553_753_Revel), // TODO
|
Minimed_554_754_Veo("Medtronic 554/754 (Veo)", Minimed_553_753_Revel), // TODO
|
||||||
|
|
||||||
Minimed_640G("Medtronic 640G", 0.025f, null, //
|
Minimed_640G("Medtronic 640G", 0.025f, null, //
|
||||||
new DoseSettings(0.05f, 30, 8*60, 0.05f), //
|
new DoseSettings(0.05f, 30, 8 * 60, 0.05f), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05f, 30, 24*60, 0f, 35f), //
|
new DoseSettings(0.05f, 30, 24 * 60, 0f, 35f), //
|
||||||
0.025f, 0.025f, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPump), //
|
0.025f, 0.025f, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPump), //
|
||||||
|
|
||||||
// Tandem
|
// Tandem
|
||||||
TandemTSlim("Tandem t:slim", 0.01f, null, //
|
TandemTSlim("Tandem t:slim", 0.01f, null, //
|
||||||
new DoseSettings(0.01f,15, 8*60, 0.4f) ,
|
new DoseSettings(0.01f, 15, 8 * 60, 0.4f),
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(1,15, 8*60, 0f, 250f), //
|
new DoseSettings(1, 15, 8 * 60, 0f, 250f), //
|
||||||
0.1f, 0.001f, null, PumpCapability.VirtualPump),
|
0.1f, 0.001f, null, PumpCapability.VirtualPump),
|
||||||
|
|
||||||
TandemTFlex("Tandem t:flex", TandemTSlim), //
|
TandemTFlex("Tandem t:flex", TandemTSlim), //
|
||||||
|
@ -129,10 +133,9 @@ public enum PumpType {
|
||||||
private PumpCapability pumpCapability;
|
private PumpCapability pumpCapability;
|
||||||
|
|
||||||
private PumpType parent;
|
private PumpType parent;
|
||||||
private static Map<String,PumpType> mapByDescription;
|
private static Map<String, PumpType> mapByDescription;
|
||||||
|
|
||||||
static
|
static {
|
||||||
{
|
|
||||||
mapByDescription = new HashMap<>();
|
mapByDescription = new HashMap<>();
|
||||||
|
|
||||||
for (PumpType pumpType : values()) {
|
for (PumpType pumpType : values()) {
|
||||||
|
@ -141,14 +144,12 @@ public enum PumpType {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PumpType(String description, PumpType parent)
|
PumpType(String description, PumpType parent) {
|
||||||
{
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
PumpType(String description, PumpType parent, PumpCapability pumpCapability)
|
PumpType(String description, PumpType parent, PumpCapability pumpCapability) {
|
||||||
{
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.pumpCapability = pumpCapability;
|
this.pumpCapability = pumpCapability;
|
||||||
|
@ -157,16 +158,14 @@ public enum PumpType {
|
||||||
PumpType(String description, float bolusSize, DoseStepSize specialBolusSize, //
|
PumpType(String description, float bolusSize, DoseStepSize specialBolusSize, //
|
||||||
DoseSettings extendedBolusSettings, //
|
DoseSettings extendedBolusSettings, //
|
||||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, //
|
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, //
|
||||||
float baseBasalMinValue, float baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability)
|
float baseBasalMinValue, float baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) {
|
||||||
{
|
|
||||||
this(description, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability);
|
this(description, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability);
|
||||||
}
|
}
|
||||||
|
|
||||||
PumpType(String description, float bolusSize, DoseStepSize specialBolusSize, //
|
PumpType(String description, float bolusSize, DoseStepSize specialBolusSize, //
|
||||||
DoseSettings extendedBolusSettings, //
|
DoseSettings extendedBolusSettings, //
|
||||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, //
|
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, //
|
||||||
float baseBasalMinValue, Float baseBasalMaxValue, float baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability)
|
float baseBasalMinValue, Float baseBasalMaxValue, float baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) {
|
||||||
{
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.bolusSize = bolusSize;
|
this.bolusSize = bolusSize;
|
||||||
this.specialBolusSize = specialBolusSize;
|
this.specialBolusSize = specialBolusSize;
|
||||||
|
@ -243,20 +242,15 @@ public enum PumpType {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isParentSet()
|
private boolean isParentSet() {
|
||||||
{
|
return this.parent != null;
|
||||||
return this.parent!=null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static PumpType getByDescription(String desc)
|
public static PumpType getByDescription(String desc) {
|
||||||
{
|
if (mapByDescription.containsKey(desc)) {
|
||||||
if (mapByDescription.containsKey(desc))
|
|
||||||
{
|
|
||||||
return mapByDescription.get(desc);
|
return mapByDescription.get(desc);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return PumpType.GenericAAPS;
|
return PumpType.GenericAAPS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,30 +258,28 @@ public enum PumpType {
|
||||||
|
|
||||||
public String getFullDescription(String i18nTemplate) {
|
public String getFullDescription(String i18nTemplate) {
|
||||||
|
|
||||||
String unit = getPumpTempBasalType()==PumpTempBasalType.Percent ? "%" : "";
|
String unit = getPumpTempBasalType() == PumpTempBasalType.Percent ? "%" : "";
|
||||||
|
|
||||||
DoseSettings eb = getExtendedBolusSettings();
|
DoseSettings eb = getExtendedBolusSettings();
|
||||||
DoseSettings tbr = getTbrSettings();
|
DoseSettings tbr = getTbrSettings();
|
||||||
|
|
||||||
return String.format(i18nTemplate, //
|
return String.format(i18nTemplate, //
|
||||||
getStep("" + getBolusSize(), getSpecialBolusSize()), //
|
getStep("" + getBolusSize(), getSpecialBolusSize()), //
|
||||||
eb.getStep(), eb.getDurationStep(), eb.getMaxDuration()/60, //
|
eb.getStep(), eb.getDurationStep(), eb.getMaxDuration() / 60, //
|
||||||
getStep(getBaseBasalRange(), getBaseBasalSpecialSteps()), //
|
getStep(getBaseBasalRange(), getBaseBasalSpecialSteps()), //
|
||||||
tbr.getMinDose() + unit + "-" + tbr.getMaxDose() + unit, tbr.getStep() + unit, tbr.getDurationStep(), tbr.getMaxDuration()/60);
|
tbr.getMinDose() + unit + "-" + tbr.getMaxDose() + unit, tbr.getStep() + unit, tbr.getDurationStep(), tbr.getMaxDuration() / 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getBaseBasalRange()
|
private String getBaseBasalRange() {
|
||||||
{
|
|
||||||
Float maxValue = getBaseBasalMaxValue();
|
Float maxValue = getBaseBasalMaxValue();
|
||||||
|
|
||||||
return maxValue==null ? "" + getBaseBasalMinValue() : getBaseBasalMinValue() + "-" + maxValue;
|
return maxValue == null ? "" + getBaseBasalMinValue() : getBaseBasalMinValue() + "-" + maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getStep(String step, DoseStepSize stepSize)
|
private String getStep(String step, DoseStepSize stepSize) {
|
||||||
{
|
if (stepSize != null)
|
||||||
if (stepSize!=null)
|
|
||||||
return step + " [" + stepSize.getDescription() + "] *";
|
return step + " [" + stepSize.getDescription() + "] *";
|
||||||
else
|
else
|
||||||
return "" + step;
|
return "" + step;
|
||||||
|
@ -295,9 +287,8 @@ public enum PumpType {
|
||||||
|
|
||||||
|
|
||||||
public boolean hasExtendedBasals() {
|
public boolean hasExtendedBasals() {
|
||||||
return ((getBaseBasalSpecialSteps() !=null) || (getSpecialBolusSize() != null));
|
return ((getBaseBasalSpecialSteps() != null) || (getSpecialBolusSize() != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.defs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum RileyLinkTargetDevice {
|
||||||
|
MedtronicPump, //
|
||||||
|
Omnipod, //
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.dialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface RefreshableInterface {
|
||||||
|
|
||||||
|
void refreshData();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,360 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.dialog;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.le.BluetoothLeScanner;
|
||||||
|
import android.bluetooth.le.ScanCallback;
|
||||||
|
import android.bluetooth.le.ScanFilter;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
|
import android.bluetooth.le.ScanSettings;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.gxwtech.roundtrip2.RT2Const;
|
||||||
|
import com.gxwtech.roundtrip2.util.LocationHelper;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicConst;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
|
|
||||||
|
public class RileyLinkBLEScanActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkBLEScanActivity.class);
|
||||||
|
private final static String TAG = "RileyLinkScan";
|
||||||
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
private BluetoothLeScanner mLEScanner;
|
||||||
|
private LeDeviceListAdapter mLeDeviceListAdapter;
|
||||||
|
public boolean mScanning;
|
||||||
|
private Handler mHandler;
|
||||||
|
public Snackbar snackbar;
|
||||||
|
public ScanSettings settings;
|
||||||
|
public List<ScanFilter> filters;
|
||||||
|
public ListView listBTScan;
|
||||||
|
public Toolbar toolbarBTScan;
|
||||||
|
public Context mContext = this;
|
||||||
|
|
||||||
|
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 30241; // arbitrary.
|
||||||
|
private static final int REQUEST_ENABLE_BT = 30242; // arbitrary
|
||||||
|
// Stops scanning after 10 seconds.
|
||||||
|
private static final long SCAN_PERIOD = 30000;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.rileylink_scan_activity);
|
||||||
|
|
||||||
|
// Initializes Bluetooth adapter.
|
||||||
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
mHandler = new Handler();
|
||||||
|
|
||||||
|
mLeDeviceListAdapter = new LeDeviceListAdapter();
|
||||||
|
listBTScan = (ListView) findViewById(R.id.rileylink_listBTScan);
|
||||||
|
listBTScan.setAdapter(mLeDeviceListAdapter);
|
||||||
|
listBTScan.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
TextView textview = (TextView) view.findViewById(R.id.rileylink_device_address);
|
||||||
|
String bleAddress = textview.getText().toString();
|
||||||
|
|
||||||
|
//SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
|
SP.putString(MedtronicConst.Prefs.RileyLinkAddress, bleAddress);
|
||||||
|
|
||||||
|
//Notify that we have a new rileylinkAddressKey
|
||||||
|
RileyLinkUtil.sendBroadcastMessage(RT2Const.local.INTENT_NEW_rileylinkAddressKey);
|
||||||
|
|
||||||
|
Log.d(TAG, "New rileylinkAddressKey: " + bleAddress);
|
||||||
|
|
||||||
|
//Notify that we have a new pumpIDKey
|
||||||
|
RileyLinkUtil.sendBroadcastMessage(RT2Const.local.INTENT_NEW_pumpIDKey);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbarBTScan = (Toolbar) findViewById(R.id.rileylink_toolbarBTScan);
|
||||||
|
toolbarBTScan.setTitle(R.string.rileylink_scanner_title);
|
||||||
|
setSupportActionBar(toolbarBTScan);
|
||||||
|
|
||||||
|
snackbar = Snackbar.make(findViewById(R.id.RileyLinkScan), "Scanning...", Snackbar.LENGTH_INDEFINITE);
|
||||||
|
snackbar.setAction("STOP", new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
scanLeDevice(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
startScanBLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
scanLeDevice(false);
|
||||||
|
mLeDeviceListAdapter.clear();
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_rileylink_ble_scan, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.rileylink_miScan:
|
||||||
|
startScanBLE();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void startScanBLE() {
|
||||||
|
// https://developer.android.com/training/permissions/requesting.html
|
||||||
|
// http://developer.radiusnetworks.com/2015/09/29/is-your-beacon-app-ready-for-android-6.html
|
||||||
|
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||||
|
Toast.makeText(this, "R.string.ble_not_supported", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// Use this check to determine whether BLE is supported on the device. Then
|
||||||
|
// you can selectively disable BLE-related features.
|
||||||
|
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
//your code that requires permission
|
||||||
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures Bluetooth is available on the device and it is enabled. If not,
|
||||||
|
// displays a dialog requesting user permission to enable Bluetooth.
|
||||||
|
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||||||
|
Toast.makeText(this, "R.string.ble_not_enabled", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
// Will request that GPS be enabled for devices running Marshmallow or newer.
|
||||||
|
LocationHelper.requestLocationForBluetooth(this);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||||
|
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||||
|
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
|
||||||
|
filters = new ArrayList<ScanFilter>();
|
||||||
|
|
||||||
|
scanLeDevice(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (requestCode == REQUEST_ENABLE_BT) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
// User allowed Bluetooth to turn on
|
||||||
|
} else if (resultCode == RESULT_CANCELED) {
|
||||||
|
// Error, or user said "NO"
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void scanLeDevice(final boolean enable) {
|
||||||
|
if (enable) {
|
||||||
|
// Stops scanning after a pre-defined scan period.
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mScanning = false;
|
||||||
|
mLEScanner.stopScan(mScanCallback);
|
||||||
|
Log.d(TAG, "scanLeDevice: Scanning Stop");
|
||||||
|
//Toast.makeText(mContext, "Scanning finished", Toast.LENGTH_SHORT).show();
|
||||||
|
snackbar.dismiss();
|
||||||
|
}
|
||||||
|
}, SCAN_PERIOD);
|
||||||
|
|
||||||
|
mScanning = true;
|
||||||
|
mLEScanner.startScan(mScanCallback);
|
||||||
|
Log.d(TAG, "scanLeDevice: Scanning Start");
|
||||||
|
//Toast.makeText(this, "Scanning", Toast.LENGTH_SHORT).show();
|
||||||
|
snackbar.show();
|
||||||
|
} else {
|
||||||
|
mScanning = false;
|
||||||
|
mLEScanner.stopScan(mScanCallback);
|
||||||
|
|
||||||
|
Log.d(TAG, "scanLeDevice: Scanning Stop");
|
||||||
|
//Toast.makeText(this, "Scanning finished", Toast.LENGTH_SHORT).show();
|
||||||
|
snackbar.dismiss();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ScanCallback mScanCallback = new ScanCallback() {
|
||||||
|
@Override
|
||||||
|
public void onScanResult(int callbackType, ScanResult result) {
|
||||||
|
final BluetoothDevice device = result.getDevice();
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (device.getName() != null && device.getName().length() > 0) {
|
||||||
|
mLeDeviceListAdapter.addDevice(device);
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
Log.d(TAG, "Found BLE" + device.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchScanResults(final List<ScanResult> results) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
for (ScanResult result : results) {
|
||||||
|
BluetoothDevice device = result.getDevice();
|
||||||
|
if (device.getName() != null && device.getName().length() > 0) {
|
||||||
|
mLeDeviceListAdapter.addDevice(device);
|
||||||
|
Log.d(TAG, "Found BLE" + result.toString());
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Found BLE, but name appears to be missing. Ignoring. " + device.getAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanFailed(int errorCode) {
|
||||||
|
Log.e("Scan Failed", "Error Code: " + errorCode);
|
||||||
|
Toast.makeText(mContext, "Scan Failed " + errorCode, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private class LeDeviceListAdapter extends BaseAdapter {
|
||||||
|
private ArrayList<BluetoothDevice> mLeDevices;
|
||||||
|
private LayoutInflater mInflator;
|
||||||
|
|
||||||
|
|
||||||
|
public LeDeviceListAdapter() {
|
||||||
|
super();
|
||||||
|
mLeDevices = new ArrayList<>();
|
||||||
|
mInflator = RileyLinkBLEScanActivity.this.getLayoutInflater();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addDevice(BluetoothDevice device) {
|
||||||
|
if (!mLeDevices.contains(device)) {
|
||||||
|
mLeDevices.add(device);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BluetoothDevice getDevice(int position) {
|
||||||
|
return mLeDevices.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
mLeDevices.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mLeDevices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int i) {
|
||||||
|
return mLeDevices.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int i) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||||
|
ViewHolder viewHolder;
|
||||||
|
// General ListView optimization code.
|
||||||
|
if (view == null) {
|
||||||
|
view = mInflator.inflate(R.layout.rileylink_scan_item, null);
|
||||||
|
viewHolder = new ViewHolder();
|
||||||
|
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.rileylink_device_address);
|
||||||
|
viewHolder.deviceName = (TextView) view.findViewById(R.id.rileylink_device_name);
|
||||||
|
view.setTag(viewHolder);
|
||||||
|
} else {
|
||||||
|
viewHolder = (ViewHolder) view.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothDevice device = mLeDevices.get(i);
|
||||||
|
String deviceName = device.getName();
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
|
if (SP.getString(MedtronicConst.Prefs.RileyLinkAddress, "").compareTo(device.getAddress()) == 0) {
|
||||||
|
//viewHolder.deviceName.setTextColor(getColor(R.color.secondary_text_light));
|
||||||
|
//viewHolder.deviceAddress.setTextColor(getColor(R.color.secondary_text_light));
|
||||||
|
deviceName += " (" + getResources().getString(R.string.rileylink_scanner_selected_device) + ")";
|
||||||
|
}
|
||||||
|
viewHolder.deviceName.setText(deviceName);
|
||||||
|
viewHolder.deviceAddress.setText(device.getAddress());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class ViewHolder {
|
||||||
|
TextView deviceName;
|
||||||
|
TextView deviceAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.dialog;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.RileyLinkTargetDevice;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkSettingsTab1 extends Fragment implements RefreshableInterface {
|
||||||
|
|
||||||
|
TextView connectionStatus;
|
||||||
|
TextView configuredAddress;
|
||||||
|
TextView connectedDevice;
|
||||||
|
TextView connectionError;
|
||||||
|
TextView deviceType;
|
||||||
|
TextView deviceModel;
|
||||||
|
TextView serialNumber;
|
||||||
|
TextView pumpFrequency;
|
||||||
|
TextView lastUsedFrequency;
|
||||||
|
TextView lastDeviceContact;
|
||||||
|
|
||||||
|
RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
|
||||||
|
MedtronicPumpStatus medtronicPumpStatus;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.rileylink_settings_tab1, container, false);
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData();
|
||||||
|
|
||||||
|
this.connectionStatus = (TextView) getActivity().findViewById(R.id.rls_t1_connection_status);
|
||||||
|
this.configuredAddress = (TextView) getActivity().findViewById(R.id.rls_t1_configured_address);
|
||||||
|
this.connectedDevice = (TextView) getActivity().findViewById(R.id.rls_t1_connected_device);
|
||||||
|
this.connectionError = (TextView) getActivity().findViewById(R.id.rls_t1_connection_error);
|
||||||
|
this.deviceType = (TextView) getActivity().findViewById(R.id.rls_t1_device_type);
|
||||||
|
this.deviceModel = (TextView) getActivity().findViewById(R.id.rls_t1_device_model);
|
||||||
|
this.serialNumber = (TextView) getActivity().findViewById(R.id.rls_t1_serial_number);
|
||||||
|
this.pumpFrequency = (TextView) getActivity().findViewById(R.id.rls_t1_pump_frequency);
|
||||||
|
this.lastUsedFrequency = (TextView) getActivity().findViewById(R.id.rls_t1_last_used_frequency);
|
||||||
|
this.lastDeviceContact = (TextView) getActivity().findViewById(R.id.rls_t1_last_device_contact);
|
||||||
|
|
||||||
|
|
||||||
|
// 7-12
|
||||||
|
int[] ids = {R.id.rls_t1_tv02, R.id.rls_t1_tv03, R.id.rls_t1_tv04, R.id.rls_t1_tv05, R.id.rls_t1_tv07, //
|
||||||
|
R.id.rls_t1_tv08, R.id.rls_t1_tv09, R.id.rls_t1_tv10, R.id.rls_t1_tv11, R.id.rls_t1_tv12};
|
||||||
|
|
||||||
|
for (int id : ids) {
|
||||||
|
|
||||||
|
TextView tv = (TextView) getActivity().findViewById(id);
|
||||||
|
tv.setText(tv.getText() + ":");
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void refreshData() {
|
||||||
|
|
||||||
|
// FIXME i18n
|
||||||
|
this.connectionStatus.setText(rileyLinkServiceData.serviceState.name());
|
||||||
|
this.configuredAddress.setText(rileyLinkServiceData.rileylinkAddress);
|
||||||
|
// FIXME
|
||||||
|
this.connectedDevice.setText("???");
|
||||||
|
// FIXME i18n
|
||||||
|
|
||||||
|
|
||||||
|
this.connectionError.setText(rileyLinkServiceData.errorCode == null ? "-" : rileyLinkServiceData.errorCode.name());
|
||||||
|
|
||||||
|
this.medtronicPumpStatus = RileyLinkUtil.getPumpStatus();
|
||||||
|
|
||||||
|
if (medtronicPumpStatus != null) {
|
||||||
|
this.deviceType.setText(RileyLinkTargetDevice.MedtronicPump.name());
|
||||||
|
this.deviceModel.setText(medtronicPumpStatus.pumpType.getDescription());
|
||||||
|
this.serialNumber.setText(medtronicPumpStatus.serialNumber);
|
||||||
|
this.pumpFrequency.setText(medtronicPumpStatus.pumpFrequency);
|
||||||
|
|
||||||
|
if (rileyLinkServiceData.lastGoodFrequency != null)
|
||||||
|
this.lastUsedFrequency.setText(rileyLinkServiceData.lastGoodFrequency.toString());
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
if (medtronicPumpStatus.lastConnection == 0)
|
||||||
|
this.lastDeviceContact.setText("" + medtronicPumpStatus.lastDataTime);
|
||||||
|
else
|
||||||
|
this.lastDeviceContact.setText("Never");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.dialog;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkSettingsTab2 extends Fragment implements RefreshableInterface {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.rileylink_settings_tab2, container, false);
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.dialog;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.design.widget.TabLayout;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
|
||||||
|
|
||||||
|
public class RileylinkSettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
|
||||||
|
@BindView(R.id.rls_t1_connection_status)
|
||||||
|
TextView connectionStatus;
|
||||||
|
|
||||||
|
@BindView(R.id.rls_t1_configured_address)
|
||||||
|
TextView configuredAddress;
|
||||||
|
|
||||||
|
@BindView(R.id.rls_t1_connected_device)
|
||||||
|
TextView connectedDevice;
|
||||||
|
|
||||||
|
@BindView(R.id.rls_t1_connection_error)
|
||||||
|
TextView connectionError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link android.support.v4.view.PagerAdapter} that will provide
|
||||||
|
* fragments for each of the sections. We use a
|
||||||
|
* {@link FragmentPagerAdapter} derivative, which will keep every
|
||||||
|
* loaded fragment in memory. If this becomes too memory intensive, it
|
||||||
|
* may be best to switch to a
|
||||||
|
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
|
||||||
|
*/
|
||||||
|
private SectionsPagerAdapter mSectionsPagerAdapter;
|
||||||
|
private FloatingActionButton floatingActionButton;
|
||||||
|
private TabLayout tabLayout;
|
||||||
|
|
||||||
|
RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ViewPager} that will host the section contents.
|
||||||
|
*/
|
||||||
|
private ViewPager mViewPager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.rileylink_settings);
|
||||||
|
|
||||||
|
|
||||||
|
// Create the adapter that will return a fragment for each of the three
|
||||||
|
// primary sections of the activity.
|
||||||
|
|
||||||
|
|
||||||
|
// Set up the ViewPager with the sections adapter.
|
||||||
|
mViewPager = (ViewPager) findViewById(R.id.rileylink_settings_container);
|
||||||
|
//mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||||
|
setupViewPager(mViewPager);
|
||||||
|
|
||||||
|
tabLayout = (TabLayout) findViewById(R.id.rileylink_settings_tabs);
|
||||||
|
tabLayout.setupWithViewPager(mViewPager);
|
||||||
|
|
||||||
|
|
||||||
|
floatingActionButton = (FloatingActionButton) findViewById(R.id.rileylink_settings_fab);
|
||||||
|
floatingActionButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
|
||||||
|
RefreshableInterface selectableInterface = (RefreshableInterface) mSectionsPagerAdapter.getItem(tabLayout.getSelectedTabPosition());
|
||||||
|
selectableInterface.refreshData();
|
||||||
|
|
||||||
|
//refreshData(tabLayout.getSelectedTabPosition());
|
||||||
|
|
||||||
|
//Toast.makeText(getApplicationContext(), "Test pos: " + tabLayout.getSelectedTabPosition(), Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.connectionStatus = (TextView) findViewById(R.id.rls_t1_connection_status);
|
||||||
|
this.configuredAddress = (TextView) findViewById(R.id.rls_t1_configured_address);
|
||||||
|
this.connectedDevice = (TextView) findViewById(R.id.rls_t1_connected_device);
|
||||||
|
this.connectionError = (TextView) findViewById(R.id.rls_t1_connection_error);
|
||||||
|
|
||||||
|
|
||||||
|
rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData();
|
||||||
|
|
||||||
|
// // 7-12
|
||||||
|
// int[] ids = {R.id.rls_t1_tv02, R.id.rls_t1_tv03, R.id.rls_t1_tv04, R.id.rls_t1_tv05, R.id.rls_t1_tv07, //
|
||||||
|
// R.id.rls_t1_tv08, R.id.rls_t1_tv09, R.id.rls_t1_tv10, R.id.rls_t1_tv11, R.id.rls_t1_tv12};
|
||||||
|
//
|
||||||
|
// for (int id : ids) {
|
||||||
|
//
|
||||||
|
// TextView tv = (TextView) findViewById(id);
|
||||||
|
// tv.setText(tv.getText() + ":");
|
||||||
|
// }
|
||||||
|
|
||||||
|
//refreshData(0);
|
||||||
|
//refreshData(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void refreshData(int position) {
|
||||||
|
if (position == 0) {
|
||||||
|
// FIXME i18n
|
||||||
|
this.connectionStatus.setText(rileyLinkServiceData.serviceState.name());
|
||||||
|
this.configuredAddress.setText(rileyLinkServiceData.rileylinkAddress);
|
||||||
|
// FIXME
|
||||||
|
this.connectedDevice.setText("???");
|
||||||
|
// FIXME i18n
|
||||||
|
this.connectionError.setText(rileyLinkServiceData.errorCode.name());
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setupViewPager(ViewPager pager) {
|
||||||
|
|
||||||
|
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
|
||||||
|
|
||||||
|
mSectionsPagerAdapter.addFragment(new RileyLinkSettingsTab1(), MainApp.gs(R.string.rileylink_settings_tab1));
|
||||||
|
mSectionsPagerAdapter.addFragment(new RileyLinkSettingsTab2(), MainApp.gs(R.string.rileylink_settings_tab2));
|
||||||
|
//mSectionsPagerAdapter.addFragment(new RileyLinkSettingsTab3(), "Tab 3");
|
||||||
|
|
||||||
|
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
|
||||||
|
* one of the sections/tabs/pages.
|
||||||
|
*/
|
||||||
|
public class SectionsPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
|
List<Fragment> fragmentList = new ArrayList<>();
|
||||||
|
List<String> fragmentTitle = new ArrayList<>();
|
||||||
|
int lastSelectedPosition = 0;
|
||||||
|
|
||||||
|
public SectionsPagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
this.lastSelectedPosition = position;
|
||||||
|
return fragmentList.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
// Show 3 total pages.
|
||||||
|
return fragmentList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFragment(Fragment fragment, String title) {
|
||||||
|
this.fragmentList.add(fragment);
|
||||||
|
this.fragmentTitle.add(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
return fragmentTitle.get(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
|
||||||
public abstract class PumpDriverAbstract implements PumpDriverInterface {
|
public abstract class PumpDriverAbstract implements PumpDriverInterface {
|
||||||
|
|
||||||
protected PumpDescription pumpDescription = new PumpDescription();
|
protected PumpDescription pumpDescription;
|
||||||
protected PumpStatus pumpStatusData;
|
protected PumpStatus pumpStatusData;
|
||||||
|
|
||||||
protected static final PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult()
|
protected static final PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult()
|
||||||
|
@ -23,15 +23,23 @@ public abstract class PumpDriverAbstract implements PumpDriverInterface {
|
||||||
protected static final PumpEnactResult OPERATION_NOT_YET_SUPPORTED = new PumpEnactResult()
|
protected static final PumpEnactResult OPERATION_NOT_YET_SUPPORTED = new PumpEnactResult()
|
||||||
.success(false).enacted(false).comment(MainApp.gs(R.string.pump_operation_not_yet_supported_by_pump));
|
.success(false).enacted(false).comment(MainApp.gs(R.string.pump_operation_not_yet_supported_by_pump));
|
||||||
|
|
||||||
|
protected PumpDriverAbstract() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initDriver(PumpStatus pumpStatus, PumpDescription pumpDescription) {
|
||||||
|
this.pumpDescription = pumpDescription;
|
||||||
|
this.pumpStatusData = pumpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String deviceID() {
|
public String deviceID() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PumpStatus getPumpStatusData()
|
public PumpStatus getPumpStatusData() {
|
||||||
{
|
|
||||||
return this.pumpStatusData;
|
return this.pumpStatusData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.PumpCommon.driver;
|
package info.nightscout.androidaps.plugins.PumpCommon.driver;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
|
||||||
public interface PumpDriverInterface extends PumpInterface {
|
public interface PumpDriverInterface extends PumpInterface {
|
||||||
|
|
||||||
|
void initDriver(PumpStatus pumpStatus, PumpDescription pumpDescription);
|
||||||
|
|
||||||
PumpStatus getPumpStatusData();
|
PumpStatus getPumpStatusData();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RFSpy;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.FrequencyScanResults;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.FrequencyTrial;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RFSpyResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RLMessage;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RLMessageType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RadioPacket;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RadioResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PumpMessage;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicConst;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is abstract class for RileyLink Communication, this one needs to be extended by specific "Pump" class.
|
||||||
|
* <p>
|
||||||
|
* Created by andy on 5/10/18.
|
||||||
|
*/
|
||||||
|
public abstract class RileyLinkCommunicationManager {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkCommunicationManager.class);
|
||||||
|
|
||||||
|
protected final RFSpy rfspy;
|
||||||
|
protected final Context context;
|
||||||
|
|
||||||
|
private double[] scanFrequencies;
|
||||||
|
|
||||||
|
protected int receiverDeviceAwakeForMinutes = 6; // override this in constructor of specific implementation
|
||||||
|
protected String receiverDeviceID; // String representation of receiver device (ex. Pump (xxxxxx) or Pod (yyyyyy))
|
||||||
|
protected long lastGoodReceiverCommunicationTime = 0;
|
||||||
|
protected PumpStatus pumpStatus;
|
||||||
|
|
||||||
|
// internal flag
|
||||||
|
private boolean showPumpMessages = true;
|
||||||
|
|
||||||
|
public RileyLinkCommunicationManager(Context context, RFSpy rfspy, double[] scanFrequencies) {
|
||||||
|
this.context = context;
|
||||||
|
this.rfspy = rfspy;
|
||||||
|
this.scanFrequencies = scanFrequencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage sendAndListen(RLMessage msg) {
|
||||||
|
return sendAndListen(msg, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All pump communications go through this function.
|
||||||
|
protected PumpMessage sendAndListen(RLMessage msg, int timeout_ms) {
|
||||||
|
|
||||||
|
if (showPumpMessages) {
|
||||||
|
LOG.info("Sent:" + ByteUtil.shortHexString(msg.getTxData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), timeout_ms);
|
||||||
|
PumpMessage rval = new PumpMessage(resp.getRadioResponse().getPayload());
|
||||||
|
if (rval.isValid()) {
|
||||||
|
// Mark this as the last time we heard from the pump.
|
||||||
|
rememberLastGoodPumpCommunicationTime();
|
||||||
|
}
|
||||||
|
if (showPumpMessages) {
|
||||||
|
LOG.info("Received:" + ByteUtil.shortHexString(resp.getRadioResponse().getPayload()));
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void tryoutPacket(byte[] pkt) {
|
||||||
|
sendAndListen(makeRLMessage(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO we might need to fix this. Maybe make pump awake for shorter time (battery factor for pump) - Andy
|
||||||
|
public void wakeup(int duration_minutes) {
|
||||||
|
// If it has been longer than n minutes, do wakeup. Otherwise assume pump is still awake.
|
||||||
|
// **** FIXME: this wakeup doesn't seem to work well... must revisit
|
||||||
|
receiverDeviceAwakeForMinutes = duration_minutes;
|
||||||
|
|
||||||
|
long lastGoodPlus = getLastGoodReceiverCommunicationTime() + (receiverDeviceAwakeForMinutes * 60 * 1000);
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() > lastGoodPlus) {
|
||||||
|
LOG.info("Waking pump...");
|
||||||
|
RLMessage msg = makeRLMessage(RLMessageType.PowerOn, new byte[]{(byte) duration_minutes});
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), (byte) 0, (byte) 200, (byte) 0, (byte) 0, 15000, (byte) 0);
|
||||||
|
LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
} else {
|
||||||
|
LOG.debug("Last pump communication was recent, not waking pump.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setRadioFrequencyForPump(double freqMHz) {
|
||||||
|
rfspy.setBaseFrequency(freqMHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double tuneForPump() {
|
||||||
|
return scanForPump(scanFrequencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double scanForPump(double[] frequencies) {
|
||||||
|
LOG.info("Scanning for receiver ({})", receiverDeviceID);
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
FrequencyScanResults results = new FrequencyScanResults();
|
||||||
|
|
||||||
|
for (int i = 0; i < frequencies.length; i++) {
|
||||||
|
int tries = 3;
|
||||||
|
FrequencyTrial trial = new FrequencyTrial();
|
||||||
|
trial.frequencyMHz = frequencies[i];
|
||||||
|
rfspy.setBaseFrequency(frequencies[i]);
|
||||||
|
|
||||||
|
int sumRSSI = 0;
|
||||||
|
for (int j = 0; j < tries; j++) {
|
||||||
|
RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData);
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), (byte) 0, (byte) 0, (byte) 0, (byte) 0, rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS, (byte) 0);
|
||||||
|
if (resp.wasTimeout()) {
|
||||||
|
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
|
||||||
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
RadioResponse radioResponse = new RadioResponse(resp.getRaw());
|
||||||
|
if (radioResponse.isValid()) {
|
||||||
|
sumRSSI += radioResponse.rssi;
|
||||||
|
trial.successes++;
|
||||||
|
} else {
|
||||||
|
LOG.warn("Failed to parse radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("scanForPump: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
}
|
||||||
|
trial.tries++;
|
||||||
|
}
|
||||||
|
sumRSSI += -99.0 * (trial.tries - trial.successes);
|
||||||
|
trial.averageRSSI = (double) (sumRSSI) / (double) (trial.tries);
|
||||||
|
results.trials.add(trial);
|
||||||
|
}
|
||||||
|
results.sort(); // sorts in ascending order
|
||||||
|
LOG.debug("Sorted scan results:");
|
||||||
|
for (int k = 0; k < results.trials.size(); k++) {
|
||||||
|
FrequencyTrial one = results.trials.get(k);
|
||||||
|
LOG.debug("Scan Result[{}]: Freq={}, avg RSSI = {}", k, one.frequencyMHz, one.averageRSSI);
|
||||||
|
}
|
||||||
|
FrequencyTrial bestTrial = results.trials.get(results.trials.size() - 1);
|
||||||
|
results.bestFrequencyMHz = bestTrial.frequencyMHz;
|
||||||
|
if (bestTrial.successes > 0) {
|
||||||
|
rfspy.setBaseFrequency(results.bestFrequencyMHz);
|
||||||
|
return results.bestFrequencyMHz;
|
||||||
|
} else {
|
||||||
|
LOG.error("No pump response during scan.");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RLMessage makeRLMessage(RLMessageType type) {
|
||||||
|
return makeRLMessage(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract RLMessage makeRLMessage(RLMessageType type, byte[] data);
|
||||||
|
|
||||||
|
public abstract RLMessage makeRLMessage(byte[] data);
|
||||||
|
|
||||||
|
|
||||||
|
private int tune_tryFrequency(double freqMHz) {
|
||||||
|
rfspy.setBaseFrequency(freqMHz);
|
||||||
|
RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData);
|
||||||
|
RadioPacket pkt = new RadioPacket(msg.getTxData());
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, rfspy.EXPECTED_MAX_BLUETOOTH_LATENCY_MS, (byte) 0);
|
||||||
|
if (resp.wasTimeout()) {
|
||||||
|
LOG.warn("tune_tryFrequency: no pump response at frequency {}", freqMHz);
|
||||||
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
RadioResponse radioResponse = new RadioResponse(resp.getRaw());
|
||||||
|
if (radioResponse.isValid()) {
|
||||||
|
LOG.warn("tune_tryFrequency: saw response level {} at frequency {}", radioResponse.rssi, freqMHz);
|
||||||
|
return radioResponse.rssi;
|
||||||
|
} else {
|
||||||
|
LOG.warn("tune_tryFrequency: invalid radio response:" + ByteUtil.shortHexString(radioResponse.getPayload()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double quickTuneForPump(double startFrequencyMHz) {
|
||||||
|
double betterFrequency = startFrequencyMHz;
|
||||||
|
double stepsize = 0.05;
|
||||||
|
for (int tries = 0; tries < 4; tries++) {
|
||||||
|
double evenBetterFrequency = quickTunePumpStep(betterFrequency, stepsize);
|
||||||
|
if (evenBetterFrequency == 0.0) {
|
||||||
|
// could not see the pump at all.
|
||||||
|
// Try again at larger step size
|
||||||
|
stepsize += 0.05;
|
||||||
|
} else {
|
||||||
|
if ((int) (evenBetterFrequency * 100) == (int) (betterFrequency * 100)) {
|
||||||
|
// value did not change, so we're done.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
betterFrequency = evenBetterFrequency; // and go again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (betterFrequency == 0.0) {
|
||||||
|
// we've failed... caller should try a full scan for pump
|
||||||
|
LOG.error("quickTuneForPump: failed to find pump");
|
||||||
|
} else {
|
||||||
|
rfspy.setBaseFrequency(betterFrequency);
|
||||||
|
if (betterFrequency != startFrequencyMHz) {
|
||||||
|
LOG.info("quickTuneForPump: new frequency is {}MHz", betterFrequency);
|
||||||
|
} else {
|
||||||
|
LOG.info("quickTuneForPump: pump frequency is the same: {}MHz", startFrequencyMHz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return betterFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double quickTunePumpStep(double startFrequencyMHz, double stepSizeMHz) {
|
||||||
|
LOG.info("Doing quick radio tune for receiver ({})", receiverDeviceID);
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
int startRssi = tune_tryFrequency(startFrequencyMHz);
|
||||||
|
double lowerFrequency = startFrequencyMHz - stepSizeMHz;
|
||||||
|
int lowerRssi = tune_tryFrequency(lowerFrequency);
|
||||||
|
double higherFrequency = startFrequencyMHz + stepSizeMHz;
|
||||||
|
int higherRssi = tune_tryFrequency(higherFrequency);
|
||||||
|
if ((higherRssi == 0.0) && (lowerRssi == 0.0) && (startRssi == 0.0)) {
|
||||||
|
// we can't see the pump at all...
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if (higherRssi > startRssi) {
|
||||||
|
// need to move higher
|
||||||
|
return higherFrequency;
|
||||||
|
} else if (lowerRssi > startRssi) {
|
||||||
|
// need to move lower.
|
||||||
|
return lowerFrequency;
|
||||||
|
}
|
||||||
|
return startFrequencyMHz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void rememberLastGoodPumpCommunicationTime() {
|
||||||
|
lastGoodReceiverCommunicationTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
SP.putLong(MedtronicConst.Prefs.LastGoodPumpCommunicationTime, lastGoodReceiverCommunicationTime);
|
||||||
|
pumpStatus.setLastDataTimeToNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLastGoodReceiverCommunicationTime() {
|
||||||
|
// If we have a value of zero, we need to load from prefs.
|
||||||
|
if (lastGoodReceiverCommunicationTime == 0L) {
|
||||||
|
lastGoodReceiverCommunicationTime = SP.getLong(MedtronicConst.Prefs.LastGoodPumpCommunicationTime, 0L);
|
||||||
|
// Might still be zero, but that's fine.
|
||||||
|
}
|
||||||
|
double minutesAgo = (System.currentTimeMillis() - lastGoodReceiverCommunicationTime) / (1000.0 * 60.0);
|
||||||
|
LOG.debug("Last good pump communication was " + minutesAgo + " minutes ago.");
|
||||||
|
return lastGoodReceiverCommunicationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpStatus getPumpStatus() {
|
||||||
|
return pumpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 16/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkConst {
|
||||||
|
|
||||||
|
static final String Prefix = "AAPS.RileyLink.";
|
||||||
|
|
||||||
|
public class Intents {
|
||||||
|
|
||||||
|
public static final String RileyLinkReady = Prefix + "RileyLink_Ready";
|
||||||
|
public static final String RileyLinkGattFailed = Prefix + "RileyLink_Gatt_Failed";
|
||||||
|
|
||||||
|
public static final String BluetoothConnected = Prefix + "Bluetooth_Connected";
|
||||||
|
public static final String BluetoothDisconnected = Prefix + "Bluetooth_Disconnected";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by andy on 4/28/18.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class RileyLinkLayer {
|
|
||||||
|
|
||||||
// this is just placeholder, but it should be start of RileyLink protocol layer, which can be later used for Medtronic and Omnipod, or
|
|
||||||
// any other solution dependent on Radio Frequency communication (that can use RileyLinkLayer.
|
|
||||||
}
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.data.RLHistoryItem;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTask;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 17/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkUtil {
|
||||||
|
|
||||||
|
private static Context context;
|
||||||
|
private static RileyLinkBLE rileyLinkBLE;
|
||||||
|
private static RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
private static List<RLHistoryItem> historyRileyLink = new ArrayList<>();
|
||||||
|
private static PumpType pumpType;
|
||||||
|
private static MedtronicPumpStatus medtronicPumpStatus;
|
||||||
|
|
||||||
|
// BAD dependencies in Classes: RileyLinkService
|
||||||
|
|
||||||
|
// Broadcasts: RileyLinkBLE, RileyLinkService,
|
||||||
|
|
||||||
|
|
||||||
|
public static void setContext(Context contextIn) {
|
||||||
|
context = contextIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void sendBroadcastMessage(String message) {
|
||||||
|
Intent intent = new Intent(message);
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkBLE(RileyLinkBLE rileyLinkBLEIn) {
|
||||||
|
rileyLinkBLE = rileyLinkBLEIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkBLE getRileyLinkBLE() {
|
||||||
|
return rileyLinkBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RileyLinkServiceData getRileyLinkServiceData() {
|
||||||
|
return rileyLinkServiceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRileyLinkServiceData(RileyLinkServiceData rileyLinkServiceData) {
|
||||||
|
RileyLinkUtil.rileyLinkServiceData = rileyLinkServiceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCurrentTask(ServiceTask task) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void finishCurrentTask(ServiceTask task) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addHistoryEntry(RLHistoryItem rlHistoryItem) {
|
||||||
|
historyRileyLink.add(rlHistoryItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPumpType(PumpType pumpType) {
|
||||||
|
RileyLinkUtil.pumpType = pumpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPumpStatus(MedtronicPumpStatus medtronicPumpStatus) {
|
||||||
|
|
||||||
|
RileyLinkUtil.medtronicPumpStatus = medtronicPumpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static MedtronicPumpStatus getPumpStatus() {
|
||||||
|
|
||||||
|
return medtronicPumpStatus;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RFSpyResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RadioPacket;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RadioResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.BLECommOperationResult;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ThreadUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class RFSpy {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RFSpy.class);
|
||||||
|
|
||||||
|
public static final byte RFSPY_GET_STATE = 1;
|
||||||
|
public static final byte RFSPY_GET_VERSION = 2;
|
||||||
|
public static final byte RFSPY_GET_PACKET = 3; // aka Listen, receive
|
||||||
|
public static final byte RFSPY_SEND = 4;
|
||||||
|
public static final byte RFSPY_SEND_AND_LISTEN = 5;
|
||||||
|
public static final byte RFSPY_UPDATE_REGISTER = 6;
|
||||||
|
public static final byte RFSPY_RESET = 7;
|
||||||
|
|
||||||
|
public static final long RILEYLINK_FREQ_XTAL = 24000000;
|
||||||
|
|
||||||
|
public static final byte CC111X_REG_FREQ2 = 0x09;
|
||||||
|
public static final byte CC111X_REG_FREQ1 = 0x0A;
|
||||||
|
public static final byte CC111X_REG_FREQ0 = 0x0B;
|
||||||
|
public static final byte CC111X_MDMCFG4 = 0x0C;
|
||||||
|
public static final byte CC111X_MDMCFG3 = 0x0D;
|
||||||
|
public static final byte CC111X_MDMCFG2 = 0x0E;
|
||||||
|
public static final byte CC111X_MDMCFG1 = 0x0F;
|
||||||
|
public static final byte CC111X_MDMCFG0 = 0x10;
|
||||||
|
public static final byte CC111X_AGCCTRL2 = 0x17;
|
||||||
|
public static final byte CC111X_AGCCTRL1 = 0x18;
|
||||||
|
public static final byte CC111X_AGCCTRL0 = 0x19;
|
||||||
|
public static final byte CC111X_FREND1 = 0x1A;
|
||||||
|
public static final byte CC111X_FREND0 = 0x1B;
|
||||||
|
|
||||||
|
public static final int EXPECTED_MAX_BLUETOOTH_LATENCY_MS = 1500;
|
||||||
|
|
||||||
|
private RileyLinkBLE rileyLinkBle;
|
||||||
|
private RFSpyReader reader;
|
||||||
|
private Context context;
|
||||||
|
UUID radioServiceUUID = UUID.fromString(GattAttributes.SERVICE_RADIO);
|
||||||
|
UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA);
|
||||||
|
UUID radioVersionUUID = UUID.fromString(GattAttributes.CHARA_RADIO_VERSION);
|
||||||
|
UUID responseCountUUID = UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT);
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpy(Context context, RileyLinkBLE rileyLinkBle) {
|
||||||
|
this.context = context;
|
||||||
|
this.rileyLinkBle = rileyLinkBle;
|
||||||
|
reader = new RFSpyReader(context, rileyLinkBle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this after the RL services are discovered.
|
||||||
|
// Starts an async task to read when data is available
|
||||||
|
public void startReader() {
|
||||||
|
rileyLinkBle.registerRadioResponseCountNotification(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
newDataIsAvailable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reader.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this from the "response count" notification handler.
|
||||||
|
public void newDataIsAvailable() {
|
||||||
|
// pass the message to the reader (which should be internal to RFSpy)
|
||||||
|
reader.newDataIsAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gets the version from the BLE113, not from the CC1110.
|
||||||
|
// I.e., this gets the version from the BLE interface, not from the radio.
|
||||||
|
public String getVersion() {
|
||||||
|
BLECommOperationResult result = rileyLinkBle.readCharacteristic_blocking(radioServiceUUID, radioVersionUUID);
|
||||||
|
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
return StringUtil.fromBytes(result.value);
|
||||||
|
} else {
|
||||||
|
LOG.error("getVersion failed with code: " + result.resultCode);
|
||||||
|
return "(null)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The caller has to know how long the RFSpy will be busy with what was sent to it.
|
||||||
|
private RFSpyResponse writeToData(byte[] bytes, int responseTimeout_ms) {
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
// FIXME drain read queue?
|
||||||
|
byte[] junkInBuffer = reader.poll(0);
|
||||||
|
|
||||||
|
while (junkInBuffer != null) {
|
||||||
|
LOG.warn(ThreadUtil.sig() + "writeToData: draining read queue, found this: " + ByteUtil.shortHexString(junkInBuffer));
|
||||||
|
junkInBuffer = reader.poll(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend length, and send it.
|
||||||
|
byte[] prepended = ByteUtil.concat(new byte[]{(byte) (bytes.length)}, bytes);
|
||||||
|
BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristic_blocking(radioServiceUUID, radioDataUUID, prepended);
|
||||||
|
if (writeCheck.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
LOG.error("BLE Write operation failed, code=" + writeCheck.resultCode);
|
||||||
|
return new RFSpyResponse(); // will be a null (invalid) response
|
||||||
|
}
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
//Log.i(TAG,ThreadUtil.sig()+String.format(" writeToData:(timeout %d) %s",(responseTimeout_ms),ByteUtil.shortHexString(prepended)));
|
||||||
|
byte[] rawResponse = reader.poll(responseTimeout_ms);
|
||||||
|
RFSpyResponse resp = new RFSpyResponse(rawResponse);
|
||||||
|
if (rawResponse == null) {
|
||||||
|
LOG.error("writeToData: No response from RileyLink");
|
||||||
|
} else {
|
||||||
|
if (resp.wasInterrupted()) {
|
||||||
|
LOG.error("writeToData: RileyLink was interrupted");
|
||||||
|
} else if (resp.wasTimeout()) {
|
||||||
|
LOG.error("writeToData: RileyLink reports timeout");
|
||||||
|
} else if (resp.isOK()) {
|
||||||
|
LOG.warn("writeToData: RileyLink reports OK");
|
||||||
|
} else {
|
||||||
|
if (resp.looksLikeRadioPacket()) {
|
||||||
|
RadioResponse radioResp = resp.getRadioResponse();
|
||||||
|
byte[] responsePayload = radioResp.getPayload();
|
||||||
|
LOG.info("writeToData: decoded radio response is " + ByteUtil.shortHexString(responsePayload));
|
||||||
|
}
|
||||||
|
//Log.i(TAG, "writeToData: raw response is " + ByteUtil.shortHexString(rawResponse));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse getRadioVersion() {
|
||||||
|
RFSpyResponse resp = writeToData(new byte[]{RFSPY_GET_VERSION}, 1000);
|
||||||
|
if (resp == null) {
|
||||||
|
LOG.error("getRadioVersion returned null");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Log.d(TAG,"checking response count");
|
||||||
|
BLECommOperationResult checkRC = rileyLinkBle.readCharacteristic_blocking(radioServiceUUID,responseCountUUID);
|
||||||
|
if (checkRC.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
Log.d(TAG,"Response count is: " + ByteUtil.shortHexString(checkRC.value));
|
||||||
|
} else {
|
||||||
|
LOG.error("Error getting response count, code is " + checkRC.resultCode);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse transmit(RadioPacket radioPacket, byte sendChannel, byte repeatCount, byte delay_ms) {
|
||||||
|
// append checksum, encode data, send it.
|
||||||
|
byte[] fullPacket = ByteUtil.concat(new byte[]{RFSPY_SEND, sendChannel, repeatCount, delay_ms}, radioPacket.getEncoded());
|
||||||
|
RFSpyResponse response = writeToData(fullPacket, repeatCount * delay_ms);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse receive(byte listenChannel, int timeout_ms, byte retryCount) {
|
||||||
|
int receiveDelay = timeout_ms * (retryCount + 1);
|
||||||
|
byte[] listen = {RFSPY_GET_PACKET, listenChannel,
|
||||||
|
(byte) ((timeout_ms >> 24) & 0x0FF),
|
||||||
|
(byte) ((timeout_ms >> 16) & 0x0FF),
|
||||||
|
(byte) ((timeout_ms >> 8) & 0x0FF),
|
||||||
|
(byte) (timeout_ms & 0x0FF),
|
||||||
|
retryCount};
|
||||||
|
return writeToData(listen, receiveDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse transmitThenReceive(RadioPacket pkt, int timeout_ms) {
|
||||||
|
return transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, timeout_ms, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse transmitThenReceive(RadioPacket pkt, byte sendChannel, byte repeatCount, byte delay_ms, byte listenChannel, int timeout_ms, byte retryCount) {
|
||||||
|
|
||||||
|
int sendDelay = repeatCount * delay_ms;
|
||||||
|
int receiveDelay = timeout_ms * (retryCount + 1);
|
||||||
|
byte[] sendAndListen = {RFSPY_SEND_AND_LISTEN, sendChannel, repeatCount, delay_ms, listenChannel,
|
||||||
|
(byte) ((timeout_ms >> 24) & 0x0FF),
|
||||||
|
(byte) ((timeout_ms >> 16) & 0x0FF),
|
||||||
|
(byte) ((timeout_ms >> 8) & 0x0FF),
|
||||||
|
(byte) (timeout_ms & 0x0FF),
|
||||||
|
retryCount};
|
||||||
|
byte[] fullPacket = ByteUtil.concat(sendAndListen, pkt.getEncoded());
|
||||||
|
return writeToData(fullPacket, sendDelay + receiveDelay + EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse updateRegister(byte addr, byte val) {
|
||||||
|
byte[] updateRegisterPkt = new byte[]{RFSPY_UPDATE_REGISTER, addr, val};
|
||||||
|
RFSpyResponse resp = writeToData(updateRegisterPkt, EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseFrequency(double freqMHz) {
|
||||||
|
int value = (int) (freqMHz * 1000000 / ((double) (RILEYLINK_FREQ_XTAL) / Math.pow(2.0, 16.0)));
|
||||||
|
updateRegister(CC111X_REG_FREQ0, (byte) (value & 0xff));
|
||||||
|
updateRegister(CC111X_REG_FREQ1, (byte) ((value >> 8) & 0xff));
|
||||||
|
updateRegister(CC111X_REG_FREQ2, (byte) ((value >> 16) & 0xff));
|
||||||
|
LOG.warn("Set frequency to {}", freqMHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// func configureRadio(for region: PumpRegion) throws {
|
||||||
|
// try session.resetRadioConfig()
|
||||||
|
//
|
||||||
|
// switch region {
|
||||||
|
// case .worldWide:
|
||||||
|
// //try session.updateRegister(.mdmcfg4, value: 0x59)
|
||||||
|
// try setRXFilterMode(.wide)
|
||||||
|
// //try session.updateRegister(.mdmcfg3, value: 0x66)
|
||||||
|
// //try session.updateRegister(.mdmcfg2, value: 0x33)
|
||||||
|
// try session.updateRegister(.mdmcfg1, value: 0x62)
|
||||||
|
// try session.updateRegister(.mdmcfg0, value: 0x1A)
|
||||||
|
// try session.updateRegister(.deviatn, value: 0x13)
|
||||||
|
// case .northAmerica:
|
||||||
|
// //try session.updateRegister(.mdmcfg4, value: 0x99)
|
||||||
|
// try setRXFilterMode(.narrow)
|
||||||
|
// //try session.updateRegister(.mdmcfg3, value: 0x66)
|
||||||
|
// //try session.updateRegister(.mdmcfg2, value: 0x33)
|
||||||
|
// try session.updateRegister(.mdmcfg1, value: 0x61)
|
||||||
|
// try session.updateRegister(.mdmcfg0, value: 0x7E)
|
||||||
|
// try session.updateRegister(.deviatn, value: 0x15)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.BLECommOperationResult;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ThreadUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class RFSpyReader {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RFSpyReader.class);
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private RileyLinkBLE rileyLinkBle;
|
||||||
|
private Semaphore waitForRadioData = new Semaphore(0, true);
|
||||||
|
AsyncTask<Void, Void, Void> readerTask;
|
||||||
|
private LinkedBlockingQueue<byte[]> mDataQueue = new LinkedBlockingQueue<>();
|
||||||
|
private int acquireCount = 0;
|
||||||
|
private int releaseCount = 0;
|
||||||
|
|
||||||
|
public RFSpyReader(Context context, RileyLinkBLE rileyLinkBle) {
|
||||||
|
this.context = context;
|
||||||
|
this.rileyLinkBle = rileyLinkBle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Context context, RileyLinkBLE rileyLinkBLE) {
|
||||||
|
this.context = context;
|
||||||
|
this.rileyLinkBle = rileyLinkBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRileyLinkBle(RileyLinkBLE rileyLinkBle) {
|
||||||
|
if (readerTask != null) {
|
||||||
|
readerTask.cancel(true);
|
||||||
|
}
|
||||||
|
this.rileyLinkBle = rileyLinkBle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This timeout must be coordinated with the length of the RFSpy radio operation or Bad Things Happen.
|
||||||
|
public byte[] poll(int timeout_ms) {
|
||||||
|
LOG.debug(ThreadUtil.sig() + "Entering poll at t==" + SystemClock.uptimeMillis() + ", timeout is " + timeout_ms + " mDataQueue size is " + mDataQueue.size());
|
||||||
|
if (mDataQueue.isEmpty())
|
||||||
|
try {
|
||||||
|
// block until timeout or data available.
|
||||||
|
// returns null if timeout.
|
||||||
|
byte[] dataFromQueue = mDataQueue.poll(timeout_ms, TimeUnit.MILLISECONDS);
|
||||||
|
if (dataFromQueue != null) {
|
||||||
|
LOG.debug("Got data [" + ByteUtil.shortHexString(dataFromQueue) + "] at t==" + SystemClock.uptimeMillis());
|
||||||
|
} else {
|
||||||
|
LOG.debug("Got data [null] at t==" + SystemClock.uptimeMillis());
|
||||||
|
}
|
||||||
|
return dataFromQueue;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("poll: Interrupted waiting for data");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this from the "response count" notification handler.
|
||||||
|
public void newDataIsAvailable() {
|
||||||
|
releaseCount++;
|
||||||
|
|
||||||
|
LOG.debug(ThreadUtil.sig() + "waitForRadioData released(count=" + releaseCount + ") at t=" + SystemClock.uptimeMillis());
|
||||||
|
waitForRadioData.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
readerTask = new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
UUID serviceUUID = UUID.fromString(GattAttributes.SERVICE_RADIO);
|
||||||
|
UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA);
|
||||||
|
BLECommOperationResult result;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
acquireCount++;
|
||||||
|
waitForRadioData.acquire();
|
||||||
|
LOG.debug(ThreadUtil.sig() + "waitForRadioData acquired (count=" + acquireCount + ") at t=" + SystemClock.uptimeMillis());
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
SystemClock.sleep(1);
|
||||||
|
result = rileyLinkBle.readCharacteristic_blocking(serviceUUID, radioDataUUID);
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
|
||||||
|
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
// only data up to the first null is valid
|
||||||
|
for (int i = 0; i < result.value.length; i++) {
|
||||||
|
if (result.value[i] == 0) {
|
||||||
|
result.value = ByteUtil.substring(result.value, 0, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDataQueue.add(result.value);
|
||||||
|
} else if (result.resultCode == BLECommOperationResult.RESULT_INTERRUPTED) {
|
||||||
|
LOG.error("Read operation was interrupted");
|
||||||
|
} else if (result.resultCode == BLECommOperationResult.RESULT_TIMEOUT) {
|
||||||
|
LOG.error("Read operation on Radio Data timed out");
|
||||||
|
} else if (result.resultCode == BLECommOperationResult.RESULT_BUSY) {
|
||||||
|
LOG.error("FAIL: RileyLinkBLE reports operation already in progress");
|
||||||
|
} else if (result.resultCode == BLECommOperationResult.RESULT_NONE) {
|
||||||
|
LOG.error("FAIL: got invalid result code: " + result.resultCode);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Interrupted while waiting for data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,299 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 7/31/15.
|
||||||
|
*/
|
||||||
|
public class RFTools {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RFTools.class);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
CodeSymbols is an ordered list of translations
|
||||||
|
6bits -> 4 bits, in order from 0x0 to 0xF
|
||||||
|
The 6 bit codes are what is used on the RF side of the RileyLink
|
||||||
|
to communicate with a Medtronic pump.
|
||||||
|
|
||||||
|
*/
|
||||||
|
public static byte[] CodeSymbols = {
|
||||||
|
0x15,
|
||||||
|
0x31,
|
||||||
|
0x32,
|
||||||
|
0x23,
|
||||||
|
0x34,
|
||||||
|
0x25,
|
||||||
|
0x26,
|
||||||
|
0x16,
|
||||||
|
0x1a,
|
||||||
|
0x19,
|
||||||
|
0x2a,
|
||||||
|
0x0b,
|
||||||
|
0x2c,
|
||||||
|
0x0d,
|
||||||
|
0x0e,
|
||||||
|
0x1c
|
||||||
|
};
|
||||||
|
|
||||||
|
public static byte[] appendChecksum(final byte[] input) {
|
||||||
|
if (input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (input.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] rval = new byte[input.length + 1];
|
||||||
|
System.arraycopy(input, 0, rval, 0, input.length);
|
||||||
|
byte mycrc = CRC.crc8(input);
|
||||||
|
LOG.debug(String.format("Adding checksum 0x%02X to %d byte array from 0x%02X to 0x%02X", mycrc, input.length, input[0], input[input.length - 1]));
|
||||||
|
rval[input.length] = mycrc;
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Byte> fromBytes(byte[] data) {
|
||||||
|
ArrayList<Byte> rval = new ArrayList<>();
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
rval.add(data[i]);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(ArrayList<Byte> data) {
|
||||||
|
byte[] rval = new byte[data.size()];
|
||||||
|
for (int i = 0; i < data.size(); i++) {
|
||||||
|
rval[i] = data.get(i);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
+ (NSData*)encode4b6b:(NSData*)data {
|
||||||
|
NSMutableData *outData = [NSMutableData data];
|
||||||
|
NSMutableData *dataPlusCrc = [data mutableCopy];
|
||||||
|
unsigned char crc = [MinimedPacket crcForData:data];
|
||||||
|
[dataPlusCrc appendBytes:&crc length:1];
|
||||||
|
char codes[16] = {21,49,50,35,52,37,38,22,26,25,42,11,44,13,14,28};
|
||||||
|
const unsigned char *inBytes = [dataPlusCrc bytes];
|
||||||
|
unsigned int acc = 0x0;
|
||||||
|
int bitcount = 0;
|
||||||
|
for (int i=0; i < dataPlusCrc.length; i++) {
|
||||||
|
acc <<= 6;
|
||||||
|
acc |= codes[inBytes[i] >> 4];
|
||||||
|
bitcount += 6;
|
||||||
|
|
||||||
|
acc <<= 6;
|
||||||
|
acc |= codes[inBytes[i] & 0x0f];
|
||||||
|
bitcount += 6;
|
||||||
|
|
||||||
|
while (bitcount >= 8) {
|
||||||
|
unsigned char outByte = acc >> (bitcount-8) & 0xff;
|
||||||
|
[outData appendBytes:&outByte length:1];
|
||||||
|
bitcount -= 8;
|
||||||
|
acc &= (0xffff >> (16-bitcount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bitcount > 0) {
|
||||||
|
acc <<= (8-bitcount);
|
||||||
|
unsigned char outByte = acc & 0xff;
|
||||||
|
[outData appendBytes:&outByte length:1];
|
||||||
|
}
|
||||||
|
return outData;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static final byte[] codes = new byte[]{21, 49, 50, 35, 52, 37, 38, 22, 26, 25, 42, 11, 44, 13, 14, 28};
|
||||||
|
|
||||||
|
/* O(n) lookup. Run on an O(n) translation of a byte-stream, gives O(n**2) performance. Sigh. */
|
||||||
|
public static int codeIndex(byte b) {
|
||||||
|
for (int i = 0; i < codes.length; i++) {
|
||||||
|
if (b == codes[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encode4b6b(byte[] data) {
|
||||||
|
if ((data.length % 2) != 0) {
|
||||||
|
// LOG.error("Warning: data is odd number of bytes");
|
||||||
|
}
|
||||||
|
// use arraylists because byte[] is annoying.
|
||||||
|
ArrayList<Byte> inData = fromBytes(data);
|
||||||
|
ArrayList<Byte> outData = new ArrayList<>();
|
||||||
|
|
||||||
|
int acc = 0;
|
||||||
|
int bitcount = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < inData.size(); i++) {
|
||||||
|
acc <<= 6;
|
||||||
|
acc |= codes[(inData.get(i) >> 4) & 0x0f];
|
||||||
|
bitcount += 6;
|
||||||
|
|
||||||
|
acc <<= 6;
|
||||||
|
acc |= codes[inData.get(i) & 0x0f];
|
||||||
|
bitcount += 6;
|
||||||
|
|
||||||
|
while (bitcount >= 8) {
|
||||||
|
byte outByte = (byte) (acc >> (bitcount - 8) & 0xff);
|
||||||
|
outData.add(outByte);
|
||||||
|
bitcount -= 8;
|
||||||
|
acc &= (0xffff >> (16 - bitcount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bitcount > 0) {
|
||||||
|
acc <<= 6;
|
||||||
|
acc |= 0x14; // marks uneven packet boundary.
|
||||||
|
bitcount += 6;
|
||||||
|
if (bitcount >= 8) {
|
||||||
|
byte outByte = (byte) ((acc >> (bitcount - 8)) & 0xff);
|
||||||
|
outData.add(outByte);
|
||||||
|
bitcount -= 8;
|
||||||
|
// acc &= (0xffff >> (16 - bitcount));
|
||||||
|
}
|
||||||
|
while (bitcount >= 8) {
|
||||||
|
outData.add((byte) 0);
|
||||||
|
bitcount -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// convert back to byte[]
|
||||||
|
byte[] rval = toBytes(outData);
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void test() {
|
||||||
|
/*
|
||||||
|
{0xa7} -> {0xa9, 0x60}
|
||||||
|
{0xa7, 0x12} -> {0xa9, 0x6c, 0x72}
|
||||||
|
{0xa7, 0x12, 0xa7} -> {0xa9, 0x6c, 0x72, 0xa9, 0x60}
|
||||||
|
*/
|
||||||
|
/* test compare */
|
||||||
|
byte[] s1 = {0, 1, 2};
|
||||||
|
byte[] s2 = {2, 1, 0, 3};
|
||||||
|
byte[] s3 = {0, 1, 2, 3};
|
||||||
|
if (ByteUtil.compare(s1, s1) != 0) {
|
||||||
|
LOG.error("test: compare failed.");
|
||||||
|
}
|
||||||
|
if (ByteUtil.compare(s1, s2) >= 0) {
|
||||||
|
LOG.error("test: compare failed.");
|
||||||
|
}
|
||||||
|
if (ByteUtil.compare(s2, s1) <= 0) {
|
||||||
|
LOG.error("test: compare failed.");
|
||||||
|
}
|
||||||
|
if (ByteUtil.compare(s1, s3) >= 0) {
|
||||||
|
LOG.error("test: compare failed.");
|
||||||
|
}
|
||||||
|
//testCompose(new byte[] {(byte)0xa7, (byte)0xa7});
|
||||||
|
byte[] bs = encode4b6b(new byte[]{(byte) 0xa7});
|
||||||
|
byte[] out = new byte[]{(byte) (0xa9), 0x65};
|
||||||
|
if (ByteUtil.compare(bs, out) != 0) {
|
||||||
|
LOG.error("encode Data failed: expected " + ByteUtil.shortHexString(out) + " but got " + ByteUtil.shortHexString(bs));
|
||||||
|
}
|
||||||
|
bs = encode4b6b(new byte[]{(byte) 0xa7, 0x12});
|
||||||
|
out = new byte[]{(byte) (0xa9), 0x6c, 0x72};
|
||||||
|
if (ByteUtil.compare(bs, out) != 0) {
|
||||||
|
LOG.error("encode Data failed: expected " + ByteUtil.shortHexString(out) + " but got " + ByteUtil.shortHexString(bs));
|
||||||
|
}
|
||||||
|
bs = encode4b6b(new byte[]{(byte) 0xa7, 0x12, (byte) 0xa7});
|
||||||
|
out = new byte[]{(byte) (0xa9), 0x6c, 0x72, (byte) 0xa9, 0x65};
|
||||||
|
if (ByteUtil.compare(bs, out) != 0) {
|
||||||
|
LOG.error("encode Data failed: expected " + ByteUtil.shortHexString(out) + " but got " + ByteUtil.shortHexString(bs));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode4b6b(byte[] raw) throws NumberFormatException {
|
||||||
|
/*
|
||||||
|
if ((raw.length % 2) != 0) {
|
||||||
|
LOG.error("Warning: data is odd number of bytes");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
byte[] rval = new byte[]{};
|
||||||
|
int availableBits = 0;
|
||||||
|
int codingErrors = 0;
|
||||||
|
int x = 0;
|
||||||
|
//Log.w(TAG,"decode4b6b: untested code");
|
||||||
|
//Log.w(TAG,String.format("Decoding %d bytes: %s",raw.length,ByteUtil.shortHexString(raw)));
|
||||||
|
for (int i = 0; i < raw.length; i++) {
|
||||||
|
int unsignedValue = raw[i];
|
||||||
|
if (unsignedValue < 0) {
|
||||||
|
unsignedValue += 256;
|
||||||
|
}
|
||||||
|
x = (x << 8) + unsignedValue;
|
||||||
|
availableBits += 8;
|
||||||
|
if (availableBits >= 12) {
|
||||||
|
// take top six
|
||||||
|
int highcode = (x >> (availableBits - 6)) & 0x3F;
|
||||||
|
int highIndex = codeIndex((byte) (highcode));
|
||||||
|
// take bottom six
|
||||||
|
int lowcode = (x >> (availableBits - 12)) & 0x3F;
|
||||||
|
int lowIndex = codeIndex((byte) (lowcode));
|
||||||
|
// special case at end of transmission on uneven boundaries:
|
||||||
|
if ((highIndex >= 0) && (lowIndex >= 0)) {
|
||||||
|
byte decoded = (byte) ((highIndex << 4) + lowIndex);
|
||||||
|
rval = ByteUtil.concat(rval, decoded);
|
||||||
|
/*
|
||||||
|
LOG.debug(String.format("i=%d,x=0x%08X,0x%02X->0x%02X, 0x%02X->0x%02X, result: 0x%02X, %d bits remaining, errors %d, bytes remaining: %s",
|
||||||
|
i,x,highcode,highIndex, lowcode, lowIndex,decoded,availableBits,codingErrors,ByteUtil.shortHexString(ByteUtil.substring(raw,i+1,raw.length-i-1))));
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
//LOG.debug(String.format("i=%d,x=%08X, coding error: highcode=0x%02X, lowcode=0x%02X, %d bits remaining",i,x,highcode,lowcode,availableBits));
|
||||||
|
codingErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableBits -= 12;
|
||||||
|
x = x & (0x0000ffff >> (16 - availableBits));
|
||||||
|
} else {
|
||||||
|
//LOG.debug(String.format("i=%d, skip: x=0x%08X, available bits %d",i,x,availableBits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (availableBits != 0) {
|
||||||
|
if ((availableBits == 4) && (x == 0x05)) {
|
||||||
|
// normal end
|
||||||
|
} else {
|
||||||
|
LOG.error("decode4b6b: failed clean decode -- extra bits available (not marker)(" + availableBits + ")");
|
||||||
|
codingErrors++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// also normal end.
|
||||||
|
}
|
||||||
|
if (codingErrors > 0) {
|
||||||
|
LOG.error("decode4b6b: " + codingErrors + " coding errors encountered.");
|
||||||
|
throw new NumberFormatException();
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(byte[] array) {
|
||||||
|
return toHexString(array, 0, array.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static char[] HEX_DIGITS = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
public static String toHexString(byte[] array, int offset, int length) {
|
||||||
|
char[] buf = new char[length * 2];
|
||||||
|
|
||||||
|
int bufIndex = 0;
|
||||||
|
for (int i = offset; i < offset + length; i++) {
|
||||||
|
byte b = array[i];
|
||||||
|
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
|
||||||
|
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,517 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCallback;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor;
|
||||||
|
import android.bluetooth.BluetoothGattService;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkConst;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.BLECommOperation;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.BLECommOperationResult;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.CharacteristicReadOperation;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.CharacteristicWriteOperation;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations.DescriptorWriteOperation;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ThreadUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class RileyLinkBLE {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RFTools.class);
|
||||||
|
|
||||||
|
public boolean gattDebugEnabled = true;
|
||||||
|
|
||||||
|
private BluetoothAdapter bluetoothAdapter;
|
||||||
|
private BluetoothGattCallback bluetoothGattCallback;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private BluetoothDevice rileyLinkDevice;
|
||||||
|
private BluetoothGatt bluetoothConnectionGatt = null;
|
||||||
|
|
||||||
|
private BLECommOperation mCurrentOperation;
|
||||||
|
private Semaphore gattOperationSema = new Semaphore(1, true);
|
||||||
|
|
||||||
|
private Runnable radioResponseCountNotified;
|
||||||
|
|
||||||
|
private boolean mIsConnected = false;
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkBLE(final Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
|
||||||
|
//this.bluetoothAdapter = bluetoothAdapter;
|
||||||
|
LOG.debug("BT Adapter: " + this.bluetoothAdapter);
|
||||||
|
bluetoothGattCallback = new BluetoothGattCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||||
|
super.onCharacteristicChanged(gatt, characteristic);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.debug(ThreadUtil.sig() + "onCharacteristicChanged " + GattAttributes.lookup(characteristic.getUuid()) + " " + HexDump.toHexString(characteristic.getValue()));
|
||||||
|
if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
|
||||||
|
LOG.debug("Response Count is " + HexDump.toHexString(characteristic.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (radioResponseCountNotified != null) {
|
||||||
|
radioResponseCountNotified.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
super.onCharacteristicRead(gatt, characteristic, status);
|
||||||
|
|
||||||
|
|
||||||
|
final String statusMessage = getGattStatusMessage(status);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.debug(ThreadUtil.sig() + "onCharacteristicRead (" + GattAttributes.lookup(characteristic.getUuid()) + ") " + statusMessage + ":" + HexDump.toHexString(characteristic.getValue()));
|
||||||
|
}
|
||||||
|
mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
super.onCharacteristicWrite(gatt, characteristic, status);
|
||||||
|
|
||||||
|
final String uuidString = GattAttributes.lookup(characteristic.getUuid());
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.debug(ThreadUtil.sig() + "onCharacteristicWrite " + getGattStatusMessage(status) + " " + uuidString + " " + HexDump.toHexString(characteristic.getValue()));
|
||||||
|
}
|
||||||
|
mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
|
||||||
|
super.onConnectionStateChange(gatt, status, newState);
|
||||||
|
|
||||||
|
// https://github.com/NordicSemiconductor/puck-central-android/blob/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt/GattManager.java#L117
|
||||||
|
if (status == 133) {
|
||||||
|
LOG.error("Got the status 133 bug, closing gatt");
|
||||||
|
disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
final String stateMessage;
|
||||||
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
stateMessage = "CONNECTED";
|
||||||
|
} else if (newState == BluetoothProfile.STATE_CONNECTING) {
|
||||||
|
stateMessage = "CONNECTING";
|
||||||
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
|
stateMessage = "DISCONNECTED";
|
||||||
|
} else if (newState == BluetoothProfile.STATE_DISCONNECTING) {
|
||||||
|
stateMessage = "DISCONNECTING";
|
||||||
|
} else {
|
||||||
|
stateMessage = "UNKNOWN (" + newState + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.warn("onConnectionStateChange " + getGattStatusMessage(status) + " " + stateMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(RileyLinkConst.Intents.BluetoothConnected));
|
||||||
|
} else {
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(RileyLinkConst.Intents.BluetoothDisconnected));
|
||||||
|
disconnect();
|
||||||
|
LOG.warn("Cannot establish Bluetooth connection.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
super.onDescriptorWrite(gatt, descriptor, status);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onDescriptorWrite " + GattAttributes.lookup(descriptor.getUuid()) + " " + getGattStatusMessage(status) + " written: " + HexDump.toHexString(descriptor.getValue()));
|
||||||
|
}
|
||||||
|
mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
super.onDescriptorRead(gatt, descriptor, status);
|
||||||
|
mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue());
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onDescriptorRead " + getGattStatusMessage(status) + " status " + descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||||
|
super.onMtuChanged(gatt, mtu, status);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onMtuChanged " + mtu + " status " + status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReadRemoteRssi(final BluetoothGatt gatt, int rssi, int status) {
|
||||||
|
super.onReadRemoteRssi(gatt, rssi, status);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onReadRemoteRssi " + getGattStatusMessage(status) + ": " + rssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||||
|
super.onReliableWriteCompleted(gatt, status);
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onReliableWriteCompleted status " + status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
|
||||||
|
super.onServicesDiscovered(gatt, status);
|
||||||
|
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
final List<BluetoothGattService> services = gatt.getServices();
|
||||||
|
// TODO: in here, we need to determine if this Bluetooth device is a RileyLink with appropriate
|
||||||
|
// software (subg_rfspy) and set mIsConnected if the GATT is ok.
|
||||||
|
|
||||||
|
boolean rileyLinkFound = false;
|
||||||
|
|
||||||
|
for (BluetoothGattService service : services) {
|
||||||
|
final UUID uuidService = service.getUuid();
|
||||||
|
|
||||||
|
if (isAnyRileyLinkServiceFound(service)) {
|
||||||
|
rileyLinkFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
debugService(service, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.warn("onServicesDiscovered " + getGattStatusMessage(status));
|
||||||
|
}
|
||||||
|
mIsConnected = true;
|
||||||
|
RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkReady);
|
||||||
|
} else {
|
||||||
|
LOG.error("onServicesDiscovered " + getGattStatusMessage(status));
|
||||||
|
RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkGattFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isAnyRileyLinkServiceFound(BluetoothGattService service) {
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
found = GattAttributes.isRileyLink(service.getUuid());
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
List<BluetoothGattService> includedServices = service.getIncludedServices();
|
||||||
|
|
||||||
|
for (BluetoothGattService serviceI : includedServices) {
|
||||||
|
if (isAnyRileyLinkServiceFound(serviceI)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BluetoothDevice getRileyLinkDevice() {
|
||||||
|
return this.rileyLinkDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void debugService(BluetoothGattService service, int indentCount) {
|
||||||
|
|
||||||
|
String indentString = StringUtils.repeat(' ', indentCount);
|
||||||
|
|
||||||
|
final UUID uuidService = service.getUuid();
|
||||||
|
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
final String uuidServiceString = uuidService.toString();
|
||||||
|
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
stringBuilder.append(indentString);
|
||||||
|
stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service"));
|
||||||
|
stringBuilder.append(" (" + uuidServiceString + ")");
|
||||||
|
|
||||||
|
for (BluetoothGattCharacteristic character : service.getCharacteristics()) {
|
||||||
|
final String uuidCharacteristicString = character.getUuid().toString();
|
||||||
|
|
||||||
|
stringBuilder.append("\n ");
|
||||||
|
stringBuilder.append(indentString);
|
||||||
|
stringBuilder.append(" - " + GattAttributes.lookup(uuidCharacteristicString, "Unknown Characteristic"));
|
||||||
|
stringBuilder.append(" (" + uuidCharacteristicString + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
stringBuilder.append("\n\n");
|
||||||
|
|
||||||
|
LOG.warn(stringBuilder.toString());
|
||||||
|
|
||||||
|
List<BluetoothGattService> includedServices = service.getIncludedServices();
|
||||||
|
|
||||||
|
for (BluetoothGattService serviceI : includedServices) {
|
||||||
|
debugService(serviceI, indentCount + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void registerRadioResponseCountNotification(Runnable notifier) {
|
||||||
|
radioResponseCountNotified = notifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return mIsConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean discoverServices() {
|
||||||
|
if (bluetoothConnectionGatt.discoverServices()) {
|
||||||
|
LOG.warn("Starting to discover GATT Services.");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG.error("Cannot discover GATT Services.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean enableNotifications() {
|
||||||
|
BLECommOperationResult result = setNotification_blocking(UUID.fromString(GattAttributes.SERVICE_RADIO), //
|
||||||
|
UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT));
|
||||||
|
if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
LOG.error("Error setting response count notification");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void findRileyLink(String RileyLinkAddress) {
|
||||||
|
LOG.debug("Rileylink address: " + RileyLinkAddress);
|
||||||
|
// Must verify that this is a valid MAC, or crash.
|
||||||
|
|
||||||
|
rileyLinkDevice = bluetoothAdapter.getRemoteDevice(RileyLinkAddress);
|
||||||
|
// if this succeeds, we get a connection state change callback?
|
||||||
|
connectGatt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This function must be run on UI thread.
|
||||||
|
public void connectGatt() {
|
||||||
|
bluetoothConnectionGatt = rileyLinkDevice.connectGatt(context, true, bluetoothGattCallback);
|
||||||
|
if (bluetoothConnectionGatt == null) {
|
||||||
|
LOG.error("Failed to connect to Bluetooth Low Energy device at " + bluetoothAdapter.getAddress());
|
||||||
|
Toast.makeText(context, "No Rileylink at " + bluetoothAdapter.getAddress(), Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
LOG.debug("Gatt Connected?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
mIsConnected = false;
|
||||||
|
LOG.warn("Closing GATT connection");
|
||||||
|
// Close old conenction
|
||||||
|
if (bluetoothConnectionGatt != null) {
|
||||||
|
// Not sure if to disconnect or to close first..
|
||||||
|
bluetoothConnectionGatt.disconnect();
|
||||||
|
bluetoothConnectionGatt.close();
|
||||||
|
bluetoothConnectionGatt = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BLECommOperationResult setNotification_blocking(UUID serviceUUID, UUID charaUUID) {
|
||||||
|
BLECommOperationResult rval = new BLECommOperationResult();
|
||||||
|
if (bluetoothConnectionGatt != null) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
gattOperationSema.acquire();
|
||||||
|
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("setNotification_blocking: interrupted waiting for gattOperationSema");
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||||
|
} else {
|
||||||
|
if (bluetoothConnectionGatt.getService(serviceUUID) == null) {
|
||||||
|
// Catch if the service is not supported by the BLE device
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_NONE;
|
||||||
|
LOG.error("BT Device not supported");
|
||||||
|
// TODO: 11/07/2016 UI update for user
|
||||||
|
} else {
|
||||||
|
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID).getCharacteristic(charaUUID);
|
||||||
|
// Tell Android that we want the notifications
|
||||||
|
bluetoothConnectionGatt.setCharacteristicNotification(chara, true);
|
||||||
|
List<BluetoothGattDescriptor> list = chara.getDescriptors();
|
||||||
|
if (gattDebugEnabled) {
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
LOG.debug("Found descriptor: " + list.get(i).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BluetoothGattDescriptor descr = list.get(0);
|
||||||
|
// Tell the remote device to send the notifications
|
||||||
|
mCurrentOperation = new DescriptorWriteOperation(bluetoothConnectionGatt, descr, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
|
||||||
|
mCurrentOperation.execute(this);
|
||||||
|
if (mCurrentOperation.timedOut) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||||
|
} else if (mCurrentOperation.interrupted) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||||
|
} else {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCurrentOperation = null;
|
||||||
|
gattOperationSema.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("setNotification_blocking: not configured!");
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// call from main
|
||||||
|
public BLECommOperationResult writeCharacteristic_blocking(UUID serviceUUID, UUID charaUUID, byte[] value) {
|
||||||
|
BLECommOperationResult rval = new BLECommOperationResult();
|
||||||
|
if (bluetoothConnectionGatt != null) {
|
||||||
|
rval.value = value;
|
||||||
|
try {
|
||||||
|
gattOperationSema.acquire();
|
||||||
|
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("writeCharacteristic_blocking: interrupted waiting for gattOperationSema");
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||||
|
} else {
|
||||||
|
if (bluetoothConnectionGatt.getService(serviceUUID) == null) {
|
||||||
|
// Catch if the service is not supported by the BLE device
|
||||||
|
// GGW: Tue Jul 12 01:14:01 UTC 2016: This can also happen if the
|
||||||
|
// app that created the bluetoothConnectionGatt has been destroyed/created,
|
||||||
|
// e.g. when the user switches from portrait to landscape.
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_NONE;
|
||||||
|
LOG.error("BT Device not supported");
|
||||||
|
// TODO: 11/07/2016 UI update for user
|
||||||
|
} else {
|
||||||
|
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID).getCharacteristic(charaUUID);
|
||||||
|
mCurrentOperation = new CharacteristicWriteOperation(bluetoothConnectionGatt, chara, value);
|
||||||
|
mCurrentOperation.execute(this);
|
||||||
|
if (mCurrentOperation.timedOut) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||||
|
} else if (mCurrentOperation.interrupted) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||||
|
} else {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCurrentOperation = null;
|
||||||
|
gattOperationSema.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("writeCharacteristic_blocking: not configured!");
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BLECommOperationResult readCharacteristic_blocking(UUID serviceUUID, UUID charaUUID) {
|
||||||
|
BLECommOperationResult rval = new BLECommOperationResult();
|
||||||
|
if (bluetoothConnectionGatt != null) {
|
||||||
|
try {
|
||||||
|
gattOperationSema.acquire();
|
||||||
|
SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("readCharacteristic_blocking: Interrupted waiting for gattOperationSema");
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_BUSY;
|
||||||
|
} else {
|
||||||
|
BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID).getCharacteristic(charaUUID);
|
||||||
|
mCurrentOperation = new CharacteristicReadOperation(bluetoothConnectionGatt, chara);
|
||||||
|
mCurrentOperation.execute(this);
|
||||||
|
if (mCurrentOperation.timedOut) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT;
|
||||||
|
} else if (mCurrentOperation.interrupted) {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED;
|
||||||
|
} else {
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_SUCCESS;
|
||||||
|
rval.value = mCurrentOperation.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mCurrentOperation = null;
|
||||||
|
gattOperationSema.release();
|
||||||
|
} else {
|
||||||
|
LOG.error("readCharacteristic_blocking: not configured!");
|
||||||
|
rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getGattStatusMessage(final int status) {
|
||||||
|
final String statusMessage;
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
statusMessage = "SUCCESS";
|
||||||
|
} else if (status == BluetoothGatt.GATT_FAILURE) {
|
||||||
|
statusMessage = "FAILED";
|
||||||
|
} else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
|
||||||
|
statusMessage = "NOT PERMITTED";
|
||||||
|
} else if (status == 133) {
|
||||||
|
statusMessage = "Found the strange 133 bug";
|
||||||
|
} else {
|
||||||
|
statusMessage = "UNKNOWN (" + status + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/30/16.
|
||||||
|
*/
|
||||||
|
public class FrequencyScanResults {
|
||||||
|
public ArrayList<FrequencyTrial> trials = new ArrayList<>();
|
||||||
|
public double bestFrequencyMHz = 0.0;
|
||||||
|
|
||||||
|
public void sort() {
|
||||||
|
Collections.sort(trials, new Comparator<FrequencyTrial>() {
|
||||||
|
@Override
|
||||||
|
public int compare(FrequencyTrial trial1, FrequencyTrial trial2) {
|
||||||
|
return trial1.averageRSSI.compareTo(trial2.averageRSSI);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/30/16.
|
||||||
|
*/
|
||||||
|
public class FrequencyTrial {
|
||||||
|
public int tries = 0;
|
||||||
|
public int successes = 0;
|
||||||
|
public Double averageRSSI = 0.0;
|
||||||
|
public double frequencyMHz = 0.0;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/21/16.
|
||||||
|
*/
|
||||||
|
public class GattAttributes {
|
||||||
|
|
||||||
|
// NOTE: these uuid strings must be lower case!
|
||||||
|
private static Map<String, String> attributes;
|
||||||
|
public static String PREFIX = "0000";
|
||||||
|
public static String SUFFIX = "-0000-1000-8000-00805f9b34fb";
|
||||||
|
public static String SERVICE_GAP = PREFIX + "1800" + SUFFIX;
|
||||||
|
public static String CHARA_GAP_NAME = PREFIX + "2a00" + SUFFIX; // RileyLink RFSpy
|
||||||
|
public static String CHARA_GAP_NUM = PREFIX + "2a01" + SUFFIX; // 0000
|
||||||
|
public static String CHARA_GAP_UNK = PREFIX + "2a01" + SUFFIX; // a
|
||||||
|
|
||||||
|
public static String SERVICE_BATTERY = PREFIX + "180f" + SUFFIX; // Battery
|
||||||
|
public static String CHARA_BATTERY_UNK = PREFIX + "2a19" + SUFFIX;
|
||||||
|
|
||||||
|
// RileyLink Radio Service
|
||||||
|
public static String SERVICE_RADIO = "0235733b-99c5-4197-b856-69219c2a3845";
|
||||||
|
public static String CHARA_RADIO_DATA = "c842e849-5028-42e2-867c-016adada9155";
|
||||||
|
public static String CHARA_RADIO_RESPONSE_COUNT = "6e6c7910-b89e-43a5-a0fe-50c5e2b81f4a";
|
||||||
|
public static String CHARA_RADIO_TIMER_TICK = "6e6c7910-b89e-43a5-78af-50c5e2b86f7e";
|
||||||
|
public static String CHARA_RADIO_CUSTOM_NAME = "d93b2af0-1e28-11e4-8c21-0800200c9a66";
|
||||||
|
public static String CHARA_RADIO_VERSION = "30d99dc9-7c91-4295-a051-0a104d238cf2";
|
||||||
|
public static String CHARA_RADIO_LED_MODE = "c6d84241-f1a7-4f9c-a25f-fce16732f14e";
|
||||||
|
|
||||||
|
|
||||||
|
// table of names for uuids
|
||||||
|
static {
|
||||||
|
attributes = new HashMap<>();
|
||||||
|
|
||||||
|
attributes.put(SERVICE_GAP, "Device Information Service");
|
||||||
|
attributes.put(CHARA_GAP_NAME, "Name"); //
|
||||||
|
attributes.put(CHARA_GAP_NUM, "Number"); //
|
||||||
|
|
||||||
|
|
||||||
|
attributes.put(SERVICE_BATTERY, "Battery Service");
|
||||||
|
|
||||||
|
attributes.put(SERVICE_RADIO, "Radio Interface"); // a
|
||||||
|
attributes.put(CHARA_RADIO_CUSTOM_NAME, "Custom Name");
|
||||||
|
attributes.put(CHARA_RADIO_DATA, "Data");
|
||||||
|
attributes.put(CHARA_RADIO_RESPONSE_COUNT, "Response Count");
|
||||||
|
attributes.put(CHARA_RADIO_TIMER_TICK, "Timer Tick");
|
||||||
|
attributes.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion
|
||||||
|
attributes.put(CHARA_RADIO_LED_MODE, "Led Mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String lookup(UUID uuid) {
|
||||||
|
return lookup(uuid.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String lookup(String uuid) {
|
||||||
|
return lookup(uuid, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String lookup(String uuid, String defaultName) {
|
||||||
|
String name = attributes.get(uuid);
|
||||||
|
return name == null ? defaultName : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO check if service is rileylink
|
||||||
|
public static boolean isRileyLink(UUID uuid) {
|
||||||
|
return attributes.containsKey(uuid.toString());
|
||||||
|
//return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class RFSpyResponse {
|
||||||
|
// 0xaa == timeout
|
||||||
|
// 0xbb == interrupted
|
||||||
|
// 0xcc == zero-data
|
||||||
|
protected byte[] raw;
|
||||||
|
protected RadioResponse radioResponse;
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse() {
|
||||||
|
init(new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFSpyResponse(byte[] bytes) {
|
||||||
|
init(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(byte[] bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
raw = new byte[0];
|
||||||
|
} else {
|
||||||
|
raw = bytes;
|
||||||
|
}
|
||||||
|
if (looksLikeRadioPacket()) {
|
||||||
|
radioResponse = new RadioResponse(raw);
|
||||||
|
} else {
|
||||||
|
radioResponse = new RadioResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RadioResponse getRadioResponse() {
|
||||||
|
return radioResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasTimeout() {
|
||||||
|
if ((raw.length == 1) || (raw.length == 2)) {
|
||||||
|
if (raw[0] == (byte) 0xaa) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasInterrupted() {
|
||||||
|
if ((raw.length == 1) || (raw.length == 2)) {
|
||||||
|
if (raw[0] == (byte) 0xbb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOK() {
|
||||||
|
if ((raw.length == 1) || (raw.length == 2)) {
|
||||||
|
if (raw[0] == (byte) 0x01) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean looksLikeRadioPacket() {
|
||||||
|
if (raw.length > 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRaw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/6/18.
|
||||||
|
*/
|
||||||
|
public interface RLMessage {
|
||||||
|
|
||||||
|
byte[] getTxData();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/6/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum RLMessageType {
|
||||||
|
PowerOn, // for powering on the pump (wakeup)
|
||||||
|
ReadSimpleData, // for checking if pump is readable (for Medtronic we can use GetModel)
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RFTools;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/22/16.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RadioPacket {
|
||||||
|
protected byte[] pkt;
|
||||||
|
|
||||||
|
public RadioPacket(byte[] pkt) {
|
||||||
|
this.pkt = pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRaw() {
|
||||||
|
return pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
byte[] withCRC = ByteUtil.concat(pkt, CRC.crc8(pkt));
|
||||||
|
byte[] encoded = RFTools.encode4b6b(withCRC);
|
||||||
|
byte[] withNullTerm = ByteUtil.concat(encoded, (byte) 0);
|
||||||
|
return withNullTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RFTools;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/30/16.
|
||||||
|
*/
|
||||||
|
public class RadioResponse {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RadioResponse.class);
|
||||||
|
|
||||||
|
public boolean decodedOK = false;
|
||||||
|
public int rssi;
|
||||||
|
public int responseNumber;
|
||||||
|
public byte[] decodedPayload = new byte[0];
|
||||||
|
public byte receivedCRC;
|
||||||
|
|
||||||
|
|
||||||
|
public RadioResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RadioResponse(byte[] rxData) {
|
||||||
|
init(rxData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
if (!decodedOK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (decodedPayload != null) {
|
||||||
|
if (receivedCRC == CRC.crc8(decodedPayload)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(byte[] rxData) {
|
||||||
|
if (rxData == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rxData.length < 3) {
|
||||||
|
// This does not look like something valid heard from a RileyLink device
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rssi = rxData[0];
|
||||||
|
responseNumber = rxData[1];
|
||||||
|
byte[] encodedPayload = ByteUtil.substring(rxData, 2, rxData.length - 2);
|
||||||
|
try {
|
||||||
|
byte[] decodeThis = RFTools.decode4b6b(encodedPayload);
|
||||||
|
decodedOK = true;
|
||||||
|
decodedPayload = ByteUtil.substring(decodeThis, 0, decodeThis.length - 1);
|
||||||
|
byte calculatedCRC = CRC.crc8(decodedPayload);
|
||||||
|
receivedCRC = decodeThis[decodeThis.length - 1];
|
||||||
|
if (receivedCRC != calculatedCRC) {
|
||||||
|
LOG.error("RadioResponse: CRC mismatch, calculated 0x%02x, received 0x%02x", calculatedCRC, receivedCRC);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
decodedOK = false;
|
||||||
|
LOG.error("Failed to decode radio data: " + ByteUtil.shortHexString(encodedPayload));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPayload() {
|
||||||
|
return decodedPayload;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public abstract class BLECommOperation {
|
||||||
|
|
||||||
|
public boolean timedOut = false;
|
||||||
|
public boolean interrupted = false;
|
||||||
|
protected byte[] value;
|
||||||
|
protected BluetoothGatt gatt;
|
||||||
|
protected Semaphore operationComplete = new Semaphore(0, true);
|
||||||
|
|
||||||
|
// This is to be run on the main thread
|
||||||
|
public abstract void execute(RileyLinkBLE comm);
|
||||||
|
|
||||||
|
public void gattOperationCompletionCallback(UUID uuid, byte[] value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGattOperationTimeout_ms() {
|
||||||
|
return 22000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class BLECommOperationResult {
|
||||||
|
public byte[] value;
|
||||||
|
public int resultCode;
|
||||||
|
|
||||||
|
public static final int RESULT_NONE = 0;
|
||||||
|
public static final int RESULT_SUCCESS = 1;
|
||||||
|
public static final int RESULT_TIMEOUT = 2;
|
||||||
|
public static final int RESULT_BUSY = 3;
|
||||||
|
public static final int RESULT_INTERRUPTED = 4;
|
||||||
|
public static final int RESULT_NOT_CONFIGURED = 5;
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class CharacteristicReadOperation extends BLECommOperation {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CharacteristicReadOperation.class);
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic characteristic;
|
||||||
|
|
||||||
|
public CharacteristicReadOperation(BluetoothGatt gatt, BluetoothGattCharacteristic chara) {
|
||||||
|
this.gatt = gatt;
|
||||||
|
this.characteristic = chara;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(RileyLinkBLE comm) {
|
||||||
|
gatt.readCharacteristic(characteristic);
|
||||||
|
// wait here for callback to notify us that value was read.
|
||||||
|
try {
|
||||||
|
boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS);
|
||||||
|
if (didAcquire) {
|
||||||
|
SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier understanding of the sequence of events.
|
||||||
|
// success
|
||||||
|
} else {
|
||||||
|
LOG.error("Timeout waiting for gatt write operation to complete");
|
||||||
|
timedOut = true;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Interrupted while waiting for gatt write operation to complete");
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
|
value = characteristic.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void gattOperationCompletionCallback(UUID uuid, byte[] value) {
|
||||||
|
super.gattOperationCompletionCallback(uuid, value);
|
||||||
|
if (!characteristic.getUuid().equals(uuid)) {
|
||||||
|
LOG.error(String.format("Completion callback: UUID does not match! out of sequence? Found: %s, should be %s",
|
||||||
|
GattAttributes.lookup(characteristic.getUuid()), GattAttributes.lookup(uuid)));
|
||||||
|
}
|
||||||
|
operationComplete.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class CharacteristicWriteOperation extends BLECommOperation {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(CharacteristicWriteOperation.class);
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic characteristic;
|
||||||
|
|
||||||
|
public CharacteristicWriteOperation(BluetoothGatt gatt, BluetoothGattCharacteristic chara, byte[] value) {
|
||||||
|
this.gatt = gatt;
|
||||||
|
this.characteristic = chara;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(RileyLinkBLE comm) {
|
||||||
|
|
||||||
|
characteristic.setValue(value);
|
||||||
|
gatt.writeCharacteristic(characteristic);
|
||||||
|
// wait here for callback to notify us that value was written.
|
||||||
|
try {
|
||||||
|
boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS);
|
||||||
|
if (didAcquire) {
|
||||||
|
SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier understanding of the sequence of events.
|
||||||
|
// success
|
||||||
|
} else {
|
||||||
|
LOG.error("Timeout waiting for gatt write operation to complete");
|
||||||
|
timedOut = true;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Interrupted while waiting for gatt write operation to complete");
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be run on the IBinder thread
|
||||||
|
@Override
|
||||||
|
public void gattOperationCompletionCallback(UUID uuid, byte[] value) {
|
||||||
|
if (!characteristic.getUuid().equals(uuid)) {
|
||||||
|
LOG.error(String.format("Completion callback: UUID does not match! out of sequence? Found: %s, should be %s",
|
||||||
|
GattAttributes.lookup(characteristic.getUuid()), GattAttributes.lookup(uuid)));
|
||||||
|
}
|
||||||
|
operationComplete.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.operations;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class DescriptorWriteOperation extends BLECommOperation {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DescriptorWriteOperation.class);
|
||||||
|
|
||||||
|
private BluetoothGattDescriptor descr;
|
||||||
|
|
||||||
|
|
||||||
|
public DescriptorWriteOperation(BluetoothGatt gatt, BluetoothGattDescriptor descr, byte[] value) {
|
||||||
|
this.gatt = gatt;
|
||||||
|
this.descr = descr;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void gattOperationCompletionCallback(UUID uuid, byte[] value) {
|
||||||
|
super.gattOperationCompletionCallback(uuid, value);
|
||||||
|
operationComplete.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(RileyLinkBLE comm) {
|
||||||
|
descr.setValue(value);
|
||||||
|
gatt.writeDescriptor(descr);
|
||||||
|
// wait here for callback to notify us that value was read.
|
||||||
|
try {
|
||||||
|
boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS);
|
||||||
|
if (didAcquire) {
|
||||||
|
SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier understanding of the sequence of events.
|
||||||
|
// success
|
||||||
|
} else {
|
||||||
|
LOG.error("Timeout waiting for descriptor write operation to complete");
|
||||||
|
timedOut = true;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Interrupted while waiting for descriptor write operation to complete");
|
||||||
|
interrupted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.data;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkErrorCode;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RLHistoryItem {
|
||||||
|
|
||||||
|
String dateTimeString;
|
||||||
|
long dateTime;
|
||||||
|
GregorianCalendar gregorianCalendar;
|
||||||
|
RileyLinkServiceState serviceState;
|
||||||
|
RileyLinkErrorCode errorCode;
|
||||||
|
|
||||||
|
// 2010 10 11 12 30 00
|
||||||
|
|
||||||
|
public RLHistoryItem(GregorianCalendar gregorianCalendar, RileyLinkServiceState serviceState, RileyLinkErrorCode errorCode) {
|
||||||
|
this.dateTime = gregorianCalendar.get(Calendar.SECOND) //
|
||||||
|
+ gregorianCalendar.get(Calendar.MINUTE) * 100 //
|
||||||
|
+ gregorianCalendar.get(Calendar.HOUR_OF_DAY) * 10000
|
||||||
|
+ gregorianCalendar.get(Calendar.DAY_OF_MONTH) * 1000000
|
||||||
|
+ (gregorianCalendar.get(Calendar.MONTH) + 1) * 100000000
|
||||||
|
+ gregorianCalendar.get(Calendar.YEAR) * 10000000000L;
|
||||||
|
|
||||||
|
this.dateTimeString = "" + getNumber(gregorianCalendar.get(Calendar.DAY_OF_MONTH)) + "." + //
|
||||||
|
getNumber(gregorianCalendar.get(Calendar.MONTH) + 1) + "." + //
|
||||||
|
gregorianCalendar.get(Calendar.YEAR) + " " + //
|
||||||
|
getNumber(gregorianCalendar.get(Calendar.HOUR_OF_DAY)) + ":" + //
|
||||||
|
getNumber(gregorianCalendar.get(Calendar.MINUTE)) + ":" + //
|
||||||
|
getNumber(gregorianCalendar.get(Calendar.SECOND)); //
|
||||||
|
|
||||||
|
this.serviceState = serviceState;
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getNumber(int number) {
|
||||||
|
if (number > 9)
|
||||||
|
return "" + number;
|
||||||
|
else
|
||||||
|
return "0" + number;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 14/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum RileyLinkErrorCode {
|
||||||
|
|
||||||
|
ParametersNotSetOrInvalid, //
|
||||||
|
GattDiscoveryFailed, //
|
||||||
|
TuneUpOfPumpFailed, //
|
||||||
|
UnableToObtainBluetoothAdapter, //
|
||||||
|
BluetoothDisabled, //
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,442 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.gxwtech.roundtrip2.RT2Const;
|
||||||
|
import com.gxwtech.roundtrip2.RoundtripService.Tasks.InitializePumpManagerTask;
|
||||||
|
import com.gxwtech.roundtrip2.RoundtripService.Tasks.ServiceTask;
|
||||||
|
import com.gxwtech.roundtrip2.RoundtripService.Tasks.ServiceTaskExecutor;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceResult;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceTransport;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkCommunicationManager;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkConst;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RFSpy;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.data.RLHistoryItem;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicConst;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/6/18.
|
||||||
|
* Split from original file and renamed.
|
||||||
|
*/
|
||||||
|
public abstract class RileyLinkService extends Service {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkService.class);
|
||||||
|
|
||||||
|
|
||||||
|
protected BluetoothAdapter bluetoothAdapter;
|
||||||
|
|
||||||
|
// Our hardware/software connection
|
||||||
|
public RileyLinkBLE rileyLinkBLE; // android-bluetooth management
|
||||||
|
protected RFSpy rfspy; // interface for xxx Mhz (916MHz) radio.
|
||||||
|
protected boolean needBluetoothPermission = true;
|
||||||
|
//protected RileyLinkIPCConnection rileyLinkIPCConnection;
|
||||||
|
protected Context context;
|
||||||
|
public RileyLinkCommunicationManager pumpCommunicationManager;
|
||||||
|
protected BroadcastReceiver mBroadcastReceiver;
|
||||||
|
|
||||||
|
protected RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
|
||||||
|
//protected static final String WAKELOCKNAME = "com.gxwtech.roundtrip2.RoundtripServiceWakeLock";
|
||||||
|
//protected static volatile PowerManager.WakeLock lockStatic = null;
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkService() {
|
||||||
|
super();
|
||||||
|
this.context = MainApp.instance().getApplicationContext();
|
||||||
|
RileyLinkUtil.setContext(this.context);
|
||||||
|
initRileyLinkServiceData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you have customized RileyLinkServiceData you need to override this
|
||||||
|
*/
|
||||||
|
public abstract void initRileyLinkServiceData();
|
||||||
|
// {
|
||||||
|
// rileyLinkServiceData = new RileyLinkServiceData();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onUnbind(Intent intent) {
|
||||||
|
LOG.warn("onUnbind");
|
||||||
|
return super.onUnbind(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRebind(Intent intent) {
|
||||||
|
LOG.warn("onRebind");
|
||||||
|
super.onRebind(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
LOG.error("I die! I die!");
|
||||||
|
|
||||||
|
if (rileyLinkBLE != null) {
|
||||||
|
rileyLinkBLE.disconnect(); // dispose of Gatt (disconnect and close)
|
||||||
|
rileyLinkBLE = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
LOG.debug("onCreate");
|
||||||
|
|
||||||
|
RileyLinkUtil.setRileyLinkServiceData(rileyLinkServiceData);
|
||||||
|
|
||||||
|
//rileyLinkIPCConnection = new RileyLinkIPCConnection(context); // We might be able to remove this -- Andy
|
||||||
|
|
||||||
|
// get most recently used RileyLink address
|
||||||
|
rileyLinkServiceData.rileylinkAddress = SP.getString(MedtronicConst.Prefs.RileyLinkAddress, "");
|
||||||
|
|
||||||
|
rileyLinkBLE = new RileyLinkBLE(this);
|
||||||
|
rfspy = new RFSpy(context, rileyLinkBLE);
|
||||||
|
rfspy.startReader();
|
||||||
|
|
||||||
|
RileyLinkUtil.setRileyLinkBLE(rileyLinkBLE);
|
||||||
|
|
||||||
|
loadPumpCommunicationManager();
|
||||||
|
|
||||||
|
mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
/* here we can listen for local broadcasts, then send ourselves
|
||||||
|
* a specific intent to deal with them, if we wish
|
||||||
|
*/
|
||||||
|
if (intent == null) {
|
||||||
|
LOG.error("onReceive: received null intent");
|
||||||
|
} else {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action == null) {
|
||||||
|
LOG.error("onReceive: null action");
|
||||||
|
} else {
|
||||||
|
if (action.equals(RileyLinkConst.Intents.BluetoothConnected)) {
|
||||||
|
LOG.warn("serviceLocal.bluetooth_connected");
|
||||||
|
//rileyLinkIPCConnection.sendNotification(new ServiceNotification(RT2Const.IPC.MSG_note_FindingRileyLink), null);
|
||||||
|
// ServiceTaskExecutor.startTask(new DiscoverGattServicesTask());
|
||||||
|
rileyLinkBLE.discoverServices();
|
||||||
|
|
||||||
|
|
||||||
|
// If this is successful,
|
||||||
|
// We will get a broadcast of RT2Const.serviceLocal.BLE_services_discovered
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.RileyLinkReady)) {
|
||||||
|
LOG.warn("MedtronicConst.Intents.RileyLinkReady");
|
||||||
|
// FIXME
|
||||||
|
//rileyLinkIPCConnection.sendNotification(new ServiceNotification(RT2Const.IPC.MSG_note_WakingPump), null);
|
||||||
|
rileyLinkBLE.enableNotifications();
|
||||||
|
rfspy.startReader(); // call startReader from outside?
|
||||||
|
ServiceTask task = new InitializePumpManagerTask();
|
||||||
|
ServiceTaskExecutor.startTask(task);
|
||||||
|
LOG.info("Announcing RileyLink open For business");
|
||||||
|
} else if (action.equals(RT2Const.serviceLocal.ipcBound)) {
|
||||||
|
// If we still need permission for bluetooth, ask now.
|
||||||
|
// FIXME removed Andy - doesn't do anything
|
||||||
|
// if (needBluetoothPermission) {
|
||||||
|
// sendBLERequestForAccess();
|
||||||
|
// }
|
||||||
|
|
||||||
|
} else if (RT2Const.IPC.MSG_BLE_accessGranted.equals(action)) {
|
||||||
|
//initializeLeAdapter();
|
||||||
|
//bluetoothInit();
|
||||||
|
} else if (RT2Const.IPC.MSG_BLE_accessDenied.equals(action)) {
|
||||||
|
LOG.error("BLE_Access_Denied recived. Stoping the service.");
|
||||||
|
stopSelf(); // This will stop the service.
|
||||||
|
} else if (action.equals(RT2Const.IPC.MSG_PUMP_tunePump)) {
|
||||||
|
doTunePump();
|
||||||
|
} else if (action.equals(RT2Const.IPC.MSG_PUMP_quickTune)) {
|
||||||
|
doTunePump();
|
||||||
|
} else if (action.startsWith("MSG_PUMP_")) {
|
||||||
|
handlePumpSpecificIntents(intent);
|
||||||
|
} else if (RT2Const.IPC.MSG_ServiceCommand.equals(action)) {
|
||||||
|
handleIncomingServiceTransport(intent);
|
||||||
|
}
|
||||||
|
// } else
|
||||||
|
// if (RT2Const.serviceLocal.INTENT_sessionCompleted.equals(action)) {
|
||||||
|
// Bundle bundle = intent.getBundleExtra(RT2Const.IPC.bundleKey);
|
||||||
|
// if (bundle != null) {
|
||||||
|
// ServiceTransport transport = new ServiceTransport(bundle);
|
||||||
|
// rileyLinkIPCConnection.sendTransport(transport, transport.getSenderHashcode());
|
||||||
|
// } else {
|
||||||
|
// LOG.error("sessionCompleted: no bundle!");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
else {
|
||||||
|
LOG.error("Unhandled broadcast: action=" + action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(RileyLinkConst.Intents.BluetoothConnected);
|
||||||
|
intentFilter.addAction(RileyLinkConst.Intents.BluetoothDisconnected);
|
||||||
|
//intentFilter.addAction(RT2Const.serviceLocal.BLE_services_discovered); AAPS - RileyLinkReady
|
||||||
|
intentFilter.addAction(RT2Const.serviceLocal.ipcBound);
|
||||||
|
intentFilter.addAction(RT2Const.IPC.MSG_BLE_accessGranted);
|
||||||
|
intentFilter.addAction(RT2Const.IPC.MSG_BLE_accessDenied);
|
||||||
|
//intentFilter.addAction(RT2Const.IPC.MSG_BLE_useThisDevice);
|
||||||
|
intentFilter.addAction(RT2Const.IPC.MSG_PUMP_tunePump);
|
||||||
|
|
||||||
|
//intentFilter.addAction(RT2Const.IPC.MSG_PUMP_useThisAddress);
|
||||||
|
|
||||||
|
intentFilter.addAction(RT2Const.IPC.MSG_ServiceCommand);
|
||||||
|
intentFilter.addAction(RT2Const.serviceLocal.INTENT_sessionCompleted);
|
||||||
|
// after AAPS refactoring
|
||||||
|
intentFilter.addAction(RileyLinkConst.Intents.RileyLinkReady);
|
||||||
|
|
||||||
|
|
||||||
|
addPumpSpecificIntents(intentFilter);
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(context).registerReceiver(mBroadcastReceiver, intentFilter);
|
||||||
|
|
||||||
|
LOG.debug("onCreate(): It's ALIVE!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void addPumpSpecificIntents(IntentFilter intentFilter);
|
||||||
|
|
||||||
|
public abstract void handlePumpSpecificIntents(Intent intent);
|
||||||
|
|
||||||
|
public abstract void loadPumpCommunicationManager();
|
||||||
|
|
||||||
|
public abstract void handleIncomingServiceTransport(Intent intent);
|
||||||
|
|
||||||
|
|
||||||
|
// Here is where the wake-lock begins:
|
||||||
|
// We've received a service startCommand, we grab the lock.
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LOG.debug("onStartCommand");
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void bluetoothInit() {
|
||||||
|
LOG.debug("bluetoothInit: attempting to get an adapter");
|
||||||
|
setServiceState(RileyLinkServiceState.InitializingBluetooth);
|
||||||
|
|
||||||
|
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
|
||||||
|
if (bluetoothAdapter == null) {
|
||||||
|
LOG.error("Unable to obtain a BluetoothAdapter.");
|
||||||
|
setServiceState(RileyLinkServiceState.BluetoothNotAvailable, RileyLinkErrorCode.UnableToObtainBluetoothAdapter);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!bluetoothAdapter.isEnabled()) {
|
||||||
|
|
||||||
|
sendBLERequestForAccess();
|
||||||
|
|
||||||
|
LOG.error("Bluetooth is not enabled.");
|
||||||
|
setServiceState(RileyLinkServiceState.BlueToothDisabled, RileyLinkErrorCode.BluetoothDisabled);
|
||||||
|
} else {
|
||||||
|
setServiceState(RileyLinkServiceState.BlueToothEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// returns true if our Rileylink configuration changed
|
||||||
|
public boolean reconfigureRileylink(String deviceAddress) {
|
||||||
|
|
||||||
|
setServiceState(RileyLinkServiceState.RileyLinkInitializing);
|
||||||
|
|
||||||
|
if (rileyLinkBLE.isConnected()) {
|
||||||
|
if (deviceAddress.equals(rileyLinkServiceData.rileylinkAddress)) {
|
||||||
|
LOG.info("No change to RL address. Not reconnecting.");
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
LOG.warn("Disconnecting from old RL (" + rileyLinkServiceData.rileylinkAddress + "), reconnecting to new: " + deviceAddress);
|
||||||
|
rileyLinkBLE.disconnect();
|
||||||
|
// prolly need to shut down listening thread too?
|
||||||
|
SP.putString(MedtronicConst.Prefs.RileyLinkAddress, deviceAddress);
|
||||||
|
|
||||||
|
rileyLinkServiceData.rileylinkAddress = deviceAddress;
|
||||||
|
rileyLinkBLE.findRileyLink(rileyLinkServiceData.rileylinkAddress);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "Using RL " + deviceAddress, Toast.LENGTH_SHORT).show();
|
||||||
|
LOG.debug("handleIPCMessage: Using RL " + deviceAddress);
|
||||||
|
if (bluetoothAdapter == null) {
|
||||||
|
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bluetoothAdapter != null) {
|
||||||
|
if (bluetoothAdapter.isEnabled()) {
|
||||||
|
// FIXME: this may be a long running function:
|
||||||
|
rileyLinkBLE.findRileyLink(deviceAddress);
|
||||||
|
// If successful, we will get a broadcast from RileyLinkBLE: RT2Const.serviceLocal.bluetooth_connected
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
LOG.error("Bluetooth is not enabled.");
|
||||||
|
setServiceState(RileyLinkServiceState.BlueToothDisabled, RileyLinkErrorCode.BluetoothDisabled);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("Failed to get adapter");
|
||||||
|
setServiceState(RileyLinkServiceState.BluetoothNotAvailable, RileyLinkErrorCode.UnableToObtainBluetoothAdapter);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void setServiceState(RileyLinkServiceState newState) {
|
||||||
|
setServiceState(newState, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void setServiceState(RileyLinkServiceState newState, RileyLinkErrorCode errorCode) {
|
||||||
|
this.rileyLinkServiceData.serviceState = newState;
|
||||||
|
|
||||||
|
if (errorCode != null)
|
||||||
|
this.rileyLinkServiceData.errorCode = errorCode;
|
||||||
|
|
||||||
|
RileyLinkUtil.addHistoryEntry(new RLHistoryItem(new GregorianCalendar(), newState, errorCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public synchronized static PowerManager.WakeLock getLock(Context context) {
|
||||||
|
// if (lockStatic == null) {
|
||||||
|
// PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
//
|
||||||
|
// lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCKNAME);
|
||||||
|
// lockStatic.setReferenceCounted(true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return lockStatic;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public void sendServiceTransportResponse(ServiceTransport transport, ServiceResult serviceResult) {
|
||||||
|
|
||||||
|
LOG.warn("UNWANTED: {}", "sendServiceTransportResponse");
|
||||||
|
|
||||||
|
// get the key (hashcode) of the client who requested this
|
||||||
|
Integer clientHashcode = transport.getSenderHashcode();
|
||||||
|
// make a new bundle to send as the message data
|
||||||
|
transport.setServiceResult(serviceResult);
|
||||||
|
// FIXME
|
||||||
|
transport.setTransportType(RT2Const.IPC.MSG_ServiceResult);
|
||||||
|
//rileyLinkIPCConnection.sendTransport(transport, clientHashcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public boolean sendNotification(ServiceNotification notification, Integer clientHashcode) {
|
||||||
|
// //return rileyLinkIPCConnection.sendNotification(notification, clientHashcode);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
protected void sendBLERequestForAccess() {
|
||||||
|
// FIXME
|
||||||
|
//serviceConnection.sendMessage(RT2Const.IPC.MSG_BLE_requestAccess);
|
||||||
|
|
||||||
|
//Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||||
|
//startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: This needs to be run in a session so that is interruptable, has a separate thread, etc.
|
||||||
|
protected void doTunePump() {
|
||||||
|
|
||||||
|
setServiceState(RileyLinkServiceState.TuneUpPump);
|
||||||
|
|
||||||
|
double lastGoodFrequency = 0.0d;
|
||||||
|
|
||||||
|
if (rileyLinkServiceData.lastGoodFrequency == null) {
|
||||||
|
lastGoodFrequency = SP.getDouble(MedtronicConst.Prefs.LastGoodPumpFrequency, 0.0d);
|
||||||
|
} else {
|
||||||
|
lastGoodFrequency = rileyLinkServiceData.lastGoodFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
//double lastGoodFrequency = SP.getFloat(MedtronicConst.Prefs.LastGoodPumpFrequency, 0.0f);
|
||||||
|
double newFrequency;
|
||||||
|
if (lastGoodFrequency != 0.0) {
|
||||||
|
LOG.info("Checking for pump near last saved frequency of {}MHz", lastGoodFrequency);
|
||||||
|
// we have an old frequency, so let's start there.
|
||||||
|
newFrequency = pumpCommunicationManager.quickTuneForPump(lastGoodFrequency);
|
||||||
|
if (newFrequency == 0.0) {
|
||||||
|
// quick scan failed to find pump. Try full scan
|
||||||
|
LOG.warn("Failed to find pump near last saved frequency, doing full scan");
|
||||||
|
newFrequency = pumpCommunicationManager.tuneForPump();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.warn("No saved frequency for pump, doing full scan.");
|
||||||
|
// we don't have a saved frequency, so do the full scan.
|
||||||
|
newFrequency = pumpCommunicationManager.tuneForPump();
|
||||||
|
|
||||||
|
}
|
||||||
|
if ((newFrequency != 0.0) && (newFrequency != lastGoodFrequency)) {
|
||||||
|
LOG.info("Saving new pump frequency of {}MHz", newFrequency);
|
||||||
|
SP.putDouble(MedtronicConst.Prefs.LastGoodPumpFrequency, newFrequency);
|
||||||
|
rileyLinkServiceData.lastGoodFrequency = newFrequency;
|
||||||
|
rileyLinkServiceData.tuneUpDone = true;
|
||||||
|
rileyLinkServiceData.lastTuneUpTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFrequency == 0.0d) {
|
||||||
|
// error tuning pump, pump not present ??
|
||||||
|
//this.errorCode = RileyLinkErrorCode.TuneUpOfPumpFailed;
|
||||||
|
setServiceState(RileyLinkServiceState.RileyLinkReady, RileyLinkErrorCode.TuneUpOfPumpFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PumpInterface
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return this.rileyLinkServiceData.serviceState == RileyLinkServiceState.RileyLinkReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return this.rileyLinkServiceData.serviceState == RileyLinkServiceState.RileyLinkReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnecting() {
|
||||||
|
return this.rileyLinkServiceData.serviceState != RileyLinkServiceState.RileyLinkReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(String reason) {
|
||||||
|
bluetoothInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void disconnect(String reason) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void stopConnecting() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.RileyLinkTargetDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 16/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkServiceData {
|
||||||
|
|
||||||
|
public boolean tuneUpDone = false;
|
||||||
|
public RileyLinkErrorCode errorCode;
|
||||||
|
public RileyLinkServiceState serviceState = RileyLinkServiceState.NotStarted;
|
||||||
|
public String rileylinkAddress;
|
||||||
|
public long lastTuneUpTime = 0L;
|
||||||
|
public Double lastGoodFrequency;
|
||||||
|
|
||||||
|
public RileyLinkTargetDevice targetDevice;
|
||||||
|
|
||||||
|
// Medtronic Pump
|
||||||
|
public String pumpID;
|
||||||
|
public byte[] pumpIDBytes;
|
||||||
|
|
||||||
|
public RileyLinkServiceData(RileyLinkTargetDevice targetDevice) {
|
||||||
|
this.targetDevice = targetDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPumpID(String pumpId, byte[] pumpIdBytes) {
|
||||||
|
this.pumpID = pumpId;
|
||||||
|
this.pumpIDBytes = pumpIdBytes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 14/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum RileyLinkServiceState {
|
||||||
|
|
||||||
|
NotStarted, //
|
||||||
|
|
||||||
|
|
||||||
|
BluetoothNotAvailable, // BT not available, would happen only if device has no BT
|
||||||
|
Initializing, // get all parameters required for connection (if not possible -> Disabled, if sucessful -> EnableBluetooth)
|
||||||
|
|
||||||
|
BlueToothDisabled, // if BT gets disabled ( -> EnableBluetooth)
|
||||||
|
EnableBlueTooth, // enable BT (if error no BT interface -> Disabled, BT not enabled -> BluetoothDisabled)
|
||||||
|
BlueToothEnabled, // -> InitializeRileyLink
|
||||||
|
RileyLinkInitializing, // start Gatt discovery (OK -> RileyLinkInitialized, Error -> BluetoothEnabled) ??
|
||||||
|
RileyLinkInitialized, //
|
||||||
|
RileyLinkReady, //
|
||||||
|
RileyLinkConnected, // -> TuneUpPump (on 1st), else PumpConnectorReady
|
||||||
|
TuneUpPump, // -> PumpConnectorReady
|
||||||
|
PumpConnectorReady, //
|
||||||
|
|
||||||
|
PumpConnected, //
|
||||||
|
|
||||||
|
|
||||||
|
InitializingBluetooth;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks;
|
||||||
|
|
||||||
|
import com.gxwtech.roundtrip2.RoundtripService.Tasks.ServiceTask;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 7/9/16.
|
||||||
|
*/
|
||||||
|
public class DiscoverGattServicesTask extends ServiceTask {
|
||||||
|
|
||||||
|
public DiscoverGattServicesTask() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
RileyLinkUtil.getRileyLinkBLE().discoverServices();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks;
|
||||||
|
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceTransport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 7/9/16.
|
||||||
|
*/
|
||||||
|
public class ServiceTask implements Runnable {
|
||||||
|
private static final String TAG = "ServiceTask(base)";
|
||||||
|
protected ServiceTransport mTransport;
|
||||||
|
public boolean completed = false;
|
||||||
|
|
||||||
|
public ServiceTask() {
|
||||||
|
init(new ServiceTransport());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceTask(ServiceTransport transport) {
|
||||||
|
init(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(ServiceTransport transport) {
|
||||||
|
mTransport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preOp() {
|
||||||
|
// This function is called by UI thread before running asynch thread.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postOp() {
|
||||||
|
// This function is called by UI thread after running asynch thread.
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceTransport getServiceTransport() {
|
||||||
|
return mTransport;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
protected void sendResponse(ServiceResult result) {
|
||||||
|
RoundtripService.getInstance().sendServiceTransportResponse(mTransport,result);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 7/9/16.
|
||||||
|
*/
|
||||||
|
public class ServiceTaskExecutor extends ThreadPoolExecutor {
|
||||||
|
private static final String TAG = "ServiceTaskExecutor";
|
||||||
|
private static ServiceTaskExecutor instance;
|
||||||
|
private static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
instance = new ServiceTaskExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceTaskExecutor() {
|
||||||
|
super(1, 1, 10000, TimeUnit.MILLISECONDS, taskQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceTask startTask(ServiceTask task) {
|
||||||
|
instance.execute(task); // task will be run on async thread from pool.
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void beforeExecute(Thread t, Runnable r) {
|
||||||
|
// This is run on either caller UI thread or Service UI thread.
|
||||||
|
ServiceTask task = (ServiceTask) r;
|
||||||
|
Log.v(TAG, "About to run task " + task.getClass().getSimpleName());
|
||||||
|
RileyLinkUtil.setCurrentTask(task);
|
||||||
|
task.preOp();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
|
// This is run on either caller UI thread or Service UI thread.
|
||||||
|
ServiceTask task = (ServiceTask) r;
|
||||||
|
task.postOp();
|
||||||
|
Log.v(TAG, "Finishing task " + task.getClass().getSimpleName());
|
||||||
|
RileyLinkUtil.finishCurrentTask(task);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import com.gxwtech.roundtrip2.RT2Const;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceCommand;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceMessage;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceNotification;
|
||||||
|
import com.gxwtech.roundtrip2.ServiceData.ServiceResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 7/6/16.
|
||||||
|
* <p>
|
||||||
|
* This class exists to hold a ServiceCommand along with transport variables
|
||||||
|
* such as time sent, time received, sender.
|
||||||
|
* May also contain result, if the command is completed.
|
||||||
|
*/
|
||||||
|
public class ServiceTransport extends ServiceMessage {
|
||||||
|
public ServiceTransport() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceTransport(Bundle b) {
|
||||||
|
if (b != null) {
|
||||||
|
if ("ServiceTransport".equals(b.getString("ServiceMessageType"))) {
|
||||||
|
setMap(b);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
super.init();
|
||||||
|
map.putString("ServiceMessageType", "ServiceTransport");
|
||||||
|
setTransportType("unknown");
|
||||||
|
setSenderHashcode(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSenderHashcode(Integer senderHashcode) {
|
||||||
|
map.putInt("senderHashcode", senderHashcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSenderHashcode() {
|
||||||
|
return new Integer(map.getInt("senderHashCode", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceCommand(ServiceCommand serviceCommand) {
|
||||||
|
map.putBundle("ServiceCommand", serviceCommand.getMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceCommand getServiceCommand() {
|
||||||
|
return new ServiceCommand(map.getBundle("ServiceCommand"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasServiceCommand() {
|
||||||
|
return (getMap().containsKey("ServiceCommand"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// On remote end, this will be converted to the "action" of a local Intent,
|
||||||
|
// so can be used for separating types of messages to different internal handlers.
|
||||||
|
public void setTransportType(String transportType) {
|
||||||
|
map.putString("transportType", transportType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTransportType() {
|
||||||
|
return map.getString("transportType", "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceResult(ServiceResult serviceResult) {
|
||||||
|
map.putBundle("ServiceResult", serviceResult.getMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceResult getServiceResult() {
|
||||||
|
return new ServiceResult(map.getBundle("ServiceResult"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasServiceResult() {
|
||||||
|
return (getMap().containsKey("ServiceResult"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceNotification(ServiceNotification notification) {
|
||||||
|
map.putBundle("ServiceNotification", notification.getMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceNotification getServiceNotification() {
|
||||||
|
return new ServiceNotification(map.getBundle("ServiceNotification"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasServiceNotification() {
|
||||||
|
return (getMap().containsKey("ServiceNotification"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean commandDidCompleteOK() {
|
||||||
|
return getServiceResult().resultIsOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalCommandName() {
|
||||||
|
return getServiceCommand().getCommandName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String describeContentsShort() {
|
||||||
|
String rval = "";
|
||||||
|
rval += getTransportType();
|
||||||
|
if (RT2Const.IPC.MSG_ServiceNotification.equals(getTransportType())) {
|
||||||
|
rval += "note: " + getServiceNotification().getNotificationType();
|
||||||
|
} else if (RT2Const.IPC.MSG_ServiceCommand.equals(getTransportType())) {
|
||||||
|
rval += ", cmd=" + getOriginalCommandName();
|
||||||
|
} else if (RT2Const.IPC.MSG_ServiceResult.equals(getTransportType())) {
|
||||||
|
rval += ", cmd=" + getOriginalCommandName();
|
||||||
|
rval += ", rslt=" + getServiceResult().getResult();
|
||||||
|
rval += ", err=" + getServiceResult().getErrorDescription();
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceTransport clone() {
|
||||||
|
Parcel p = Parcel.obtain();
|
||||||
|
Parcel p2 = Parcel.obtain();
|
||||||
|
getMap().writeToParcel(p, 0);
|
||||||
|
byte[] bytes = p.marshall();
|
||||||
|
p2.unmarshall(bytes, 0, bytes.length);
|
||||||
|
p2.setDataPosition(0);
|
||||||
|
Bundle b = p2.readBundle();
|
||||||
|
ServiceTransport rval = new ServiceTransport();
|
||||||
|
rval.setMap(b);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 4/28/15.
|
||||||
|
*/
|
||||||
|
public class ByteUtil {
|
||||||
|
private final static char[] HEX_DIGITS = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
private final static String HEX_DIGITS_STR = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
public static byte highByte(short s) {
|
||||||
|
return (byte) (s / 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte lowByte(short s) {
|
||||||
|
return (byte) (s % 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int asUINT8(byte b) {
|
||||||
|
return (b < 0) ? b + 256 : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Reference: static void System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length) */
|
||||||
|
|
||||||
|
public static byte[] concat(byte[] a, byte[] b) {
|
||||||
|
int aLen = a.length;
|
||||||
|
int bLen = b.length;
|
||||||
|
byte[] c = new byte[aLen + bLen];
|
||||||
|
System.arraycopy(a, 0, c, 0, aLen);
|
||||||
|
System.arraycopy(b, 0, c, aLen, bLen);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] concat(byte[] a, byte b) {
|
||||||
|
int aLen = a.length;
|
||||||
|
byte[] c = new byte[aLen + 1];
|
||||||
|
System.arraycopy(a, 0, c, 0, aLen);
|
||||||
|
c[aLen] = b;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] substring(byte[] a, int start, int len) {
|
||||||
|
byte[] rval = new byte[len];
|
||||||
|
System.arraycopy(a, start, rval, 0, len);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String shortHexString(byte[] ra) {
|
||||||
|
String rval = "";
|
||||||
|
if (ra == null) {
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
if (ra.length == 0) {
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ra.length; i++) {
|
||||||
|
rval = rval + HEX_DIGITS[(ra[i] & 0xF0) >> 4];
|
||||||
|
rval = rval + HEX_DIGITS[(ra[i] & 0x0F)];
|
||||||
|
if (i < ra.length - 1) {
|
||||||
|
rval = rval + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String showPrintable(byte[] ra) {
|
||||||
|
String s = new String();
|
||||||
|
for (int i = 0; i < ra.length; i++) {
|
||||||
|
char c = (char) ra[i];
|
||||||
|
if (((c >= '0') && (c <= '9')) ||
|
||||||
|
((c >= 'A') && (c <= 'Z')) ||
|
||||||
|
((c >= 'a') && (c <= 'z'))) {
|
||||||
|
s = s + c;
|
||||||
|
} else {
|
||||||
|
s = s + '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] fromHexString(String src) {
|
||||||
|
String s = src.toUpperCase();
|
||||||
|
byte[] rval = new byte[]{};
|
||||||
|
if ((s.length() % 2) != 0) {
|
||||||
|
// invalid hex string!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s.length(); i += 2) {
|
||||||
|
int highNibbleOrd = HEX_DIGITS_STR.indexOf(s.charAt(i));
|
||||||
|
if (highNibbleOrd < 0) {
|
||||||
|
// Not a hex digit.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int lowNibbleOrd = HEX_DIGITS_STR.indexOf(s.charAt(i + 1));
|
||||||
|
if (lowNibbleOrd < 0) {
|
||||||
|
// Not a hex digit
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
rval = concat(rval, (byte) (highNibbleOrd * 16 + lowNibbleOrd));
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] fromByteArray(List<Byte> byteArray) {
|
||||||
|
byte[] rval = new byte[byteArray.size()];
|
||||||
|
for (int i = 0; i < byteArray.size(); i++) {
|
||||||
|
rval[i] = byteArray.get(i);
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Byte> toByteArray(byte[] data) {
|
||||||
|
ArrayList<Byte> rval = new ArrayList<>(data.length);
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
rval.add(i, new Byte(data[i]));
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compares byte strings like strcmp
|
||||||
|
public static int compare(byte[] s1, byte[] s2) {
|
||||||
|
int i;
|
||||||
|
int len1 = s1.length;
|
||||||
|
int len2 = s2.length;
|
||||||
|
if (len1 > len2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (len2 > len1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int acc = 0;
|
||||||
|
for (i = 0; i < len1; i++) {
|
||||||
|
acc += s1[i];
|
||||||
|
acc -= s2[i];
|
||||||
|
if (acc != 0) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 4/27/15.
|
||||||
|
*/
|
||||||
|
public class CRC {
|
||||||
|
|
||||||
|
static final int[] crc8lookup = new int[]{0, 155, 173, 54, 193, 90, 108, 247, 25, 130, 180, 47,
|
||||||
|
216, 67, 117, 238, 50, 169, 159, 4, 243, 104, 94, 197, 43, 176,
|
||||||
|
134, 29, 234, 113, 71, 220, 100, 255, 201, 82, 165, 62, 8, 147,
|
||||||
|
125, 230, 208, 75, 188, 39, 17, 138, 86, 205, 251, 96, 151, 12,
|
||||||
|
58, 161, 79, 212, 226, 121, 142, 21, 35, 184, 200, 83, 101, 254,
|
||||||
|
9, 146, 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, 250, 97,
|
||||||
|
87, 204, 59, 160, 150, 13, 227, 120, 78, 213, 34, 185, 143, 20,
|
||||||
|
172, 55, 1, 154, 109, 246, 192, 91, 181, 46, 24, 131, 116, 239,
|
||||||
|
217, 66, 158, 5, 51, 168, 95, 196, 242, 105, 135, 28, 42, 177,
|
||||||
|
70, 221, 235, 112, 11, 144, 166, 61, 202, 81, 103, 252, 18, 137,
|
||||||
|
191, 36, 211, 72, 126, 229, 57, 162, 148, 15, 248, 99, 85, 206,
|
||||||
|
32, 187, 141, 22, 225, 122, 76, 215, 111, 244, 194, 89, 174, 53,
|
||||||
|
3, 152, 118, 237, 219, 64, 183, 44, 26, 129, 93, 198, 240, 107,
|
||||||
|
156, 7, 49, 170, 68, 223, 233, 114, 133, 30, 40, 179, 195, 88,
|
||||||
|
110, 245, 2, 153, 175, 52, 218, 65, 119, 236, 27, 128, 182, 45,
|
||||||
|
241, 106, 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, 41, 178,
|
||||||
|
132, 31, 167, 60, 10, 145, 102, 253, 203, 80, 190, 37, 19, 136,
|
||||||
|
127, 228, 210, 73, 149, 14, 56, 163, 84, 207, 249, 98, 140, 23,
|
||||||
|
33, 186, 77, 214, 224, 123};
|
||||||
|
|
||||||
|
|
||||||
|
public static byte crc8(byte[] data, int len) {
|
||||||
|
byte result = 0;
|
||||||
|
if (data == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (len > data.length) {
|
||||||
|
len = data.length;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int tmp = result;
|
||||||
|
int tmp2 = tmp ^ data[i];
|
||||||
|
int tmp3 = tmp2 & 0xFF;
|
||||||
|
int idx = tmp3;
|
||||||
|
result = (byte) crc8lookup[idx];
|
||||||
|
// log(String.format("iter=%d,tmp=0x%02x, tmp2=0x%02x, tmp3=0x%02x, lookup=0x%02x",i,tmp,tmp2,tmp3,result));
|
||||||
|
}
|
||||||
|
// orig python:
|
||||||
|
//result = klass.lookup[ ( result ^ block[ i ] & 0xFF ) ]
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte crc8(byte[] data) {
|
||||||
|
return crc8(data, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] calculate16CCITT(byte[] data) {
|
||||||
|
int crc = 0xFFFF;
|
||||||
|
int polynomial = 0x1021;
|
||||||
|
if (data != null) {
|
||||||
|
if (data.length > 0) {
|
||||||
|
for (int j = 0; j < data.length; j++) {
|
||||||
|
byte b = data[j];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
boolean bit = ((b >> (7 - i) & 1) == 1);
|
||||||
|
boolean c15 = ((crc >> 15 & 1) == 1);
|
||||||
|
crc <<= 1;
|
||||||
|
if (c15 ^ bit) crc ^= polynomial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crc &= 0xffff;
|
||||||
|
return new byte[]{(byte) ((crc & 0xFF00) >> 8), (byte) (crc & 0xFF)};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2006 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone of Android's HexDump class, for use in debugging. Cosmetic changes
|
||||||
|
* only.
|
||||||
|
*/
|
||||||
|
public class HexDump {
|
||||||
|
private final static char[] HEX_DIGITS = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
public static String dumpHexString(byte[] array) {
|
||||||
|
return dumpHexString(array, 0, array.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dumpHexString(byte[] array, int offset, int length) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
|
byte[] line = new byte[16];
|
||||||
|
int lineIndex = 0;
|
||||||
|
|
||||||
|
result.append("\n0x");
|
||||||
|
result.append(toHexString(offset));
|
||||||
|
|
||||||
|
for (int i = offset; i < offset + length; i++) {
|
||||||
|
if (lineIndex == 16) {
|
||||||
|
result.append(" ");
|
||||||
|
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
if (line[j] > ' ' && line[j] < '~') {
|
||||||
|
result.append(new String(line, j, 1));
|
||||||
|
} else {
|
||||||
|
result.append(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append("\n0x");
|
||||||
|
result.append(toHexString(i));
|
||||||
|
lineIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte b = array[i];
|
||||||
|
result.append(" ");
|
||||||
|
result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
|
||||||
|
result.append(HEX_DIGITS[b & 0x0F]);
|
||||||
|
|
||||||
|
line[lineIndex++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineIndex != 16) {
|
||||||
|
int count = (16 - lineIndex) * 3;
|
||||||
|
count++;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
result.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < lineIndex; i++) {
|
||||||
|
if (line[i] > ' ' && line[i] < '~') {
|
||||||
|
result.append(new String(line, i, 1));
|
||||||
|
} else {
|
||||||
|
result.append(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(byte b) {
|
||||||
|
return toHexString(toByteArray(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(byte[] array) {
|
||||||
|
return toHexString(array, 0, array.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(byte[] array, int offset, int length) {
|
||||||
|
char[] buf = new char[length * 2];
|
||||||
|
|
||||||
|
int bufIndex = 0;
|
||||||
|
for (int i = offset; i < offset + length; i++) {
|
||||||
|
byte b = array[i];
|
||||||
|
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
|
||||||
|
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(int i) {
|
||||||
|
return toHexString(toByteArray(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(short i) {
|
||||||
|
return toHexString(toByteArray(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(byte b) {
|
||||||
|
byte[] array = new byte[1];
|
||||||
|
array[0] = b;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(int i) {
|
||||||
|
byte[] array = new byte[4];
|
||||||
|
|
||||||
|
array[3] = (byte) (i & 0xFF);
|
||||||
|
array[2] = (byte) ((i >> 8) & 0xFF);
|
||||||
|
array[1] = (byte) ((i >> 16) & 0xFF);
|
||||||
|
array[0] = (byte) ((i >> 24) & 0xFF);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(short i) {
|
||||||
|
byte[] array = new byte[2];
|
||||||
|
|
||||||
|
array[1] = (byte) (i & 0xFF);
|
||||||
|
array[0] = (byte) ((i >> 8) & 0xFF);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int toByte(char c) {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return (c - '0');
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return (c - 'A' + 10);
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return (c - 'a' + 10);
|
||||||
|
|
||||||
|
throw new RuntimeException("Invalid hex char '" + c + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hexStringToByteArray(String hexString) {
|
||||||
|
int length = hexString.length();
|
||||||
|
byte[] buffer = new byte[length / 2];
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i += 2) {
|
||||||
|
buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString
|
||||||
|
.charAt(i + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.utils;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 4/28/15.
|
||||||
|
*/
|
||||||
|
public class StringUtil {
|
||||||
|
|
||||||
|
public static String fromBytes(byte[] ra) {
|
||||||
|
return new String(ra, Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// these should go in some project-wide string utils package
|
||||||
|
public static String join(ArrayList<String> ra, String joiner) {
|
||||||
|
int sz = ra.size();
|
||||||
|
String rval = "";
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < sz; n++) {
|
||||||
|
rval = rval + ra.get(n);
|
||||||
|
if (n < sz - 1) {
|
||||||
|
rval = rval + joiner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String testJoin() {
|
||||||
|
ArrayList<String> ra = new ArrayList<String>();
|
||||||
|
ra.add("one");
|
||||||
|
ra.add("two");
|
||||||
|
ra.add("three");
|
||||||
|
return join(ra, "+");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/27/16.
|
||||||
|
*/
|
||||||
|
public class ThreadUtil {
|
||||||
|
public static long getThreadId() {
|
||||||
|
return Thread.currentThread().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getThreadName() {
|
||||||
|
return Thread.currentThread().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sig() {
|
||||||
|
Thread t = Thread.currentThread();
|
||||||
|
return t.getName() + "[" + t.getId() + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,10 +30,11 @@ import info.nightscout.androidaps.events.EventTempBasalChange;
|
||||||
import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
|
import info.nightscout.androidaps.plugins.Common.SubscriberFragment;
|
||||||
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
import info.nightscout.androidaps.plugins.PumpCommon.data.PumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.dialog.RileylinkSettingsActivity;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump;
|
import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog;
|
import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity;
|
import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.events.EventMedtronicNewStatus;
|
||||||
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
|
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
|
||||||
import info.nightscout.androidaps.queue.events.EventQueueChanged;
|
import info.nightscout.androidaps.queue.events.EventQueueChanged;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.utils.DateUtil;
|
||||||
|
@ -98,6 +99,7 @@ public class MedtronicFragment extends SubscriberFragment {
|
||||||
loopHandler.postDelayed(refreshLoop, 60 * 1000L);
|
loopHandler.postDelayed(refreshLoop, 60 * 1000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
@ -133,10 +135,10 @@ public class MedtronicFragment extends SubscriberFragment {
|
||||||
profileViewDialog.show(manager, "ProfileViewDialog");
|
profileViewDialog.show(manager, "ProfileViewDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
//@OnClick(R.id.medtronic_stats)
|
@OnClick(R.id.medtronic_stats)
|
||||||
//void onStatsClick() {
|
void onStatsClick() {
|
||||||
// startActivity(new Intent(getContext(), DanaRStatsActivity.class));
|
startActivity(new Intent(getContext(), RileylinkSettingsActivity.class));
|
||||||
//}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.medtronic_btconnection)
|
@OnClick(R.id.medtronic_btconnection)
|
||||||
void onBtConnectionClick() {
|
void onBtConnectionClick() {
|
||||||
|
@ -174,7 +176,7 @@ public class MedtronicFragment extends SubscriberFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onStatusEvent(final EventDanaRNewStatus s) {
|
public void onStatusEvent(final EventMedtronicNewStatus s) {
|
||||||
updateGUI();
|
updateGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,12 +248,6 @@ public class MedtronicFragment extends SubscriberFragment {
|
||||||
SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d);
|
SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d);
|
||||||
iobView.setText(pump.iob + " U");
|
iobView.setText(pump.iob + " U");
|
||||||
|
|
||||||
//if (pump.isNewPump) {
|
|
||||||
// firmwareView.setText(String.format(MainApp.sResources.getString(R.string.danar_model), pump.model, pump.protocol, pump.productCode));
|
|
||||||
//} else {
|
|
||||||
// firmwareView.setText("OLD");
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
if (queueView != null) {
|
if (queueView != null) {
|
||||||
// FIXME
|
// FIXME
|
||||||
|
|
|
@ -1,23 +1,21 @@
|
||||||
package info.nightscout.androidaps.plugins.PumpMedtronic;
|
package info.nightscout.androidaps.plugins.PumpMedtronic;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import android.content.ComponentName;
|
||||||
import org.json.JSONObject;
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.BuildConfig;
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
|
||||||
import info.nightscout.androidaps.R;
|
import info.nightscout.androidaps.R;
|
||||||
import info.nightscout.androidaps.data.Profile;
|
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||||
import info.nightscout.androidaps.db.ExtendedBolus;
|
import info.nightscout.androidaps.interfaces.PluginType;
|
||||||
import info.nightscout.androidaps.db.TemporaryBasal;
|
|
||||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.PumpPluginAbstract;
|
import info.nightscout.androidaps.plugins.PumpCommon.PumpPluginAbstract;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpDriver;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpDriver;
|
||||||
import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpStatus;
|
||||||
import info.nightscout.utils.DateUtil;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.service.RileyLinkMedtronicService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by andy on 23.04.18.
|
* Created by andy on 23.04.18.
|
||||||
|
@ -29,8 +27,11 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
|
|
||||||
//private ServiceClientConnection serviceClientConnection;
|
//private ServiceClientConnection serviceClientConnection;
|
||||||
|
|
||||||
|
private RileyLinkMedtronicService medtronicService;
|
||||||
|
protected static MedtronicPumpPlugin plugin = null;
|
||||||
|
protected MedtronicPumpStatus pumpStatusLocal = null;
|
||||||
|
|
||||||
public static PumpPluginAbstract getPlugin() {
|
public static MedtronicPumpPlugin getPlugin() {
|
||||||
|
|
||||||
if (plugin == null)
|
if (plugin == null)
|
||||||
plugin = new MedtronicPumpPlugin();
|
plugin = new MedtronicPumpPlugin();
|
||||||
|
@ -41,71 +42,61 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
private MedtronicPumpPlugin() {
|
private MedtronicPumpPlugin() {
|
||||||
super(new MedtronicPumpDriver(), //
|
super(new MedtronicPumpDriver(), //
|
||||||
"MedtronicPump", //
|
"MedtronicPump", //
|
||||||
MedtronicFragment.class.getName(), //
|
new PluginDescription() //
|
||||||
R.string.medtronic_name, //
|
.mainType(PluginType.PUMP) //
|
||||||
R.string.medtronic_name_short //
|
.fragmentClass(MedtronicFragment.class.getName()) //
|
||||||
|
.pluginName(R.string.medtronic_name) //
|
||||||
|
.shortName(R.string.medtronic_name_short) //
|
||||||
|
.preferencesId(R.xml.pref_medtronic), //
|
||||||
|
PumpType.Minimed_512_712 // we default to most basic model, correct model from config is loaded later
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
|
serviceConnection = new ServiceConnection() {
|
||||||
|
|
||||||
@Override
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
protected void startPumpService() {
|
LOG.debug("Service is disconnected");
|
||||||
|
medtronicService = null;
|
||||||
|
}
|
||||||
|
|
||||||
//serviceClientConnection = new ServiceClientConnection();
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
}
|
LOG.debug("Service is connected");
|
||||||
|
RileyLinkMedtronicService.LocalBinder mLocalBinder = (RileyLinkMedtronicService.LocalBinder) service;
|
||||||
|
medtronicService = mLocalBinder.getServiceInstance();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void stopPumpService() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSONObject getJSONStatus(Profile profile, String profileName) {
|
public void initPumpStatusData() {
|
||||||
//if (!SP.getBoolean("virtualpump_uploadstatus", false)) {
|
pumpStatusLocal = new MedtronicPumpStatus(pumpDescription);
|
||||||
// return null;
|
pumpStatusLocal.refreshConfiguration();
|
||||||
//}
|
|
||||||
JSONObject pump = new JSONObject();
|
|
||||||
JSONObject battery = new JSONObject();
|
|
||||||
JSONObject status = new JSONObject();
|
|
||||||
JSONObject extended = new JSONObject();
|
|
||||||
try {
|
|
||||||
battery.put("percent", 90);
|
|
||||||
status.put("status", "normal");
|
|
||||||
extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
|
|
||||||
try {
|
|
||||||
extended.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
TemporaryBasal tb = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis());
|
this.pumpStatus = pumpStatusLocal;
|
||||||
if (tb != null) {
|
|
||||||
extended.put("TempBasalAbsoluteRate", tb.tempBasalConvertedToAbsolute(System.currentTimeMillis(), profile));
|
|
||||||
extended.put("TempBasalStart", DateUtil.dateAndTimeString(tb.date));
|
|
||||||
extended.put("TempBasalRemaining", tb.getPlannedRemainingMinutes());
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedBolus eb = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis());
|
if (pumpStatusLocal.maxBasal != null)
|
||||||
if (eb != null) {
|
pumpDescription.maxTempAbsolute = (pumpStatusLocal.maxBasal != null) ? pumpStatusLocal.maxBasal : 35.0d;
|
||||||
extended.put("ExtendedBolusAbsoluteRate", eb.absoluteRate());
|
|
||||||
extended.put("ExtendedBolusStart", DateUtil.dateAndTimeString(eb.date));
|
|
||||||
extended.put("ExtendedBolusRemaining", eb.getPlannedRemainingMinutes());
|
|
||||||
}
|
|
||||||
|
|
||||||
status.put("timestamp", DateUtil.toISOString(new Date()));
|
// needs to be changed in configuration, after all functionalities are done
|
||||||
|
pumpDescription.isBolusCapable = false;
|
||||||
|
pumpDescription.isTempBasalCapable = true;
|
||||||
|
pumpDescription.isExtendedBolusCapable = false;
|
||||||
|
pumpDescription.isSetBasalProfileCapable = true;
|
||||||
|
pumpDescription.isRefillingCapable = false;
|
||||||
|
pumpDescription.storesCarbInfo = false;
|
||||||
|
|
||||||
pump.put("battery", battery);
|
|
||||||
pump.put("status", status);
|
|
||||||
pump.put("extended", extended);
|
|
||||||
pump.put("reservoir", 66);
|
|
||||||
pump.put("clock", DateUtil.toISOString(new Date()));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
LOG.error("Unhandled exception", e);
|
|
||||||
}
|
|
||||||
return pump;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onStartCustomActions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getServiceClass() {
|
||||||
|
return RileyLinkMedtronicService.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String deviceID() {
|
public String deviceID() {
|
||||||
return "Medtronic";
|
return "Medtronic";
|
||||||
|
@ -123,10 +114,4 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPreferencesId() {
|
|
||||||
return R.xml.pref_medtronic;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,547 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.joda.time.IllegalFieldValueException;
|
||||||
|
import org.joda.time.Instant;
|
||||||
|
import org.joda.time.LocalDateTime;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkCommunicationManager;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.RFSpy;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RLMessage;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.data.RLMessageType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.BasalProfile;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.Page;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.RawHistoryPage;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.TempBasalPair;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.Record;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.ButtonPressCarelinkMessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.CarelinkShortMessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.GetHistoryPageCarelinkMessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.GetPumpModelCarelinkMessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.MessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.MessageType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PacketType;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PumpAckMessageBody;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.PumpMessage;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.service.RileyLinkMedtronicService;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
|
||||||
|
|
||||||
|
//import com.gxwtech.roundtrip2.ServiceData.ServiceResult;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/30/16.
|
||||||
|
* <p>
|
||||||
|
* Split into 2 implementations, so that we can split it by target device. - Andy
|
||||||
|
*/
|
||||||
|
public class MedtronicCommunicationManager extends RileyLinkCommunicationManager {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(MedtronicCommunicationManager.class);
|
||||||
|
|
||||||
|
private static double[] scanFrequenciesUS = {916.45, 916.50, 916.55, 916.60, 916.65, 916.70, 916.75, 916.80};
|
||||||
|
private static double[] scanFrequenciesWorldwide = {868.25, 868.30, 868.35, 868.40, 868.45, 868.50, 868.55, 868.60, 868.65};
|
||||||
|
|
||||||
|
byte[] pumpID;
|
||||||
|
|
||||||
|
public MedtronicCommunicationManager(Context context, RFSpy rfspy, boolean hasUSfrequency, byte[] pumpID) {
|
||||||
|
super(context, rfspy, hasUSfrequency ? scanFrequenciesUS : scanFrequenciesWorldwide);
|
||||||
|
this.pumpID = pumpID;
|
||||||
|
//prefs = context.getSharedPreferences(RT2Const.serviceLocal.sharedPreferencesKey, Context.MODE_PRIVATE);
|
||||||
|
this.pumpStatus = MedtronicPumpPlugin.getPlugin().getPumpStatusData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PumpMessage runCommandWithArgs(PumpMessage msg) {
|
||||||
|
PumpMessage rval;
|
||||||
|
PumpMessage shortMessage = makePumpMessage(msg.messageType, new CarelinkShortMessageBody(new byte[]{0}));
|
||||||
|
// look for ack from short message
|
||||||
|
PumpMessage shortResponse = sendAndListen(shortMessage);
|
||||||
|
if (shortResponse.messageType == MessageType.PumpAck) {
|
||||||
|
rval = sendAndListen(msg);
|
||||||
|
return rval;
|
||||||
|
} else {
|
||||||
|
LOG.error("runCommandWithArgs: Pump did not ack Attention packet");
|
||||||
|
}
|
||||||
|
return new PumpMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Page getPumpHistoryPage(int pageNumber) {
|
||||||
|
RawHistoryPage rval = new RawHistoryPage();
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
PumpMessage getHistoryMsg = makePumpMessage(MessageType.GetHistoryPage, new GetHistoryPageCarelinkMessageBody(pageNumber));
|
||||||
|
//LOG.info("getPumpHistoryPage("+pageNumber+"): "+ByteUtil.shortHexString(getHistoryMsg.getTxData()));
|
||||||
|
// Ask the pump to transfer history (we get first frame?)
|
||||||
|
PumpMessage firstResponse = runCommandWithArgs(getHistoryMsg);
|
||||||
|
//LOG.info("getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents()));
|
||||||
|
|
||||||
|
PumpMessage ackMsg = makePumpMessage(MessageType.PumpAck, new PumpAckMessageBody());
|
||||||
|
GetHistoryPageCarelinkMessageBody currentResponse = new GetHistoryPageCarelinkMessageBody(firstResponse.getMessageBody().getTxData());
|
||||||
|
int expectedFrameNum = 1;
|
||||||
|
boolean done = false;
|
||||||
|
//while (expectedFrameNum == currentResponse.getFrameNumber()) {
|
||||||
|
int failures = 0;
|
||||||
|
while (!done) {
|
||||||
|
// examine current response for problems.
|
||||||
|
byte[] frameData = currentResponse.getFrameData();
|
||||||
|
if ((frameData != null) && (frameData.length > 0) && currentResponse.getFrameNumber() == expectedFrameNum) {
|
||||||
|
// success! got a frame.
|
||||||
|
if (frameData.length != 64) {
|
||||||
|
LOG.warn("Expected frame of length 64, got frame of length " + frameData.length);
|
||||||
|
// but append it anyway?
|
||||||
|
}
|
||||||
|
// handle successful frame data
|
||||||
|
rval.appendData(currentResponse.getFrameData());
|
||||||
|
RileyLinkMedtronicService.getInstance().announceProgress(((100 / 16) * currentResponse.getFrameNumber() + 1));
|
||||||
|
LOG.info("getPumpHistoryPage: Got frame " + currentResponse.getFrameNumber());
|
||||||
|
// Do we need to ask for the next frame?
|
||||||
|
if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722
|
||||||
|
expectedFrameNum++;
|
||||||
|
} else {
|
||||||
|
done = true; // successful completion
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (frameData == null) {
|
||||||
|
LOG.error("null frame data, retrying");
|
||||||
|
} else if (currentResponse.getFrameNumber() != expectedFrameNum) {
|
||||||
|
LOG.warn("Expected frame number %d, received %d (retrying)", expectedFrameNum, currentResponse.getFrameNumber());
|
||||||
|
} else if (frameData.length == 0) {
|
||||||
|
LOG.warn("Frame has zero length, retrying");
|
||||||
|
}
|
||||||
|
failures++;
|
||||||
|
if (failures == 6) {
|
||||||
|
LOG.error("6 failures in attempting to download frame %d of page %d, giving up.", expectedFrameNum, pageNumber);
|
||||||
|
done = true; // failure completion.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done) {
|
||||||
|
// ask for next frame
|
||||||
|
PumpMessage nextMsg = sendAndListen(ackMsg);
|
||||||
|
currentResponse = new GetHistoryPageCarelinkMessageBody(nextMsg.getMessageBody().getTxData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rval.getLength() != 1024) {
|
||||||
|
LOG.warn("getPumpHistoryPage: short page. Expected length of 1024, found length of " + rval.getLength());
|
||||||
|
}
|
||||||
|
if (!rval.isChecksumOK()) {
|
||||||
|
LOG.error("getPumpHistoryPage: checksum is wrong");
|
||||||
|
}
|
||||||
|
|
||||||
|
rval.dumpToDebug();
|
||||||
|
|
||||||
|
Page page = new Page();
|
||||||
|
//page.parseFrom(rval.getData(),PumpModel.MM522);
|
||||||
|
// FIXME
|
||||||
|
page.parseFrom(rval.getData(), PumpModel.MM522);
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Page> getAllHistoryPages() {
|
||||||
|
ArrayList<Page> pages = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int pageNum = 0; pageNum < 16; pageNum++) {
|
||||||
|
pages.add(getPumpHistoryPage(pageNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Page> getHistoryEventsSinceDate(Instant when) {
|
||||||
|
ArrayList<Page> pages = new ArrayList<>();
|
||||||
|
for (int pageNum = 0; pageNum < 16; pageNum++) {
|
||||||
|
pages.add(getPumpHistoryPage(pageNum));
|
||||||
|
for (Page page : pages) {
|
||||||
|
for (Record r : page.mRecordList) {
|
||||||
|
LocalDateTime timestamp = r.getTimestamp().getLocalDateTime();
|
||||||
|
LOG.info("Found record: (" + r.getClass().getSimpleName() + ") " + timestamp.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void hunt() {
|
||||||
|
//tryoutPacket(new byte[] {MessageType.CMD_M_READ_PUMP_STATUS,0});
|
||||||
|
//tryoutPacket(new byte[] {MessageType.CMD_M_READ_FIRMWARE_VER,0});
|
||||||
|
//tryoutPacket(new byte[] {MessageType.CMD_M_READ_INSULIN_REMAINING,0});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ButtonPressCarelinkMessageBody
|
||||||
|
public void pressButton(int which) {
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
PumpMessage pressButtonMessage = makePumpMessage(MessageType.ButtonPress, new ButtonPressCarelinkMessageBody(which));
|
||||||
|
PumpMessage resp = sendAndListen(pressButtonMessage);
|
||||||
|
if (resp.messageType != MessageType.PumpAck) {
|
||||||
|
LOG.error("Pump did not ack button press.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RLMessage makeRLMessage(RLMessageType type, byte[] data) {
|
||||||
|
switch (type) {
|
||||||
|
case PowerOn:
|
||||||
|
return makePumpMessage(MessageType.PowerOn, new CarelinkShortMessageBody(data));
|
||||||
|
|
||||||
|
case ReadSimpleData:
|
||||||
|
return makePumpMessage(MessageType.GetPumpModel, new GetPumpModelCarelinkMessageBody());
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RLMessage makeRLMessage(byte[] data) {
|
||||||
|
return makePumpMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage makePumpMessage(MessageType messageType, MessageBody messageBody) {
|
||||||
|
PumpMessage msg = new PumpMessage();
|
||||||
|
msg.init(PacketType.Carelink, pumpID, messageType, messageBody);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage makePumpMessage(byte msgType, MessageBody body) {
|
||||||
|
return makePumpMessage(MessageType.getByValue(msgType), body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage makePumpMessage(MessageType messageType, byte[] body) {
|
||||||
|
return makePumpMessage(messageType, body == null ? new CarelinkShortMessageBody() : new CarelinkShortMessageBody(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage makePumpMessage(MessageType messageType) {
|
||||||
|
return makePumpMessage(messageType, (byte[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpMessage makePumpMessage(byte[] typeAndBody) {
|
||||||
|
PumpMessage msg = new PumpMessage();
|
||||||
|
msg.init(ByteUtil.concat(ByteUtil.concat(new byte[]{(byte) 0xa7}, pumpID), typeAndBody));
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private PumpMessage sendAndGetResponse(MessageType messageType) {
|
||||||
|
return sendAndGetResponse(messageType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private PumpMessage sendAndGetResponse(MessageType messageType, byte[] bodyData) {
|
||||||
|
// wakeUp
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
|
||||||
|
// create message
|
||||||
|
PumpMessage msg;
|
||||||
|
|
||||||
|
if (bodyData == null)
|
||||||
|
msg = makePumpMessage(messageType);
|
||||||
|
else
|
||||||
|
msg = makePumpMessage(messageType, bodyData);
|
||||||
|
|
||||||
|
// send and wait for response
|
||||||
|
PumpMessage response = sendAndListen(msg);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get Medtronic specific data
|
||||||
|
private LocalDateTime parsePumpRTCBytes(byte[] bytes) {
|
||||||
|
if (bytes == null) return null;
|
||||||
|
if (bytes.length < 7) return null;
|
||||||
|
int hours = ByteUtil.asUINT8(bytes[0]);
|
||||||
|
int minutes = ByteUtil.asUINT8(bytes[1]);
|
||||||
|
int seconds = ByteUtil.asUINT8(bytes[2]);
|
||||||
|
int year = (ByteUtil.asUINT8(bytes[4]) & 0x3f) + 1984;
|
||||||
|
int month = ByteUtil.asUINT8(bytes[5]);
|
||||||
|
int day = ByteUtil.asUINT8(bytes[6]);
|
||||||
|
try {
|
||||||
|
LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds);
|
||||||
|
return pumpTime;
|
||||||
|
} catch (IllegalFieldValueException e) {
|
||||||
|
LOG.error("parsePumpRTCBytes: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d", year, month, day, hours, minutes, seconds);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LocalDateTime getPumpRTC() {
|
||||||
|
//ReadPumpClockResult rval = new ReadPumpClockResult();
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
PumpMessage getRTCMsg = makePumpMessage(MessageType.ReadTime, new byte[]{0});
|
||||||
|
LOG.info("getPumpRTC: " + ByteUtil.shortHexString(getRTCMsg.getTxData()));
|
||||||
|
PumpMessage response = sendAndListen(getRTCMsg);
|
||||||
|
if (response.isValid()) {
|
||||||
|
byte[] receivedData = response.getContents();
|
||||||
|
if (receivedData != null) {
|
||||||
|
if (receivedData.length >= 9) {
|
||||||
|
LocalDateTime pumpTime = parsePumpRTCBytes(ByteUtil.substring(receivedData, 2, 7));
|
||||||
|
return pumpTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.error("Invalid response: {}", ByteUtil.showPrintable(response.getContents()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpModel getPumpModel() {
|
||||||
|
wakeup(receiverDeviceAwakeForMinutes);
|
||||||
|
PumpMessage msg = makePumpMessage(MessageType.GetPumpModel, new GetPumpModelCarelinkMessageBody());
|
||||||
|
LOG.info("getPumpModel: " + ByteUtil.shortHexString(msg.getTxData()));
|
||||||
|
PumpMessage response = sendAndListen(msg);
|
||||||
|
LOG.info("getPumpModel response: " + ByteUtil.shortHexString(response.getContents()));
|
||||||
|
byte[] contents = response.getContents();
|
||||||
|
PumpModel rval = PumpModel.UNSET;
|
||||||
|
if (contents != null) {
|
||||||
|
if (contents.length >= 7) {
|
||||||
|
rval = PumpModel.fromString(StringUtil.fromBytes(ByteUtil.substring(contents, 3, 3)));
|
||||||
|
} else {
|
||||||
|
LOG.warn("getPumpModel: Cannot return pump model number: data is too short.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.warn("getPumpModel: Cannot return pump model number: null response");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO remove for AAPS
|
||||||
|
// public ISFTable getPumpISFProfile() {
|
||||||
|
//
|
||||||
|
// PumpMessage response = sendAndGetResponse(MessageType.GetISFProfile);
|
||||||
|
//
|
||||||
|
// ISFTable table = new ISFTable();
|
||||||
|
// table.parseFrom(response.getContents());
|
||||||
|
// return table;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO remove for AAPS
|
||||||
|
public PumpMessage getBolusWizardCarbProfile() {
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.CMD_M_READ_CARB_RATIOS);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check
|
||||||
|
public Integer getRemainingBattery() {
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.GetBattery);
|
||||||
|
|
||||||
|
// TODO decode here
|
||||||
|
|
||||||
|
if (response.isValid()) {
|
||||||
|
byte[] remainingBatteryBytes = response.getContents();
|
||||||
|
if (remainingBatteryBytes != null) {
|
||||||
|
if (remainingBatteryBytes.length == 5) {
|
||||||
|
/**
|
||||||
|
* 0x72 0x03, 0x00, 0x00, 0x82
|
||||||
|
* meaning what ????
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIXME use RawData and decoding is not correct
|
||||||
|
// TODO review this !!! Andy
|
||||||
|
|
||||||
|
return ByteUtil.asUINT8(remainingBatteryBytes[5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO check
|
||||||
|
public Float getRemainingInsulin() {
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.CMD_M_READ_INSULIN_REMAINING);
|
||||||
|
|
||||||
|
// TODO decode here
|
||||||
|
byte[] data = response.getRawContent();
|
||||||
|
|
||||||
|
float value = MedtronicUtil.makeUnsignedShort(data[1], data[0]) / 10.0f;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME check
|
||||||
|
public TempBasalPair getCurrentBasalRate() {
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.ReadTempBasal);
|
||||||
|
|
||||||
|
TempBasalPair tbr = new TempBasalPair(response.getRawContent());
|
||||||
|
|
||||||
|
return tbr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO test
|
||||||
|
public BasalProfile getProfile() {
|
||||||
|
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.ReadBasalProfileSTD);
|
||||||
|
|
||||||
|
return new BasalProfile(response.getRawContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO generateRawData (check if it works correctly) and test
|
||||||
|
public PumpMessage setProfile(BasalProfile basalProfile) {
|
||||||
|
|
||||||
|
basalProfile.generateRawData();
|
||||||
|
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.SetBasalProfileSTD, basalProfile.getRawData());
|
||||||
|
|
||||||
|
// what kind of response are we expecting when set it sent
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO test
|
||||||
|
public PumpMessage setTBR(TempBasalPair tbr) {
|
||||||
|
|
||||||
|
// TODO check getAs Raw Data is correct data
|
||||||
|
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.ChangeTempBasal, tbr.getAsRawData());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO test
|
||||||
|
public PumpMessage cancelTBR() {
|
||||||
|
return setTBR(new TempBasalPair(0.0d, false, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO test ??? this might work correctly, check low value and some high value 25 (25 is max bolus)
|
||||||
|
public PumpMessage setBolus(double units) {
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.Bolus, MedtronicUtil.getBolusStrokes(units));
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpMessage cancelBolus() {
|
||||||
|
//? maybe suspend and resume
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpMessage setExtendedBolus(double units, int duration) {
|
||||||
|
// FIXME see decocare
|
||||||
|
PumpMessage response = sendAndGetResponse(MessageType.Bolus, MedtronicUtil.getBolusStrokes(units));
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpMessage cancelExtendedBolus() {
|
||||||
|
// set cancelBolus
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set TBR
|
||||||
|
// Cancel TBR (set TBR 100%)
|
||||||
|
// Get Status (40%)
|
||||||
|
|
||||||
|
// Set Bolus 20%
|
||||||
|
// Set Extended Bolus 20%
|
||||||
|
// Cancel Bolus 0% ?
|
||||||
|
// Cancel Extended Bolus 0% ?
|
||||||
|
|
||||||
|
// Get Basal Profile (0x92) Read STD 20%
|
||||||
|
// Set Basal Profile 20%
|
||||||
|
// Read History 60%
|
||||||
|
// Load TDD ?
|
||||||
|
|
||||||
|
public void updatePumpManagerStatus() {
|
||||||
|
|
||||||
|
Integer resp = getRemainingBattery();
|
||||||
|
pumpStatus.batteryRemaining = resp == null ? -1 : resp;
|
||||||
|
|
||||||
|
//pumpStatus.remainUnits = getRemainingInsulin();
|
||||||
|
|
||||||
|
/* current basal */
|
||||||
|
TempBasalPair basalRate = getCurrentBasalRate();
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
// byte[] basalRateBytes = resp.getContents();
|
||||||
|
// if (basalRateBytes != null) {
|
||||||
|
// if (basalRateBytes.length == 2) {
|
||||||
|
// /**
|
||||||
|
// * 0x98 0x06
|
||||||
|
// * 0x98 is "basal rate"
|
||||||
|
// * 0x06 is what? Not currently running a temp basal, current basal is "standard" at 0
|
||||||
|
// */
|
||||||
|
// double basalRate = ByteUtil.asUINT8(basalRateBytes[1]);
|
||||||
|
// pumpStatus.currentBasal = basalRate;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// get last bolus amount
|
||||||
|
// get last bolus time
|
||||||
|
// get tempBasalInProgress
|
||||||
|
// get tempBasalRatio
|
||||||
|
// get tempBasalRemainMin
|
||||||
|
// get tempBasalStart
|
||||||
|
|
||||||
|
// get pump time
|
||||||
|
LocalDateTime clockResult = getPumpRTC();
|
||||||
|
if (clockResult != null) {
|
||||||
|
//pumpStatus.time = clockResult.toDate();
|
||||||
|
}
|
||||||
|
// get last sync time
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPageDecode() {
|
||||||
|
byte[] raw = new byte[]{(byte) 0x6D, (byte) 0x62, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x63, (byte) 0x10, (byte) 0x6D, (byte) 0x63, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
|
||||||
|
(byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x5A, (byte) 0xA5, (byte) 0x49, (byte) 0x04, (byte) 0x10, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x6D, (byte) 0xA5, (byte) 0x49, (byte) 0x04, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x10, (byte) 0x6D, (byte) 0x64, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x64, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x0C, (byte) 0x00,
|
||||||
|
(byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x64, (byte) 0x01, (byte) 0x75, (byte) 0x94, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x64, (byte) 0x01, (byte) 0x44, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x17, (byte) 0x00, (byte) 0x4E, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10, (byte) 0x18, (byte) 0x00, (byte) 0x40, (byte) 0x95, (byte) 0x0D, (byte) 0x05, (byte) 0x10,
|
||||||
|
(byte) 0x19, (byte) 0x00, (byte) 0x40, (byte) 0x81, (byte) 0x15, (byte) 0x05, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x65, (byte) 0x10, (byte) 0x6D, (byte) 0x65, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x1A, (byte) 0x00, (byte) 0x47, (byte) 0x82, (byte) 0x09, (byte) 0x06,
|
||||||
|
(byte) 0x10, (byte) 0x1A, (byte) 0x01, (byte) 0x5C, (byte) 0x82, (byte) 0x09, (byte) 0x06, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x66, (byte) 0x10, (byte) 0x6D, (byte) 0x66, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x67, (byte) 0x10, (byte) 0x6D, (byte) 0x67, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x68, (byte) 0x10, (byte) 0x6D, (byte) 0x68, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x69, (byte) 0x10, (byte) 0x6D, (byte) 0x69, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A, (byte) 0x10, (byte) 0x6D, (byte) 0x6A, (byte) 0x10, (byte) 0x05, (byte) 0x0C,
|
||||||
|
(byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6B, (byte) 0x10, (byte) 0x6D, (byte) 0x6B, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6C,
|
||||||
|
(byte) 0x10, (byte) 0x6D, (byte) 0x6C, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6D, (byte) 0x10, (byte) 0x6D, (byte) 0x6D, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6E, (byte) 0x10, (byte) 0x6D, (byte) 0x6E, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6F, (byte) 0x10, (byte) 0x6D, (byte) 0x6F, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00,
|
||||||
|
(byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x19, (byte) 0x00, (byte) 0x40, (byte) 0x81, (byte) 0x03, (byte) 0x10, (byte) 0x10, (byte) 0x1A, (byte) 0x00, (byte) 0x68, (byte) 0x96, (byte) 0x0A, (byte) 0x10, (byte) 0x10, (byte) 0x1A, (byte) 0x01, (byte) 0x40, (byte) 0x97, (byte) 0x0A, (byte) 0x10, (byte) 0x10, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x70, (byte) 0x10, (byte) 0x6D, (byte) 0x70, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x71, (byte) 0x10, (byte) 0x6D, (byte) 0x71, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x72, (byte) 0x10, (byte) 0x6D, (byte) 0x72, (byte) 0x10, (byte) 0x05, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x73, (byte) 0x10, (byte) 0x6D, (byte) 0x73, (byte) 0x10, (byte) 0x05, (byte) 0x0C,
|
||||||
|
(byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
||||||
|
(byte) 0x00, (byte) 0x0C, (byte) 0x00, (byte) 0xE8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x74, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x2C, (byte) 0x79,
|
||||||
|
};
|
||||||
|
Page page = new Page();
|
||||||
|
page.parseFrom(raw, PumpModel.MM522);
|
||||||
|
page.parseByDates(raw, PumpModel.MM522);
|
||||||
|
page.parsePicky(raw, PumpModel.MM522);
|
||||||
|
LOG.info("testPageDecode: done");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
|
||||||
|
import org.joda.time.Instant;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/1/15.
|
||||||
|
* <p>
|
||||||
|
* There are three basal profiles stored on the pump. (722 only?)
|
||||||
|
* They are all parsed the same, the user just has 3 to choose from:
|
||||||
|
* Standard, A, and B
|
||||||
|
* <p>
|
||||||
|
* The byte array seems to be 21 three byte entries long, plus a zero?
|
||||||
|
* If the profile is completely empty, it should have one entry: [0,0,0x3F] (?)
|
||||||
|
* The first entry of [0,0,0] marks the end of the used entries.
|
||||||
|
* <p>
|
||||||
|
* Each entry is assumed to span from the specified start time to the start time of the
|
||||||
|
* next entry, or to midnight if there are no more entries.
|
||||||
|
* <p>
|
||||||
|
* Individual entries are of the form [r,z,m] where
|
||||||
|
* r is the rate (in 0.025 U increments)
|
||||||
|
* z is zero (?)
|
||||||
|
* m is the start time-of-day for the basal rate period (in 30 minute increments?)
|
||||||
|
*/
|
||||||
|
public class BasalProfile {
|
||||||
|
//private static final String TAG = "BasalProfile";
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(BasalProfile.class);
|
||||||
|
|
||||||
|
private static final boolean DEBUG_BASALPROFILE = false;
|
||||||
|
protected static final int MAX_RAW_DATA_SIZE = (21 * 3) + 1;
|
||||||
|
protected byte[] mRawData; // store as byte array to make transport (via parcel) easier
|
||||||
|
|
||||||
|
public BasalProfile() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BasalProfile(byte[] data) {
|
||||||
|
setRawData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
mRawData = new byte[MAX_RAW_DATA_SIZE];
|
||||||
|
mRawData[0] = 0;
|
||||||
|
mRawData[1] = 0;
|
||||||
|
mRawData[2] = 0x3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this asUINT8 should be combined with Record.asUINT8, and placed in a new util class.
|
||||||
|
protected static int readUnsignedByte(byte b) {
|
||||||
|
return (b < 0) ? b + 256 : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRawData(byte[] data) {
|
||||||
|
if (data == null) {
|
||||||
|
LOG.error("setRawData: buffer is null!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int len = Math.min(MAX_RAW_DATA_SIZE, data.length);
|
||||||
|
System.arraycopy(data, 0, mRawData, 0, len);
|
||||||
|
if (DEBUG_BASALPROFILE) {
|
||||||
|
LOG.debug(String.format("setRawData: copied raw data buffer of %d bytes.", len));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dumpBasalProfile() {
|
||||||
|
LOG.debug("Basal Profile entries:");
|
||||||
|
List<BasalProfileEntry> entries = getEntries();
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
BasalProfileEntry entry = entries.get(i);
|
||||||
|
String startString = entry.startTime.toString("HH:mm");
|
||||||
|
LOG.debug(String.format("Entry %d, rate=%.3f (0x%02X), start=%s (0x%02X)",
|
||||||
|
i + 1, entry.rate, entry.rate_raw,
|
||||||
|
startString, entry.startTime_raw));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this function must be expanded to include changes in which profile is in use.
|
||||||
|
// and changes to the profiles themselves.
|
||||||
|
public BasalProfileEntry getEntryForTime(Instant when) {
|
||||||
|
BasalProfileEntry rval = new BasalProfileEntry();
|
||||||
|
List<BasalProfileEntry> entries = getEntries();
|
||||||
|
if (entries.size() == 0) {
|
||||||
|
LOG.warn(String.format("getEntryForTime(%s): table is empty",
|
||||||
|
when.toDateTime().toLocalTime().toString("HH:mm")));
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
//Log.w(TAG,"Assuming first entry");
|
||||||
|
rval = entries.get(0);
|
||||||
|
if (entries.size() == 1) {
|
||||||
|
LOG.debug("getEntryForTime: Only one entry in profile");
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int localMillis = when.toDateTime().toLocalTime().getMillisOfDay();
|
||||||
|
boolean done = false;
|
||||||
|
int i = 1;
|
||||||
|
while (!done) {
|
||||||
|
BasalProfileEntry entry = entries.get(i);
|
||||||
|
if (DEBUG_BASALPROFILE) {
|
||||||
|
LOG.debug(String.format("Comparing 'now'=%s to entry 'start time'=%s",
|
||||||
|
when.toDateTime().toLocalTime().toString("HH:mm"),
|
||||||
|
entry.startTime.toString("HH:mm")));
|
||||||
|
}
|
||||||
|
if (localMillis >= entry.startTime.getMillisOfDay()) {
|
||||||
|
rval = entry;
|
||||||
|
if (DEBUG_BASALPROFILE) LOG.debug("Accepted Entry");
|
||||||
|
} else {
|
||||||
|
// entry at i has later start time, keep older entry
|
||||||
|
if (DEBUG_BASALPROFILE) LOG.debug("Rejected Entry");
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (i >= entries.size()) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DEBUG_BASALPROFILE) {
|
||||||
|
LOG.debug(String.format("getEntryForTime(%s): Returning entry: rate=%.3f (%d), start=%s (%d)",
|
||||||
|
when.toDateTime().toLocalTime().toString("HH:mm"),
|
||||||
|
rval.rate, rval.rate_raw,
|
||||||
|
rval.startTime.toString("HH:mm"), rval.startTime_raw));
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BasalProfileEntry> getEntries() {
|
||||||
|
List<BasalProfileEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
if (mRawData[2] == 0x3f) {
|
||||||
|
LOG.warn("Raw Data is empty.");
|
||||||
|
return entries; // an empty list
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
boolean done = false;
|
||||||
|
int r, st;
|
||||||
|
while (!done) {
|
||||||
|
|
||||||
|
r = MedtronicUtil.makeUnsignedShort(mRawData[i + 1], mRawData[i]); //readUnsignedByte(mRawData[i]);
|
||||||
|
// What is mRawData[i+1]? Not used in decocare.
|
||||||
|
st = readUnsignedByte(mRawData[i + 2]);
|
||||||
|
entries.add(new BasalProfileEntry(r, st));
|
||||||
|
i = i + 3;
|
||||||
|
if (i >= MAX_RAW_DATA_SIZE) {
|
||||||
|
done = true;
|
||||||
|
} else if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<BasalProfileEntry> listEntries;
|
||||||
|
|
||||||
|
|
||||||
|
public void addEntry(BasalProfileEntry entry) {
|
||||||
|
if (listEntries == null)
|
||||||
|
listEntries = new ArrayList<>();
|
||||||
|
|
||||||
|
listEntries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void generateRawData() {
|
||||||
|
|
||||||
|
List<Byte> outData = new ArrayList<>();
|
||||||
|
|
||||||
|
for (BasalProfileEntry profileEntry : listEntries) {
|
||||||
|
|
||||||
|
byte[] strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true);
|
||||||
|
|
||||||
|
// TODO check if this is correct
|
||||||
|
outData.add(strokes[0]);
|
||||||
|
outData.add(strokes[1]);
|
||||||
|
|
||||||
|
int time = profileEntry.startTime.getHourOfDay();
|
||||||
|
|
||||||
|
if (profileEntry.startTime.getMinuteOfHour() == 30) {
|
||||||
|
time++;
|
||||||
|
}
|
||||||
|
|
||||||
|
outData.add((byte) time);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setRawData(MedtronicUtil.createByteArray(outData));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void testParser() {
|
||||||
|
byte[] testData = new byte[]{
|
||||||
|
32, 0, 0,
|
||||||
|
38, 0, 13,
|
||||||
|
44, 0, 19,
|
||||||
|
38, 0, 28,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
/* from decocare:
|
||||||
|
_test_schedule = {'total': 22.50, 'schedule': [
|
||||||
|
{ 'start': '12:00A', 'rate': 0.80 },
|
||||||
|
{ 'start': '6:30A', 'rate': 0.95 },
|
||||||
|
{ 'start': '9:30A', 'rate': 1.10 },
|
||||||
|
{ 'start': '2:00P', 'rate': 0.95 },
|
||||||
|
]}
|
||||||
|
*/
|
||||||
|
BasalProfile profile = new BasalProfile();
|
||||||
|
profile.setRawData(testData);
|
||||||
|
List<BasalProfileEntry> entries = profile.getEntries();
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
LOG.error("testParser: failed");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
BasalProfileEntry e = entries.get(i);
|
||||||
|
LOG.debug(String.format("testParser entry #%d: rate: %.2f, start %d:%d",
|
||||||
|
i, e.rate, e.startTime.getHourOfDay(),
|
||||||
|
e.startTime.getMinuteOfHour()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRawData() {
|
||||||
|
return this.mRawData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
|
||||||
|
import org.joda.time.LocalTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/1/15.
|
||||||
|
* This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile
|
||||||
|
*/
|
||||||
|
public class BasalProfileEntry {
|
||||||
|
public byte rate_raw;
|
||||||
|
public double rate;
|
||||||
|
public byte startTime_raw;
|
||||||
|
public LocalTime startTime; // Just a "time of day"
|
||||||
|
|
||||||
|
// FIXME rate is two bits not one -- Andy see MedtronicUtil
|
||||||
|
public BasalProfileEntry() {
|
||||||
|
rate = -9.999E6;
|
||||||
|
rate_raw = (byte) 0xFF;
|
||||||
|
startTime = new LocalTime(0);
|
||||||
|
startTime_raw = (byte) 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasalProfileEntry(int rateByte, int startTimeByte) {
|
||||||
|
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
||||||
|
// startTimeByte is time-of-day, in 30 minute increments
|
||||||
|
rate_raw = (byte) rateByte;
|
||||||
|
rate = rateByte * 0.025;
|
||||||
|
startTime_raw = (byte) startTimeByte;
|
||||||
|
startTime = new LocalTime(startTimeByte / 2, (startTimeByte % 2) * 30);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,405 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/13/15.
|
||||||
|
* <p>
|
||||||
|
* This class was taken from medtronic-android-uploader.
|
||||||
|
* This class was written such that the constructors did all the work, which resulted
|
||||||
|
* in annoyances such as exceptions during constructors.
|
||||||
|
* <p>
|
||||||
|
* TODO: This class needs to be revisited and probably rewritten. (2016-06-12)
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* Pete Schwamb
|
||||||
|
*
|
||||||
|
* @ps2 12:04
|
||||||
|
* History entries will not reorder themselves, but some events do update, like dual wave bolus entries
|
||||||
|
* It's like an append only log, and when a page is full, it rotates the page ids and starts appending to a new blank page
|
||||||
|
* Darrell Wright
|
||||||
|
* @beached 12:05
|
||||||
|
* so the timestamp is entry creation not update
|
||||||
|
* time
|
||||||
|
* Pete Schwamb
|
||||||
|
* @ps2 12:06
|
||||||
|
* Yes, I don't think the timestamps ever change
|
||||||
|
* <p>
|
||||||
|
* <p>
|
||||||
|
* GGW: TODO: examine src/ecc1/medtronic for better history parsing
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.PumpTimeStamp;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.Record;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.RecordTypeEnum;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeFormat;
|
||||||
|
|
||||||
|
public class Page {
|
||||||
|
private final static String TAG = "Page";
|
||||||
|
private static final boolean DEBUG_PAGE = true;
|
||||||
|
|
||||||
|
private byte[] crc;
|
||||||
|
private byte[] data;
|
||||||
|
protected PumpModel model;
|
||||||
|
public List<Record> mRecordList;
|
||||||
|
|
||||||
|
public Page() {
|
||||||
|
this.model = PumpModel.UNSET;
|
||||||
|
mRecordList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRawData() {
|
||||||
|
if (data == null) {
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
if (crc == null) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return ByteUtil.concat(data, crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PumpTimeStamp collectTimeStamp(byte[] data, int offset) {
|
||||||
|
try {
|
||||||
|
PumpTimeStamp timestamp = new PumpTimeStamp(TimeFormat.parse5ByteDate(data, offset));
|
||||||
|
return timestamp;
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parsePicky(byte[] rawPage, PumpModel model) {
|
||||||
|
mRecordList = new ArrayList<>();
|
||||||
|
this.model = model;
|
||||||
|
int pageOffset = 0;
|
||||||
|
|
||||||
|
if ((rawPage == null) || (rawPage.length == 0)) return false;
|
||||||
|
this.data = Arrays.copyOfRange(rawPage, 0, rawPage.length - 2);
|
||||||
|
this.crc = Arrays.copyOfRange(rawPage, rawPage.length - 2, rawPage.length);
|
||||||
|
byte[] expectedCrc = CRC.calculate16CCITT(this.data);
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, String.format("Data length: %d", data.length));
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(crc, expectedCrc)) {
|
||||||
|
Log.w(TAG, String.format("CRC does not match expected value. Expected: %s Was: %s", HexDump.toHexString(expectedCrc), HexDump.toHexString(crc)));
|
||||||
|
} else {
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, "CRC OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Record record = null;
|
||||||
|
while (pageOffset < data.length) {
|
||||||
|
if (data[pageOffset] == 0) {
|
||||||
|
if (record != null) {
|
||||||
|
Log.i(TAG, String.format("End of page or Previous parse fail: prev opcode 0x%02x, curr offset %d, %d bytes remaining",
|
||||||
|
record.getRecordOp(), pageOffset, data.length - pageOffset + 1));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "WTF?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
record = attemptParseRecord(data, pageOffset);
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
record = null;
|
||||||
|
}
|
||||||
|
if (record == null) {
|
||||||
|
Log.i(TAG, "PARSE FAIL");
|
||||||
|
pageOffset++;
|
||||||
|
} else {
|
||||||
|
mRecordList.add(record);
|
||||||
|
pageOffset += record.getLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList<Record> pickyRecords = new ArrayList<>();
|
||||||
|
pickyRecords.addAll(mRecordList);
|
||||||
|
parseByDates(rawPage, model);
|
||||||
|
for (Record r : mRecordList) {
|
||||||
|
for (Record r2 : pickyRecords) {
|
||||||
|
if (r.getFoundAtOffset() == r2.getFoundAtOffset()) {
|
||||||
|
Log.v(TAG, "Found matching record at offset " + r.getFoundAtOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parseByDates(byte[] rawPage, PumpModel model) {
|
||||||
|
mRecordList = new ArrayList<>();
|
||||||
|
if (rawPage.length != 1024) {
|
||||||
|
Log.e(TAG, "Unexpected page size. Expected: 1024 Was: " + rawPage.length);
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
this.model = model;
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, "Parsing page");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawPage.length < 4) {
|
||||||
|
Log.e(TAG, "Page too short, need at least 4 bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = Arrays.copyOfRange(rawPage, 0, rawPage.length - 2);
|
||||||
|
this.crc = Arrays.copyOfRange(rawPage, rawPage.length - 2, rawPage.length);
|
||||||
|
byte[] expectedCrc = CRC.calculate16CCITT(this.data);
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, String.format("Data length: %d", data.length));
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(crc, expectedCrc)) {
|
||||||
|
Log.w(TAG, String.format("CRC does not match expected value. Expected: %s Was: %s", HexDump.toHexString(expectedCrc), HexDump.toHexString(crc)));
|
||||||
|
} else {
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, "CRC OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageOffset = 0;
|
||||||
|
PumpTimeStamp lastPumpTimeStamp = new PumpTimeStamp();
|
||||||
|
while (pageOffset < this.data.length - 7) {
|
||||||
|
PumpTimeStamp timestamp = collectTimeStamp(data, pageOffset + 2);
|
||||||
|
if (timestamp != null) {
|
||||||
|
String year = timestamp.toString().substring(0, 3);
|
||||||
|
Record record;
|
||||||
|
if ("201".equals(year)) {
|
||||||
|
// maybe found a record.
|
||||||
|
try {
|
||||||
|
record = attemptParseRecord(data, pageOffset);
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
record = null;
|
||||||
|
}
|
||||||
|
if (record != null) {
|
||||||
|
if (timestamp.getLocalDateTime().compareTo(lastPumpTimeStamp.getLocalDateTime()) >= 0) {
|
||||||
|
Log.i(TAG, "Timestamp is increasing");
|
||||||
|
lastPumpTimeStamp = timestamp;
|
||||||
|
mRecordList.add(record);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Timestamp is decreasing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parseFrom(byte[] rawPage, PumpModel model) {
|
||||||
|
mRecordList = new ArrayList<>(); // wipe old contents each time when parsing.
|
||||||
|
if (rawPage.length != 1024) {
|
||||||
|
Log.e(TAG, "Unexpected page size. Expected: 1024 Was: " + rawPage.length);
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
this.model = model;
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, "Parsing page");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawPage.length < 4) {
|
||||||
|
Log.e(TAG, "Page too short, need at least 4 bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = Arrays.copyOfRange(rawPage, 0, rawPage.length - 2);
|
||||||
|
this.crc = Arrays.copyOfRange(rawPage, rawPage.length - 2, rawPage.length);
|
||||||
|
byte[] expectedCrc = CRC.calculate16CCITT(this.data);
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, String.format("Data length: %d", data.length));
|
||||||
|
}
|
||||||
|
if (!Arrays.equals(crc, expectedCrc)) {
|
||||||
|
Log.w(TAG, String.format("CRC does not match expected value. Expected: %s Was: %s", HexDump.toHexString(expectedCrc), HexDump.toHexString(crc)));
|
||||||
|
} else {
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, "CRC OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dataIndex = 0;
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
Record record = null;
|
||||||
|
if (data[dataIndex] != 0) {
|
||||||
|
// If the data byte is zero, assume that means end of page
|
||||||
|
try {
|
||||||
|
record = attemptParseRecord(data, dataIndex);
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
record = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Zero opcode encountered -- end of page. " + (rawPage.length - dataIndex) + " bytes remaining.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (record != null) {
|
||||||
|
Log.v(TAG, "parseFrom: found event " + record.getClass().getSimpleName() + " length=" + record.getLength() + " offset=" + record.getFoundAtOffset());
|
||||||
|
mRecordList.add(record);
|
||||||
|
dataIndex += record.getLength();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, String.format("parseFrom: Failed to parse opcode 0x%02x, offset=%d", data[dataIndex], dataIndex));
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
if (dataIndex >= data.length - 2) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DEBUG_PAGE) {
|
||||||
|
Log.i(TAG, String.format("Number of records: %d", mRecordList.size()));
|
||||||
|
int index = 1;
|
||||||
|
for (Record r : mRecordList) {
|
||||||
|
Log.v(TAG, String.format("Record #%d: %s", index, r.getShortTypeName()));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* attemptParseRecord will attempt to create a subclass of Record from the given
|
||||||
|
* data and offset. It will return NULL if it fails. If it succeeds, the returned
|
||||||
|
* subclass of Record can be examined for its length, so that the next attempt can be made.
|
||||||
|
*
|
||||||
|
* TODO maybe try to change this, so that we can loose all the classes and using enum instead with full
|
||||||
|
* configuration. Its something to think about for later versions -- Andy
|
||||||
|
*/
|
||||||
|
public static <T extends Record> T attemptParseRecord(byte[] data, int offsetStart) {
|
||||||
|
// no data?
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// invalid offset?
|
||||||
|
if (data.length < offsetStart) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Log.d(TAG,String.format("checking for handler for record type 0x%02X at index %d",data[offsetStart],offsetStart));
|
||||||
|
RecordTypeEnum en = RecordTypeEnum.fromByte(data[offsetStart]);
|
||||||
|
T record = en.getRecordClassInstance(PumpModel.MM522);
|
||||||
|
if (record != null) {
|
||||||
|
// have to do this to set the record's opCode
|
||||||
|
byte[] tmpData = new byte[data.length];
|
||||||
|
System.arraycopy(data, offsetStart, tmpData, 0, data.length - offsetStart);
|
||||||
|
boolean didParse = record.parseWithOffset(tmpData, PumpModel.MM522, offsetStart);
|
||||||
|
if (!didParse) {
|
||||||
|
Log.e(TAG, String.format("attemptParseRecord: class %s (opcode 0x%02X) failed to parse at offset %d", record.getShortTypeName(), data[offsetStart], offsetStart));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime parseSimpleDate(byte[] data, int offset) {
|
||||||
|
DateTime timeStamp = null;
|
||||||
|
int seconds = 0;
|
||||||
|
int minutes = 0;
|
||||||
|
int hour = 0;
|
||||||
|
//int high = data[0] >> 4;
|
||||||
|
int low = data[0 + offset] & 0x1F;
|
||||||
|
//int year_high = data[1] >> 4;
|
||||||
|
int mhigh = (data[0 + offset] & 0xE0) >> 4;
|
||||||
|
int mlow = (data[1 + offset] & 0x80) >> 7;
|
||||||
|
int month = mhigh + mlow;
|
||||||
|
int dayOfMonth = low + 1;
|
||||||
|
// python code says year is data[1] & 0x0F, but that will cause problem in 2016.
|
||||||
|
// Hopefully, the remaining bits are part of the year...
|
||||||
|
int year = data[1 + offset] & 0x3F;
|
||||||
|
/*
|
||||||
|
Log.w(TAG, String.format("Attempting to create DateTime from: %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
year + 2000, month, dayOfMonth, hour, minutes, seconds));
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
timeStamp = new DateTime(year + 2000, month, dayOfMonth, hour, minutes, seconds);
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
//Log.e(TAG,"Illegal DateTime field");
|
||||||
|
//e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void discoverRecords(byte[] data) {
|
||||||
|
int i = 0;
|
||||||
|
boolean done = false;
|
||||||
|
|
||||||
|
ArrayList<Integer> keyLocations = new ArrayList();
|
||||||
|
while (!done) {
|
||||||
|
RecordTypeEnum en = RecordTypeEnum.fromByte(data[i]);
|
||||||
|
if (en != RecordTypeEnum.RECORD_TYPE_NULL) {
|
||||||
|
keyLocations.add(i);
|
||||||
|
Log.v(TAG, String.format("Possible record of type %s found at index %d", en, i));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
DateTime ts = parseSimpleDate(data,i);
|
||||||
|
if (ts != null) {
|
||||||
|
if (ts.year().get() == 2015) {
|
||||||
|
Log.w(TAG, String.format("Possible simple date at index %d", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
i = i + 1;
|
||||||
|
done = (i >= data.length - 2);
|
||||||
|
}
|
||||||
|
// for each of the discovered key locations, attempt to parse a sequence of records
|
||||||
|
for (RecordTypeEnum en : RecordTypeEnum.values()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
for (int ix = 0; ix < keyLocations.size(); ix++) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* For IPC serialization
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
private byte[] crc;
|
||||||
|
private byte[] data;
|
||||||
|
protected PumpModel model;
|
||||||
|
public List<Record> mRecordList;
|
||||||
|
*/
|
||||||
|
|
||||||
|
public Bundle pack() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putByteArray("crc", crc);
|
||||||
|
bundle.putByteArray("data", data);
|
||||||
|
bundle.putString("model", PumpModel.toString(model));
|
||||||
|
ArrayList<Bundle> records = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mRecordList.size(); i++) {
|
||||||
|
try {
|
||||||
|
records.add(mRecordList.get(i).dictionaryRepresentation());
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bundle.putParcelableArrayList("mRecordList", records);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unpack(Bundle in) {
|
||||||
|
crc = in.getByteArray("crc");
|
||||||
|
data = in.getByteArray("data");
|
||||||
|
model = PumpModel.fromString(in.getString("model"));
|
||||||
|
ArrayList<Bundle> records = in.getParcelableArrayList("mRecordList");
|
||||||
|
mRecordList = new ArrayList<>();
|
||||||
|
if (records != null) {
|
||||||
|
for (int i = 0; i < records.size(); i++) {
|
||||||
|
Record r = RecordTypeEnum.getRecordClassInstance(records.get(i), model);
|
||||||
|
r.readFromBundle(records.get(i));
|
||||||
|
mRecordList.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
// cribbed from:
|
||||||
|
//package com.nightscout.core.drivers.Medtronic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/13/15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum PumpModel {
|
||||||
|
UNSET,
|
||||||
|
MM508,
|
||||||
|
MM515,
|
||||||
|
MM522,
|
||||||
|
MM523;
|
||||||
|
|
||||||
|
public static boolean isLargerFormat(PumpModel model) {
|
||||||
|
if (model == MM523) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(PumpModel model) {
|
||||||
|
switch (model) {
|
||||||
|
case UNSET:
|
||||||
|
return "UNSET";
|
||||||
|
case MM508:
|
||||||
|
return "508";
|
||||||
|
case MM515:
|
||||||
|
return "515";
|
||||||
|
case MM522:
|
||||||
|
return "522";
|
||||||
|
case MM523:
|
||||||
|
return "523";
|
||||||
|
default:
|
||||||
|
return "(error)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PumpModel fromString(String s) {
|
||||||
|
if ("UNSET".equals(s)) {
|
||||||
|
return UNSET;
|
||||||
|
}
|
||||||
|
if (("508".equals(s)) || ("MM508".equals(s))) {
|
||||||
|
return MM508;
|
||||||
|
}
|
||||||
|
if (("515".equals(s)) || ("MM515".equals(s))) {
|
||||||
|
return MM515;
|
||||||
|
}
|
||||||
|
if (("522".equals(s)) || ("MM522".equals(s))) {
|
||||||
|
return MM522;
|
||||||
|
}
|
||||||
|
if (("523".equals(s)) || ("MM523".equals(s))) {
|
||||||
|
return MM523;
|
||||||
|
}
|
||||||
|
return UNSET;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/4/16.
|
||||||
|
*/
|
||||||
|
public class RawHistoryPage {
|
||||||
|
private static final String TAG = "RawHistoryPage";
|
||||||
|
byte[] data = new byte[0];
|
||||||
|
|
||||||
|
public RawHistoryPage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendData(byte[] newdata) {
|
||||||
|
data = ByteUtil.concat(data, newdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChecksumOK() {
|
||||||
|
if (getLength() != 1024) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] computedCRC = CRC.calculate16CCITT(ByteUtil.substring(data, 0, 1022));
|
||||||
|
return ((computedCRC[0] == data[1022]) && (computedCRC[1] == data[1023]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dumpToDebug() {
|
||||||
|
int linesize = 80;
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < data.length) {
|
||||||
|
int bytesToLog = linesize;
|
||||||
|
if (offset + linesize > data.length) {
|
||||||
|
bytesToLog = data.length - offset;
|
||||||
|
}
|
||||||
|
Log.d(TAG, ByteUtil.shortHexString(ByteUtil.substring(data, offset, bytesToLog)));
|
||||||
|
offset += linesize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/29/15.
|
||||||
|
* <p>
|
||||||
|
* Just need a class to keep the pair together, for parcel transport.
|
||||||
|
*/
|
||||||
|
public class TempBasalPair {
|
||||||
|
private double mInsulinRate = 0.0;
|
||||||
|
private int mDurationMinutes = 0;
|
||||||
|
private boolean mIsPercent = false;
|
||||||
|
|
||||||
|
public double getInsulinRate() {
|
||||||
|
return mInsulinRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInsulinRate(double insulinRate) {
|
||||||
|
this.mInsulinRate = insulinRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDurationMinutes() {
|
||||||
|
return mDurationMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDurationMinutes(int durationMinutes) {
|
||||||
|
this.mDurationMinutes = durationMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPercent() {
|
||||||
|
return mIsPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsPercent(boolean yesIsPercent) {
|
||||||
|
this.mIsPercent = yesIsPercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempBasalPair() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) {
|
||||||
|
mInsulinRate = insulinRate;
|
||||||
|
mIsPercent = isPercent;
|
||||||
|
mDurationMinutes = durationMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TempBasalPair(byte[] response) {
|
||||||
|
|
||||||
|
mIsPercent = response[0] == 1;
|
||||||
|
|
||||||
|
if (mIsPercent) {
|
||||||
|
mInsulinRate = response[1];
|
||||||
|
} else {
|
||||||
|
int strokes = MedtronicUtil.makeUnsignedShort(response[2], response[3]);
|
||||||
|
|
||||||
|
mInsulinRate = strokes / 40.0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDurationMinutes = MedtronicUtil.makeUnsignedShort(response[4], response[5]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getAsRawData() {
|
||||||
|
|
||||||
|
List<Byte> list = new ArrayList<Byte>();
|
||||||
|
|
||||||
|
list.add((byte) 0); // absolute
|
||||||
|
list.add((byte) 0); // percent amount
|
||||||
|
|
||||||
|
byte[] insulinRate = MedtronicUtil.getBasalStrokes(mInsulinRate, true);
|
||||||
|
|
||||||
|
list.add(insulinRate[0]);
|
||||||
|
list.add(insulinRate[1]);
|
||||||
|
|
||||||
|
byte[] timeMin = MedtronicUtil.getByteArrayFromUnsignedShort(mDurationMinutes, true);
|
||||||
|
|
||||||
|
list.add(timeMin[0]);
|
||||||
|
list.add(timeMin[1]);
|
||||||
|
|
||||||
|
return MedtronicUtil.createByteArray(list);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.joda.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/4/16.
|
||||||
|
* Exists to easily merge 2 byte timestamps and 5 byte timestamps.
|
||||||
|
*/
|
||||||
|
public class PumpTimeStamp {
|
||||||
|
private LocalDateTime localDateTime;
|
||||||
|
|
||||||
|
public PumpTimeStamp() {
|
||||||
|
localDateTime = new LocalDateTime(1973, 1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpTimeStamp(String stringRepresentation) {
|
||||||
|
localDateTime.parse(stringRepresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpTimeStamp(LocalDate localDate) {
|
||||||
|
try {
|
||||||
|
localDateTime = new LocalDateTime(localDate);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// This should be caught earlier
|
||||||
|
localDateTime = new LocalDateTime(1973, 1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpTimeStamp(LocalDateTime localDateTime) {
|
||||||
|
this.localDateTime = localDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getLocalDateTime() {
|
||||||
|
return localDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getLocalDateTime().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
|
||||||
|
abstract public class Record {
|
||||||
|
|
||||||
|
protected PumpModel model;
|
||||||
|
protected byte recordOp;
|
||||||
|
//protected int length;
|
||||||
|
protected int foundAtOffset;
|
||||||
|
protected byte[] rawbytes = new byte[0];
|
||||||
|
//protected String recordTypeName = this.getClass().getSimpleName();
|
||||||
|
|
||||||
|
public String getRecordTypeName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPumpModel(PumpModel model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFoundAtOffset() {
|
||||||
|
return foundAtOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Record() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parseWithOffset(byte[] data, PumpModel model, int foundAtOffset) {
|
||||||
|
// keep track of where the record was found for later analysis
|
||||||
|
this.foundAtOffset = foundAtOffset;
|
||||||
|
if (data == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data.length < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
recordOp = data[0];
|
||||||
|
boolean didParse = parseFrom(data, model);
|
||||||
|
if (didParse) {
|
||||||
|
captureRawBytes(data);
|
||||||
|
}
|
||||||
|
return didParse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void captureRawBytes(byte[] data) {
|
||||||
|
this.rawbytes = new byte[getLength()];
|
||||||
|
System.arraycopy(data, 0, this.rawbytes, 0, getLength() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PumpTimeStamp getTimestamp() {
|
||||||
|
return new PumpTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getRecordOp() {
|
||||||
|
return recordOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int asUINT8(byte b) {
|
||||||
|
return (b < 0) ? b + 256 : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle dictionaryRepresentation() {
|
||||||
|
Bundle rval = new Bundle();
|
||||||
|
writeToBundle(rval);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
// length is determined at instantiation
|
||||||
|
// record type name is "static"
|
||||||
|
// opcode has already been read.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
in.putInt("length", getLength());
|
||||||
|
in.putInt("foundAtOffset", foundAtOffset);
|
||||||
|
in.putInt("_opcode", recordOp);
|
||||||
|
in.putString("_type", getRecordTypeName());
|
||||||
|
in.putString("_stype", getShortTypeName());
|
||||||
|
in.putByteArray("rawbytes", rawbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean isAAPSRelevant();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.AlarmClockReminderPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.AlarmSensorPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.BGReceivedPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.BasalProfileStart;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.BatteryPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.BolusNormalPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.BolusWizardBolusEstimatePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.CalBgForPhPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeAlarmClockEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeAlarmNotifyModePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeAudioBolusPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBGReminderEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBasalProfilePatternPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBasalProfilePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBolusReminderEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBolusReminderTimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBolusScrollStepSizePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeBolusWizardSetupPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeCaptureEventEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeCarbUnitsPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeChildBlockEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeMaxBolusPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeOtherDeviceIDPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeReservoirWarningTimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeSensorRateOfChangeAlertSetupPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeSensorSetup2PumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeTempBasalTypePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeTimeFormatPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeTimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeVariableBolusPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeWatchdogEnablePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ChangeWatchdogMarriageProfilePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ClearAlarmPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.DeleteAlarmClockTimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.DeleteBolusReminderTimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.DeleteOtherDeviceIDPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.EnableDisableRemotePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.InsulinMarkerEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.JournalEntryExerciseMarkerPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.JournalEntryPumpLowBatteryPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.JournalEntryPumpLowReservoirPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.Model522ResultTotalsPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.NewTimeSet;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.PrimePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.PumpAlarmPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ResultDailyTotalPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.ResumePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.RewindPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.Sara6EPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.SuspendPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.TempBasalDurationPumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.TempBasalRatePumpEvent;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.UnabsorbedInsulin;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record.Unknown7ByteEvent1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/28/15.
|
||||||
|
*/
|
||||||
|
public enum RecordTypeEnum {
|
||||||
|
RECORD_TYPE_NULL((byte) 0x00, null),
|
||||||
|
RECORD_TYPE_BOLUSNORMAL((byte) 0x01, BolusNormalPumpEvent.class),
|
||||||
|
RECORD_TYPE_PRIME((byte) 0x03, PrimePumpEvent.class),
|
||||||
|
RECORD_TYPE_ALARMPUMP((byte) 0x06, PumpAlarmPumpEvent.class),
|
||||||
|
RECORD_TYPE_RESULTDAILYTOTAL((byte) 0x07, ResultDailyTotalPumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGEBASALPROFILEPATTERN((byte) 0x08, ChangeBasalProfilePatternPumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGEBASALPROFILE((byte) 0x09, ChangeBasalProfilePumpEvent.class),
|
||||||
|
RECORD_TYPE_CALBGFORPH((byte) 0x0A, CalBgForPhPumpEvent.class),
|
||||||
|
RECORD_TYPE_ALARMSENSOR((byte) 0x0B, AlarmSensorPumpEvent.class),
|
||||||
|
RECORD_TYPE_CLEARALARM((byte) 0x0C, ClearAlarmPumpEvent.class),
|
||||||
|
//RECORD_TYPE_SELECTBASALPROFILE((byte)0x14,SelectBasalProfile.class),
|
||||||
|
RECORD_TYPE_TEMPBASALDURATION((byte) 0x16, TempBasalDurationPumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGETIME((byte) 0x17, ChangeTimePumpEvent.class),
|
||||||
|
RECORD_TYPE_NEWTIMESET((byte) 0x18, NewTimeSet.class),
|
||||||
|
RECORD_TYPE_JournalEntryPumpLowBattery((byte) 0x19, JournalEntryPumpLowBatteryPumpEvent.class),
|
||||||
|
RECORD_TYPE_BATTERY((byte) 0x1A, BatteryPumpEvent.class),
|
||||||
|
RECORD_TYPE_PUMPSUSPENDED((byte) 0x1E, SuspendPumpEvent.class),
|
||||||
|
RECORD_TYPE_PUMPRESUMED((byte) 0x1F, ResumePumpEvent.class),
|
||||||
|
RECORD_TYPE_REWIND((byte) 0x21, RewindPumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGECHILDBLOCKENABLE((byte) 0x23, ChangeChildBlockEnablePumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGEMAXBOLUS((byte) 0x24, ChangeMaxBolusPumpEvent.class),
|
||||||
|
RECORD_TYPE_ENABLEDISABLEREMOTE((byte) 0x26, EnableDisableRemotePumpEvent.class),
|
||||||
|
RECORD_TYPE_TEMPBASALRATE((byte) 0x33, TempBasalRatePumpEvent.class),
|
||||||
|
RECORD_TYPE_LOWRESERVOIR((byte) 0x34, JournalEntryPumpLowReservoirPumpEvent.class),
|
||||||
|
RECORD_TYPE_AlarmClockReminder((byte) 0x35, AlarmClockReminderPumpEvent.class),
|
||||||
|
RECORD_TYPE_BGRECEIVED((byte) 0x3F, BGReceivedPumpEvent.class),
|
||||||
|
RECORD_TYPE_JournalEntryExerciseMarker((byte) 0x41, JournalEntryExerciseMarkerPumpEvent.class),
|
||||||
|
RECORD_TYPE_Unknown7Byte_1((byte) 0x42, Unknown7ByteEvent1.class),
|
||||||
|
RECORD_TYPE_InsulinMarker((byte) 0x43, InsulinMarkerEvent.class),
|
||||||
|
RECORD_TYPE_CHANGESENSORSETUP2((byte) 0x50, ChangeSensorSetup2PumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeSensorRateOfChangeAlertSetup((byte) 0x56, ChangeSensorRateOfChangeAlertSetupPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeBolusScrollStepSize((byte) 0x57, ChangeBolusScrollStepSizePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeBolusWizardSetup((byte) 0x5A, ChangeBolusWizardSetupPumpEvent.class),
|
||||||
|
RECORD_TYPE_BolusWizardBolusEstimate((byte) 0x5B, BolusWizardBolusEstimatePumpEvent.class),
|
||||||
|
RECORD_TYPE_UNABSORBEDINSULIN((byte) 0x5C, UnabsorbedInsulin.class),
|
||||||
|
RECORD_TYPE_CHANGEVARIABLEBOLUS((byte) 0x5e, ChangeVariableBolusPumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGEAUDIOBOLUS((byte) 0x5f, ChangeAudioBolusPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeBGReminderEnable((byte) 0x60, ChangeBGReminderEnablePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeAlarmClockEnable((byte) 0x61, ChangeAlarmClockEnablePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeTempBasalType((byte) 0x62, ChangeTempBasalTypePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeAlarmNotifyMode((byte) 0x63, ChangeAlarmNotifyModePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeTimeFormat((byte) 0x64, ChangeTimeFormatPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeReservoirWarningTime((byte) 0x65, ChangeReservoirWarningTimePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeBolusReminderEnable((byte) 0x66, ChangeBolusReminderEnablePumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeBolusReminderTime((byte) 0x67, ChangeBolusReminderTimePumpEvent.class),
|
||||||
|
RECORD_TYPE_DeleteBolusReminderTime((byte) 0x68, DeleteBolusReminderTimePumpEvent.class),
|
||||||
|
RECORD_TYPE_DeleteAlarmClockTime((byte) 0x6a, DeleteAlarmClockTimePumpEvent.class),
|
||||||
|
RECORD_TYPE_MODEL522RESULTTOTALS((byte) 0x6D, Model522ResultTotalsPumpEvent.class),
|
||||||
|
RECORD_TYPE_SARA6E((byte) 0x6E, Sara6EPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeCarbUnits((byte) 0x6f, ChangeCarbUnitsPumpEvent.class),
|
||||||
|
RECORD_TYPE_BASALPROFILESTART((byte) 0x7B, BasalProfileStart.class),
|
||||||
|
RECORD_TYPE_ChangeWatchdogEnable((byte) 0x7c, ChangeWatchdogEnablePumpEvent.class),
|
||||||
|
RECORD_TYPE_CHANGEOTHERDEVICEID((byte) 0x7d, ChangeOtherDeviceIDPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeWatchdogMarriageProfile((byte) 0x81, ChangeWatchdogMarriageProfilePumpEvent.class),
|
||||||
|
RECORD_TYPE_DeleteOtherDeviceID((byte) 0x82, DeleteOtherDeviceIDPumpEvent.class),
|
||||||
|
RECORD_TYPE_ChangeCaptureEventEnable((byte) 0x83, ChangeCaptureEventEnablePumpEvent.class);
|
||||||
|
|
||||||
|
|
||||||
|
private byte opcode;
|
||||||
|
private Class mRecordClass;
|
||||||
|
|
||||||
|
public byte opcode() {
|
||||||
|
return opcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class recordClass() {
|
||||||
|
return mRecordClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordTypeEnum(byte b, Class c) {
|
||||||
|
opcode = b;
|
||||||
|
mRecordClass = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecordTypeEnum fromByte(byte b) {
|
||||||
|
for (RecordTypeEnum en : RecordTypeEnum.values()) {
|
||||||
|
if (en.opcode() == b) {
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RECORD_TYPE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "RecordTypeEnum";
|
||||||
|
|
||||||
|
public <T extends Record> T getRecordClassInstance(PumpModel model) {
|
||||||
|
Constructor<T> ctor;
|
||||||
|
T record = null;
|
||||||
|
try {
|
||||||
|
Class c = recordClass();
|
||||||
|
if (c != null) {
|
||||||
|
ctor = recordClass().getConstructor();
|
||||||
|
if (ctor != null) {
|
||||||
|
record = ctor.newInstance();
|
||||||
|
record.setPumpModel(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// NOTE: these were all OR'd together, but android requires us to separate them.
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Record> T getRecordClassInstance(Bundle bundle, PumpModel model) {
|
||||||
|
byte opcode = bundle.getByte("_opcode");
|
||||||
|
RecordTypeEnum e = RecordTypeEnum.fromByte(opcode);
|
||||||
|
return e.getRecordClassInstance(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.joda.time.LocalDateTime;
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/4/16.
|
||||||
|
*/
|
||||||
|
public class TimeFormat {
|
||||||
|
private static final boolean DEBUG_TIMEFORMAT = false;
|
||||||
|
private static final String TAG = "TimeFormat";
|
||||||
|
|
||||||
|
public TimeFormat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String standardFormatString = "YYYY-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
|
public static DateTimeFormatter standardFormatter() {
|
||||||
|
return DateTimeFormat.forPattern(standardFormatString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDate parse2ByteDate(byte[] data, int offset) throws org.joda.time.IllegalFieldValueException {
|
||||||
|
int low = ByteUtil.asUINT8(data[0 + offset]) & 0x1F;
|
||||||
|
int mhigh = (ByteUtil.asUINT8(data[0 + offset]) & 0xE0) >> 4;
|
||||||
|
int mlow = (ByteUtil.asUINT8(data[1 + offset]) & 0x80) >> 7;
|
||||||
|
int month = mhigh + mlow;
|
||||||
|
int dayOfMonth = low + 1;
|
||||||
|
int year = 2000 + (ByteUtil.asUINT8(data[offset + 1]) & 0x7F);
|
||||||
|
/*
|
||||||
|
Log.w(TAG, String.format("Attempting to create DateTime from: %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
year + 2000, month, dayOfMonth, hour, minutes, seconds));
|
||||||
|
*/
|
||||||
|
// try {
|
||||||
|
LocalDate rval = new LocalDate(year, month, dayOfMonth);
|
||||||
|
return rval;
|
||||||
|
/*
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
Log.e(TAG,"Illegal DateTime field");
|
||||||
|
//e.printStackTrace();
|
||||||
|
return new LocalDate(1973,3,3);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// for relation to old code, replace offset with headerSize
|
||||||
|
|
||||||
|
|
||||||
|
public static LocalDateTime parse5ByteDate(byte[] data, int offset) throws org.joda.time.IllegalFieldValueException {
|
||||||
|
//offset = headerSize;
|
||||||
|
if (DEBUG_TIMEFORMAT) {
|
||||||
|
Log.w(TAG, String.format("bytes to parse: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X",
|
||||||
|
data[offset], data[offset + 1], data[offset + 2], data[offset + 3], data[offset + 4]));
|
||||||
|
}
|
||||||
|
int seconds = data[offset] & 0x3F;
|
||||||
|
int minutes = data[offset + 1] & 0x3F;
|
||||||
|
int hour = data[offset + 2] & 0x1F;
|
||||||
|
int dayOfMonth = data[offset + 3] & 0x1F;
|
||||||
|
// Yes, the month bits are stored in the high bits above seconds and minutes!!
|
||||||
|
int month = ((data[offset] >> 4) & 0x0c) + ((data[offset + 1] >> 6) & 0x03);
|
||||||
|
int year = data[offset + 4] & 0x3F; // Assuming this is correct, need to verify. Otherwise this will be a problem in 2016.
|
||||||
|
/*
|
||||||
|
Log.w(TAG,String.format("Attempting to create DateTime from: %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
year+2000,month,dayOfMonth,hour,minutes,seconds));
|
||||||
|
*/
|
||||||
|
// try {
|
||||||
|
LocalDateTime timeStamp = new LocalDateTime(year + 2000, month, dayOfMonth, hour, minutes, seconds);
|
||||||
|
return timeStamp;
|
||||||
|
/*
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
Log.e(TAG, "Illegal DateTime field");
|
||||||
|
//e.printStackTrace();
|
||||||
|
return new LocalDateTime(1973,2,2,2,2);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Many events in the history only consist of a single opcode and a datestamp.
|
||||||
|
* This serves to record that a particular event happened at a particular date.
|
||||||
|
* Many of the subclasses of this class only override the opcode.
|
||||||
|
*/
|
||||||
|
abstract public class TimeStampedRecord extends Record {
|
||||||
|
//private final static String TAG = "TimeStampedRecord";
|
||||||
|
private final static boolean DEBUG_TIMESTAMPEDRECORD = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDatestampOffset() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PumpTimeStamp timestamp;
|
||||||
|
|
||||||
|
public TimeStampedRecord() {
|
||||||
|
timestamp = new PumpTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpTimeStamp getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
return simpleParse(data, getDatestampOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is useful if there is no data inside, or we don't care about the data.
|
||||||
|
public boolean simpleParse(byte[] data, int fiveByteDateOffset) {
|
||||||
|
if (getLength() > data.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!collectTimeStamp(data, fiveByteDateOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rawbytes = ByteUtil.substring(data, 0, getLength());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean collectTimeStamp(byte[] data, int offset) {
|
||||||
|
try {
|
||||||
|
timestamp = new PumpTimeStamp(TimeFormat.parse5ByteDate(data, offset));
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
String timestampString = in.getString("timestamp");
|
||||||
|
timestamp = new PumpTimeStamp(timestampString);
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putString("timestamp", timestamp.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/11/16.
|
||||||
|
*/
|
||||||
|
public class AlarmClockReminderPumpEvent extends TimeStampedRecord {
|
||||||
|
|
||||||
|
public AlarmClockReminderPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Alarm Reminder";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class AlarmSensorPumpEvent extends TimeStampedRecord {
|
||||||
|
public AlarmSensorPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Alarm Sensor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class BGReceivedPumpEvent extends TimeStampedRecord {
|
||||||
|
private int amount = 0;
|
||||||
|
private byte[] meter = new byte[3];
|
||||||
|
|
||||||
|
public BGReceivedPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "BG Received";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (!super.simpleParse(data, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amount = (asUINT8(data[1]) << 3) + (asUINT8(data[4]) >> 5);
|
||||||
|
meter = ByteUtil.substring(data, 7, 3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
amount = in.getInt("amount");
|
||||||
|
meter = in.getByteArray("meter");
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putInt("amount", amount);
|
||||||
|
in.putByteArray("meter", meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class BasalProfileStart extends TimeStampedRecord {
|
||||||
|
private static final String TAG = "BasalProfileStart";
|
||||||
|
private int offset = 0;
|
||||||
|
private double rate = 0.0;
|
||||||
|
private int profileIndex = 0;
|
||||||
|
|
||||||
|
public BasalProfileStart() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Basal Profile Start";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (!simpleParse(data, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileIndex = asUINT8(data[1]);
|
||||||
|
offset = asUINT8(data[7]) * 30 * 1000 * 60;
|
||||||
|
rate = (double) (asUINT8(data[8])) / 40.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
offset = in.getInt("offset");
|
||||||
|
rate = in.getDouble("rate");
|
||||||
|
profileIndex = in.getInt("profileIndex");
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putInt("offset", offset);
|
||||||
|
in.putDouble("rate", rate);
|
||||||
|
in.putInt("profileIndex", profileIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class BatteryPumpEvent extends TimeStampedRecord {
|
||||||
|
public BatteryPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Battery";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.PumpTimeStamp;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeFormat;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class BolusNormalPumpEvent extends TimeStampedRecord {
|
||||||
|
private final static String TAG = "BolusNormalPumpEvent";
|
||||||
|
|
||||||
|
private double programmedAmount = 0.0;
|
||||||
|
private double deliveredAmount = 0.0;
|
||||||
|
private int duration = 0;
|
||||||
|
private double unabsorbedInsulinTotal = 0.0;
|
||||||
|
private String bolusType = "Unset";
|
||||||
|
|
||||||
|
public BolusNormalPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return PumpModel.isLargerFormat(model) ? 13 : 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Normal Bolus";
|
||||||
|
}
|
||||||
|
|
||||||
|
private double insulinDecode(int a, int b) {
|
||||||
|
return ((a << 8) + b) / 40.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (getLength() > data.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (PumpModel.isLargerFormat(model)) {
|
||||||
|
programmedAmount = insulinDecode(asUINT8(data[1]), asUINT8(data[2]));
|
||||||
|
deliveredAmount = insulinDecode(asUINT8(data[3]), asUINT8(data[4]));
|
||||||
|
unabsorbedInsulinTotal = insulinDecode(asUINT8(data[5]), asUINT8(data[6]));
|
||||||
|
duration = asUINT8(data[7]) * 30;
|
||||||
|
try {
|
||||||
|
timestamp = new PumpTimeStamp(TimeFormat.parse5ByteDate(data, 8));
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
programmedAmount = asUINT8(data[1]) / 10.0f;
|
||||||
|
deliveredAmount = asUINT8(data[2]) / 10.0f;
|
||||||
|
duration = asUINT8(data[3]) * 30;
|
||||||
|
unabsorbedInsulinTotal = 0;
|
||||||
|
try {
|
||||||
|
timestamp = new PumpTimeStamp(TimeFormat.parse5ByteDate(data, 4));
|
||||||
|
} catch (org.joda.time.IllegalFieldValueException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bolusType = (duration > 0) ? "square" : "normal";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
programmedAmount = in.getDouble("programmedAmount", 0.0);
|
||||||
|
deliveredAmount = in.getDouble("deliveredAmount", 0.0);
|
||||||
|
duration = in.getInt("duration", 0);
|
||||||
|
unabsorbedInsulinTotal = in.getDouble("unabsorbedInsulinTotal", 0.0);
|
||||||
|
bolusType = in.getString("bolusType", "Unset");
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putDouble("programmedAmount", programmedAmount);
|
||||||
|
in.putDouble("deliveredAmount", deliveredAmount);
|
||||||
|
in.putInt("duration", duration);
|
||||||
|
in.putDouble("unabsorbedInsulinTotal", unabsorbedInsulinTotal);
|
||||||
|
in.putString("bolusType", bolusType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class BolusWizardBolusEstimatePumpEvent extends TimeStampedRecord {
|
||||||
|
|
||||||
|
private int carbohydrates;
|
||||||
|
private int bloodGlucose;
|
||||||
|
private double foodEstimate;
|
||||||
|
private double correctionEstimate;
|
||||||
|
private double bolusEstimate;
|
||||||
|
private double unabsorbedInsulinTotal;
|
||||||
|
private int bgTargetLow;
|
||||||
|
private int bgTargetHigh;
|
||||||
|
private int insulinSensitivity;
|
||||||
|
private double carbRatio;
|
||||||
|
|
||||||
|
public BolusWizardBolusEstimatePumpEvent() {
|
||||||
|
correctionEstimate = (double) 0.0;
|
||||||
|
bloodGlucose = 0;
|
||||||
|
carbohydrates = 0;
|
||||||
|
carbRatio = 0.0;
|
||||||
|
insulinSensitivity = 0;
|
||||||
|
bgTargetLow = 0;
|
||||||
|
bgTargetHigh = 0;
|
||||||
|
bolusEstimate = 0.0;
|
||||||
|
foodEstimate = 0.0;
|
||||||
|
unabsorbedInsulinTotal = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return PumpModel.isLargerFormat(model) ? 22 : 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Bolus Wizard Est.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
carbohydrates = in.getInt("carbohydrates", 0);
|
||||||
|
bloodGlucose = in.getInt("bloodGlucose", 0);
|
||||||
|
foodEstimate = in.getDouble("foodEstimate", 0);
|
||||||
|
correctionEstimate = in.getDouble("correctionEstimate", 0);
|
||||||
|
bolusEstimate = in.getDouble("bolusEstimate", 0);
|
||||||
|
unabsorbedInsulinTotal = in.getDouble("unabsorbedInsulinTotal", 0);
|
||||||
|
bgTargetLow = in.getInt("bgTargetLow", 0);
|
||||||
|
bgTargetHigh = in.getInt("bgTargetHigh", 0);
|
||||||
|
insulinSensitivity = in.getInt("insulinSensitivity", 0);
|
||||||
|
carbRatio = in.getDouble("carbRatio", 0);
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putInt("carbohydrates", carbohydrates);
|
||||||
|
in.putInt("bloodGlucose", bloodGlucose);
|
||||||
|
in.putDouble("foodEstimate", foodEstimate);
|
||||||
|
in.putDouble("correctionEstimate", correctionEstimate);
|
||||||
|
in.putDouble("bolusEstimate", bolusEstimate);
|
||||||
|
in.putDouble("unabsorbedInsulinTotal", unabsorbedInsulinTotal);
|
||||||
|
in.putInt("bgTargetLow", bgTargetLow);
|
||||||
|
in.putInt("bgTargetHigh", bgTargetHigh);
|
||||||
|
in.putInt("insulinSensitivity", insulinSensitivity);
|
||||||
|
in.putDouble("carbRatio", carbRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCorrectionEstimate() {
|
||||||
|
return correctionEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBG() {
|
||||||
|
return bloodGlucose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCarbohydrates() {
|
||||||
|
return carbohydrates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getICRatio() {
|
||||||
|
return carbRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInsulinSensitivity() {
|
||||||
|
return insulinSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgTargetLow() {
|
||||||
|
return bgTargetLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgTargetHigh() {
|
||||||
|
return bgTargetHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBolusEstimate() {
|
||||||
|
return bolusEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFoodEstimate() {
|
||||||
|
return foodEstimate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getUnabsorbedInsulinTotal() {
|
||||||
|
return unabsorbedInsulinTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double insulinDecode(int a, int b) {
|
||||||
|
return ((a << 8) + b) / 40.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (!simpleParse(data, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (PumpModel.isLargerFormat(model)) {
|
||||||
|
carbohydrates = (asUINT8(data[8]) & 0x0c << 6) + asUINT8(data[7]);
|
||||||
|
bloodGlucose = (asUINT8(data[8]) & 0x03 << 8) + asUINT8(data[1]);
|
||||||
|
foodEstimate = insulinDecode(asUINT8(data[14]), asUINT8(data[15]));
|
||||||
|
correctionEstimate = (double) ((asUINT8(data[16]) & 0b111000) << 5 + asUINT8(data[13])) / 40.0;
|
||||||
|
bolusEstimate = insulinDecode(asUINT8(data[19]), asUINT8(data[20]));
|
||||||
|
unabsorbedInsulinTotal = insulinDecode(asUINT8(data[17]), asUINT8(data[18]));
|
||||||
|
bgTargetLow = asUINT8(data[12]);
|
||||||
|
bgTargetHigh = asUINT8(data[21]);
|
||||||
|
insulinSensitivity = asUINT8(data[11]);
|
||||||
|
carbRatio = (double) (((asUINT8(data[9]) & 0x07) << 8) + asUINT8(data[10])) / 40.0;
|
||||||
|
} else {
|
||||||
|
carbohydrates = asUINT8(data[7]);
|
||||||
|
bloodGlucose = ((asUINT8(data[8]) & 0x03) << 8) + asUINT8(data[1]);
|
||||||
|
foodEstimate = (double) (asUINT8(data[13])) / 10.0;
|
||||||
|
correctionEstimate = (double) ((asUINT8(data[14]) << 8) + asUINT8(data[12])) / 10.0;
|
||||||
|
bolusEstimate = (double) (asUINT8(data[18])) / 10.0;
|
||||||
|
unabsorbedInsulinTotal = (double) (asUINT8(data[16])) / 10.0;
|
||||||
|
bgTargetLow = asUINT8(data[11]);
|
||||||
|
bgTargetHigh = asUINT8(data[19]);
|
||||||
|
insulinSensitivity = asUINT8(data[10]);
|
||||||
|
carbRatio = (double) asUINT8(data[9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class CalBgForPhPumpEvent extends TimeStampedRecord {
|
||||||
|
private int amount = 0;
|
||||||
|
|
||||||
|
public CalBgForPhPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Cal Bg For Ph";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (!simpleParse(data, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amount = ((asUINT8(data[6]) & 0x80) << 1) + asUINT8(data[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
amount = in.getInt("amount", 0);
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
super.writeToBundle(in);
|
||||||
|
in.putInt("amount", amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeAlarmClockEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeAlarmClockEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Alarm Clock Enable";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeAlarmNotifyModePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeAlarmNotifyModePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Alarm Notify Mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeAudioBolusPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeAudioBolusPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Audio Bolus";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeBGReminderEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBGReminderEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch BG Rmndr Enable";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeBasalProfilePatternPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBasalProfilePatternPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 152;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Basal Prof Pat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeBasalProfilePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBasalProfilePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 152;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Basal Profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeBolusReminderEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBolusReminderEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Bolus Rmndr Enable";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeBolusReminderTimePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBolusReminderTimePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Bolus Rmndr Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeBolusScrollStepSizePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeBolusScrollStepSizePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Bolus Scroll SS";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeBolusWizardSetupPumpEvent extends TimeStampedRecord {
|
||||||
|
|
||||||
|
public ChangeBolusWizardSetupPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 144;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Bolus Wizard Setup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeCaptureEventEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeCaptureEventEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Capture Event Ena";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeCarbUnitsPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeCarbUnitsPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Carb Units";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeChildBlockEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeChildBlockEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Child Block Ena";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeMaxBolusPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeMaxBolusPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Max Bolux";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeOtherDeviceIDPumpEvent extends TimeStampedRecord {
|
||||||
|
|
||||||
|
public ChangeOtherDeviceIDPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 37;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Other Dev ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeReservoirWarningTimePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeReservoirWarningTimePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Res Warn Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeSensorRateOfChangeAlertSetupPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeSensorRateOfChangeAlertSetupPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Sensor ROC Alert";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeSensorSetup2PumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeSensorSetup2PumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 37;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Sensor Setup2";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.PumpModel;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeTempBasalTypePumpEvent extends TimeStampedRecord {
|
||||||
|
private boolean isPercent = false; // either absolute or percent
|
||||||
|
|
||||||
|
public ChangeTempBasalTypePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Temp Basal Type";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean parseFrom(byte[] data, PumpModel model) {
|
||||||
|
if (!simpleParse(data, 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (asUINT8(data[1]) == 1) {
|
||||||
|
isPercent = true;
|
||||||
|
} else {
|
||||||
|
isPercent = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readFromBundle(Bundle in) {
|
||||||
|
isPercent = in.getBoolean("isPercent", false);
|
||||||
|
return super.readFromBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToBundle(Bundle in) {
|
||||||
|
in.putBoolean("isPercent", isPercent);
|
||||||
|
super.writeToBundle(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeTimeFormatPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeTimeFormatPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Time Format";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ChangeTimePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeTimePumpEvent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Change Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeVariableBolusPumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeVariableBolusPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Var. Bolus";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeWatchdogEnablePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeWatchdogEnablePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch Watchdog Enable";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class ChangeWatchdogMarriageProfilePumpEvent extends TimeStampedRecord {
|
||||||
|
public ChangeWatchdogMarriageProfilePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Ch WD Marriage";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
public class ClearAlarmPumpEvent extends TimeStampedRecord {
|
||||||
|
public ClearAlarmPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Clear Alarm";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class DeleteAlarmClockTimePumpEvent extends TimeStampedRecord {
|
||||||
|
public DeleteAlarmClockTimePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Del Alarm Clock Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class DeleteBolusReminderTimePumpEvent extends TimeStampedRecord {
|
||||||
|
public DeleteBolusReminderTimePumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Del Bolus Rmndr Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.record;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history.TimeStampedRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 6/5/16.
|
||||||
|
*/
|
||||||
|
public class DeleteOtherDeviceIDPumpEvent extends TimeStampedRecord {
|
||||||
|
public DeleteOtherDeviceIDPumpEvent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLength() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortTypeName() {
|
||||||
|
return "Del Other Dev ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAAPSRelevant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue