* Read all history
* Simple viewer for TDDs, errors
* Clean up cancelling TBR (incomplete)
* Initializing pump robustness
This commit is contained in:
Johannes Mockenhaupt 2017-11-12 00:01:01 +01:00
parent 3072a42cd7
commit c11086d9ea
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1
16 changed files with 278 additions and 104 deletions

View file

@ -73,6 +73,3 @@ Usage
have to wait till the pump's display turns off before it can reconnect to the pump). have to wait till the pump's display turns off before it can reconnect to the pump).
If the pump's alarm continues, the last action might have failed, in which case the user needs to confirm the alarm If the pump's alarm continues, the last action might have failed, in which case the user needs to confirm the alarm
v3 TODOs
- Reading and displaying TDDs

View file

@ -8,10 +8,21 @@
- [ ] Bolus deleted in treatments (marked invalid?!) is re-added when pump reads history - [ ] Bolus deleted in treatments (marked invalid?!) is re-added when pump reads history
- [ ] Tasks - [ ] Tasks
- [ ] Main - [ ] Main
- [ ] on command error: recover by returning to main menu
- [ ] Reading history - [ ] Reading history
- [ ] Bolus - [x] Bolus
- [x] Read
- [x] Sync DB
- [ ] TBRs - [ ] TBRs
- [x] Read
- [ ] Sync DB
- [ ] Alerts - [ ] Alerts
- [x] Read
- [ ] Sync DB?
- [x] Display in UI
- [ ] TDD
- [x] Read
- [ ] Sync DB?
- [ ] Display in UI - [ ] Display in UI
- [ ] Taking over alerts - [ ] Taking over alerts
- [ ] On connect - [ ] On connect
@ -24,6 +35,7 @@
- [ ] Run readReservoirAndBolusLevel after SetTbr too so boluses on the pump are caught sooner? - [ ] Run readReservoirAndBolusLevel after SetTbr too so boluses on the pump are caught sooner?
Currently the pump gets to know such a record when bolusing or when refresh() is called Currently the pump gets to know such a record when bolusing or when refresh() is called
after 15m of no other command taking place. IOB will then be current with next loop after 15m of no other command taking place. IOB will then be current with next loop
- [ ] Optimize reading full history to pass timestamps of last known records to avoid reading known records
iteration. iteration.
- [ ] Cleanups - [ ] Cleanups
- [ ] Finish 'enacted' removal rewrite (esp. cancel tbr) - [ ] Finish 'enacted' removal rewrite (esp. cancel tbr)
@ -39,6 +51,8 @@
- [ ] Display errors in combo tap - [ ] Display errors in combo tap
- [x] Option to raise overview notifications as android notification with noise (for urgent ones?) - [x] Option to raise overview notifications as android notification with noise (for urgent ones?)
- [ ] Low prio - [ ] Low prio
- [ ] Naming is messed up: pump has warnings and errors, which cause alerts; W+E are thus alerts,
e.g. pumpErrorHistory should be renamed to alertHistory
- [ ] Enable BT if disabled? does dana does this? - [ ] Enable BT if disabled? does dana does this?
- [ ] Finish and test German translation - [ ] Finish and test German translation
- [ ] No clean startup/shutdown; RuffyScripter is instanciated once, idle disconnect thread never killed - [ ] No clean startup/shutdown; RuffyScripter is instanciated once, idle disconnect thread never killed
@ -46,12 +60,6 @@
Android logs it as crashed and restarts it, thereby restarting the app (or just keeping it alive, Android logs it as crashed and restarts it, thereby restarting the app (or just keeping it alive,
also causes errors with the DB as there were attemtps to open a closed DB instance/ref. also causes errors with the DB as there were attemtps to open a closed DB instance/ref.
- [ ] v3
- [ ] Tasks (this is probably best left to the command-mode people)
- [ ] Reading TDD
- [ ] UI for TDDs
- [ ] Optimize reading full history to pass timestamps of last known records to avoid reading known records
Inbox Inbox
- [ ] Where/when to call checkTbrMisMatch? - [ ] Where/when to call checkTbrMisMatch?
- [ ] pairing: just start SetupFragment? - [ ] pairing: just start SetupFragment?

View file

@ -53,6 +53,9 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
Button errorHistory = (Button) view.findViewById(R.id.combo_error_history); Button errorHistory = (Button) view.findViewById(R.id.combo_error_history);
errorHistory.setOnClickListener(this); errorHistory.setOnClickListener(this);
Button tddHistory = (Button) view.findViewById(R.id.combo_tdd_history);
tddHistory.setOnClickListener(this);
updateGUI(); updateGUI();
return view; return view;
} }
@ -65,8 +68,11 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
break; break;
case R.id.combo_error_history: case R.id.combo_error_history:
ComboErrorHistoryDialog ehd = new ComboErrorHistoryDialog(); ComboErrorHistoryDialog ehd = new ComboErrorHistoryDialog();
FragmentManager manager = getFragmentManager(); ehd.show(getFragmentManager(), ComboErrorHistoryDialog.class.getSimpleName());
ehd.show(manager, ComboErrorHistoryDialog.class.getSimpleName()); break;
case R.id.combo_tdd_history:
ComboTddHistoryDialog thd = new ComboTddHistoryDialog();
thd.show(getFragmentManager(), ComboTddHistoryDialog.class.getSimpleName());
break; break;
} }
} }

