Finished Omnipod Dash program basal command

This commit is contained in:
Bart Sopers 2021-02-17 16:10:18 +01:00
parent 8d554fa35b
commit a3b0044874
8 changed files with 66 additions and 33 deletions

View file

@ -36,12 +36,12 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec; this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
} }
public short getLength() { short getLength() {
return (short) (insulinProgramElements.size() * 2 + 14); return (short) (insulinProgramElements.size() * 6 + 10);
} }
public byte getBodyLength() { byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 2 + 12); return (byte) (insulinProgramElements.size() * 6 + 8);
} }
@Override public byte[] getEncoded() { @Override public byte[] getEncoded() {
@ -60,11 +60,11 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
byte[] interlockCommand = this.interlockCommand.getEncoded(); byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (bolusCommand.length + interlockCommand.length), multiCommandFlag); byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (bolusCommand.length + interlockCommand.length), multiCommandFlag);
return ByteBuffer.allocate(bolusCommand.length + interlockCommand.length + header.length) // return appendCrc(ByteBuffer.allocate(bolusCommand.length + interlockCommand.length + header.length) //
.put(header) // .put(header) //
.put(interlockCommand) // .put(interlockCommand) //
.put(bolusCommand) // .put(bolusCommand) //
.array(); .array());
} }
@Override public String toString() { @Override public String toString() {
@ -116,12 +116,12 @@ public final class ProgramBasalCommand extends HeaderEnabledCommand {
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram); short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime); CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
short checksum = ProgramBasalUtil.createChecksum(pulsesPerSlot, currentSlot); short checksum = ProgramBasalUtil.createChecksum(pulsesPerSlot, currentSlot);
List<LongInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToLongInsulinProgramElements(pulsesPerSlot); List<LongInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram));
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot); List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
CurrentLongInsulinProgramElement currentLongInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime); CurrentLongInsulinProgramElement currentLongInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce, ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
shortInsulinProgramElements, currentSlot.getIndex(), checksum, (short) (currentSlot.getEighthSecondsRemaining() * 8), shortInsulinProgramElements, currentSlot.getIndex(), checksum, currentSlot.getEighthSecondsRemaining(),
currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL); currentSlot.getPulsesRemaining(), ProgramInsulinCommand.DeliveryType.BASAL);
return new ProgramBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag, return new ProgramBasalCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag,

View file

