WIP on Omnipod Dash program basal command
This commit is contained in:
parent
c183a68798
commit
1c490f72ae
14 changed files with 430 additions and 210 deletions
|
@ -10,7 +10,7 @@ public final class DeactivateCommand extends NonceEnabledCommand {
|
|||
private static final short LENGTH = 6;
|
||||
private static final byte BODY_LENGTH = 4;
|
||||
|
||||
private DeactivateCommand(int address, short sequenceNumber, boolean multiCommandFlag, int nonce) {
|
||||
DeactivateCommand(int address, short sequenceNumber, boolean multiCommandFlag, int nonce) {
|
||||
super(CommandType.DEACTIVATE, address, sequenceNumber, multiCommandFlag, nonce);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ public final class GetVersionCommand extends HeaderEnabledCommand {
|
|||
private static final short LENGTH = 6;
|
||||
private static final byte BODY_LENGTH = 4;
|
||||
|
||||
private GetVersionCommand(int address, short sequenceNumber, boolean multiCommandFlag) {
|
||||
GetVersionCommand(int address, short sequenceNumber, boolean multiCommandFlag) {
|
||||
super(CommandType.GET_VERSION, address, sequenceNumber, multiCommandFlag);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
public final class ProgramAlertsCommand extends NonceEnabledCommand {
|
||||
private final List<AlertConfiguration> alertConfigurations;
|
||||
|
||||
private ProgramAlertsCommand(int address, short sequenceNumber, boolean multiCommandFlag, List<AlertConfiguration> alertConfigurations, int nonce) {
|
||||
ProgramAlertsCommand(int address, short sequenceNumber, boolean multiCommandFlag, List<AlertConfiguration> alertConfigurations, int nonce) {
|
||||
super(CommandType.PROGRAM_ALERTS, address, sequenceNumber, multiCommandFlag, nonce);
|
||||
this.alertConfigurations = new ArrayList<>(alertConfigurations);
|
||||
}
|
||||
|
|
|
@ -2,23 +2,32 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.CommandBuilder;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.HeaderEnabledCommand;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentSlot;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.LongInsulinProgramElement;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ProgramBasalUtil;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
|
||||
|
||||
// Always preceded by 0x1a ProgramInsulinCommand
|
||||
public final class ProgramBasalCommand implements Command {
|
||||
private final List<InsulinProgramElement> insulinProgramElements;
|
||||
public final class ProgramBasalCommand extends HeaderEnabledCommand {
|
||||
private final ProgramInsulinCommand interlockCommand;
|
||||
private final List<LongInsulinProgramElement> insulinProgramElements;
|
||||
private final ProgramReminder programReminder;
|
||||
private final byte currentInsulinProgramElementIndex;
|
||||
private final short remainingTenthPulsesInCurrentInsulinProgramElement;
|
||||
private final int delayUntilNextTenthPulseInUsec;
|
||||
|
||||
private ProgramBasalCommand(List<InsulinProgramElement> insulinProgramElements, ProgramReminder programReminder, byte currentInsulinProgramElementIndex, short remainingTenthPulsesInCurrentInsulinProgramElement, int delayUntilNextTenthPulseInUsec) {
|
||||
ProgramBasalCommand(ProgramInsulinCommand interlockCommand, int address, short sequenceNumber, boolean multiCommandFlag, List<LongInsulinProgramElement> insulinProgramElements, ProgramReminder programReminder, byte currentInsulinProgramElementIndex, short remainingTenthPulsesInCurrentInsulinProgramElement, int delayUntilNextTenthPulseInUsec) {
|
||||
super(CommandType.PROGRAM_BASAL, address, sequenceNumber, multiCommandFlag);
|
||||
|
||||
this.interlockCommand = interlockCommand;
|
||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||
this.programReminder = programReminder;
|
||||
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
|
||||
|
@ -42,55 +51,46 @@ public final class ProgramBasalCommand implements Command {
|
|||
.put(currentInsulinProgramElementIndex) //
|
||||
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
|
||||
.putInt(delayUntilNextTenthPulseInUsec);
|
||||
for (InsulinProgramElement insulinProgramElement : insulinProgramElements) {
|
||||
for (LongInsulinProgramElement insulinProgramElement : insulinProgramElements) {
|
||||
buffer.put(insulinProgramElement.getEncoded());
|
||||
}
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
@Override public CommandType getCommandType() {
|
||||
return CommandType.PROGRAM_BASAL;
|
||||
byte[] bolusCommand = buffer.array();
|
||||
byte[] interlockCommand = this.interlockCommand.getEncoded();
|
||||
byte[] header = encodeHeader(address, sequenceNumber, (short) (bolusCommand.length + interlockCommand.length), multiCommandFlag);
|
||||
|
||||
return ByteBuffer.allocate(bolusCommand.length + interlockCommand.length + header.length) //
|
||||
.put(header) //
|
||||
.put(interlockCommand) //
|
||||
.put(bolusCommand) //
|
||||
.array();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "ProgramBasalCommand{" +
|
||||
"uniqueInsulinProgramElements=" + insulinProgramElements +
|
||||
"interlockCommand=" + interlockCommand +
|
||||
", insulinProgramElements=" + insulinProgramElements +
|
||||
", programReminder=" + programReminder +
|
||||
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
|
||||
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
|
||||
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
|
||||
", commandType=" + commandType +
|
||||
", address=" + address +
|
||||
", sequenceNumber=" + sequenceNumber +
|
||||
", multiCommandFlag=" + multiCommandFlag +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class InsulinProgramElement implements Encodable {
|
||||
private final short totalTenthPulses;
|
||||
private final int delayBetweenTenthPulses;
|
||||
|
||||
public InsulinProgramElement(byte totalTenthPulses, short delayBetweenTenthPulses) {
|
||||
this.totalTenthPulses = totalTenthPulses;
|
||||
this.delayBetweenTenthPulses = delayBetweenTenthPulses;
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
return ByteBuffer.allocate(6) //
|
||||
.putShort(totalTenthPulses) //
|
||||
.putInt(delayBetweenTenthPulses) //
|
||||
.array();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Builder implements CommandBuilder<ProgramBasalCommand> {
|
||||
private List<InsulinProgramElement> insulinProgramElements;
|
||||
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBasalCommand> {
|
||||
private BasalProgram basalProgram;
|
||||
private ProgramReminder programReminder;
|
||||
private Byte currentInsulinProgramElementIndex;
|
||||
private Short remainingTenthPulsesInCurrentInsulinProgramElement;
|
||||
private Integer delayUntilNextTenthPulseInUsec;
|
||||
private Date currentTime;
|
||||
|
||||
public Builder setInsulinProgramElements(List<InsulinProgramElement> insulinProgramElements) {
|
||||
if (insulinProgramElements == null) {
|
||||
throw new IllegalArgumentException("insulinProgramElements can not be null");
|
||||
public Builder setBasalProgram(BasalProgram basalProgram) {
|
||||
if (basalProgram == null) {
|
||||
throw new IllegalArgumentException("basalProgram can not be null");
|
||||
}
|
||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||
this.basalProgram = basalProgram;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -99,38 +99,36 @@ public final class ProgramBasalCommand implements Command {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setCurrentInsulinProgramElementIndex(Byte currentInsulinProgramElementIndex) {
|
||||
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
|
||||
public Builder setCurrentTime(Date currentTime) {
|
||||
this.currentTime = currentTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRemainingTenthPulsesInCurrentInsulinProgramElement(Short remainingTenthPulsesInCurrentInsulinProgramElement) {
|
||||
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDelayUntilNextTenthPulseInUsec(Integer delayUntilNextTenthPulseInUsec) {
|
||||
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public ProgramBasalCommand build() {
|
||||
if (insulinProgramElements == null) {
|
||||
throw new IllegalArgumentException("insulinProgramElements can not be null");
|
||||
@Override protected ProgramBasalCommand buildCommand() {
|
||||
if (basalProgram == null) {
|
||||
throw new IllegalArgumentException("basalProgram can not be null");
|
||||
}
|
||||
if (programReminder == null) {
|
||||
throw new IllegalArgumentException("programReminder can not be null");
|
||||
}
|
||||
if (currentInsulinProgramElementIndex == null) {
|
||||
throw new IllegalArgumentException("currentInsulinProgramElementIndex can not be null");
|
||||
if (currentTime == null) {
|
||||
throw new IllegalArgumentException("currentTime can not be null");
|
||||
}
|
||||
if (remainingTenthPulsesInCurrentInsulinProgramElement == null) {
|
||||
throw new IllegalArgumentException("remainingTenthPulsesInCurrentInsulinProgramElement can not be null");
|
||||
}
|
||||
if (delayUntilNextTenthPulseInUsec == null) {
|
||||
throw new IllegalArgumentException("delayUntilNextTenthPulseInUsec can not be null");
|
||||
}
|
||||
return new ProgramBasalCommand(insulinProgramElements, programReminder, currentInsulinProgramElementIndex, remainingTenthPulsesInCurrentInsulinProgramElement, delayUntilNextTenthPulseInUsec);
|
||||
|
||||
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
|
||||
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
|
||||
List<LongInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToLongInsulinProgramElements(pulsesPerSlot);
|
||||
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
|
||||
short checksum = ProgramBasalUtil.createChecksum();
|
||||
byte currentInsulinProgramElementIndex = 0; // TODO
|
||||
short remainingTenthPulsesInCurrentInsulinProgramElement = 0; // TODO
|
||||
int delayUntilNextPulseInUsec = 0; // TODO
|
||||
|
||||
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(address, sequenceNumber, multiCommandFlag, nonce,
|
||||
shortInsulinProgramElements, currentSlot.getIndex(), checksum, (short) (currentSlot.getSecondsRemaining() * 8),
|
||||
currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL);
|
||||
|
||||
return new ProgramBasalCommand(interlockCommand, address, sequenceNumber, multiCommandFlag, longInsulinProgramElements, programReminder, currentInsulinProgramElementIndex, remainingTenthPulsesInCurrentInsulinProgramElement, delayUntilNextPulseInUsec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,153 +2,58 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
|
||||
|
||||
// Always followed by one of: 0x13, 0x16, 0x17
|
||||
public final class ProgramInsulinCommand extends NonceEnabledCommand {
|
||||
private final List<InsulinProgramElement> insulinProgramElements;
|
||||
private final byte currentHalfHourEntryIndex;
|
||||
final class ProgramInsulinCommand extends NonceEnabledCommand {
|
||||
private final List<ShortInsulinProgramElement> insulinProgramElements;
|
||||
private final byte currentSlot;
|
||||
private final short checksum;
|
||||
private final short remainingEighthSecondsInCurrentHalfHourEntry;
|
||||
private final short remainingPulsesInCurrentHalfHourEntry;
|
||||
private final short remainingEighthSecondsInCurrentSlot;
|
||||
private final short remainingPulsesInCurrentSlot;
|
||||
private final DeliveryType deliveryType;
|
||||
private final Command interlockCommand;
|
||||
|
||||
private static final List<CommandType> ALLOWED_INTERLOCK_COMMANDS = Arrays.asList(
|
||||
CommandType.PROGRAM_BASAL,
|
||||
CommandType.PROGRAM_TEMP_BASAL,
|
||||
CommandType.PROGRAM_BOLUS
|
||||
);
|
||||
|
||||
private ProgramInsulinCommand(int address, short sequenceNumber, boolean multiCommandFlag, int nonce, List<InsulinProgramElement> insulinProgramElements, byte currentHalfHourEntryIndex, short checksum, short remainingEighthSecondsInCurrentHalfHourEntry, short remainingPulsesInCurrentHalfHourEntry, DeliveryType deliveryType, Command interlockCommand) {
|
||||
ProgramInsulinCommand(int address, short sequenceNumber, boolean multiCommandFlag, int nonce, List<ShortInsulinProgramElement> insulinProgramElements, byte currentSlot, short checksum, short remainingEighthSecondsInCurrentSlot, short remainingPulsesInCurrentSlot, DeliveryType deliveryType) {
|
||||
super(CommandType.PROGRAM_INSULIN, address, sequenceNumber, multiCommandFlag, nonce);
|
||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||
this.currentHalfHourEntryIndex = currentHalfHourEntryIndex;
|
||||
this.currentSlot = currentSlot;
|
||||
this.checksum = checksum;
|
||||
this.remainingEighthSecondsInCurrentHalfHourEntry = remainingEighthSecondsInCurrentHalfHourEntry;
|
||||
this.remainingPulsesInCurrentHalfHourEntry = remainingPulsesInCurrentHalfHourEntry;
|
||||
this.remainingEighthSecondsInCurrentSlot = remainingEighthSecondsInCurrentSlot;
|
||||
this.remainingPulsesInCurrentSlot = remainingPulsesInCurrentSlot;
|
||||
this.deliveryType = deliveryType;
|
||||
this.interlockCommand = interlockCommand;
|
||||
}
|
||||
|
||||
public short getLength() {
|
||||
short getLength() {
|
||||
return (short) (insulinProgramElements.size() * 6 + 10);
|
||||
}
|
||||
|
||||
public byte getBodyLength() {
|
||||
byte getBodyLength() {
|
||||
return (byte) (insulinProgramElements.size() * 6 + 8);
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
ByteBuffer commandBuffer = ByteBuffer.allocate(this.getLength()) //
|
||||
ByteBuffer buffer = ByteBuffer.allocate(this.getLength()) //
|
||||
.put(commandType.getValue()) //
|
||||
.put(getBodyLength()) //
|
||||
.putInt(nonce) //
|
||||
.put(deliveryType.getValue()) //
|
||||
.putShort(checksum) //
|
||||
.put(currentHalfHourEntryIndex) //
|
||||
.putShort(remainingEighthSecondsInCurrentHalfHourEntry) //
|
||||
.putShort(remainingPulsesInCurrentHalfHourEntry);
|
||||
.put(currentSlot) //
|
||||
.putShort(remainingEighthSecondsInCurrentSlot) //
|
||||
.putShort(remainingPulsesInCurrentSlot);
|
||||
|
||||
for (InsulinProgramElement element : insulinProgramElements) {
|
||||
commandBuffer.put(element.getEncoded());
|
||||
for (ShortInsulinProgramElement element : insulinProgramElements) {
|
||||
buffer.put(element.getEncoded());
|
||||
}
|
||||
|
||||
byte[] command = commandBuffer.array();
|
||||
byte[] interlock = interlockCommand.getEncoded();
|
||||
short totalLength = (short) (command.length + interlock.length + HEADER_LENGTH);
|
||||
|
||||
return ByteBuffer.allocate(totalLength) //
|
||||
.put(encodeHeader(address, sequenceNumber, totalLength, multiCommandFlag)) //
|
||||
.put(command) //
|
||||
.put(interlock) //
|
||||
.array();
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramInsulinCommand> {
|
||||
private List<InsulinProgramElement> insulinProgramElements;
|
||||
private Byte currentHalfOurEntryIndex;
|
||||
private Short checksum;
|
||||
private Short remainingEighthSecondsInCurrentHalfHourEntry;
|
||||
private Short remainingPulsesInCurrentHalfHourEntry;
|
||||
private DeliveryType deliveryType;
|
||||
private Command interlockCommand;
|
||||
|
||||
public Builder setInsulinProgramElements(List<InsulinProgramElement> insulinProgramElements) {
|
||||
if (insulinProgramElements == null) {
|
||||
throw new IllegalArgumentException("insulinProgramElements can not be null");
|
||||
}
|
||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCurrentHalfOurEntryIndex(byte currentHalfOurEntryIndex) {
|
||||
this.currentHalfOurEntryIndex = currentHalfOurEntryIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setChecksum(short checksum) {
|
||||
this.checksum = checksum;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRemainingEighthSecondsInCurrentHalfHourEntryIndex(short remainingEighthSecondsInCurrentHalfHourEntry) {
|
||||
this.remainingEighthSecondsInCurrentHalfHourEntry = remainingEighthSecondsInCurrentHalfHourEntry;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRemainingPulsesInCurrentHalfHourEntry(short remainingPulsesInCurrentHalfHourEntry) {
|
||||
this.remainingPulsesInCurrentHalfHourEntry = remainingPulsesInCurrentHalfHourEntry;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDeliveryType(DeliveryType deliveryType) {
|
||||
this.deliveryType = deliveryType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setInterlockCommand(Command interlockCommand) {
|
||||
if (!ALLOWED_INTERLOCK_COMMANDS.contains(interlockCommand.getCommandType())) {
|
||||
throw new IllegalArgumentException("Illegal interlock command type");
|
||||
}
|
||||
this.interlockCommand = interlockCommand;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected final ProgramInsulinCommand buildCommand() {
|
||||
if (insulinProgramElements == null) {
|
||||
throw new IllegalArgumentException("insulinProgramElements can not be null");
|
||||
}
|
||||
if (currentHalfOurEntryIndex == null) {
|
||||
throw new IllegalArgumentException("currentHalfOurEntryIndex can not be null");
|
||||
}
|
||||
if (checksum == null) {
|
||||
throw new IllegalArgumentException("checksum can not be null");
|
||||
}
|
||||
if (remainingEighthSecondsInCurrentHalfHourEntry == null) {
|
||||
throw new IllegalArgumentException("remainingEighthSecondsInCurrentHalfHourEntry can not be null");
|
||||
}
|
||||
if (remainingPulsesInCurrentHalfHourEntry == null) {
|
||||
throw new IllegalArgumentException("remainingPulsesInCurrentHalfHourEntry can not be null");
|
||||
}
|
||||
if (deliveryType == null) {
|
||||
throw new IllegalArgumentException("deliveryType can not be null");
|
||||
}
|
||||
if (interlockCommand == null) {
|
||||
throw new IllegalArgumentException("interlockCommand can not be null");
|
||||
}
|
||||
return new ProgramInsulinCommand(address, sequenceNumber, multiCommandFlag, nonce, insulinProgramElements, currentHalfOurEntryIndex, checksum, remainingEighthSecondsInCurrentHalfHourEntry, remainingPulsesInCurrentHalfHourEntry, deliveryType, interlockCommand);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeliveryType {
|
||||
enum DeliveryType {
|
||||
BASAL((byte) 0x00),
|
||||
TEMP_BASAL((byte) 0x01),
|
||||
BOLUS((byte) 0x02);
|
||||
|
@ -167,12 +72,11 @@ public final class ProgramInsulinCommand extends NonceEnabledCommand {
|
|||
@Override public String toString() {
|
||||
return "ProgramInsulinCommand{" +
|
||||
"insulinProgramElements=" + insulinProgramElements +
|
||||
", currentHalfHourEntryIndex=" + currentHalfHourEntryIndex +
|
||||
", currentSlot=" + currentSlot +
|
||||
", checksum=" + checksum +
|
||||
", remainingEighthSecondsInCurrentHalfHourEntry=" + remainingEighthSecondsInCurrentHalfHourEntry +
|
||||
", remainingPulsesInCurrentHalfHourEntry=" + remainingPulsesInCurrentHalfHourEntry +
|
||||
", remainingEighthSecondsInCurrentSlot=" + remainingEighthSecondsInCurrentSlot +
|
||||
", remainingPulsesInCurrentSlot=" + remainingPulsesInCurrentSlot +
|
||||
", deliveryType=" + deliveryType +
|
||||
", interlockCommand=" + interlockCommand +
|
||||
", nonce=" + nonce +
|
||||
", commandType=" + commandType +
|
||||
", address=" + address +
|
||||
|
@ -181,26 +85,4 @@ public final class ProgramInsulinCommand extends NonceEnabledCommand {
|
|||
'}';
|
||||
}
|
||||
|
||||
public static class InsulinProgramElement implements Encodable {
|
||||
private final byte numberOfHalfOurEntries; // 4 bits
|
||||
private final short numberOfPulsesPerHalfOurEntry; // 10 bits
|
||||
private final boolean extraAlternatePulse;
|
||||
|
||||
public InsulinProgramElement(byte numberOfHalfOurEntries, short numberOfPulsesPerHalfOurEntry, boolean extraAlternatePulse) {
|
||||
this.numberOfHalfOurEntries = numberOfHalfOurEntries;
|
||||
this.numberOfPulsesPerHalfOurEntry = numberOfPulsesPerHalfOurEntry;
|
||||
this.extraAlternatePulse = extraAlternatePulse;
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
byte firstByte = (byte) ((((numberOfHalfOurEntries - 1) & 0x0f) << 4) //
|
||||
| ((extraAlternatePulse ? 1 : 0) << 3) //
|
||||
| ((numberOfPulsesPerHalfOurEntry >>> 8) & 0x03));
|
||||
|
||||
return ByteBuffer.allocate(2) //
|
||||
.put(firstByte) //
|
||||
.put((byte) (numberOfPulsesPerHalfOurEntry & 0xff)) //
|
||||
.array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public final class SetUniqueIdCommand extends HeaderEnabledCommand {
|
|||
private final int podSequenceNumber;
|
||||
private final Date initializationTime;
|
||||
|
||||
private SetUniqueIdCommand(int address, short sequenceNumber, boolean multiCommandFlag, int lotNumber, int podSequenceNumber, Date initializationTime) {
|
||||
SetUniqueIdCommand(int address, short sequenceNumber, boolean multiCommandFlag, int lotNumber, int podSequenceNumber, Date initializationTime) {
|
||||
super(CommandType.SET_UNIQUE_ID, address, sequenceNumber, multiCommandFlag);
|
||||
this.lotNumber = lotNumber;
|
||||
this.podSequenceNumber = podSequenceNumber;
|
||||
|
|
|
@ -14,7 +14,7 @@ public final class SilenceAlertsCommand extends NonceEnabledCommand {
|
|||
|
||||
private final SilenceAlertCommandParameters parameters;
|
||||
|
||||
private SilenceAlertsCommand(int address, short sequenceNumber, boolean multiCommandFlag, SilenceAlertCommandParameters parameters, int nonce) {
|
||||
SilenceAlertsCommand(int address, short sequenceNumber, boolean multiCommandFlag, SilenceAlertCommandParameters parameters, int nonce) {
|
||||
super(CommandType.SILENCE_ALERTS, address, sequenceNumber, multiCommandFlag, nonce);
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public final class StopDeliveryCommand extends NonceEnabledCommand {
|
|||
private final DeliveryType deliveryType;
|
||||
private final BeepType beepType;
|
||||
|
||||
private StopDeliveryCommand(int address, short sequenceNumber, boolean multiCommandFlag, DeliveryType deliveryType, BeepType beepType, int nonce) {
|
||||
StopDeliveryCommand(int address, short sequenceNumber, boolean multiCommandFlag, DeliveryType deliveryType, BeepType beepType, int nonce) {
|
||||
super(CommandType.STOP_DELIVERY, address, sequenceNumber, multiCommandFlag, nonce);
|
||||
this.deliveryType = deliveryType;
|
||||
this.beepType = beepType;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
public class CurrentSlot {
|
||||
private byte index;
|
||||
private short secondsRemaining;
|
||||
private short pulsesRemaining;
|
||||
|
||||
public CurrentSlot(byte index, short secondsRemaining, short pulsesRemaining) {
|
||||
this.index = index;
|
||||
this.secondsRemaining = secondsRemaining;
|
||||
this.pulsesRemaining = pulsesRemaining;
|
||||
}
|
||||
|
||||
public byte getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public short getSecondsRemaining() {
|
||||
return secondsRemaining;
|
||||
}
|
||||
|
||||
public short getPulsesRemaining() {
|
||||
return pulsesRemaining;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "CurrentSlot{" +
|
||||
"index=" + index +
|
||||
", secondsRemaining=" + secondsRemaining +
|
||||
", pulsesRemaining=" + pulsesRemaining +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
|
||||
public class LongInsulinProgramElement implements Encodable {
|
||||
private final short totalTenthPulses;
|
||||
private final int delayBetweenTenthPulses;
|
||||
|
||||
public LongInsulinProgramElement(byte totalTenthPulses, short delayBetweenTenthPulses) {
|
||||
this.totalTenthPulses = totalTenthPulses;
|
||||
this.delayBetweenTenthPulses = delayBetweenTenthPulses;
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
return ByteBuffer.allocate(6) //
|
||||
.putShort(totalTenthPulses) //
|
||||
.putInt(delayBetweenTenthPulses) //
|
||||
.array();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "LongInsulinProgramElement{" +
|
||||
"totalTenthPulses=" + totalTenthPulses +
|
||||
", delayBetweenTenthPulses=" + delayBetweenTenthPulses +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram;
|
||||
|
||||
public final class ProgramBasalUtil {
|
||||
private static final byte NUMBER_OF_BASAL_SLOTS = 48;
|
||||
private static final byte MAX_NUMBER_OF_SLOTS_IN_SHORT_INSULIN_PROGRAM_ELEMENT = 16;
|
||||
|
||||
private ProgramBasalUtil() {
|
||||
}
|
||||
|
||||
public static List<LongInsulinProgramElement> mapPulsesPerSlotToLongInsulinProgramElements(short[] pulsesPerSlot) {
|
||||
if (pulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
|
||||
throw new IllegalArgumentException("Basal program must contain 48 slots");
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static List<ShortInsulinProgramElement> mapPulsesPerSlotToShortInsulinProgramElements(short[] pulsesPerSlot) {
|
||||
if (pulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
|
||||
throw new IllegalArgumentException("Basal program must contain 48 slots");
|
||||
}
|
||||
|
||||
List<ShortInsulinProgramElement> elements = new ArrayList<>();
|
||||
boolean extraAlternatePulse = false;
|
||||
short previousPulsesPerSlot = 0;
|
||||
byte numberOfSlotsInCurrentElement = 0;
|
||||
byte currentTotalNumberOfSlots = 0;
|
||||
|
||||
while (currentTotalNumberOfSlots < NUMBER_OF_BASAL_SLOTS) {
|
||||
if (currentTotalNumberOfSlots == 0) {
|
||||
// First slot
|
||||
|
||||
previousPulsesPerSlot = pulsesPerSlot[0];
|
||||
currentTotalNumberOfSlots++;
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
} else if (pulsesPerSlot[currentTotalNumberOfSlots] == previousPulsesPerSlot) {
|
||||
// Subsequent slot in element (same pulses per slot as previous slot)
|
||||
|
||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_SHORT_INSULIN_PROGRAM_ELEMENT) {
|
||||
numberOfSlotsInCurrentElement++;
|
||||
} else {
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, false));
|
||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
extraAlternatePulse = false;
|
||||
}
|
||||
|
||||
currentTotalNumberOfSlots++;
|
||||
} else if (numberOfSlotsInCurrentElement == 1 && pulsesPerSlot[currentTotalNumberOfSlots] == previousPulsesPerSlot + 1) {
|
||||
// Second slot of segment with extra alternate pulse
|
||||
|
||||
boolean expectAlternatePulseForNextSegment = false;
|
||||
currentTotalNumberOfSlots++;
|
||||
extraAlternatePulse = true;
|
||||
while (currentTotalNumberOfSlots < NUMBER_OF_BASAL_SLOTS) {
|
||||
// Loop rest alternate pulse segment
|
||||
|
||||
if (pulsesPerSlot[currentTotalNumberOfSlots] == previousPulsesPerSlot + (expectAlternatePulseForNextSegment ? 1 : 0)) {
|
||||
// Still in alternate pulse segment
|
||||
|
||||
currentTotalNumberOfSlots++;
|
||||
expectAlternatePulseForNextSegment = !expectAlternatePulseForNextSegment;
|
||||
|
||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_SHORT_INSULIN_PROGRAM_ELEMENT) {
|
||||
numberOfSlotsInCurrentElement++;
|
||||
} else {
|
||||
// End of alternate pulse segment (no slots left in element)
|
||||
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, true));
|
||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
extraAlternatePulse = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// End of alternate pulse segment (new number of pulses per slot)
|
||||
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, true));
|
||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
extraAlternatePulse = false;
|
||||
currentTotalNumberOfSlots++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (previousPulsesPerSlot != pulsesPerSlot[currentTotalNumberOfSlots]) {
|
||||
// End of segment (new number of pulses per slot)
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, false));
|
||||
|
||||
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
|
||||
currentTotalNumberOfSlots++;
|
||||
extraAlternatePulse = false;
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
} else {
|
||||
throw new IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements");
|
||||
}
|
||||
}
|
||||
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
public static short[] mapBasalProgramToPulsesPerSlot(BasalProgram basalProgram) {
|
||||
short[] pulsesPerSlot = new short[NUMBER_OF_BASAL_SLOTS];
|
||||
for (BasalProgram.Segment segment : basalProgram.getSegments()) {
|
||||
boolean remainingPulse = false;
|
||||
for (int i = segment.getStartSlotIndex(); i < segment.getEndSlotIndex(); i++) {
|
||||
pulsesPerSlot[i] = (short) (segment.getPulsesPerHour() / 2);
|
||||
if (segment.getPulsesPerHour() % 2 == 1) { // Do extra alternate pulse
|
||||
if (remainingPulse) {
|
||||
pulsesPerSlot[i] += 1;
|
||||
}
|
||||
remainingPulse = !remainingPulse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pulsesPerSlot;
|
||||
}
|
||||
|
||||
public static CurrentSlot calculateCurrentSlot(short[] pulsesPerSlot, Date currentTime) {
|
||||
Calendar instance = Calendar.getInstance();
|
||||
instance.setTime(currentTime);
|
||||
|
||||
int hourOfDay = instance.get(Calendar.HOUR_OF_DAY);
|
||||
int minuteOfHour = instance.get(Calendar.MINUTE);
|
||||
int secondOfMinute = instance.get(Calendar.SECOND);
|
||||
|
||||
byte index = (byte) ((hourOfDay * 60 + minuteOfHour) / 30);
|
||||
short secondsRemaining = (short) ((index + 1) * 1800 - (secondOfMinute + hourOfDay * 3600 + minuteOfHour * 60));
|
||||
short pulsesRemaining = (short) ((double) pulsesPerSlot[index] * secondsRemaining / 1800);
|
||||
|
||||
return new CurrentSlot(index, secondsRemaining, pulsesRemaining);
|
||||
}
|
||||
|
||||
public static short createChecksum() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
|
||||
public class ShortInsulinProgramElement implements Encodable {
|
||||
private final byte numberOfSlotsMinusOne; // 4 bits
|
||||
private final short pulsesPerSlot; // 10 bits
|
||||
private final boolean extraAlternatePulse;
|
||||
|
||||
public ShortInsulinProgramElement(byte numberOfSlotsMinusOne, short pulsesPerSlot, boolean extraAlternatePulse) {
|
||||
this.numberOfSlotsMinusOne = numberOfSlotsMinusOne;
|
||||
this.pulsesPerSlot = pulsesPerSlot;
|
||||
this.extraAlternatePulse = extraAlternatePulse;
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
byte firstByte = (byte) ((((numberOfSlotsMinusOne - 1) & 0x0f) << 4) //
|
||||
| ((extraAlternatePulse ? 1 : 0) << 3) //
|
||||
| ((pulsesPerSlot >>> 8) & 0x03));
|
||||
|
||||
return ByteBuffer.allocate(2) //
|
||||
.put(firstByte) //
|
||||
.put((byte) (pulsesPerSlot & 0xff)) //
|
||||
.array();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "ShortInsulinProgramElement{" +
|
||||
"numberOfSlotsMinusOne=" + numberOfSlotsMinusOne +
|
||||
", pulsesPerSlot=" + pulsesPerSlot +
|
||||
", extraAlternatePulse=" + extraAlternatePulse +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BasalProgram {
|
||||
private final List<Segment> segments;
|
||||
|
||||
public BasalProgram(List<Segment> segments) {
|
||||
if (segments == null) {
|
||||
throw new IllegalArgumentException("segments can not be null");
|
||||
}
|
||||
|
||||
// TODO validate segments
|
||||
|
||||
this.segments = new ArrayList<>(segments);
|
||||
}
|
||||
|
||||
public void addSegment(Segment segment) {
|
||||
segments.add(segment);
|
||||
}
|
||||
|
||||
public List<Segment> getSegments() {
|
||||
return Collections.unmodifiableList(segments);
|
||||
}
|
||||
|
||||
public boolean isZeroBasal() {
|
||||
int total = 0;
|
||||
for (Segment segment : segments) {
|
||||
total += segment.getBasalRateInHundredthUnitsPerHour();
|
||||
}
|
||||
return total == 0;
|
||||
}
|
||||
|
||||
public boolean hasZeroUnitSegments() {
|
||||
for (Segment segment : segments) {
|
||||
if (segment.getBasalRateInHundredthUnitsPerHour() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class Segment {
|
||||
private static final byte PULSES_PER_UNIT = 20;
|
||||
|
||||
private final short startSlotIndex;
|
||||
private final short endSlotIndex;
|
||||
private final int basalRateInHundredthUnitsPerHour;
|
||||
|
||||
public Segment(short startSlotIndex, short endSlotIndex, int basalRateInHundredthUnitsPerHour) {
|
||||
this.startSlotIndex = startSlotIndex;
|
||||
this.endSlotIndex = endSlotIndex;
|
||||
this.basalRateInHundredthUnitsPerHour = basalRateInHundredthUnitsPerHour;
|
||||
}
|
||||
|
||||
public short getStartSlotIndex() {
|
||||
return startSlotIndex;
|
||||
}
|
||||
|
||||
public short getEndSlotIndex() {
|
||||
return endSlotIndex;
|
||||
}
|
||||
|
||||
public int getBasalRateInHundredthUnitsPerHour() {
|
||||
return basalRateInHundredthUnitsPerHour;
|
||||
}
|
||||
|
||||
public short getPulsesPerHour() {
|
||||
return (short) (basalRateInHundredthUnitsPerHour * PULSES_PER_UNIT / 100);
|
||||
}
|
||||
|
||||
public short getNumberOfSlots() {
|
||||
return (short) (endSlotIndex - startSlotIndex);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "Segment{" +
|
||||
"startSlotIndex=" + startSlotIndex +
|
||||
", endSlotIndex=" + endSlotIndex +
|
||||
", basalRateInHundredthUnitsPerHour=" + basalRateInHundredthUnitsPerHour +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "BasalProgram{" +
|
||||
"segments=" + segments +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -16,8 +16,8 @@ public class NakResponseTest {
|
|||
@Test
|
||||
public void testValidResponse() throws DecoderException {
|
||||
byte[] encoded = Hex.decodeHex("0603070009");
|
||||
|
||||
NakResponse response = new NakResponse(encoded);
|
||||
|
||||
assertArrayEquals(encoded, response.getEncoded());
|
||||
assertNotSame(encoded, response.getEncoded());
|
||||
assertEquals(ResponseType.NAK_RESPONSE, response.getResponseType());
|
||||
|
|
Loading…
Reference in a new issue