Merge pull request #6 from 0pen-dash/adrian/kotlinify-driver-pod

Kotlinify Omnipod Dash driver
This commit is contained in:
bartsopers 2021-02-26 10:56:14 +01:00 committed by GitHub
commit 5db17e735c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
122 changed files with 3919 additions and 3348 deletions

View file

@ -32,7 +32,7 @@
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_address"
android:id="@+id/unique_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -110,7 +110,7 @@
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/pod_tid"
android:id="@+id/pod_sequence_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash
import info.nightscout.androidaps.events.Event
class EventOmnipodDashPumpValuesChanged : Event()

View file

@ -1,213 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash;
import androidx.annotation.Nullable;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.PumpPluginBase;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.common.ManufacturerType;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment;
import info.nightscout.androidaps.queue.commands.CustomCommand;
import info.nightscout.androidaps.utils.TimeChangeType;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@Singleton
public class OmnipodDashPumpPlugin extends PumpPluginBase implements PumpInterface {
private static final PumpDescription PUMP_DESCRIPTION = new PumpDescription(PumpType.Omnipod_Dash);
private final AAPSLogger aapsLogger;
private final ResourceHelper resourceHelper;
private final CommandQueueProvider commandQueue;
@Inject
public OmnipodDashPumpPlugin(HasAndroidInjector injector, AAPSLogger aapsLogger, ResourceHelper resourceHelper, CommandQueueProvider commandQueue) {
super(new PluginDescription() //
.mainType(PluginType.PUMP) //
.fragmentClass(OmnipodDashOverviewFragment.class.getName()) //
.pluginIcon(R.drawable.ic_pod_128)
.pluginName(R.string.omnipod_dash_name) //
.shortName(R.string.omnipod_dash_name_short) //
.preferencesId(R.xml.omnipod_dash_preferences) //
.description(R.string.omnipod_dash_pump_description), injector, aapsLogger, resourceHelper, commandQueue);
this.aapsLogger = aapsLogger;
this.resourceHelper = resourceHelper;
this.commandQueue = commandQueue;
}
@Override public boolean isInitialized() {
return true;
}
@Override public boolean isSuspended() {
return false;
}
@Override public boolean isBusy() {
// prevents the queue from executing
return true;
}
@Override public boolean isConnected() {
return false;
}
@Override public boolean isConnecting() {
return false;
}
@Override public boolean isHandshakeInProgress() {
return false;
}
@Override public void finishHandshaking() {
}
@Override public void connect(@NotNull String reason) {
}
@Override public void disconnect(@NotNull String reason) {
}
@Override public void stopConnecting() {
}
@Override public void getPumpStatus(@NotNull String reason) {
}
@NotNull @Override public PumpEnactResult setNewBasalProfile(@NotNull Profile profile) {
// TODO
return new PumpEnactResult(getInjector()).success(true).enacted(true);
}
@Override public boolean isThisProfileSet(@NotNull Profile profile) {
// TODO
return true;
}
@Override public long lastDataTime() {
// TODO
return System.currentTimeMillis();
}
@Override public double getBaseBasalRate() {
return 0;
}
@Override public double getReservoirLevel() {
return 0;
}
@Override public int getBatteryLevel() {
return 0;
}
@NotNull @Override public PumpEnactResult deliverTreatment(@NotNull DetailedBolusInfo detailedBolusInfo) {
return null;
}
@Override public void stopBolusDelivering() {
}
@NotNull @Override public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NotNull Profile profile, boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) {
return null;
}
@NotNull @Override public PumpEnactResult cancelTempBasal(boolean enforceNew) {
return null;
}
@NotNull @Override public PumpEnactResult cancelExtendedBolus() {
return null;
}
@NotNull @Override public JSONObject getJSONStatus(@NotNull Profile profile, @NotNull String profileName, @NotNull String version) {
return new JSONObject();
}
@NotNull @Override public ManufacturerType manufacturer() {
return getPumpDescription().pumpType.getManufacturer();
}
@NotNull @Override public PumpType model() {
return getPumpDescription().pumpType;
}
@NotNull @Override public String serialNumber() {
return null;
}
@NotNull @Override public PumpDescription getPumpDescription() {
return PUMP_DESCRIPTION;
}
@NotNull @Override public String shortStatus(boolean veryShort) {
return null;
}
@Override public boolean isFakingTempsByExtendedBoluses() {
return false;
}
@NotNull @Override public PumpEnactResult loadTDDs() {
return null;
}
@Override public boolean canHandleDST() {
return false;
}
@Override
public List<CustomAction> getCustomActions() {
return Collections.emptyList();
}
@Override
public void executeCustomAction(@NotNull CustomActionType customActionType) {
aapsLogger.warn(LTag.PUMP, "Unsupported custom action: " + customActionType);
}
@Nullable @Override public PumpEnactResult executeCustomCommand(@NotNull CustomCommand customCommand) {
return null;
}
@Override public void timezoneOrDSTChanged(@NotNull TimeChangeType timeChangeType) {
}
}

View file

@ -0,0 +1,210 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.common.ManufacturerType
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
import info.nightscout.androidaps.queue.commands.CustomCommand
import info.nightscout.androidaps.utils.TimeChangeType
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class OmnipodDashPumpPlugin @Inject constructor(
private val omnipodManager: OmnipodDashManager,
private val podStateManager: OmnipodDashPodStateManager,
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper,
commandQueue: CommandQueueProvider
) : PumpPluginBase(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue), PumpInterface {
companion object {
private val pluginDescription = PluginDescription() //
.mainType(PluginType.PUMP) //
.fragmentClass(OmnipodDashOverviewFragment::class.java.name) //
.pluginIcon(R.drawable.ic_pod_128)
.pluginName(R.string.omnipod_dash_name) //
.shortName(R.string.omnipod_dash_name_short) //
.preferencesId(R.xml.omnipod_dash_preferences) //
.description(R.string.omnipod_dash_pump_description)
private val pumpDescription = PumpDescription(PumpType.Omnipod_Dash)
}
override fun isInitialized(): Boolean {
// TODO
return true
}
override fun isSuspended(): Boolean {
// TODO
return false
}
override fun isBusy(): Boolean {
// prevents the queue from executing commands
// TODO
return true
}
override fun isConnected(): Boolean {
// TODO
return false
}
override fun isConnecting(): Boolean {
// TODO
return false
}
override fun isHandshakeInProgress(): Boolean {
// TODO
return false
}
override fun finishHandshaking() {
// TODO
}
override fun connect(reason: String) {
// TODO
}
override fun disconnect(reason: String) {
// TODO
}
override fun stopConnecting() {
// TODO
}
override fun getPumpStatus(reason: String) {
// TODO
}
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
// TODO
return PumpEnactResult(injector).success(true).enacted(true)
}
override fun isThisProfileSet(profile: Profile): Boolean {
// TODO
return true
}
override fun lastDataTime(): Long {
// TODO
return System.currentTimeMillis()
}
override val baseBasalRate: Double
get() = 0.0 // TODO
override val reservoirLevel: Double
get() = 0.0 // TODO
override val batteryLevel: Int
get() = 0
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
// TODO
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
}
override fun stopBolusDelivering() {
// TODO
}
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
// TODO
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
}
override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult {
// TODO i18n
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support percentage temp basals")
}
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
// TODO i18n
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
}
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
// TODO
return PumpEnactResult(injector).success(false).enacted(false).comment("TODO")
}
override fun cancelExtendedBolus(): PumpEnactResult {
// TODO i18n
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support extended boluses")
}
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
// TODO
return JSONObject()
}
override val pumpDescription: PumpDescription = Companion.pumpDescription
override fun manufacturer(): ManufacturerType {
return pumpDescription.pumpType.manufacturer
}
override fun model(): PumpType {
return pumpDescription.pumpType
}
override fun serialNumber(): String {
// TODO
return "TODO"
}
override fun shortStatus(veryShort: Boolean): String {
// TODO
return "TODO"
}
override val isFakingTempsByExtendedBoluses: Boolean
get() = false
override fun loadTDDs(): PumpEnactResult {
// TODO i18n
return PumpEnactResult(injector).success(false).enacted(false).comment("Omnipod Dash driver does not support TDD")
}
override fun canHandleDST(): Boolean {
return false
}
override fun getCustomActions(): List<CustomAction> {
return emptyList()
}
override fun executeCustomAction(customActionType: CustomActionType) {
aapsLogger.warn(LTag.PUMP, "Unsupported custom action: $customActionType")
}
override fun executeCustomCommand(customCommand: CustomCommand): PumpEnactResult? {
// TODO
return null
}
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
// TODO
}
}

View file

@ -1,10 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.dagger
import dagger.Binds
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.ActivityScope
import info.nightscout.androidaps.plugins.pump.omnipod.common.dagger.OmnipodWizardModule
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.BleManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManagerImpl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManagerImpl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.DashPodManagementActivity
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment
import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.wizard.activation.DashPodActivationWizardActivity
@ -31,6 +37,14 @@ abstract class OmnipodDashModule {
@ContributesAndroidInjector
abstract fun contributesOmnipodDashOverviewFragment(): OmnipodDashOverviewFragment
@ContributesAndroidInjector
abstract fun contributesBleManager(): BleManager
}
// MANAGERS
@Binds
abstract fun bindsOmnipodDashBleManagerImpl(bleManager: OmnipodDashBleManagerImpl): OmnipodDashBleManager
@Binds
abstract fun bindsOmnipodDashPodStateManagerImpl(podStateManager: OmnipodDashPodStateManagerImpl): OmnipodDashPodStateManager
@Binds
abstract fun bindsOmnipodDashManagerImpl(omnipodManager: OmnipodDashManagerImpl): OmnipodDashManager
}

View file

@ -1,4 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver;
public interface OmnipodDashManager {
}

View file

@ -0,0 +1,32 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import io.reactivex.Observable
interface OmnipodDashManager {
fun activatePodPart1(): Observable<PodEvent>
fun activatePodPart2(): Observable<PodEvent>
fun getStatus(): Observable<PodEvent>
fun setBasalProgram(program: BasalProgram): Observable<PodEvent>
fun suspendDelivery(): Observable<PodEvent>
fun setTime(): Observable<PodEvent>
fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent>
fun cancelTempBasal(): Observable<PodEvent>
fun bolus(amount: Double): Observable<PodEvent>
fun cancelBolus(): Observable<PodEvent>
fun silenceAlerts(): Observable<PodEvent>
fun deactivatePod(): Observable<PodEvent>
}

View file

@ -0,0 +1,78 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BasalProgram
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import io.reactivex.Observable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class OmnipodDashManagerImpl @Inject constructor(
private val aapsLogger: AAPSLogger,
private val podStateManager: OmnipodDashPodStateManager,
private val bleManager: OmnipodDashBleManager
) : OmnipodDashManager {
override fun activatePodPart1(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun activatePodPart2(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun getStatus(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun setBasalProgram(program: BasalProgram): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun suspendDelivery(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun setTime(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun cancelTempBasal(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun bolus(amount: Double): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun cancelBolus(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun silenceAlerts(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
override fun deactivatePod(): Observable<PodEvent> {
// TODO
return Observable.empty()
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
interface OmnipodDashBleManager {
// TODO should we keep this method?
fun activateNewPod()
}

View file

@ -20,13 +20,13 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BleManager @Inject constructor(private val context: Context, private val aapsLogger: AAPSLogger) : OmnipodDashCommunicationManager {
class OmnipodDashBleManagerImpl @Inject constructor(private val context: Context, private val aapsLogger: AAPSLogger) : OmnipodDashBleManager {
private val bluetoothManager: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
@Throws(InterruptedException::class, ScanFailException::class, FailedToConnectException::class, CouldNotSendBleException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWriteException::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
fun activateNewPod() {
override fun activateNewPod() {
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
val podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).scanResult.device.address

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
interface OmnipodDashCommunicationManager

View file

@ -7,7 +7,7 @@ enum class BleCommandType(val value: Byte) {
@JvmStatic
fun byValue(value: Byte): BleCommandType =
BleCommandType.values().firstOrNull { it.value == value }
values().firstOrNull { it.value == value }
?: throw IllegalArgumentException("Unknown BleCommandType: $value")
}
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event
class PodEvent(
val type: PodEventType,
val data: Any?
)

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event
enum class PodEventType {
SCANNING,
PAIRING,
CONNECTING,
CONNECTED,
COMMAND_SENDING,
COMMAND_SENT,
RESPONSE_RECEIVED,
// ...
}

View file

@ -1,41 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
public final class DeactivateCommand extends NonceEnabledCommand {
private static final short LENGTH = 6;
private static final byte BODY_LENGTH = 4;
DeactivateCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int nonce) {
super(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce);
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.array());
}
@Override public String toString() {
return "DeactivateCommand{" +
"nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, DeactivateCommand> {
@Override protected final DeactivateCommand buildCommand() {
return new DeactivateCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce);
}
}
}

View file

@ -0,0 +1,42 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
import java.nio.ByteBuffer
class DeactivateCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
nonce: Int
) : NonceEnabledCommand(CommandType.DEACTIVATE, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
override val encoded: ByteArray
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.array())
override fun toString(): String = "DeactivateCommand{" +
"nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
class Builder : NonceEnabledCommandBuilder<Builder, DeactivateCommand>() {
override fun buildCommand(): DeactivateCommand =
DeactivateCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!) // TODO this might crash if not all are set
}
companion object {
private const val LENGTH: Short = 6
private const val BODY_LENGTH: Byte = 4
}
}

View file

@ -1,42 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
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.HeaderEnabledCommandBuilder;
public final class GetVersionCommand extends HeaderEnabledCommand {
public static final int DEFAULT_UNIQUE_ID = -1; // FIXME move
private static final short LENGTH = 6;
private static final byte BODY_LENGTH = 4;
GetVersionCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag) {
super(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag);
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(uniqueId) //
.array());
}
@Override public String toString() {
return "GetVersionCommand{" +
"commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends HeaderEnabledCommandBuilder<Builder, GetVersionCommand> {
@Override protected final GetVersionCommand buildCommand() {
return new GetVersionCommand(uniqueId, sequenceNumber, multiCommandFlag);
}
}
}

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.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.HeaderEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.HeaderEnabledCommandBuilder
import java.nio.ByteBuffer
class GetVersionCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean
) : HeaderEnabledCommand(CommandType.GET_VERSION, uniqueId, sequenceNumber, multiCommandFlag) {
override val encoded: ByteArray
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.putInt(uniqueId) //
.array())
override fun toString(): String {
return "GetVersionCommand{" +
"commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class Builder : HeaderEnabledCommandBuilder<Builder, GetVersionCommand>() {
override fun buildCommand(): GetVersionCommand {
return GetVersionCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag)
}
}
companion object {
const val DEFAULT_UNIQUE_ID = -1 // FIXME move
private const val LENGTH: Short = 6
private const val BODY_LENGTH: Byte = 4
}
}

View file

@ -1,66 +0,0 @@
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.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration;
public final class ProgramAlertsCommand extends NonceEnabledCommand {
private final List<AlertConfiguration> alertConfigurations;
ProgramAlertsCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, List<AlertConfiguration> alertConfigurations, int nonce) {
super(CommandType.PROGRAM_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.alertConfigurations = new ArrayList<>(alertConfigurations);
}
@Override public byte[] getEncoded() {
ByteBuffer byteBuffer = ByteBuffer.allocate(getLength() + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, getLength(), multiCommandFlag)) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.putInt(nonce);
for (AlertConfiguration configuration : alertConfigurations) {
byteBuffer.put(configuration.getEncoded());
}
return appendCrc(byteBuffer.array());
}
private short getLength() {
return (short) (alertConfigurations.size() * 6 + 6);
}
private byte getBodyLength() {
return (byte) (alertConfigurations.size() * 6 + 4);
}
@Override public String toString() {
return "ProgramAlertsCommand{" +
"alertConfigurations=" + alertConfigurations +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramAlertsCommand> {
private List<AlertConfiguration> alertConfigurations;
public Builder setAlertConfigurations(List<AlertConfiguration> alertConfigurations) {
this.alertConfigurations = alertConfigurations;
return this;
}
@Override protected final ProgramAlertsCommand buildCommand() {
if (this.alertConfigurations == null) {
throw new IllegalArgumentException("alertConfigurations can not be null");
}
return new ProgramAlertsCommand(uniqueId, sequenceNumber, multiCommandFlag, alertConfigurations, nonce);
}
}
}

View file

@ -0,0 +1,69 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertConfiguration
import java.nio.ByteBuffer
import java.util.*
class ProgramAlertsCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
alertConfigurations: List<AlertConfiguration>?,
nonce: Int
) : NonceEnabledCommand(CommandType.PROGRAM_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
private val alertConfigurations: List<AlertConfiguration>
private fun getLength(): Short {
return (alertConfigurations.size * 6 + 6).toShort()
}
private fun getBodyLength(): Byte {
return (alertConfigurations.size * 6 + 4).toByte()
}
override val encoded: ByteArray
get() {
val byteBuffer: ByteBuffer = ByteBuffer.allocate(getLength() + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, getLength(), multiCommandFlag)) //
.put(commandType.value) //
.put(getBodyLength()) //
.putInt(nonce)
for (configuration in alertConfigurations) {
byteBuffer.put(configuration.encoded)
}
return appendCrc(byteBuffer.array())
}
override fun toString(): String {
return "ProgramAlertsCommand{" +
"alertConfigurations=" + alertConfigurations +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class Builder : NonceEnabledCommandBuilder<Builder, ProgramAlertsCommand>() {
private var alertConfigurations: List<AlertConfiguration>? = null
fun setAlertConfigurations(alertConfigurations: List<AlertConfiguration>?): Builder {
this.alertConfigurations = alertConfigurations
return this
}
override fun buildCommand(): ProgramAlertsCommand {
requireNotNull(alertConfigurations) { "alertConfigurations can not be null" } // !!?
return ProgramAlertsCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, alertConfigurations, nonce!!) // TODO this might crash if not all are set
}
}
init {
this.alertConfigurations = ArrayList(alertConfigurations)
}
}

View file

@ -1,132 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.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.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.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;
// Always preceded by 0x1a ProgramInsulinCommand
public final class ProgramBasalCommand extends HeaderEnabledCommand {
private final ProgramInsulinCommand interlockCommand;
private final List<BasalInsulinProgramElement> insulinProgramElements;
private final ProgramReminder programReminder;
private final byte currentInsulinProgramElementIndex;
private final short remainingTenthPulsesInCurrentInsulinProgramElement;
private final int delayUntilNextTenthPulseInUsec;
ProgramBasalCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag, List<BasalInsulinProgramElement> insulinProgramElements, ProgramReminder programReminder, byte currentInsulinProgramElementIndex, short remainingTenthPulsesInCurrentInsulinProgramElement, int delayUntilNextTenthPulseInUsec) {
super(CommandType.PROGRAM_BASAL, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
this.programReminder = programReminder;
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex;
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement;
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec;
}
short getLength() {
return (short) (insulinProgramElements.size() * 6 + 10);
}
byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 6 + 8);
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(getLength()) //
.put(getCommandType().getValue()) //
.put(getBodyLength()) //
.put(programReminder.getEncoded()) //
.put(currentInsulinProgramElementIndex) //
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
.putInt(delayUntilNextTenthPulseInUsec);
for (BasalInsulinProgramElement insulinProgramElement : insulinProgramElements) {
buffer.put(insulinProgramElement.getEncoded());
}
byte[] basalCommand = buffer.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (basalCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(basalCommand.length + interlockCommand.length + header.length) //
.put(header) //
.put(interlockCommand) //
.put(basalCommand) //
.array());
}
@Override public String toString() {
return "ProgramBasalCommand{" +
"interlockCommand=" + interlockCommand +
", insulinProgramElements=" + insulinProgramElements +
", programReminder=" + programReminder +
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBasalCommand> {
private BasalProgram basalProgram;
private ProgramReminder programReminder;
private Date currentTime;
public Builder setBasalProgram(BasalProgram basalProgram) {
this.basalProgram = basalProgram;
return this;
}
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
public Builder setCurrentTime(Date currentTime) {
this.currentTime = currentTime;
return this;
}
@Override protected ProgramBasalCommand buildCommand() {
if (basalProgram == null) {
throw new IllegalArgumentException("basalProgram can not be null");
}
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
if (currentTime == null) {
throw new IllegalArgumentException("currentTime can not be null");
}
short[] pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram);
CurrentSlot currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime);
short checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot);
List<BasalInsulinProgramElement> longInsulinProgramElements = ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram));
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
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, currentBasalInsulinProgramElement.getIndex(),
currentBasalInsulinProgramElement.getRemainingTenthPulses(), currentBasalInsulinProgramElement.getDelayUntilNextTenthPulseInUsec());
}
}
}