@ -27,12 +27,12 @@ final class ProgramInsulinCommand extends NonceEnabledCommand {
this.deliveryType = deliveryType; this.deliveryType = deliveryType;
} }
short getLength() { public short getLength() {
return (short) (insulinProgramElements.size() * 6 + 10); return (short) (insulinProgramElements.size() * 2 + 14);
} }
byte getBodyLength() { public byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 6 + 8); return (byte) (insulinProgramElements.size() * 2 + 12);
} }
@Override public byte[] getEncoded() { @Override public byte[] getEncoded() {

View file

@ -5,10 +5,10 @@ public enum CommandType {
GET_VERSION((byte) 0x07), GET_VERSION((byte) 0x07),
GET_STATUS((byte) 0x0e), GET_STATUS((byte) 0x0e),
SILENCE_ALERTS((byte) 0x11), SILENCE_ALERTS((byte) 0x11),
PROGRAM_BASAL((byte) 0x13), PROGRAM_BASAL((byte) 0x13), // Always preceded by 0x1a
PROGRAM_TEMP_BASAL((byte) 0x16), // Always preceded by 0x1a PROGRAM_TEMP_BASAL((byte) 0x16), // Always preceded by 0x1a
PROGRAM_BOLUS((byte) 0x17), // Always preceded by 0x1a PROGRAM_BOLUS((byte) 0x17), // Always preceded by 0x1a
PROGRAM_ALERTS((byte) 0x19), // Always preceded by 0x1a PROGRAM_ALERTS((byte) 0x19),
PROGRAM_INSULIN((byte) 0x1a), // Always followed by one of: 0x13, 0x16, 0x17 PROGRAM_INSULIN((byte) 0x1a), // Always followed by one of: 0x13, 0x16, 0x17
DEACTIVATE((byte) 0x1c), DEACTIVATE((byte) 0x1c),
PROGRAM_BEEPS((byte) 0x1e), PROGRAM_BEEPS((byte) 0x1e),

View file

@ -1,8 +1,6 @@
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;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable; public class CurrentLongInsulinProgramElement {
public class CurrentLongInsulinProgramElement implements Encodable {
private final byte index; private final byte index;
private final int delayUntilNextTenthPulseInUsec; private final int delayUntilNextTenthPulseInUsec;
private final short remainingTenthPulses; private final short remainingTenthPulses;
@ -32,8 +30,4 @@ public class CurrentLongInsulinProgramElement implements Encodable {
", remainingTenthPulses=" + remainingTenthPulses + ", remainingTenthPulses=" + remainingTenthPulses +
'}'; '}';
} }
@Override public byte[] getEncoded() {
return new byte[0];
}
} }

View file

@ -30,8 +30,8 @@ public final class ProgramBasalUtil {
if (i == 0) { if (i == 0) {
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]; previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
numberOfSlotsInCurrentElement = 1; numberOfSlotsInCurrentElement = 1;
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || numberOfSlotsInCurrentElement >= MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) { } else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || (numberOfSlotsInCurrentElement + 1) * previousTenthPulsesPerSlot > 65_534) {
elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) ((NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement)))); elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]; previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
numberOfSlotsInCurrentElement = 1; numberOfSlotsInCurrentElement = 1;
@ -40,7 +40,7 @@ public final class ProgramBasalUtil {
numberOfSlotsInCurrentElement++; numberOfSlotsInCurrentElement++;
} }
} }
elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) ((NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement)))); elements.add(new LongInsulinProgramElement(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement), (int) (((long) NUMBER_OF_USEC_IN_SLOT * numberOfSlotsInCurrentElement) / (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement))));
return elements; return elements;
} }
@ -205,7 +205,7 @@ public final class ProgramBasalUtil {
int durationInSeconds = endTimeInSeconds - startTimeInSeconds; int durationInSeconds = endTimeInSeconds - startTimeInSeconds;
int secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds; int secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds;
long remainingTenThousandthPulses = (durationInSeconds - secondsPassedInCurrentSlot) * (long) totalNumberOfTenThousandthPulsesInSlot; long remainingTenThousandthPulses = (long) ((durationInSeconds - secondsPassedInCurrentSlot) / (double) durationInSeconds * totalNumberOfTenThousandthPulsesInSlot);
int delayBetweenTenthPulsesInUsec = (int) (durationInSeconds * 1_000_000L * 1_000 / totalNumberOfTenThousandthPulsesInSlot); int delayBetweenTenthPulsesInUsec = (int) (durationInSeconds * 1_000_000L * 1_000 / totalNumberOfTenThousandthPulsesInSlot);
int secondsRemaining = secondsPassedInCurrentSlot % 1_800; int secondsRemaining = secondsPassedInCurrentSlot % 1_800;
int delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec; int delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec;
@ -215,7 +215,7 @@ public final class ProgramBasalUtil {
delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec; delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec;
} }
} }
short remainingTenthPulses = (short) ((0 != remainingTenThousandthPulses % 1_000 ? 1 : 0) + remainingTenThousandthPulses / 1_000); short remainingTenthPulses = (short) ((remainingTenThousandthPulses % 1_000 != 0 ? 1 : 0) + remainingTenThousandthPulses / 1_000);
return new CurrentLongInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses); return new CurrentLongInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses);
} }

View file

