WIP on Omnipod program temp basal command

This commit is contained in:
Bart Sopers 2021-02-18 00:31:25 +01:00
parent e961a7990e
commit 5347ae4f2d
11 changed files with 351 additions and 32 deletions

View file

@ -9,10 +9,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.b
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.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentLongInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentBasalInsulinProgramElement;
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.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.command.insulin.program.util.ProgramBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
@ -115,18 +115,18 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
short checksum = ProgramBasalUtil.createChecksum(pulsesPerSlot, currentSlot);
List<BasalInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram));
short checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot);
List<BasalInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram));
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
CurrentLongInsulinProgramElement currentLongInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime);
CurrentBasalInsulinProgramElement currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
shortInsulinProgramElements, checksum, currentSlot.getIndex(), currentSlot.getEighthSecondsRemaining(),
currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL);
return new ProgramBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag,
longInsulinProgramElements, programReminder, currentLongInsulinProgramElement.getIndex(),
currentLongInsulinProgramElement.getRemainingTenthPulses(), currentLongInsulinProgramElement.getDelayUntilNextTenthPulseInUsec());
longInsulinProgramElements, programReminder, currentBasalInsulinProgramElement.getIndex(),
currentBasalInsulinProgramElement.getRemainingTenthPulses(), currentBasalInsulinProgramElement.getDelayUntilNextTenthPulseInUsec());
}
}
}

View file