View file

@ -0,0 +1,120 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.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.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.util.ProgramBasalUtil
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements
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 java.nio.ByteBuffer
import java.util.*
// Always preceded by 0x1a ProgramInsulinCommand
class ProgramBasalCommand internal constructor(
private val interlockCommand: ProgramInsulinCommand,
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
insulinProgramElements: List<BasalInsulinProgramElement>,
programReminder: ProgramReminder,
currentInsulinProgramElementIndex: Byte,
remainingTenthPulsesInCurrentInsulinProgramElement: Short,
delayUntilNextTenthPulseInUsec: Int
) : HeaderEnabledCommand(CommandType.PROGRAM_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
private val insulinProgramElements: List<BasalInsulinProgramElement>
private val programReminder: ProgramReminder
private val currentInsulinProgramElementIndex: Byte
private val remainingTenthPulsesInCurrentInsulinProgramElement: Short
private val delayUntilNextTenthPulseInUsec: Int
val length: Short
get() = (insulinProgramElements.size * 6 + 10).toShort()
val bodyLength: Byte
get() = (insulinProgramElements.size * 6 + 8).toByte()
override val encoded: ByteArray
get() {
val buffer = ByteBuffer.allocate(length.toInt()) //
.put(commandType.value) //
.put(bodyLength) //
.put(programReminder.encoded) //
.put(currentInsulinProgramElementIndex) //
.putShort(remainingTenthPulsesInCurrentInsulinProgramElement) //
.putInt(delayUntilNextTenthPulseInUsec)
for (insulinProgramElement in insulinProgramElements) {
buffer.put(insulinProgramElement.encoded)
}
val basalCommand = buffer.array()
val interlockCommand = interlockCommand.encoded
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (basalCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
return appendCrc(ByteBuffer.allocate(basalCommand.size + interlockCommand.size + header.size) //
.put(header) //
.put(interlockCommand) //
.put(basalCommand) //
.array())
}
override fun toString(): String {
return "ProgramBasalCommand{" +
"interlockCommand=" + interlockCommand +
", insulinProgramElements=" + insulinProgramElements +
", programReminder=" + programReminder +
", currentInsulinProgramElementIndex=" + currentInsulinProgramElementIndex +
", remainingTenthPulsesInCurrentInsulinProgramElement=" + remainingTenthPulsesInCurrentInsulinProgramElement +
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class Builder : NonceEnabledCommandBuilder<Builder, ProgramBasalCommand>() {
private var basalProgram: BasalProgram? = null
private var programReminder: ProgramReminder? = null
private var currentTime: Date? = null
fun setBasalProgram(basalProgram: BasalProgram?): Builder {
this.basalProgram = basalProgram
return this
}
fun setProgramReminder(programReminder: ProgramReminder?): Builder {
this.programReminder = programReminder
return this
}
fun setCurrentTime(currentTime: Date?): Builder {
this.currentTime = currentTime
return this
}
override fun buildCommand(): ProgramBasalCommand {
requireNotNull(basalProgram) { "basalProgram can not be null" }
requireNotNull(programReminder) { "programReminder can not be null" }
requireNotNull(currentTime) { "currentTime can not be null" }
val pulsesPerSlot = ProgramBasalUtil.mapBasalProgramToPulsesPerSlot(basalProgram!!)
val currentSlot = ProgramBasalUtil.calculateCurrentSlot(pulsesPerSlot, currentTime)
val checksum = ProgramBasalUtil.calculateChecksum(pulsesPerSlot, currentSlot)
val longInsulinProgramElements: List<BasalInsulinProgramElement> = mapTenthPulsesPerSlotToLongInsulinProgramElements(ProgramBasalUtil.mapBasalProgramToTenthPulsesPerSlot(basalProgram!!))
val shortInsulinProgramElements = ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
val currentBasalInsulinProgramElement = ProgramBasalUtil.calculateCurrentLongInsulinProgramElement(longInsulinProgramElements, currentTime)
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!,
shortInsulinProgramElements, checksum, currentSlot.index, currentSlot.eighthSecondsRemaining,
currentSlot.pulsesRemaining, ProgramInsulinCommand.DeliveryType.BASAL)
return ProgramBasalCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag,
longInsulinProgramElements, programReminder!!, currentBasalInsulinProgramElement.index,
currentBasalInsulinProgramElement.remainingTenthPulses, currentBasalInsulinProgramElement.delayUntilNextTenthPulseInUsec)
}
}
init {
this.insulinProgramElements = ArrayList(insulinProgramElements)
this.programReminder = programReminder
this.currentInsulinProgramElementIndex = currentInsulinProgramElementIndex
this.remainingTenthPulsesInCurrentInsulinProgramElement = remainingTenthPulsesInCurrentInsulinProgramElement
this.delayUntilNextTenthPulseInUsec = delayUntilNextTenthPulseInUsec
}
}

View file

@ -1,123 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.Collections;
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.BolusShortInsulinProgramElement;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
// NOT SUPPORTED: extended bolus
public final class ProgramBolusCommand extends HeaderEnabledCommand {
private static final short LENGTH = 15;
private static final byte BODY_LENGTH = 13;
private final ProgramInsulinCommand interlockCommand;
private final ProgramReminder programReminder;
private final short numberOfTenthPulses;
private final int delayUntilFirstTenthPulseInUsec;
ProgramBolusCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag, ProgramReminder programReminder, short numberOfTenthPulses, int delayUntilFirstTenthPulseInUsec) {
super(CommandType.PROGRAM_BOLUS, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.programReminder = programReminder;
this.numberOfTenthPulses = numberOfTenthPulses;
this.delayUntilFirstTenthPulseInUsec = delayUntilFirstTenthPulseInUsec;
}
@Override public byte[] getEncoded() {
byte[] bolusCommand = ByteBuffer.allocate(LENGTH) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.put(programReminder.getEncoded()) //
.putShort(numberOfTenthPulses) //
.putInt(delayUntilFirstTenthPulseInUsec) //
.putShort((short) 0) // Extended bolus pulses
.putInt(0) // Delay between tenth extended pulses in usec
.array();
byte[] interlockCommand = this.interlockCommand.getEncoded();
byte[] header = encodeHeader(uniqueId, sequenceNumber, (short) (bolusCommand.length + interlockCommand.length), multiCommandFlag);
return appendCrc(ByteBuffer.allocate(header.length + interlockCommand.length + bolusCommand.length) //
.put(header) //
.put(interlockCommand) //
.put(bolusCommand) //
.array());
}
@Override public String toString() {
return "ProgramBolusCommand{" +
"interlockCommand=" + interlockCommand +
", programReminder=" + programReminder +
", numberOfTenthPulses=" + numberOfTenthPulses +
", delayUntilFirstTenthPulseInUsec=" + delayUntilFirstTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, ProgramBolusCommand> {
private Double numberOfUnits;
private Byte delayBetweenPulsesInEighthSeconds;
private ProgramReminder programReminder;
public Builder setNumberOfUnits(double numberOfUnits) {
if (numberOfUnits <= 0.0D) {
throw new IllegalArgumentException("Number of units should be greater than zero");
}
if ((int) (numberOfUnits * 1000) % 50 != 0) {
throw new IllegalArgumentException("Number of units must be dividable by 0.05");
}
this.numberOfUnits = ((int) (numberOfUnits * 100)) / 100.0d;
return this;
}
public Builder setDelayBetweenPulsesInEighthSeconds(byte delayBetweenPulsesInEighthSeconds) {
this.delayBetweenPulsesInEighthSeconds = delayBetweenPulsesInEighthSeconds;
return this;
}
public Builder setProgramReminder(ProgramReminder programReminder) {
this.programReminder = programReminder;
return this;
}
@Override protected ProgramBolusCommand buildCommand() {
if (numberOfUnits == null) {
throw new IllegalArgumentException("numberOfUnits can not be null");
}
if (delayBetweenPulsesInEighthSeconds == null) {
throw new IllegalArgumentException("delayBetweenPulsesInEighthSeconds can not be null");
}
if (programReminder == null) {
throw new IllegalArgumentException("programReminder can not be null");
}
short numberOfPulses = (short) Math.round(numberOfUnits * 20);
short byte10And11 = (short) (numberOfPulses * delayBetweenPulsesInEighthSeconds);
ProgramInsulinCommand interlockCommand = new ProgramInsulinCommand(uniqueId, sequenceNumber, multiCommandFlag, nonce,
Collections.singletonList(new BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum((byte) 0x01, byte10And11, numberOfPulses),
(byte) 0x01, byte10And11, (short) numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS);
int delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds / 8 * 100_000;
return new ProgramBolusCommand(interlockCommand, uniqueId, sequenceNumber, multiCommandFlag, programReminder, (short) (numberOfPulses * 10), delayUntilFirstTenthPulseInUsec);
}
}
private static short calculateChecksum(byte numberOfSlots, short byte10And11, short numberOfPulses) {
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
.put(numberOfSlots) //
.putShort(byte10And11) //
.putShort(numberOfPulses) //
.putShort(numberOfPulses) //
.array());
}
}

View file

@ -0,0 +1,103 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.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.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.BolusShortInsulinProgramElement
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ProgramReminder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil
import java.nio.ByteBuffer
// NOT SUPPORTED: extended bolus
class ProgramBolusCommand internal constructor(
private val interlockCommand: ProgramInsulinCommand,
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val programReminder: ProgramReminder,
private val numberOfTenthPulses: Short,
private val delayUntilFirstTenthPulseInUsec: Int
) : HeaderEnabledCommand(CommandType.PROGRAM_BOLUS, uniqueId, sequenceNumber, multiCommandFlag) {
override val encoded: ByteArray
get() {
val bolusCommand = ByteBuffer.allocate(LENGTH.toInt()) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.put(programReminder.encoded) //
.putShort(numberOfTenthPulses) //
.putInt(delayUntilFirstTenthPulseInUsec) //
.putShort(0.toShort()) // Extended bolus pulses
.putInt(0) // Delay between tenth extended pulses in usec
.array()
val interlockCommand = interlockCommand.encoded
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (bolusCommand.size + interlockCommand.size).toShort(), multiCommandFlag)
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + bolusCommand.size) //
.put(header) //
.put(interlockCommand) //
.put(bolusCommand) //
.array())
}
override fun toString(): String {
return "ProgramBolusCommand{" +
"interlockCommand=" + interlockCommand +
", programReminder=" + programReminder +
", numberOfTenthPulses=" + numberOfTenthPulses +
", delayUntilFirstTenthPulseInUsec=" + delayUntilFirstTenthPulseInUsec +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class Builder : NonceEnabledCommandBuilder<Builder, ProgramBolusCommand>() {
private var numberOfUnits: Double? = null
private var delayBetweenPulsesInEighthSeconds: Byte? = null
private var programReminder: ProgramReminder? = null
fun setNumberOfUnits(numberOfUnits: Double): Builder {
require(numberOfUnits > 0.0) { "Number of units should be greater than zero" }
require((numberOfUnits * 1000).toInt() % 50 == 0) { "Number of units must be dividable by 0.05" }
this.numberOfUnits = (numberOfUnits * 100).toInt() / 100.0
return this
}
fun setDelayBetweenPulsesInEighthSeconds(delayBetweenPulsesInEighthSeconds: Byte): Builder {
this.delayBetweenPulsesInEighthSeconds = delayBetweenPulsesInEighthSeconds
return this
}
fun setProgramReminder(programReminder: ProgramReminder?): Builder {
this.programReminder = programReminder
return this
}
override fun buildCommand(): ProgramBolusCommand {
requireNotNull(numberOfUnits) { "numberOfUnits can not be null" }
requireNotNull(delayBetweenPulsesInEighthSeconds) { "delayBetweenPulsesInEighthSeconds can not be null" }
requireNotNull(programReminder) { "programReminder can not be null" }
val numberOfPulses = Math.round(numberOfUnits!! * 20).toShort()
val byte10And11 = (numberOfPulses * delayBetweenPulsesInEighthSeconds!!).toShort()
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!, listOf(BolusShortInsulinProgramElement(numberOfPulses)), calculateChecksum(0x01.toByte(), byte10And11, numberOfPulses),
0x01.toByte(), byte10And11, numberOfPulses, ProgramInsulinCommand.DeliveryType.BOLUS)
val delayUntilFirstTenthPulseInUsec = delayBetweenPulsesInEighthSeconds!! / 8 * 100000
return ProgramBolusCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag, programReminder!!, (numberOfPulses * 10).toShort(), delayUntilFirstTenthPulseInUsec)
}
}
companion object {
private const val LENGTH: Short = 15
private const val BODY_LENGTH: Byte = 13
private fun calculateChecksum(numberOfSlots: Byte, byte10And11: Short, numberOfPulses: Short): Short {
return MessageUtil.calculateChecksum(ByteBuffer.allocate(7) //
.put(numberOfSlots) //
.putShort(byte10And11) //
.putShort(numberOfPulses) //
.putShort(numberOfPulses) //
.array())
}
}
}

View file

@ -1,96 +0,0 @@
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.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement;
// Always followed by one of: 0x13, 0x16, 0x17
final class ProgramInsulinCommand extends NonceEnabledCommand {
private final List<ShortInsulinProgramElement> insulinProgramElements;
private final short checksum;
private final byte byte9;
private final short byte10And11;
private final short byte12And13;
private final DeliveryType deliveryType;
ProgramInsulinCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int nonce, List<ShortInsulinProgramElement> insulinProgramElements, short checksum, byte byte9, short byte10And11, short byte12And13, DeliveryType deliveryType) {
super(CommandType.PROGRAM_INSULIN, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
this.checksum = checksum;
this.byte9 = byte9;
this.byte10And11 = byte10And11;
this.byte12And13 = byte12And13;
this.deliveryType = deliveryType;
}
public short getLength() {
return (short) (insulinProgramElements.size() * 2 + 14);
}
public byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 2 + 12);
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(this.getLength()) //
.put(commandType.getValue()) //
.put(getBodyLength()) //
.putInt(nonce) //
.put(deliveryType.getValue()) //
.putShort(checksum) //
.put(byte9) // BASAL: currentSlot // BOLUS: number of ShortInsulinProgramElements
.putShort(byte10And11) // BASAL: remainingEighthSecondsInCurrentSlot // BOLUS: immediate pulses multiplied by delay between pulses in eighth seconds
.putShort(byte12And13); // BASAL: remainingPulsesInCurrentSlot // BOLUS: immediate pulses
for (ShortInsulinProgramElement element : insulinProgramElements) {
buffer.put(element.getEncoded());
}
return buffer.array();
}
enum DeliveryType {
BASAL((byte) 0x00),
TEMP_BASAL((byte) 0x01),
BOLUS((byte) 0x02);
private final byte value;
DeliveryType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}
public short calculateChecksum(byte[] bytes) {
short sum = 0;
for (byte b : bytes) {
sum += (short) (b & 0xff);
}
return sum;
}
@Override public String toString() {
return "ProgramInsulinCommand{" +
"insulinProgramElements=" + insulinProgramElements +
", checksum=" + checksum +
", byte9=" + byte9 +
", byte10And11=" + byte10And11 +
", byte12And13=" + byte12And13 +
", deliveryType=" + deliveryType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
}

View file

@ -0,0 +1,77 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.ShortInsulinProgramElement
import java.nio.ByteBuffer
import java.util.*
// Always followed by one of: 0x13, 0x16, 0x17
class ProgramInsulinCommand(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
nonce: Int, insulinProgramElements:
List<ShortInsulinProgramElement>,
private val checksum: Short,
private val byte9: Byte,
private val byte10And11: Short,
private val byte12And13: Short,
private val deliveryType: DeliveryType
) : NonceEnabledCommand(CommandType.PROGRAM_INSULIN, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
private val insulinProgramElements: List<ShortInsulinProgramElement> = ArrayList(insulinProgramElements)
fun getLength(): Short = (insulinProgramElements.size * 2 + 14).toShort()
fun getBodyLength(): Byte = (insulinProgramElements.size * 2 + 12).toByte()
enum class DeliveryType(private val value: Byte) {
BASAL(0x00.toByte()), TEMP_BASAL(0x01.toByte()), BOLUS(0x02.toByte());
fun getValue(): Byte {
return value
}
}
fun calculateChecksum(bytes: ByteArray): Short {
var sum: Short = 0
for (b in bytes) {
sum = ((b.toInt() and 0xff) + sum).toShort() // TODO Adrian: int conversion ok?
}
return sum
}
override val encoded: ByteArray
get() {
val buffer = ByteBuffer.allocate(getLength().toInt()) //
.put(commandType.value) //
.put(getBodyLength()) //
.putInt(nonce) //
.put(deliveryType.getValue()) //
.putShort(checksum) //
.put(byte9) // BASAL: currentSlot // BOLUS: number of ShortInsulinProgramElements
.putShort(byte10And11) // BASAL: remainingEighthSecondsInCurrentSlot // BOLUS: immediate pulses multiplied by delay between pulses in eighth seconds
.putShort(byte12And13) // BASAL: remainingPulsesInCurrentSlot // BOLUS: immediate pulses
for (element in insulinProgramElements) {
buffer.put(element.encoded)
}
return buffer.array()
}
override fun toString(): String {
return "ProgramInsulinCommand{" +
"insulinProgramElements=" + insulinProgramElements +
", checksum=" + checksum +
", byte9=" + byte9 +
", byte10And11=" + byte10And11 +
", byte12And13=" + byte12And13 +
", deliveryType=" + deliveryType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
}

View file

@ -1,123 +0,0 @@
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;
protected ProgramTempBasalCommand(ProgramInsulinCommand interlockCommand, int uniqueId, short sequenceNumber, boolean multiCommandFlag,
ProgramReminder programReminder, List<BasalInsulinProgramElement> insulinProgramElements) {
super(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag);
this.interlockCommand = interlockCommand;
this.programReminder = programReminder;
this.insulinProgramElements = new ArrayList<>(insulinProgramElements);
}
public byte getBodyLength() {
return (byte) (insulinProgramElements.size() * 6 + 8);
}
public short getLength() {
return (short) (getBodyLength() + 2);
}
@Override public byte[] getEncoded() {
BasalInsulinProgramElement firstProgramElement = insulinProgramElements.get(0);
short remainingTenthPulsesInFirstElement;
int delayUntilNextTenthPulseInUsec;
if (firstProgramElement.getTotalTenthPulses() == 0) {
remainingTenthPulsesInFirstElement = firstProgramElement.getNumberOfSlots();
delayUntilNextTenthPulseInUsec = ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT;
} else {
remainingTenthPulsesInFirstElement = firstProgramElement.getTotalTenthPulses();
delayUntilNextTenthPulseInUsec = (int) ((long) 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);
List<ShortInsulinProgramElement> shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot);
List<BasalInsulinProgramElement> insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot);
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);
}
}
}

View file