View file

@ -17,8 +17,10 @@ import de.jotomo.ruffy.spi.CommandResult;
import de.jotomo.ruffy.spi.PumpState; import de.jotomo.ruffy.spi.PumpState;
import de.jotomo.ruffy.spi.RuffyCommands; import de.jotomo.ruffy.spi.RuffyCommands;
import de.jotomo.ruffy.spi.history.Bolus; import de.jotomo.ruffy.spi.history.Bolus;
import de.jotomo.ruffy.spi.history.PumpError;
import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import de.jotomo.ruffy.spi.history.PumpHistoryRequest;
import de.jotomo.ruffy.spi.history.Tbr; import de.jotomo.ruffy.spi.history.Tbr;
import de.jotomo.ruffy.spi.history.Tdd;
import de.jotomo.ruffyscripter.RuffyCommandsV1Impl; import de.jotomo.ruffyscripter.RuffyCommandsV1Impl;
import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.BuildConfig;
import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.MainApp;
@ -38,6 +40,7 @@ import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.Overview.Notification;
import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress;
import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI;
@ -243,24 +246,35 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
} }
} }
CommandResult stateResult = runCommand("Refreshing", 3, ruffyScripter::readReservoirLevelAndLastBolus); CommandResult stateResult = runCommand(pump.initialized ? MainApp.sResources.getString(R.string.combo_pump_action_refreshing) : MainApp.sResources.getString(R.string.combo_pump_action_initializing),
1, ruffyScripter::readReservoirLevelAndLastBolus);
if (!stateResult.success) {
return;
}
checkForTbrMismatch(stateResult.state); checkForTbrMismatch(stateResult.state);
int minutesOfDayNow = Calendar.getInstance().get(Calendar.HOUR) + Calendar.getInstance().get(Calendar.MINUTE) * 60; int minutesOfDayNow = Calendar.getInstance().get(Calendar.HOUR) + Calendar.getInstance().get(Calendar.MINUTE) * 60;
if (!pump.initialized || (Math.abs(stateResult.state.pumpTimeMinutesOfDay - minutesOfDayNow) >= 2)) { if (!pump.initialized || (Math.abs(stateResult.state.pumpTimeMinutesOfDay - minutesOfDayNow) >= 2)) {
runCommand("Updating pump clock", 2, ruffyScripter::setDateAndTime); if (!runCommand("Updating pump clock", 2, ruffyScripter::setDateAndTime).success) {
return;
}
} }
if (!pump.initialized) { if (!pump.initialized) {
runCommand("Reading basal profile", 2, ruffyScripter::readBasalProfile); if (!runCommand("Reading basal profile", 2, ruffyScripter::readBasalProfile).success) {
return;
}
} }
checkPumpHistory(); if (!checkPumpHistory()) {
return;
}
// if (!pump.initialized) {
// pump.initialized = clockUpdated && basalProfileRead;
// }
pump.initialized = true; pump.initialized = true;
// ComboFragment updates state fully only after the pump has initialized,
// this fetches state again and updates the ui proper
runCommand(null, 0, ruffyScripter::readPumpState);
} }
@ -504,11 +518,11 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
PumpState state = commandResult.state; PumpState state = commandResult.state;
if (state.tbrActive && state.tbrPercent == percent if (state.tbrActive && state.tbrPercent == percent
&& (state.tbrRemainingDuration == durationInMinutes || state.tbrRemainingDuration == durationInMinutes - 1)) { && (state.tbrRemainingDuration == durationInMinutes || state.tbrRemainingDuration == durationInMinutes - 1)) {
// TODO timestamp: can this cause problems? setting TBR while a minute flips over?
// might that be the cause if the TBR duration instantly flips from e.g. 0:30 -> 0:29 ?
TemporaryBasal tempStart = new TemporaryBasal(state.timestamp); TemporaryBasal tempStart = new TemporaryBasal(state.timestamp);
// TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ... // TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ...
// we should check this, but really ... something must be really screwed up if that number was anything different // we should check this, but really ... something must be really screwed up if that number was anything different
// TODO actually ... might setting 29 help with gaps between TBRs? w/o the hack in TemporaryBasal?
// nah, fucks up duration in treatment history
tempStart.durationInMinutes = durationInMinutes; tempStart.durationInMinutes = durationInMinutes;
tempStart.percentRate = adjustedPercent; tempStart.percentRate = adjustedPercent;
tempStart.isAbsolute = false; tempStart.isAbsolute = false;
@ -529,78 +543,53 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
return OPERATION_NOT_SUPPORTED; return OPERATION_NOT_SUPPORTED;
} }
// TODO clean this method up: // TODO review and test, esp. aaps/pump mismatch situations
// check PumpState to verify TBR has been cancelled.
// enacted field.
@Override @Override
public PumpEnactResult cancelTempBasal(boolean userRequested) { public PumpEnactResult cancelTempBasal(boolean userRequested) {
log.debug("cancelTempBasal called"); log.debug("cancelTempBasal called");
CommandResult commandResult = null;
TemporaryBasal tempBasal = null;
PumpEnactResult pumpEnactResult = new PumpEnactResult();
final TemporaryBasal activeTemp = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); final TemporaryBasal activeTemp = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
// checkForTbrMismatch();
if (activeTemp == null || userRequested) { if (activeTemp == null || userRequested) {
// TODO
/* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */ /* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */
log.debug("cancelTempBasal: hard-cancelling TBR since user requested"); log.debug("cancelTempBasal: hard-cancelling TBR since user requested");
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), 2, ruffyScripter::cancelTbr); CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), 2, ruffyScripter::cancelTbr);
if (!commandResult.state.tbrActive) { if (!commandResult.state.tbrActive) {
tempBasal = new TemporaryBasal(System.currentTimeMillis()); TemporaryBasal tempBasal = new TemporaryBasal(System.currentTimeMillis());
tempBasal.durationInMinutes = 0; tempBasal.durationInMinutes = 0;
tempBasal.source = Source.USER; tempBasal.source = Source.USER;
pumpEnactResult.isTempCancel = true; MainApp.getConfigBuilder().addToHistoryTempBasal(tempBasal);
return new PumpEnactResult().isTempCancel(true).success(true).enacted(true);
} else {
return new PumpEnactResult().success(false).enacted(false);
} }
} else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15) { } else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15) {
// Let fake neutral temp keep run (see below) // Let fake neutral temp keep run (see below)
log.debug("cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins."); log.debug("cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins.");
pumpEnactResult.comment = "cancelTempBasal skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins."; return new PumpEnactResult().success(true).enacted(true)
// TODO check what AAPS does with this; no DB update is required; .comment("cancelTempBasal skipping changing tbr since it already is at "
// looking at info/nightscout/androidaps/plugins/Loop/LoopPlugin.java:280 + activeTemp.percentRate + "% and running for another "
// both values are used as one: + activeTemp.getPlannedRemainingMinutes() + " mins.");
// if (applyResult.enacted || applyResult.success) { ...
pumpEnactResult.enacted = true; // all fine though...
pumpEnactResult.success = true; // just roll with it...
} 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%.
final int percentage = (activeTemp.percentRate > 100) ? 110 : 90; final int percentage = (activeTemp.percentRate > 100) ? 110 : 90;
log.debug("cancelTempBasal: changing TBR to " + percentage + "% for 15 mins."); log.debug("cancelTempBasal: changing TBR to " + percentage + "% for 15 mins.");
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), 2, () -> ruffyScripter.setTbr(percentage, 15)); CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_cancelling_tbr), 2, () -> ruffyScripter.setTbr(percentage, 15));
// TODO properly check fake neutral temp is active if (commandResult.state.tbrActive && commandResult.state.tbrPercent == percentage
// if (!commandResult.state.tbrActive) { && (commandResult.state.tbrRemainingDuration == 15 || commandResult.state.tbrRemainingDuration == 14)) {
if (commandResult.success) { TemporaryBasal tempBasal = new TemporaryBasal(System.currentTimeMillis());
tempBasal = new TemporaryBasal(System.currentTimeMillis());
tempBasal.durationInMinutes = 15; tempBasal.durationInMinutes = 15;
tempBasal.source = Source.USER; tempBasal.source = Source.USER;
tempBasal.percentRate = percentage; tempBasal.percentRate = percentage;
tempBasal.isAbsolute = false; tempBasal.isAbsolute = false;
MainApp.getConfigBuilder().addToHistoryTempBasal(tempBasal);
return new PumpEnactResult().success(true).enacted(true);
} else {
return new PumpEnactResult().success(false).enacted(false);
} }
} }
// TODO properly check pumpstate to confirm cancellation
// PumpState state = commandResult.state;
// if (!state.tbrActive && state.tbrPercent == percent
// && (state.tbrRemainingDuration == durationInMinutes || state.tbrRemainingDuration == durationInMinutes - 1)) {
//
// }
if (tempBasal != null) {
ConfigBuilderPlugin treatmentsInterface = MainApp.getConfigBuilder();
treatmentsInterface.addToHistoryTempBasal(tempBasal);
}
if (commandResult != null) {
pumpEnactResult.success = commandResult.success;
pumpEnactResult.enacted = true;
}
return pumpEnactResult;
} }
private interface CommandExecution { private interface CommandExecution {
@ -609,6 +598,7 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
// TODO remove this method, make stuff explicit (state updates, activity updates) // TODO remove this method, make stuff explicit (state updates, activity updates)
// and only have a withRetries() method that simply retries a command? // and only have a withRetries() method that simply retries a command?
/** /**
* Runs a command, sets an activity if provided, retries if requested and updates fields * Runs a command, sets an activity if provided, retries if requested and updates fields
* concerned with last connection. * concerned with last connection.
@ -663,7 +653,7 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
if (commandResult.state.unsafeUsageDetected) { if (commandResult.state.unsafeUsageDetected) {
lastViolation = System.currentTimeMillis(); lastViolation = System.currentTimeMillis();
} }
if (commandResult.lastBolus != null && !commandResult.lastBolus.isValid) { if (commandResult.lastBolus != null && !commandResult.lastBolus.isValid) {
lastViolation = commandResult.lastBolus.timestamp; lastViolation = commandResult.lastBolus.timestamp;
} else if (commandResult.history != null) { } else if (commandResult.history != null) {
for (Bolus bolus : commandResult.history.bolusHistory) { for (Bolus bolus : commandResult.history.bolusHistory) {
@ -687,7 +677,9 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
} }
} }
/** Checks the main screen to determine if TBR on pump matches app state. */ /**
* Checks the main screen to determine if TBR on pump matches app state.
*/
private boolean checkForTbrMismatch(PumpState state) { private boolean checkForTbrMismatch(PumpState state) {
// detectTbrMismatch(): 'quick' check with no overhead on the pump side // detectTbrMismatch(): 'quick' check with no overhead on the pump side
// TODO check if this works with pump suspend, esp. around pump suspend there'll be syncing to do; // TODO check if this works with pump suspend, esp. around pump suspend there'll be syncing to do;
@ -736,20 +728,18 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
new PumpHistoryRequest() new PumpHistoryRequest()
.bolusHistory(PumpHistoryRequest.LAST) .bolusHistory(PumpHistoryRequest.LAST)
.tbrHistory(PumpHistoryRequest.LAST) .tbrHistory(PumpHistoryRequest.LAST)
.errorHistory(PumpHistoryRequest.LAST))); .pumpErrorHistory(PumpHistoryRequest.LAST)));
if (!commandResult.success || commandResult.history == null) { if (!commandResult.success || commandResult.history == null) {
// TODO error case, command // TODO error case, command
return false; return false;
} }
// TODO opt, construct PumpHistoryRequest to requset only what needs updating // TODO v2.1 optimize, construct PumpHistoryRequest to requset only what needs updating
boolean syncNeeded = false;
PumpHistoryRequest request = new PumpHistoryRequest(); PumpHistoryRequest request = new PumpHistoryRequest();
// last bolus // last bolus
List<Treatment> treatments = MainApp.getConfigBuilder().getTreatmentsFromHistory(); List<Treatment> treatments = MainApp.getConfigBuilder().getTreatmentsFromHistory();
// Collections.reverse(treatments);
Treatment aapsBolus = null; Treatment aapsBolus = null;
for (Treatment t : treatments) { for (Treatment t : treatments) {
if (t.insulin != 0) { if (t.insulin != 0) {
@ -766,7 +756,6 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
if ((aapsBolus == null || pumpBolus == null) if ((aapsBolus == null || pumpBolus == null)
|| (Math.abs(aapsBolus.insulin - pumpBolus.amount) > 0.05 || aapsBolus.date != pumpBolus.timestamp)) { || (Math.abs(aapsBolus.insulin - pumpBolus.amount) > 0.05 || aapsBolus.date != pumpBolus.timestamp)) {
log.debug("Last bolus on pump is unknown, refreshing full bolus history"); log.debug("Last bolus on pump is unknown, refreshing full bolus history");
syncNeeded = true;
request.bolusHistory = PumpHistoryRequest.FULL; request.bolusHistory = PumpHistoryRequest.FULL;
} }
@ -782,30 +771,40 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
pumpTbr = tbrHistory.get(0); pumpTbr = tbrHistory.get(0);
} }
if ((aapsTbr == null || pumpTbr == null) if ((aapsTbr == null || pumpTbr == null)
|| (aapsTbr.percentRate != pumpTbr.percent || aapsTbr.durationInMinutes != pumpTbr.duration)) { || (aapsTbr.date != pumpTbr.timestamp || aapsTbr.percentRate != pumpTbr.percent || aapsTbr.durationInMinutes != pumpTbr.duration)) {
// TODO log.debug("Last TBR on pump is unknown, refreshing full TBR history");
// log.debug("Last TBR on pump is unknown, refreshing full TBR history"); request.tbrHistory = PumpHistoryRequest.FULL;
// syncNeeded = true;
// request.tbrHistory = PumpHistoryRequest.FULL;
} }
// last error // last error
// TODO add DB table ... or just keep in memory? does android allow that (fragment kill frenzy) without workarounds? PumpError aapsError = null;
// is comboplugin a service or a class with statics? if (!pump.history.pumpErrorHistory.isEmpty()) {
// request.pumpErrorHistory = PumpHistoryRequest.FULL; aapsError = pump.history.pumpErrorHistory.get(0);
}
PumpError pumpError = null;
List<PumpError> pumpErrorHistory = commandResult.history.pumpErrorHistory;
if (!pumpErrorHistory.isEmpty()){
pumpError = pumpErrorHistory.get(0);
}
if ((aapsError == null || pumpError == null)
|| aapsError.timestamp != pumpError.timestamp) {
log.debug("Last pump error is unknown, refrehing full error history");
}
// tdd // tdd
// TODO; ... just fetch them on-deamand when the user opens the fragment? // TODO v2.1
if (request.bolusHistory != PumpHistoryRequest.SKIP
if (syncNeeded) { || request.tbrHistory != PumpHistoryRequest.SKIP
syncHistory(request); || request.pumpErrorHistory != PumpHistoryRequest.SKIP
|| request.tddHistory != PumpHistoryRequest.SKIP) {
readHistory(request);
} }
return true; return true;
} }
private void syncHistory(final PumpHistoryRequest request) { private void readHistory(final PumpHistoryRequest request) {
CommandResult result = runCommand(MainApp.sResources.getString(R.string.combo_activity_reading_pump_history), 3, () -> ruffyScripter.readHistory(request)); CommandResult result = runCommand(MainApp.sResources.getString(R.string.combo_activity_reading_pump_history), 3, () -> ruffyScripter.readHistory(request));
if (!result.success) { if (!result.success) {
// TODO // TODO
@ -833,20 +832,28 @@ public class ComboPlugin implements PluginBase, PumpInterface, ConstraintsInterf
// TODO tolerance for start/end deviations? temp start is based on pump time, but in ms; round to seconds, so we can find it here more easily? // TODO tolerance for start/end deviations? temp start is based on pump time, but in ms; round to seconds, so we can find it here more easily?
// errors // errors
// TODO for (PumpError pumpError : result.history.pumpErrorHistory) {
if (!pump.history.pumpErrorHistory.contains(pumpError)) {
pump.history.pumpErrorHistory.add(pumpError);
}
}
// tdd // tdd
// TODO v3 for (Tdd tdd : result.history.tddHistory) {
if (!pump.history.tddHistory.contains(tdd)) {
pump.history.tddHistory.add(tdd);
}
}
// fetch state (updates local state as well)
runCommand(null, 3, ruffyScripter::readReservoirLevelAndLastBolus); runCommand(null, 3, ruffyScripter::readReservoirLevelAndLastBolus);
} }
void forceSyncFullHistory() { void forceFullHistoryRead() {
syncHistory(new PumpHistoryRequest() // TODO v2.1: optimize to pass timestamps of last known records to avoid reading known records
readHistory(new PumpHistoryRequest()
.bolusHistory(PumpHistoryRequest.FULL) .bolusHistory(PumpHistoryRequest.FULL)
.tbrHistory(PumpHistoryRequest.FULL) .tbrHistory(PumpHistoryRequest.FULL)
.errorHistory(PumpHistoryRequest.FULL) .pumpErrorHistory(PumpHistoryRequest.FULL)
.tddHistory(PumpHistoryRequest.FULL)); .tddHistory(PumpHistoryRequest.FULL));
} }

