actionStringHandler, // TODO Adrian use RxBus instead of Lazy
- IobCobCalculatorPlugin iobCobCalculatorPlugin,
- ReceiverStatusStore receiverStatusStore,
- FabricPrivacy fabricPrivacy,
- NSUpload nsUpload,
- HardLimits hardLimits
- ) {
- super(new PluginDescription()
- .mainType(PluginType.LOOP)
- .fragmentClass(LoopFragment.class.getName())
- .pluginIcon(R.drawable.ic_loop_closed_white)
- .pluginName(R.string.loop)
- .shortName(R.string.loop_shortname)
- .preferencesId(R.xml.pref_loop)
- .enableByDefault(config.getAPS())
- .description(R.string.description_loop),
- aapsLogger, resourceHelper, injector
- );
- this.injector = injector;
- this.sp = sp;
- this.rxBus = rxBus;
- this.constraintChecker = constraintChecker;
- this.resourceHelper = resourceHelper;
- this.profileFunction = profileFunction;
- this.context = context;
- this.activePlugin = activePlugin;
- this.commandQueue = commandQueue;
- this.treatmentsPlugin = treatmentsPlugin;
- this.virtualPumpPlugin = virtualPumpPlugin;
- this.actionStringHandler = actionStringHandler;
- this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
- this.receiverStatusStore = receiverStatusStore;
- this.fabricPrivacy = fabricPrivacy;
- this.nsUpload = nsUpload;
- this.hardLimits = hardLimits;
-
- loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L);
- isSuperBolus = sp.getBoolean("isSuperBolus", false);
- isDisconnected = sp.getBoolean("isDisconnected", false);
- }
-
- @Override
- protected void onStart() {
- createNotificationChannel();
- super.onStart();
- disposable.add(rxBus
- .toObservable(EventTempTargetChange.class)
- .observeOn(Schedulers.io())
- .subscribe(event -> invoke("EventTempTargetChange", true), fabricPrivacy::logException)
- );
- /**
- * This method is triggered once autosens calculation has completed, so the LoopPlugin
- * has current data to work with. However, autosens calculation can be triggered by multiple
- * sources and currently only a new BG should trigger a loop run. Hence we return early if
- * the event causing the calculation is not EventNewBg.
- *
- */
- disposable.add(rxBus
- .toObservable(EventAutosensCalculationFinished.class)
- .observeOn(Schedulers.io())
- .subscribe(event -> {
- // Autosens calculation not triggered by a new BG
- if (!(event.getCause() instanceof EventNewBG)) return;
-
- BgReading bgReading = iobCobCalculatorPlugin.actualBg();
- // BG outdated
- if (bgReading == null) return;
- // already looped with that value
- if (bgReading.date <= lastBgTriggeredRun) return;
-
- lastBgTriggeredRun = bgReading.date;
- invoke("AutosenseCalculation for " + bgReading, true);
- }, fabricPrivacy::logException)
- );
- }
-
- private void createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-
- NotificationManager mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- @SuppressLint("WrongConstant") NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
- CHANNEL_ID,
- NotificationManager.IMPORTANCE_HIGH);
- mNotificationManager.createNotificationChannel(channel);
- }
- }
-
- @Override
- protected void onStop() {
- disposable.clear();
- super.onStop();
- }
-
- @Override
- public boolean specialEnableCondition() {
- try {
- PumpInterface pump = activePlugin.getActivePump();
- return pump.getPumpDescription().isTempBasalCapable;
- } catch (Exception ignored) {
- // may fail during initialization
- return true;
- }
- }
-
- public long suspendedTo() {
- return loopSuspendedTill;
- }
-
- public void suspendTo(long endTime) {
- loopSuspendedTill = endTime;
- isSuperBolus = false;
- isDisconnected = false;
- sp.putLong("loopSuspendedTill", loopSuspendedTill);
- sp.putBoolean("isSuperBolus", isSuperBolus);
- sp.putBoolean("isDisconnected", isDisconnected);
- }
-
- public void superBolusTo(long endTime) {
- loopSuspendedTill = endTime;
- isSuperBolus = true;
- isDisconnected = false;
- sp.putLong("loopSuspendedTill", loopSuspendedTill);
- sp.putBoolean("isSuperBolus", isSuperBolus);
- sp.putBoolean("isDisconnected", isDisconnected);
- }
-
- private void disconnectTo(long endTime) {
- loopSuspendedTill = endTime;
- isSuperBolus = false;
- isDisconnected = true;
- sp.putLong("loopSuspendedTill", loopSuspendedTill);
- sp.putBoolean("isSuperBolus", isSuperBolus);
- sp.putBoolean("isDisconnected", isDisconnected);
- }
-
- public int minutesToEndOfSuspend() {
- if (loopSuspendedTill == 0)
- return 0;
-
- long now = System.currentTimeMillis();
- long msecDiff = loopSuspendedTill - now;
-
- if (loopSuspendedTill <= now) { // time exceeded
- suspendTo(0L);
- return 0;
- }
-
- return (int) (msecDiff / 60d / 1000d);
- }
-
- public boolean isSuspended() {
- if (loopSuspendedTill == 0)
- return false;
-
- long now = System.currentTimeMillis();
-
- if (loopSuspendedTill <= now) { // time exceeded
- suspendTo(0L);
- return false;
- }
-
- return true;
- }
-
- public boolean isLGS() {
- Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
- Double MaxIOBallowed = constraintChecker.getMaxIOBAllowed().value();
- String APSmode = sp.getString(R.string.key_aps_mode, "open");
- PumpInterface pump = activePlugin.getActivePump();
- boolean isLGS = false;
-
- if (!isSuspended() && !pump.isSuspended())
- if (closedLoopEnabled.value())
- if ((MaxIOBallowed.equals(hardLimits.getMAXIOB_LGS())) || (APSmode.equals("lgs")))
- isLGS = true;
-
- return isLGS;
- }
-
- public boolean isSuperBolus() {
- if (loopSuspendedTill == 0)
- return false;
-
- long now = System.currentTimeMillis();
-
- if (loopSuspendedTill <= now) { // time exceeded
- suspendTo(0L);
- return false;
- }
-
- return isSuperBolus;
- }
-
- public boolean isDisconnected() {
- if (loopSuspendedTill == 0)
- return false;
-
- long now = System.currentTimeMillis();
-
- if (loopSuspendedTill <= now) { // time exceeded
- suspendTo(0L);
- return false;
- }
- return isDisconnected;
- }
- public boolean treatmentTimethreshold(int duartionMinutes) {
- long threshold = System.currentTimeMillis() + (duartionMinutes*60*1000);
- boolean bool = false;
- if (treatmentsPlugin.getLastBolusTime() > threshold || treatmentsPlugin.getLastCarbTime() > threshold)
- bool = true;
-
- return bool;
- }
-
- public synchronized void invoke(String initiator, boolean allowNotification) {
- invoke(initiator, allowNotification, false);
- }
-
- public synchronized void invoke(String initiator, boolean allowNotification, boolean tempBasalFallback) {
- try {
- getAapsLogger().debug(LTag.APS, "invoke from " + initiator);
- Constraint loopEnabled = constraintChecker.isLoopInvocationAllowed();
-
- if (!loopEnabled.value()) {
- String message = resourceHelper.gs(R.string.loopdisabled) + "\n" + loopEnabled.getReasons(getAapsLogger());
- getAapsLogger().debug(LTag.APS, message);
- rxBus.send(new EventLoopSetLastRunGui(message));
- return;
- }
- final PumpInterface pump = activePlugin.getActivePump();
- APSResult result = null;
-
- if (!isEnabled(PluginType.LOOP))
- return;
-
- Profile profile = profileFunction.getProfile();
-
- if (profile == null || !profileFunction.isProfileValid("Loop")) {
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
- rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected)));
- return;
- }
-
- // Check if pump info is loaded
- if (pump.getBaseBasalRate() < 0.01d) return;
-
- APSInterface usedAPS = activePlugin.getActiveAPS();
- if (((PluginBase) usedAPS).isEnabled(PluginType.APS)) {
- usedAPS.invoke(initiator, tempBasalFallback);
- result = usedAPS.getLastAPSResult();
- }
-
- // Check if we have any result
- if (result == null) {
- rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)));
- return;
- }
-
- // Prepare for pumps using % basals
- if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
- result.usePercent = true;
- }
- result.percent = (int) (result.rate / profile.getBasal() * 100);
-
- // check rate for constraints
- final APSResult resultAfterConstraints = result.newAndClone(injector);
- resultAfterConstraints.rateConstraint = new Constraint<>(resultAfterConstraints.rate);
- resultAfterConstraints.rate = constraintChecker.applyBasalConstraints(resultAfterConstraints.rateConstraint, profile).value();
-
- resultAfterConstraints.percentConstraint = new Constraint<>(resultAfterConstraints.percent);
- resultAfterConstraints.percent = constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.percentConstraint, profile).value();
-
- resultAfterConstraints.smbConstraint = new Constraint<>(resultAfterConstraints.smb);
- resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint).value();
-
- // safety check for multiple SMBs
- long lastBolusTime = treatmentsPlugin.getLastBolusTime();
- if (lastBolusTime != 0 && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) {
- getAapsLogger().debug(LTag.APS, "SMB requsted but still in 3 min interval");
- resultAfterConstraints.smb = 0;
- }
-
- if (lastRun != null && lastRun.getConstraintsProcessed() != null) {
- prevCarbsreq = lastRun.getConstraintsProcessed().carbsReq;
- }
-
- if (lastRun == null) lastRun = new LastRun();
- lastRun.setRequest(result);
- lastRun.setConstraintsProcessed(resultAfterConstraints);
- lastRun.setLastAPSRun(DateUtil.now());
- lastRun.setSource(((PluginBase) usedAPS).getName());
- lastRun.setTbrSetByPump(null);
- lastRun.setSmbSetByPump(null);
- lastRun.setLastTBREnact(0);
- lastRun.setLastTBRRequest(0);
- lastRun.setLastSMBEnact(0);
- lastRun.setLastSMBRequest(0);
-
- nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
-
- if (isSuspended()) {
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended));
- rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)));
- return;
- }
-
- if (pump.isSuspended()) {
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended));
- rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)));
- return;
- }
-
- Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
-
- if (closedLoopEnabled.value()) {
- if (allowNotification) {
- if (resultAfterConstraints.isCarbsRequired()
- && resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0)
- && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimethreshold(-15)) {
-
- if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local,true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)) {
- Notification carbreqlocal = new Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.getCarbsRequiredText(), Notification.NORMAL);
- rxBus.send(new EventNewNotification(carbreqlocal));
- }
- if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
- nsUpload.uploadError(resultAfterConstraints.getCarbsRequiredText());
- }
- if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local,true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)){
- Intent intentAction5m = new Intent(context, CarbSuggestionReceiver.class);
- intentAction5m.putExtra("ignoreDuration", 5);
- PendingIntent pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT);
- NotificationCompat.Action actionIgnore5m = new
- NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m,"Ignore 5m"), pendingIntent5m);
-
- Intent intentAction15m = new Intent(context, CarbSuggestionReceiver.class);
- intentAction15m.putExtra("ignoreDuration", 15);
- PendingIntent pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT);
- NotificationCompat.Action actionIgnore15m = new
- NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m,"Ignore 15m"), pendingIntent15m);
-
- Intent intentAction30m = new Intent(context, CarbSuggestionReceiver.class);
- intentAction30m.putExtra("ignoreDuration", 30);
- PendingIntent pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT);
- NotificationCompat.Action actionIgnore30m = new
- NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m,"Ignore 30m"), pendingIntent30m);
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
- builder.setSmallIcon(R.drawable.notif_icon)
- .setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
- .setContentText(resultAfterConstraints.getCarbsRequiredText())
- .setAutoCancel(true)
- .setPriority(Notification.IMPORTANCE_HIGH)
- .setCategory(Notification.CATEGORY_ALARM)
- .addAction(actionIgnore5m)
- .addAction(actionIgnore15m)
- .addAction(actionIgnore30m)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
-
- NotificationManager mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-
- // mId allows you to update the notification later on.
- mNotificationManager.notify(Constants.notificationID, builder.build());
- rxBus.send(new EventNewOpenLoopNotification());
-
- //only send to wear if Native notifications are turned off
- if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)) {
- // Send to Wear
- actionStringHandler.get().handleInitiate("changeRequest");
- }
- }
-
- } else {
- //If carbs were required previously, but are no longer needed, dismiss notifications
- if ( prevCarbsreq > 0 ) {
- dismissSuggestion();
- rxBus.send(new EventDismissNotification(Notification.CARBS_REQUIRED));
- }
- }
- }
-
- if (resultAfterConstraints.isChangeRequested()
- && !commandQueue.bolusInQueue()
- && !commandQueue.isRunning(Command.CommandType.BOLUS)) {
- final PumpEnactResult waiting = new PumpEnactResult(getInjector());
- waiting.queued = true;
- if (resultAfterConstraints.tempBasalRequested)
- lastRun.setTbrSetByPump(waiting);
- if (resultAfterConstraints.bolusRequested)
- lastRun.setSmbSetByPump(waiting);
- rxBus.send(new EventLoopUpdateGui());
- fabricPrivacy.logCustom("APSRequest");
- applyTBRRequest(resultAfterConstraints, profile, new Callback() {
- @Override
- public void run() {
- if (result.enacted || result.success) {
- lastRun.setTbrSetByPump(result);
- lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
- lastRun.setLastTBREnact(DateUtil.now());
- rxBus.send(new EventLoopUpdateGui());
- applySMBRequest(resultAfterConstraints, new Callback() {
- @Override
- public void run() {
- // Callback is only called if a bolus was actually requested
- if (result.enacted || result.success) {
- lastRun.setSmbSetByPump(result);
- lastRun.setLastSMBRequest(lastRun.getLastAPSRun());
- lastRun.setLastSMBEnact(DateUtil.now());
- } else {
- new Thread(() -> {
- SystemClock.sleep(1000);
- invoke("tempBasalFallback", allowNotification, true);
- }).start();
- }
- rxBus.send(new EventLoopUpdateGui());
- }
- });
- } else {
- lastRun.setTbrSetByPump(result);
- lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
- }
- rxBus.send(new EventLoopUpdateGui());
- }
- });
- } else {
- lastRun.setTbrSetByPump(null);
- lastRun.setSmbSetByPump(null);
- }
- } else {
- if (resultAfterConstraints.isChangeRequested() && allowNotification) {
- NotificationCompat.Builder builder =
- new NotificationCompat.Builder(context, CHANNEL_ID);
- builder.setSmallIcon(R.drawable.notif_icon)
- .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
- .setContentText(resultAfterConstraints.toString())
- .setAutoCancel(true)
- .setPriority(Notification.IMPORTANCE_HIGH)
- .setCategory(Notification.CATEGORY_ALARM)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
- if (sp.getBoolean("wearcontrol", false)) {
- builder.setLocalOnly(true);
- }
- presentSuggestion(builder);
- } else if (allowNotification) {
- dismissSuggestion();
- }
- }
-
- rxBus.send(new EventLoopUpdateGui());
- } finally {
- getAapsLogger().debug(LTag.APS, "invoke end");
- }
- }
-
- public void disableCarbSuggestions(int duartionMinutes) {
- carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + (duartionMinutes*60*1000);
- dismissSuggestion();
- }
-
- private void presentSuggestion(NotificationCompat.Builder builder) {
- // Creates an explicit intent for an Activity in your app
- Intent resultIntent = new Intent(context, MainActivity.class);
-
- // The stack builder object will contain an artificial back stack for the
- // started Activity.
- // This ensures that navigating backward from the Activity leads out of
- // your application to the Home screen.
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
- stackBuilder.addParentStack(MainActivity.class);
- // Adds the Intent that starts the Activity to the top of the stack
- stackBuilder.addNextIntent(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
- builder.setContentIntent(resultPendingIntent);
- builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
- NotificationManager mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- // mId allows you to update the notification later on.
- mNotificationManager.notify(Constants.notificationID, builder.build());
- rxBus.send(new EventNewOpenLoopNotification());
-
- // Send to Wear
- actionStringHandler.get().handleInitiate("changeRequest");
- }
-
- private void dismissSuggestion() {
- // dismiss notifications
- NotificationManager notificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(Constants.notificationID);
- actionStringHandler.get().handleInitiate("cancelChangeRequest");
- }
-
- public void acceptChangeRequest() {
- Profile profile = profileFunction.getProfile();
- final LoopPlugin lp = this;
- applyTBRRequest(lastRun.getConstraintsProcessed(), profile, new Callback() {
- @Override
- public void run() {
- if (result.enacted) {
- lastRun.setTbrSetByPump(result);
- lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
- lastRun.setLastTBREnact(DateUtil.now());
- lastRun.setLastOpenModeAccept(DateUtil.now());
- nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
- sp.incInt(R.string.key_ObjectivesmanualEnacts);
- }
- rxBus.send(new EventAcceptOpenLoopChange());
- }
- });
- fabricPrivacy.logCustom("AcceptTemp");
- }
-
- /**
- * expect absolute request and allow both absolute and percent response based on pump capabilities
- * TODO: update pump drivers to support APS request in %
- */
-
- private void applyTBRRequest(APSResult request, Profile profile, Callback callback) {
-
- if (!request.tempBasalRequested) {
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested))).run();
- }
- return;
- }
-
- PumpInterface pump = activePlugin.getActivePump();
-
- if (!pump.isInitialized()) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized));
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run();
- }
- return;
- }
-
- if (pump.isSuspended()) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended));
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run();
- }
- return;
- }
-
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + request.toString());
-
- long now = System.currentTimeMillis();
- TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(now);
- if (request.usePercent && allowPercentage()) {
- if (request.percent == 100 && request.duration == 0) {
- if (activeTemp != null) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()");
- commandQueue.cancelTempBasal(false, callback);
- } else {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly");
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).percent(request.percent).duration(0)
- .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
- }
- }
- } else if (activeTemp != null
- && activeTemp.getPlannedRemainingMinutes() > 5
- && request.duration - activeTemp.getPlannedRemainingMinutes() < 30
- && request.percent == activeTemp.percentRate) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly");
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).percent(request.percent)
- .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes())
- .comment(resourceHelper.gs(R.string.let_temp_basal_run))).run();
- }
- } else {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: tempBasalPercent()");
- commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, callback);
- }
- } else {
- if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) {
- if (activeTemp != null) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()");
- commandQueue.cancelTempBasal(false, callback);
- } else {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly");
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).absolute(request.rate).duration(0)
- .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
- }
- }
- } else if (activeTemp != null
- && activeTemp.getPlannedRemainingMinutes() > 5
- && request.duration - activeTemp.getPlannedRemainingMinutes() < 30
- && Math.abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly");
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile))
- .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes())
- .comment(resourceHelper.gs(R.string.let_temp_basal_run))).run();
- }
- } else {
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()");
- commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, callback);
- }
- }
- }
-
- private void applySMBRequest(APSResult request, Callback callback) {
- if (!request.bolusRequested) {
- return;
- }
-
- PumpInterface pump = activePlugin.getActivePump();
-
- long lastBolusTime = treatmentsPlugin.getLastBolusTime();
- if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
- getAapsLogger().debug(LTag.APS, "SMB requested but still in 3 min interval");
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector())
- .comment(resourceHelper.gs(R.string.smb_frequency_exceeded))
- .enacted(false).success(false)).run();
- }
- return;
- }
-
- if (!pump.isInitialized()) {
- getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized));
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run();
- }
- return;
- }
-
- if (pump.isSuspended()) {
- getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended));
- if (callback != null) {
- callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run();
- }
- return;
- }
-
- getAapsLogger().debug(LTag.APS, "applySMBRequest: " + request.toString());
-
- // deliver SMB
- DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
- detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.getLastBolusTime();
- detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS;
- detailedBolusInfo.insulin = request.smb;
- detailedBolusInfo.isSMB = true;
- detailedBolusInfo.source = Source.USER;
- detailedBolusInfo.deliverAt = request.deliverAt;
- getAapsLogger().debug(LTag.APS, "applyAPSRequest: bolus()");
- commandQueue.bolus(detailedBolusInfo, callback);
- }
-
- private boolean allowPercentage() {
- return virtualPumpPlugin.isEnabled(PluginType.PUMP);
- }
-
- public void disconnectPump(int durationInMinutes, Profile profile) {
- PumpInterface pump = activePlugin.getActivePump();
-
- disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L);
-
- if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
- commandQueue.tempBasalAbsolute(0, durationInMinutes, true, profile, new Callback() {
- @Override
- public void run() {
- if (!result.success) {
- Intent i = new Intent(context, ErrorHelperActivity.class);
- i.putExtra("soundid", R.raw.boluserror);
- i.putExtra("status", result.comment);
- i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
- }
- });
- } else {
- commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, new Callback() {
- @Override
- public void run() {
- if (!result.success) {
- Intent i = new Intent(context, ErrorHelperActivity.class);
- i.putExtra("soundid", R.raw.boluserror);
- i.putExtra("status", result.comment);
- i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
- }
- });
- }
-
- if (pump.getPumpDescription().isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) {
- commandQueue.cancelExtended(new Callback() {
- @Override
- public void run() {
- if (!result.success) {
- Intent i = new Intent(context, ErrorHelperActivity.class);
- i.putExtra("soundid", R.raw.boluserror);
- i.putExtra("status", result.comment);
- i.putExtra("title", resourceHelper.gs(R.string.extendedbolusdeliveryerror));
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
- }
- });
- }
- createOfflineEvent(durationInMinutes);
- }
-
- public void suspendLoop(int durationInMinutes) {
- suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000);
- commandQueue.cancelTempBasal(true, new Callback() {
- @Override
- public void run() {
- if (!result.success) {
- Intent i = new Intent(context, ErrorHelperActivity.class);
- i.putExtra("soundid", R.raw.boluserror);
- i.putExtra("status", result.comment);
- i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(i);
- }
- }
- });
- createOfflineEvent(durationInMinutes);
- }
-
- public void createOfflineEvent(int durationInMinutes) {
- JSONObject data = new JSONObject();
- try {
- data.put("eventType", CareportalEvent.OPENAPSOFFLINE);
- data.put("duration", durationInMinutes);
- } catch (JSONException e) {
- getAapsLogger().error("Unhandled exception", e);
- }
- CareportalEvent event = new CareportalEvent(getInjector());
- event.date = DateUtil.now();
- event.source = Source.USER;
- event.eventType = CareportalEvent.OPENAPSOFFLINE;
- event.json = data.toString();
- MainApp.getDbHelper().createOrUpdate(event);
- nsUpload.uploadOpenAPSOffline(event);
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
new file mode 100644
index 0000000000..91ced93606
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
@@ -0,0 +1,674 @@
+package info.nightscout.androidaps.plugins.aps.loop
+
+import android.annotation.SuppressLint
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.TaskStackBuilder
+import android.content.Context
+import android.content.Intent
+import android.os.SystemClock
+import androidx.core.app.NotificationCompat
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.*
+import info.nightscout.androidaps.activities.ErrorHelperActivity
+import info.nightscout.androidaps.data.DetailedBolusInfo
+import info.nightscout.androidaps.data.Profile
+import info.nightscout.androidaps.data.PumpEnactResult
+import info.nightscout.androidaps.db.CareportalEvent
+import info.nightscout.androidaps.db.Source
+import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
+import info.nightscout.androidaps.events.EventNewBG
+import info.nightscout.androidaps.events.EventTempTargetChange
+import info.nightscout.androidaps.interfaces.*
+import info.nightscout.androidaps.interfaces.LoopInterface.LastRun
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui
+import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
+import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
+import info.nightscout.androidaps.plugins.bus.RxBusWrapper
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
+import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
+import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
+import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
+import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
+import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
+import info.nightscout.androidaps.queue.Callback
+import info.nightscout.androidaps.queue.commands.Command
+import info.nightscout.androidaps.receivers.ReceiverStatusStore
+import info.nightscout.androidaps.utils.DateUtil
+import info.nightscout.androidaps.utils.FabricPrivacy
+import info.nightscout.androidaps.utils.HardLimits
+import info.nightscout.androidaps.utils.T
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.androidaps.utils.sharedPreferences.SP
+import io.reactivex.disposables.CompositeDisposable
+import org.json.JSONException
+import org.json.JSONObject
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlin.math.abs
+
+@Singleton
+open class LoopPlugin @Inject constructor(
+ injector: HasAndroidInjector,
+ aapsLogger: AAPSLogger?,
+ private val aapsSchedulers: AapsSchedulers,
+ private val rxBus: RxBusWrapper,
+ private val sp: SP,
+ config: Config,
+ private val constraintChecker: ConstraintChecker,
+ resourceHelper: ResourceHelper,
+ private val profileFunction: ProfileFunction,
+ private val context: Context,
+ private val commandQueue: CommandQueueProvider,
+ private val activePlugin: ActivePluginProvider,
+ private val treatmentsPlugin: TreatmentsPlugin,
+ private val virtualPumpPlugin: VirtualPumpPlugin,
+ private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
+ private val receiverStatusStore: ReceiverStatusStore,
+ private val fabricPrivacy: FabricPrivacy,
+ private val nsUpload: NSUpload,
+ private val hardLimits: HardLimits
+) : PluginBase(PluginDescription()
+ .mainType(PluginType.LOOP)
+ .fragmentClass(LoopFragment::class.java.name)
+ .pluginIcon(R.drawable.ic_loop_closed_white)
+ .pluginName(R.string.loop)
+ .shortName(R.string.loop_shortname)
+ .preferencesId(R.xml.pref_loop)
+ .enableByDefault(config.APS)
+ .description(R.string.description_loop),
+ aapsLogger!!, resourceHelper, injector
+), LoopInterface {
+
+ private val disposable = CompositeDisposable()
+ private var lastBgTriggeredRun: Long = 0
+ private var carbsSuggestionsSuspendedUntil: Long = 0
+ private var prevCarbsreq = 0
+ override var lastRun: LastRun? = null
+ override fun onStart() {
+ createNotificationChannel()
+ super.onStart()
+ disposable.add(rxBus
+ .toObservable(EventTempTargetChange::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({ invoke("EventTempTargetChange", true) }, fabricPrivacy::logException)
+ )
+ /*
+ This method is triggered once autosens calculation has completed, so the LoopPlugin
+ has current data to work with. However, autosens calculation can be triggered by multiple
+ sources and currently only a new BG should trigger a loop run. Hence we return early if
+ the event causing the calculation is not EventNewBg.
+
+ */
+ disposable.add(rxBus
+ .toObservable(EventAutosensCalculationFinished::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({ event: EventAutosensCalculationFinished ->
+ // Autosens calculation not triggered by a new BG
+ if (event.cause !is EventNewBG) return@subscribe
+ val glucoseValue = iobCobCalculatorPlugin.actualBg() ?: return@subscribe
+ // BG outdated
+ // already looped with that value
+ if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
+ lastBgTriggeredRun = glucoseValue.timestamp
+ invoke("AutosenseCalculation for $glucoseValue", true)
+ }, fabricPrivacy::logException)
+ )
+ }
+
+ private fun createNotificationChannel() {
+ val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ @SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID,
+ CHANNEL_ID,
+ NotificationManager.IMPORTANCE_HIGH)
+ mNotificationManager.createNotificationChannel(channel)
+ }
+
+ override fun onStop() {
+ disposable.clear()
+ super.onStop()
+ }
+
+ override fun specialEnableCondition(): Boolean {
+ return try {
+ val pump = activePlugin.activePump
+ pump.pumpDescription.isTempBasalCapable
+ } catch (ignored: Exception) {
+ // may fail during initialization
+ true
+ }
+ }
+
+ fun suspendTo(endTime: Long) {
+ sp.putLong("loopSuspendedTill", endTime)
+ sp.putBoolean("isSuperBolus", false)
+ sp.putBoolean("isDisconnected", false)
+ }
+
+ fun superBolusTo(endTime: Long) {
+ sp.putLong("loopSuspendedTill", endTime)
+ sp.putBoolean("isSuperBolus", true)
+ sp.putBoolean("isDisconnected", false)
+ }
+
+ private fun disconnectTo(endTime: Long) {
+ sp.putLong("loopSuspendedTill", endTime)
+ sp.putBoolean("isSuperBolus", false)
+ sp.putBoolean("isDisconnected", true)
+ }
+
+ fun minutesToEndOfSuspend(): Int {
+ val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
+ if (loopSuspendedTill == 0L) return 0
+ val now = System.currentTimeMillis()
+ val millisDiff = loopSuspendedTill - now
+ if (loopSuspendedTill <= now) { // time exceeded
+ suspendTo(0L)
+ return 0
+ }
+ return (millisDiff / 60.0 / 1000.0).toInt()
+ }
+
+ // time exceeded
+ val isSuspended: Boolean
+ get() {
+ val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
+ if (loopSuspendedTill == 0L) return false
+ val now = System.currentTimeMillis()
+ if (loopSuspendedTill <= now) { // time exceeded
+ suspendTo(0L)
+ return false
+ }
+ return true
+ }
+ val isLGS: Boolean
+ get() {
+ val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
+ val maxIobAllowed = constraintChecker.getMaxIOBAllowed().value()
+ val apsMode = sp.getString(R.string.key_aps_mode, "open")
+ val pump = activePlugin.activePump
+ var isLGS = false
+ if (!isSuspended && !pump.isSuspended) if (closedLoopEnabled.value()) if (maxIobAllowed == hardLimits.MAXIOB_LGS || apsMode == "lgs") isLGS = true
+ return isLGS
+ }
+
+ // time exceeded
+ val isSuperBolus: Boolean
+ get() {
+ val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
+ if (loopSuspendedTill == 0L) return false
+ val now = System.currentTimeMillis()
+ if (loopSuspendedTill <= now) { // time exceeded
+ suspendTo(0L)
+ return false
+ }
+ return sp.getBoolean("isSuperBolus", false)
+ }
+
+ // time exceeded
+ val isDisconnected: Boolean
+ get() {
+ val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
+ if (loopSuspendedTill == 0L) return false
+ val now = System.currentTimeMillis()
+ if (loopSuspendedTill <= now) { // time exceeded
+ suspendTo(0L)
+ return false
+ }
+ return sp.getBoolean("isDisconnected", false)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun treatmentTimeThreshold(durationMinutes: Int): Boolean {
+ val threshold = System.currentTimeMillis() + durationMinutes * 60 * 1000
+ var bool = false
+ if (treatmentsPlugin.lastBolusTime > threshold || treatmentsPlugin.lastCarbTime > threshold) bool = true
+ return bool
+ }
+
+ @Synchronized operator fun invoke(initiator: String, allowNotification: Boolean) {
+ invoke(initiator, allowNotification, false)
+ }
+
+ @Synchronized
+ operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {
+ try {
+ aapsLogger.debug(LTag.APS, "invoke from $initiator")
+ val loopEnabled = constraintChecker.isLoopInvocationAllowed()
+ if (!loopEnabled.value()) {
+ val message = """
+ ${resourceHelper.gs(R.string.loopdisabled)}
+ ${loopEnabled.getReasons(aapsLogger)}
+ """.trimIndent()
+ aapsLogger.debug(LTag.APS, message)
+ rxBus.send(EventLoopSetLastRunGui(message))
+ return
+ }
+ val pump = activePlugin.activePump
+ var apsResult: APSResult? = null
+ if (!isEnabled(PluginType.LOOP)) return
+ val profile = profileFunction.getProfile()
+ if (profile == null || !profileFunction.isProfileValid("Loop")) {
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
+ rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected)))
+ return
+ }
+
+ // Check if pump info is loaded
+ if (pump.baseBasalRate < 0.01) return
+ val usedAPS = activePlugin.activeAPS
+ if ((usedAPS as PluginBase).isEnabled(PluginType.APS)) {
+ usedAPS.invoke(initiator, tempBasalFallback)
+ apsResult = usedAPS.lastAPSResult
+ }
+
+ // Check if we have any result
+ if (apsResult == null) {
+ rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)))
+ return
+ }
+
+ // Prepare for pumps using % basals
+ if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
+ apsResult.usePercent = true
+ }
+ apsResult.percent = (apsResult.rate / profile.basal * 100).toInt()
+
+ // check rate for constraints
+ val resultAfterConstraints = apsResult.newAndClone(injector)
+ resultAfterConstraints.rateConstraint = Constraint(resultAfterConstraints.rate)
+ resultAfterConstraints.rate = constraintChecker.applyBasalConstraints(resultAfterConstraints.rateConstraint!!, profile).value()
+ resultAfterConstraints.percentConstraint = Constraint(resultAfterConstraints.percent)
+ resultAfterConstraints.percent = constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.percentConstraint!!, profile).value()
+ resultAfterConstraints.smbConstraint = Constraint(resultAfterConstraints.smb)
+ resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint!!).value()
+
+ // safety check for multiple SMBs
+ val lastBolusTime = treatmentsPlugin.lastBolusTime
+ if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) {
+ aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
+ resultAfterConstraints.smb = 0.0
+ }
+ if (lastRun != null && lastRun!!.constraintsProcessed != null) {
+ prevCarbsreq = lastRun!!.constraintsProcessed!!.carbsReq
+ }
+ if (lastRun == null) lastRun = LastRun()
+ lastRun!!.request = apsResult
+ lastRun!!.constraintsProcessed = resultAfterConstraints
+ lastRun!!.lastAPSRun = DateUtil.now()
+ lastRun!!.source = (usedAPS as PluginBase).name
+ lastRun!!.tbrSetByPump = null
+ lastRun!!.smbSetByPump = null
+ lastRun!!.lastTBREnact = 0
+ lastRun!!.lastTBRRequest = 0
+ lastRun!!.lastSMBEnact = 0
+ lastRun!!.lastSMBRequest = 0
+ nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
+ if (isSuspended) {
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
+ rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
+ return
+ }
+ if (pump.isSuspended) {
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended))
+ rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)))
+ return
+ }
+ val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
+ if (closedLoopEnabled.value()) {
+ if (allowNotification) {
+ if (resultAfterConstraints.isCarbsRequired
+ && resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) {
+ if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
+ val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
+ rxBus.send(EventNewNotification(carbReqLocal))
+ }
+ if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
+ nsUpload.uploadError(resultAfterConstraints.carbsRequiredText)
+ }
+ if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
+ val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java)
+ intentAction5m.putExtra("ignoreDuration", 5)
+ val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT)
+ val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m)
+ val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java)
+ intentAction15m.putExtra("ignoreDuration", 15)
+ val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT)
+ val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m)
+ val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java)
+ intentAction30m.putExtra("ignoreDuration", 30)
+ val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT)
+ val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m)
+ val builder = NotificationCompat.Builder(context, CHANNEL_ID)
+ builder.setSmallIcon(R.drawable.notif_icon)
+ .setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
+ .setContentText(resultAfterConstraints.carbsRequiredText)
+ .setAutoCancel(true)
+ .setPriority(Notification.IMPORTANCE_HIGH)
+ .setCategory(Notification.CATEGORY_ALARM)
+ .addAction(actionIgnore5m)
+ .addAction(actionIgnore15m)
+ .addAction(actionIgnore30m)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
+ val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ // mId allows you to update the notification later on.
+ mNotificationManager.notify(Constants.notificationID, builder.build())
+ rxBus.send(EventNewOpenLoopNotification())
+
+ //only send to wear if Native notifications are turned off
+ if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
+ // Send to Wear
+ rxBus.send(EventWearDoAction("changeRequest"))
+ }
+ }
+ } else {
+ //If carbs were required previously, but are no longer needed, dismiss notifications
+ if (prevCarbsreq > 0) {
+ dismissSuggestion()
+ rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED))
+ }
+ }
+ }
+ if (resultAfterConstraints.isChangeRequested
+ && !commandQueue.bolusInQueue()
+ && !commandQueue.isRunning(Command.CommandType.BOLUS)) {
+ val waiting = PumpEnactResult(injector)
+ waiting.queued = true
+ if (resultAfterConstraints.tempBasalRequested) lastRun!!.tbrSetByPump = waiting
+ if (resultAfterConstraints.bolusRequested) lastRun!!.smbSetByPump = waiting
+ rxBus.send(EventLoopUpdateGui())
+ fabricPrivacy.logCustom("APSRequest")
+ applyTBRRequest(resultAfterConstraints, profile, object : Callback() {
+ override fun run() {
+ if (result.enacted || result.success) {
+ lastRun!!.tbrSetByPump = result
+ lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
+ lastRun!!.lastTBREnact = DateUtil.now()
+ rxBus.send(EventLoopUpdateGui())
+ applySMBRequest(resultAfterConstraints, object : Callback() {
+ override fun run() {
+ // Callback is only called if a bolus was actually requested
+ if (result.enacted || result.success) {
+ lastRun!!.smbSetByPump = result
+ lastRun!!.lastSMBRequest = lastRun!!.lastAPSRun
+ lastRun!!.lastSMBEnact = DateUtil.now()
+ } else {
+ Thread {
+ SystemClock.sleep(1000)
+ invoke("tempBasalFallback", allowNotification, true)
+ }.start()
+ }
+ rxBus.send(EventLoopUpdateGui())
+ }
+ })
+ } else {
+ lastRun!!.tbrSetByPump = result
+ lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
+ }
+ rxBus.send(EventLoopUpdateGui())
+ }
+ })
+ } else {
+ lastRun!!.tbrSetByPump = null
+ lastRun!!.smbSetByPump = null
+ }
+ } else {
+ if (resultAfterConstraints.isChangeRequested && allowNotification) {
+ val builder = NotificationCompat.Builder(context, CHANNEL_ID)
+ builder.setSmallIcon(R.drawable.notif_icon)
+ .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
+ .setContentText(resultAfterConstraints.toString())
+ .setAutoCancel(true)
+ .setPriority(Notification.IMPORTANCE_HIGH)
+ .setCategory(Notification.CATEGORY_ALARM)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ if (sp.getBoolean(R.string.key_wear_control, false)) {
+ builder.setLocalOnly(true)
+ }
+ presentSuggestion(builder)
+ } else if (allowNotification) {
+ dismissSuggestion()
+ }
+ }
+ rxBus.send(EventLoopUpdateGui())
+ } finally {
+ aapsLogger.debug(LTag.APS, "invoke end")
+ }
+ }
+
+ fun disableCarbSuggestions(durationMinutes: Int) {
+ carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + durationMinutes * 60 * 1000
+ dismissSuggestion()
+ }
+
+ private fun presentSuggestion(builder: NotificationCompat.Builder) {
+ // Creates an explicit intent for an Activity in your app
+ val resultIntent = Intent(context, MainActivity::class.java)
+
+ // The stack builder object will contain an artificial back stack for the
+ // started Activity.
+ // This ensures that navigating backward from the Activity leads out of
+ // your application to the Home screen.
+ val stackBuilder = TaskStackBuilder.create(context)
+ stackBuilder.addParentStack(MainActivity::class.java)
+ // Adds the Intent that starts the Activity to the top of the stack
+ stackBuilder.addNextIntent(resultIntent)
+ val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
+ builder.setContentIntent(resultPendingIntent)
+ builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
+ val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ // mId allows you to update the notification later on.
+ mNotificationManager.notify(Constants.notificationID, builder.build())
+ rxBus.send(EventNewOpenLoopNotification())
+
+ // Send to Wear
+ rxBus.send(EventWearDoAction("changeRequest"))
+ }
+
+ private fun dismissSuggestion() {
+ // dismiss notifications
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.cancel(Constants.notificationID)
+ rxBus.send(EventWearDoAction("cancelChangeRequest"))
+ }
+
+ fun acceptChangeRequest() {
+ val profile = profileFunction.getProfile()
+ val lp = this
+ applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() {
+ override fun run() {
+ if (result.enacted) {
+ lastRun!!.tbrSetByPump = result
+ lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
+ lastRun!!.lastTBREnact = DateUtil.now()
+ lastRun!!.lastOpenModeAccept = DateUtil.now()
+ nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
+ sp.incInt(R.string.key_ObjectivesmanualEnacts)
+ }
+ rxBus.send(EventAcceptOpenLoopChange())
+ }
+ })
+ fabricPrivacy.logCustom("AcceptTemp")
+ }
+
+ /**
+ * expect absolute request and allow both absolute and percent response based on pump capabilities
+ * TODO: update pump drivers to support APS request in %
+ */
+ private fun applyTBRRequest(request: APSResult?, profile: Profile?, callback: Callback?) {
+ if (!request!!.tempBasalRequested) {
+ callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested)))?.run()
+ return
+ }
+ val pump = activePlugin.activePump
+ if (!pump.isInitialized) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
+ callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
+ return
+ }
+ if (pump.isSuspended) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended))
+ callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
+ return
+ }
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: $request")
+ val now = System.currentTimeMillis()
+ val activeTemp = treatmentsPlugin.getTempBasalFromHistory(now)
+ if (request.usePercent && allowPercentage()) {
+ if (request.percent == 100 && request.duration == 0) {
+ if (activeTemp != null) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
+ commandQueue.cancelTempBasal(false, callback)
+ } else {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
+ callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0)
+ .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
+ }
+ } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.percentRate) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
+ callback?.result(PumpEnactResult(injector).percent(request.percent)
+ .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
+ .comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
+ } else {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()")
+ commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback)
+ }
+ } else {
+ if (request.rate == 0.0 && request.duration == 0 || abs(request.rate - pump.baseBasalRate) < pump.pumpDescription.basalStep) {
+ if (activeTemp != null) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
+ commandQueue.cancelTempBasal(false, callback)
+ } else {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
+ callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0)
+ .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
+ }
+ } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
+ callback?.result(PumpEnactResult(injector).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile))
+ .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
+ .comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
+ } else {
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()")
+ commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback)
+ }
+ }
+ }
+
+ private fun applySMBRequest(request: APSResult, callback: Callback?) {
+ if (!request.bolusRequested) {
+ return
+ }
+ val pump = activePlugin.activePump
+ val lastBolusTime = treatmentsPlugin.lastBolusTime
+ if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
+ aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
+ callback?.result(PumpEnactResult(injector)
+ .comment(resourceHelper.gs(R.string.smb_frequency_exceeded))
+ .enacted(false).success(false))?.run()
+ return
+ }
+ if (!pump.isInitialized) {
+ aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
+ callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
+ return
+ }
+ if (pump.isSuspended) {
+ aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended))
+ callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
+ return
+ }
+ aapsLogger.debug(LTag.APS, "applySMBRequest: $request")
+
+ // deliver SMB
+ val detailedBolusInfo = DetailedBolusInfo()
+ detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.lastBolusTime
+ detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
+ detailedBolusInfo.insulin = request.smb
+ detailedBolusInfo.isSMB = true
+ detailedBolusInfo.source = Source.USER
+ detailedBolusInfo.deliverAt = request.deliverAt
+ aapsLogger.debug(LTag.APS, "applyAPSRequest: bolus()")
+ commandQueue.bolus(detailedBolusInfo, callback)
+ }
+
+ private fun allowPercentage(): Boolean {
+ return virtualPumpPlugin.isEnabled(PluginType.PUMP)
+ }
+
+ fun disconnectPump(durationInMinutes: Int, profile: Profile?) {
+ val pump = activePlugin.activePump
+ disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L)
+ if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
+ commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, object : Callback() {
+ override fun run() {
+ if (!result.success) {
+ ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
+ }
+ }
+ })
+ } else {
+ commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, object : Callback() {
+ override fun run() {
+ if (!result.success) {
+ ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
+ }
+ }
+ })
+ }
+ if (pump.pumpDescription.isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
+ commandQueue.cancelExtended(object : Callback() {
+ override fun run() {
+ if (!result.success) {
+ ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
+ }
+ }
+ })
+ }
+ createOfflineEvent(durationInMinutes)
+ }
+
+ fun suspendLoop(durationInMinutes: Int) {
+ suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000)
+ commandQueue.cancelTempBasal(true, object : Callback() {
+ override fun run() {
+ if (!result.success) {
+ ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
+ }
+ }
+ })
+ createOfflineEvent(durationInMinutes)
+ }
+
+ fun createOfflineEvent(durationInMinutes: Int) {
+ val data = JSONObject()
+ try {
+ data.put("eventType", CareportalEvent.OPENAPSOFFLINE)
+ data.put("duration", durationInMinutes)
+ } catch (e: JSONException) {
+ aapsLogger.error("Unhandled exception", e)
+ }
+ val event = CareportalEvent(injector)
+ event.date = DateUtil.now()
+ event.source = Source.USER
+ event.eventType = CareportalEvent.OPENAPSOFFLINE
+ event.json = data.toString()
+ MainApp.getDbHelper().createOrUpdate(event)
+ nsUpload.uploadOpenAPSOffline(event)
+ }
+
+ companion object {
+
+ private const val CHANNEL_ID = "AndroidAPS-OpenLoop"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java
deleted file mode 100644
index e3a64dbece..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.loop;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class ScriptReader {
-
- private final Context mContext;
-
- public ScriptReader(Context context) {
- mContext = context;
- }
-
- public byte[] readFile(String fileName) throws IOException {
-
- AssetManager assetManager = mContext.getAssets();
- InputStream is = assetManager.open(fileName);
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-
- int nRead;
- byte[] data = new byte[16384];
-
- while ((nRead = is.read(data, 0, data.length)) != -1) {
- buffer.write(data, 0, nRead);
- }
-
- buffer.flush();
-
- byte[] bytes = buffer.toByteArray();
- is.close();
- buffer.close();
-
-
- return bytes;
-
- }
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt
new file mode 100644
index 0000000000..000cd55900
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt
@@ -0,0 +1,25 @@
+package info.nightscout.androidaps.plugins.aps.loop
+
+import android.content.Context
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+
+class ScriptReader(private val context: Context) {
+
+ @Throws(IOException::class)
+ fun readFile(fileName: String): ByteArray {
+ val assetManager = context.assets
+ val `is` = assetManager.open(fileName)
+ val buffer = ByteArrayOutputStream()
+ var nRead: Int
+ val data = ByteArray(16384)
+ while (`is`.read(data, 0, data.size).also { nRead = it } != -1) {
+ buffer.write(data, 0, nRead)
+ }
+ buffer.flush()
+ val bytes = buffer.toByteArray()
+ `is`.close()
+ buffer.close()
+ return bytes
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java
deleted file mode 100644
index a6876a3144..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java
+++ /dev/null
@@ -1,299 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSAMA;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.Function;
-import org.mozilla.javascript.NativeJSON;
-import org.mozilla.javascript.NativeObject;
-import org.mozilla.javascript.RhinoException;
-import org.mozilla.javascript.Scriptable;
-import org.mozilla.javascript.ScriptableObject;
-import org.mozilla.javascript.Undefined;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.charset.StandardCharsets;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-
-import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.Constants;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.IobTotal;
-import info.nightscout.androidaps.data.MealData;
-import info.nightscout.androidaps.data.Profile;
-import info.nightscout.androidaps.db.TemporaryBasal;
-import info.nightscout.androidaps.logging.AAPSLogger;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
-import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback;
-import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
-import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
-import info.nightscout.androidaps.interfaces.ProfileFunction;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
-import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
-import info.nightscout.androidaps.utils.sharedPreferences.SP;
-
-public class DetermineBasalAdapterAMAJS {
- private final HasAndroidInjector injector;
- @Inject AAPSLogger aapsLogger;
- @Inject ConstraintChecker constraintChecker;
- @Inject SP sp;
- @Inject ProfileFunction profileFunction;
- @Inject TreatmentsPlugin treatmentsPlugin;
- @Inject OpenHumansUploader openHumansUploader;
-
- private final ScriptReader mScriptReader;
-
- private JSONObject mProfile;
- private JSONObject mGlucoseStatus;
- private JSONArray mIobData;
- private JSONObject mMealData;
- private JSONObject mCurrentTemp;
- private JSONObject mAutosensData = null;
-
- private String storedCurrentTemp = null;
- private String storedIobData = null;
- private String storedGlucoseStatus = null;
- private String storedProfile = null;
- private String storedMeal_data = null;
- private String storedAutosens_data = null;
-
- private String scriptDebug = "";
-
- DetermineBasalAdapterAMAJS(ScriptReader scriptReader, HasAndroidInjector injector) {
- injector.androidInjector().inject(this);
- mScriptReader = scriptReader;
- this.injector = injector;
- }
-
- @Nullable
- public DetermineBasalResultAMA invoke() {
-
- aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<");
- aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString()));
- aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString()));
- aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString()));
- aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString()));
- aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString()));
- if (mAutosensData != null)
- aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = mAutosensData.toString()));
- else
- aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = "undefined"));
-
-
- DetermineBasalResultAMA determineBasalResultAMA = null;
-
- Context rhino = Context.enter();
- Scriptable scope = rhino.initStandardObjects();
- // Turn off optimization to make Rhino Android compatible
- rhino.setOptimizationLevel(-1);
-
- try {
-
- //register logger callback for console.log and console.error
- ScriptableObject.defineClass(scope, LoggerCallback.class);
- Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null);
- scope.put("console2", scope, myLogger);
- rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null);
-
- //set module parent
- rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null);
- rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null);
- rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null);
-
- //generate functions "determine_basal" and "setTempBasal"
- rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null);
- rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null);
- Object determineBasalObj = scope.get("determine_basal", scope);
- Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope);
-
- //call determine-basal
- if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) {
- Function determineBasalJS = (Function) determineBasalObj;
-
- //prepare parameters
- Object[] params = new Object[]{
- makeParam(mGlucoseStatus, rhino, scope),
- makeParam(mCurrentTemp, rhino, scope),
- makeParamArray(mIobData, rhino, scope),
- makeParam(mProfile, rhino, scope),
- makeParam(mAutosensData, rhino, scope),
- makeParam(mMealData, rhino, scope),
- setTempBasalFunctionsObj};
-
- NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params);
- scriptDebug = LoggerCallback.getScriptDebug();
-
- // Parse the jsResult object to a JSON-String
- String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString();
- aapsLogger.debug(LTag.APS, "Result: " + result);
- try {
- JSONObject resultJson = new JSONObject(result);
- openHumansUploader.enqueueAMAData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, resultJson);
- determineBasalResultAMA = new DetermineBasalResultAMA(injector, jsResult, resultJson);
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Unhandled exception", e);
- }
- } else {
- aapsLogger.error(LTag.APS, "Problem loading JS Functions");
- }
- } catch (IOException e) {
- aapsLogger.error(LTag.APS, "IOException");
- } catch (RhinoException e) {
- aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString());
- } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
- aapsLogger.error(LTag.APS, e.toString());
- } finally {
- Context.exit();
- }
-
- storedGlucoseStatus = mGlucoseStatus.toString();
- storedIobData = mIobData.toString();
- storedCurrentTemp = mCurrentTemp.toString();
- storedProfile = mProfile.toString();
- storedMeal_data = mMealData.toString();
-
- return determineBasalResultAMA;
-
- }
-
- String getGlucoseStatusParam() {
- return storedGlucoseStatus;
- }
-
- String getCurrentTempParam() {
- return storedCurrentTemp;
- }
-
- String getIobDataParam() {
- return storedIobData;
- }
-
- String getProfileParam() {
- return storedProfile;
- }
-
- String getMealDataParam() {
- return storedMeal_data;
- }
-
- String getAutosensDataParam() {
- return storedAutosens_data;
- }
-
- String getScriptDebug() {
- return scriptDebug;
- }
-
- public void setData(Profile profile,
- double maxIob,
- double maxBasal,
- double minBg,
- double maxBg,
- double targetBg,
- double basalrate,
- IobTotal[] iobArray,
- GlucoseStatus glucoseStatus,
- MealData mealData,
- double autosensDataRatio,
- boolean tempTargetSet) throws JSONException {
-
- mProfile = new JSONObject();
- mProfile.put("max_iob", maxIob);
- mProfile.put("dia", Math.min(profile.getDia(), 3d));
- mProfile.put("type", "current");
- mProfile.put("max_daily_basal", profile.getMaxDailyBasal());
- mProfile.put("max_basal", maxBasal);
- mProfile.put("min_bg", minBg);
- mProfile.put("max_bg", maxBg);
- mProfile.put("target_bg", targetBg);
- mProfile.put("carb_ratio", profile.getIc());
- mProfile.put("sens", profile.getIsfMgdl());
- mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
- mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
- mProfile.put("skip_neutral_temps", true);
- mProfile.put("current_basal", basalrate);
- mProfile.put("temptargetSet", tempTargetSet);
- mProfile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true));
- //align with max-absorption model in AMA sensitivity
- if (mealData.usedMinCarbsImpact > 0) {
- mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
- } else {
- mProfile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
- }
-
- if (profileFunction.getUnits().equals(Constants.MMOL)) {
- mProfile.put("out_units", "mmol/L");
- }
-
- long now = System.currentTimeMillis();
- TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now);
-
- mCurrentTemp = new JSONObject();
- mCurrentTemp.put("temp", "absolute");
- mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0);
- mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d);
-
- // as we have non default temps longer than 30 mintues
- TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis());
- if (tempBasal != null) {
- mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration());
- }
-
- mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray);
-
- mGlucoseStatus = new JSONObject();
- mGlucoseStatus.put("glucose", glucoseStatus.glucose);
-
- if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
- mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta);
- } else {
- mGlucoseStatus.put("delta", glucoseStatus.delta);
- }
- mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta);
- mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta);
-
- mMealData = new JSONObject();
- mMealData.put("carbs", mealData.carbs);
- mMealData.put("boluses", mealData.boluses);
- mMealData.put("mealCOB", mealData.mealCOB);
-
- if (constraintChecker.isAutosensModeEnabled().value()) {
- mAutosensData = new JSONObject();
- mAutosensData.put("ratio", autosensDataRatio);
- } else {
- mAutosensData = null;
- }
- }
-
-
- private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) {
-
- if (jsonObject == null) return Undefined.instance;
-
- Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
- return param;
- }
-
- private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) {
- //Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() {
- Object param = NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
- return param;
- }
-
- private String readFile(String filename) throws IOException {
- byte[] bytes = mScriptReader.readFile(filename);
- String string = new String(bytes, StandardCharsets.UTF_8);
- if (string.startsWith("#!/usr/bin/env node")) {
- string = string.substring(20);
- }
- return string;
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt
new file mode 100644
index 0000000000..ee0be787e4
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt
@@ -0,0 +1,238 @@
+package info.nightscout.androidaps.plugins.aps.openAPSAMA
+
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.Constants
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.data.IobTotal
+import info.nightscout.androidaps.data.MealData
+import info.nightscout.androidaps.data.Profile
+import info.nightscout.androidaps.interfaces.ProfileFunction
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
+import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
+import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
+import info.nightscout.androidaps.utils.sharedPreferences.SP
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+import org.mozilla.javascript.*
+import org.mozilla.javascript.Function
+import java.io.IOException
+import java.lang.reflect.InvocationTargetException
+import java.nio.charset.StandardCharsets
+import javax.inject.Inject
+import kotlin.math.min
+
+class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader, injector: HasAndroidInjector) {
+
+ private val injector: HasAndroidInjector
+
+ @Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var constraintChecker: ConstraintChecker
+ @Inject lateinit var sp: SP
+ @Inject lateinit var profileFunction: ProfileFunction
+ @Inject lateinit var treatmentsPlugin: TreatmentsPlugin
+ @Inject lateinit var openHumansUploader: OpenHumansUploader
+
+ private val mScriptReader: ScriptReader
+ private var profile = JSONObject()
+ private var glucoseStatus = JSONObject()
+ private var iobData: JSONArray? = null
+ private var mealData = JSONObject()
+ private var currentTemp = JSONObject()
+ private var autosensData = JSONObject()
+
+ var currentTempParam: String? = null
+ private set
+ var iobDataParam: String? = null
+ private set
+ var glucoseStatusParam: String? = null
+ private set
+ var profileParam: String? = null
+ private set
+ var mealDataParam: String? = null
+ private set
+ var scriptDebug = ""
+ private set
+
+ operator fun invoke(): DetermineBasalResultAMA? {
+ aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<")
+ aapsLogger.debug(LTag.APS, "Glucose status: " + glucoseStatus.toString().also { glucoseStatusParam = it })
+ aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it })
+ aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it })
+ aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it })
+ aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it })
+ aapsLogger.debug(LTag.APS, "Autosens data: $autosensData")
+ var determineBasalResultAMA: DetermineBasalResultAMA? = null
+ val rhino = Context.enter()
+ val scope: Scriptable = rhino.initStandardObjects()
+ // Turn off optimization to make Rhino Android compatible
+ rhino.optimizationLevel = -1
+ try {
+
+ //register logger callback for console.log and console.error
+ ScriptableObject.defineClass(scope, LoggerCallback::class.java)
+ val myLogger = rhino.newObject(scope, "LoggerCallback", null)
+ scope.put("console2", scope, myLogger)
+ rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null)
+
+ //set module parent
+ rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null)
+ rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null)
+ rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null)
+
+ //generate functions "determine_basal" and "setTempBasal"
+ rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null)
+ rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null)
+ val determineBasalObj = scope["determine_basal", scope]
+ val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope]
+
+ //call determine-basal
+ if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) {
+
+ //prepare parameters
+ val params = arrayOf(
+ makeParam(glucoseStatus, rhino, scope),
+ makeParam(currentTemp, rhino, scope),
+ makeParamArray(iobData, rhino, scope),
+ makeParam(profile, rhino, scope),
+ makeParam(autosensData, rhino, scope),
+ makeParam(mealData, rhino, scope),
+ setTempBasalFunctionsObj)
+ val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
+ scriptDebug = LoggerCallback.scriptDebug
+
+ // Parse the jsResult object to a JSON-String
+ val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString()
+ aapsLogger.debug(LTag.APS, "Result: $result")
+ try {
+ val resultJson = JSONObject(result)
+ openHumansUploader.enqueueAMAData(profile, glucoseStatus, iobData, mealData, currentTemp, autosensData, resultJson)
+ determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson)
+ } catch (e: JSONException) {
+ aapsLogger.error(LTag.APS, "Unhandled exception", e)
+ }
+ } else {
+ aapsLogger.error(LTag.APS, "Problem loading JS Functions")
+ }
+ } catch (e: IOException) {
+ aapsLogger.error(LTag.APS, "IOException")
+ } catch (e: RhinoException) {
+ aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString())
+ } catch (e: IllegalAccessException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } catch (e: InstantiationException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } catch (e: InvocationTargetException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } finally {
+ Context.exit()
+ }
+ glucoseStatusParam = glucoseStatus.toString()
+ iobDataParam = iobData.toString()
+ currentTempParam = currentTemp.toString()
+ profileParam = profile.toString()
+ mealDataParam = mealData.toString()
+ return determineBasalResultAMA
+ }
+
+ @Throws(JSONException::class) fun setData(profile: Profile,
+ maxIob: Double,
+ maxBasal: Double,
+ minBg: Double,
+ maxBg: Double,
+ targetBg: Double,
+ basalRate: Double,
+ iobArray: Array?,
+ glucoseStatus: GlucoseStatus,
+ mealData: MealData,
+ autosensDataRatio: Double,
+ tempTargetSet: Boolean) {
+ this.profile = JSONObject()
+ this.profile.put("max_iob", maxIob)
+ this.profile.put("dia", min(profile.dia, 3.0))
+ this.profile.put("type", "current")
+ this.profile.put("max_daily_basal", profile.maxDailyBasal)
+ this.profile.put("max_basal", maxBasal)
+ this.profile.put("min_bg", minBg)
+ this.profile.put("max_bg", maxBg)
+ this.profile.put("target_bg", targetBg)
+ this.profile.put("carb_ratio", profile.ic)
+ this.profile.put("sens", profile.isfMgdl)
+ this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
+ this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
+ this.profile.put("skip_neutral_temps", true)
+ this.profile.put("current_basal", basalRate)
+ this.profile.put("temptargetSet", tempTargetSet)
+ this.profile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true))
+ //align with max-absorption model in AMA sensitivity
+ if (mealData.usedMinCarbsImpact > 0) {
+ this.profile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact)
+ } else {
+ this.profile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact))
+ }
+ if (profileFunction.getUnits() == Constants.MMOL) {
+ this.profile.put("out_units", "mmol/L")
+ }
+ val now = System.currentTimeMillis()
+ val tb = treatmentsPlugin.getTempBasalFromHistory(now)
+ currentTemp = JSONObject()
+ currentTemp.put("temp", "absolute")
+ currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
+ currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
+
+ // as we have non default temps longer than 30 minutes
+ val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
+ if (tempBasal != null) {
+ currentTemp.put("minutesrunning", tempBasal.realDuration)
+ }
+ iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
+ this.glucoseStatus = JSONObject()
+ this.glucoseStatus.put("glucose", glucoseStatus.glucose)
+ if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
+ this.glucoseStatus.put("delta", glucoseStatus.short_avgdelta)
+ } else {
+ this.glucoseStatus.put("delta", glucoseStatus.delta)
+ }
+ this.glucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta)
+ this.glucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta)
+ this.mealData = JSONObject()
+ this.mealData.put("carbs", mealData.carbs)
+ this.mealData.put("boluses", mealData.boluses)
+ this.mealData.put("mealCOB", mealData.mealCOB)
+ if (constraintChecker.isAutosensModeEnabled().value()) {
+ autosensData.put("ratio", autosensDataRatio)
+ } else {
+ autosensData.put("ratio", 1.0)
+ }
+ }
+
+ private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {
+ return if (jsonObject == null) Undefined.instance else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] }
+ }
+
+ private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
+ return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] }
+ }
+
+ @Throws(IOException::class) private fun readFile(filename: String): String {
+ val bytes = mScriptReader.readFile(filename)
+ var string = String(bytes, StandardCharsets.UTF_8)
+ if (string.startsWith("#!/usr/bin/env node")) {
+ string = string.substring(20)
+ }
+ return string
+ }
+
+ init {
+ injector.androidInjector().inject(this)
+ mScriptReader = scriptReader
+ this.injector = injector
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java
deleted file mode 100644
index 0bf4839f84..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSAMA;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.javascript.NativeObject;
-
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.logging.AAPSLogger;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.plugins.aps.loop.APSResult;
-import info.nightscout.androidaps.utils.DateUtil;
-
-public class DetermineBasalResultAMA extends APSResult {
- private AAPSLogger aapsLogger;
-
- private double eventualBG;
- private double snoozeBG;
-
- DetermineBasalResultAMA(HasAndroidInjector injector, NativeObject result, JSONObject j) {
- this(injector);
- date = DateUtil.now();
- json = j;
- if (result.containsKey("error")) {
- reason = result.get("error").toString();
- tempBasalRequested = false;
- rate = -1;
- duration = -1;
- } else {
- reason = result.get("reason").toString();
- if (result.containsKey("eventualBG")) eventualBG = (Double) result.get("eventualBG");
- if (result.containsKey("snoozeBG")) snoozeBG = (Double) result.get("snoozeBG");
- if (result.containsKey("rate")) {
- rate = (Double) result.get("rate");
- if (rate < 0d) rate = 0d;
- tempBasalRequested = true;
- } else {
- rate = -1;
- tempBasalRequested = false;
- }
- if (result.containsKey("duration")) {
- duration = ((Double) result.get("duration")).intValue();
- //changeRequested as above
- } else {
- duration = -1;
- tempBasalRequested = false;
- }
- }
- bolusRequested = false;
- }
-
- private DetermineBasalResultAMA(HasAndroidInjector injector) {
- super(injector);
- hasPredictions = true;
- }
-
- @Override
- public DetermineBasalResultAMA newAndClone(HasAndroidInjector injector) {
- DetermineBasalResultAMA newResult = new DetermineBasalResultAMA(injector);
- doClone(newResult);
-
- newResult.eventualBG = eventualBG;
- newResult.snoozeBG = snoozeBG;
- return newResult;
- }
-
- @Override
- public JSONObject json() {
- try {
- JSONObject ret = new JSONObject(this.json.toString());
- return ret;
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Unhandled exception", e);
- }
- return null;
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt
new file mode 100644
index 0000000000..8f462b2f19
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt
@@ -0,0 +1,67 @@
+package info.nightscout.androidaps.plugins.aps.openAPSAMA
+
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.loop.APSResult
+import info.nightscout.androidaps.utils.DateUtil
+import org.json.JSONException
+import org.json.JSONObject
+import org.mozilla.javascript.NativeObject
+
+class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector) : APSResult(injector) {
+
+ private var eventualBG = 0.0
+ private var snoozeBG = 0.0
+
+ internal constructor(injector: HasAndroidInjector, result: NativeObject, j: JSONObject) : this(injector) {
+ date = DateUtil.now()
+ json = j
+ if (result.containsKey("error")) {
+ reason = result["error"].toString()
+ tempBasalRequested = false
+ rate = (-1).toDouble()
+ duration = -1
+ } else {
+ reason = result["reason"].toString()
+ if (result.containsKey("eventualBG")) eventualBG = result["eventualBG"] as Double
+ if (result.containsKey("snoozeBG")) snoozeBG = result["snoozeBG"] as Double
+ if (result.containsKey("rate")) {
+ rate = result["rate"] as Double
+ if (rate < 0.0) rate = 0.0
+ tempBasalRequested = true
+ } else {
+ rate = (-1).toDouble()
+ tempBasalRequested = false
+ }
+ if (result.containsKey("duration")) {
+ duration = (result["duration"] as Double).toInt()
+ //changeRequested as above
+ } else {
+ duration = -1
+ tempBasalRequested = false
+ }
+ }
+ bolusRequested = false
+ }
+
+ override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultAMA {
+ val newResult = DetermineBasalResultAMA(injector)
+ doClone(newResult)
+ newResult.eventualBG = eventualBG
+ newResult.snoozeBG = snoozeBG
+ return newResult
+ }
+
+ override fun json(): JSONObject? {
+ try {
+ return JSONObject(json.toString())
+ } catch (e: JSONException) {
+ aapsLogger.error(LTag.APS, "Unhandled exception", e)
+ }
+ return null
+ }
+
+ init {
+ hasPredictions = true
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt
index fcd78a9adc..84bc71daf7 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt
@@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
+import info.nightscout.androidaps.databinding.OpenapsamaFragmentBinding
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
@@ -15,34 +16,42 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
-import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
-import io.reactivex.android.schedulers.AndroidSchedulers
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
-import kotlinx.android.synthetic.main.openapsama_fragment.*
+import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
class OpenAPSAMAFragment : DaggerFragment() {
+
private var disposable: CompositeDisposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin
@Inject lateinit var dateUtil: DateUtil
+ private var _binding: OpenapsamaFragmentBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.openapsama_fragment, container, false)
+ savedInstanceState: Bundle?): View {
+ _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- openapsma_run.setOnClickListener {
+ binding.run.setOnClickListener {
openAPSAMAPlugin.invoke("OpenAPSAMA button", false)
}
}
@@ -53,16 +62,16 @@ class OpenAPSAMAFragment : DaggerFragment() {
disposable += rxBus
.toObservable(EventOpenAPSUpdateGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
updateGUI()
- }, { fabricPrivacy.logException(it) })
+ }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOpenAPSUpdateResultGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
updateResultGUI(it.text)
- }, { fabricPrivacy.logException(it) })
+ }, fabricPrivacy::logException)
updateGUI()
}
@@ -73,47 +82,53 @@ class OpenAPSAMAFragment : DaggerFragment() {
disposable.clear()
}
+ @Synchronized
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
@Synchronized
private fun updateGUI() {
- if (openapsma_result == null) return
+ if (_binding == null) return
openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult ->
- openapsma_result.text = JSONFormatter.format(lastAPSResult.json)
- openapsma_request.text = lastAPSResult.toSpanned()
+ binding.result.text = JSONFormatter.format(lastAPSResult.json)
+ binding.request.text = lastAPSResult.toSpanned()
}
openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS ->
- openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam)
- openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam)
+ binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam)
+ binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam)
try {
val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam)
- openapsma_iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
+ binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
@Suppress("SetTextI18n")
- openapsma_iobdata.text = "JSONException see log for details"
+ binding.iobdata.text = "JSONException see log for details"
}
- openapsma_profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam)
- openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam)
- openapsma_scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug
+ binding.profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam)
+ binding.mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam)
+ binding.scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug
}
if (openAPSAMAPlugin.lastAPSRun != 0L) {
- openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
+ binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
}
- openAPSAMAPlugin.lastAutosensResult?.let {
- openapsma_autosensdata.text = JSONFormatter.format(it.json())
+ openAPSAMAPlugin.lastAutosensResult.let {
+ binding.autosensdata.text = JSONFormatter.format(it.json())
}
}
private fun updateResultGUI(text: String) {
- openapsma_result.text = text
- openapsma_glucosestatus.text = ""
- openapsma_currenttemp.text = ""
- openapsma_iobdata.text = ""
- openapsma_profile.text = ""
- openapsma_mealdata.text = ""
- openapsma_autosensdata.text = ""
- openapsma_scriptdebugdata.text = ""
- openapsma_request.text = ""
- openapsma_lastrun.text = ""
+ binding.result.text = text
+ binding.glucosestatus.text = ""
+ binding.currenttemp.text = ""
+ binding.iobdata.text = ""
+ binding.profile.text = ""
+ binding.mealdata.text = ""
+ binding.autosensdata.text = ""
+ binding.scriptdebugdata.text = ""
+ binding.request.text = ""
+ binding.lastrun.text = ""
}
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java
deleted file mode 100644
index 7d132db4d3..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java
+++ /dev/null
@@ -1,263 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSAMA;
-
-import android.content.Context;
-
-import org.json.JSONException;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.IobTotal;
-import info.nightscout.androidaps.data.MealData;
-import info.nightscout.androidaps.data.Profile;
-import info.nightscout.androidaps.db.TempTarget;
-import info.nightscout.androidaps.interfaces.APSInterface;
-import info.nightscout.androidaps.interfaces.ActivePluginProvider;
-import info.nightscout.androidaps.interfaces.PluginBase;
-import info.nightscout.androidaps.interfaces.PluginDescription;
-import info.nightscout.androidaps.interfaces.PluginType;
-import info.nightscout.androidaps.interfaces.PumpInterface;
-import info.nightscout.androidaps.logging.AAPSLogger;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.plugins.aps.loop.APSResult;
-import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
-import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui;
-import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui;
-import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
-import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
-import info.nightscout.androidaps.interfaces.ProfileFunction;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
-import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
-import info.nightscout.androidaps.utils.DateUtil;
-import info.nightscout.androidaps.utils.FabricPrivacy;
-import info.nightscout.androidaps.utils.HardLimits;
-import info.nightscout.androidaps.utils.Profiler;
-import info.nightscout.androidaps.utils.Round;
-import info.nightscout.androidaps.utils.resources.ResourceHelper;
-
-@Singleton
-public class OpenAPSAMAPlugin extends PluginBase implements APSInterface {
- private final AAPSLogger aapsLogger;
- private final RxBusWrapper rxBus;
- private final ConstraintChecker constraintChecker;
- private final ResourceHelper resourceHelper;
- private final ProfileFunction profileFunction;
- private final Context context;
- private final ActivePluginProvider activePlugin;
- private final TreatmentsPlugin treatmentsPlugin;
- private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
- private final HardLimits hardLimits;
- private final Profiler profiler;
- private final FabricPrivacy fabricPrivacy;
-
- // last values
- DetermineBasalAdapterAMAJS lastDetermineBasalAdapterAMAJS = null;
- long lastAPSRun = 0;
- DetermineBasalResultAMA lastAPSResult = null;
- AutosensResult lastAutosensResult = null;
-
- @Inject
- public OpenAPSAMAPlugin(
- HasAndroidInjector injector,
- AAPSLogger aapsLogger,
- RxBusWrapper rxBus,
- ConstraintChecker constraintChecker,
- ResourceHelper resourceHelper,
- ProfileFunction profileFunction,
- Context context,
- ActivePluginProvider activePlugin,
- TreatmentsPlugin treatmentsPlugin,
- IobCobCalculatorPlugin iobCobCalculatorPlugin,
- HardLimits hardLimits,
- Profiler profiler,
- FabricPrivacy fabricPrivacy
- ) {
- super(new PluginDescription()
- .mainType(PluginType.APS)
- .fragmentClass(OpenAPSAMAFragment.class.getName())
- .pluginIcon(R.drawable.ic_generic_icon)
- .pluginName(R.string.openapsama)
- .shortName(R.string.oaps_shortname)
- .preferencesId(R.xml.pref_openapsama)
- .description(R.string.description_ama),
- aapsLogger, resourceHelper, injector
- );
- this.aapsLogger = aapsLogger;
- this.rxBus = rxBus;
- this.constraintChecker = constraintChecker;
- this.resourceHelper = resourceHelper;
- this.profileFunction = profileFunction;
- this.context = context;
- this.activePlugin = activePlugin;
- this.treatmentsPlugin = treatmentsPlugin;
- this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
- this.hardLimits = hardLimits;
- this.profiler = profiler;
- this.fabricPrivacy = fabricPrivacy;
- }
-
- @Override
- public boolean specialEnableCondition() {
- try {
- PumpInterface pump = activePlugin.getActivePump();
- return pump.getPumpDescription().isTempBasalCapable;
- } catch (Exception ignored) {
- // may fail during initialization
- return true;
- }
- }
-
- @Override
- public boolean specialShowInListCondition() {
- PumpInterface pump = activePlugin.getActivePump();
- return pump.getPumpDescription().isTempBasalCapable;
- }
-
- @Override
- public APSResult getLastAPSResult() {
- return lastAPSResult;
- }
-
- @Override
- public long getLastAPSRun() {
- return lastAPSRun;
- }
-
- @Override
- public void invoke(String initiator, boolean tempBasalFallback) {
- aapsLogger.debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback);
- lastAPSResult = null;
- DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS;
- determineBasalAdapterAMAJS = new DetermineBasalAdapterAMAJS(new ScriptReader(context), getInjector());
-
- GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData();
- Profile profile = profileFunction.getProfile();
- PumpInterface pump = activePlugin.getActivePump();
-
- if (profile == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)));
- aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
- return;
- }
-
- if (!isEnabled(PluginType.APS)) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)));
- aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled));
- return;
- }
-
- if (glucoseStatus == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)));
- aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata));
- return;
- }
-
- double maxBasal = constraintChecker.getMaxBasalAllowed(profile).value();
- double minBg = profile.getTargetLowMgdl();
- double maxBg = profile.getTargetHighMgdl();
- double targetBg = profile.getTargetMgdl();
-
- minBg = Round.roundTo(minBg, 0.1d);
- maxBg = Round.roundTo(maxBg, 0.1d);
-
- long start = System.currentTimeMillis();
- long startPart = System.currentTimeMillis();
- IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile);
- profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart);
-
- startPart = System.currentTimeMillis();
- MealData mealData = iobCobCalculatorPlugin.getMealData();
- profiler.log(LTag.APS, "getMealData()", startPart);
-
- double maxIob = constraintChecker.getMaxIOBAllowed().value();
-
- minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]);
- maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]);
- targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]);
-
- boolean isTempTarget = false;
- TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis());
- if (tempTarget != null) {
- isTempTarget = true;
- minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]);
- maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]);
- targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]);
- }
-
-
- if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal()))
- return;
- if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal()))
- return;
-
- startPart = System.currentTimeMillis();
- if (constraintChecker.isAutosensModeEnabled().value()) {
- AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin");
- if (autosensData == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)));
- return;
- }
- lastAutosensResult = autosensData.autosensResult;
- } else {
- lastAutosensResult = new AutosensResult();
- lastAutosensResult.sensResult = "autosens disabled";
- }
- profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart);
- profiler.log(LTag.APS, "AMA data gathering", start);
-
- start = System.currentTimeMillis();
-
- try {
- determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
- lastAutosensResult.ratio, //autosensDataRatio
- isTempTarget
- );
- } catch (JSONException e) {
- fabricPrivacy.logException(e);
- return;
- }
-
-
- DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke();
- profiler.log(LTag.APS, "AMA calculation", start);
- // Fix bug determine basal
- if (determineBasalResultAMA == null) {
- aapsLogger.error(LTag.APS, "SMB calculation returned null");
- lastDetermineBasalAdapterAMAJS = null;
- lastAPSResult = null;
- lastAPSRun = 0;
- } else {
- if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress())
- determineBasalResultAMA.tempBasalRequested = false;
-
- determineBasalResultAMA.iob = iobArray[0];
-
- long now = System.currentTimeMillis();
-
- try {
- determineBasalResultAMA.json.put("timestamp", DateUtil.toISOString(now));
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Unhandled exception", e);
- }
-
- lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS;
- lastAPSResult = determineBasalResultAMA;
- lastAPSRun = now;
- }
- rxBus.send(new EventOpenAPSUpdateGui());
-
- //deviceStatus.suggested = determineBasalResultAMA.json;
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt
new file mode 100644
index 0000000000..97a6079e4a
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt
@@ -0,0 +1,172 @@
+package info.nightscout.androidaps.plugins.aps.openAPSAMA
+
+import android.content.Context
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.data.Profile
+import info.nightscout.androidaps.interfaces.*
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
+import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui
+import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
+import info.nightscout.androidaps.plugins.bus.RxBusWrapper
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
+import info.nightscout.androidaps.utils.DateUtil
+import info.nightscout.androidaps.utils.FabricPrivacy
+import info.nightscout.androidaps.utils.HardLimits
+import info.nightscout.androidaps.utils.Profiler
+import info.nightscout.androidaps.utils.Round
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import org.json.JSONException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+open class OpenAPSAMAPlugin @Inject constructor(
+ injector: HasAndroidInjector,
+ aapsLogger: AAPSLogger,
+ private val rxBus: RxBusWrapper,
+ private val constraintChecker: ConstraintChecker,
+ resourceHelper: ResourceHelper,
+ private val profileFunction: ProfileFunction,
+ private val context: Context,
+ private val activePlugin: ActivePluginProvider,
+ private val treatmentsPlugin: TreatmentsPlugin,
+ private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
+ private val hardLimits: HardLimits,
+ private val profiler: Profiler,
+ private val fabricPrivacy: FabricPrivacy
+) : PluginBase(PluginDescription()
+ .mainType(PluginType.APS)
+ .fragmentClass(OpenAPSAMAFragment::class.java.name)
+ .pluginIcon(R.drawable.ic_generic_icon)
+ .pluginName(R.string.openapsama)
+ .shortName(R.string.oaps_shortname)
+ .preferencesId(R.xml.pref_openapsama)
+ .description(R.string.description_ama),
+ aapsLogger, resourceHelper, injector
+), APSInterface {
+
+ // last values
+ override var lastAPSRun: Long = 0
+ override var lastAPSResult: DetermineBasalResultAMA? = null
+ var lastDetermineBasalAdapterAMAJS: DetermineBasalAdapterAMAJS? = null
+ var lastAutosensResult: AutosensResult = AutosensResult()
+
+ override fun specialEnableCondition(): Boolean {
+ return try {
+ val pump = activePlugin.activePump
+ pump.pumpDescription.isTempBasalCapable
+ } catch (ignored: Exception) {
+ // may fail during initialization
+ true
+ }
+ }
+
+ override fun specialShowInListCondition(): Boolean {
+ val pump = activePlugin.activePump
+ return pump.pumpDescription.isTempBasalCapable
+ }
+
+ override fun invoke(initiator: String, tempBasalFallback: Boolean) {
+ aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
+ lastAPSResult = null
+ val determineBasalAdapterAMAJS = DetermineBasalAdapterAMAJS(ScriptReader(context), injector)
+ val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
+ val profile = profileFunction.getProfile()
+ val pump = activePlugin.activePump
+ if (profile == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
+ return
+ }
+ if (!isEnabled(PluginType.APS)) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled))
+ return
+ }
+ if (glucoseStatus == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata))
+ return
+ }
+ val inputConstraints = Constraint(0.0) // fake. only for collecting all results
+ val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also {
+ inputConstraints.copyReasons(it)
+ }.value()
+ var start = System.currentTimeMillis()
+ var startPart = System.currentTimeMillis()
+ val iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile)
+ profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
+ startPart = System.currentTimeMillis()
+ val mealData = iobCobCalculatorPlugin.mealData
+ profiler.log(LTag.APS, "getMealData()", startPart)
+ val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint ->
+ inputConstraints.copyReasons(maxIOBAllowedConstraint)
+ }.value()
+ var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
+ var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
+ var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
+ var isTempTarget = false
+ treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
+ isTempTarget = true
+ minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
+ maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
+ targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
+ }
+ if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
+ if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
+ if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
+ if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
+ if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
+ startPart = System.currentTimeMillis()
+ if (constraintChecker.isAutosensModeEnabled().value()) {
+ val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
+ if (autosensData == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
+ return
+ }
+ lastAutosensResult = autosensData.autosensResult
+ } else {
+ lastAutosensResult.sensResult = "autosens disabled"
+ }
+ profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart)
+ profiler.log(LTag.APS, "AMA data gathering", start)
+ start = System.currentTimeMillis()
+ try {
+ determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.activePump.baseBasalRate, iobArray, glucoseStatus, mealData,
+ lastAutosensResult.ratio,
+ isTempTarget
+ )
+ } catch (e: JSONException) {
+ fabricPrivacy.logException(e)
+ return
+ }
+ val determineBasalResultAMA = determineBasalAdapterAMAJS.invoke()
+ profiler.log(LTag.APS, "AMA calculation", start)
+ // Fix bug determine basal
+ if (determineBasalResultAMA == null) {
+ aapsLogger.error(LTag.APS, "SMB calculation returned null")
+ lastDetermineBasalAdapterAMAJS = null
+ lastAPSResult = null
+ lastAPSRun = 0
+ } else {
+ if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultAMA.tempBasalRequested = false
+ determineBasalResultAMA.iob = iobArray[0]
+ val now = System.currentTimeMillis()
+ determineBasalResultAMA.json?.put("timestamp", DateUtil.toISOString(now))
+ determineBasalResultAMA.inputConstraints = inputConstraints
+ lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS
+ lastAPSResult = determineBasalResultAMA
+ lastAPSRun = now
+ }
+ rxBus.send(EventOpenAPSUpdateGui())
+
+ //deviceStatus.suggested = determineBasalResultAMA.json;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java
deleted file mode 100644
index a94581ca94..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java
+++ /dev/null
@@ -1,386 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSSMB;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.Function;
-import org.mozilla.javascript.NativeJSON;
-import org.mozilla.javascript.NativeObject;
-import org.mozilla.javascript.RhinoException;
-import org.mozilla.javascript.Scriptable;
-import org.mozilla.javascript.ScriptableObject;
-import org.mozilla.javascript.Undefined;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.nio.charset.StandardCharsets;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-
-import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.Constants;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.IobTotal;
-import info.nightscout.androidaps.data.MealData;
-import info.nightscout.androidaps.data.Profile;
-import info.nightscout.androidaps.db.TemporaryBasal;
-import info.nightscout.androidaps.interfaces.ActivePluginProvider;
-import info.nightscout.androidaps.interfaces.PumpInterface;
-import info.nightscout.androidaps.logging.AAPSLogger;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback;
-import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
-import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
-import info.nightscout.androidaps.interfaces.ProfileFunction;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
-import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
-import info.nightscout.androidaps.utils.SafeParse;
-import info.nightscout.androidaps.utils.resources.ResourceHelper;
-import info.nightscout.androidaps.utils.sharedPreferences.SP;
-
-
-public class DetermineBasalAdapterSMBJS {
- private final HasAndroidInjector injector;
- @Inject AAPSLogger aapsLogger;
- @Inject ConstraintChecker constraintChecker;
- @Inject SP sp;
- @Inject ResourceHelper resourceHelper;
- @Inject ProfileFunction profileFunction;
- @Inject TreatmentsPlugin treatmentsPlugin;
- @Inject ActivePluginProvider activePluginProvider;
- @Inject OpenHumansUploader openHumansUploader;
-
-
- private final ScriptReader mScriptReader;
- private JSONObject mProfile;
- private JSONObject mGlucoseStatus;
- private JSONArray mIobData;
- private JSONObject mMealData;
- private JSONObject mCurrentTemp;
- private JSONObject mAutosensData = null;
- private boolean mMicrobolusAllowed;
- private boolean mSMBAlwaysAllowed;
- private long mCurrentTime;
- private boolean mIsSaveCgmSource;
-
- private String storedCurrentTemp = null;
- private String storedIobData = null;
-
- private String storedGlucoseStatus = null;
- private String storedProfile = null;
- private String storedMeal_data = null;
- private String storedAutosens_data = null;
- private String storedMicroBolusAllowed = null;
- private String storedSMBAlwaysAllowed = null;
- private String storedCurrentTime = null;
-
- private String scriptDebug = "";
-
- /**
- * Main code
- */
-
- DetermineBasalAdapterSMBJS(ScriptReader scriptReader, HasAndroidInjector injector) {
- mScriptReader = scriptReader;
- this.injector = injector;
- injector.androidInjector().inject(this);
- }
-
-
- @Nullable
- public DetermineBasalResultSMB invoke() {
-
-
- aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<");
- aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString()));
- aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString()));
- aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString()));
- aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString()));
- aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString()));
- if (mAutosensData != null)
- aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = mAutosensData.toString()));
- else
- aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = "undefined"));
- aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined");
- aapsLogger.debug(LTag.APS, "MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed));
- aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: " + (storedSMBAlwaysAllowed = "" + mSMBAlwaysAllowed));
- aapsLogger.debug(LTag.APS, "CurrentTime: " + (storedCurrentTime = "" + mCurrentTime));
- aapsLogger.debug(LTag.APS, "isSaveCgmSource: " + mIsSaveCgmSource);
-
-
- DetermineBasalResultSMB determineBasalResultSMB = null;
-
- Context rhino = Context.enter();
- Scriptable scope = rhino.initStandardObjects();
- // Turn off optimization to make Rhino Android compatible
- rhino.setOptimizationLevel(-1);
-
- try {
-
- //register logger callback for console.log and console.error
- ScriptableObject.defineClass(scope, LoggerCallback.class);
- Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null);
- scope.put("console2", scope, myLogger);
- rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null);
-
- //set module parent
- rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null);
- rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null);
- rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null);
-
- //generate functions "determine_basal" and "setTempBasal"
- rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null);
- rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null);
- Object determineBasalObj = scope.get("determine_basal", scope);
- Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope);
-
- //call determine-basal
- if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) {
- Function determineBasalJS = (Function) determineBasalObj;
-
- //prepare parameters
- Object[] params = new Object[]{
- makeParam(mGlucoseStatus, rhino, scope),
- makeParam(mCurrentTemp, rhino, scope),
- makeParamArray(mIobData, rhino, scope),
- makeParam(mProfile, rhino, scope),
- makeParam(mAutosensData, rhino, scope),
- makeParam(mMealData, rhino, scope),
- setTempBasalFunctionsObj,
- new Boolean(mMicrobolusAllowed),
- makeParam(null, rhino, scope), // reservoir data as undefined
- new Long(mCurrentTime)
- };
-
-
- NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params);
- scriptDebug = LoggerCallback.getScriptDebug();
-
- // Parse the jsResult object to a JSON-String
- String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString();
- aapsLogger.debug(LTag.APS, "Result: " + result);
- try {
- JSONObject resultJson = new JSONObject(result);
- openHumansUploader.enqueueSMBData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, mMicrobolusAllowed, mSMBAlwaysAllowed, resultJson);
- determineBasalResultSMB = new DetermineBasalResultSMB(injector, resultJson);
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Unhandled exception", e);
- }
- } else {
- aapsLogger.error(LTag.APS, "Problem loading JS Functions");
- }
- } catch (IOException e) {
- aapsLogger.error(LTag.APS, "IOException");
- } catch (RhinoException e) {
- aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString());
- } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
- aapsLogger.error(LTag.APS, e.toString());
- } finally {
- Context.exit();
- }
-
- storedGlucoseStatus = mGlucoseStatus.toString();
- storedIobData = mIobData.toString();
- storedCurrentTemp = mCurrentTemp.toString();
- storedProfile = mProfile.toString();
- storedMeal_data = mMealData.toString();
-
- return determineBasalResultSMB;
-
- }
-
- String getGlucoseStatusParam() {
- return storedGlucoseStatus;
- }
-
- String getCurrentTempParam() {
- return storedCurrentTemp;
- }
-
- String getIobDataParam() {
- return storedIobData;
- }
-
- String getProfileParam() {
- return storedProfile;
- }
-
- String getMealDataParam() {
- return storedMeal_data;
- }
-
- String getAutosensDataParam() {
- return storedAutosens_data;
- }
-
- String getMicroBolusAllowedParam() {
- return storedMicroBolusAllowed;
- }
-
- String getScriptDebug() {
- return scriptDebug;
- }
-
- public void setData(Profile profile,
- double maxIob,
- double maxBasal,
- double minBg,
- double maxBg,
- double targetBg,
- double basalrate,
- IobTotal[] iobArray,
- GlucoseStatus glucoseStatus,
- MealData mealData,
- double autosensDataRatio,
- boolean tempTargetSet,
- boolean microBolusAllowed,
- boolean uamAllowed,
- boolean advancedFiltering,
- boolean isSaveCgmSource
- ) throws JSONException {
-
- String units = profile.getUnits();
- PumpInterface pump = activePluginProvider.getActivePump();
- Double pumpbolusstep = pump.getPumpDescription().bolusStep;
- mProfile = new JSONObject();
-
- mProfile.put("max_iob", maxIob);
- //mProfile.put("dia", profile.getDia());
- mProfile.put("type", "current");
- mProfile.put("max_daily_basal", profile.getMaxDailyBasal());
- mProfile.put("max_basal", maxBasal);
- mProfile.put("min_bg", minBg);
- mProfile.put("max_bg", maxBg);
- mProfile.put("target_bg", targetBg);
- mProfile.put("carb_ratio", profile.getIc());
- mProfile.put("sens", profile.getIsfMgdl());
- mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
- mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
-
- //mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity));
- mProfile.put("high_temptarget_raises_sensitivity", false);
- //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity));
- mProfile.put("low_temptarget_lowers_sensitivity", false);
-
-
- mProfile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target,SMBDefaults.sensitivity_raises_target));
- mProfile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target,SMBDefaults.resistance_lowers_target));
- mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments);
- mProfile.put("exercise_mode", SMBDefaults.exercise_mode);
- mProfile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target);
- mProfile.put("maxCOB", SMBDefaults.maxCOB);
- mProfile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour());
- // min_5m_carbimpact is not used within SMB determinebasal
- //if (mealData.usedMinCarbsImpact > 0) {
- // mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
- //} else {
- // mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
- //}
- mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap);
- mProfile.put("enableUAM", uamAllowed);
- mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable);
-
- boolean smbEnabled = sp.getBoolean(R.string.key_use_smb, false);
- mProfile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval));
- mProfile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false));
- mProfile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false));
- mProfile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false));
- mProfile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering);
- mProfile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering);
- mProfile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes));
- mProfile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes));
- //set the min SMB amount to be the amount set by the pump.
- mProfile.put("bolus_increment", pumpbolusstep);
- mProfile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold));
-
- mProfile.put("current_basal", basalrate);
- mProfile.put("temptargetSet", tempTargetSet);
- mProfile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")));
-
- if (profileFunction.getUnits().equals(Constants.MMOL)) {
- mProfile.put("out_units", "mmol/L");
- }
-
-
- long now = System.currentTimeMillis();
- TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now);
-
- mCurrentTemp = new JSONObject();
- mCurrentTemp.put("temp", "absolute");
- mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0);
- mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d);
-
- // as we have non default temps longer than 30 mintues
- TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis());
- if (tempBasal != null) {
- mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration());
- }
-
- mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray);
-
- mGlucoseStatus = new JSONObject();
- mGlucoseStatus.put("glucose", glucoseStatus.glucose);
- mGlucoseStatus.put("noise", glucoseStatus.noise);
-
- if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
- mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta);
- } else {
- mGlucoseStatus.put("delta", glucoseStatus.delta);
- }
- mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta);
- mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta);
- mGlucoseStatus.put("date", glucoseStatus.date);
-
- mMealData = new JSONObject();
- mMealData.put("carbs", mealData.carbs);
- mMealData.put("boluses", mealData.boluses);
- mMealData.put("mealCOB", mealData.mealCOB);
- mMealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation);
- mMealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation);
- mMealData.put("lastBolusTime", mealData.lastBolusTime);
- mMealData.put("lastCarbTime", mealData.lastCarbTime);
-
-
- if (constraintChecker.isAutosensModeEnabled().value()) {
- mAutosensData = new JSONObject();
- mAutosensData.put("ratio", autosensDataRatio);
- } else {
- mAutosensData = new JSONObject();
- mAutosensData.put("ratio", 1.0);
- }
- mMicrobolusAllowed = microBolusAllowed;
- mSMBAlwaysAllowed = advancedFiltering;
-
- mCurrentTime = now;
-
- mIsSaveCgmSource = isSaveCgmSource;
- }
-
- private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) {
-
- if (jsonObject == null) return Undefined.instance;
-
- Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
- return param;
- }
-
- private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) {
- //Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() {
- Object param = NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
- return param;
- }
-
- private String readFile(String filename) throws IOException {
- byte[] bytes = mScriptReader.readFile(filename);
- String string = new String(bytes, StandardCharsets.UTF_8);
- if (string.startsWith("#!/usr/bin/env node")) {
- string = string.substring(20);
- }
- return string;
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt
new file mode 100644
index 0000000000..13a11fa774
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt
@@ -0,0 +1,290 @@
+package info.nightscout.androidaps.plugins.aps.openAPSSMB
+
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.Constants
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.data.IobTotal
+import info.nightscout.androidaps.data.MealData
+import info.nightscout.androidaps.data.Profile
+import info.nightscout.androidaps.interfaces.ActivePluginProvider
+import info.nightscout.androidaps.interfaces.ProfileFunction
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
+import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
+import info.nightscout.androidaps.utils.SafeParse
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.sharedPreferences.SP
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+import org.mozilla.javascript.*
+import org.mozilla.javascript.Function
+import java.io.IOException
+import java.lang.reflect.InvocationTargetException
+import java.nio.charset.StandardCharsets
+import javax.inject.Inject
+
+class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: ScriptReader, private val injector: HasAndroidInjector) {
+
+ @Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var constraintChecker: ConstraintChecker
+ @Inject lateinit var sp: SP
+ @Inject lateinit var resourceHelper: ResourceHelper
+ @Inject lateinit var profileFunction: ProfileFunction
+ @Inject lateinit var treatmentsPlugin: TreatmentsPlugin
+ @Inject lateinit var activePluginProvider: ActivePluginProvider
+ @Inject lateinit var openHumansUploader: OpenHumansUploader
+
+ private var profile = JSONObject()
+ private var mGlucoseStatus = JSONObject()
+ private var iobData: JSONArray? = null
+ private var mealData = JSONObject()
+ private var currentTemp = JSONObject()
+ private var autosensData = JSONObject()
+ private var microBolusAllowed = false
+ private var smbAlwaysAllowed = false
+ private var currentTime: Long = 0
+ private var saveCgmSource = false
+ var currentTempParam: String? = null
+ private set
+ var iobDataParam: String? = null
+ private set
+ var glucoseStatusParam: String? = null
+ private set
+ var profileParam: String? = null
+ private set
+ var mealDataParam: String? = null
+ private set
+ var scriptDebug = ""
+ private set
+
+ @Suppress("SpellCheckingInspection")
+ operator fun invoke(): DetermineBasalResultSMB? {
+ aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<")
+ aapsLogger.debug(LTag.APS, "Glucose status: " + mGlucoseStatus.toString().also { glucoseStatusParam = it })
+ aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it })
+ aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it })
+ aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it })
+ aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it })
+ aapsLogger.debug(LTag.APS, "Autosens data: $autosensData")
+ aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined")
+ aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed")
+ aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed")
+ aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime")
+ aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource")
+ var determineBasalResultSMB: DetermineBasalResultSMB? = null
+ val rhino = Context.enter()
+ val scope: Scriptable = rhino.initStandardObjects()
+ // Turn off optimization to make Rhino Android compatible
+ rhino.optimizationLevel = -1
+ try {
+
+ //register logger callback for console.log and console.error
+ ScriptableObject.defineClass(scope, LoggerCallback::class.java)
+ val myLogger = rhino.newObject(scope, "LoggerCallback", null)
+ scope.put("console2", scope, myLogger)
+ rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null)
+
+ //set module parent
+ rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null)
+ rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null)
+ rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null)
+
+ //generate functions "determine_basal" and "setTempBasal"
+ rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null)
+ rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null)
+ val determineBasalObj = scope["determine_basal", scope]
+ val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope]
+
+ //call determine-basal
+ if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) {
+
+ //prepare parameters
+ val params = arrayOf(
+ makeParam(mGlucoseStatus, rhino, scope),
+ makeParam(currentTemp, rhino, scope),
+ makeParamArray(iobData, rhino, scope),
+ makeParam(profile, rhino, scope),
+ makeParam(autosensData, rhino, scope),
+ makeParam(mealData, rhino, scope),
+ setTempBasalFunctionsObj,
+ java.lang.Boolean.valueOf(microBolusAllowed),
+ makeParam(null, rhino, scope), // reservoir data as undefined
+ java.lang.Long.valueOf(currentTime),
+ java.lang.Boolean.valueOf(saveCgmSource)
+ )
+ val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
+ scriptDebug = LoggerCallback.scriptDebug
+
+ // Parse the jsResult object to a JSON-String
+ val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString()
+ aapsLogger.debug(LTag.APS, "Result: $result")
+ try {
+ val resultJson = JSONObject(result)
+ openHumansUploader.enqueueSMBData(profile, mGlucoseStatus, iobData, mealData, currentTemp, autosensData, microBolusAllowed, smbAlwaysAllowed, resultJson)
+ determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson)
+ } catch (e: JSONException) {
+ aapsLogger.error(LTag.APS, "Unhandled exception", e)
+ }
+ } else {
+ aapsLogger.error(LTag.APS, "Problem loading JS Functions")
+ }
+ } catch (e: IOException) {
+ aapsLogger.error(LTag.APS, "IOException")
+ } catch (e: RhinoException) {
+ aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString())
+ } catch (e: IllegalAccessException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } catch (e: InstantiationException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } catch (e: InvocationTargetException) {
+ aapsLogger.error(LTag.APS, e.toString())
+ } finally {
+ Context.exit()
+ }
+ glucoseStatusParam = mGlucoseStatus.toString()
+ iobDataParam = iobData.toString()
+ currentTempParam = currentTemp.toString()
+ profileParam = profile.toString()
+ mealDataParam = mealData.toString()
+ return determineBasalResultSMB
+ }
+
+ @Suppress("SpellCheckingInspection") fun setData(profile: Profile,
+ maxIob: Double,
+ maxBasal: Double,
+ minBg: Double,
+ maxBg: Double,
+ targetBg: Double,
+ basalRate: Double,
+ iobArray: Array,
+ glucoseStatus: GlucoseStatus,
+ mealData: MealData,
+ autosensDataRatio: Double,
+ tempTargetSet: Boolean,
+ microBolusAllowed: Boolean,
+ uamAllowed: Boolean,
+ advancedFiltering: Boolean,
+ isSaveCgmSource: Boolean
+ ) {
+ val pump = activePluginProvider.activePump
+ val pumpBolusStep = pump.pumpDescription.bolusStep
+ this.profile.put("max_iob", maxIob)
+ //mProfile.put("dia", profile.getDia());
+ this.profile.put("type", "current")
+ this.profile.put("max_daily_basal", profile.maxDailyBasal)
+ this.profile.put("max_basal", maxBasal)
+ this.profile.put("min_bg", minBg)
+ this.profile.put("max_bg", maxBg)
+ this.profile.put("target_bg", targetBg)
+ this.profile.put("carb_ratio", profile.ic)
+ this.profile.put("sens", profile.isfMgdl)
+ this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
+ this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
+
+ //mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity));
+ this.profile.put("high_temptarget_raises_sensitivity", false)
+ //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity));
+ this.profile.put("low_temptarget_lowers_sensitivity", false)
+ this.profile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target))
+ this.profile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target))
+ this.profile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments)
+ this.profile.put("exercise_mode", SMBDefaults.exercise_mode)
+ this.profile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target)
+ this.profile.put("maxCOB", SMBDefaults.maxCOB)
+ this.profile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour())
+ // min_5m_carbimpact is not used within SMB determinebasal
+ //if (mealData.usedMinCarbsImpact > 0) {
+ // mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
+ //} else {
+ // mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
+ //}
+ this.profile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap)
+ this.profile.put("enableUAM", uamAllowed)
+ this.profile.put("A52_risk_enable", SMBDefaults.A52_risk_enable)
+ val smbEnabled = sp.getBoolean(R.string.key_use_smb, false)
+ this.profile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval))
+ this.profile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false))
+ this.profile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false))
+ this.profile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false))
+ this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering)
+ this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering)
+ this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes))
+ this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes))
+ //set the min SMB amount to be the amount set by the pump.
+ this.profile.put("bolus_increment", pumpBolusStep)
+ this.profile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold))
+ this.profile.put("current_basal", basalRate)
+ this.profile.put("temptargetSet", tempTargetSet)
+ this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")))
+ if (profileFunction.getUnits() == Constants.MMOL) {
+ this.profile.put("out_units", "mmol/L")
+ }
+ val now = System.currentTimeMillis()
+ val tb = treatmentsPlugin.getTempBasalFromHistory(now)
+ currentTemp.put("temp", "absolute")
+ currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
+ currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
+
+ // as we have non default temps longer than 30 mintues
+ val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
+ if (tempBasal != null) {
+ currentTemp.put("minutesrunning", tempBasal.realDuration)
+ }
+ iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
+ mGlucoseStatus.put("glucose", glucoseStatus.glucose)
+ mGlucoseStatus.put("noise", glucoseStatus.noise)
+ if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
+ mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta)
+ } else {
+ mGlucoseStatus.put("delta", glucoseStatus.delta)
+ }
+ mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta)
+ mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta)
+ mGlucoseStatus.put("date", glucoseStatus.date)
+ this.mealData.put("carbs", mealData.carbs)
+ this.mealData.put("boluses", mealData.boluses)
+ this.mealData.put("mealCOB", mealData.mealCOB)
+ this.mealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation)
+ this.mealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation)
+ this.mealData.put("lastBolusTime", mealData.lastBolusTime)
+ this.mealData.put("lastCarbTime", mealData.lastCarbTime)
+ if (constraintChecker.isAutosensModeEnabled().value()) {
+ autosensData.put("ratio", autosensDataRatio)
+ } else {
+ autosensData.put("ratio", 1.0)
+ }
+ this.microBolusAllowed = microBolusAllowed
+ smbAlwaysAllowed = advancedFiltering
+ currentTime = now
+ saveCgmSource = isSaveCgmSource
+ }
+
+ private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {
+ return if (jsonObject == null) Undefined.instance
+ else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] }
+ }
+
+ private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
+ return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] }
+ }
+
+ @Throws(IOException::class) private fun readFile(filename: String): String {
+ val bytes = scriptReader.readFile(filename)
+ var string = String(bytes, StandardCharsets.UTF_8)
+ if (string.startsWith("#!/usr/bin/env node")) {
+ string = string.substring(20)
+ }
+ return string
+ }
+
+ init {
+ injector.androidInjector().inject(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java
deleted file mode 100644
index 640d1b1585..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSSMB;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import javax.inject.Inject;
-
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.plugins.aps.loop.APSResult;
-import info.nightscout.androidaps.utils.DateUtil;
-import info.nightscout.androidaps.utils.sharedPreferences.SP;
-
-public class DetermineBasalResultSMB extends APSResult {
- @Inject SP sp;
-
- private double eventualBG;
- private double snoozeBG;
-
- private DetermineBasalResultSMB(HasAndroidInjector injector) {
- super(injector);
- hasPredictions = true;
- }
-
- DetermineBasalResultSMB(HasAndroidInjector injector, JSONObject result) {
- this(injector);
- date = DateUtil.now();
- json = result;
- try {
- if (result.has("error")) {
- reason = result.getString("error");
- return;
- }
-
- reason = result.getString("reason");
- if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG");
- if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG");
- //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq");
-
- if (result.has("carbsReq")) carbsReq = result.getInt("carbsReq");
- if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin");
-
-
- if (result.has("rate") && result.has("duration")) {
- tempBasalRequested = true;
- rate = result.getDouble("rate");
- if (rate < 0d) rate = 0d;
- duration = result.getInt("duration");
- } else {
- rate = -1;
- duration = -1;
- }
-
- if (result.has("units")) {
- bolusRequested = true;
- smb = result.getDouble("units");
- } else {
- smb = 0d;
- }
- if (result.has("targetBG")) {
- targetBG = result.getDouble("targetBG");
- }
-
- if (result.has("deliverAt")) {
- String date = result.getString("deliverAt");
- try {
- deliverAt = DateUtil.fromISODateString(date).getTime();
- } catch (Exception e) {
- aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: " + date, e);
- }
- }
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e);
- }
- }
-
- @Override
- public DetermineBasalResultSMB newAndClone(HasAndroidInjector injector) {
- DetermineBasalResultSMB newResult = new DetermineBasalResultSMB(injector);
- doClone(newResult);
-
- newResult.eventualBG = eventualBG;
- newResult.snoozeBG = snoozeBG;
- return newResult;
- }
-
- @Override
- public JSONObject json() {
- try {
- return new JSONObject(this.json.toString());
- } catch (JSONException e) {
- aapsLogger.error(LTag.APS, "Error converting determine-basal result to JSON", e);
- }
- return null;
- }
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt
new file mode 100644
index 0000000000..46b68bbd0a
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt
@@ -0,0 +1,80 @@
+package info.nightscout.androidaps.plugins.aps.openAPSSMB
+
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.loop.APSResult
+import info.nightscout.androidaps.utils.DateUtil
+import org.json.JSONException
+import org.json.JSONObject
+
+class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector) : APSResult(injector) {
+
+ private var eventualBG = 0.0
+ private var snoozeBG = 0.0
+
+ internal constructor(injector: HasAndroidInjector, result: JSONObject) : this(injector) {
+ date = DateUtil.now()
+ json = result
+ try {
+ if (result.has("error")) {
+ reason = result.getString("error")
+ return
+ }
+ reason = result.getString("reason")
+ if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG")
+ if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG")
+ //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq");
+ if (result.has("carbsReq")) carbsReq = result.getInt("carbsReq")
+ if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin")
+ if (result.has("rate") && result.has("duration")) {
+ tempBasalRequested = true
+ rate = result.getDouble("rate")
+ if (rate < 0.0) rate = 0.0
+ duration = result.getInt("duration")
+ } else {
+ rate = (-1).toDouble()
+ duration = -1
+ }
+ if (result.has("units")) {
+ bolusRequested = true
+ smb = result.getDouble("units")
+ } else {
+ smb = 0.0
+ }
+ if (result.has("targetBG")) {
+ targetBG = result.getDouble("targetBG")
+ }
+ if (result.has("deliverAt")) {
+ val date = result.getString("deliverAt")
+ try {
+ deliverAt = DateUtil.fromISODateString(date).time
+ } catch (e: Exception) {
+ aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e)
+ }
+ }
+ } catch (e: JSONException) {
+ aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e)
+ }
+ }
+
+ override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultSMB {
+ val newResult = DetermineBasalResultSMB(injector)
+ doClone(newResult)
+ newResult.eventualBG = eventualBG
+ newResult.snoozeBG = snoozeBG
+ return newResult
+ }
+
+ override fun json(): JSONObject? {
+ try {
+ return JSONObject(json.toString())
+ } catch (e: JSONException) {
+ aapsLogger.error(LTag.APS, "Error converting determine-basal result to JSON", e)
+ }
+ return null
+ }
+
+ init {
+ hasPredictions = true
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt
index c16a432370..be4e1015fa 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
+import info.nightscout.androidaps.databinding.OpenapsamaFragmentBinding
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
@@ -16,34 +17,42 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
-import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
-import io.reactivex.android.schedulers.AndroidSchedulers
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
-import kotlinx.android.synthetic.main.openapsama_fragment.*
+import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
class OpenAPSSMBFragment : DaggerFragment() {
+
private var disposable: CompositeDisposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin
@Inject lateinit var dateUtil: DateUtil
+ private var _binding: OpenapsamaFragmentBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.openapsama_fragment, container, false)
+ savedInstanceState: Bundle?): View {
+ _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- openapsma_run.setOnClickListener {
+ binding.run.setOnClickListener {
openAPSSMBPlugin.invoke("OpenAPSSMB button", false)
}
}
@@ -53,16 +62,16 @@ class OpenAPSSMBFragment : DaggerFragment() {
super.onResume()
disposable += rxBus
.toObservable(EventOpenAPSUpdateGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
updateGUI()
- }, { fabricPrivacy.logException(it) })
+ }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOpenAPSUpdateResultGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
updateResultGUI(it.text)
- }, { fabricPrivacy.logException(it) })
+ }, fabricPrivacy::logException)
updateGUI()
}
@@ -73,52 +82,58 @@ class OpenAPSSMBFragment : DaggerFragment() {
disposable.clear()
}
+ @Synchronized
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
@Synchronized
fun updateGUI() {
- if (openapsma_result == null) return
+ if (_binding == null) return
openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult ->
- openapsma_result.text = JSONFormatter.format(lastAPSResult.json)
- openapsma_request.text = lastAPSResult.toSpanned()
+ binding.result.text = JSONFormatter.format(lastAPSResult.json)
+ binding.request.text = lastAPSResult.toSpanned()
}
openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS ->
- openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam)
- openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam)
+ binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam)
+ binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam)
try {
val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam)
- openapsma_iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
+ binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0)))
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
@SuppressLint("SetTextI18n")
- openapsma_iobdata.text = "JSONException see log for details"
+ binding.iobdata.text = "JSONException see log for details"
}
- openapsma_profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam)
- openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam)
- openapsma_scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug
+ binding.profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam)
+ binding.mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam)
+ binding.scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug
openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let {
- openapsma_constraints.text = it.getReasons(aapsLogger)
+ binding.constraints.text = it.getReasons(aapsLogger)
}
}
if (openAPSSMBPlugin.lastAPSRun != 0L) {
- openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
+ binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
}
- openAPSSMBPlugin.lastAutosensResult?.let {
- openapsma_autosensdata.text = JSONFormatter.format(it.json())
+ openAPSSMBPlugin.lastAutosensResult.let {
+ binding.autosensdata.text = JSONFormatter.format(it.json())
}
}
@Synchronized
private fun updateResultGUI(text: String) {
- if (openapsma_result == null) return
- openapsma_result.text = text
- openapsma_glucosestatus.text = ""
- openapsma_currenttemp.text = ""
- openapsma_iobdata.text = ""
- openapsma_profile.text = ""
- openapsma_mealdata.text = ""
- openapsma_autosensdata.text = ""
- openapsma_scriptdebugdata.text = ""
- openapsma_request.text = ""
- openapsma_lastrun.text = ""
+ if (_binding == null) return
+ binding.result.text = text
+ binding.glucosestatus.text = ""
+ binding.currenttemp.text = ""
+ binding.iobdata.text = ""
+ binding.profile.text = ""
+ binding.mealdata.text = ""
+ binding.autosensdata.text = ""
+ binding.scriptdebugdata.text = ""
+ binding.request.text = ""
+ binding.lastrun.text = ""
}
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java
deleted file mode 100644
index 4a6ec60b7d..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java
+++ /dev/null
@@ -1,323 +0,0 @@
-package info.nightscout.androidaps.plugins.aps.openAPSSMB;
-
-import android.content.Context;
-
-import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.SwitchPreference;
-
-import org.jetbrains.annotations.NotNull;
-import org.json.JSONException;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.IobTotal;
-import info.nightscout.androidaps.data.MealData;
-import info.nightscout.androidaps.data.Profile;
-import info.nightscout.androidaps.db.TempTarget;
-import info.nightscout.androidaps.interfaces.APSInterface;
-import info.nightscout.androidaps.interfaces.ActivePluginProvider;
-import info.nightscout.androidaps.interfaces.Constraint;
-import info.nightscout.androidaps.interfaces.ConstraintsInterface;
-import info.nightscout.androidaps.interfaces.PluginBase;
-import info.nightscout.androidaps.interfaces.PluginDescription;
-import info.nightscout.androidaps.interfaces.PluginType;
-import info.nightscout.androidaps.interfaces.PumpInterface;
-import info.nightscout.androidaps.logging.AAPSLogger;
-import info.nightscout.androidaps.logging.LTag;
-import info.nightscout.androidaps.plugins.aps.loop.APSResult;
-import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
-import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui;
-import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui;
-import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
-import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
-import info.nightscout.androidaps.interfaces.ProfileFunction;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
-import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
-import info.nightscout.androidaps.utils.DateUtil;
-import info.nightscout.androidaps.utils.FabricPrivacy;
-import info.nightscout.androidaps.utils.HardLimits;
-import info.nightscout.androidaps.utils.Profiler;
-import info.nightscout.androidaps.utils.Round;
-import info.nightscout.androidaps.utils.resources.ResourceHelper;
-import info.nightscout.androidaps.utils.sharedPreferences.SP;
-
-@Singleton
-public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, ConstraintsInterface {
- private final ConstraintChecker constraintChecker;
- private final ResourceHelper resourceHelper;
- private final ProfileFunction profileFunction;
- private final Context context;
- private final RxBusWrapper rxBus;
- private final ActivePluginProvider activePlugin;
- private final TreatmentsPlugin treatmentsPlugin;
- private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
- private final HardLimits hardLimits;
- private final Profiler profiler;
- private final FabricPrivacy fabricPrivacy;
- private final SP sp;
-
- // last values
- DetermineBasalAdapterSMBJS lastDetermineBasalAdapterSMBJS = null;
- long lastAPSRun = 0;
- DetermineBasalResultSMB lastAPSResult = null;
- AutosensResult lastAutosensResult = null;
-
- @Inject
- public OpenAPSSMBPlugin(
- HasAndroidInjector injector,
- AAPSLogger aapsLogger,
- RxBusWrapper rxBus,
- ConstraintChecker constraintChecker,
- ResourceHelper resourceHelper,
- ProfileFunction profileFunction,
- Context context,
- ActivePluginProvider activePlugin,
- TreatmentsPlugin treatmentsPlugin,
- IobCobCalculatorPlugin iobCobCalculatorPlugin,
- HardLimits hardLimits,
- Profiler profiler,
- FabricPrivacy fabricPrivacy,
- SP sp
- ) {
- super(new PluginDescription()
- .mainType(PluginType.APS)
- .fragmentClass(OpenAPSSMBFragment.class.getName())
- .pluginIcon(R.drawable.ic_generic_icon)
- .pluginName(R.string.openapssmb)
- .shortName(R.string.smb_shortname)
- .preferencesId(R.xml.pref_openapssmb)
- .description(R.string.description_smb)
- .setDefault(),
- aapsLogger, resourceHelper, injector
- );
- this.constraintChecker = constraintChecker;
- this.resourceHelper = resourceHelper;
- this.profileFunction = profileFunction;
- this.rxBus = rxBus;
- this.context = context;
- this.activePlugin = activePlugin;
- this.treatmentsPlugin = treatmentsPlugin;
- this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
- this.hardLimits = hardLimits;
- this.profiler = profiler;
- this.fabricPrivacy = fabricPrivacy;
- this.sp = sp;
- }
-
- @Override
- public boolean specialEnableCondition() {
- try {
- PumpInterface pump = activePlugin.getActivePump();
- return pump.getPumpDescription().isTempBasalCapable;
- } catch (Exception ignored) {
- // may fail during initialization
- return true;
- }
- }
-
- @Override
- public boolean specialShowInListCondition() {
- PumpInterface pump = activePlugin.getActivePump();
- return pump.getPumpDescription().isTempBasalCapable;
- }
-
- @Override
- public void preprocessPreferences(@NotNull PreferenceFragmentCompat preferenceFragment) {
- super.preprocessPreferences(preferenceFragment);
- boolean smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false);
-
- SwitchPreference withCOB = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_COB));
- if (withCOB != null) {
- withCOB.setVisible(!smbAlwaysEnabled);
- }
- SwitchPreference withTempTarget = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_temptarget));
- if (withTempTarget != null) {
- withTempTarget.setVisible(!smbAlwaysEnabled);
- }
- SwitchPreference afterCarbs = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_after_carbs));
- if (afterCarbs != null) {
- afterCarbs.setVisible(!smbAlwaysEnabled);
- }
- }
-
- @Override
- public APSResult getLastAPSResult() {
- return lastAPSResult;
- }
-
- @Override
- public long getLastAPSRun() {
- return lastAPSRun;
- }
-
- @Override
- public void invoke(String initiator, boolean tempBasalFallback) {
- getAapsLogger().debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback);
- lastAPSResult = null;
- DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS;
- determineBasalAdapterSMBJS = new DetermineBasalAdapterSMBJS(new ScriptReader(context), getInjector());
-
- GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData();
- Profile profile = profileFunction.getProfile();
- PumpInterface pump = activePlugin.getActivePump();
-
- if (profile == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)));
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
- return;
- }
-
- if (!isEnabled(PluginType.APS)) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)));
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled));
- return;
- }
-
- if (glucoseStatus == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)));
- getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata));
- return;
- }
-
- Constraint inputConstraints = new Constraint<>(0d); // fake. only for collecting all results
-
- Constraint maxBasalConstraint = constraintChecker.getMaxBasalAllowed(profile);
- inputConstraints.copyReasons(maxBasalConstraint);
- double maxBasal = maxBasalConstraint.value();
- double minBg = profile.getTargetLowMgdl();
- double maxBg = profile.getTargetHighMgdl();
- double targetBg = profile.getTargetMgdl();
-
- minBg = Round.roundTo(minBg, 0.1d);
- maxBg = Round.roundTo(maxBg, 0.1d);
-
- long start = System.currentTimeMillis();
- long startPart = System.currentTimeMillis();
-
- MealData mealData = iobCobCalculatorPlugin.getMealData();
- profiler.log(LTag.APS, "getMealData()", startPart);
-
- Constraint maxIOBAllowedConstraint = constraintChecker.getMaxIOBAllowed();
- inputConstraints.copyReasons(maxIOBAllowedConstraint);
- double maxIob = maxIOBAllowedConstraint.value();
-
- minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]);
- maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]);
- targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]);
-
- boolean isTempTarget = false;
- TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis());
- if (tempTarget != null) {
- isTempTarget = true;
- minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]);
- maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]);
- targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]);
- }
-
-
- if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF()))
- return;
- if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal()))
- return;
- if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal()))
- return;
-
- startPart = System.currentTimeMillis();
- if (constraintChecker.isAutosensModeEnabled().value()) {
- AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin");
- if (autosensData == null) {
- rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)));
- return;
- }
- lastAutosensResult = autosensData.autosensResult;
- } else {
- lastAutosensResult = new AutosensResult();
- lastAutosensResult.sensResult = "autosens disabled";
- }
-
- IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget);
- profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart);
-
- startPart = System.currentTimeMillis();
- Constraint smbAllowed = new Constraint<>(!tempBasalFallback);
- constraintChecker.isSMBModeEnabled(smbAllowed);
- inputConstraints.copyReasons(smbAllowed);
-
- Constraint advancedFiltering = new Constraint<>(!tempBasalFallback);
- constraintChecker.isAdvancedFilteringEnabled(advancedFiltering);
- inputConstraints.copyReasons(advancedFiltering);
-
- Constraint uam = new Constraint<>(true);
- constraintChecker.isUAMEnabled(uam);
- inputConstraints.copyReasons(uam);
-
- profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart);
- profiler.log(LTag.APS, "SMB data gathering", start);
-
- start = System.currentTimeMillis();
- try {
- determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
- lastAutosensResult.ratio, //autosensDataRatio
- isTempTarget,
- smbAllowed.value(),
- uam.value(),
- advancedFiltering.value(),
- activePlugin.getActiveBgSource().getClass().getSimpleName().equals("DexcomPlugin")
- );
- } catch (JSONException e) {
- fabricPrivacy.logException(e);
- return;
- }
-
- long now = System.currentTimeMillis();
-
- DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke();
- profiler.log(LTag.APS, "SMB calculation", start);
- if (determineBasalResultSMB == null) {
- getAapsLogger().error(LTag.APS, "SMB calculation returned null");
- lastDetermineBasalAdapterSMBJS = null;
- lastAPSResult = null;
- lastAPSRun = 0;
- } else {
- // TODO still needed with oref1?
- // Fix bug determine basal
- if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress())
- determineBasalResultSMB.tempBasalRequested = false;
-
- determineBasalResultSMB.iob = iobArray[0];
-
- try {
- determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now));
- } catch (JSONException e) {
- getAapsLogger().error(LTag.APS, "Unhandled exception", e);
- }
-
- determineBasalResultSMB.inputConstraints = inputConstraints;
-
- lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS;
- lastAPSResult = determineBasalResultSMB;
- lastAPSRun = now;
- }
- rxBus.send(new EventOpenAPSUpdateGui());
-
- //deviceStatus.suggested = determineBasalResultAMA.json;
- }
-
- @NotNull
- @Override
- public Constraint isSuperBolusEnabled(Constraint value) {
- value.set(getAapsLogger(), false);
- return value;
- }
-
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt
new file mode 100644
index 0000000000..f1245810f0
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt
@@ -0,0 +1,202 @@
+package info.nightscout.androidaps.plugins.aps.openAPSSMB
+
+import android.content.Context
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.SwitchPreference
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.data.Profile
+import info.nightscout.androidaps.interfaces.*
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
+import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui
+import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
+import info.nightscout.androidaps.plugins.bus.RxBusWrapper
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
+import info.nightscout.androidaps.utils.DateUtil
+import info.nightscout.androidaps.utils.HardLimits
+import info.nightscout.androidaps.utils.Profiler
+import info.nightscout.androidaps.utils.Round
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.sharedPreferences.SP
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+open class OpenAPSSMBPlugin @Inject constructor(
+ injector: HasAndroidInjector,
+ aapsLogger: AAPSLogger,
+ private val rxBus: RxBusWrapper,
+ private val constraintChecker: ConstraintChecker,
+ resourceHelper: ResourceHelper,
+ private val profileFunction: ProfileFunction,
+ private val context: Context,
+ private val activePlugin: ActivePluginProvider,
+ private val treatmentsPlugin: TreatmentsPlugin,
+ private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
+ private val hardLimits: HardLimits,
+ private val profiler: Profiler,
+ private val sp: SP
+) : PluginBase(PluginDescription()
+ .mainType(PluginType.APS)
+ .fragmentClass(OpenAPSSMBFragment::class.java.name)
+ .pluginIcon(R.drawable.ic_generic_icon)
+ .pluginName(R.string.openapssmb)
+ .shortName(R.string.smb_shortname)
+ .preferencesId(R.xml.pref_openapssmb)
+ .description(R.string.description_smb)
+ .setDefault(),
+ aapsLogger, resourceHelper, injector
+), APSInterface, ConstraintsInterface {
+
+ // last values
+ override var lastAPSRun: Long = 0
+ override var lastAPSResult: DetermineBasalResultSMB? = null
+ var lastDetermineBasalAdapterSMBJS: DetermineBasalAdapterSMBJS? = null
+ var lastAutosensResult = AutosensResult()
+
+ override fun specialEnableCondition(): Boolean {
+ return try {
+ activePlugin.activePump.pumpDescription.isTempBasalCapable
+ } catch (ignored: Exception) {
+ // may fail during initialization
+ true
+ }
+ }
+
+ override fun specialShowInListCondition(): Boolean {
+ val pump = activePlugin.activePump
+ return pump.pumpDescription.isTempBasalCapable
+ }
+
+ override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
+ super.preprocessPreferences(preferenceFragment)
+ val smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false)
+ preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_COB))?.isVisible = !smbAlwaysEnabled
+ preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_temptarget))?.isVisible = !smbAlwaysEnabled
+ preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_after_carbs))?.isVisible = !smbAlwaysEnabled
+ }
+
+ override fun invoke(initiator: String, tempBasalFallback: Boolean) {
+ aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
+ lastAPSResult = null
+ val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
+ val profile = profileFunction.getProfile()
+ val pump = activePlugin.activePump
+ if (profile == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
+ return
+ }
+ if (!isEnabled(PluginType.APS)) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled))
+ return
+ }
+ if (glucoseStatus == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)))
+ aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata))
+ return
+ }
+
+ val inputConstraints = Constraint(0.0) // fake. only for collecting all results
+ val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also {
+ inputConstraints.copyReasons(it)
+ }.value()
+ var start = System.currentTimeMillis()
+ var startPart = System.currentTimeMillis()
+ profiler.log(LTag.APS, "getMealData()", startPart)
+ val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint ->
+ inputConstraints.copyReasons(maxIOBAllowedConstraint)
+ }.value()
+
+ var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
+ var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
+ var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
+ var isTempTarget = false
+ treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
+ isTempTarget = true
+ minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
+ maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
+ targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
+ }
+ if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
+ if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
+ if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
+ if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
+ if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
+ startPart = System.currentTimeMillis()
+ if (constraintChecker.isAutosensModeEnabled().value()) {
+ val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
+ if (autosensData == null) {
+ rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
+ return
+ }
+ lastAutosensResult = autosensData.autosensResult
+ } else {
+ lastAutosensResult.sensResult = "autosens disabled"
+ }
+ val iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
+ profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
+ startPart = System.currentTimeMillis()
+ val smbAllowed = Constraint(!tempBasalFallback).also {
+ constraintChecker.isSMBModeEnabled(it)
+ inputConstraints.copyReasons(it)
+ }
+ val advancedFiltering = Constraint(!tempBasalFallback).also {
+ constraintChecker.isAdvancedFilteringEnabled(it)
+ inputConstraints.copyReasons(it)
+ }
+ val uam = Constraint(true).also {
+ constraintChecker.isUAMEnabled(it)
+ inputConstraints.copyReasons(it)
+ }
+ profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart)
+ profiler.log(LTag.APS, "SMB data gathering", start)
+ start = System.currentTimeMillis()
+
+ DetermineBasalAdapterSMBJS(ScriptReader(context), injector).also { determineBasalAdapterSMBJS ->
+ determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg,
+ activePlugin.activePump.baseBasalRate,
+ iobArray,
+ glucoseStatus,
+ iobCobCalculatorPlugin.mealData,
+ lastAutosensResult.ratio,
+ isTempTarget,
+ smbAllowed.value(),
+ uam.value(),
+ advancedFiltering.value(),
+ activePlugin.activeBgSource.javaClass.simpleName == "DexcomPlugin")
+ val now = System.currentTimeMillis()
+ val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke()
+ profiler.log(LTag.APS, "SMB calculation", start)
+ if (determineBasalResultSMB == null) {
+ aapsLogger.error(LTag.APS, "SMB calculation returned null")
+ lastDetermineBasalAdapterSMBJS = null
+ lastAPSResult = null
+ lastAPSRun = 0
+ } else {
+ // TODO still needed with oref1?
+ // Fix bug determine basal
+ if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultSMB.tempBasalRequested = false
+ determineBasalResultSMB.iob = iobArray[0]
+ determineBasalResultSMB.json?.put("timestamp", DateUtil.toISOString(now))
+ determineBasalResultSMB.inputConstraints = inputConstraints
+ lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS
+ lastAPSResult = determineBasalResultSMB
+ lastAPSRun = now
+ }
+ }
+ rxBus.send(EventOpenAPSUpdateGui())
+ }
+
+ override fun isSuperBolusEnabled(value: Constraint): Constraint {
+ value[aapsLogger] = false
+ return value
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt
index 09bbf3c04d..8a19808833 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt
@@ -12,22 +12,24 @@ import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.PreferencesActivity
+import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy
-import info.nightscout.androidaps.utils.extensions.plusAssign
+import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
-import io.reactivex.android.schedulers.AndroidSchedulers
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
-import kotlinx.android.synthetic.main.configbuilder_fragment.*
import java.util.*
import javax.inject.Inject
class ConfigBuilderFragment : DaggerFragment() {
+
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@@ -39,25 +41,32 @@ class ConfigBuilderFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable()
private val pluginViewHolders = ArrayList()
+ private var _binding: ConfigbuilderFragmentBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.configbuilder_fragment, container, false)
+ savedInstanceState: Bundle?): View {
+ _binding = ConfigbuilderFragmentBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES))
- configbuilder_main_layout.visibility = View.GONE
+ binding.mainLayout.visibility = View.GONE
else
- unlock.visibility = View.GONE
+ binding.unlock.visibility = View.GONE
- unlock.setOnClickListener {
+ binding.unlock.setOnClickListener {
activity?.let { activity ->
- protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable {
+ protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, {
activity.runOnUiThread {
- configbuilder_main_layout.visibility = View.VISIBLE
- unlock.visibility = View.GONE
+ binding.mainLayout.visibility = View.VISIBLE
+ binding.unlock.visibility = View.GONE
}
})
}
@@ -69,10 +78,10 @@ class ConfigBuilderFragment : DaggerFragment() {
super.onResume()
disposable += rxBus
.toObservable(EventConfigBuilderUpdateGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update()
- }, { fabricPrivacy.logException(it) })
+ }, fabricPrivacy::logException)
updateGUI()
}
@@ -82,9 +91,15 @@ class ConfigBuilderFragment : DaggerFragment() {
disposable.clear()
}
+ @Synchronized
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
@Synchronized
private fun updateGUI() {
- configbuilder_categories.removeAllViews()
+ binding.categories.removeAllViews()
if (!config.NSCLIENT) {
createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE))
}
@@ -115,7 +130,7 @@ class ConfigBuilderFragment : DaggerFragment() {
pluginContainer.addView(pluginViewHolder.baseView)
pluginViewHolders.add(pluginViewHolder)
}
- configbuilder_categories.addView(parent)
+ binding.categories.addView(parent)
}
inner class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment,
@@ -157,7 +172,7 @@ class ConfigBuilderFragment : DaggerFragment() {
pluginPreferences.setOnClickListener {
fragment.activity?.let { activity ->
- protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable {
+ protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, {
val i = Intent(fragment.context, PreferencesActivity::class.java)
i.putExtra("id", plugin.preferencesId)
fragment.startActivity(i)
@@ -174,7 +189,7 @@ class ConfigBuilderFragment : DaggerFragment() {
enabledInclusive.isChecked = plugin.isEnabled(pluginType)
enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled
enabledExclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled
- if(plugin.menuIcon != -1) {
+ if (plugin.menuIcon != -1) {
pluginIcon.visibility = View.VISIBLE
pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) })
} else {
@@ -196,7 +211,5 @@ class ConfigBuilderFragment : DaggerFragment() {
private fun areMultipleSelectionsAllowed(type: PluginType): Boolean {
return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP
}
-
}
-
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt
index 4fb140d55a..3f54b6e616 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt
@@ -9,9 +9,10 @@ import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
+import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
-import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
+import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
@@ -25,7 +26,8 @@ class ConfigBuilderPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val sp: SP,
private val rxBus: RxBusWrapper,
- private val activePlugin: ActivePluginProvider
+ private val activePlugin: ActivePluginProvider,
+ private val uel: UserEntryLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(ConfigBuilderFragment::class.java.name)
@@ -137,9 +139,10 @@ class ConfigBuilderPlugin @Inject constructor(
if (allowHardwarePump || activity == null) {
performPluginSwitch(changedPlugin, newState, type)
} else {
- showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable {
+ OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable {
performPluginSwitch(changedPlugin, newState, type)
sp.putBoolean("allow_hardware_pump", true)
+ uel.log("HW PUMP ALLOWED")
aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!")
}, Runnable {
rxBus.send(EventConfigBuilderUpdateGui())
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt
index 69a8892bdc..63c1719a45 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt
@@ -9,8 +9,6 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.Button
-import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
@@ -19,11 +17,14 @@ import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
-import info.nightscout.androidaps.logging.AAPSLogger
-import info.nightscout.androidaps.plugins.bus.RxBusWrapper
-import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
+import info.nightscout.androidaps.databinding.ObjectivesFragmentBinding
+import info.nightscout.androidaps.databinding.ObjectivesItemBinding
import info.nightscout.androidaps.dialogs.NtpProgressDialog
import info.nightscout.androidaps.events.EventNtpStatus
+import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.UserEntryLogger
+import info.nightscout.androidaps.plugins.bus.RxBusWrapper
+import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask
import info.nightscout.androidaps.receivers.ReceiverStatusStore
@@ -31,19 +32,20 @@ import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
-import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.SntpClient
-import info.nightscout.androidaps.utils.extensions.plusAssign
+import info.nightscout.androidaps.utils.alertDialogs.OKDialog
+import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
-import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
-import kotlinx.android.synthetic.main.objectives_fragment.*
import javax.inject.Inject
class ObjectivesFragment : DaggerFragment() {
+
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@@ -51,6 +53,7 @@ class ObjectivesFragment : DaggerFragment() {
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var sntpClient: SntpClient
+ @Inject lateinit var uel: UserEntryLogger
private val objectivesAdapter = ObjectivesAdapter()
private val handler = Handler(Looper.getMainLooper())
@@ -64,19 +67,23 @@ class ObjectivesFragment : DaggerFragment() {
}
}
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.objectives_fragment, container, false)
- }
+ private var _binding: ObjectivesFragmentBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
+ ObjectivesFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- objectives_recyclerview.layoutManager = LinearLayoutManager(view.context)
- objectives_recyclerview.adapter = objectivesAdapter
- objectives_fake.setOnClickListener { updateGUI() }
- objectives_reset.setOnClickListener {
+ binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
+ binding.recyclerview.adapter = objectivesAdapter
+ binding.fake.setOnClickListener { updateGUI() }
+ binding.reset.setOnClickListener {
objectivesPlugin.reset()
- objectives_recyclerview.adapter?.notifyDataSetChanged()
+ binding.recyclerview.adapter?.notifyDataSetChanged()
scrollToCurrentObjective()
}
scrollToCurrentObjective()
@@ -88,11 +95,10 @@ class ObjectivesFragment : DaggerFragment() {
super.onResume()
disposable += rxBus
.toObservable(EventObjectivesUpdateGui::class.java)
- .observeOn(AndroidSchedulers.mainThread())
+ .observeOn(aapsSchedulers.main)
.subscribe({
- objectives_recyclerview.adapter?.notifyDataSetChanged()
- }, { fabricPrivacy.logException(it) }
- )
+ binding.recyclerview.adapter?.notifyDataSetChanged()
+ }, fabricPrivacy::logException)
}
@Synchronized
@@ -105,6 +111,7 @@ class ObjectivesFragment : DaggerFragment() {
override fun onDestroyView() {
super.onDestroyView()
handler.removeCallbacks(objectiveUpdater)
+ _binding = null
}
private fun startUpdateTimer() {
@@ -129,7 +136,7 @@ class ObjectivesFragment : DaggerFragment() {
override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4
}
smoothScroller.targetPosition = i
- objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller)
+ binding.recyclerview.layoutManager?.startSmoothScroll(smoothScroller)
}
break
}
@@ -145,67 +152,66 @@ class ObjectivesFragment : DaggerFragment() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val objective = objectivesPlugin.objectives[position]
- holder.title.text = resourceHelper.gs(R.string.nth_objective, position + 1)
+ holder.binding.title.text = resourceHelper.gs(R.string.nth_objective, position + 1)
if (objective.objective != 0) {
- holder.objective.visibility = View.VISIBLE
- holder.objective.text = resourceHelper.gs(objective.objective)
+ holder.binding.objective.visibility = View.VISIBLE
+ holder.binding.objective.text = resourceHelper.gs(objective.objective)
} else
- holder.objective.visibility = View.GONE
+ holder.binding.objective.visibility = View.GONE
if (objective.gate != 0) {
- holder.gate.visibility = View.VISIBLE
- holder.gate.text = resourceHelper.gs(objective.gate)
+ holder.binding.gate.visibility = View.VISIBLE
+ holder.binding.gate.text = resourceHelper.gs(objective.gate)
} else
- holder.gate.visibility = View.GONE
+ holder.binding.gate.visibility = View.GONE
if (!objective.isStarted) {
- holder.gate.setTextColor(-0x1)
- holder.verify.visibility = View.GONE
- holder.progress.visibility = View.GONE
- holder.accomplished.visibility = View.GONE
- holder.unFinish.visibility = View.GONE
- holder.unStart.visibility = View.GONE
+ holder.binding.gate.setTextColor(-0x1)
+ holder.binding.verify.visibility = View.GONE
+ holder.binding.progress.visibility = View.GONE
+ holder.binding.accomplished.visibility = View.GONE
+ holder.binding.unfinish.visibility = View.GONE
+ holder.binding.unstart.visibility = View.GONE
if (position == 0 || objectivesPlugin.allPriorAccomplished(position))
- holder.start.visibility = View.VISIBLE
+ holder.binding.start.visibility = View.VISIBLE
else
- holder.start.visibility = View.GONE
+ holder.binding.start.visibility = View.GONE
} else if (objective.isAccomplished) {
- holder.gate.setTextColor(-0xb350b0)
- holder.verify.visibility = View.GONE
- holder.progress.visibility = View.GONE
- holder.start.visibility = View.GONE
- holder.accomplished.visibility = View.VISIBLE
- holder.unFinish.visibility = View.VISIBLE
- holder.unStart.visibility = View.GONE
+ holder.binding.gate.setTextColor(-0xb350b0)
+ holder.binding.verify.visibility = View.GONE
+ holder.binding.progress.visibility = View.GONE
+ holder.binding.start.visibility = View.GONE
+ holder.binding.accomplished.visibility = View.VISIBLE
+ holder.binding.unfinish.visibility = View.VISIBLE
+ holder.binding.unstart.visibility = View.GONE
} else if (objective.isStarted) {
- holder.gate.setTextColor(-0x1)
- holder.verify.visibility = View.VISIBLE
- holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked
- holder.start.visibility = View.GONE
- holder.accomplished.visibility = View.GONE
- holder.unFinish.visibility = View.GONE
- holder.unStart.visibility = View.VISIBLE
- holder.progress.visibility = View.VISIBLE
- holder.progress.removeAllViews()
+ holder.binding.gate.setTextColor(-0x1)
+ holder.binding.verify.visibility = View.VISIBLE
+ holder.binding.verify.isEnabled = objective.isCompleted || binding.fake.isChecked
+ holder.binding.start.visibility = View.GONE
+ holder.binding.accomplished.visibility = View.GONE
+ holder.binding.unfinish.visibility = View.GONE
+ holder.binding.unstart.visibility = View.VISIBLE
+ holder.binding.progress.visibility = View.VISIBLE
+ holder.binding.progress.removeAllViews()
for (task in objective.tasks) {
if (task.shouldBeIgnored()) continue
// name
- val name = TextView(holder.progress.context)
- @Suppress("SetTextlI8n")
- name.text = resourceHelper.gs(task.task) + ":"
+ val name = TextView(holder.binding.progress.context)
+ name.text = "${resourceHelper.gs(task.task)}:"
name.setTextColor(-0x1)
- holder.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ holder.binding.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
// hint
task.hints.forEach { h ->
- if (!task.isCompleted)
- holder.progress.addView(h.generate(context))
+ if (!task.isCompleted())
+ context?.let { holder.binding.progress.addView(h.generate(it)) }
}
// state
- val state = TextView(holder.progress.context)
+ val state = TextView(holder.binding.progress.context)
state.setTextColor(-0x1)
val basicHTML = "%2\$s"
- val formattedHTML = String.format(basicHTML, if (task.isCompleted) "#4CAF50" else "#FF9800", task.progress)
+ val formattedHTML = String.format(basicHTML, if (task.isCompleted()) "#4CAF50" else "#FF9800", task.progress)
state.text = HtmlHelper.fromHtml(formattedHTML)
state.gravity = Gravity.END
- holder.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+ holder.binding.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
if (task is ExamTask) {
state.setOnClickListener {
val dialog = ObjectivesExamDialog()
@@ -218,16 +224,16 @@ class ObjectivesFragment : DaggerFragment() {
}
}
// horizontal line
- val separator = View(holder.progress.context)
+ val separator = View(holder.binding.progress.context)
separator.setBackgroundColor(Color.DKGRAY)
- holder.progress.addView(separator, LinearLayout.LayoutParams.MATCH_PARENT, 2)
+ holder.binding.progress.addView(separator, LinearLayout.LayoutParams.MATCH_PARENT, 2)
}
}
- holder.accomplished.text = resourceHelper.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn))
- holder.accomplished.setTextColor(-0x3e3e3f)
- holder.verify.setOnClickListener {
+ holder.binding.accomplished.text = resourceHelper.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn))
+ holder.binding.accomplished.setTextColor(-0x3e3e3f)
+ holder.binding.verify.setOnClickListener {
receiverStatusStore.updateNetworkStatus()
- if (objectives_fake.isChecked) {
+ if (binding.fake.isChecked) {
objective.accomplishedOn = DateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
@@ -264,9 +270,9 @@ class ObjectivesFragment : DaggerFragment() {
}.start()
}
}
- holder.start.setOnClickListener {
+ holder.binding.start.setOnClickListener {
receiverStatusStore.updateNetworkStatus()
- if (objectives_fake.isChecked) {
+ if (binding.fake.isChecked) {
objective.startedOn = DateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
@@ -298,9 +304,10 @@ class ObjectivesFragment : DaggerFragment() {
}, receiverStatusStore.isConnected)
}.start()
}
- holder.unStart.setOnClickListener {
+ holder.binding.unstart.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable {
+ uel.log("OBJECTIVE UNSTARTED", i1 = position + 1)
objective.startedOn = 0
scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui())
@@ -308,7 +315,7 @@ class ObjectivesFragment : DaggerFragment() {
})
}
}
- holder.unFinish.setOnClickListener {
+ holder.binding.unfinish.setOnClickListener {
objective.accomplishedOn = 0
scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui())
@@ -318,21 +325,21 @@ class ObjectivesFragment : DaggerFragment() {
// generate random request code if none exists
val request = sp.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt()))
sp.putString(R.string.key_objectives_request_code, request)
- holder.requestCode.text = resourceHelper.gs(R.string.requestcode, request)
- holder.requestCode.visibility = View.VISIBLE
- holder.enterButton.visibility = View.VISIBLE
- holder.input.visibility = View.VISIBLE
- holder.inputHint.visibility = View.VISIBLE
- holder.enterButton.setOnClickListener {
- val input = holder.input.text.toString()
- objective.specialAction(activity, input)
+ holder.binding.requestcode.text = resourceHelper.gs(R.string.requestcode, request)
+ holder.binding.requestcode.visibility = View.VISIBLE
+ holder.binding.enterbutton.visibility = View.VISIBLE
+ holder.binding.input.visibility = View.VISIBLE
+ holder.binding.inputhint.visibility = View.VISIBLE
+ holder.binding.enterbutton.setOnClickListener {
+ val input = holder.binding.input.text.toString()
+ activity?.let { activity -> objective.specialAction(activity, input) }
rxBus.send(EventObjectivesUpdateGui())
}
} else {
- holder.enterButton.visibility = View.GONE
- holder.input.visibility = View.GONE
- holder.inputHint.visibility = View.GONE
- holder.requestCode.visibility = View.GONE
+ holder.binding.enterbutton.visibility = View.GONE
+ holder.binding.input.visibility = View.GONE
+ holder.binding.inputhint.visibility = View.GONE
+ holder.binding.requestcode.visibility = View.GONE
}
}
@@ -340,20 +347,9 @@ class ObjectivesFragment : DaggerFragment() {
return objectivesPlugin.objectives.size
}
- inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
- val title: TextView = itemView.findViewById(R.id.objective_title)
- val objective: TextView = itemView.findViewById(R.id.objective_objective)
- val gate: TextView = itemView.findViewById(R.id.objective_gate)
- val accomplished: TextView = itemView.findViewById(R.id.objective_accomplished)
- val progress: LinearLayout = itemView.findViewById(R.id.objective_progress)
- val verify: Button = itemView.findViewById(R.id.objective_verify)
- val start: Button = itemView.findViewById(R.id.objective_start)
- val unFinish: Button = itemView.findViewById(R.id.objective_unfinish)
- val unStart: Button = itemView.findViewById(R.id.objective_unstart)
- val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint)
- val input: EditText = itemView.findViewById(R.id.objective_input)
- val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton)
- val requestCode: TextView = itemView.findViewById(R.id.objective_requestcode)
+ inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+ val binding = ObjectivesItemBinding.bind(itemView)
}
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt
index 009812a1f2..c5b836ac71 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt
@@ -9,6 +9,7 @@ import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
+import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.*
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@@ -25,8 +26,8 @@ class ObjectivesPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val activePlugin: ActivePluginProvider,
private val sp: SP,
- private val config: Config
-
+ config: Config,
+ private val uel: UserEntryLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.fragmentClass(ObjectivesFragment::class.qualifiedName)
@@ -141,6 +142,7 @@ class ObjectivesPlugin @Inject constructor(
sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now())
setupObjectives()
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted))
+ uel.log("OBJECTIVES SKIPPED")
} else {
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid))
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt
index e44135eb45..c45d61bfd4 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
+import info.nightscout.androidaps.databinding.ObjectivesExamFragmentBinding
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective
@@ -15,28 +16,36 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper
-import kotlinx.android.synthetic.main.objectives_exam_fragment.*
import javax.inject.Inject
class ObjectivesExamDialog : DaggerDialogFragment() {
+
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var dateUtil: DateUtil
companion object {
+
var objective: Objective? = null
}
private var currentTask = 0
+ private var _binding: ObjectivesExamFragmentBinding? = null
+
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
+ savedInstanceState: Bundle?): View {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
currentTask = bundle.getInt("currentTask", 0)
}
- return inflater.inflate(R.layout.objectives_exam_fragment, container, false)
+ _binding = ObjectivesExamFragmentBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onStart() {
@@ -55,36 +64,46 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
bundle.putInt("currentTask", currentTask)
}
+ @Synchronized
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ @Synchronized
fun updateGui() {
+ if (_binding == null) return
objective?.let { objective ->
val task: ExamTask = objective.tasks[currentTask] as ExamTask
- objectives_exam_name.setText(task.task)
- objectives_exam_question.setText(task.question)
+ binding.examName.setText(task.task)
+ binding.examQuestion.setText(task.question)
// Options
- objectives_exam_options.removeAllViews()
+ binding.examOptions.removeAllViews()
task.options.forEach {
- val cb = it.generate(context)
- if (task.answered) {
- cb.isEnabled = false
- if (it.isCorrect)
- cb.isChecked = true
+ context?.let { context ->
+ val cb = it.generate(context)
+ if (task.answered) {
+ cb.isEnabled = false
+ if (it.isCorrect)
+ cb.isChecked = true
+ }
+ binding.examOptions.addView(cb)
}
- objectives_exam_options.addView(cb)
}
// Hints
- objectives_exam_hints.removeAllViews()
+ binding.examHints.removeAllViews()
for (h in task.hints) {
- objectives_exam_hints.addView(h.generate(context))
+ context?.let { binding.examHints.addView(h.generate(it)) }
}
// Disabled to
- objectives_exam_disabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo))
- objectives_exam_disabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE
+ binding.examDisabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo))
+ binding.examDisabledto.visibility = if (task.isEnabledAnswer()) View.GONE else View.VISIBLE
// Buttons
- objectives_exam_verify.isEnabled = !task.answered && task.isEnabledAnswer
- objectives_exam_verify.setOnClickListener {
+ binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer()
+ binding.examVerify.setOnClickListener {
var result = true
for (o in task.options) {
- val option: Option = o as Option
+ val option: Option = o
result = result && option.evaluate()
}
task.answered = result
@@ -95,35 +114,35 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
updateGui()
rxBus.send(EventObjectivesUpdateGui())
}
- close.setOnClickListener { dismiss() }
- objectives_exam_reset.setOnClickListener {
+ binding.close.setOnClickListener { dismiss() }
+ binding.examReset.setOnClickListener {
task.answered = false
//task.disabledTo = 0
updateGui()
rxBus.send(EventObjectivesUpdateGui())
}
- objectives_back_button.isEnabled = currentTask != 0
- objectives_back_button.setOnClickListener {
+ binding.backButton.isEnabled = currentTask != 0
+ binding.backButton.setOnClickListener {
currentTask--
updateGui()
}
- objectives_next_button.isEnabled = currentTask != objective.tasks.size - 1
- objectives_next_button.setOnClickListener {
+ binding.nextButton.isEnabled = currentTask != objective.tasks.size - 1
+ binding.nextButton.setOnClickListener {
currentTask++
updateGui()
}
- objectives_next_unanswered_button.isEnabled = !objective.isCompleted
- objectives_next_unanswered_button.setOnClickListener {
+ binding.nextUnansweredButton.isEnabled = !objective.isCompleted
+ binding.nextUnansweredButton.setOnClickListener {
for (i in (currentTask + 1) until objective.tasks.size) {
- if (!objective.tasks[i].isCompleted) {
+ if (!objective.tasks[i].isCompleted()) {
currentTask = i
updateGui()
return@setOnClickListener
}
}
for (i in 0..currentTask) {
- if (!objective.tasks[i].isCompleted) {
+ if (!objective.tasks[i].isCompleted()) {
currentTask = i
updateGui()
return@setOnClickListener
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java
deleted file mode 100644
index feef4a4222..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java
+++ /dev/null
@@ -1,296 +0,0 @@
-package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.text.util.Linkify;
-import android.widget.CheckBox;
-import android.widget.TextView;
-
-import androidx.annotation.StringRes;
-import androidx.fragment.app.FragmentActivity;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import dagger.android.HasAndroidInjector;
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.utils.DateUtil;
-import info.nightscout.androidaps.utils.T;
-import info.nightscout.androidaps.utils.resources.ResourceHelper;
-import info.nightscout.androidaps.utils.sharedPreferences.SP;
-
-public abstract class Objective {
- @Inject public SP sp;
- @Inject public ResourceHelper resourceHelper;
-
- private final String spName;
- @StringRes private final int objective;
- @StringRes private final int gate;
- private long startedOn;
- private long accomplishedOn;
- List tasks = new ArrayList<>();
- public boolean hasSpecialInput = false;
-
- public Objective(HasAndroidInjector injector, String spName, @StringRes int objective, @StringRes int gate) {
- injector.androidInjector().inject(this);
- this.spName = spName;
- this.objective = objective;
- this.gate = gate;
- startedOn = sp.getLong("Objectives_" + spName + "_started", 0L);
- accomplishedOn = sp.getLong("Objectives_" + spName + "_accomplished", 0L);
- if ((accomplishedOn - DateUtil.now()) > T.hours(3).msecs() || (startedOn - DateUtil.now()) > T.hours(3).msecs()) { // more than 3 hours in the future
- startedOn = 0;
- accomplishedOn = 0;
- }
- setupTasks(tasks);
- for (Task task : tasks) task.objective = this;
- }
-
- public boolean isCompleted() {
- for (Task task : tasks) {
- if (!task.shouldBeIgnored() && !task.isCompleted())
- return false;
- }
- return true;
- }
-
- public boolean isCompleted(long trueTime) {
- for (Task task : tasks) {
- if (!task.shouldBeIgnored() && !task.isCompleted(trueTime))
- return false;
- }
- return true;
- }
-
- public boolean isAccomplished() {
- return accomplishedOn != 0 && accomplishedOn < DateUtil.now();
- }
-
- public boolean isStarted() {
- return startedOn != 0;
- }
-
- public long getStartedOn() {
- return startedOn;
- }
-
- public int getObjective() {
- return objective;
- }
-
- public int getGate() {
- return gate;
- }
-
- public void setStartedOn(long startedOn) {
- this.startedOn = startedOn;
- sp.putLong("Objectives_" + spName + "_started", startedOn);
- }
-
- public void setAccomplishedOn(long accomplishedOn) {
- this.accomplishedOn = accomplishedOn;
- sp.putLong("Objectives_" + spName + "_accomplished", accomplishedOn);
- }
-
- public long getAccomplishedOn() {
- return accomplishedOn;
- }
-
- protected void setupTasks(List tasks) {
-
- }
-
- public List getTasks() {
- return tasks;
- }
-
- public boolean specialActionEnabled() {
- return true;
- }
-
- public void specialAction(FragmentActivity activity, String input) {
- }
-
- public abstract class Task {
- @StringRes
- private final int task;
- private Objective objective;
- ArrayList hints = new ArrayList<>();
-
- public Task(@StringRes int task) {
- this.task = task;
- }
-
- public @StringRes int getTask() {
- return task;
- }
-
- protected Objective getObjective() {
- return objective;
- }
-
- public abstract boolean isCompleted();
-
- public boolean isCompleted(long trueTime) {
- return isCompleted();
- }
-
- public String getProgress() {
- return resourceHelper.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet);
- }
-
- Task hint(Hint hint) {
- hints.add(hint);
- return this;
- }
-
- public ArrayList getHints() {
- return hints;
- }
-
- public boolean shouldBeIgnored() {
- return false;
- }
- }
-
- public class MinimumDurationTask extends Task {
-
- private final long minimumDuration;
-
- MinimumDurationTask(long minimumDuration) {
- super(R.string.time_elapsed);
- this.minimumDuration = minimumDuration;
- }
-
- @Override
- public boolean isCompleted() {
- return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn() >= minimumDuration;
- }
-
- @Override
- public boolean isCompleted(long trueTime) {
- return getObjective().isStarted() && trueTime - getObjective().getStartedOn() >= minimumDuration;
- }
-
- @Override
- public String getProgress() {
- return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn())
- + " / " + getDurationText(minimumDuration);
- }
-
- private String getDurationText(long duration) {
- int days = (int) Math.floor((double) duration / T.days(1).msecs());
- int hours = (int) Math.floor((double) duration / T.hours(1).msecs());
- int minutes = (int) Math.floor((double) duration / T.mins(1).msecs());
- if (days > 0) return resourceHelper.gq(R.plurals.objective_days, days, days);
- else if (hours > 0) return resourceHelper.gq(R.plurals.objective_hours, hours, hours);
- else return resourceHelper.gq(R.plurals.objective_minutes, minutes, minutes);
- }
- }
-
- public class ExamTask extends Task {
- @StringRes
- int question;
- ArrayList