@ -0,0 +1,100 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.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.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.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
import java.nio.ByteBuffer
import java.util.*
// NOT SUPPORTED: percentage temp basal
class ProgramTempBasalCommand protected constructor(
private val interlockCommand: ProgramInsulinCommand,
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val programReminder: ProgramReminder,
insulinProgramElements: List<BasalInsulinProgramElement>
) : HeaderEnabledCommand(CommandType.PROGRAM_TEMP_BASAL, uniqueId, sequenceNumber, multiCommandFlag) {
private val insulinProgramElements: List<BasalInsulinProgramElement>
fun getBodyLength(): Byte = (insulinProgramElements.size * 6 + 8).toByte()
fun getLength(): Short = (getBodyLength() + 2).toShort()
class Builder : NonceEnabledCommandBuilder<Builder, ProgramTempBasalCommand>() {
private var programReminder: ProgramReminder? = null
private var rateInUnitsPerHour: Double? = null
private var durationInMinutes: Short? = null
fun setProgramReminder(programReminder: ProgramReminder?): Builder {
this.programReminder = programReminder
return this
}
fun setRateInUnitsPerHour(rateInUnitsPerHour: Double): Builder {
this.rateInUnitsPerHour = rateInUnitsPerHour
return this
}
fun setDurationInMinutes(durationInMinutes: Short): Builder {
require(durationInMinutes % 30 == 0) { "durationInMinutes must be dividable by 30" }
this.durationInMinutes = durationInMinutes
return this
}
override fun buildCommand(): ProgramTempBasalCommand {
requireNotNull(programReminder) { "programReminder can not be null" }
requireNotNull(rateInUnitsPerHour) { "rateInUnitsPerHour can not be null" }
requireNotNull(durationInMinutes) { "durationInMinutes can not be null" }
val durationInSlots = (durationInMinutes!! / 30).toByte()
val pulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToPulsesPerSlot(durationInSlots, rateInUnitsPerHour!!)
val tenthPulsesPerSlot = ProgramTempBasalUtil.mapTempBasalToTenthPulsesPerSlot(durationInSlots.toInt(), rateInUnitsPerHour!!)
val shortInsulinProgramElements = ProgramTempBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
val insulinProgramElements = ProgramTempBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot)
val interlockCommand = ProgramInsulinCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, nonce!!, shortInsulinProgramElements,
ProgramTempBasalUtil.calculateChecksum(durationInSlots, pulsesPerSlot!![0], pulsesPerSlot), durationInSlots,
0x3840.toShort(), pulsesPerSlot[0], ProgramInsulinCommand.DeliveryType.TEMP_BASAL)
return ProgramTempBasalCommand(interlockCommand, uniqueId!!, sequenceNumber!!, multiCommandFlag, programReminder!!, insulinProgramElements)
}
}
init {
this.insulinProgramElements = ArrayList(insulinProgramElements)
}
override val encoded: ByteArray
get() {
val firstProgramElement = insulinProgramElements[0]
val remainingTenthPulsesInFirstElement: Short
val delayUntilNextTenthPulseInUsec: Int
if (firstProgramElement.totalTenthPulses.toInt() == 0) {
remainingTenthPulsesInFirstElement = firstProgramElement.numberOfSlots.toShort()
delayUntilNextTenthPulseInUsec = ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT
} else {
remainingTenthPulsesInFirstElement = firstProgramElement.totalTenthPulses
delayUntilNextTenthPulseInUsec = (firstProgramElement.numberOfSlots.toLong() * 1800.0 / remainingTenthPulsesInFirstElement * 1000000).toInt()
}
val buffer = ByteBuffer.allocate(getLength().toInt()) //
.put(commandType.value) //
.put(getBodyLength()) //
.put(programReminder.encoded) //
.put(0x00.toByte()) // Current slot index
.putShort(remainingTenthPulsesInFirstElement) //
.putInt(delayUntilNextTenthPulseInUsec)
for (element in insulinProgramElements) {
buffer.put(element.encoded)
}
val tempBasalCommand = buffer.array()
val interlockCommand = interlockCommand.encoded
val header: ByteArray = encodeHeader(uniqueId, sequenceNumber, (tempBasalCommand.size + interlockCommand!!.size).toShort(), multiCommandFlag)
return appendCrc(ByteBuffer.allocate(header.size + interlockCommand.size + tempBasalCommand.size) //
.put(header) //
.put(interlockCommand) //
.put(tempBasalCommand) //
.array())
}
}

View file

@ -1,99 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.Calendar;
import java.util.Date;
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.HeaderEnabledCommandBuilder;
public final class SetUniqueIdCommand extends HeaderEnabledCommand {
private static final int DEFAULT_UNIQUE_ID = -1;
private static final short LENGTH = 21;
private static final byte BODY_LENGTH = 19;
private final int lotNumber;
private final int podSequenceNumber;
private final Date initializationTime;
SetUniqueIdCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, int lotNumber, int podSequenceNumber, Date initializationTime) {
super(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag);
this.lotNumber = lotNumber;
this.podSequenceNumber = podSequenceNumber;
this.initializationTime = initializationTime;
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(uniqueId) //
.put((byte) 0x14) // FIXME ??
.put((byte) 0x04) // FIXME ??
.put(encodeInitializationTime(initializationTime)) //
.putInt(lotNumber) //
.putInt(podSequenceNumber) //
.array());
}
private static byte[] encodeInitializationTime(Date date) {
Calendar instance = Calendar.getInstance();
instance.setTime(date);
return new byte[]{ //
(byte) (instance.get(Calendar.MONTH) + 1), //
(byte) instance.get(Calendar.DATE), //
(byte) (instance.get(Calendar.YEAR) % 100), //
(byte) instance.get(Calendar.HOUR_OF_DAY), //
(byte) instance.get(Calendar.MINUTE) //
};
}
@Override public String toString() {
return "SetUniqueIdCommand{" +
"lotNumber=" + lotNumber +
", podSequenceNumber=" + podSequenceNumber +
", initializationTime=" + initializationTime +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public static final class Builder extends HeaderEnabledCommandBuilder<Builder, SetUniqueIdCommand> {
private Integer lotNumber;
private Integer podSequenceNumber;
private Date initializationTime;
public Builder setLotNumber(int lotNumber) {
this.lotNumber = lotNumber;
return this;
}
public Builder setPodSequenceNumber(int podSequenceNumber) {
this.podSequenceNumber = podSequenceNumber;
return this;
}
public Builder setInitializationTime(Date initializationTime) {
this.initializationTime = initializationTime;
return this;
}
@Override protected final SetUniqueIdCommand buildCommand() {
if (lotNumber == null) {
throw new IllegalArgumentException("lotNumber can not be null");
}
if (podSequenceNumber == null) {
throw new IllegalArgumentException("podSequenceNumber can not be null");
}
if (initializationTime == null) {
throw new IllegalArgumentException("initializationTime can not be null");
}
return new SetUniqueIdCommand(uniqueId, sequenceNumber, multiCommandFlag, lotNumber, podSequenceNumber, initializationTime);
}
}
}

View file

@ -0,0 +1,88 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.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.HeaderEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.HeaderEnabledCommandBuilder
import java.nio.ByteBuffer
import java.util.*
class SetUniqueIdCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val lotNumber: Int,
private val podSequenceNumber: Int,
private val initializationTime: Date
) : HeaderEnabledCommand(CommandType.SET_UNIQUE_ID, uniqueId, sequenceNumber, multiCommandFlag) {
override val encoded: ByteArray
get() = appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(DEFAULT_UNIQUE_ID, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.putInt(uniqueId) //
.put(0x14.toByte()) // FIXME ??
.put(0x04.toByte()) // FIXME ??
.put(encodeInitializationTime(initializationTime)) //
.putInt(lotNumber) //
.putInt(podSequenceNumber) //
.array())
override fun toString(): String {
return "SetUniqueIdCommand{" +
"lotNumber=" + lotNumber +
", podSequenceNumber=" + podSequenceNumber +
", initializationTime=" + initializationTime +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class Builder : HeaderEnabledCommandBuilder<Builder, SetUniqueIdCommand>() {
private var lotNumber: Int? = null
private var podSequenceNumber: Int? = null
private var initializationTime: Date? = null
fun setLotNumber(lotNumber: Int): Builder {
this.lotNumber = lotNumber
return this
}
fun setPodSequenceNumber(podSequenceNumber: Int): Builder {
this.podSequenceNumber = podSequenceNumber
return this
}
fun setInitializationTime(initializationTime: Date?): Builder {
this.initializationTime = initializationTime
return this
}
override fun buildCommand(): SetUniqueIdCommand {
requireNotNull(lotNumber) { "lotNumber can not be null" }
requireNotNull(podSequenceNumber) { "podSequenceNumber can not be null" }
requireNotNull(initializationTime) { "initializationTime can not be null" }
return SetUniqueIdCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, lotNumber!!, podSequenceNumber!!, initializationTime!!)
}
}
companion object {
private const val DEFAULT_UNIQUE_ID = -1
private const val LENGTH: Short = 21
private const val BODY_LENGTH: Byte = 19
private fun encodeInitializationTime(date: Date): ByteArray {
val instance = Calendar.getInstance()
instance.time = date
return byteArrayOf( //
(instance[Calendar.MONTH] + 1).toByte(), //
instance[Calendar.DATE].toByte(), //
(instance[Calendar.YEAR] % 100).toByte(), //
instance[Calendar.HOUR_OF_DAY].toByte(), //
instance[Calendar.MINUTE].toByte() //
)
}
}
}

View file

@ -1,133 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.BitSet;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public final class SilenceAlertsCommand extends NonceEnabledCommand {
private static final short LENGTH = (short) 7;
private static final byte BODY_LENGTH = (byte) 5;
private final SilenceAlertCommandParameters parameters;
SilenceAlertsCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, SilenceAlertCommandParameters parameters, int nonce) {
super(CommandType.SILENCE_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.parameters = parameters;
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.put(parameters.getEncoded()) //
.array());
}
@Override public String toString() {
return "SilenceAlertsCommand{" +
"parameters=" + parameters +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
private static final class SilenceAlertCommandParameters implements Encodable {
private final boolean silenceAutoOffAlert;
private final boolean silenceMultiCommandAlert;
private final boolean silenceExpirationImminentAlert;
private final boolean silenceUserSetExpirationAlert;
private final boolean silenceLowReservoirAlert;
private final boolean silenceSuspendInProgressAlert;
private final boolean silenceSuspendEndedAlert;
private final boolean silencePodExpirationAlert;
private SilenceAlertCommandParameters(boolean silenceAutoOffAlert, boolean silenceMultiCommandAlert, boolean silenceExpirationImminentAlert, boolean silenceUserSetExpirationAlert, boolean silenceLowReservoirAlert, boolean silenceSuspendInProgressAlert, boolean silenceSuspendEndedAlert, boolean silencePodExpirationAlert) {
this.silenceAutoOffAlert = silenceAutoOffAlert;
this.silenceMultiCommandAlert = silenceMultiCommandAlert;
this.silenceExpirationImminentAlert = silenceExpirationImminentAlert;
this.silenceUserSetExpirationAlert = silenceUserSetExpirationAlert;
this.silenceLowReservoirAlert = silenceLowReservoirAlert;
this.silenceSuspendInProgressAlert = silenceSuspendInProgressAlert;
this.silenceSuspendEndedAlert = silenceSuspendEndedAlert;
this.silencePodExpirationAlert = silencePodExpirationAlert;
}
@Override
public byte[] getEncoded() {
BitSet bitSet = new BitSet(8);
bitSet.set(0, this.silenceAutoOffAlert);
bitSet.set(1, this.silenceMultiCommandAlert);
bitSet.set(2, this.silenceExpirationImminentAlert);
bitSet.set(3, this.silenceUserSetExpirationAlert);
bitSet.set(4, this.silenceLowReservoirAlert);
bitSet.set(5, this.silenceSuspendInProgressAlert);
bitSet.set(6, this.silenceSuspendEndedAlert);
bitSet.set(7, this.silencePodExpirationAlert);
return bitSet.toByteArray();
}
}
public static class Builder extends NonceEnabledCommandBuilder<Builder, SilenceAlertsCommand> {
private boolean silenceAutoOffAlert;
private boolean silenceMultiCommandAlert;
private boolean silenceExpirationImminentAlert;
private boolean silenceUserSetExpirationAlert;
private boolean silenceLowReservoirAlert;
private boolean silenceSuspendInProgressAlert;
private boolean silenceSuspendEndedAlert;
private boolean silencePodExpirationAlert;
public Builder setSilenceAutoOffAlert(boolean silenceAutoOffAlert) {
this.silenceAutoOffAlert = silenceAutoOffAlert;
return this;
}
public Builder setSilenceMultiCommandAlert(boolean silenceMultiCommandAlert) {
this.silenceMultiCommandAlert = silenceMultiCommandAlert;
return this;
}
public Builder setSilenceExpirationImminentAlert(boolean silenceExpirationImminentAlert) {
this.silenceExpirationImminentAlert = silenceExpirationImminentAlert;
return this;
}
public Builder setSilenceUserSetExpirationAlert(boolean silenceUserSetExpirationAlert) {
this.silenceUserSetExpirationAlert = silenceUserSetExpirationAlert;
return this;
}
public Builder setSilenceLowReservoirAlert(boolean silenceLowReservoirAlert) {
this.silenceLowReservoirAlert = silenceLowReservoirAlert;
return this;
}
public Builder setSilenceSuspendInProgressAlert(boolean silenceSuspendInProgressAlert) {
this.silenceSuspendInProgressAlert = silenceSuspendInProgressAlert;
return this;
}
public Builder setSilenceSuspendEndedAlert(boolean silenceSuspendEndedAlert) {
this.silenceSuspendEndedAlert = silenceSuspendEndedAlert;
return this;
}
public Builder setSilencePodExpirationAlert(boolean silencePodExpirationAlert) {
this.silencePodExpirationAlert = silencePodExpirationAlert;
return this;
}
@Override protected final SilenceAlertsCommand buildCommand() {
return new SilenceAlertsCommand(uniqueId, sequenceNumber, multiCommandFlag, new SilenceAlertCommandParameters(silenceAutoOffAlert, silenceMultiCommandAlert, silenceExpirationImminentAlert, silenceUserSetExpirationAlert, silenceLowReservoirAlert, silenceSuspendInProgressAlert, silenceSuspendEndedAlert, silencePodExpirationAlert), nonce);
}
}
}

View file

@ -0,0 +1,116 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
import java.nio.ByteBuffer
import java.util.*
class SilenceAlertsCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val parameters: SilenceAlertCommandParameters,
nonce: Int
) : NonceEnabledCommand(CommandType.SILENCE_ALERTS, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
override val encoded: ByteArray
get() =
appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.put(parameters.encoded) //
.array())
override fun toString(): String {
return "SilenceAlertsCommand{" +
"parameters=" + parameters +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
class SilenceAlertCommandParameters(private val silenceAutoOffAlert: Boolean, private val silenceMultiCommandAlert: Boolean, private val silenceExpirationImminentAlert: Boolean, private val silenceUserSetExpirationAlert: Boolean, private val silenceLowReservoirAlert: Boolean, private val silenceSuspendInProgressAlert: Boolean, private val silenceSuspendEndedAlert: Boolean, private val silencePodExpirationAlert: Boolean) : Encodable {
override val encoded: ByteArray
get() {
val bitSet = BitSet(8)
bitSet[0] = silenceAutoOffAlert
bitSet[1] = silenceMultiCommandAlert
bitSet[2] = silenceExpirationImminentAlert
bitSet[3] = silenceUserSetExpirationAlert
bitSet[4] = silenceLowReservoirAlert
bitSet[5] = silenceSuspendInProgressAlert
bitSet[6] = silenceSuspendEndedAlert
bitSet[7] = silencePodExpirationAlert
return bitSet.toByteArray()
}
}
class Builder : NonceEnabledCommandBuilder<Builder, SilenceAlertsCommand>() {
private var silenceAutoOffAlert = false
private var silenceMultiCommandAlert = false
private var silenceExpirationImminentAlert = false
private var silenceUserSetExpirationAlert = false
private var silenceLowReservoirAlert = false
private var silenceSuspendInProgressAlert = false
private var silenceSuspendEndedAlert = false
private var silencePodExpirationAlert = false
fun setSilenceAutoOffAlert(silenceAutoOffAlert: Boolean): Builder {
this.silenceAutoOffAlert = silenceAutoOffAlert
return this
}
fun setSilenceMultiCommandAlert(silenceMultiCommandAlert: Boolean): Builder {
this.silenceMultiCommandAlert = silenceMultiCommandAlert
return this
}
fun setSilenceExpirationImminentAlert(silenceExpirationImminentAlert: Boolean): Builder {
this.silenceExpirationImminentAlert = silenceExpirationImminentAlert
return this
}
fun setSilenceUserSetExpirationAlert(silenceUserSetExpirationAlert: Boolean): Builder {
this.silenceUserSetExpirationAlert = silenceUserSetExpirationAlert
return this
}
fun setSilenceLowReservoirAlert(silenceLowReservoirAlert: Boolean): Builder {
this.silenceLowReservoirAlert = silenceLowReservoirAlert
return this
}
fun setSilenceSuspendInProgressAlert(silenceSuspendInProgressAlert: Boolean): Builder {
this.silenceSuspendInProgressAlert = silenceSuspendInProgressAlert
return this
}
fun setSilenceSuspendEndedAlert(silenceSuspendEndedAlert: Boolean): Builder {
this.silenceSuspendEndedAlert = silenceSuspendEndedAlert
return this
}
fun setSilencePodExpirationAlert(silencePodExpirationAlert: Boolean): Builder {
this.silencePodExpirationAlert = silencePodExpirationAlert
return this
}
override fun buildCommand(): SilenceAlertsCommand {
return SilenceAlertsCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, SilenceAlertCommandParameters(silenceAutoOffAlert, silenceMultiCommandAlert, silenceExpirationImminentAlert, silenceUserSetExpirationAlert, silenceLowReservoirAlert, silenceSuspendInProgressAlert, silenceSuspendEndedAlert, silencePodExpirationAlert), nonce!!)
}
}
companion object {
private const val LENGTH = 7.toShort()
private const val BODY_LENGTH = 5.toByte()
}
}

View file

@ -1,97 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command;
import java.nio.ByteBuffer;
import java.util.BitSet;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public final class StopDeliveryCommand extends NonceEnabledCommand {
private static final short LENGTH = 7;
private static final byte BODY_LENGTH = 5;
private final DeliveryType deliveryType;
private final BeepType beepType;
StopDeliveryCommand(int uniqueId, short sequenceNumber, boolean multiCommandFlag, DeliveryType deliveryType, BeepType beepType, int nonce) {
super(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce);
this.deliveryType = deliveryType;
this.beepType = beepType;
}
@Override public byte[] getEncoded() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.getValue()) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.put((byte) ((beepType.getValue() << 4) | deliveryType.getEncoded()[0])) //
.array());
}
@Override public String toString() {
return "StopDeliveryCommand{" +
"deliveryType=" + deliveryType +
", beepType=" + beepType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}';
}
public enum DeliveryType implements Encodable {
BASAL(true, false, false),
TEMP_BASAL(false, true, false),
BOLUS(false, false, true),
ALL(true, true, true);
private final boolean basal;
private final boolean tempBasal;
private final boolean bolus;
DeliveryType(boolean basal, boolean tempBasal, boolean bolus) {
this.basal = basal;
this.tempBasal = tempBasal;
this.bolus = bolus;
}
@Override public byte[] getEncoded() {
BitSet bitSet = new BitSet(8);
bitSet.set(0, this.basal);
bitSet.set(1, this.tempBasal);
bitSet.set(2, this.bolus);
return bitSet.toByteArray();
}
}
public static final class Builder extends NonceEnabledCommandBuilder<Builder, StopDeliveryCommand> {
private DeliveryType deliveryType;
private BeepType beepType = BeepType.LONG_SINGLE_BEEP;
public Builder setDeliveryType(DeliveryType deliveryType) {
this.deliveryType = deliveryType;
return this;
}
public Builder setBeepType(BeepType beepType) {
this.beepType = beepType;
return this;
}
@Override protected final StopDeliveryCommand buildCommand() {
if (deliveryType == null) {
throw new IllegalArgumentException("deliveryType can not be null");
}
if (beepType == null) {
throw new IllegalArgumentException("beepType can not be null");
}
return new StopDeliveryCommand(uniqueId, sequenceNumber, multiCommandFlag, deliveryType, beepType, nonce);
}
}
}

View file

@ -0,0 +1,82 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
import java.nio.ByteBuffer
import java.util.*
class StopDeliveryCommand internal constructor(
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
private val deliveryType: DeliveryType,
private val beepType: BeepType,
nonce: Int
) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
override val encoded: ByteArray
get() {
return appendCrc(ByteBuffer.allocate(LENGTH + HEADER_LENGTH) //
.put(encodeHeader(uniqueId, sequenceNumber, LENGTH, multiCommandFlag)) //
.put(commandType.value) //
.put(BODY_LENGTH) //
.putInt(nonce) //
.put((beepType.value.toInt() shl 4 or deliveryType.encoded[0].toInt()).toByte()) // TODO bitstuff
.array())
}
override fun toString(): String {
return "StopDeliveryCommand{" +
"deliveryType=" + deliveryType +
", beepType=" + beepType +
", nonce=" + nonce +
", commandType=" + commandType +
", uniqueId=" + uniqueId +
", sequenceNumber=" + sequenceNumber +
", multiCommandFlag=" + multiCommandFlag +
'}'
}
enum class DeliveryType(private val basal: Boolean, private val tempBasal: Boolean, private val bolus: Boolean) : Encodable {
BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true);
override val encoded: ByteArray
get() {
val bitSet = BitSet(8)
bitSet[0] = basal
bitSet[1] = tempBasal
bitSet[2] = bolus
return bitSet.toByteArray()
}
}
class Builder : NonceEnabledCommandBuilder<Builder, StopDeliveryCommand>() {
private var deliveryType: DeliveryType? = null
private var beepType: BeepType? = BeepType.LONG_SINGLE_BEEP
fun setDeliveryType(deliveryType: DeliveryType?): Builder {
this.deliveryType = deliveryType
return this
}
fun setBeepType(beepType: BeepType?): Builder {
this.beepType = beepType
return this
}
override fun buildCommand(): StopDeliveryCommand {
requireNotNull(deliveryType) { "deliveryType can not be null" }
requireNotNull(beepType) { "beepType can not be null" }
return StopDeliveryCommand(uniqueId!!, sequenceNumber!!, multiCommandFlag, deliveryType!!, beepType!!, nonce!!)
}
}
companion object {
private const val LENGTH: Short = 7
private const val BODY_LENGTH: Byte = 5
}
}