@ -5,18 +5,18 @@ import java.nio.ByteBuffer;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable; import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public class ShortInsulinProgramElement implements Encodable { public class ShortInsulinProgramElement implements Encodable {
private final byte numberOfSlotsMinusOne; // 4 bits private final byte numberOfSlots; // 4 bits
private final short pulsesPerSlot; // 10 bits private final short pulsesPerSlot; // 10 bits
private final boolean extraAlternatePulse; private final boolean extraAlternatePulse;
public ShortInsulinProgramElement(byte numberOfSlotsMinusOne, short pulsesPerSlot, boolean extraAlternatePulse) { public ShortInsulinProgramElement(byte numberOfSlots, short pulsesPerSlot, boolean extraAlternatePulse) {
this.numberOfSlotsMinusOne = numberOfSlotsMinusOne; this.numberOfSlots = numberOfSlots;
this.pulsesPerSlot = pulsesPerSlot; this.pulsesPerSlot = pulsesPerSlot;
this.extraAlternatePulse = extraAlternatePulse; this.extraAlternatePulse = extraAlternatePulse;
} }
@Override public byte[] getEncoded() { @Override public byte[] getEncoded() {
byte firstByte = (byte) ((((numberOfSlotsMinusOne - 1) & 0x0f) << 4) // byte firstByte = (byte) ((((numberOfSlots - 1) & 0x0f) << 4) //
| ((extraAlternatePulse ? 1 : 0) << 3) // | ((extraAlternatePulse ? 1 : 0) << 3) //
| ((pulsesPerSlot >>> 8) & 0x03)); | ((pulsesPerSlot >>> 8) & 0x03));
@ -28,7 +28,7 @@ public class ShortInsulinProgramElement implements Encodable {
@Override public String toString() { @Override public String toString() {
return "ShortInsulinProgramElement{" + return "ShortInsulinProgramElement{" +
"numberOfSlotsMinusOne=" + numberOfSlotsMinusOne + "numberOfSlotsMinusOne=" + numberOfSlots +
", pulsesPerSlot=" + pulsesPerSlot + ", pulsesPerSlot=" + pulsesPerSlot +
", extraAlternatePulse=" + extraAlternatePulse + ", extraAlternatePulse=" + extraAlternatePulse +
'}'; '}';

View file

@ -12,8 +12,8 @@ public class ProgramReminder implements Encodable {
} }
@Override public byte[] getEncoded() { @Override public byte[] getEncoded() {
return new byte[]{(byte) (((this.atStart ? 0 : 1) << 7) return new byte[]{(byte) (((this.atStart ? 1 : 0) << 7)
| ((this.atEnd ? 0 : 1) << 6) | ((this.atEnd ? 1 : 0) << 6)
| (this.atInterval & 0x3f))}; | (this.atInterval & 0x3f))};
} }
} }

View file

@ -0,0 +1,39 @@
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 java.util.Arrays;
import java.util.Date;
import java.util.List;
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 static org.junit.Assert.assertArrayEquals;
public class ProgramBasalCommandTest {
@Test
public void testProgramBasalCommand() throws DecoderException {
List<BasalProgram.Segment> segments = Arrays.asList(
new BasalProgram.Segment((short) 0, (short) 48, 300)
);
BasalProgram basalProgram = new BasalProgram(segments);
Date date = new Date(2021, 1, 17, 14, 47, 43);
byte[] encoded = new ProgramBasalCommand.Builder() //
.setUniqueId(37879809) //
.setNonce(1229869870) //
.setSequenceNumber((short) 10) //
.setBasalProgram(basalProgram) //
.setCurrentTime(date) //
.setProgramReminder(new ProgramReminder(false, true, (byte) 0)) //
.build() //
.getEncoded();
System.out.println(Hex.encodeHexString(encoded));
assertArrayEquals(Hex.decodeHex("0242000128241A12494E532E0005E81D1708000CF01EF01EF01E130E40001593004C4B403840005B8D80827C"), encoded);
}
}