View file

@ -4,8 +4,8 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import de.jotomo.ruffy.spi.BasalProfile; import de.jotomo.ruffy.spi.BasalProfile;
import de.jotomo.ruffy.spi.PumpState;
import de.jotomo.ruffy.spi.CommandResult; import de.jotomo.ruffy.spi.CommandResult;
import de.jotomo.ruffy.spi.PumpState;
import de.jotomo.ruffy.spi.history.Bolus; import de.jotomo.ruffy.spi.history.Bolus;
import de.jotomo.ruffy.spi.history.PumpHistory; import de.jotomo.ruffy.spi.history.PumpHistory;
@ -28,6 +28,8 @@ class ComboPump {
volatile BasalProfile basalProfile; volatile BasalProfile basalProfile;
@NonNull @NonNull
volatile PumpHistory history = new PumpHistory(); volatile PumpHistory history = new PumpHistory();
/** Time the active TBR was set (if any). Needed to calculate remaining time in fragment */ /**
* Time the active TBR was set (if any). Needed to calculate remaining time in fragment
*/
long tbrSetTime; long tbrSetTime;
} }

View file

@ -0,0 +1,48 @@
package info.nightscout.androidaps.plugins.PumpCombo;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.List;
import de.jotomo.ruffy.spi.history.Tdd;
import info.nightscout.androidaps.R;
/**
* Created by adrian on 17/08/17.
*/
public class ComboTddHistoryDialog extends DialogFragment {
private static Logger log = LoggerFactory.getLogger(ComboTddHistoryDialog.class);
private TextView text;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.combo_tdd_history_fragment, container, false);
text = (TextView) layout.findViewById(R.id.combo_tdd_history_text);
List<Tdd> tdds = ComboPlugin.getPlugin().getPump().history.tddHistory;
StringBuilder sb = new StringBuilder();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM");
if (tdds.isEmpty()) {
text.setText("No TDDs. To retrieve the TDD history from the pump, long press the Refresh button.");
} else {
for (Tdd tdd : tdds) {
sb.append(simpleDateFormat.format(tdd.timestamp));
sb.append(" ");
sb.append(tdd.total);
sb.append("\n");
}
text.setText(sb.toString());
}
return layout;
}
}

