Merge pull request #470 from jotomo/meallink+combo

Combo PumpSync update
This commit is contained in:
Milos Kozak 2021-04-13 15:35:57 +02:00 committed by GitHub
commit b9ad45ae32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 84 deletions

View file

@ -6,6 +6,7 @@ import android.os.SystemClock;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import info.nightscout.androidaps.extensions.PumpStateExtensionKt;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.json.JSONObject; import org.json.JSONObject;
@ -96,7 +97,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
*/ */
private volatile boolean scripterIsBolusing; private volatile boolean scripterIsBolusing;
/** /**
* This is set to true to request a bolus cancellation. {@link #deliverBolus(DetailedBolusInfo)} * This is set to true to request a bolus cancellation. {@link #deliverTreatment(DetailedBolusInfo)} (DetailedBolusInfo)}
* will reset this flag. * will reset this flag.
*/ */
private volatile boolean cancelBolus; private volatile boolean cancelBolus;
@ -105,7 +106,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
* This is set (in {@link #checkHistory()} whenever a connection to the pump is made and * This is set (in {@link #checkHistory()} whenever a connection to the pump is made and
* indicates if new history records on the pump have been found. This effectively blocks * indicates if new history records on the pump have been found. This effectively blocks
* high temps ({@link #setTempBasalPercent(Integer, Integer, PumpSync.TemporaryBasalType)} and boluses * high temps ({@link #setTempBasalPercent(Integer, Integer, PumpSync.TemporaryBasalType)} and boluses
* ({@link #deliverBolus(DetailedBolusInfo)} till the queue is empty and the connection * ({@link #deliverTreatment(DetailedBolusInfo)} till the queue is empty and the connection
* is shut down. * is shut down.
* {@link #initializePump()} resets this since on startup the history is allowed to have * {@link #initializePump()} resets this since on startup the history is allowed to have
* changed (and the user can't possible have already calculated anything with out of date IOB). * changed (and the user can't possible have already calculated anything with out of date IOB).
@ -120,7 +121,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
/** /**
* Cache of the last <=2 boluses on the pump. Used to detect changes in pump history, * Cache of the last <=2 boluses on the pump. Used to detect changes in pump history,
* requiring reading more pump history. This is read/set in {@link #checkHistory()} when changed * requiring reading more pump history. This is read/set in {@link #checkHistory()} when changed
* pump history was detected and was read, as well as in {@link #deliverBolus(DetailedBolusInfo)} * pump history was detected and was read, as well as in {@link #deliverTreatment(DetailedBolusInfo)}
* after bolus delivery. Newest record is the first one. * after bolus delivery. Newest record is the first one.
*/ */
private volatile List<Bolus> recentBoluses = new ArrayList<>(0); private volatile List<Bolus> recentBoluses = new ArrayList<>(0);
@ -474,41 +475,9 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
*/ */
@NonNull @Override @NonNull @Override
public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) {
try { if (detailedBolusInfo.insulin == 0 || detailedBolusInfo.carbs > 0) {
if (detailedBolusInfo.insulin == 0 && detailedBolusInfo.carbs == 0) { throw new IllegalArgumentException(detailedBolusInfo.toString());
// neither carbs nor bolus requested
getAapsLogger().error("deliverTreatment: Invalid input");
return new PumpEnactResult(getInjector()).success(false).enacted(false)
.bolusDelivered(0d).carbsDelivered(0d)
.comment(R.string.invalidinput);
// } else if (detailedBolusInfo.insulin > 0) {
} else {
// bolus needed, ask pump to deliver it
return deliverBolus(detailedBolusInfo);
}
/* This should not happen anymore
else {
// no bolus required, carb only treatment
treatmentsPlugin.addToHistoryTreatment(detailedBolusInfo, false);
EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE;
bolusingEvent.setT(new EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB));
bolusingEvent.setPercent(100);
rxBus.send(bolusingEvent);
return new PumpEnactResult(getInjector()).success(true).enacted(true)
.bolusDelivered(0d).carbsDelivered(detailedBolusInfo.carbs)
.comment(R.string.virtualpump_resultok);
}
*/
} finally {
rxBus.send(new EventComboPumpUpdateGUI());
} }
}
@NonNull
private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
try { try {
pump.activity = getResourceHelper().gs(R.string.combo_pump_action_bolusing, detailedBolusInfo.insulin); pump.activity = getResourceHelper().gs(R.string.combo_pump_action_bolusing, detailedBolusInfo.insulin);
rxBus.send(new EventComboPumpUpdateGUI()); rxBus.send(new EventComboPumpUpdateGUI());
@ -668,39 +637,18 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
* Creates a treatment record based on the request in DetailBolusInfo and the delivered bolus. * Creates a treatment record based on the request in DetailBolusInfo and the delivered bolus.
*/ */
private boolean addBolusToTreatments(DetailedBolusInfo detailedBolusInfo, Bolus lastPumpBolus) { private boolean addBolusToTreatments(DetailedBolusInfo detailedBolusInfo, Bolus lastPumpBolus) {
DetailedBolusInfo bolusInfo = detailedBolusInfo.copy();
bolusInfo.setBolusTimestamp(calculateFakeBolusDate(lastPumpBolus));
bolusInfo.setPumpType(PumpType.ACCU_CHEK_COMBO);
bolusInfo.setPumpSerial(serialNumber());
bolusInfo.setBolusPumpId(bolusInfo.timestamp);
bolusInfo.insulin = lastPumpBolus.amount;
try { try {
/* this should not happen
if (bolusInfo.carbs > 0 && bolusInfo.carbTime != 0) {
// split out a separate carbs record without a pumpId
DetailedBolusInfo carbInfo = new DetailedBolusInfo();
carbInfo.timestamp = bolusInfo.timestamp + bolusInfo.carbTime * 60L * 1000L;
carbInfo.carbs = bolusInfo.carbs;
carbInfo.setPumpType(PumpType.USER);
treatmentsPlugin.addToHistoryTreatment(carbInfo, true);
// remove carbs from bolusInfo to not trigger any unwanted code paths in
// TreatmentsPlugin.addToHistoryTreatment() method
bolusInfo.carbTime = 0;
bolusInfo.carbs = 0;
}
*/
pumpSync.syncBolusWithPumpId( pumpSync.syncBolusWithPumpId(
calculateFakeBolusDate(lastPumpBolus), lastPumpBolus.timestamp,
lastPumpBolus.amount, lastPumpBolus.amount,
detailedBolusInfo.getBolusType(), detailedBolusInfo.getBolusType(),
bolusInfo.timestamp, generatePumpBolusId(lastPumpBolus),
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
} catch (Exception e) { } catch (Exception e) {
getAapsLogger().error("Adding treatment record failed", e); getAapsLogger().error("Adding treatment record failed", e);
if (bolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB) { if (detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB) {
Notification notification = new Notification(Notification.COMBO_PUMP_ALARM, getResourceHelper().gs(R.string.combo_error_updating_treatment_record), Notification.URGENT); Notification notification = new Notification(Notification.COMBO_PUMP_ALARM, getResourceHelper().gs(R.string.combo_error_updating_treatment_record), Notification.URGENT);
rxBus.send(new EventNewNotification(notification)); rxBus.send(new EventNewNotification(notification));
} }
@ -791,7 +739,12 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
T.mins(state.tbrRemainingDuration).msecs(), T.mins(state.tbrRemainingDuration).msecs(),
false, false,
tbrType, tbrType,
state.timestamp, // no pumpId available ???? // There are no IDs for TBRs on the pump and none is calculated (in contrast to boluses).
// The current time is used here as an ID, which has no meaning and does not allow identifying
// the record on the pump (which isn't needed), but only needs to be unique.
// Generally, TBR records are created when a TBR is set by AAPS or when a change on the pump has
// been detected, rather than checking the pumps history of TBRs.
state.timestamp,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
@ -820,9 +773,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
@NonNull @Override @NonNull @Override
public PumpEnactResult cancelTempBasal(boolean enforceNew) { public PumpEnactResult cancelTempBasal(boolean enforceNew) {
getAapsLogger().debug(LTag.PUMP, "cancelTempBasal called"); getAapsLogger().debug(LTag.PUMP, "cancelTempBasal called");
//final TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistoryIncludingConvertedExtended(System.currentTimeMillis()); final PumpSync.PumpState.TemporaryBasal activeTemp = pumpSync.expectedPumpState().getTemporaryBasal();
PumpSync.PumpState pumpSate = pumpSync.expectedPumpState();
final PumpSync.PumpState.TemporaryBasal activeTemp = pumpSate.getTemporaryBasal();
if (enforceNew) { if (enforceNew) {
CommandResult stateResult = runCommand(getResourceHelper().gs(R.string.combo_pump_action_refreshing), 2, ruffyScripter::readPumpState); CommandResult stateResult = runCommand(getResourceHelper().gs(R.string.combo_pump_action_refreshing), 2, ruffyScripter::readPumpState);
if (!stateResult.success) { if (!stateResult.success) {
@ -839,6 +790,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
if (!cancelResult.state.tbrActive) { if (!cancelResult.state.tbrActive) {
pumpSync.syncStopTemporaryBasalWithPumpId( pumpSync.syncStopTemporaryBasalWithPumpId(
cancelResult.state.timestamp, cancelResult.state.timestamp,
// Combo doesn't have nor uses IDs for TBRs, see note in #setTempBasalPercent
cancelResult.state.timestamp, cancelResult.state.timestamp,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
@ -849,15 +801,16 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
} }
} else if (activeTemp == null) { } else if (activeTemp == null) {
return new PumpEnactResult(getInjector()).success(true).enacted(false); return new PumpEnactResult(getInjector()).success(true).enacted(false);
} else if ((activeTemp.getRate() >= 90 && activeTemp.getRate() <= 110) && info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) <= 15) { } else if ((activeTemp.getRate() >= 90 && activeTemp.getRate() <= 110)
&& PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) <= 15) {
// Let fake neutral temp keep run (see below) // Let fake neutral temp keep run (see below)
// Note that since this runs on the queue a connection is opened regardless, but this // Note that since this runs on the queue a connection is opened regardless, but this
// case doesn't occur all that often, so it's not worth optimizing (1.3k SetTBR vs 4 cancelTBR). // case doesn't occur all that often, so it's not worth optimizing (1.3k SetTBR vs 4 cancelTBR).
getAapsLogger().debug(LTag.PUMP, "cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.getRate() + "% and running for another " + info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) + " mins."); getAapsLogger().debug(LTag.PUMP, "cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.getRate() + "% and running for another " + PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) + " mins.");
return new PumpEnactResult(getInjector()).success(true).enacted(true) return new PumpEnactResult(getInjector()).success(true).enacted(true)
.comment("cancelTempBasal skipping changing tbr since it already is at " .comment("cancelTempBasal skipping changing tbr since it already is at "
+ activeTemp.getRate() + "% and running for another " + activeTemp.getRate() + "% and running for another "
+ info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) + " mins."); + PumpStateExtensionKt.getPlannedRemainingMinutes(activeTemp) + " mins.");
} else { } else {
// Set a fake neutral temp to avoid TBR cancel alert. Decide 90% vs 110% based on // Set a fake neutral temp to avoid TBR cancel alert. Decide 90% vs 110% based on
// on whether the TBR we're cancelling is above or below 100%. // on whether the TBR we're cancelling is above or below 100%.
@ -992,7 +945,8 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
T.mins(15).msecs(), T.mins(15).msecs(),
false, false,
PumpSync.TemporaryBasalType.PUMP_SUSPEND, PumpSync.TemporaryBasalType.PUMP_SUSPEND,
now, // no pumpId available ???? // Combo doesn't have nor uses IDs for TBRs, see note in #setTempBasalPercent
now,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
@ -1113,6 +1067,8 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
private void checkAndResolveTbrMismatch(PumpState state) { private void checkAndResolveTbrMismatch(PumpState state) {
// compare with: info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusTempBasal.updateTempBasalInDB() // compare with: info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusTempBasal.updateTempBasalInDB()
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Combo doesn't have nor uses IDs for TBRs, see note in #setTempBasalPercent
long tbrId = now;
//TemporaryBasal aapsTbr = treatmentsPlugin.getTempBasalFromHistoryIncludingConvertedExtended(now); //TemporaryBasal aapsTbr = treatmentsPlugin.getTempBasalFromHistoryIncludingConvertedExtended(now);
PumpSync.PumpState.TemporaryBasal aapsTbr = pumpSync.expectedPumpState().getTemporaryBasal(); PumpSync.PumpState.TemporaryBasal aapsTbr = pumpSync.expectedPumpState().getTemporaryBasal();
if (aapsTbr == null && state.tbrActive && state.tbrRemainingDuration > 2) { if (aapsTbr == null && state.tbrActive && state.tbrRemainingDuration > 2) {
@ -1123,36 +1079,40 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
T.mins(state.tbrRemainingDuration).msecs(), T.mins(state.tbrRemainingDuration).msecs(),
false, false,
PumpSync.TemporaryBasalType.NORMAL, PumpSync.TemporaryBasalType.NORMAL,
now, // no pumpId available ???? tbrId,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
} else if (aapsTbr != null && info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes(aapsTbr) > 2 && !state.tbrActive) { } else if (aapsTbr != null && PumpStateExtensionKt.getPlannedRemainingMinutes(aapsTbr) > 2 && !state.tbrActive) {
getAapsLogger().debug(LTag.PUMP, "Ending AAPS-TBR since pump has no TBR active"); getAapsLogger().debug(LTag.PUMP, "Ending AAPS-TBR since pump has no TBR active");
pumpSync.syncStopTemporaryBasalWithPumpId( pumpSync.syncStopTemporaryBasalWithPumpId(
now, now,
now, tbrId,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
} else if (aapsTbr != null && state.tbrActive } else if (aapsTbr != null && state.tbrActive
&& (aapsTbr.getRate() != state.tbrPercent || && (aapsTbr.getRate() != state.tbrPercent ||
Math.abs(info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes(aapsTbr) - state.tbrRemainingDuration) > 2)) { Math.abs(PumpStateExtensionKt.getPlannedRemainingMinutes(aapsTbr) - state.tbrRemainingDuration) > 2)) {
getAapsLogger().debug(LTag.PUMP, "AAPSs and pump-TBR differ; ending AAPS-TBR and creating new TBR based on pump TBR"); getAapsLogger().debug(LTag.PUMP, "AAPSs and pump-TBR differ; ending AAPS-TBR and creating new TBR based on pump TBR");
// crate TBR end record a second ago
pumpSync.syncStopTemporaryBasalWithPumpId( pumpSync.syncStopTemporaryBasalWithPumpId(
now - 1000, now - 1000,
now - 1000, // fake a unique ID that doesn't clash with the record below
tbrId - 1000,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
// Create TBR start record, starting now
pumpSync.syncTemporaryBasalWithPumpId( pumpSync.syncTemporaryBasalWithPumpId(
now, now,
state.tbrPercent, state.tbrPercent,
T.mins(state.tbrRemainingDuration).msecs(), T.mins(state.tbrRemainingDuration).msecs(),
false, false,
PumpSync.TemporaryBasalType.NORMAL, PumpSync.TemporaryBasalType.NORMAL,
now, // no pumpId available ???? tbrId,
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
); );
@ -1189,10 +1149,10 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
boolean updated = false; boolean updated = false;
for (Bolus pumpBolus : history.bolusHistory) { for (Bolus pumpBolus : history.bolusHistory) {
if (pumpSync.syncBolusWithPumpId( if (pumpSync.syncBolusWithPumpId(
calculateFakeBolusDate(pumpBolus), pumpBolus.timestamp,
pumpBolus.amount, pumpBolus.amount,
DetailedBolusInfo.BolusType.NORMAL, DetailedBolusInfo.BolusType.NORMAL,
System.currentTimeMillis(), generatePumpBolusId(pumpBolus),
PumpType.ACCU_CHEK_COMBO, PumpType.ACCU_CHEK_COMBO,
serialNumber() serialNumber()
)) { )) {
@ -1209,7 +1169,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr
* Should be good enough, even with command mode, it's a challenge to create that situation * Should be good enough, even with command mode, it's a challenge to create that situation
* and most time clashes will be around SMBs which are covered. * and most time clashes will be around SMBs which are covered.
*/ */
long calculateFakeBolusDate(Bolus pumpBolus) { long generatePumpBolusId(Bolus pumpBolus) {
double bolus = pumpBolus.amount - 0.1; double bolus = pumpBolus.amount - 0.1;
int secondsFromBolus = (int) (bolus * 10 * 1000); int secondsFromBolus = (int) (bolus * 10 * 1000);
return pumpBolus.timestamp + Math.min(secondsFromBolus, 59 * 1000); return pumpBolus.timestamp + Math.min(secondsFromBolus, 59 * 1000);

View file

@ -64,22 +64,22 @@ class ComboPluginTest : TestBase() {
} }
@Test @Test
fun calculateFakePumpTimestamp() { fun `generate bolus ID from timestamp and amount`() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val pumpTimestamp = now - now % 1000 val pumpTimestamp = now - now % 1000
// same timestamp, different bolus leads to different fake timestamp // same timestamp, different bolus leads to different fake timestamp
Assert.assertNotEquals( Assert.assertNotEquals(
comboPlugin.calculateFakeBolusDate(Bolus(pumpTimestamp, 0.1, true)), comboPlugin.generatePumpBolusId(Bolus(pumpTimestamp, 0.1, true)),
comboPlugin.calculateFakeBolusDate(Bolus(pumpTimestamp, 0.3, true)) comboPlugin.generatePumpBolusId(Bolus(pumpTimestamp, 0.3, true))
) )
// different timestamp, same bolus leads to different fake timestamp // different timestamp, same bolus leads to different fake timestamp
Assert.assertNotEquals( Assert.assertNotEquals(
comboPlugin.calculateFakeBolusDate(Bolus(pumpTimestamp, 0.3, true)), comboPlugin.generatePumpBolusId(Bolus(pumpTimestamp, 0.3, true)),
comboPlugin.calculateFakeBolusDate(Bolus(pumpTimestamp + 60 * 1000, 0.3, true)) comboPlugin.generatePumpBolusId(Bolus(pumpTimestamp + 60 * 1000, 0.3, true))
) )
// generated timestamp has second-precision // generated timestamp has second-precision
val bolus = Bolus(pumpTimestamp, 0.2, true) val bolus = Bolus(pumpTimestamp, 0.2, true)
val calculatedTimestamp = comboPlugin.calculateFakeBolusDate(bolus) val calculatedTimestamp = comboPlugin.generatePumpBolusId(bolus)
Assert.assertEquals(calculatedTimestamp, calculatedTimestamp - calculatedTimestamp % 1000) Assert.assertEquals(calculatedTimestamp, calculatedTimestamp - calculatedTimestamp % 1000)
} }
} }