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 short LENGTH = 6;
|
||||||
private static final byte BODY_LENGTH = 4;
|
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);
|
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 short LENGTH = 6;
|
||||||
private static final byte BODY_LENGTH = 4;
|
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);
|
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 {
|
public final class ProgramAlertsCommand extends NonceEnabledCommand {
|
||||||
private final List<AlertConfiguration> alertConfigurations;
|
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);
|
super(CommandType.PROGRAM_ALERTS, address, sequenceNumber, multiCommandFlag, nonce);
|
||||||
this.alertConfigurations = new ArrayList<>(alertConfigurations);
|
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.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
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.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.command.base.HeaderEnabledCommand;
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
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;
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
|
||||||
|
|
||||||
// Always preceded by 0x1a ProgramInsulinCommand
|
// Always preceded by 0x1a ProgramInsulinCommand
|
||||||
public final class ProgramBasalCommand implements Command {
|
public final class ProgramBasalCommand extends HeaderEnabledCommand {
|
||||||
private final List<InsulinProgramElement> insulinProgramElements;
|
private final ProgramInsulinCommand interlockCommand;
|
||||||
|
private final List<LongInsulinProgramElement> insulinProgramElements;
|
||||||
private final ProgramReminder programReminder;
|
private final ProgramReminder programReminder;
|
||||||
private final byte currentInsulinProgramElementIndex;
|
private final byte currentInsulinProgramElementIndex;
|
||||||
private final short remainingTenthPulsesInCurrentInsulinProgramElement;
|
private final short remainingTenthPulsesInCurrentInsulinProgramElement;
|
||||||
private final int delayUntilNextTenthPulseInUsec;
|
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.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||||
this.programReminder = programReminder;
|
this.programReminder = programReminder;
|
||||||
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
|
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
|
||||||
|
@ -42,55 +51,46 @@ public final class ProgramBasalCommand implements Command {
|
||||||
.put(currentInsulinProgramElementIndex) //
|
.put(currentInsulinProgramElementIndex) //
|
||||||
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
|
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
|
||||||
.putInt(delayUntilNextTenthPulseInUsec);
|
.putInt(delayUntilNextTenthPulseInUsec);
|
||||||
for (InsulinProgramElement insulinProgramElement : insulinProgramElements) {
|
for (LongInsulinProgramElement insulinProgramElement : insulinProgramElements) {
|
||||||
buffer.put(insulinProgramElement.getEncoded());
|
buffer.put(insulinProgramElement.getEncoded());
|
||||||
}
|
}
|
||||||
return buffer.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public CommandType getCommandType() {
|
byte[] bolusCommand = buffer.array();
|
||||||
return CommandType.PROGRAM_BASAL;
|
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() {
|
@Override public String toString() {
|
||||||
return "ProgramBasalCommand{" +
|
return "ProgramBasalCommand{" +
|
||||||
"uniqueInsulinProgramElements=" + insulinProgramElements +
|
"interlockCommand=" + interlockCommand +
|
||||||
|
", insulinProgramElements=" + insulinProgramElements +
|
||||||
", programReminder=" + programReminder +
|
", programReminder=" + programReminder +
|
||||||
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
|
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
|
||||||
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
|
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
|
||||||
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
|
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
|
||||||
|
", commandType=" + commandType +
|
||||||
|
", address=" + address +
|
||||||
|
", sequenceNumber=" + sequenceNumber +
|
||||||
|
", multiCommandFlag=" + multiCommandFlag +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InsulinProgramElement implements Encodable {
|
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBasalCommand> {
|
||||||
private final short totalTenthPulses;
|
private BasalProgram basalProgram;
|
||||||
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;
|
|
||||||
private ProgramReminder programReminder;
|
private ProgramReminder programReminder;
|
||||||
private Byte currentInsulinProgramElementIndex;
|
private Date currentTime;
|
||||||
private Short remainingTenthPulsesInCurrentInsulinProgramElement;
|
|
||||||
private Integer delayUntilNextTenthPulseInUsec;
|
|
||||||
|
|
||||||
public Builder setInsulinProgramElements(List<InsulinProgramElement> insulinProgramElements) {
|
public Builder setBasalProgram(BasalProgram basalProgram) {
|
||||||
if (insulinProgramElements == null) {
|
if (basalProgram == null) {
|
||||||
throw new IllegalArgumentException("insulinProgramElements can not be null");
|
throw new IllegalArgumentException("basalProgram can not be null");
|
||||||
}
|
}
|
||||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
this.basalProgram = basalProgram;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,38 +99,36 @@ public final class ProgramBasalCommand implements Command {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCurrentInsulinProgramElementIndex(Byte currentInsulinProgramElementIndex) {
|
public Builder setCurrentTime(Date currentTime) {
|
||||||
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
|
this.currentTime = currentTime;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setRemainingTenthPulsesInCurrentInsulinProgramElement(Short remainingTenthPulsesInCurrentInsulinProgramElement) {
|
@Override protected ProgramBasalCommand buildCommand() {
|
||||||
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement;
|
if (basalProgram == null) {
|
||||||
return this;
|
throw new IllegalArgumentException("basalProgram can not be null");
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
if (programReminder == null) {
|
if (programReminder == null) {
|
||||||
throw new IllegalArgumentException("programReminder can not be null");
|
throw new IllegalArgumentException("programReminder can not be null");
|
||||||
}
|
}
|
||||||
if (currentInsulinProgramElementIndex == null) {
|
if (currentTime == null) {
|
||||||
throw new IllegalArgumentException("currentInsulinProgramElementIndex can not be null");
|
throw new IllegalArgumentException("currentTime can not be null");
|
||||||
}
|
}
|
||||||
if (remainingTenthPulsesInCurrentInsulinProgramElement == null) {
|
|
||||||
throw new IllegalArgumentException("remainingTenthPulsesInCurrentInsulinProgramElement can not be null");
|
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
|
||||||
}
|
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
|
||||||
if (delayUntilNextTenthPulseInUsec == null) {
|
List<LongInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToLongInsulinProgramElements(pulsesPerSlot);
|
||||||
throw new IllegalArgumentException("delayUntilNextTenthPulseInUsec can not be null");
|
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
|
||||||
}
|
short checksum = ProgramBasalUtil.createChecksum();
|
||||||
return new ProgramBasalCommand(insulinProgramElements, programReminder, currentInsulinProgramElementIndex, remainingTenthPulsesInCurrentInsulinProgramElement, delayUntilNextTenthPulseInUsec);
|
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.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
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.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.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.command.insulin.program.ShortInsulinProgramElement;
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
|
||||||
|
|
||||||
// Always followed by one of: 0x13, 0x16, 0x17
|
// Always followed by one of: 0x13, 0x16, 0x17
|
||||||
public final class ProgramInsulinCommand extends NonceEnabledCommand {
|
final class ProgramInsulinCommand extends NonceEnabledCommand {
|
||||||
private final List<InsulinProgramElement> insulinProgramElements;
|
private final List<ShortInsulinProgramElement> insulinProgramElements;
|
||||||
private final byte currentHalfHourEntryIndex;
|
private final byte currentSlot;
|
||||||
private final short checksum;
|
private final short checksum;
|
||||||
private final short remainingEighthSecondsInCurrentHalfHourEntry;
|
private final short remainingEighthSecondsInCurrentSlot;
|
||||||
private final short remainingPulsesInCurrentHalfHourEntry;
|
private final short remainingPulsesInCurrentSlot;
|
||||||
private final DeliveryType deliveryType;
|
private final DeliveryType deliveryType;
|
||||||
private final Command interlockCommand;
|
|
||||||
|
|
||||||
private static final List<CommandType> ALLOWED_INTERLOCK_COMMANDS = Arrays.asList(
|
ProgramInsulinCommand(int address, short sequenceNumber, boolean multiCommandFlag, int nonce, List<ShortInsulinProgramElement> insulinProgramElements, byte currentSlot, short checksum, short remainingEighthSecondsInCurrentSlot, short remainingPulsesInCurrentSlot, DeliveryType deliveryType) {
|
||||||
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) {
|
|
||||||
super(CommandType.PROGRAM_INSULIN, address, sequenceNumber, multiCommandFlag, nonce);
|
super(CommandType.PROGRAM_INSULIN, address, sequenceNumber, multiCommandFlag, nonce);
|
||||||
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
|
||||||
this.currentHalfHourEntryIndex = currentHalfHourEntryIndex;
|
this.currentSlot = currentSlot;
|
||||||
this.checksum = checksum;
|
this.checksum = checksum;
|
||||||
this.remainingEighthSecondsInCurrentHalfHourEntry = remainingEighthSecondsInCurrentHalfHourEntry;
|
this.remainingEighthSecondsInCurrentSlot = remainingEighthSecondsInCurrentSlot;
|
||||||
this.remainingPulsesInCurrentHalfHourEntry = remainingPulsesInCurrentHalfHourEntry;
|
this.remainingPulsesInCurrentSlot = remainingPulsesInCurrentSlot;
|
||||||
this.deliveryType = deliveryType;
|
this.deliveryType = deliveryType;
|
||||||
this.interlockCommand = interlockCommand;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getLength() {
|
short getLength() {
|
||||||
return (short) (insulinProgramElements.size() * 6 + 10);
|
return (short) (insulinProgramElements.size() * 6 + 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte getBodyLength() {
|
byte getBodyLength() {
|
||||||
return (byte) (insulinProgramElements.size() * 6 + 8);
|
return (byte) (insulinProgramElements.size() * 6 + 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public byte[] getEncoded() {
|
@Override public byte[] getEncoded() {
|
||||||
ByteBuffer commandBuffer = ByteBuffer.allocate(this.getLength()) //
|
ByteBuffer buffer = ByteBuffer.allocate(this.getLength()) //
|
||||||
.put(commandType.getValue()) //
|
.put(commandType.getValue()) //
|
||||||
.put(getBodyLength()) //
|
.put(getBodyLength()) //
|
||||||
.putInt(nonce) //
|
.putInt(nonce) //
|
||||||
.put(deliveryType.getValue()) //
|
.put(deliveryType.getValue()) //
|
||||||
.putShort(checksum) //
|
.putShort(checksum) //
|
||||||
.put(currentHalfHourEntryIndex) //
|
.put(currentSlot) //
|
||||||
.putShort(remainingEighthSecondsInCurrentHalfHourEntry) //
|
.putShort(remainingEighthSecondsInCurrentSlot) //
|
||||||
.putShort(remainingPulsesInCurrentHalfHourEntry);
|
.putShort(remainingPulsesInCurrentSlot);
|
||||||
|
|
||||||
for (InsulinProgramElement element : insulinProgramElements) {
|
for (ShortInsulinProgramElement element : insulinProgramElements) {
|
||||||
commandBuffer.put(element.getEncoded());
|
buffer.put(element.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] command = commandBuffer.array();
|
return buffer.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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramInsulinCommand> {
|
enum DeliveryType {
|
||||||
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 {
|
|
||||||
BASAL((byte) 0x00),
|
BASAL((byte) 0x00),
|
||||||
TEMP_BASAL((byte) 0x01),
|
TEMP_BASAL((byte) 0x01),
|
||||||
BOLUS((byte) 0x02);
|
BOLUS((byte) 0x02);
|
||||||
|
@ -167,12 +72,11 @@ public final class ProgramInsulinCommand extends NonceEnabledCommand {
|
||||||
@Override public String toString() {
|
@Override public String toString() {
|
||||||
return "ProgramInsulinCommand{" +
|
return "ProgramInsulinCommand{" +
|
||||||
"insulinProgramElements=" + insulinProgramElements +
|
"insulinProgramElements=" + insulinProgramElements +
|
||||||
", currentHalfHourEntryIndex=" + currentHalfHourEntryIndex +
|
", currentSlot=" + currentSlot +
|
||||||
", checksum=" + checksum +
|
", checksum=" + checksum +
|
||||||
", remainingEighthSecondsInCurrentHalfHourEntry=" + remainingEighthSecondsInCurrentHalfHourEntry +
|
", remainingEighthSecondsInCurrentSlot=" + remainingEighthSecondsInCurrentSlot +
|
||||||
", remainingPulsesInCurrentHalfHourEntry=" + remainingPulsesInCurrentHalfHourEntry +
|
", remainingPulsesInCurrentSlot=" + remainingPulsesInCurrentSlot +
|
||||||
", deliveryType=" + deliveryType +
|
", deliveryType=" + deliveryType +
|
||||||
", interlockCommand=" + interlockCommand +
|
|
||||||
", nonce=" + nonce +
|
", nonce=" + nonce +
|
||||||
", commandType=" + commandType +
|
", commandType=" + commandType +
|
||||||
", address=" + address +
|
", 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 int podSequenceNumber;
|
||||||
private final Date initializationTime;
|
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);
|
super(CommandType.SET_UNIQUE_ID, address, sequenceNumber, multiCommandFlag);
|
||||||
this.lotNumber = lotNumber;
|
this.lotNumber = lotNumber;
|
||||||
this.podSequenceNumber = podSequenceNumber;
|
this.podSequenceNumber = podSequenceNumber;
|
||||||
|
|
|
@ -14,7 +14,7 @@ public final class SilenceAlertsCommand extends NonceEnabledCommand {
|
||||||
|
|
||||||
private final SilenceAlertCommandParameters parameters;
|
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);
|
super(CommandType.SILENCE_ALERTS, address, sequenceNumber, multiCommandFlag, nonce);
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ public final class StopDeliveryCommand extends NonceEnabledCommand {
|
||||||
private final DeliveryType deliveryType;
|
private final DeliveryType deliveryType;
|
||||||
private final BeepType beepType;
|
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);
|
super(CommandType.STOP_DELIVERY, address, sequenceNumber, multiCommandFlag, nonce);
|
||||||
this.deliveryType = deliveryType;
|
this.deliveryType = deliveryType;
|
||||||
this.beepType = beepType;
|
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
|
@Test
|
||||||
public void testValidResponse() throws DecoderException {
|
public void testValidResponse() throws DecoderException {
|
||||||
byte[] encoded = Hex.decodeHex("0603070009");
|
byte[] encoded = Hex.decodeHex("0603070009");
|
||||||
|
|
||||||
NakResponse response = new NakResponse(encoded);
|
NakResponse response = new NakResponse(encoded);
|
||||||
|
|
||||||
assertArrayEquals(encoded, response.getEncoded());
|
assertArrayEquals(encoded, response.getEncoded());
|
||||||
assertNotSame(encoded, response.getEncoded());
|
assertNotSame(encoded, response.getEncoded());
|
||||||
assertEquals(ResponseType.NAK_RESPONSE, response.getResponseType());
|
assertEquals(ResponseType.NAK_RESPONSE, response.getResponseType());
|
||||||
|
|
Loading…
Reference in a new issue