View file

@ -2,7 +2,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".plugins.ProfileNS.NSProfileFragment"> tools:context=".plugins.PumpCombo.ComboFragment">
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.PumpCombo.activities.PairingActivity"> tools:context=".plugins.PumpCombo.activities.PairingActivity">
<ListView <ListView
android:id="@+id/combo_pairing_listview" android:id="@+id/combo_pairing_listview"

View file

@ -0,0 +1,27 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.PumpCombo.ComboFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/combo_tdd_history_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textStart"
android:padding="10dp"
android:gravity="start"/>
</LinearLayout>
</ScrollView>
</FrameLayout>

View file

@ -373,6 +373,17 @@
android:paddingRight="0dp" android:paddingRight="0dp"
android:text="@string/pump_errors_history" /> android:text="@string/pump_errors_history" />
<Button
android:id="@+id/combo_tdd_history"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_danarstats"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/combo_stats" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,11 +1,7 @@
package de.jotomo.ruffy.spi.history; package de.jotomo.ruffy.spi.history;
public class PumpError extends HistoryRecord { public class PumpError extends HistoryRecord {
/** Code is an E for error or W for warning, followed by a single digit, e.g. W7 (TBR cancelled). */
// public final String code;
public final Integer warningCode; public final Integer warningCode;
public final Integer errorCode; public final Integer errorCode;
/** Error message, in the language configured on the pump. */ /** Error message, in the language configured on the pump. */
public final String message; public final String message;
@ -17,6 +13,30 @@ public class PumpError extends HistoryRecord {
this.message = message; this.message = message;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PumpError pumpError = (PumpError) o;
if (timestamp != pumpError.timestamp) return false;
if (warningCode != null ? !warningCode.equals(pumpError.warningCode) : pumpError.warningCode != null)
return false;
if (errorCode != null ? !errorCode.equals(pumpError.errorCode) : pumpError.errorCode != null)
return false;
return message != null ? message.equals(pumpError.message) : pumpError.message == null;
}
@Override
public int hashCode() {
int result = (int) (timestamp ^ (timestamp >>> 32));
result = 31 * result + (warningCode != null ? warningCode.hashCode() : 0);
result = 31 * result + (errorCode != null ? errorCode.hashCode() : 0);
result = 31 * result + (message != null ? message.hashCode() : 0);
return result;
}
@Override @Override
public String toString() { public String toString() {
return "PumpError{" + return "PumpError{" +

View file

@ -3,6 +3,7 @@ package de.jotomo.ruffy.spi.history;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
/** History data as read from the pump's My Data menu. /** History data as read from the pump's My Data menu.
@ -13,7 +14,7 @@ public class PumpHistory {
@NonNull @NonNull
public List<Tbr> tbrHistory = new ArrayList<>(); public List<Tbr> tbrHistory = new ArrayList<>();
@NonNull @NonNull
public List<PumpError> pumpErrorHistory = new ArrayList<>(); public List<PumpError> pumpErrorHistory = new LinkedList<>();
@NonNull @NonNull
public List<Tdd> tddHistory = new ArrayList<>(); public List<Tdd> tddHistory = new ArrayList<>();

View file

@ -25,8 +25,8 @@ public class PumpHistoryRequest {
return this; return this;
} }
public PumpHistoryRequest errorHistory(long errorHistory) { public PumpHistoryRequest pumpErrorHistory(long pumpErrorHistory) {
this.pumpErrorHistory = errorHistory; this.pumpErrorHistory = pumpErrorHistory;
return this; return this;
} }

View file

@ -1,5 +1,6 @@
package de.jotomo.ruffy.spi.history; package de.jotomo.ruffy.spi.history;
/** Note: the timestamp is the time the TBR **ended**, not started .*/
public class Tbr extends HistoryRecord { public class Tbr extends HistoryRecord {
public final int duration; public final int duration;
public final int percent; public final int percent;
@ -10,6 +11,26 @@ public class Tbr extends HistoryRecord {
this.percent = percent; this.percent = percent;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tbr tbr = (Tbr) o;
if (timestamp != tbr.timestamp) return false;
if (duration != tbr.duration) return false;
return percent == tbr.percent;
}
@Override
public int hashCode() {
int result = (int) (timestamp ^ (timestamp >>> 32));
result = 31 * result + duration;
result = 31 * result + percent;
return result;
}
@Override @Override
public String toString() { public String toString() {
return "Tbr{" + return "Tbr{" +

View file

@ -9,6 +9,27 @@ public class Tdd extends HistoryRecord {
this.total = total; this.total = total;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tdd tdd = (Tdd) o;
if (timestamp != tdd.timestamp) return false;
return Double.compare(tdd.total, total) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
result = (int) (timestamp ^ (timestamp >>> 32));
temp = Double.doubleToLongBits(total);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override @Override
public String toString() { public String toString() {
return "Tdd{" + return "Tdd{" +

View file

@ -10,6 +10,7 @@ import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import de.jotomo.ruffy.spi.history.Bolus; import de.jotomo.ruffy.spi.history.Bolus;
@ -156,7 +157,11 @@ public class ReadHistoryCommand extends BaseCommand {
private Tdd readTddRecord() { private Tdd readTddRecord() {
scripter.verifyMenuIsDisplayed(MenuType.DAILY_DATA); scripter.verifyMenuIsDisplayed(MenuType.DAILY_DATA);
Double dailyTotal = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.DAILY_TOTAL); Double dailyTotal = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.DAILY_TOTAL);
long recordDate = readRecordDate(); MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
Calendar instance = Calendar.getInstance();
int year = date.getMonth() == 12 ? Calendar.getInstance().get(Calendar.YEAR) - 1 : Calendar.getInstance().get(Calendar.YEAR);
instance.set(year, date.getMonth(), date.getDay());
long recordDate = instance.getTimeInMillis();
return new Tdd(recordDate, dailyTotal); return new Tdd(recordDate, dailyTotal);
} }