@ -103,7 +103,7 @@ public final class ProgramBolusCommand extends HeaderEnabledCommand {
short byte10And11 = (short) (numberOfPulses * delayBetweenPulsesInEighthSeconds);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
Collections.singletonList(new BolusShortInsulinProgramElement(numberOfPulses)), createChecksum((byte) 0x01, byte10And11, numberOfPulses),
Collections.singletonList(new BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum((byte) 0x01, byte10And11, numberOfPulses),
(byte) 0x01, byte10And11, (short) numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS);
int delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds / 8 * 100_000;
@ -112,8 +112,8 @@ public final class ProgramBolusCommand extends HeaderEnabledCommand {
}
}
private static short createChecksum(byte numberOfSlots, short byte10And11, short numberOfPulses) {
return MessageUtil.createCheckSum(ByteBuffer.allocate(7) //
private static short calculateChecksum(byte numberOfSlots, short byte10And11, short numberOfPulses) {
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
.put(numberOfSlots) //
.putShort(byte10And11) //
.putShort(numberOfPulses) //

View file

@ -69,7 +69,7 @@ final class ProgramInsulinCommand extends NonceEnabledCommand {
}
}
public short createChecksum(byte[] bytes) {
public short calculateChecksum(byte[] bytes) {
short sum = 0;
for (byte b : bytes) {
sum += (short) (b & 0xff);

View file

@ -0,0 +1,152 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
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.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.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramTempBasalUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
// NOT SUPPORTED: percentage temp basal
public final class ProgramTempBasalCommand extends HeaderEnabledCommand {
private final ProgramInsulinCommand interlockCommand;
private final ProgramReminder programReminder;
private final List<BasalInsulinProgramElement> insulinProgramElements;
private final TempBasalMethod tempBasalMethod;
protected ProgramTempBasalCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag, ProgramReminder programReminder,
List<BasalInsulinProgramElement> insulinProgramElements, TempBasalMethod tempBasalMethod) {
super(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.programReminder = programReminder;
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
this.tempBasalMethod = tempBasalMethod;
}
public TempBasalMethod getTempBasalMethod() {
return tempBasalMethod;
}
public byte getBodyLength() {
byte bodyLength = (byte) (insulinProgramElements.size() * 6 + 8);
if (tempBasalMethod == TempBasalMethod.SECOND_METHOD) {
return bodyLength;
}
// TempBasalMethod.FIRST_METHOD
for (BasalInsulinProgramElement element : insulinProgramElements) {
if (element.getTotalTenthPulses() == 0 && element.getNumberOfSlots() > 1) {
bodyLength = (byte) ((element.getNumberOfSlots() - 1) * 6 + bodyLength);
}
}
return bodyLength;
}
public short getLength() {
return (short) (getBodyLength() + 2);
}
@Override public byte[] getEncoded() {
BasalInsulinProgramElement firstProgramElement = insulinProgramElements.get(0);
short remainingTenthPulsesInFirstElement;
int delayUntilNextTenthPulseInUsec;
if (firstProgramElement.getTotalTenthPulses() == 0) {
if (tempBasalMethod == TempBasalMethod.FIRST_METHOD) {
remainingTenthPulsesInFirstElement = 0;
} else {
remainingTenthPulsesInFirstElement = firstProgramElement.getNumberOfSlots();
}
delayUntilNextTenthPulseInUsec = ProgramBasalUtil.NUMBER_OF_USEC_IN_SLOT;
} else {
remainingTenthPulsesInFirstElement = firstProgramElement.getTotalTenthPulses();
delayUntilNextTenthPulseInUsec = (int) (firstProgramElement.getNumberOfSlots() * 1_800.0d / remainingTenthPulsesInFirstElement * 1_000_000);
}
ByteBuffer buffer = ByteBuffer.allocate(getLength()) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.put(programReminder.getEncoded()) //
.put((byte) 0x00) // Current slot index
.putShort(remainingTenthPulsesInFirstElement) //
.putInt(delayUntilNextTenthPulseInUsec);
for (BasalInsulinProgramElement element : insulinProgramElements) {
buffer.put(element.getEncoded());
}
byte[] tempBasalCommand = buffer.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (tempBasalCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(header.length + interlockCommand.length + tempBasalCommand.length) //
.put(header) //
.put(interlockCommand) //
.put(tempBasalCommand) //
.array());
}
public static class Builder extends NonceEnabledCommandBuilder<Builder, ProgramTempBasalCommand> {
private ProgramReminder programReminder;
private Double rateInUnitsPerHour;
private Short durationInMinutes;
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
public Builder setRateInUnitsPerHour(double rateInUnitsPerHour) {
this.rateInUnitsPerHour = rateInUnitsPerHour;
return this;
}
public Builder setDurationInMinutes(short durationInMinutes) {
if (durationInMinutes % 30 != 0) {
throw new IllegalArgumentException("durationInMinutes must be dividable by 30");
}
this.durationInMinutes = durationInMinutes;
return this;
}
@Override protected ProgramTempBasalCommand buildCommand() {
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
if (rateInUnitsPerHour == null) {
throw new IllegalArgumentException("rateInUnitsPerHour can not be null");
}
if (durationInMinutes == null) {
throw new IllegalArgumentException("durationInMinutes can not be null");
}
byte durationInSlots = (byte) (durationInMinutes % 30);
short[] pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour);
short[] tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(durationInSlots, rateInUnitsPerHour);
TempBasalMethod tempBasalMethod = tenthPulsesPerSlot[0] == 0 ? TempBasalMethod.SECOND_METHOD : TempBasalMethod.FIRST_METHOD;
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
List<BasalInsulinProgramElement> insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot, tempBasalMethod);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce, shortInsulinProgramElements,
ProgramTempBasalUtil.calculateChecksum(durationInSlots, pulsesPerSlot[0], pulsesPerSlot), durationInSlots,
(short) 0x3840, pulsesPerSlot[0], ProgramInsulinCommand.DeliveryType.TEMP_BASAL);
return new ProgramTempBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag, programReminder, insulinProgramElements, tempBasalMethod);
}
}
public enum TempBasalMethod {
FIRST_METHOD,
SECOND_METHOD
}
}

View file

@ -32,6 +32,10 @@ public class BasalInsulinProgramElement implements Encodable {
return numberOfSlots;
}
public short getDurationInSeconds() {
return (short) (numberOfSlots * 1_800);
}
public short getTotalTenthPulses() {
return totalTenthPulses;
}

View file

@ -1,11 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
public class CurrentLongInsulinProgramElement {
public class CurrentBasalInsulinProgramElement {
private final byte index;
private final int delayUntilNextTenthPulseInUsec;
private final short remainingTenthPulses;
public CurrentLongInsulinProgramElement(byte index, int delayUntilNextTenthPulseInUsec, short remainingTenthPulses) {
public CurrentBasalInsulinProgramElement(byte index, int delayUntilNextTenthPulseInUsec, short remainingTenthPulses) {
this.index = index;
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
this.remainingTenthPulses = remainingTenthPulses;

View file

@ -0,0 +1,35 @@
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.command.ProgramTempBasalCommand;
public class TempBasalInsulinProgramElement extends BasalInsulinProgramElement {
private final ProgramTempBasalCommand.TempBasalMethod tempBasalMethod;
public TempBasalInsulinProgramElement(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses, int delayBetweenTenthPulsesInUsec, ProgramTempBasalCommand.TempBasalMethod tempBasalMethod) {
super(startSlotIndex, numberOfSlots, totalTenthPulses, delayBetweenTenthPulsesInUsec);
this.tempBasalMethod = tempBasalMethod;
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(6);
if (getTotalTenthPulses() == 0) {
if (tempBasalMethod == ProgramTempBasalCommand.TempBasalMethod.FIRST_METHOD) {
for (int i = 0; i < getNumberOfSlots(); i++) {
buffer.putShort((short) 0) //
.putInt((int) ((long) getDurationInSeconds() * 1_000_000d / getNumberOfSlots()));
}
} else {
// Zero basal and temp basal second method
buffer.putShort(getNumberOfSlots()) //
.putInt((int) ((long) getDurationInSeconds() * 1_000_000d / getNumberOfSlots()));
}
} else {
buffer.putShort(getTotalTenthPulses()) //
.putInt(getDelayBetweenTenthPulsesInUsec());
}
return buffer.array();
}
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -6,20 +6,33 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BasalShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.CurrentBasalInsulinProgramElement;
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.ShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
public final class ProgramBasalUtil {
private static final byte NUMBER_OF_BASAL_SLOTS = 48;
private static final byte MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT = 16;
private static final int NUMBER_OF_USEC_IN_SLOT = 1_800_000_000;
public static final int NUMBER_OF_USEC_IN_SLOT = 1_800_000_000;
public static final byte NUMBER_OF_BASAL_SLOTS = 48;
public static final byte MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT = 16;
private ProgramBasalUtil() {
}
public static List<BasalInsulinProgramElement> mapPulsesPerSlotToLongInsulinProgramElements(short[] tenthPulsesPerSlot) {
if (tenthPulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
throw new IllegalArgumentException("Basal program must contain 48 slots");
public interface BasalInsulinProgramElementFactory<T extends BasalInsulinProgramElement> {
T create(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses, int delayBetweenTenthPulsesInUsec);
}
public static List<BasalInsulinProgramElement> mapTenthPulsesPerSlotToLongInsulinProgramElements(short[] tenthPulsesPerSlot) {
return mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot, BasalInsulinProgramElement::new);
}
public static <T extends BasalInsulinProgramElement> List<BasalInsulinProgramElement> mapTenthPulsesPerSlotToLongInsulinProgramElements(short[] tenthPulsesPerSlot, BasalInsulinProgramElementFactory<T> insulinProgramElementFactory) {
if (tenthPulsesPerSlot.length > NUMBER_OF_BASAL_SLOTS) {
throw new IllegalArgumentException("Basal program must contain at most 48 slots");
}
List<BasalInsulinProgramElement> elements = new ArrayList<>();
@ -27,12 +40,12 @@ public final class ProgramBasalUtil {
byte numberOfSlotsInCurrentElement = 0;
byte startSlotIndex = 0;
for (int i = 0; i < NUMBER_OF_BASAL_SLOTS; i++) {
for (int i = 0; i < tenthPulsesPerSlot.length; i++) {
if (i == 0) {
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
numberOfSlotsInCurrentElement = 1;
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || (numberOfSlotsInCurrentElement + 1) * previousTenthPulsesPerSlot > 65_534) {
elements.add(new BasalInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
elements.add(insulinProgramElementFactory.create(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
numberOfSlotsInCurrentElement = 1;
@ -41,14 +54,14 @@ public final class ProgramBasalUtil {
numberOfSlotsInCurrentElement++;
}
}
elements.add(new BasalInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
elements.add(insulinProgramElementFactory.create(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
return elements;
}
public static List<ShortInsulinProgramElement> mapPulsesPerSlotToShortInsulinProgramElements(short[] pulsesPerSlot) {
if (pulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
throw new IllegalArgumentException("Basal program must contain 48 slots");
if (pulsesPerSlot.length > NUMBER_OF_BASAL_SLOTS) {
throw new IllegalArgumentException("Basal program must contain at most 48 slots");
}
List<ShortInsulinProgramElement> elements = new ArrayList<>();
@ -57,7 +70,7 @@ public final class ProgramBasalUtil {
byte numberOfSlotsInCurrentElement = 0;
byte currentTotalNumberOfSlots = 0;
while (currentTotalNumberOfSlots < NUMBER_OF_BASAL_SLOTS) {
while (currentTotalNumberOfSlots < pulsesPerSlot.length) {
if (currentTotalNumberOfSlots == 0) {
// First slot
@ -83,7 +96,7 @@ public final class ProgramBasalUtil {
boolean expectAlternatePulseForNextSegment = false;
currentTotalNumberOfSlots++;
extraAlternatePulse = true;
while (currentTotalNumberOfSlots < NUMBER_OF_BASAL_SLOTS) {
while (currentTotalNumberOfSlots < pulsesPerSlot.length) {
// Loop rest alternate pulse segment
if (pulsesPerSlot[currentTotalNumberOfSlots] == previousPulsesPerSlot + (expectAlternatePulseForNextSegment ? 1 : 0)) {
@ -182,7 +195,7 @@ public final class ProgramBasalUtil {
return new CurrentSlot(index, (short) (secondsRemaining * 8), pulsesRemaining);
}
public static CurrentLongInsulinProgramElement calculateCurrentLongInsulinProgramElement(List<BasalInsulinProgramElement> elements, Date currentTime) {
public static CurrentBasalInsulinProgramElement calculateCurrentLongInsulinProgramElement(List<BasalInsulinProgramElement> elements, Date currentTime) {
Calendar instance = Calendar.getInstance();
instance.setTime(currentTime);
@ -218,7 +231,7 @@ public final class ProgramBasalUtil {
}
short remainingTenthPulses = (short) ((remainingTenThousandthPulses % 1_000 != 0 ? 1 : 0) + remainingTenThousandthPulses / 1_000);
return new CurrentLongInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses);
return new CurrentBasalInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses);
}
index++;
@ -227,7 +240,7 @@ public final class ProgramBasalUtil {
throw new IllegalStateException("Could not determine current long insulin program element");
}
public static short createChecksum(short[] pulsesPerSlot, CurrentSlot currentSlot) {
public static short calculateChecksum(short[] pulsesPerSlot, CurrentSlot currentSlot) {
ByteBuffer buffer = ByteBuffer.allocate(1 + 2 + 2 + NUMBER_OF_BASAL_SLOTS * 2) //
.put(currentSlot.getIndex()) //
.putShort(currentSlot.getPulsesRemaining()) //
@ -237,6 +250,6 @@ public final class ProgramBasalUtil {
buffer.putShort(pulses);
}
return MessageUtil.createCheckSum(buffer.array());
return MessageUtil.calculateChecksum(buffer.array());
}
}

View file

@ -0,0 +1,71 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util;
import java.nio.ByteBuffer;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.ProgramTempBasalCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.BasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.TempBasalInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
public final class ProgramTempBasalUtil {
private ProgramTempBasalUtil() {
}
public static List<BasalInsulinProgramElement> mapTenthPulsesPerSlotToLongInsulinProgramElements(short[] tenthPulsesPerSlot, ProgramTempBasalCommand.TempBasalMethod tempBasalMethod) {
return ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot,
(startSlotIndex, numberOfSlots, totalTenthPulses, delayBetweenTenthPulsesInUsec) ->
new TempBasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses, delayBetweenTenthPulsesInUsec, tempBasalMethod));
}
public static short[] mapTempBasalToTenthPulsesPerSlot(int durationInSlots, double rateInUnitsPerHour) {
short pulsesPerHour = (short) Math.round(rateInUnitsPerHour * 20);
short[] tenthPulsesPerSlot = new short[durationInSlots];
for (int i = 0; durationInSlots > i; i++) {
tenthPulsesPerSlot[i] = (short) (roundToHalf(pulsesPerHour / 2.0d) * 10);
}
return tenthPulsesPerSlot;
}
private static double roundToHalf(double d) {
return (double) (short) ((short) (int) (d * 10.0d) / 5 * 5) / 10.0d;
}
public static short[] mapTempBasalToPulsesPerSlot(byte durationInSlots, double rateInUnitsPerHour) {
short pulsesPerHour = (short) Math.round(rateInUnitsPerHour * 20);
short[] pulsesPerSlot = new short[durationInSlots];
boolean remainingPulse = false;
for (int i = 0; durationInSlots > i; i++) {
pulsesPerSlot[i] = (short) (pulsesPerHour / 2);
if (pulsesPerHour % 2 == 1) { // Do extra alternate pulse
if (remainingPulse) {
pulsesPerSlot[i] += 1;
}
remainingPulse = !remainingPulse;
}
}
return pulsesPerSlot;
}
public static short calculateChecksum(byte totalNumberOfSlots, short pulsesInFirstSlot, short[] pulsesPerSlot) {
ByteBuffer buffer = ByteBuffer.allocate(1 + 2 + 2 + 2 * pulsesPerSlot.length) //
.put(totalNumberOfSlots) //
.putShort((short) 0x3840) //
.putShort(pulsesInFirstSlot);
for (short pulses : pulsesPerSlot) {
buffer.putShort(pulses);
}
return MessageUtil.calculateChecksum(buffer.array());
}
public static List<ShortInsulinProgramElement> mapPulsesPerSlotToShortInsulinProgramElements(short[] pulsesPerSlot) {
return ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
}
}

View file

@ -30,7 +30,7 @@ public class MessageUtil {
return s;
}
public static short createCheckSum(byte[] bytes) {
public static short calculateChecksum(byte[] bytes) {
short sum = 0;
for (byte b : bytes) {
sum += (short) (b & 0xff);

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class ProgramTempBasalCommandTest {
@Test
public void testFirstTempBasalMethod() throws DecoderException {
ProgramTempBasalCommand command = new ProgramTempBasalCommand.Builder() //
.setUniqueId(37879809) //
.setNonce(1229869870) //
.setSequenceNumber((short) 3) //
.setRateInUnitsPerHour(5d) //
.setDurationInMinutes((short) 60) //
.setProgramReminder(new ProgramReminder(false, true, (byte) 0)) //
.build();
assertEquals(ProgramTempBasalCommand.TempBasalMethod.SECOND_METHOD, command.getTempBasalMethod());
assertArrayEquals(Hex.decodeHex("024200010C201A0E494E532E01014303384000322032160E400005DC0036EE8005DC0036EE808396"), command.getEncoded());
}
@Test
public void testSecondTempBasalMethod() throws DecoderException {
ProgramTempBasalCommand command = new ProgramTempBasalCommand.Builder() //
.setUniqueId(37879809) //
.setNonce(1229869870) //
.setSequenceNumber((short) 13) //
.setRateInUnitsPerHour(0.0) //
.setDurationInMinutes((short) 60) //
.setProgramReminder(new ProgramReminder(true, true, (byte) 0)) //
.build();
assertEquals(ProgramTempBasalCommand.TempBasalMethod.SECOND_METHOD, command.getTempBasalMethod());
assertArrayEquals(Hex.decodeHex("0242000134201A0E494E532E01007B03384000002000160EC00000036B49D2000003EB49D2000223"), command.getEncoded());
}
}