Merge the code from RileyLinkAAPS

- First real results with Pump History (not complete yet)
- Set Basal Profile changes (not complete yet)
- version set to medtronic-0.4.0-SNAPSHOT
This commit is contained in:
Andy Rozman 2018-10-24 11:25:18 +01:00
parent 88b75d3496
commit f228475617
21 changed files with 1178 additions and 134 deletions

View file

@ -71,10 +71,12 @@ android {
targetSdkVersion 25
multiDexEnabled true
versionCode 1500
version "2.0g-medtronic-0.3"
// dev_version: 2.0g
version "medtronic-0.4.0-snapshot"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
buildConfigField "String", "DEV_VERSION", '"2.0g"'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk {

View file

@ -21,7 +21,6 @@ import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -182,7 +181,7 @@ public class RileyLinkBLEScanActivity extends AppCompatActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Will request that GPS be enabled for devices running Marshmallow or newer.
if (!isLocationEnabled(this)) {
if (!LocationHelper.isLocationEnabled(this)) {
LocationHelper.requestLocationForBluetooth(this);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -203,20 +202,6 @@ public class RileyLinkBLEScanActivity extends AppCompatActivity {
}
public boolean isLocationEnabled(Context context) {
final LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if (manager != null
&& (manager.isProviderEnabled(LocationManager.GPS_PROVIDER) || manager
.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) {
return true;
}
// otherwise return false
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

View file

@ -18,6 +18,8 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.defs.Riley
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkError;
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTaskExecutor;
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.WakeAndTuneTask;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.PumpDeviceState;
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
@ -89,8 +91,7 @@ public abstract class RileyLinkCommunicationManager {
RileyLinkUtil.setServiceState(RileyLinkServiceState.PumpConnectorError,
RileyLinkError.NoContactWithDevice);
timeoutCount = 0;
tuneForDevice();
// RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_quickTune);
ServiceTaskExecutor.startTask(new WakeAndTuneTask());
}
}
}

View file

@ -55,6 +55,7 @@ public class RileyLinkUtil {
private static RileyLinkTargetFrequency rileyLinkTargetFrequency;
// Broadcasts: RileyLinkBLE, RileyLinkService,
//private static RileyLinkIPCConnection rileyLinkIPCConnection;
private static RileyLinkTargetDevice targetDevice;
private static RileyLinkEncodingType encoding;
private static RileyLinkSelectPreference rileyLinkSelectPreference;

View file

@ -345,4 +345,34 @@ public class ByteUtil {
BIG_ENDIAN // 0 0 0 20 = normal - java
}
public static String getCompactString(byte[] data) {
String vval2 = ByteUtil.getHex(data);
vval2 = vval2.replace(" 0x", "");
vval2 = vval2.replace("0x", "");
return vval2;
}
public static byte[] createByteArray(String dataFull, int startIndex) {
return createByteArray(dataFull, startIndex, dataFull.length());
}
public static byte[] createByteArray(String dataFull, int startIndex, int length) {
String data = dataFull.substring(startIndex);
data = data.substring(0, length);
int len = data.length();
byte[] outArray = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
outArray[i / 2] = (byte)((Character.digit(data.charAt(i), 16) << 4) + Character.digit(data.charAt(i + 1),
16));
}
return outArray;
}
}

View file

@ -25,7 +25,11 @@ public class LocationHelper {
*/
public static boolean isLocationEnabled(Context context) {
LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
return (locationManager != null && //
(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || //
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)));
// return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.joda.time.Instant;
@ -24,6 +25,9 @@ import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.Page;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.history_old.Record;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump.MedtronicPumpHistoryDecoder;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump.PumpHistoryEntry;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump.PumpHistoryResult;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.ButtonPressCarelinkMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.CarelinkLongMessageBody;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.message.CarelinkShortMessageBody;
@ -58,6 +62,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
String errorMessage;
private MedtronicConverter medtronicConverter;
private boolean debugSetCommands = true;
private MedtronicPumpHistoryDecoder pumpHistoryDecoder;
private boolean doWakeUpBeforeCommand = true;
private boolean firstConnection = true;
@ -66,6 +71,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
super(context, rfspy, targetFrequency);
medtronicCommunicationManager = this;
this.medtronicConverter = new MedtronicConverter();
this.pumpHistoryDecoder = new MedtronicPumpHistoryDecoder();
MedtronicUtil.getPumpStatus().previousConnection = SP.getLong(
RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
}
@ -231,6 +237,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
if (debugSetCommands)
LOG.debug("Run command with Args: ");
PumpMessage rval;
PumpMessage shortMessage = makePumpMessage(msg.commandType, new CarelinkShortMessageBody(new byte[] { 0 }));
// look for ack from short message
@ -238,9 +245,11 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
if (shortResponse.commandType == MedtronicCommandType.CommandACK) {
if (debugSetCommands)
LOG.debug("Run command with Args: Got ACK response");
rval = sendAndListen(msg);
if (debugSetCommands)
LOG.debug("2nd Response: {}", rval);
return rval;
} else {
LOG.error("runCommandWithArgs: Pump did not ack Attention packet");
@ -249,6 +258,7 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
@Deprecated
private PumpMessage runCommandWithArgsLong(MedtronicCommandType commandType, byte[] content) {
LOG.debug("Run command with Args (Long): {}", commandType.name());
@ -316,7 +326,172 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
// TODO fix this with new code, and new response (Page)
private PumpMessage runCommandWithFrames(MedtronicCommandType commandType, List<List<Byte>> frames) {
LOG.debug("Run command with Frames: {}", commandType.name());
PumpMessage rval = null;
PumpMessage shortMessage = makePumpMessage(commandType, new CarelinkShortMessageBody(new byte[] { 0 }));
// look for ack from short message
PumpMessage shortResponse = sendAndListen(shortMessage);
if (shortResponse.commandType != MedtronicCommandType.CommandACK) {
LOG.error("runCommandWithFrames: Pump did not ack Attention packet");
return new PumpMessage("No ACK after start message.");
} else {
LOG.debug("Run command with Frames: Got ACK response for Attention packet");
}
int start = 0;
int frameNr = 1;
int len = 0;
for (List<Byte> frame : frames) {
byte[] frameData = MedtronicUtil.createByteArray(frame);
LOG.debug("Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData));
PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(frameData));
rval = sendAndListen(msg);
if (rval.commandType != MedtronicCommandType.CommandACK) {
LOG.error("runCommandWithFrames: Pump did not ACK frame #{}", frameNr);
return new PumpMessage("No ACK after frame #" + frameNr);
} else {
LOG.debug("Run command with Frames: Got ACK response for frame #{}", (frameNr));
}
frameNr++;
}
return rval;
}
public PumpHistoryResult getPumpHistory(PumpHistoryEntry lastEntry, LocalDateTime targetDate) {
// int pageNumber = 0;
// TODO multiple history pages
PumpHistoryResult pumpTotalResult = new PumpHistoryResult(lastEntry, targetDate);
if (doWakeUpBeforeCommand)
wakeUp(receiverDeviceAwakeForMinutes, false);
for (int pageNumber = 0; pageNumber < 16; pageNumber++) {
RawHistoryPage rawHistoryPage = new RawHistoryPage();
// wakeUp(receiverDeviceAwakeForMinutes, false);
PumpMessage getHistoryMsg = makePumpMessage(MedtronicCommandType.GetHistoryData,
new GetHistoryPageCarelinkMessageBody(pageNumber));
LOG.info("getPumpHistory: Page {}", 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(MedtronicCommandType.CommandACK, 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
rawHistoryPage.appendData(currentResponse.getFrameData());
// RileyLinkMedtronicService.getInstance().announceProgress(
// ((100 / 16) * currentResponse.getFrameNumber() + 1));
LOG.info("getPumpHistory: Got frame {} of Page {}", currentResponse.getFrameNumber(), pageNumber);
// 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 {}, received {} (retrying)", expectedFrameNum,
currentResponse.getFrameNumber());
} else if (frameData.length == 0) {
LOG.warn("Frame has zero length, retrying");
}
failures++;
if (failures == 6) {
LOG.error(
"getPumpHistory: 6 failures in attempting to download frame {} of page {}, 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 (rawHistoryPage.getLength() != 1024) {
LOG.warn("getPumpHistory: short page. Expected length of 1024, found length of "
+ rawHistoryPage.getLength());
}
if (!rawHistoryPage.isChecksumOK()) {
LOG.error("getPumpHistory: checksum is wrong");
}
rawHistoryPage.dumpToDebug();
List<PumpHistoryEntry> medtronicHistoryEntries = pumpHistoryDecoder.processPageAndCreateRecords(
rawHistoryPage, false, PumpHistoryEntry.class);
LOG.debug("getPumpHistory: Found {} history entries.", medtronicHistoryEntries.size());
// PumpHistoryResult pumpHistoryResult = new PumpHistoryResult(lastEntry, targetDate);
pumpTotalResult.addHistoryEntries(medtronicHistoryEntries);
LOG.debug("getPumpHistory: Search status: Search finished: {}", pumpTotalResult.isSearchFinished());
if (pumpTotalResult.isSearchFinished()) {
return pumpTotalResult;
}
}
return pumpTotalResult;
// Page page = new Page();
// // page.parseFrom(rval.getData(),PumpModel.MM522);
// // FIXME
// page.parseFrom(rawHistoryPage.getData(), MedtronicDeviceType.Medtronic_522);
//
// // return page;
//
// return null;
}
@Deprecated
public Page getPumpHistoryPage(int pageNumber) {
RawHistoryPage rval = new RawHistoryPage();
@ -474,13 +649,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
// protected PumpMessage makePumpMessage(byte[] typeAndBody) {
// PumpMessage msg = new PumpMessage();
// msg.init(ByteUtil.concat(ByteUtil.concat(new byte[]{PacketType.Carelink.getValue()},
// rileyLinkServiceData.pumpIDBytes), typeAndBody));
// return msg;
// }
private PumpMessage sendAndGetResponse(MedtronicCommandType commandType) {
return sendAndGetResponse(commandType, null, DEFAULT_TIMEOUT);
@ -531,24 +699,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
// FIXME remove
// private PumpMessage sendAndGetACK(MedtronicCommandType commandType, byte[] bodyData, int timeoutMs) {
// // wakeUp
// wakeUp(receiverDeviceAwakeForMinutes, false);
//
// // create message
// PumpMessage msg;
//
// if (bodyData == null)
// msg = makePumpMessage(commandType);
// else
// msg = makePumpMessage(commandType, bodyData);
//
// // send and wait for ACK
// PumpMessage response = send(msg, timeoutMs);
// return response;
// }
protected PumpMessage sendAndListen(RLMessage msg) {
return sendAndListen(msg, 4000); // 2000
}
@ -818,7 +968,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
// TODO test with values bigger than 30U
public Boolean setBolus(double units) {
LOG.info("setBolus: " + units);
@ -978,12 +1127,30 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
// byte[] body = basalProfile.generateRawData();
byte[] body = new byte[] { 32, 0, 0, 38, 0, 13, 44, 0, 19, 38, 0, 28 };
// byte[] body = new byte[] { 32, 0, 0, 38, 0, 13, 44, 0, 19, 38, 0, 28 };
PumpMessage responseMessage;
byte[] body = ByteUtil
.createByteArray(
"06000052000178050202000304000402000504000602000704000802000904000a02000b04000c02000d02000e02000f040010020011040012020013040014020015040016020017040018020019000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
0);
if (debugSetCommands)
LOG.debug("Set Basal Profile: Body [{}] - {}", body.length, HexDump.toHexStringDisplayable(body));
List<List<Byte>> basalProfileFrames = MedtronicUtil.getBasalProfileFrames(body);
PumpMessage responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileA, basalProfileFrames);
// PumpMessage responseMessage;
//
// if (debugSetCommands)
// LOG.debug("Set Basal Profile: Body [{}] - {}", body.length, HexDump.toHexStringDisplayable(body));
//
// for (List<Byte> basalProfileFrame : basalProfileFrames) {
//
// PumpMessage msg = makePumpMessage(MedtronicCommandType.SetBasalProfileA, //
// new CarelinkLongMessageBody(ByteUtil.concat((byte)body.length, body)));
//
// responseMessage = runCommandWithArgs(msg);
//
// }
// if (body.length <= 64) {
//
@ -992,12 +1159,14 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
//
// responseMessage = runCommandWithArgs(msg);
// } else
{
// {
//
// responseMessage = runCommandWithArgsLong(MedtronicCommandType.SetBasalProfileA, body);
// }
responseMessage = runCommandWithArgsLong(MedtronicCommandType.SetBasalProfileA, body);
}
// if (debugSetCommands)
// LOG.debug("Set Basal Profile: {}", HexDump.toHexStringDisplayable(responseMessage.getRawContent()));
if (debugSetCommands)
LOG.debug("Set Basal Profile: {}", HexDump.toHexStringDisplayable(responseMessage.getRawContent()));
return responseMessage.commandType == MedtronicCommandType.CommandACK;
@ -1032,20 +1201,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
}
public PumpMessage setExtendedBolus(double units, int duration) {
// FIXME see decocare
PumpMessage response = sendAndGetResponse(MedtronicCommandType.SetBolus, MedtronicUtil.getBolusStrokes(units));
return response;
}
public PumpMessage cancelExtendedBolus() {
// set cancelBolus
return null;
}
// Set TBR 100%
// Cancel TBR (set TBR 100%) 100%
// Get Status (40%)

View file

@ -38,11 +38,6 @@ public class MedtronicConverter {
this.pumpModel = MedtronicUtil.getMedtronicPumpModel();
// if (this.pumpModel == null) {
// LOG.warn("Pump model was not identified. Defaulting to 522.");
// this.pumpModel = MedtronicDeviceType.Medtronic_522;
// }
switch (commandType) {
case PumpModel: {

View file

@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicDeviceType;
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
/**
@ -43,6 +44,7 @@ public abstract class MedtronicHistoryDecoder {
protected boolean statisticsEnabled = true;
protected Map<Integer, Integer> unknownOpCodes;
protected Map<RecordDecodeStatus, Map<String, String>> mapStatistics;
protected MedtronicDeviceType deviceType;
public MedtronicHistoryDecoder() {
@ -57,42 +59,57 @@ public abstract class MedtronicHistoryDecoder {
// public abstract void refreshOutputWriter();
public boolean decodePage(RawHistoryPage dataPage) throws Exception {
// refreshOutputWriter();
List<? extends MedtronicHistoryEntry> minimedHistoryRecords = processPageAndCreateRecords(dataPage);
for (MedtronicHistoryEntry record : minimedHistoryRecords) {
decodeRecord(record);
}
runPostDecodeTasks();
return true;
}
// public List<? extends MedtronicHistoryEntry> decodePage(RawHistoryPage dataPage) throws Exception {
// // refreshOutputWriter();
//
// List<? extends MedtronicHistoryEntry> minimedHistoryRecords = processPageAndCreateRecords(dataPage);
//
// for (MedtronicHistoryEntry record : minimedHistoryRecords) {
// decodeRecord(record);
// }
//
// runPostDecodeTasks();
//
// return minimedHistoryRecords;
// }
// public List<? extends MedtronicHistoryEntry> decodePartialPage(RawHistoryPage dataPage) throws Exception {
// // refreshOutputWriter();
//
// List<? extends MedtronicHistoryEntry> minimedHistoryRecords = processPageAndCreateRecords(dataPage, true);
//
// for (MedtronicHistoryEntry record : minimedHistoryRecords) {
// decodeRecord(record);
// }
//
// runPostDecodeTasks();
//
// return minimedHistoryRecords;
// }
protected abstract void runPostDecodeTasks();
// TODO_ extend this to also use bigger pages (for now we support only 1024
// pages)
public List<Byte> checkPage(RawHistoryPage page) throws RuntimeException {
public List<Byte> checkPage(RawHistoryPage page, boolean partial) throws RuntimeException {
List<Byte> byteList = new ArrayList<Byte>();
if (page.getData().length != 1024 /* page.commandType.getRecordLength() */) {
LOG.error("Page size is not correct. Size should be {}, but it was {} instead.", 1024,
page.getData().length);
// throw exception perhaps
return byteList;
}
// if (!partial && page.getData().length != 1024 /* page.commandType.getRecordLength() */) {
// LOG.error("Page size is not correct. Size should be {}, but it was {} instead.", 1024,
// page.getData().length);
// // throw exception perhaps
// return byteList;
// }
if (MedtronicUtil.getMedtronicPumpModel() == null) {
LOG.error("Device Type is not defined.");
return byteList;
}
if (page.isChecksumOK()) {
if (page.getData().length != 1024) {
return ByteUtil.getListFromByteArray(page.getData());
} else if (page.isChecksumOK()) {
return ByteUtil.getListFromByteArray(page.getOnlyData());
} else {
return null;
@ -100,9 +117,13 @@ public abstract class MedtronicHistoryDecoder {
}
public abstract List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page)
throws Exception;
// public abstract List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page,
// boolean partial) throws Exception;
//
//
// public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) throws Exception {
// return processPageAndCreateRecords(page, false);
// }
protected void prepareStatistics() {
if (!statisticsEnabled)
@ -190,4 +211,27 @@ public abstract class MedtronicHistoryDecoder {
return StringUtil.getFormatedValueUS(value, decimals);
}
// public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
// boolean partial) {
// return (processPageAndCreateRecords(
// rawHistoryPage, partial, clazz);
// }
public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
boolean partial, Class<E> clazz) {
List<Byte> dataClear = checkPage(rawHistoryPage, partial);
List<E> records = createRecords(dataClear, clazz);
for (MedtronicHistoryEntry record : records) {
decodeRecord(record);
}
runPostDecodeTasks();
return records;
}
protected abstract <E extends MedtronicHistoryEntry> List<E> createRecords(List<Byte> dataClear, Class<E> clazz);
}

View file

@ -43,7 +43,7 @@ public abstract class MedtronicHistoryEntry implements MedtronicHistoryEntryInte
protected LocalDateTime dateTime;
// protected PumpTimeStampedRecord historyEntryDetails;
private Map<String, Object> decodedData;
protected Map<String, Object> decodedData;
public void setData(List<Byte> listRawData, boolean doNotProcess) {

View file

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryDecoder;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RecordDecodeStatus;
/**
@ -111,14 +110,13 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
}
@Override
public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) throws Exception {
List<Byte> dataClear = checkPage(page);
return createRecords(dataClear);
}
// @Override
// public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) throws Exception {
// List<Byte> dataClear = checkPage(page, false);
// return createRecords(dataClear);
// }
private List<? extends MedtronicHistoryEntry> createRecords(List<Byte> dataClearInput) {
protected <E extends MedtronicHistoryEntry> List<E> createRecords(List<Byte> dataClearInput, Class<E> clazz) {
// List<MinimedHistoryEntry> listRecords = new
// ArrayList<MinimedHistoryEntry>();
@ -134,7 +132,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
int counter = 0;
int record = 0;
List<MedtronicHistoryEntry> outList = new ArrayList<MedtronicHistoryEntry>();
List<E> outList = new ArrayList<E>();
// create CGMS entries (without dates)
do {
@ -164,7 +162,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
// System.out.println("Record: " + pe);
outList.add(pe);
outList.add((E)pe);
} else {
List<Byte> listRawData = new ArrayList<Byte>();
listRawData.add((byte)opCode);
@ -182,7 +180,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
// System.out.println("Record: " + pe);
outList.add(pe);
outList.add((E)pe);
}
} else {
CGMSHistoryEntry pe = new CGMSHistoryEntry();
@ -196,7 +194,7 @@ public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder {
// System.out.println("Record: " + pe);
outList.add(pe);
outList.add((E)pe);
}
} while (counter < dataClear.size());

View file

@ -16,7 +16,6 @@ import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryDecoder;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RawHistoryPage;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.RecordDecodeStatus;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BolusDTO;
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BolusWizardDTO;
@ -62,17 +61,22 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
public MedtronicPumpHistoryDecoder() {
super();
}
public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) {
List<Byte> dataClear = checkPage(page);
return createRecords(dataClear);
}
// public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) {
// List<Byte> dataClear = checkPage(page, false);
// return createRecords(dataClear);
// }
// public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
// boolean partial, Class<E> clazz) {
//
// return null;
// }
public List<? extends MedtronicHistoryEntry> createRecords(List<Byte> dataClear) {
public <E extends MedtronicHistoryEntry> List<E> createRecords(List<Byte> dataClear, Class<E> clazz) {
prepareStatistics();
int counter = 0;
@ -80,7 +84,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
boolean incompletePacket = false;
deviceType = MedtronicUtil.getMedtronicPumpModel();
List<MedtronicHistoryEntry> outList = new ArrayList<MedtronicHistoryEntry>();
List<E> outList = new ArrayList<E>();
String skipped = null;
int elementStart = 0;
@ -223,7 +227,7 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
if (decoded == RecordDecodeStatus.OK) // we add only OK records, all others are ignored
{
outList.add(pe);
outList.add((E)pe);
}
}
@ -249,6 +253,19 @@ public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder {
}
public <E extends MedtronicHistoryEntry> List<E> getTypedList(List<? extends MedtronicHistoryEntry> list,
Class<E> clazz) {
List<E> listOut = new ArrayList<>();
for (MedtronicHistoryEntry medtronicHistoryEntry : list) {
listOut.add((E)medtronicHistoryEntry);
}
return listOut;
}
public RecordDecodeStatus decodeRecord(MedtronicHistoryEntry entryIn, boolean x) {
// FIXME
// TODO

View file

@ -1,5 +1,11 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump;
import java.util.Objects;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.PumpCommon.utils.HexDump;
import info.nightscout.androidaps.plugins.PumpCommon.utils.StringUtil;
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHistoryEntry;
@ -27,6 +33,8 @@ import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.MedtronicHi
public class PumpHistoryEntry extends MedtronicHistoryEntry {
private static Logger LOG = LoggerFactory.getLogger(PumpHistoryEntry.class);
private PumpHistoryEntryType entryType;
private Integer opCode; // this is set only when we have unknown entry...
// private LocalDateTime timeOfEntry;
@ -98,4 +106,42 @@ public class PumpHistoryEntry extends MedtronicHistoryEntry {
public int getDateLength() {
return this.entryType.getDateLength();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof PumpHistoryEntry))
return false;
PumpHistoryEntry that = (PumpHistoryEntry)o;
return entryType == that.entryType && //
Objects.equals(this.dateTime, that.dateTime); // && //
// Objects.equals(this.decodedData, that.decodedData);
}
@Override
public int hashCode() {
return Objects.hash(entryType, opCode, offset);
}
public boolean isAfter(LocalDateTime dateTimeIn) {
// LOG.debug("Entry: " + this.dateTime);
// LOG.debug("Datetime: " + dateTimeIn);
// LOG.debug("Item after: " + this.dateTime.isAfter(dateTimeIn));
return this.dateTime.isAfter(dateTimeIn);
}
public static class Comparator implements java.util.Comparator<PumpHistoryEntry> {
@Override
public int compare(PumpHistoryEntry o1, PumpHistoryEntry o2) {
return o2.dateTime.compareTo(o1.dateTime);
}
}
}

View file

@ -0,0 +1,130 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by andy on 9/23/18.
*/
/**
* History page contains data, sorted from oldest to newest
*/
public class PumpHistoryResult {
private static final Logger LOG = LoggerFactory.getLogger(PumpHistoryResult.class);
private boolean searchFinished = false;
private PumpHistoryEntry searchEntry = null;
private LocalDateTime searchDate = null;
private SearchType searchType = SearchType.None;
private List<PumpHistoryEntry> unprocessedEntries;
public List<PumpHistoryEntry> validEntries;
// private Object validValues;
public PumpHistoryResult(PumpHistoryEntry searchEntry, LocalDateTime targetDate) {
if (searchEntry != null) {
this.searchEntry = searchEntry;
this.searchType = SearchType.LastEntry;
LOG.debug("PumpHistoryResult. Search parameters: Last Entry: " + searchEntry.getLocalDateTime() + " type="
+ searchEntry.getEntryType().name());
} else if (targetDate != null) {
this.searchDate = targetDate;
this.searchType = SearchType.Date;
LOG.debug("PumpHistoryResult. Search parameters: Date: " + targetDate);
}
// this.unprocessedEntries = new ArrayList<>();
this.validEntries = new ArrayList<>();
}
public void addHistoryEntries(List<PumpHistoryEntry> entries) {
this.unprocessedEntries = entries;
LOG.debug("PumpHistoryResult. Unprocessed entries: {}", entries);
processEntries();
}
public void processEntries() {
int olderEntries = 0;
switch (searchType) {
case None:
this.validEntries.addAll(this.unprocessedEntries);
// this.unprocessedEntries = null;
break;
case LastEntry: {
if (this.validEntries == null)
this.validEntries = new ArrayList<>();
Collections.sort(this.unprocessedEntries, new PumpHistoryEntry.Comparator());
LOG.debug("PumpHistoryResult. Search entry date: " + searchEntry.getLocalDateTime());
for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) {
if (unprocessedEntry.equals(searchEntry)) {
searchFinished = true;
break;
}
this.validEntries.add(unprocessedEntry);
}
}
break;
case Date: {
if (this.validEntries == null)
this.validEntries = new ArrayList<>();
for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) {
if (unprocessedEntry.isAfter(this.searchDate)) {
this.validEntries.add(unprocessedEntry);
} else {
if (unprocessedEntry.getLocalDateTime().getYear() != 2000)
olderEntries++;
}
}
if (olderEntries > 0) {
Collections.sort(this.validEntries, new PumpHistoryEntry.Comparator());
searchFinished = true;
}
}
break;
} // switch
}
public boolean isSearchRequired() {
return searchType != SearchType.None;
}
public boolean isSearchFinished() {
return searchFinished;
}
public List<PumpHistoryEntry> getValidEntries() {
return validEntries;
}
enum SearchType {
None, //
LastEntry, //
Date
}
}

View file

@ -1,5 +1,9 @@
package info.nightscout.androidaps.plugins.PumpMedtronic.comm.message;
import java.util.List;
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicUtil;
/**
* Created by geoff on 6/2/16.
*/
@ -19,8 +23,17 @@ public class CarelinkLongMessageBody extends MessageBody {
}
public CarelinkLongMessageBody(List<Byte> payload) {
init(MedtronicUtil.createByteArray(payload));
}
@Override
public void init(byte[] rxData) {
if (rxData != null && rxData.length == LONG_MESSAGE_BODY_LENGTH) {
data = rxData;
} else {
data = new byte[LONG_MESSAGE_BODY_LENGTH];
if (rxData != null) {
int size = rxData.length < LONG_MESSAGE_BODY_LENGTH ? rxData.length : LONG_MESSAGE_BODY_LENGTH;
@ -29,6 +42,7 @@ public class CarelinkLongMessageBody extends MessageBody {
}
}
}
}
@Override

View file

@ -126,7 +126,7 @@ public class PumpMessage implements RLMessage {
System.arraycopy(messageBody.getTxData(), 1, arrayOut, 0, length);
Log.v("PumpMessage", "Length: " + length + ", Original Length: " + originalLength + ", CommandType: "
Log.d("PumpMessage", "Length: " + length + ", Original Length: " + originalLength + ", CommandType: "
+ commandType);
return arrayOut;

View file

@ -109,8 +109,10 @@ public enum MedtronicCommandType implements Serializable // , MinimedCommandType
GetBasalProfileSTD(146, "Get Profile Standard", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, 192, 1, 8, 1), // 146
GetBasalProfileA(147, "Get Profile A", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, 192, 1, 9), // 147
// FIXME
GetBasalProfileB(148, "Get Profile B", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, 192, 1, 10), // 148
// FIXME
SetBasalProfileSTD(0x6f, "Set Profile Standard", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, 192, 1, 8), // 111

View file

@ -255,7 +255,7 @@ public class MedtronicPumpStatus extends PumpStatus {
}
if (val > defaultValueDouble) {
SP.putString(MedtronicConst.Prefs.MaxBolus, "25.0");
SP.putString(MedtronicConst.Prefs.MaxBolus, defaultValue);
val = defaultValueDouble;
}

View file

@ -75,8 +75,8 @@ public class RileyLinkMedtronicService extends RileyLinkService {
public void addPumpSpecificIntents(IntentFilter intentFilter) {
intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchHistory);
intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchSavedHistory);
//intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchHistory);
//intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchSavedHistory);
}
@ -431,10 +431,6 @@ public class RileyLinkMedtronicService extends RileyLinkService {
}
// public MedtronicCommunicationManager getPumpManager() {
// return this.medtronicCommunicationManager;
// }
// FIXME remove
public void sendNotification(ServiceNotification serviceNotification, Object o) {
LOG.warn("Send Notification has no implementation.");

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.PumpMedtronic.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -45,6 +46,8 @@ public class MedtronicUtil extends RileyLinkUtil {
private static MedtronicPumpStatus medtronicPumpStatus;
private static MedtronicCommandType currentCommand;
private static Map<String, PumpSettingDTO> settings;
private static int BIG_FRAME_LENGTH = 65;
private static int doneBit = 1 << 7;
public static LocalTime getTimeFrom30MinInterval(int interval) {
@ -285,6 +288,105 @@ public class MedtronicUtil extends RileyLinkUtil {
}
// Note: at the moment supported only for 24 items, if you will use it for more than
// that you will need to add
public static List<List<Byte>> getBasalProfileFrames(byte[] data) {
boolean done = false;
int start = 0;
int frame = 1;
List<List<Byte>> frames = new ArrayList<>();
boolean lastFrame = false;
do {
int frameLength = BIG_FRAME_LENGTH - 1;
if (start + frameLength > data.length) {
frameLength = data.length - start;
}
// System.out.println("Framelength: " + frameLength);
byte[] substring = ByteUtil.substring(data, start, frameLength);
// System.out.println("Subarray: " + ByteUtil.getCompactString(substring));
// System.out.println("Subarray Lenths: " + substring.length);
List<Byte> frameData = ByteUtil.getListFromByteArray(substring);
if (isEmptyFrame(frameData)) {
byte b = (byte)frame;
// b |= 0x80;
// b |= 0b1000_0000;
b |= doneBit;
frameData.add(0, b);
checkAndAppenLastFrame(frameData);
lastFrame = true;
done = true;
} else {
frameData.add(0, (byte)frame);
}
// System.out.println("Subarray: " + ByteUtil.getCompactString(substring));
frames.add(frameData);
frame++;
start += (BIG_FRAME_LENGTH - 1);
if (start == data.length) {
done = true;
}
} while (!done);
if (!lastFrame) {
List<Byte> frameData = new ArrayList<>();
byte b = (byte)frame;
// b |= 0b1000_0000;
b |= doneBit;
frameData.add(b);
checkAndAppenLastFrame(frameData);
}
return frames;
}
private static void checkAndAppenLastFrame(List<Byte> frameData) {
if (frameData.size() == BIG_FRAME_LENGTH)
return;
int missing = BIG_FRAME_LENGTH - frameData.size();
for (int i = 0; i < missing; i++) {
frameData.add((byte)0x00);
}
}
private static boolean isEmptyFrame(List<Byte> frameData) {
for (Byte frameDateEntry : frameData) {
if (frameDateEntry != 0x00) {
return false;
}
}
return true;
}
public static boolean isLowLevelDebug() {
return lowLevelDebug;
}

View file

@ -0,0 +1,522 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:minWidth="300dp"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<ToggleButton
android:id="@+id/toggleButton"
android:layout_width="90dp"
android:layout_height="60dp"
android:layout_weight="1"
android:text="ToggleButton"
android:textOff="@string/treatments_wizard_bg_label"
android:textOn="@string/treatments_wizard_bg_label" />
<ToggleButton
android:layout_width="90dp"
android:layout_height="60dp"
android:layout_weight="1"
android:text="ToggleButton"
android:checked="true"
android:textOff="@string/treatments_wizard_carbs_label"
android:textOn="@string/treatments_wizard_carbs_label" />
<ToggleButton
android:layout_width="90dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="ToggleButton"
android:textOff="@string/treatments_wizard_correction_label"
android:textOn="@string/treatments_wizard_correction_label" />
<ToggleButton
android:layout_width="90dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="ToggleButton"
android:textOff="@string/careportal_newnstreatment_carbtime_label"
android:textOn="@string/careportal_newnstreatment_carbtime_label" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_weight="1"
android:textAlignment="center"
android:gravity="center"
android:text="0.0 mmol" />
<TextView
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textAlignment="center"
android:gravity="center"
android:text="0 g" />
<TextView
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textAlignment="center"
android:gravity="center"
android:text="0.00 U" />
<TextView
android:id="@+id/textView3"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textAlignment="center"
android:text="0 min" />
</LinearLayout>
<LinearLayout
android:id="@+id/treatments_wizard_carbtime_layout"
android:layout_width="wrap_content"
android:layout_height="70dp"
android:orientation="horizontal">
<TextView
android:layout_width="92dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:width="120dp"
android:gravity="center"
android:padding="10dp"
android:text="@string/treatments_wizard_carbs_label"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textStyle="bold" />
<info.nightscout.utils.NumberPicker
android:id="@+id/treatments_wizard_carbtimeinput"
android:layout_width="193dp"
android:layout_height="74dp"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="31dp"
android:layout_gravity="center_vertical"
android:gravity="center"
android:minWidth="45dp"
android:paddingLeft="5dp"
android:text="U"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:id="@+id/treatments_wizard_profile_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@string/careportal_newnstreatment_profile_label"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textStyle="bold" />
<Spinner
android:id="@+id/treatments_wizard_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_weight="0.5" />
<CheckBox
android:id="@+id/treatments_wizard_sbcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:checked="false"
android:text="Super\nbolus" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/treatments_wizard_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_darkgray"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="2.35U 28g"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
<LinearLayout
android:id="@+id/treatments_wizard_notes_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:width="120dp"
android:padding="10dp"
android:text="@string/careportal_newnstreatment_notes_label"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textStyle="bold" />
<EditText
android:id="@+id/treatment_wizard_notes"
android:layout_width="155dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="left"
android:inputType="text|textCapSentences"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<include layout="@layout/mdtp_done_button" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="-15dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<CheckBox
android:id="@+id/treatments_wizard_bgcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="24dp"
android:text="@string/treatments_wizard_bg_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<CheckBox
android:id="@+id/treatments_wizard_ttcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="false" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="30dp"
android:text="@string/treatments_wizard_tt_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bginsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<CheckBox
android:id="@+id/treatments_wizard_bgtrendcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="false" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="86dp"
android:text="@string/treatments_wizard_bgtrend_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bgtrend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bgtrendinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:id="@+id/treatments_wizard_cob_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<CheckBox
android:id="@+id/treatments_wizard_cobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="false" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="86dp"
android:text="@string/treatments_wizard_cob_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_cob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_cobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<CheckBox
android:id="@+id/treatments_wizard_bolusiobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="130dp"
android:text="@string/treatments_wizard_bolusiob_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bolusiob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bolusiobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<CheckBox
android:id="@+id/treatments_wizard_basaliobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
android:checked="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="130dp"
android:text="@string/treatments_wizard_basaliob_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_basaliob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_basaliobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="86dp"
android:text="@string/treatments_wizard_carbs_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_carbs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_carbsinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="86dp"
android:text="@string/superbolus"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_sb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_sbinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="86dp"
android:text="@string/treatments_wizard_correction_label"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_correction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="94dp"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_correctioninsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
android:gravity="end"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</ScrollView>