View file

@ -1,7 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
public interface Command extends Encodable {
CommandType getCommandType();
}

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable
import java.io.Serializable
interface Command : Encodable, Serializable {
val commandType: CommandType
}

View file

@ -1,26 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
public enum CommandType {
SET_UNIQUE_ID((byte) 0x03),
GET_VERSION((byte) 0x07),
GET_STATUS((byte) 0x0e),
SILENCE_ALERTS((byte) 0x11),
PROGRAM_BASAL((byte) 0x13), // Always preceded by 0x1a
PROGRAM_TEMP_BASAL((byte) 0x16), // Always preceded by 0x1a
PROGRAM_BOLUS((byte) 0x17), // Always preceded by 0x1a
PROGRAM_ALERTS((byte) 0x19),
PROGRAM_INSULIN((byte) 0x1a), // Always followed by one of: 0x13, 0x16, 0x17
DEACTIVATE((byte) 0x1c),
PROGRAM_BEEPS((byte) 0x1e),
STOP_DELIVERY((byte) 0x1f);
byte value;
CommandType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
enum class CommandType(
val value: Byte
) {
SET_UNIQUE_ID(0x03.toByte()),
GET_VERSION(0x07.toByte()),
GET_STATUS(0x0e.toByte()),
SILENCE_ALERTS(0x11.toByte()),
PROGRAM_BASAL(0x13.toByte()), // Always preceded by 0x1a
PROGRAM_TEMP_BASAL(0x16.toByte()), // Always preceded by 0x1a
PROGRAM_BOLUS(0x17.toByte()), // Always preceded by 0x1a
PROGRAM_ALERTS(0x19.toByte()),
PROGRAM_INSULIN(0x1a.toByte()), // Always followed by one of: 0x13, 0x16, 0x17
DEACTIVATE(0x1c.toByte()),
PROGRAM_BEEPS(0x1e.toByte()),
STOP_DELIVERY(0x1f.toByte());
}

View file

@ -1,40 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
import java.nio.ByteBuffer;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil;
public abstract class HeaderEnabledCommand implements Command {
protected static final short HEADER_LENGTH = 6;
protected final CommandType commandType;
protected final int uniqueId;
protected final short sequenceNumber;
protected final boolean multiCommandFlag;
protected HeaderEnabledCommand(CommandType commandType, int uniqueId, short sequenceNumber, boolean multiCommandFlag) {
this.commandType = commandType;
this.uniqueId = uniqueId;
this.sequenceNumber = sequenceNumber;
this.multiCommandFlag = multiCommandFlag;
}
@Override public CommandType getCommandType() {
return commandType;
}
protected static byte[] appendCrc(byte[] command) {
return ByteBuffer.allocate(command.length + 2) //
.put(command) //
.putShort(MessageUtil.createCrc(command)) //
.array();
}
protected static byte[] encodeHeader(int uniqueId, short sequenceNumber, short length, boolean multiCommandFlag) {
return ByteBuffer.allocate(6) //
.putInt(uniqueId) //
.putShort((short) (((sequenceNumber & 0x0f) << 10) | length | ((multiCommandFlag ? 1 : 0) << 15))) //
.array();
}
}

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.MessageUtil
import java.nio.ByteBuffer
abstract class HeaderEnabledCommand protected constructor(
override val commandType: CommandType,
protected val uniqueId: Int,
protected val sequenceNumber: Short,
protected val multiCommandFlag: Boolean
) : Command {
companion object {
internal fun appendCrc(command: ByteArray): ByteArray =
ByteBuffer.allocate(command.size + 2) //
.put(command) //
.putShort(MessageUtil.createCrc(command)) //
.array()
internal fun encodeHeader(uniqueId: Int, sequenceNumber: Short, length: Short, multiCommandFlag: Boolean): ByteArray =
ByteBuffer.allocate(6) //
.putInt(uniqueId) //
.putShort((sequenceNumber.toInt() and 0x0f shl 10 or length.toInt() or ((if (multiCommandFlag) 1 else 0) shl 15)).toShort()) //
.array()
internal const val HEADER_LENGTH: Short = 6
}
}

View file

@ -1,11 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base;
public abstract class NonceEnabledCommand extends HeaderEnabledCommand {
protected final int nonce;
protected NonceEnabledCommand(CommandType commandType, int uniqueId, short sequenceNumber, boolean multiCommandFlag, int nonce) {
super(commandType, uniqueId, sequenceNumber, multiCommandFlag);
this.nonce = nonce;
}
}

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base
abstract class NonceEnabledCommand protected constructor(
commandType: CommandType,
uniqueId: Int,
sequenceNumber: Short,
multiCommandFlag: Boolean,
protected val nonce: Int
) : HeaderEnabledCommand(commandType, uniqueId, sequenceNumber, multiCommandFlag)

View file

@ -1,7 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command;
public interface CommandBuilder<R extends Command> {
R build();
}

View file

@ -1,6 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.Encodable;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
public interface ShortInsulinProgramElement extends Encodable {
}
interface CommandBuilder<R : Command> {
fun build(): R
}

View file

@ -1,36 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command;
public abstract class HeaderEnabledCommandBuilder<T extends HeaderEnabledCommandBuilder<T, R>, R extends Command> implements CommandBuilder<R> {
protected Integer uniqueId;
protected Short sequenceNumber;
protected boolean multiCommandFlag = false;
public R build() {
if (uniqueId == null) {
throw new IllegalArgumentException("uniqueId can not be null");
}
if (sequenceNumber == null) {
throw new IllegalArgumentException("sequenceNumber can not be null");
}
return buildCommand();
}
public final T setUniqueId(int uniqueId) {
this.uniqueId = uniqueId;
return (T) this;
}
public final T setSequenceNumber(short sequenceNumber) {
this.sequenceNumber = sequenceNumber;
return (T) this;
}
public final T setMultiCommandFlag(boolean multiCommandFlag) {
this.multiCommandFlag = multiCommandFlag;
return (T) this;
}
protected abstract R buildCommand();
}

View file

@ -0,0 +1,32 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
abstract class HeaderEnabledCommandBuilder<T : HeaderEnabledCommandBuilder<T, R>, R : Command> : CommandBuilder<R> {
protected var uniqueId: Int? = null
protected var sequenceNumber: Short? = null
protected var multiCommandFlag = false
override fun build(): R {
requireNotNull(uniqueId) { "uniqueId can not be null" }
requireNotNull(sequenceNumber) { "sequenceNumber can not be null" }
return buildCommand()
}
fun setUniqueId(uniqueId: Int): T {
this.uniqueId = uniqueId
return this as T
}
fun setSequenceNumber(sequenceNumber: Short): T {
this.sequenceNumber = sequenceNumber
return this as T
}
fun setMultiCommandFlag(multiCommandFlag: Boolean): T {
this.multiCommandFlag = multiCommandFlag
return this as T
}
protected abstract fun buildCommand(): R
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command;
public abstract class NonceEnabledCommandBuilder<T extends NonceEnabledCommandBuilder<T, R>, R extends Command> extends HeaderEnabledCommandBuilder<T, R> {
protected Integer nonce;
public final R build() {
if (nonce == null) {
throw new IllegalArgumentException("nonce can not be null");
}
return super.build();
}
public final T setNonce(int nonce) {
this.nonce = nonce;
return (T) this;
}
}

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
abstract class NonceEnabledCommandBuilder<T : NonceEnabledCommandBuilder<T, R>, R : Command> : HeaderEnabledCommandBuilder<T, R>() {
protected var nonce: Int? = null
override fun build(): R {
requireNotNull(nonce) { "nonce can not be null" }
return super.build()
}
fun setNonce(nonce: Int): T {
this.nonce = nonce
return this as T
}
}

View file

@ -1,58 +0,0 @@
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;
import static info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util.ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT;
public class BasalInsulinProgramElement implements Encodable {
private final byte startSlotIndex;
private final byte numberOfSlots;
private final short totalTenthPulses;
public BasalInsulinProgramElement(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses) {
this.startSlotIndex = startSlotIndex;
this.numberOfSlots = numberOfSlots;
this.totalTenthPulses = totalTenthPulses;
}
@Override public byte[] getEncoded() {
return ByteBuffer.allocate(6) //
.putShort(totalTenthPulses) //
.putInt(totalTenthPulses == 0 ? Integer.MIN_VALUE | getDelayBetweenTenthPulsesInUsec() : getDelayBetweenTenthPulsesInUsec()) //
.array();
}
public byte getStartSlotIndex() {
return startSlotIndex;
}
public byte getNumberOfSlots() {
return numberOfSlots;
}
public short getDurationInSeconds() {
return (short) (numberOfSlots * 1_800);
}
public short getTotalTenthPulses() {
return totalTenthPulses;
}
public int getDelayBetweenTenthPulsesInUsec() {
if (totalTenthPulses == 0) {
return MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT;
}
return (int) (((long) MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT * numberOfSlots) / (double) totalTenthPulses);
}
@Override public String toString() {
return "LongInsulinProgramElement{" +
"startSlotIndex=" + startSlotIndex +
", numberOfSlots=" + numberOfSlots +
", totalTenthPulses=" + totalTenthPulses +
", delayBetweenTenthPulsesInUsec=" + getDelayBetweenTenthPulsesInUsec() +
'}';
}
}

View file

@ -0,0 +1,34 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
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.Encodable
import java.io.Serializable
import java.nio.ByteBuffer
open class BasalInsulinProgramElement(
val startSlotIndex: Byte,
val numberOfSlots: Byte,
val totalTenthPulses: Short
) : Encodable, Serializable {
override val encoded: ByteArray
get() = ByteBuffer.allocate(6) //
.putShort(totalTenthPulses) //
.putInt(if (totalTenthPulses.toInt() == 0) Int.MIN_VALUE or delayBetweenTenthPulsesInUsec else delayBetweenTenthPulsesInUsec) //
.array()
val durationInSeconds: Short
get() = (numberOfSlots * 1800).toShort()
val delayBetweenTenthPulsesInUsec: Int
get() = if (totalTenthPulses.toInt() == 0) {
ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT
} else (ProgramBasalUtil.MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT.toLong() * numberOfSlots / totalTenthPulses.toDouble()).toInt()
override fun toString(): String {
return "LongInsulinProgramElement{" +
"startSlotIndex=" + startSlotIndex +
", numberOfSlots=" + numberOfSlots +
", totalTenthPulses=" + totalTenthPulses +
", delayBetweenTenthPulsesInUsec=" + delayBetweenTenthPulsesInUsec +
'}'
}
}

View file

@ -1,34 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
import java.nio.ByteBuffer;
public class BasalShortInsulinProgramElement implements ShortInsulinProgramElement {
private final byte numberOfSlots; // 4 bits
private final short pulsesPerSlot; // 10 bits
private final boolean extraAlternatePulse;
public BasalShortInsulinProgramElement(byte numberOfSlots, short pulsesPerSlot, boolean extraAlternatePulse) {
this.numberOfSlots = numberOfSlots;
this.pulsesPerSlot = pulsesPerSlot;
this.extraAlternatePulse = extraAlternatePulse;
}
@Override public byte[] getEncoded() {
byte firstByte = (byte) ((((numberOfSlots - 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=" + numberOfSlots +
", pulsesPerSlot=" + pulsesPerSlot +
", extraAlternatePulse=" + extraAlternatePulse +
'}';
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
import java.nio.ByteBuffer
import kotlin.experimental.and
class BasalShortInsulinProgramElement(
private val numberOfSlots: Byte, // 4 bits
private val pulsesPerSlot: Short, //10 bits
private val extraAlternatePulse: Boolean
) : ShortInsulinProgramElement {
override val encoded: ByteArray
get() {
val firstByte = (numberOfSlots - 1 and 0x0f shl 4 //
or ((if (extraAlternatePulse) 1 else 0) shl 3) //
or (pulsesPerSlot.toInt() ushr 8 and 0x03)).toByte()
return ByteBuffer.allocate(2) //
.put(firstByte) //
.put((pulsesPerSlot and 0xff).toByte()) //
.array()
}
override fun toString(): String {
return "ShortInsulinProgramElement{" +
"numberOfSlotsMinusOne=" + numberOfSlots +
", pulsesPerSlot=" + pulsesPerSlot +
", extraAlternatePulse=" + extraAlternatePulse +
'}'
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
import java.nio.ByteBuffer;
public class BolusShortInsulinProgramElement implements ShortInsulinProgramElement {
private short numberOfPulses;
public BolusShortInsulinProgramElement(short numberOfPulses) {
this.numberOfPulses = numberOfPulses;
}
public short getNumberOfPulses() {
return numberOfPulses;
}
@Override public byte[] getEncoded() {
return ByteBuffer.allocate(2).putShort(numberOfPulses).array();
}
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
import java.nio.ByteBuffer
class BolusShortInsulinProgramElement(
private val numberOfPulses: Short
) : ShortInsulinProgramElement {
override val encoded: ByteArray
get() = ByteBuffer.allocate(2).putShort(numberOfPulses).array()
}

View file

@ -1,33 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
public class CurrentBasalInsulinProgramElement {
private final byte index;
private final int delayUntilNextTenthPulseInUsec;
private final short remainingTenthPulses;
public CurrentBasalInsulinProgramElement(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 +
'}';
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
import java.io.Serializable
class CurrentBasalInsulinProgramElement(
val index: Byte,
val delayUntilNextTenthPulseInUsec: Int,
val remainingTenthPulses: Short
) : Serializable {
override fun toString(): String = "CurrentLongInsulinProgramElement{" +
"index=" + index +
", delayUntilNextTenthPulseInUsec=" + delayUntilNextTenthPulseInUsec +
", remainingTenthPulses=" + remainingTenthPulses +
'}'
}

View file

@ -1,33 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
public class CurrentSlot {
private final byte index;
private final short eighthSecondsRemaining;
private final short pulsesRemaining;
public CurrentSlot(byte index, short eighthSecondsRemaining, short pulsesRemaining) {
this.index = index;
this.eighthSecondsRemaining = eighthSecondsRemaining;
this.pulsesRemaining = pulsesRemaining;
}
public byte getIndex() {
return index;
}
public short getEighthSecondsRemaining() {
return eighthSecondsRemaining;
}
public short getPulsesRemaining() {
return pulsesRemaining;
}
@Override public String toString() {
return "CurrentSlot{" +
"index=" + index +
", eighthSecondsRemaining=" + eighthSecondsRemaining +
", pulsesRemaining=" + pulsesRemaining +
'}';
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
import java.io.Serializable
class CurrentSlot(
val index: Byte,
val eighthSecondsRemaining: Short,
val pulsesRemaining: Short
) : Serializable {
override fun toString(): String = "CurrentSlot{" +
"index=" + index +
", eighthSecondsRemaining=" + eighthSecondsRemaining +
", pulsesRemaining=" + pulsesRemaining +
'}'
}

View file

@ -0,0 +1,6 @@
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
import java.io.Serializable
interface ShortInsulinProgramElement : Encodable, Serializable

View file

@ -1,23 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program;
import java.nio.ByteBuffer;
public class TempBasalInsulinProgramElement extends BasalInsulinProgramElement {
public TempBasalInsulinProgramElement(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses) {
super(startSlotIndex, numberOfSlots, totalTenthPulses);
}
@Override public byte[] getEncoded() {
ByteBuffer buffer = ByteBuffer.allocate(6);
if (getTotalTenthPulses() == 0) {
int i = ((int) ((((double) getDurationInSeconds()) * 1_000_000.0d) / ((double) getNumberOfSlots()))) | Integer.MIN_VALUE;
buffer.putShort(getNumberOfSlots()) //
.putInt(i);
} else {
buffer.putShort(getTotalTenthPulses()) //
.putInt(getDelayBetweenTenthPulsesInUsec());
}
return buffer.array();
}
}

View file

@ -0,0 +1,24 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program
import java.nio.ByteBuffer
class TempBasalInsulinProgramElement(
startSlotIndex: Byte,
numberOfSlots: Byte,
totalTenthPulses: Short
) : BasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses) {
override val encoded: ByteArray
get() {
val buffer = ByteBuffer.allocate(6)
if (totalTenthPulses.toInt() == 0) {
val i = (durationInSeconds.toDouble() * 1000000.0 / numberOfSlots.toDouble()).toInt() or Int.MIN_VALUE
buffer.putShort(numberOfSlots.toShort()) //
.putInt(i)
} else {
buffer.putShort(totalTenthPulses) //
.putInt(delayBetweenTenthPulsesInUsec)
}
return buffer.array()
}
}

View file

@ -1,257 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util;
import java.nio.ByteBuffer;
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.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 {
public static final int MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_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 interface BasalInsulinProgramElementFactory<T extends BasalInsulinProgramElement> {
T create(byte startSlotIndex, byte numberOfSlots, short totalTenthPulses);
}
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<>();
long previousTenthPulsesPerSlot = 0;
byte numberOfSlotsInCurrentElement = 0;
byte startSlotIndex = 0;
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(insulinProgramElementFactory.create(startSlotIndex, numberOfSlotsInCurrentElement, (short) (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement)));
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i];
numberOfSlotsInCurrentElement = 1;
startSlotIndex += numberOfSlotsInCurrentElement;
} else {
numberOfSlotsInCurrentElement++;
}
}
elements.add(insulinProgramElementFactory.create(startSlotIndex, numberOfSlotsInCurrentElement, (short) (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 at most 48 slots");
}
List<ShortInsulinProgramElement> elements = new ArrayList<>();
boolean extraAlternatePulse = false;
short previousPulsesPerSlot = 0;
byte numberOfSlotsInCurrentElement = 0;
byte currentTotalNumberOfSlots = 0;
while (currentTotalNumberOfSlots < pulsesPerSlot.length) {
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_INSULIN_PROGRAM_ELEMENT) {
numberOfSlotsInCurrentElement++;
} else {
elements.add(new BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
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++;
numberOfSlotsInCurrentElement++;
extraAlternatePulse = true;
while (currentTotalNumberOfSlots < pulsesPerSlot.length) {
// 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_INSULIN_PROGRAM_ELEMENT) {
numberOfSlotsInCurrentElement++;
} else {
// End of alternate pulse segment (no slots left in element)
elements.add(new BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
numberOfSlotsInCurrentElement = 1;
extraAlternatePulse = false;
break;
}
} else {
// End of alternate pulse segment (new number of pulses per slot)
elements.add(new BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
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 BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots];
currentTotalNumberOfSlots++;
extraAlternatePulse = false;
numberOfSlotsInCurrentElement = 1;
} else {
throw new IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements");
}
}
elements.add(new BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse));
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()) {
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);
int secondOfDay = secondOfMinute + hourOfDay * 3_600 + minuteOfHour * 60;
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 CurrentBasalInsulinProgramElement calculateCurrentLongInsulinProgramElement(List<BasalInsulinProgramElement> 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 (BasalInsulinProgramElement 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 = (long) ((durationInSeconds - secondsPassedInCurrentSlot) / (double) durationInSeconds * 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) ((remainingTenThousandthPulses % 1_000 != 0 ? 1 : 0) + remainingTenThousandthPulses / 1_000);
return new CurrentBasalInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses);
}
index++;
startSlotIndex += element.getNumberOfSlots();
}
throw new IllegalStateException("Could not determine current long insulin program element");
}
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()) //
.putShort(currentSlot.getEighthSecondsRemaining());
for (short pulses : pulsesPerSlot) {
buffer.putShort(pulses);
}
return MessageUtil.calculateChecksum(buffer.array());
}
}

View file

@ -0,0 +1,208 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util
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
import java.nio.ByteBuffer
import java.util.*
object ProgramBasalUtil {
const val MAX_DELAY_BETWEEN_TENTH_PULSES_IN_USEC_AND_USECS_IN_BASAL_SLOT = 1800000000
private const val NUMBER_OF_BASAL_SLOTS: Byte = 48
private const val MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT: Byte = 16
fun mapTenthPulsesPerSlotToLongInsulinProgramElements(
tenthPulsesPerSlot: ShortArray?,
insulinProgramElementFactory: (Byte, Byte, Short) -> BasalInsulinProgramElement = ::BasalInsulinProgramElement
): List<BasalInsulinProgramElement> {
require(tenthPulsesPerSlot!!.size <= NUMBER_OF_BASAL_SLOTS) { "Basal program must contain at most 48 slots" }
val elements: MutableList<BasalInsulinProgramElement> = ArrayList()
var previousTenthPulsesPerSlot: Short = 0
var numberOfSlotsInCurrentElement: Byte = 0
var startSlotIndex: Byte = 0
for (i in tenthPulsesPerSlot.indices) {
if (i == 0) {
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
numberOfSlotsInCurrentElement = 1
} else if (previousTenthPulsesPerSlot != tenthPulsesPerSlot[i] || (numberOfSlotsInCurrentElement + 1) * previousTenthPulsesPerSlot > 65534) {
elements.add(insulinProgramElementFactory(startSlotIndex, numberOfSlotsInCurrentElement, (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()))
previousTenthPulsesPerSlot = tenthPulsesPerSlot[i]
numberOfSlotsInCurrentElement = 1
startSlotIndex = (numberOfSlotsInCurrentElement + startSlotIndex).toByte()
} else {
numberOfSlotsInCurrentElement++
}
}
elements.add(insulinProgramElementFactory(startSlotIndex, numberOfSlotsInCurrentElement, (previousTenthPulsesPerSlot * numberOfSlotsInCurrentElement).toShort()))
return elements
}
fun mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot: ShortArray?): List<ShortInsulinProgramElement> {
require(pulsesPerSlot!!.size <= NUMBER_OF_BASAL_SLOTS) { "Basal program must contain at most 48 slots" }
val elements: MutableList<ShortInsulinProgramElement> = ArrayList()
var extraAlternatePulse = false
var previousPulsesPerSlot: Short = 0
var numberOfSlotsInCurrentElement: Byte = 0
var currentTotalNumberOfSlots: Byte = 0
while (currentTotalNumberOfSlots < pulsesPerSlot.size) {
if (currentTotalNumberOfSlots.toInt() == 0) {
// First slot
previousPulsesPerSlot = pulsesPerSlot[0]
currentTotalNumberOfSlots++
numberOfSlotsInCurrentElement = 1
} else if (pulsesPerSlot[currentTotalNumberOfSlots.toInt()] == previousPulsesPerSlot) {
// Subsequent slot in element (same pulses per slot as previous slot)
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
numberOfSlotsInCurrentElement++
} else {
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
numberOfSlotsInCurrentElement = 1
extraAlternatePulse = false
}
currentTotalNumberOfSlots++
} else if (numberOfSlotsInCurrentElement.toInt() == 1 && pulsesPerSlot[currentTotalNumberOfSlots.toInt()].toInt() == previousPulsesPerSlot + 1) {
// Second slot of segment with extra alternate pulse
var expectAlternatePulseForNextSegment = false
currentTotalNumberOfSlots++
numberOfSlotsInCurrentElement++
extraAlternatePulse = true
while (currentTotalNumberOfSlots < pulsesPerSlot.size) {
// Loop rest alternate pulse segment
if (pulsesPerSlot[currentTotalNumberOfSlots.toInt()].toInt() == previousPulsesPerSlot + (if (expectAlternatePulseForNextSegment) 1 else 0)) {
// Still in alternate pulse segment
currentTotalNumberOfSlots++
expectAlternatePulseForNextSegment = !expectAlternatePulseForNextSegment
if (numberOfSlotsInCurrentElement < MAX_NUMBER_OF_SLOTS_IN_INSULIN_PROGRAM_ELEMENT) {
numberOfSlotsInCurrentElement++
} else {
// End of alternate pulse segment (no slots left in element)
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
numberOfSlotsInCurrentElement = 1
extraAlternatePulse = false
break
}
} else {
// End of alternate pulse segment (new number of pulses per slot)
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
numberOfSlotsInCurrentElement = 1
extraAlternatePulse = false
currentTotalNumberOfSlots++
break
}
}
} else if (previousPulsesPerSlot != pulsesPerSlot[currentTotalNumberOfSlots.toInt()]) {
// End of segment (new number of pulses per slot)
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
previousPulsesPerSlot = pulsesPerSlot[currentTotalNumberOfSlots.toInt()]
currentTotalNumberOfSlots++
extraAlternatePulse = false
numberOfSlotsInCurrentElement = 1
} else {
throw IllegalStateException("Reached illegal point in mapBasalProgramToShortInsulinProgramElements")
}
}
elements.add(BasalShortInsulinProgramElement(numberOfSlotsInCurrentElement, previousPulsesPerSlot, extraAlternatePulse))
return elements
}
fun mapBasalProgramToTenthPulsesPerSlot(basalProgram: BasalProgram): ShortArray {
val tenthPulsesPerSlot = ShortArray(NUMBER_OF_BASAL_SLOTS.toInt())
for (segment in basalProgram.segments) {
for (i in segment.startSlotIndex until segment.endSlotIndex) {
tenthPulsesPerSlot[i] = (roundToHalf(segment.getPulsesPerHour() / 2.0) * 10).toInt().toShort() // TODO Adrian: int conversion ok?
}
}
return tenthPulsesPerSlot
}
private fun roundToHalf(d: Double): Double {
return ((d * 10.0).toInt().toShort() / 5 * 5).toShort().toDouble() / 10.0
}
fun mapBasalProgramToPulsesPerSlot(basalProgram: BasalProgram): ShortArray {
val pulsesPerSlot = ShortArray(NUMBER_OF_BASAL_SLOTS.toInt())
for (segment in basalProgram.segments) {
var remainingPulse = false
for (i in segment.startSlotIndex until segment.endSlotIndex) {
pulsesPerSlot[i] = (segment.getPulsesPerHour() / 2).toShort()
if (segment.getPulsesPerHour() % 2 == 1) { // Do extra alternate pulse
if (remainingPulse) {
pulsesPerSlot[i] = (pulsesPerSlot[i] + 1).toShort()
}
remainingPulse = !remainingPulse
}
}
}
return pulsesPerSlot
}
fun calculateCurrentSlot(pulsesPerSlot: ShortArray?, currentTime: Date?): CurrentSlot {
val instance = Calendar.getInstance()
instance.time = currentTime
val hourOfDay = instance[Calendar.HOUR_OF_DAY]
val minuteOfHour = instance[Calendar.MINUTE]
val secondOfMinute = instance[Calendar.SECOND]
val index = ((hourOfDay * 60 + minuteOfHour) / 30).toByte()
val secondOfDay = secondOfMinute + hourOfDay * 3600 + minuteOfHour * 60
val secondsRemaining = ((index + 1) * 1800 - secondOfDay).toShort()
val pulsesRemaining = (pulsesPerSlot!![index.toInt()].toDouble() * secondsRemaining / 1800).toShort()
return CurrentSlot(index, (secondsRemaining * 8).toShort(), pulsesRemaining)
}
fun calculateCurrentLongInsulinProgramElement(elements: List<BasalInsulinProgramElement>, currentTime: Date?): CurrentBasalInsulinProgramElement {
val instance = Calendar.getInstance()
instance.time = currentTime
val hourOfDay = instance[Calendar.HOUR_OF_DAY]
val minuteOfHour = instance[Calendar.MINUTE]
val secondOfMinute = instance[Calendar.SECOND]
val secondOfDay = secondOfMinute + hourOfDay * 3600 + minuteOfHour * 60
var startSlotIndex = 0
var index: Byte = 0
for (element in elements) {
val startTimeInSeconds = startSlotIndex * 1800
val endTimeInSeconds = startTimeInSeconds + element.numberOfSlots * 1800
if (secondOfDay in startTimeInSeconds until endTimeInSeconds) { // TODO Adrian Range check ok
var totalNumberOfTenThousandthPulsesInSlot = (element.totalTenthPulses * 1000).toLong()
if (totalNumberOfTenThousandthPulsesInSlot == 0L) {
totalNumberOfTenThousandthPulsesInSlot = (element.numberOfSlots * 1000).toLong()
}
val durationInSeconds = endTimeInSeconds - startTimeInSeconds
val secondsPassedInCurrentSlot = secondOfDay - startTimeInSeconds
val remainingTenThousandthPulses = ((durationInSeconds - secondsPassedInCurrentSlot) / durationInSeconds.toDouble() * totalNumberOfTenThousandthPulsesInSlot).toLong()
val delayBetweenTenthPulsesInUsec = (durationInSeconds * 1000000L * 1000 / totalNumberOfTenThousandthPulsesInSlot).toInt()
val secondsRemaining = secondsPassedInCurrentSlot % 1800
var delayUntilNextTenthPulseInUsec = delayBetweenTenthPulsesInUsec
for (i in 0 until secondsRemaining) {
delayUntilNextTenthPulseInUsec -= 1000000
while (delayUntilNextTenthPulseInUsec <= 0) {
delayUntilNextTenthPulseInUsec += delayBetweenTenthPulsesInUsec
}
}
val remainingTenthPulses = ((if (remainingTenThousandthPulses % 1000 != 0L) 1 else 0) + remainingTenThousandthPulses / 1000).toShort()
return CurrentBasalInsulinProgramElement(index, delayUntilNextTenthPulseInUsec, remainingTenthPulses)
}
index++
startSlotIndex += element.numberOfSlots.toInt()
}
throw IllegalStateException("Could not determine current long insulin program element")
}
fun calculateChecksum(pulsesPerSlot: ShortArray?, currentSlot: CurrentSlot): Short {
val buffer = ByteBuffer.allocate(1 + 2 + 2 + NUMBER_OF_BASAL_SLOTS * 2) //
.put(currentSlot.index) //
.putShort(currentSlot.pulsesRemaining) //
.putShort(currentSlot.eighthSecondsRemaining)
for (pulses in pulsesPerSlot!!) {
buffer.putShort(pulses)
}
return MessageUtil.calculateChecksum(buffer.array())
}
}

View file

@ -1,68 +0,0 @@
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.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) {
return ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot, TempBasalInsulinProgramElement::new);
}
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

@ -0,0 +1,62 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.insulin.program.util
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
import java.nio.ByteBuffer
import kotlin.math.roundToInt
object ProgramTempBasalUtil {
fun mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot: ShortArray?): List<BasalInsulinProgramElement> {
return ProgramBasalUtil.mapTenthPulsesPerSlotToLongInsulinProgramElements(tenthPulsesPerSlot) { startSlotIndex: Byte, numberOfSlots: Byte, totalTenthPulses: Short -> TempBasalInsulinProgramElement(startSlotIndex, numberOfSlots, totalTenthPulses) }
}
fun mapTempBasalToTenthPulsesPerSlot(durationInSlots: Int, rateInUnitsPerHour: Double): ShortArray {
val pulsesPerHour = (rateInUnitsPerHour * 20).roundToInt().toShort()
val tenthPulsesPerSlot = ShortArray(durationInSlots)
var i = 0
while (durationInSlots > i) {
tenthPulsesPerSlot[i] = (roundToHalf(pulsesPerHour / 2.0) * 10).toShort()
i++
}
return tenthPulsesPerSlot
}
private fun roundToHalf(d: Double): Double {
return ((d * 10.0).toInt().toShort() / 5 * 5).toShort().toDouble() / 10.0
}
fun mapTempBasalToPulsesPerSlot(durationInSlots: Byte, rateInUnitsPerHour: Double): ShortArray {
val pulsesPerHour = (rateInUnitsPerHour * 20).roundToInt().toShort()
val pulsesPerSlot = ShortArray(durationInSlots.toInt())
var remainingPulse = false
var i = 0
while (durationInSlots > i) {
pulsesPerSlot[i] = (pulsesPerHour / 2).toShort()
if (pulsesPerHour % 2 == 1) { // Do extra alternate pulse
if (remainingPulse) {
pulsesPerSlot[i] = (pulsesPerSlot[i] + 1).toShort()
}
remainingPulse = !remainingPulse
}
i++
}
return pulsesPerSlot
}
fun calculateChecksum(totalNumberOfSlots: Byte, pulsesInFirstSlot: Short, pulsesPerSlot: ShortArray?): Short {
val buffer = ByteBuffer.allocate(1 + 2 + 2 + 2 * pulsesPerSlot!!.size) //
.put(totalNumberOfSlots) //
.putShort(0x3840.toShort()) //
.putShort(pulsesInFirstSlot)
for (pulses in pulsesPerSlot) {
buffer.putShort(pulses)
}
return MessageUtil.calculateChecksum(buffer.array())
}
fun mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot: ShortArray?): List<ShortInsulinProgramElement> =
ProgramBasalUtil.mapPulsesPerSlotToShortInsulinProgramElements(pulsesPerSlot)
}

View file

@ -0,0 +1,22 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class ActivationProgress {
NOT_STARTED,
GOT_POD_VERSION,
SET_UNIQUE_ID,
PROGRAMMED_LOW_RESERVOIR_ALERTS,
REPROGRAMMED_LUMP_OF_COAL_ALERT,
PRIMING,
PRIME_COMPLETED,
PROGRAMMED_USER_SET_EXPIRATION_ALERT,
PHASE_1_COMPLETED,
PROGRAMMED_BASAL,
PROGRAMMED_CANCEL_LOC_ETC_ALERT,
INSERTING_CANNULA,
CANNULA_INSERTED,
COMPLETED;
fun isBefore(other: ActivationProgress): Boolean = ordinal < other.ordinal
fun isAtLeast(other: ActivationProgress): Boolean = ordinal >= other.ordinal
}

View file

@ -1,174 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum AlarmType {
NONE((byte) 0x00),
ALARM_PW_FLASH_ERASE((byte) 0x01),
ALARM_PW_FLASH_WRITE((byte) 0x02),
ALARM_BASAL_CKSUM((byte) 0x03),
ALARM_BASAL_PPULSE((byte) 0x04),
ALARM_BASAL_STEP((byte) 0x05),
ALARM_AUTO_WAKEUP_TIMEOUT((byte) 0x06),
ALARM_WIRE_OVERDRIVEN((byte) 0x07),
ALARM_BEEP_REP_INVALID_INDEX((byte) 0x08),
ALARM_INVALID_REP_PATTERN((byte) 0x09),
ALARM_TEMP_BASAL_STEP((byte) 0x0a),
ALARM_TEMP_BASAL_CKSUM((byte) 0x0b),
ALARM_BOLUS_OVERFLOW((byte) 0x0c),
ALARM_COP_RESET((byte) 0x0d),
ALARM_ILOP_RESET((byte) 0x0e),
ALARM_ILAD_RESET((byte) 0x0f),
ALARM_SAWCOP_RESET((byte) 0x10),
ALARM_BOLUS_STEP((byte) 0x11),
ALARM_LVD_RESET((byte) 0x12),
ALARM_INVALID_RF_MSG_LENGTH((byte) 0x13),
ALARM_OCCLUDED((byte) 0x14),
ALARM_BOLUSPROG_CHKSUM((byte) 0x15),
ALARM_BOLUS_LOG((byte) 0x16),
ALARM_CRITICAL_VAR((byte) 0x17),
ALARM_EMPTY_RESERVOIR((byte) 0x18),
ALARM_LOADERR((byte) 0x19),
ALARM_PSA_FAILURE((byte) 0x1a),
ALARM_TICKCNT_NOT_CLEARED((byte) 0x1b),
ALARM_PUMP_EXPIRED((byte) 0x1c),
ALARM_COMD_BIT_NOT_SET((byte) 0x1d),
ALARM_INVALID_COMD_SET((byte) 0x1e),
ALARM_ALERTS_ARRAY_CKSM((byte) 0x1f),
ALARM_UNIT_TEST((byte) 0x20),
ALARM_TICK_TIME_ERROR((byte) 0x21),
ALARM_CRITICAL_HAZARD((byte) 0x22),
ALARM_PIEZO_FREQ((byte) 0x23),
ALARM_TICKCNT_ERROR_RTC((byte) 0x24),
ALARM_TICK_FAILURE((byte) 0x25),
ALARM_INVALID((byte) 0x26),
ALARM_LUMP_ALERT_PROGRAM((byte) 0x27),
ALARM_INVALID_PASS_CODE((byte) 0x28),
ALARM_ALERT0((byte) 0x29),
ALARM_ALERT1((byte) 0x2a),
ALARM_ALERT2((byte) 0x2b),
ALARM_ALERT3((byte) 0x2c),
ALARM_ALERT4((byte) 0x2d),
ALARM_ALERT5((byte) 0x2e),
ALARM_ALERT6((byte) 0x2f),
ALARM_ALERT7((byte) 0x30),
ALARM_ILLEGAL_PUMP_STATE((byte) 0x31),
ALARM_COP_TEST_FAILURE((byte) 0x32),
ALARM_MCTF((byte) 0x33),
ALARM_ILLEGAL_RESET((byte) 0x34),
ALARM_VETO_NOT_SET((byte) 0x35),
ALARM_ILLEGAL_PIN_RESET((byte) 0x36),
ALARM_INVALID_BEEP_PATTERN((byte) 0x37),
ALARM_WIRE_STATE_MACHINE((byte) 0x38),
ALARM_VETO_TEST_DEFAULT((byte) 0x39),
ALARM_ALERT_INVALID_INDEX((byte) 0x3a),
ALARM_SAWCOP_TEST_FAIL((byte) 0x3b),
ALARM_MCUCOP_TEST_FAIL((byte) 0x3c),
ALARM_STEP_SENSOR_SHORTED((byte) 0x3d),
ALARM_FLASH_FAILURE((byte) 0x3e),
ALARM_SPARE63((byte) 0x3f),
ALARM_SS_OPEN_CNT_EXCEEDED((byte) 0x40),
ALARM_SS_EXCESSIVE_SUMMED((byte) 0x41),
ALARM_SS_MIN_PULSE_TRANSITION((byte) 0x42),
ALARM_SS_DEFAULT((byte) 0x43),
ALARM_OPEN_WIRE1((byte) 0x44),
ALARM_OPEN_WIRE2((byte) 0x45),
ALARM_LOADERR_FAILURE((byte) 0x46),
ALARM_SAW_VETO_FAILURE((byte) 0x47),
ALARM_BAD_RFM_CLOCK((byte) 0x48),
ALARM_BAD_TICK_HIGH((byte) 0x49),
ALARM_BAD_TICK_PERIOD((byte) 0x4a),
ALARM_BAD_TRIM_VALUE((byte) 0x4b),
ALARM_BAD_BUS_CLOCK((byte) 0x4c),
ALARM_BAD_CAL_MODE((byte) 0x4d),
ALARM_SAW_TRIM_ERROR((byte) 0x4e),
ALARM_RFM_CRYSTAL_ERROR((byte) 0x4f),
ALARM_CALST_TIMEOUT((byte) 0x50),
ALARM_TICKCNT_ERROR((byte) 0x51),
ALARM_BAD_RFM_XTAL_START((byte) 0x52),
ALARM_BAD_RX_SENSENSITIVITY((byte) 0x53),
ALARM_BAD_TX_PKT_SIZE((byte) 0x54),
ALARM_TICK_LOW_PHASE_EXCEEDED((byte) 0x55),
ALARM_TICK_HIGH_PHASE_EXCEEDED((byte) 0x56),
ALARM_OCCL_CRITVAR_FAIL((byte) 0x57),
ALARM_OCCL_PARAM((byte) 0x58),
ALARM_PROG_OCCL_FAIL((byte) 0x59),
ALARM_PW_TO_HIGH_FOR_OCCL_DET((byte) 0x5a),
ALARM_OCCL_CSUM((byte) 0x5b),
ALARM_PRIME_OPEN_CNT_TO_LOW((byte) 0x5c),
ALARM_BAD_RF_CDTHR((byte) 0x5d),
ALARM_FLASH_NOT_SECURE((byte) 0x5e),
ALARM_WIRE_TEST_OPEN_GROUND((byte) 0x5f),
ALARM_OCCL_STARTUP1((byte) 0x60),
ALARM_OCCL_STARTUP2((byte) 0x61),
ALARM_OCCL_EXCESS_TIMEOUTS1((byte) 0x62),
ALARM_SPARE99((byte) 0x63),
ALARM_SPARE100((byte) 0x64),
ALARM_SPARE101((byte) 0x65),
ALARM_OCCL_EXCESS_TIMEOUTS2((byte) 0x66),
ALARM_OCCL_EXCESS_TIMEOUTS3((byte) 0x67),
ALARM_OCCL_NOISY_PULSE_WIDTHS((byte) 0x68),
ALARM_OCCL_AT_BOLUS_END((byte) 0x69),
ALARM_OCCL_ABOVE_THRESHOLD((byte) 0x6a),
ALARM_BASAL_UNDERINFUSION((byte) 0x80),
ALARM_BASAL_OVERINFUSION((byte) 0x81),
ALARM_TEMP_UNDERINFUSION((byte) 0x82),
ALARM_TEMP_OVERINFUSION((byte) 0x83),
ALARM_BOLUS_UNDERINFUSION((byte) 0x84),
ALARM_BOLUS_OVERINFUSION((byte) 0x85),
ALARM_BASAL_OVERINFUSION_PULSE((byte) 0x86),
ALARM_TEMP_OVERINFUSION_PULSE((byte) 0x87),
ALARM_BOLUS_OVERINFUSION_PULSE((byte) 0x88),
ALARM_IMMBOLUS_UNDERINFUSION_PULSE((byte) 0x89),
ALARM_EXTBOLUS_OVERINFUSION_PULSE((byte) 0x8a),
ALARM_PROGRAM_CSUM((byte) 0x8b),
ALARM_UNUSED_140((byte) 0x8c),
ALARM_UNRECOGNIZED_PULSE((byte) 0x8d),
ALARM_SYNC_WITHOUT_TEMP_ACTIVE((byte) 0x8e),
ALARM_INTERLOCK_LOAD((byte) 0x8f),
ALARM_ILLEGAL_CHAN_PARAM((byte) 0x90),
ALARM_BASAL_PULSE_CHAN_INACTIVE((byte) 0x91),
ALARM_TEMP_PULSE_CHAN_INACTIVE((byte) 0x92),
ALARM_BOLUS_PULSE_CHAN_INACTIVE((byte) 0x93),
ALARM_INT_SEMAPHORE_NOT_SET((byte) 0x94),
ALARM_ILLEGAL_INTERLOCK_CHAN((byte) 0x95),
ALARM_TERMINATE_BOLUS((byte) 0x96),
ALARM_OPEN_TRANSITIONS_COUNT((byte) 0x97),
ALARM_BLE_TO((byte) 0xa0),
ALARM_BLE_INITIATED((byte) 0xa1),
ALARM_BLE_UNK_ALARM((byte) 0xa2),
ALARM_UNUSED_163((byte) 0xa3),
ALARM_UNUSED_164((byte) 0xa4),
ALARM_UNUSED_165((byte) 0xa5),
ALARM_BLE_IAAS((byte) 0xa6),
ALARM_UNUSED_167((byte) 0xa7),
ALARM_CRC_FAILURE((byte) 0xa8),
ALARM_BLE_WD_PING_TIMEOUT((byte) 0xa9),
ALARM_BLE_EXCESSIVE_RESETS((byte) 0xaa),
ALARM_BLE_NAK_ERROR((byte) 0xab),
ALARM_BLE_REQ_HIGH_TIMEOUT((byte) 0xac),
ALARM_BLE_UNKNOWN_RESP((byte) 0xad),
ALARM_BLE_UNUSED_174((byte) 0xae),
ALARM_BLE_REQ_STUCK_HIGH((byte) 0xaf),
ALARM_BLE_STATE_MACHINE_1((byte) 0xb1),
ALARM_BLE_STATE_MACHINE_2((byte) 0xb2),
ALARM_BLE_UNUSED_179((byte) 0xb3),
ALARM_BLE_ARB_LOST((byte) 0xb4),
ALARM_BLE_ER48_DUAL_NACK((byte) 0xc0),
ALARM_BLE_QN_EXCEED_MAX_RETRY((byte) 0xc1),
ALARM_BLE_QN_CRIT_VAR_FAIL((byte) 0xc2),
UNKNOWN((byte) 0xff);
private byte value;
AlarmType(byte value) {
this.value = value;
}
public static AlarmType byValue(byte value) {
for (AlarmType type : values()) {
if (type.value == value) {
return type;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,174 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class AlarmType(
private val value: Byte
) {
NONE(0x00.toByte()),
ALARM_PW_FLASH_ERASE(0x01.toByte()),
ALARM_PW_FLASH_WRITE(0x02.toByte()),
ALARM_BASAL_CKSUM(0x03.toByte()),
ALARM_BASAL_PPULSE(0x04.toByte()),
ALARM_BASAL_STEP(0x05.toByte()),
ALARM_AUTO_WAKEUP_TIMEOUT(0x06.toByte()),
ALARM_WIRE_OVERDRIVEN(0x07.toByte()),
ALARM_BEEP_REP_INVALID_INDEX(0x08.toByte()),
ALARM_INVALID_REP_PATTERN(0x09.toByte()),
ALARM_TEMP_BASAL_STEP(0x0a.toByte()),
ALARM_TEMP_BASAL_CKSUM(0x0b.toByte()),
ALARM_BOLUS_OVERFLOW(0x0c.toByte()),
ALARM_COP_RESET(0x0d.toByte()),
ALARM_ILOP_RESET(0x0e.toByte()),
ALARM_ILAD_RESET(0x0f.toByte()),
ALARM_SAWCOP_RESET(0x10.toByte()),
ALARM_BOLUS_STEP(0x11.toByte()),
ALARM_LVD_RESET(0x12.toByte()),
ALARM_INVALID_RF_MSG_LENGTH(0x13.toByte()),
ALARM_OCCLUDED(0x14.toByte()),
ALARM_BOLUSPROG_CHKSUM(0x15.toByte()),
ALARM_BOLUS_LOG(0x16.toByte()),
ALARM_CRITICAL_VAR(0x17.toByte()),
ALARM_EMPTY_RESERVOIR(0x18.toByte()),
ALARM_LOADERR(0x19.toByte()),
ALARM_PSA_FAILURE(0x1a.toByte()),
ALARM_TICKCNT_NOT_CLEARED(0x1b.toByte()),
ALARM_PUMP_EXPIRED(0x1c.toByte()),
ALARM_COMD_BIT_NOT_SET(0x1d.toByte()),
ALARM_INVALID_COMD_SET(0x1e.toByte()),
ALARM_ALERTS_ARRAY_CKSM(0x1f.toByte()),
ALARM_UNIT_TEST(0x20.toByte()),
ALARM_TICK_TIME_ERROR(0x21.toByte()),
ALARM_CRITICAL_HAZARD(0x22.toByte()),
ALARM_PIEZO_FREQ(0x23.toByte()),
ALARM_TICKCNT_ERROR_RTC(0x24.toByte()),
ALARM_TICK_FAILURE(0x25.toByte()),
ALARM_INVALID(0x26.toByte()),
ALARM_LUMP_ALERT_PROGRAM(0x27.toByte()),
ALARM_INVALID_PASS_CODE(0x28.toByte()),
ALARM_ALERT0(0x29.toByte()),
ALARM_ALERT1(0x2a.toByte()),
ALARM_ALERT2(0x2b.toByte()),
ALARM_ALERT3(0x2c.toByte()),
ALARM_ALERT4(0x2d.toByte()),
ALARM_ALERT5(0x2e.toByte()),
ALARM_ALERT6(0x2f.toByte()),
ALARM_ALERT7(0x30.toByte()),
ALARM_ILLEGAL_PUMP_STATE(0x31.toByte()),
ALARM_COP_TEST_FAILURE(0x32.toByte()),
ALARM_MCTF(0x33.toByte()),
ALARM_ILLEGAL_RESET(0x34.toByte()),
ALARM_VETO_NOT_SET(0x35.toByte()),
ALARM_ILLEGAL_PIN_RESET(0x36.toByte()),
ALARM_INVALID_BEEP_PATTERN(0x37.toByte()),
ALARM_WIRE_STATE_MACHINE(0x38.toByte()),
ALARM_VETO_TEST_DEFAULT(0x39.toByte()),
ALARM_ALERT_INVALID_INDEX(0x3a.toByte()),
ALARM_SAWCOP_TEST_FAIL(0x3b.toByte()),
ALARM_MCUCOP_TEST_FAIL(0x3c.toByte()),
ALARM_STEP_SENSOR_SHORTED(0x3d.toByte()),
ALARM_FLASH_FAILURE(0x3e.toByte()),
ALARM_SPARE63(0x3f.toByte()),
ALARM_SS_OPEN_CNT_EXCEEDED(0x40.toByte()),
ALARM_SS_EXCESSIVE_SUMMED(0x41.toByte()),
ALARM_SS_MIN_PULSE_TRANSITION(0x42.toByte()),
ALARM_SS_DEFAULT(0x43.toByte()),
ALARM_OPEN_WIRE1(0x44.toByte()),
ALARM_OPEN_WIRE2(0x45.toByte()),
ALARM_LOADERR_FAILURE(0x46.toByte()),
ALARM_SAW_VETO_FAILURE(0x47.toByte()),
ALARM_BAD_RFM_CLOCK(0x48.toByte()),
ALARM_BAD_TICK_HIGH(0x49.toByte()),
ALARM_BAD_TICK_PERIOD(0x4a.toByte()),
ALARM_BAD_TRIM_VALUE(0x4b.toByte()),
ALARM_BAD_BUS_CLOCK(0x4c.toByte()),
ALARM_BAD_CAL_MODE(0x4d.toByte()),
ALARM_SAW_TRIM_ERROR(0x4e.toByte()),
ALARM_RFM_CRYSTAL_ERROR(0x4f.toByte()),
ALARM_CALST_TIMEOUT(0x50.toByte()),
ALARM_TICKCNT_ERROR(0x51.toByte()),
ALARM_BAD_RFM_XTAL_START(0x52.toByte()),
ALARM_BAD_RX_SENSENSITIVITY(0x53.toByte()),
ALARM_BAD_TX_PKT_SIZE(0x54.toByte()),
ALARM_TICK_LOW_PHASE_EXCEEDED(0x55.toByte()),
ALARM_TICK_HIGH_PHASE_EXCEEDED(0x56.toByte()),
ALARM_OCCL_CRITVAR_FAIL(0x57.toByte()),
ALARM_OCCL_PARAM(0x58.toByte()),
ALARM_PROG_OCCL_FAIL(0x59.toByte()),
ALARM_PW_TO_HIGH_FOR_OCCL_DET(0x5a.toByte()),
ALARM_OCCL_CSUM(0x5b.toByte()),
ALARM_PRIME_OPEN_CNT_TO_LOW(0x5c.toByte()),
ALARM_BAD_RF_CDTHR(0x5d.toByte()),
ALARM_FLASH_NOT_SECURE(0x5e.toByte()),
ALARM_WIRE_TEST_OPEN_GROUND(0x5f.toByte()),
ALARM_OCCL_STARTUP1(0x60.toByte()),
ALARM_OCCL_STARTUP2(0x61.toByte()),
ALARM_OCCL_EXCESS_TIMEOUTS1(0x62.toByte()),
ALARM_SPARE99(0x63.toByte()),
ALARM_SPARE100(0x64.toByte()),
ALARM_SPARE101(0x65.toByte()),
ALARM_OCCL_EXCESS_TIMEOUTS2(0x66.toByte()),
ALARM_OCCL_EXCESS_TIMEOUTS3(0x67.toByte()),
ALARM_OCCL_NOISY_PULSE_WIDTHS(0x68.toByte()),
ALARM_OCCL_AT_BOLUS_END(0x69.toByte()),
ALARM_OCCL_ABOVE_THRESHOLD(0x6a.toByte()),
ALARM_BASAL_UNDERINFUSION(0x80.toByte()),
ALARM_BASAL_OVERINFUSION(0x81.toByte()),
ALARM_TEMP_UNDERINFUSION(0x82.toByte()),
ALARM_TEMP_OVERINFUSION(0x83.toByte()),
ALARM_BOLUS_UNDERINFUSION(0x84.toByte()),
ALARM_BOLUS_OVERINFUSION(0x85.toByte()),
ALARM_BASAL_OVERINFUSION_PULSE(0x86.toByte()),
ALARM_TEMP_OVERINFUSION_PULSE(0x87.toByte()),
ALARM_BOLUS_OVERINFUSION_PULSE(0x88.toByte()),
ALARM_IMMBOLUS_UNDERINFUSION_PULSE(0x89.toByte()),
ALARM_EXTBOLUS_OVERINFUSION_PULSE(0x8a.toByte()),
ALARM_PROGRAM_CSUM(0x8b.toByte()),
ALARM_UNUSED_140(0x8c.toByte()),
ALARM_UNRECOGNIZED_PULSE(0x8d.toByte()),
ALARM_SYNC_WITHOUT_TEMP_ACTIVE(0x8e.toByte()),
ALARM_INTERLOCK_LOAD(0x8f.toByte()),
ALARM_ILLEGAL_CHAN_PARAM(0x90.toByte()),
ALARM_BASAL_PULSE_CHAN_INACTIVE(0x91.toByte()),
ALARM_TEMP_PULSE_CHAN_INACTIVE(0x92.toByte()),
ALARM_BOLUS_PULSE_CHAN_INACTIVE(0x93.toByte()),
ALARM_INT_SEMAPHORE_NOT_SET(0x94.toByte()),
ALARM_ILLEGAL_INTERLOCK_CHAN(0x95.toByte()),
ALARM_TERMINATE_BOLUS(0x96.toByte()),
ALARM_OPEN_TRANSITIONS_COUNT(0x97.toByte()),
ALARM_BLE_TO(0xa0.toByte()),
ALARM_BLE_INITIATED(0xa1.toByte()),
ALARM_BLE_UNK_ALARM(0xa2.toByte()),
ALARM_UNUSED_163(0xa3.toByte()),
ALARM_UNUSED_164(0xa4.toByte()),
ALARM_UNUSED_165(0xa5.toByte()),
ALARM_BLE_IAAS(0xa6.toByte()),
ALARM_UNUSED_167(0xa7.toByte()),
ALARM_CRC_FAILURE(0xa8.toByte()),
ALARM_BLE_WD_PING_TIMEOUT(0xa9.toByte()),
ALARM_BLE_EXCESSIVE_RESETS(0xaa.toByte()),
ALARM_BLE_NAK_ERROR(0xab.toByte()),
ALARM_BLE_REQ_HIGH_TIMEOUT(0xac.toByte()),
ALARM_BLE_UNKNOWN_RESP(0xad.toByte()),
ALARM_BLE_UNUSED_174(0xae.toByte()),
ALARM_BLE_REQ_STUCK_HIGH(0xaf.toByte()),
ALARM_BLE_STATE_MACHINE_1(0xb1.toByte()),
ALARM_BLE_STATE_MACHINE_2(0xb2.toByte()),
ALARM_BLE_UNUSED_179(0xb3.toByte()),
ALARM_BLE_ARB_LOST(0xb4.toByte()),
ALARM_BLE_ER48_DUAL_NACK(0xc0.toByte()),
ALARM_BLE_QN_EXCEED_MAX_RETRY(0xc1.toByte()),
ALARM_BLE_QN_CRIT_VAR_FAIL(0xc2.toByte()),
UNKNOWN(0xff.toByte());
companion object {
fun byValue(value: Byte): AlarmType {
for (type in values()) {
if (type.value == value) {
return type
}
}
return UNKNOWN
}
}
}

View file

@ -1,61 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
import java.nio.ByteBuffer;
public class AlertConfiguration implements Encodable {
private AlertSlot slot;
private boolean enabled;
private short durationInMinutes;
private boolean autoOff;
private AlertTriggerType triggerType;
private short offsetInMinutesOrThresholdInMicroLiters;
private BeepType beepType;
private BeepRepetitionType beepRepetition;
public AlertConfiguration(AlertSlot slot, boolean enabled, short durationInMinutes, boolean autoOff, AlertTriggerType triggerType, short offsetInMinutesOrThresholdInMicroLiters, BeepType beepType, BeepRepetitionType beepRepetition) {
this.slot = slot;
this.enabled = enabled;
this.durationInMinutes = durationInMinutes;
this.autoOff = autoOff;
this.triggerType = triggerType;
this.offsetInMinutesOrThresholdInMicroLiters = offsetInMinutesOrThresholdInMicroLiters;
this.beepType = beepType;
this.beepRepetition = beepRepetition;
}
@Override public byte[] getEncoded() {
byte firstByte = (byte) (slot.getValue() << 4);
if (enabled) {
firstByte |= 1 << 3;
}
if (triggerType == AlertTriggerType.RESERVOIR_VOLUME_TRIGGER) {
firstByte |= 1 << 2;
}
if (autoOff) {
firstByte |= 1 << 1;
}
firstByte |= ((durationInMinutes >> 8) & 0x01);
return ByteBuffer.allocate(6) //
.put(firstByte)
.put((byte) durationInMinutes) //
.putShort(offsetInMinutesOrThresholdInMicroLiters) //
.put(beepRepetition.getValue()) //
.put(beepType.getValue()) //
.array();
}
@Override public String toString() {
return "AlertConfiguration{" +
"slot=" + slot +
", enabled=" + enabled +
", durationInMinutes=" + durationInMinutes +
", autoOff=" + autoOff +
", triggerType=" + triggerType +
", offsetInMinutesOrThresholdInMicroLiters=" + offsetInMinutesOrThresholdInMicroLiters +
", beepType=" + beepType +
", beepRepetition=" + beepRepetition +
'}';
}
}

View file

@ -0,0 +1,51 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
import java.nio.ByteBuffer
import kotlin.experimental.or
class AlertConfiguration(
private val slot: AlertSlot,
private val enabled: Boolean,
private val durationInMinutes: Short,
private val autoOff: Boolean,
private val triggerType: AlertTriggerType,
private val offsetInMinutesOrThresholdInMicroLiters: Short,
private val beepType: BeepType,
private val beepRepetition: BeepRepetitionType
) : Encodable {
override val encoded: ByteArray
get() {
var firstByte = (slot.value.toInt() shl 4).toByte()
if (enabled) {
firstByte = firstByte or (1 shl 3)
}
if (triggerType == AlertTriggerType.RESERVOIR_VOLUME_TRIGGER) {
firstByte = firstByte or (1 shl 2)
}
if (autoOff) {
firstByte = firstByte or (1 shl 1)
}
firstByte = firstByte or ((durationInMinutes.toInt() shr 8 and 0x01).toByte()) //Todo bitstuff
return ByteBuffer.allocate(6) //
.put(firstByte)
.put(durationInMinutes.toByte()) //
.putShort(offsetInMinutesOrThresholdInMicroLiters) //
.put(beepRepetition.value) //
.put(beepType.value) //
.array()
}
override fun toString(): String {
return "AlertConfiguration{" +
"slot=" + slot +
", enabled=" + enabled +
", durationInMinutes=" + durationInMinutes +
", autoOff=" + autoOff +
", triggerType=" + triggerType +
", offsetInMinutesOrThresholdInMicroLiters=" + offsetInMinutesOrThresholdInMicroLiters +
", beepType=" + beepType +
", beepRepetition=" + beepRepetition +
'}'
}
}

View file

@ -1,32 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum AlertSlot {
AUTO_OFF((byte) 0x00),
MULTI_COMMAND((byte) 0x01),
EXPIRATION_IMMINENT((byte) 0x02),
USER_SET_EXPIRATION((byte) 0x03),
LOW_RESERVOIR((byte) 0x04),
SUSPEND_IN_PROGRESS((byte) 0x05),
SUSPEND_ENDED((byte) 0x06),
EXPIRATION((byte) 0x07),
UNKNOWN((byte) 0xff);
private byte value;
AlertSlot(byte value) {
this.value = value;
}
public static AlertSlot byValue(byte value) {
for (AlertSlot slot : values()) {
if (slot.value == value) {
return slot;
}
}
return UNKNOWN;
}
public byte getValue() {
return value;
}
}

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class AlertSlot(
val value: Byte
) {
AUTO_OFF(0x00.toByte()),
MULTI_COMMAND(0x01.toByte()),
EXPIRATION_IMMINENT(0x02.toByte()),
USER_SET_EXPIRATION(0x03.toByte()),
LOW_RESERVOIR(0x04.toByte()),
SUSPEND_IN_PROGRESS(0x05.toByte()),
SUSPEND_ENDED(0x06.toByte()),
EXPIRATION(0x07.toByte()),
UNKNOWN(0xff.toByte());
companion object {
fun byValue(value: Byte): AlertSlot {
for (slot in values()) {
if (slot.value == value) {
return slot
}
}
return UNKNOWN
}
}
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
public enum AlertTriggerType {
enum class AlertTriggerType {
TIME_TRIGGER,
RESERVOIR_VOLUME_TRIGGER
}
}

View file

@ -1,92 +0,0 @@
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 +
'}';
}
}

View file

@ -0,0 +1,51 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
import java.util.*
class BasalProgram(
segments: List<Segment>
) {
val segments: MutableList<Segment> = segments.toMutableList()
get() = Collections.unmodifiableList(field) // TODO Adrian: moved method here
fun addSegment(segment: Segment) {
segments.add(segment)
}
fun hasZeroUnitSegments() = segments.any { it.basalRateInHundredthUnitsPerHour == 0 }
fun isZeroBasal() = segments.sumBy(Segment::basalRateInHundredthUnitsPerHour) == 0
fun rateAt(date: Date): Double = 0.0 // TODO
class Segment(val startSlotIndex: Short, val endSlotIndex: Short, val basalRateInHundredthUnitsPerHour: Int) {
fun getPulsesPerHour(): Short {
return (basalRateInHundredthUnitsPerHour * PULSES_PER_UNIT / 100).toShort()
}
fun getNumberOfSlots(): Short {
return (endSlotIndex - startSlotIndex).toShort()
}
override fun toString(): String {
return "Segment{" +
"startSlotIndex=" + startSlotIndex +
", endSlotIndex=" + endSlotIndex +
", basalRateInHundredthUnitsPerHour=" + basalRateInHundredthUnitsPerHour +
'}'
}
companion object {
private const val PULSES_PER_UNIT: Byte = 20
}
}
override fun toString(): String {
return "BasalProgram{" +
"segments=" + segments +
'}'
}
}

View file

@ -1,20 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
// FIXME names
public enum BeepRepetitionType {
XXX((byte) 0x01), // Used in low reservoir alert
XXX2((byte) 0x03), // Used in user pod expiration alert
XXX3((byte) 0x05), // Used in pod expiration alert
XXX4((byte) 0x06), // Used in imminent pod expiration alert
XXX5((byte) 0x08); // Used in lump of coal alert
private byte value;
BeepRepetitionType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
// FIXME names
enum class BeepRepetitionType(
val value: Byte
) {
XXX(0x01.toByte()), // Used in lump of coal alert
XXX2(0x03.toByte()), // Used in low reservoir alert
XXX3(0x05.toByte()), // Used in user pod expiration alert
XXX4(0x06.toByte()), // Used in pod expiration alert
XXX5(0x08.toByte()); // Used in imminent pod expiration alert
}

View file

@ -1,17 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum BeepType {
SILENT((byte) 0x00),
FOUR_TIMES_BIP_BEEP((byte) 0x02), // Used in low reservoir alert, user expiration alert, expiration alert, imminent expiration alert, lump of coal alert
LONG_SINGLE_BEEP((byte) 0x06); // Used in stop delivery command
private byte value;
BeepType(byte value) {
this.value = value;
}
public byte getValue() {
return value;
}
}

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class BeepType( // Used in stop delivery command
val value: Byte
) {
SILENT(0x00.toByte()),
FOUR_TIMES_BIP_BEEP(0x02.toByte()), // Used in low reservoir alert, user expiration alert, expiration alert, imminent expiration alert, lump of coal alert
LONG_SINGLE_BEEP(0x06.toByte());
}

View file

@ -1,26 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum DeliveryStatus {
SUSPENDED((byte) 0x00),
BASAL_ACTIVE((byte) 0x01),
TEMP_BASAL_ACTIVE((byte) 0x02),
PRIMING((byte) 0x04),
BOLUS_AND_BASAL_ACTIVE((byte) 0x05),
BOLUS_AND_TEMP_BASAL_ACTIVE((byte) 0x06),
UNKNOWN((byte) 0xff);
private byte value;
DeliveryStatus(byte value) {
this.value = value;
}
public static DeliveryStatus byValue(byte value) {
for (DeliveryStatus status : values()) {
if (status.value == value) {
return status;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,26 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class DeliveryStatus(
private val value: Byte
) {
SUSPENDED(0x00.toByte()),
BASAL_ACTIVE(0x01.toByte()),
TEMP_BASAL_ACTIVE(0x02.toByte()),
PRIMING(0x04.toByte()),
BOLUS_AND_BASAL_ACTIVE(0x05.toByte()),
BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()),
UNKNOWN(0xff.toByte());
companion object {
fun byValue(value: Byte): DeliveryStatus {
for (status in values()) {
if (status.value == value) {
return status
}
}
return UNKNOWN
}
}
}

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public interface Encodable {
byte[] getEncoded();
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
interface Encodable {
val encoded: ByteArray
}

View file

@ -1,49 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum NakErrorType {
FLASH_WRITE((byte) 0x01),
FLASH_ERASE((byte) 0x02),
FLASH_OPERATION((byte) 0x03),
FLASH_ADDR((byte) 0x04),
POD_STATE((byte) 0x05),
CRITICAL_VARIABLE((byte) 0x06),
ILLEGAL_PARAM((byte) 0x07),
BOLUS_CRITICAL_VAR((byte) 0x08),
INT_ILLEGAL_PARAM((byte) 0x09),
ILLEGAL_CHECKSUM((byte) 0x0a),
INVALID_MSG_LEN((byte) 0x0b),
PUMP_STATE((byte) 0x0c),
ILLEGAL_COMMAND((byte) 0x0d),
ILLEGAL_FILL_STATE((byte) 0x0e),
MAX_READWRITE_SIZE((byte) 0x0f),
ILLEGAL_READ_ADDRESS((byte) 0x10),
ILLEGAL_READ_MEM_TYPE((byte) 0x11),
INIT_POD((byte) 0x12),
ILLEGAL_CMD_STATE((byte) 0x13),
ILLEGAL_SECURITY_CODE((byte) 0x14),
POD_IN_ALARM((byte) 0x15),
COMD_NOT_SET((byte) 0x16),
ILLEGAL_RX_SENS_VALUE((byte) 0x17),
ILLEGAL_TX_PKT_SIZE((byte) 0x18),
OCCL_PARAMS_ALREADY_SET((byte) 0x19),
OCCL_PARAM((byte) 0x1a),
ILLEGAL_CDTHR_VALUE((byte) 0x1b),
IGNORE_COMMAND((byte) 0x1c),
INVALID_CRC((byte) 0x1d),
UNKNOWN((byte) 0xff);
private byte value;
NakErrorType(byte value) {
this.value = value;
}
public static NakErrorType byValue(byte value) {
for (NakErrorType type : values()) {
if (type.value == value) {
return type;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,49 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class NakErrorType(
private val value: Byte
) {
FLASH_WRITE(0x01.toByte()),
FLASH_ERASE(0x02.toByte()),
FLASH_OPERATION(0x03.toByte()),
FLASH_ADDR(0x04.toByte()),
POD_STATE(0x05.toByte()),
CRITICAL_VARIABLE(0x06.toByte()),
ILLEGAL_PARAM(0x07.toByte()),
BOLUS_CRITICAL_VAR(0x08.toByte()),
INT_ILLEGAL_PARAM(0x09.toByte()),
ILLEGAL_CHECKSUM(0x0a.toByte()),
INVALID_MSG_LEN(0x0b.toByte()),
PUMP_STATE(0x0c.toByte()),
ILLEGAL_COMMAND(0x0d.toByte()),
ILLEGAL_FILL_STATE(0x0e.toByte()),
MAX_READWRITE_SIZE(0x0f.toByte()),
ILLEGAL_READ_ADDRESS(0x10.toByte()),
ILLEGAL_READ_MEM_TYPE(0x11.toByte()),
INIT_POD(0x12.toByte()),
ILLEGAL_CMD_STATE(0x13.toByte()),
ILLEGAL_SECURITY_CODE(0x14.toByte()),
POD_IN_ALARM(0x15.toByte()),
COMD_NOT_SET(0x16.toByte()),
ILLEGAL_RX_SENS_VALUE(0x17.toByte()),
ILLEGAL_TX_PKT_SIZE(0x18.toByte()),
OCCL_PARAMS_ALREADY_SET(0x19.toByte()),
OCCL_PARAM(0x1a.toByte()),
ILLEGAL_CDTHR_VALUE(0x1b.toByte()),
IGNORE_COMMAND(0x1c.toByte()),
INVALID_CRC(0x1d.toByte()),
UNKNOWN(0xff.toByte());
companion object {
fun byValue(value: Byte): NakErrorType {
for (type in values()) {
if (type.value == value) {
return type
}
}
return UNKNOWN
}
}
}

View file

@ -1,26 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public class OmnipodEvent {
public enum OmnipodEventType {
CONNECTED,
ALREADY_CONNECTED,
FAILED_TO_CONNECT,
DISCONNECTED,
COMMAND_SENT,
GOT_POD_VERSION,
SET_UNIQUE_ID,
PRIMED_PUMP,
FINISHED_ACTIVATION_1,
PROGRAMMED_BASAL,
PROGRAMMED_ALERTS,
SET_BEEPS,
INSERTED_CANNULA,
FINISHED_ACTIVATION_2,
PROGRAMMED_TEMP_BASAL,
STARTED_BOLUS,
STOPPED_DELIVERY,
SILENCED_ALERTS,
DEACTIVATED,
COMMAND_SENDING,
}
}

View file

@ -1,36 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public enum PodStatus {
UNINITIALIZED((byte) 0x00),
MFG_TEST((byte) 0x01),
FILLED((byte) 0x02),
UID_SET((byte) 0x03),
ENGAGING_CLUTCH_DRIVE((byte) 0x04),
CLUTCH_DRIVE_ENGAGED((byte) 0x05),
BASAL_PROGRAM_RUNNING((byte) 0x06),
PRIMING((byte) 0x07),
RUNNING_ABOVE_MIN_VOLUME((byte) 0x08),
RUNNING_BELOW_MIN_VOLUME((byte) 0x09),
UNUSED_10((byte) 0x0a),
UNUSED_11((byte) 0x0b),
UNUSED_12((byte) 0x0c),
ALARM((byte) 0x0d),
LUMP_OF_COAL((byte) 0x0e),
DEACTIVATED((byte) 0x0f),
UNKNOWN((byte) 0xff);
private byte value;
PodStatus(byte value) {
this.value = value;
}
public static PodStatus byValue(byte value) {
for (PodStatus status : values()) {
if (status.value == value) {
return status;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
enum class PodStatus(
private val value: Byte
) {
UNINITIALIZED(0x00.toByte()),
MFG_TEST(0x01.toByte()),
FILLED(0x02.toByte()),
UID_SET(0x03.toByte()),
ENGAGING_CLUTCH_DRIVE(0x04.toByte()),
CLUTCH_DRIVE_ENGAGED(0x05.toByte()),
BASAL_PROGRAM_SET(0x06.toByte()),
PRIMING(0x07.toByte()),
RUNNING_ABOVE_MIN_VOLUME(0x08.toByte()),
RUNNING_BELOW_MIN_VOLUME(0x09.toByte()),
UNUSED_10(0x0a.toByte()),
UNUSED_11(0x0b.toByte()),
UNUSED_12(0x0c.toByte()),
ALARM(0x0d.toByte()),
LUMP_OF_COAL(0x0e.toByte()),
DEACTIVATED(0x0f.toByte()),
UNKNOWN(0xff.toByte());
companion object {
fun byValue(value: Byte): PodStatus {
for (status in values()) {
if (status.value == value) {
return status
}
}
return UNKNOWN
}
}
fun isRunning(): Boolean = this == RUNNING_ABOVE_MIN_VOLUME || this == RUNNING_BELOW_MIN_VOLUME
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition;
public class ProgramReminder implements Encodable {
private final boolean atStart;
private final boolean atEnd;
private final byte atInterval;
public ProgramReminder(boolean atStart, boolean atEnd, byte atIntervalInMinutes) {
this.atStart = atStart;
this.atEnd = atEnd;
this.atInterval = atIntervalInMinutes;
}
@Override public byte[] getEncoded() {
return new byte[]{(byte) (((this.atStart ? 1 : 0) << 7)
| ((this.atEnd ? 1 : 0) << 6)
| (this.atInterval & 0x3f))};
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
import java.io.Serializable
import kotlin.experimental.and
class ProgramReminder(
private val atStart: Boolean,
private val atEnd: Boolean,
private val atInterval: Byte
) : Encodable, Serializable {
override val encoded: ByteArray
get() = byteArrayOf(((if (atStart) 1 else 0) shl 7
or ((if (atEnd) 1 else 0) shl 6)
or ((atInterval and 0x3f).toInt())).toByte())
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition
import java.io.Serializable
data class SoftwareVersion(
private val major: Short,
private val minor: Short,
private val interim: Short
) : Serializable {
override fun toString(): String {
return "$major.$minor.$interim"
}
}

View file

@ -1,14 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response;
abstract class ActivationResponseBase extends ResponseBase {
final ResponseType.ActivationResponseType activationResponseType;
ActivationResponseBase(ResponseType.ActivationResponseType activationResponseType, byte[] encoded) {
super(ResponseType.ACTIVATION_RESPONSE, encoded);
this.activationResponseType = activationResponseType;
}
public ResponseType.ActivationResponseType getActivationResponseType() {
return activationResponseType;
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.ActivationResponseType
abstract class ActivationResponseBase(
val activationResponseType: ActivationResponseType,
encoded: ByteArray
) : ResponseBase(ResponseType.ACTIVATION_RESPONSE, encoded)

View file

@ -1,14 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response;
public class AdditionalStatusResponseBase extends ResponseBase {
final ResponseType.AdditionalStatusResponseType statusResponseType;
AdditionalStatusResponseBase(ResponseType.AdditionalStatusResponseType statusResponseType, byte[] encoded) {
super(ResponseType.ADDITIONAL_STATUS_RESPONSE, encoded);
this.statusResponseType = statusResponseType;
}
public ResponseType.AdditionalStatusResponseType getStatusResponseType() {
return statusResponseType;
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.AdditionalStatusResponseType
open class AdditionalStatusResponseBase internal constructor(
val statusResponseType: AdditionalStatusResponseType,
encoded: ByteArray
) : ResponseBase(ResponseType.ADDITIONAL_STATUS_RESPONSE, encoded)

View file

@ -1,240 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response;
import java.nio.ByteBuffer;
import java.util.Arrays;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus;
public class AlarmStatusResponse extends AdditionalStatusResponseBase {
private final byte messageType;
private final short messageLength;
private final byte additionalStatusResponseType;
private final PodStatus podStatus;
private final DeliveryStatus deliveryStatus;
private final short bolusPulsesRemaining;
private final short sequenceNumberOfLastProgrammingCommand;
private final short totalPulsesDelivered;
private final AlarmType alarmType;
private final short alarmTime;
private final short reservoirPulsesRemaining;
private final short minutesSinceActivation;
private final boolean alert0Active;
private final boolean alert1Active;
private final boolean alert2Active;
private final boolean alert3Active;
private final boolean alert4Active;
private final boolean alert5Active;
private final boolean alert6Active;
private final boolean alert7Active;
private final boolean occlusionAlarm;
private final boolean pulseInfoInvalid;
private final PodStatus podStatusWhenAlarmOccurred;
private final boolean immediateBolusWhenAlarmOccurred;
private final byte occlusionType;
private final boolean occurredWhenFetchingImmediateBolusActiveInformation;
private final short rssi;
private final short receiverLowerGain;
private final PodStatus podStatusWhenAlarmOccurred2;
private final short returnAddressOfPodAlarmHandlerCaller;
public AlarmStatusResponse(byte[] encoded) {
super(ResponseType.AdditionalStatusResponseType.ALARM_STATUS, encoded);
messageType = encoded[0];
messageLength = (short) (encoded[1] & 0xff);
additionalStatusResponseType = encoded[2];
podStatus = PodStatus.byValue((byte) (encoded[3] & 0x0f));
deliveryStatus = DeliveryStatus.byValue((byte) (encoded[4] & 0x0f));
bolusPulsesRemaining = (short) (ByteBuffer.wrap(new byte[]{encoded[5], encoded[6]}).getShort() & 2047);
sequenceNumberOfLastProgrammingCommand = (short) (encoded[7] & 0x0f);
totalPulsesDelivered = ByteBuffer.wrap(new byte[]{encoded[8], encoded[9]}).getShort();
alarmType = AlarmType.byValue(encoded[10]);
alarmTime = ByteBuffer.wrap(new byte[]{encoded[11], encoded[12]}).getShort();
reservoirPulsesRemaining = ByteBuffer.wrap(new byte[]{encoded[13], encoded[14]}).getShort();
minutesSinceActivation = ByteBuffer.wrap(new byte[]{encoded[15], encoded[16]}).getShort();
byte activeAlerts = encoded[17];
alert0Active = (activeAlerts & 1) == 1;
alert1Active = ((activeAlerts >>> 1) & 1) == 1;
alert2Active = ((activeAlerts >>> 2) & 1) == 1;
alert3Active = ((activeAlerts >>> 3) & 1) == 1;
alert4Active = ((activeAlerts >>> 4) & 1) == 1;
alert5Active = ((activeAlerts >>> 5) & 1) == 1;
alert6Active = ((activeAlerts >>> 6) & 1) == 1;
alert7Active = ((activeAlerts >>> 7) & 1) == 1;
byte alarmFlags = encoded[18];
occlusionAlarm = (alarmFlags & 1) == 1;
pulseInfoInvalid = ((alarmFlags >> 1) & 1) == 1;
byte byte19 = encoded[19];
byte byte20 = encoded[20];
podStatusWhenAlarmOccurred = PodStatus.byValue((byte) (byte19 & 0x0f));
immediateBolusWhenAlarmOccurred = ((byte19 >> 4) & 1) == 1;
occlusionType = (byte) ((byte19 >> 5) & 3);
occurredWhenFetchingImmediateBolusActiveInformation = ((byte19 >> 7) & 1) == 1;
rssi = (short) (byte20 & 0x3f);
receiverLowerGain = (short) ((byte20 >> 6) & 0x03);
podStatusWhenAlarmOccurred2 = PodStatus.byValue((byte) (encoded[21] & 0x0f));
returnAddressOfPodAlarmHandlerCaller = ByteBuffer.wrap(new byte[]{encoded[22], encoded[23]}).getShort();
}
public byte getMessageType() {
return messageType;
}
public short getMessageLength() {
return messageLength;
}
public byte getAdditionalStatusResponseType() {
return additionalStatusResponseType;
}
public PodStatus getPodStatus() {
return podStatus;
}
public DeliveryStatus getDeliveryStatus() {
return deliveryStatus;
}
public short getBolusPulsesRemaining() {
return bolusPulsesRemaining;
}
public short getSequenceNumberOfLastProgrammingCommand() {
return sequenceNumberOfLastProgrammingCommand;
}
public short getTotalPulsesDelivered() {
return totalPulsesDelivered;
}
public AlarmType getAlarmType() {
return alarmType;
}
public short getAlarmTime() {
return alarmTime;
}
public short getReservoirPulsesRemaining() {
return reservoirPulsesRemaining;
}
public short getMinutesSinceActivation() {
return minutesSinceActivation;
}
public boolean isAlert0Active() {
return alert0Active;
}
public boolean isAlert1Active() {
return alert1Active;
}
public boolean isAlert2Active() {
return alert2Active;
}
public boolean isAlert3Active() {
return alert3Active;
}
public boolean isAlert4Active() {
return alert4Active;
}
public boolean isAlert5Active() {
return alert5Active;
}
public boolean isAlert6Active() {
return alert6Active;
}
public boolean isAlert7Active() {
return alert7Active;
}
public boolean isOcclusionAlarm() {
return occlusionAlarm;
}
public boolean isPulseInfoInvalid() {
return pulseInfoInvalid;
}
public PodStatus getPodStatusWhenAlarmOccurred() {
return podStatusWhenAlarmOccurred;
}
public boolean isImmediateBolusWhenAlarmOccurred() {
return immediateBolusWhenAlarmOccurred;
}
public byte getOcclusionType() {
return occlusionType;
}
public boolean isOccurredWhenFetchingImmediateBolusActiveInformation() {
return occurredWhenFetchingImmediateBolusActiveInformation;
}
public short getRssi() {
return rssi;
}
public short getReceiverLowerGain() {
return receiverLowerGain;
}
public PodStatus getPodStatusWhenAlarmOccurred2() {
return podStatusWhenAlarmOccurred2;
}
public short getReturnAddressOfPodAlarmHandlerCaller() {
return returnAddressOfPodAlarmHandlerCaller;
}
@Override public String toString() {
return "AlarmStatusResponse{" +
"messageType=" + messageType +
", messageLength=" + messageLength +
", additionalStatusResponseType=" + additionalStatusResponseType +
", podStatus=" + podStatus +
", deliveryStatus=" + deliveryStatus +
", bolusPulsesRemaining=" + bolusPulsesRemaining +
", sequenceNumberOfLastProgrammingCommand=" + sequenceNumberOfLastProgrammingCommand +
", totalPulsesDelivered=" + totalPulsesDelivered +
", alarmType=" + alarmType +
", alarmTime=" + alarmTime +
", reservoirPulsesRemaining=" + reservoirPulsesRemaining +
", minutesSinceActivation=" + minutesSinceActivation +
", alert0Active=" + alert0Active +
", alert1Active=" + alert1Active +
", alert2Active=" + alert2Active +
", alert3Active=" + alert3Active +
", alert4Active=" + alert4Active +
", alert5Active=" + alert5Active +
", alert6Active=" + alert6Active +
", alert7Active=" + alert7Active +
", occlusionAlarm=" + occlusionAlarm +
", pulseInfoInvalid=" + pulseInfoInvalid +
", podStatusWhenAlarmOccurred=" + podStatusWhenAlarmOccurred +
", immediateBolusWhenAlarmOccurred=" + immediateBolusWhenAlarmOccurred +
", occlusionType=" + occlusionType +
", occurredWhenFetchingImmediateBolusActiveInformation=" + occurredWhenFetchingImmediateBolusActiveInformation +
", rssi=" + rssi +
", receiverLowerGain=" + receiverLowerGain +
", podStatusWhenAlarmOccurred2=" + podStatusWhenAlarmOccurred2 +
", returnAddressOfPodAlarmHandlerCaller=" + returnAddressOfPodAlarmHandlerCaller +
", statusResponseType=" + statusResponseType +
", responseType=" + responseType +
", encoded=" + Arrays.toString(encoded) +
'}';
}
}

View file

@ -0,0 +1,248 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType.AdditionalStatusResponseType
import java.nio.ByteBuffer
import java.util.*
import kotlin.experimental.and
class AlarmStatusResponse(
encoded: ByteArray
) : AdditionalStatusResponseBase(AdditionalStatusResponseType.ALARM_STATUS, encoded) {
private val messageType: Byte
private val messageLength: Short
private val additionalStatusResponseType: Byte
private val podStatus: PodStatus
private val deliveryStatus: DeliveryStatus
private val bolusPulsesRemaining: Short
private val sequenceNumberOfLastProgrammingCommand: Short
private val totalPulsesDelivered: Short
private val alarmType: AlarmType
private val alarmTime: Short
private val reservoirPulsesRemaining: Short
private val minutesSinceActivation: Short
private val alert0Active: Boolean
private val alert1Active: Boolean
private val alert2Active: Boolean
private val alert3Active: Boolean
private val alert4Active: Boolean
private val alert5Active: Boolean
private val alert6Active: Boolean
private val alert7Active: Boolean
private val occlusionAlarm: Boolean
private val pulseInfoInvalid: Boolean
private val podStatusWhenAlarmOccurred: PodStatus
private val immediateBolusWhenAlarmOccurred: Boolean
private val occlusionType: Byte
private val occurredWhenFetchingImmediateBolusActiveInformation: Boolean
private val rssi: Short
private val receiverLowerGain: Short
private val podStatusWhenAlarmOccurred2: PodStatus
private val returnAddressOfPodAlarmHandlerCaller: Short
fun getMessageType(): Byte {
return messageType
}
fun getMessageLength(): Short {
return messageLength
}
fun getAdditionalStatusResponseType(): Byte {
return additionalStatusResponseType
}
fun getPodStatus(): PodStatus {
return podStatus
}
fun getDeliveryStatus(): DeliveryStatus {
return deliveryStatus
}
fun getBolusPulsesRemaining(): Short {
return bolusPulsesRemaining
}
fun getSequenceNumberOfLastProgrammingCommand(): Short {
return sequenceNumberOfLastProgrammingCommand
}
fun getTotalPulsesDelivered(): Short {
return totalPulsesDelivered
}
fun getAlarmType(): AlarmType {
return alarmType
}
fun getAlarmTime(): Short {
return alarmTime
}
fun getReservoirPulsesRemaining(): Short {
return reservoirPulsesRemaining
}
fun getMinutesSinceActivation(): Short {
return minutesSinceActivation
}
fun isAlert0Active(): Boolean {
return alert0Active
}
fun isAlert1Active(): Boolean {
return alert1Active
}
fun isAlert2Active(): Boolean {
return alert2Active
}
fun isAlert3Active(): Boolean {
return alert3Active
}
fun isAlert4Active(): Boolean {
return alert4Active
}
fun isAlert5Active(): Boolean {
return alert5Active
}
fun isAlert6Active(): Boolean {
return alert6Active
}
fun isAlert7Active(): Boolean {
return alert7Active
}
fun isOcclusionAlarm(): Boolean {
return occlusionAlarm
}
fun isPulseInfoInvalid(): Boolean {
return pulseInfoInvalid
}
fun getPodStatusWhenAlarmOccurred(): PodStatus {
return podStatusWhenAlarmOccurred
}
fun isImmediateBolusWhenAlarmOccurred(): Boolean {
return immediateBolusWhenAlarmOccurred
}
fun getOcclusionType(): Byte {
return occlusionType
}
fun isOccurredWhenFetchingImmediateBolusActiveInformation(): Boolean {
return occurredWhenFetchingImmediateBolusActiveInformation
}
fun getRssi(): Short {
return rssi
}
fun getReceiverLowerGain(): Short {
return receiverLowerGain
}
fun getPodStatusWhenAlarmOccurred2(): PodStatus {
return podStatusWhenAlarmOccurred2
}
fun getReturnAddressOfPodAlarmHandlerCaller(): Short {
return returnAddressOfPodAlarmHandlerCaller
}
override fun toString(): String {
return "AlarmStatusResponse{" +
"messageType=" + messageType +
", messageLength=" + messageLength +
", additionalStatusResponseType=" + additionalStatusResponseType +
", podStatus=" + podStatus +
", deliveryStatus=" + deliveryStatus +
", bolusPulsesRemaining=" + bolusPulsesRemaining +
", sequenceNumberOfLastProgrammingCommand=" + sequenceNumberOfLastProgrammingCommand +
", totalPulsesDelivered=" + totalPulsesDelivered +
", alarmType=" + alarmType +
", alarmTime=" + alarmTime +
", reservoirPulsesRemaining=" + reservoirPulsesRemaining +
", minutesSinceActivation=" + minutesSinceActivation +
", alert0Active=" + alert0Active +
", alert1Active=" + alert1Active +
", alert2Active=" + alert2Active +
", alert3Active=" + alert3Active +
", alert4Active=" + alert4Active +
", alert5Active=" + alert5Active +
", alert6Active=" + alert6Active +
", alert7Active=" + alert7Active +
", occlusionAlarm=" + occlusionAlarm +
", pulseInfoInvalid=" + pulseInfoInvalid +
", podStatusWhenAlarmOccurred=" + podStatusWhenAlarmOccurred +
", immediateBolusWhenAlarmOccurred=" + immediateBolusWhenAlarmOccurred +
", occlusionType=" + occlusionType +
", occurredWhenFetchingImmediateBolusActiveInformation=" + occurredWhenFetchingImmediateBolusActiveInformation +
", rssi=" + rssi +
", receiverLowerGain=" + receiverLowerGain +
", podStatusWhenAlarmOccurred2=" + podStatusWhenAlarmOccurred2 +
", returnAddressOfPodAlarmHandlerCaller=" + returnAddressOfPodAlarmHandlerCaller +
", statusResponseType=" + statusResponseType +
", responseType=" + responseType +
", encoded=" + Arrays.toString(encoded) +
'}'
}
init {
messageType = encoded[0]
messageLength = (encoded[1].toInt() and 0xff).toShort()
additionalStatusResponseType = encoded[2]
podStatus = PodStatus.byValue((encoded[3] and 0x0f))
deliveryStatus = DeliveryStatus.byValue((encoded[4] and 0x0f))
bolusPulsesRemaining = (ByteBuffer.wrap(byteArrayOf(encoded[5], encoded[6])).short and 2047)
sequenceNumberOfLastProgrammingCommand = (encoded[7] and 0x0f).toShort()
totalPulsesDelivered = ByteBuffer.wrap(byteArrayOf(encoded[8], encoded[9])).short
alarmType = AlarmType.byValue(encoded[10])
alarmTime = ByteBuffer.wrap(byteArrayOf(encoded[11], encoded[12])).short
reservoirPulsesRemaining = ByteBuffer.wrap(byteArrayOf(encoded[13], encoded[14])).short
minutesSinceActivation = ByteBuffer.wrap(byteArrayOf(encoded[15], encoded[16])).short
val activeAlerts = encoded[17].toInt() // TODO: toInt()?
alert0Active = activeAlerts and 1 == 1
alert1Active = activeAlerts ushr 1 and 1 == 1
alert2Active = activeAlerts ushr 2 and 1 == 1
alert3Active = activeAlerts ushr 3 and 1 == 1
alert4Active = activeAlerts ushr 4 and 1 == 1
alert5Active = activeAlerts ushr 5 and 1 == 1
alert6Active = activeAlerts ushr 6 and 1 == 1
alert7Active = activeAlerts ushr 7 and 1 == 1
val alarmFlags = encoded[18]
occlusionAlarm = (alarmFlags.toInt() and 1) == 1
pulseInfoInvalid = alarmFlags shr 1 and 1 == 1
val byte19 = encoded[19]
val byte20 = encoded[20]
podStatusWhenAlarmOccurred = PodStatus.byValue((byte19 and 0x0f))
immediateBolusWhenAlarmOccurred = byte19 shr 4 and 1 == 1
occlusionType = ((byte19 shr 5 and 3).toByte())
occurredWhenFetchingImmediateBolusActiveInformation = byte19 shr 7 and 1 == 1
rssi = (byte20 and 0x3f).toShort()
receiverLowerGain = ((byte20 shr 6 and 0x03).toShort())
podStatusWhenAlarmOccurred2 = PodStatus.byValue((encoded[21] and 0x0f))
returnAddressOfPodAlarmHandlerCaller = ByteBuffer.wrap(byteArrayOf(encoded[22], encoded[23])).short
}
//TODO autoconvert to Int ok?
private infix fun Byte.ushr(i: Int) = toInt() ushr i
private infix fun Short.shr(i: Int): Int = toInt() shr i
private infix fun Byte.shl(i: Int): Int = toInt() shl i
private infix fun Byte.shr(i: Int): Int = toInt() shr i
}

View file

@ -1,136 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response;
import java.util.Arrays;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus;
public class DefaultStatusResponse extends ResponseBase {
private final byte messageType;
private final DeliveryStatus deliveryStatus;
private final PodStatus podStatus;
private final short totalPulsesDelivered;
private final short sequenceNumberOfLastProgrammingCommand;
private final short bolusPulsesRemaining;
private final boolean occlusionAlertActive;
private final boolean alert1Active;
private final boolean alert2Active;
private final boolean alert3Active;
private final boolean alert4Active;
private final boolean alert5Active;
private final boolean alert6Active;
private final boolean alert7Active;
private final short minutesSinceActivation;
private final short reservoirPulsesRemaining;
public DefaultStatusResponse(byte[] encoded) {
super(ResponseType.DEFAULT_STATUS_RESPONSE, encoded);
messageType = encoded[0];
deliveryStatus = DeliveryStatus.byValue((byte) ((encoded[1] >> 4) & 0x0f));
podStatus = PodStatus.byValue((byte) (encoded[1] & 0x0f));
totalPulsesDelivered = (short) (((encoded[2] & 0x0f) << 12) | ((encoded[3] & 0xff) << 1) | ((encoded[4] & 0xff) >>> 7));
sequenceNumberOfLastProgrammingCommand = (byte) ((encoded[4] >>> 3) & 0x0f);
bolusPulsesRemaining = (short) ((((encoded[4] & 0x07) << 10) | (encoded[5] & 0xff)) & 2047);
short activeAlerts = (short) (((encoded[6] & 0xff) << 1) | (encoded[7] >>> 7));
occlusionAlertActive = (activeAlerts & 1) == 1;
alert1Active = ((activeAlerts >> 1) & 1) == 1;
alert2Active = ((activeAlerts >> 2) & 1) == 1;
alert3Active = ((activeAlerts >> 3) & 1) == 1;
alert4Active = ((activeAlerts >> 4) & 1) == 1;
alert5Active = ((activeAlerts >> 5) & 1) == 1;
alert6Active = ((activeAlerts >> 6) & 1) == 1;
alert7Active = ((activeAlerts >> 7) & 1) == 1;
minutesSinceActivation = (short) (((encoded[7] & 0x7f) << 6) | (((encoded[8] & 0xff) >>> 2) & 0x3f));
reservoirPulsesRemaining = (short) (((encoded[8] << 8) | encoded[9]) & 0x3ff);
}
public byte getMessageType() {
return messageType;
}
public DeliveryStatus getDeliveryStatus() {
return deliveryStatus;
}
public PodStatus getPodStatus() {
return podStatus;
}
public short getTotalPulsesDelivered() {
return totalPulsesDelivered;
}
public short getSequenceNumberOfLastProgrammingCommand() {
return sequenceNumberOfLastProgrammingCommand;
}
public short getBolusPulsesRemaining() {
return bolusPulsesRemaining;
}
public boolean isOcclusionAlertActive() {
return occlusionAlertActive;
}
public boolean isAlert1Active() {
return alert1Active;
}
public boolean isAlert2Active() {
return alert2Active;
}
public boolean isAlert3Active() {
return alert3Active;
}
public boolean isAlert4Active() {
return alert4Active;
}
public boolean isAlert5Active() {
return alert5Active;
}
public boolean isAlert6Active() {
return alert6Active;
}
public boolean isAlert7Active() {
return alert7Active;
}
public short getMinutesSinceActivation() {
return minutesSinceActivation;
}
public short getReservoirPulsesRemaining() {
return reservoirPulsesRemaining;
}
@Override public String toString() {
return "DefaultStatusResponse{" +
"messageType=" + messageType +
", deliveryStatus=" + deliveryStatus +
", podStatus=" + podStatus +
", totalPulsesDelivered=" + totalPulsesDelivered +
", sequenceNumberOfLastProgrammingCommand=" + sequenceNumberOfLastProgrammingCommand +
", bolusPulsesRemaining=" + bolusPulsesRemaining +
", occlusionAlertActive=" + occlusionAlertActive +
", alert1Active=" + alert1Active +
", alert2Active=" + alert2Active +
", alert3Active=" + alert3Active +
", alert4Active=" + alert4Active +
", alert5Active=" + alert5Active +
", alert6Active=" + alert6Active +
", alert7Active=" + alert7Active +
", minutesSinceActivation=" + minutesSinceActivation +
", reservoirPulsesRemaining=" + reservoirPulsesRemaining +
", responseType=" + responseType +
", encoded=" + Arrays.toString(encoded) +
'}';
}
}

View file

@ -0,0 +1,120 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
import kotlin.experimental.and
class DefaultStatusResponse(
encoded: ByteArray
) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
// TODO: Here is a lot of bitshifting that had to be changed. we should go over it.
private val messageType: Byte = encoded[0]
private val deliveryStatus: DeliveryStatus = DeliveryStatus.byValue((encoded[1].toInt() shr 4 and 0x0f).toByte())
private val podStatus: PodStatus = PodStatus.byValue((encoded[1] and 0x0f) as Byte)
private val totalPulsesDelivered: Short = ((encoded[2] and 0x0f shl 12 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort())
private val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort()
private val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort())
private val activeAlerts = (encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toShort()
private val occlusionAlertActive: Boolean = (activeAlerts and 1).toInt() == 1
private val alert1Active: Boolean = activeAlerts shr 1 and 1 == 1
private val alert2Active: Boolean = activeAlerts shr 2 and 1 == 1
private val alert3Active: Boolean = activeAlerts shr 3 and 1 == 1
private val alert4Active: Boolean = activeAlerts shr 4 and 1 == 1
private val alert5Active: Boolean = activeAlerts shr 5 and 1 == 1
private val alert6Active: Boolean = activeAlerts shr 6 and 1 == 1
private val alert7Active: Boolean = activeAlerts shr 7 and 1 == 1
private val minutesSinceActivation: Short = (encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort()
private val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort()
fun getMessageType(): Byte {
return messageType
}
fun getDeliveryStatus(): DeliveryStatus {
return deliveryStatus
}
fun getPodStatus(): PodStatus {
return podStatus
}
fun getTotalPulsesDelivered(): Short {
return totalPulsesDelivered
}
fun getSequenceNumberOfLastProgrammingCommand(): Short {
return sequenceNumberOfLastProgrammingCommand
}
fun getBolusPulsesRemaining(): Short {
return bolusPulsesRemaining
}
fun isOcclusionAlertActive(): Boolean {
return occlusionAlertActive
}
fun isAlert1Active(): Boolean {
return alert1Active
}
fun isAlert2Active(): Boolean {
return alert2Active
}
fun isAlert3Active(): Boolean {
return alert3Active
}
fun isAlert4Active(): Boolean {
return alert4Active
}
fun isAlert5Active(): Boolean {
return alert5Active
}
fun isAlert6Active(): Boolean {
return alert6Active
}
fun isAlert7Active(): Boolean {
return alert7Active
}
fun getMinutesSinceActivation(): Short {
return minutesSinceActivation
}
fun getReservoirPulsesRemaining(): Short {
return reservoirPulsesRemaining
}
override fun toString(): String {
return "DefaultStatusResponse{" +
"messageType=" + messageType +
", deliveryStatus=" + deliveryStatus +
", podStatus=" + podStatus +
", totalPulsesDelivered=" + totalPulsesDelivered +
", sequenceNumberOfLastProgrammingCommand=" + sequenceNumberOfLastProgrammingCommand +
", bolusPulsesRemaining=" + bolusPulsesRemaining +
", occlusionAlertActive=" + occlusionAlertActive +
", alert1Active=" + alert1Active +
", alert2Active=" + alert2Active +
", alert3Active=" + alert3Active +
", alert4Active=" + alert4Active +
", alert5Active=" + alert5Active +
", alert6Active=" + alert6Active +
", alert7Active=" + alert7Active +
", minutesSinceActivation=" + minutesSinceActivation +
", reservoirPulsesRemaining=" + reservoirPulsesRemaining +
", responseType=" + responseType +
", encoded=" + encoded.contentToString() +
'}'
}
}
//TODO autoconvert to Int ok?
private infix fun Byte.ushr(i: Int) = toInt() ushr i
private infix fun Short.shr(i: Int): Int = toInt() shr i
private infix fun Byte.shl(i: Int): Int = toInt() shl i

View file

@ -1,71 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response;
import java.util.Arrays;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlarmType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.NakErrorType;
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus;
public class NakResponse extends ResponseBase {
private final byte messageType;
private final short messageLength;
private final NakErrorType nakErrorType;
private final AlarmType alarmType;
private final PodStatus podStatus;
private final short securityNakSyncCount;
public NakResponse(byte[] encoded) {
super(ResponseType.NAK_RESPONSE, encoded);
this.messageType = encoded[0];
this.messageLength = encoded[1];
this.nakErrorType = NakErrorType.byValue(encoded[2]);
byte byte3 = encoded[3];
byte byte4 = encoded[4];
if (nakErrorType == NakErrorType.ILLEGAL_SECURITY_CODE) {
this.securityNakSyncCount = (short) ((byte3 << 8) | byte4);
this.alarmType = null;
this.podStatus = null;
} else {
this.securityNakSyncCount = 0;
this.alarmType = AlarmType.byValue(byte3);
this.podStatus = PodStatus.byValue(byte4);
}
}
public byte getMessageType() {
return messageType;
}
public short getMessageLength() {
return messageLength;
}
public NakErrorType getNakErrorType() {
return nakErrorType;
}
public AlarmType getAlarmType() {
return alarmType;
}
public PodStatus getPodStatus() {
return podStatus;
}
public short getSecurityNakSyncCount() {
return securityNakSyncCount;
}
@Override public String toString() {
return "NakResponse{" +
"messageType=" + messageType +
", messageLength=" + messageLength +
", nakErrorType=" + nakErrorType +
", alarmType=" + alarmType +
", podStatus=" + podStatus +
", securityNakSyncCount=" + securityNakSyncCount +
", responseType=" + responseType +
", encoded=" + Arrays.toString(encoded) +
'}';
}
}

Some files were not shown because too many files have changed in this diff Show more