More WIP on Omnipod Dash program basal command
This commit is contained in:
parent
1c490f72ae
commit
79c9e9a938
|
@ -8,6 +8,7 @@ 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.CurrentLongInsulinProgramElement;
|
||||
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;
|
||||
|
@ -87,9 +88,6 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
|
|||
private Date currentTime;
|
||||
|
||||
public Builder setBasalProgram(BasalProgram basalProgram) {
|
||||
if (basalProgram == null) {
|
||||
throw new IllegalArgumentException("basalProgram can not be null");
|
||||
}
|
||||
this.basalProgram = basalProgram;
|
||||
return this;
|
||||
}
|
||||
|
@ -120,15 +118,15 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
|
|||
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
|
||||
CurrentLongInsulinProgramElement currentLongInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime);
|
||||
|
||||
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(address, sequenceNumber, multiCommandFlag, nonce,
|
||||
shortInsulinProgramElements, currentSlot.getIndex(), checksum, (short) (currentSlot.getSecondsRemaining() * 8),
|
||||
shortInsulinProgramElements, currentSlot.getIndex(), checksum, (short) (currentSlot.getEighthSecondsRemaining() * 8),
|
||||
currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL);
|
||||
|
||||
return new ProgramBasalCommand(interlockCommand, address, sequenceNumber, multiCommandFlag, longInsulinProgramElements, programReminder, currentInsulinProgramElementIndex, remainingTenthPulsesInCurrentInsulinProgramElement, delayUntilNextPulseInUsec);
|
||||
return new ProgramBasalCommand(interlockCommand, address, sequenceNumber, multiCommandFlag,
|
||||
longInsulinProgramElements, programReminder, currentLongInsulinProgramElement.getIndex(),
|
||||
currentLongInsulinProgramElement.getRemainingTenthPulses(), currentLongInsulinProgramElement.getDelayUntilNextTenthPulseInUsec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
|
||||
public class CurrentLongInsulinProgramElement implements Encodable {
|
||||
private final byte index;
|
||||
private final int delayUntilNextTenthPulseInUsec;
|
||||
private final short remainingTenthPulses;
|
||||
|
||||
public CurrentLongInsulinProgramElement(byte index, int delayUntilNextTenthPulseInUsec, short remainingTenthPulses) {
|
||||
this.index = index;
|
||||
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
|
||||
this.remainingTenthPulses = remainingTenthPulses;
|
||||
}
|
||||
|
||||
public byte getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getDelayUntilNextTenthPulseInUsec() {
|
||||
return delayUntilNextTenthPulseInUsec;
|
||||
}
|
||||
|
||||
public short getRemainingTenthPulses() {
|
||||
return remainingTenthPulses;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "CurrentLongInsulinProgramElement{" +
|
||||
"index=" + index +
|
||||
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
|
||||
", remainingTenthPulses=" + remainingTenthPulses +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
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;
|
||||
private final byte index;
|
||||
private final short eighthSecondsRemaining;
|
||||
private final short pulsesRemaining;
|
||||
|
||||
public CurrentSlot(byte index, short secondsRemaining, short pulsesRemaining) {
|
||||
public CurrentSlot(byte index, short eighthSecondsRemaining, short pulsesRemaining) {
|
||||
this.index = index;
|
||||
this.secondsRemaining = secondsRemaining;
|
||||
this.eighthSecondsRemaining = eighthSecondsRemaining;
|
||||
this.pulsesRemaining = pulsesRemaining;
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ public class CurrentSlot {
|
|||
return index;
|
||||
}
|
||||
|
||||
public short getSecondsRemaining() {
|
||||
return secondsRemaining;
|
||||
public short getEighthSecondsRemaining() {
|
||||
return eighthSecondsRemaining;
|
||||
}
|
||||
|
||||
public short getPulsesRemaining() {
|
||||
|
@ -26,7 +26,7 @@ public class CurrentSlot {
|
|||
@Override public String toString() {
|
||||
return "CurrentSlot{" +
|
||||
"index=" + index +
|
||||
", secondsRemaining=" + secondsRemaining +
|
||||
", eighthSecondsRemaining=" + eighthSecondsRemaining +
|
||||
", pulsesRemaining=" + pulsesRemaining +
|
||||
'}';
|
||||
}
|
||||
|
|
|
@ -5,25 +5,47 @@ import java.nio.ByteBuffer;
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
|
||||
|
||||
public class LongInsulinProgramElement implements Encodable {
|
||||
private final byte startSlotIndex;
|
||||
private final byte numberOfSlots;
|
||||
private final short totalTenthPulses;
|
||||
private final int delayBetweenTenthPulses;
|
||||
private final int delayBetweenTenthPulsesInUsec;
|
||||
|
||||
public LongInsulinProgramElement(byte totalTenthPulses, short delayBetweenTenthPulses) {
|
||||
public LongInsulinProgramElement(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses, int delayBetweenTenthPulsesInUsec) {
|
||||
this.startSlotIndex = startSlotIndex;
|
||||
this.numberOfSlots = numberOfSlots;
|
||||
this.totalTenthPulses = totalTenthPulses;
|
||||
this.delayBetweenTenthPulses = delayBetweenTenthPulses;
|
||||
this.delayBetweenTenthPulsesInUsec = delayBetweenTenthPulsesInUsec;
|
||||
}
|
||||
|
||||
@Override public byte[] getEncoded() {
|
||||
return ByteBuffer.allocate(6) //
|
||||
.putShort(totalTenthPulses) //
|
||||
.putInt(delayBetweenTenthPulses) //
|
||||
.putInt(delayBetweenTenthPulsesInUsec) //
|
||||
.array();
|
||||
}
|
||||
|
||||
public byte getStartSlotIndex() {
|
||||
return startSlotIndex;
|
||||
}
|
||||
|
||||
public byte getNumberOfSlots() {
|
||||
return numberOfSlots;
|
||||
}
|
||||
|
||||
public short getTotalTenthPulses() {
|
||||
return totalTenthPulses;
|
||||
}
|
||||
|
||||
public int getDelayBetweenTenthPulsesInUsec() {
|
||||
return delayBetweenTenthPulsesInUsec;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "LongInsulinProgramElement{" +
|
||||
"totalTenthPulses=" + totalTenthPulses +
|
||||
", delayBetweenTenthPulses=" + delayBetweenTenthPulses +
|
||||
"startSlotIndex=" + startSlotIndex +
|
||||
", numberOfSlots=" + numberOfSlots +
|
||||
", totalTenthPulses=" + totalTenthPulses +
|
||||
", delayBetweenTenthPulsesInUsec=" + delayBetweenTenthPulsesInUsec +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
@ -9,19 +10,39 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
|
||||
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 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;
|
||||
|
||||
private ProgramBasalUtil() {
|
||||
}
|
||||
|
||||
public static List<LongInsulinProgramElement> mapPulsesPerSlotToLongInsulinProgramElements(short[] pulsesPerSlot) {
|
||||
if (pulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
|
||||
public static List<LongInsulinProgramElement> mapPulsesPerSlotToLongInsulinProgramElements(short[] tenthPulsesPerSlot) {
|
||||
if (tenthPulsesPerSlot.length != NUMBER_OF_BASAL_SLOTS) {
|
||||
throw new IllegalArgumentException("Basal program must contain 48 slots");
|
||||
}
|
||||
|
||||
// TODO
|
||||
List<LongInsulinProgramElement> elements = new ArrayList<>();
|
||||
long previousTenthPulsesPerSlot = 0;
|
||||
byte numberOfSlotsInCurrentElement = 0;
|
||||
byte startSlotIndex = 0;
|
||||
|
||||
return new ArrayList<>();
|
||||
for (int i = 0; i < NUMBER_OF_BASAL_SLOTS; i++) {
|
||||
if (i == 0) {
|
||||
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || numberOfSlotsInCurrentElement >= MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
||||
elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) ((NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
|
||||
|
||||
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
|
||||
numberOfSlotsInCurrentElement = 1;
|
||||
startSlotIndex += numberOfSlotsInCurrentElement;
|
||||
} else {
|
||||
numberOfSlotsInCurrentElement++;
|
||||
}
|
||||
}
|
||||
elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) ((NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
public static List<ShortInsulinProgramElement> mapPulsesPerSlotToShortInsulinProgramElements(short[] pulsesPerSlot) {
|
||||
|
@ -45,7 +66,7 @@ public final class ProgramBasalUtil {
|
|||
} 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) {
|
||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
||||
numberOfSlotsInCurrentElement++;
|
||||
} else {
|
||||
elements.add(new ShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, false));
|
||||
|
@ -70,7 +91,7 @@ public final class ProgramBasalUtil {
|
|||
currentTotalNumberOfSlots++;
|
||||
expectAlternatePulseForNextSegment = !expectAlternatePulseForNextSegment;
|
||||
|
||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_SHORT_INSULIN_PROGRAM_ELEMENT) {
|
||||
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
|
||||
numberOfSlotsInCurrentElement++;
|
||||
} else {
|
||||
// End of alternate pulse segment (no slots left in element)
|
||||
|
@ -110,6 +131,21 @@ public final class ProgramBasalUtil {
|
|||
return elements;
|
||||
}
|
||||
|
||||
public static short[] mapBasalProgramToTenthPulsesPerSlot(BasalProgram basalProgram) {
|
||||
short[] tenthPulsesPerSlot = new short[NUMBER_OF_BASAL_SLOTS];
|
||||
for (BasalProgram.Segment segment : basalProgram.getSegments()) {
|
||||
for (int i = segment.getStartSlotIndex(); i < segment.getEndSlotIndex(); i++) {
|
||||
tenthPulsesPerSlot[i] = (short) (roundToHalf(segment.getPulsesPerHour() / 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[] mapBasalProgramToPulsesPerSlot(BasalProgram basalProgram) {
|
||||
short[] pulsesPerSlot = new short[NUMBER_OF_BASAL_SLOTS];
|
||||
for (BasalProgram.Segment segment : basalProgram.getSegments()) {
|
||||
|
@ -137,14 +173,75 @@ public final class ProgramBasalUtil {
|
|||
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);
|
||||
int secondOfDay = secondOfMinute + hourOfDay * 3_600 + minuteOfHour * 60;
|
||||
|
||||
return new CurrentSlot(index, secondsRemaining, pulsesRemaining);
|
||||
short secondsRemaining = (short) ((index + 1) * 1_800 - secondOfDay);
|
||||
short pulsesRemaining = (short) ((double) pulsesPerSlot[index] * secondsRemaining / 1_800);
|
||||
|
||||
return new CurrentSlot(index, (short) (secondsRemaining * 8), pulsesRemaining);
|
||||
}
|
||||
|
||||
public static short createChecksum() {
|
||||
// TODO
|
||||
return 0;
|
||||
public static CurrentLongInsulinProgramElement calculateCurrentLongInsulinProgramElement(List<LongInsulinProgramElement> elements, 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);
|
||||
|
||||
int secondOfDay = secondOfMinute + hourOfDay * 3_600 + minuteOfHour * 60;
|
||||
int startSlotIndex = 0;
|
||||
|
||||
byte index = 0;
|
||||
for (LongInsulinProgramElement element : elements) {
|
||||
int startTimeInSeconds = startSlotIndex * 1_800;
|
||||
int endTimeInSeconds = startTimeInSeconds + element.getNumberOfSlots() * 1_800;
|
||||
|
||||
if (secondOfDay >= startTimeInSeconds && secondOfDay < endTimeInSeconds) {
|
||||
long totalNumberOfTenThousandthPulsesInSlot = element.getTotalTenthPulses() * 1_000;
|
||||
if (totalNumberOfTenThousandthPulsesInSlot == 0) {
|
||||
totalNumberOfTenThousandthPulsesInSlot = element.getNumberOfSlots() * 1_000;
|
||||
}
|
||||
|
||||
int durationInSeconds = endTimeInSeconds - startTimeInSeconds;
|
||||
int secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds;
|
||||
long remainingTenThousandthPulses = (durationInSeconds - secondsPassedInCurrentSlot) * (long) totalNumberOfTenThousandthPulsesInSlot;
|
||||
int delayBetweenTenthPulsesInUsec = (int) (durationInSeconds * 1_000_000L * 1_000 / totalNumberOfTenThousandthPulsesInSlot);
|
||||
int secondsRemaining = secondsPassedInCurrentSlot % 1_800;
|
||||
int delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec;
|
||||
for (int i = 0; i < secondsRemaining; i++) {
|
||||
delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec - 1_000_000;
|
||||
while (delayUntilNextTenthPulseInUsec <= 0) {
|
||||
delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec;
|
||||
}
|
||||
}
|
||||
short remainingTenthPulses = (short) ((0 != remainingTenThousandthPulses % 1_000 ? 1 : 0) + remainingTenThousandthPulses / 1_000);
|
||||
|
||||
return new CurrentLongInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses);
|
||||
}
|
||||
|
||||
index++;
|
||||
startSlotIndex += element.getNumberOfSlots();
|
||||
}
|
||||
throw new IllegalStateException("Could not determine current long insulin program element");
|
||||
}
|
||||
|
||||
public static short createChecksum(short[] pulsesPerSlot, CurrentSlot currentSlot) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1 + 2 + 2 + NUMBER_OF_BASAL_SLOTS * 2) //
|
||||
.put(currentSlot.getIndex()) //
|
||||
.putShort(currentSlot.getPulsesRemaining()) //
|
||||
.putShort(currentSlot.getEighthSecondsRemaining());
|
||||
|
||||
for (short pulses : pulsesPerSlot) {
|
||||
buffer.putShort(pulses);
|
||||
}
|
||||
|
||||
byte[] bytes = buffer.array();
|
||||
|
||||
short sum = 0;
|
||||
for (byte b : bytes) {
|
||||
sum += (short) (b & 0xff);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue