Merge branch 'dev' into adrian/system-carb-timer

This commit is contained in:
AdrianLxM 2021-02-16 00:06:09 +01:00
commit 3314cbfbc2
152 changed files with 6447 additions and 4383 deletions

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
.gradle
/local.properties
.DS_Store
app/jacoco.exec
/build
/captures
*.apk

View file

@ -241,9 +241,6 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
// https://github.com/DavidProdinger/Weekdays-Selector (used outdated 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1')
implementation(name: 'weekdaysselector-1.1.1', ext: 'aar')
testImplementation "junit:junit:$junit_version"
testImplementation 'org.json:json:20201115'
testImplementation "org.mockito:mockito-core:${mockitoVersion}"

Binary file not shown.

Binary file not shown.

View file

@ -13,12 +13,13 @@ abstract class WizardModule {
@ContributesAndroidInjector abstract fun swBreakInjector(): SWBreak
@ContributesAndroidInjector abstract fun swButtonInjector(): SWButton
@ContributesAndroidInjector abstract fun swEditNumberWithUnitsInjector(): SWEditNumberWithUnits
@ContributesAndroidInjector abstract fun swEditNumberInjector(): SWEditNumber
@ContributesAndroidInjector abstract fun swEditStringInjector(): SWEditString
@ContributesAndroidInjector abstract fun swEditEncryptedPasswordInjector(): SWEditEncryptedPassword
@ContributesAndroidInjector abstract fun swEditUrlInjector(): SWEditUrl
@ContributesAndroidInjector abstract fun swFragmentInjector(): SWFragment
@ContributesAndroidInjector abstract fun swHtmlLinkInjector(): SWHtmlLink
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfotext
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfoText
@ContributesAndroidInjector abstract fun swItemInjector(): SWItem
@ContributesAndroidInjector abstract fun swPluginInjector(): SWPlugin
@ContributesAndroidInjector abstract fun swRadioButtonInjector(): SWRadioButton

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -92,12 +91,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -161,12 +160,7 @@ class FillDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -206,12 +205,7 @@ class InsulinDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
@ -14,7 +13,8 @@ import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
@ -53,6 +53,7 @@ class LoopDialog : DaggerDialogFragment() {
private var _binding: DialogLoopBinding? = null
private var loopHandler = Handler()
private var refreshDialog: Runnable? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
@ -293,12 +294,7 @@ class LoopDialog : DaggerDialogFragment() {
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -121,12 +120,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
val callback: Callback = object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -142,12 +141,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,63 +0,0 @@
package info.nightscout.androidaps.plugins.aps.logger;
import org.mozilla.javascript.ScriptableObject;
import javax.inject.Inject;
import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
/**
* Created by adrian on 15/10/17.
*/
public class LoggerCallback extends ScriptableObject {
@Inject
AAPSLogger aapsLogger;
private static StringBuffer errorBuffer = new StringBuffer();
private static StringBuffer logBuffer = new StringBuffer();
public LoggerCallback() {
//empty constructor needed for Rhino
errorBuffer = new StringBuffer();
logBuffer = new StringBuffer();
StaticInjector.Companion.getInstance().androidInjector().inject(this);
}
@Override
public String getClassName() {
return "LoggerCallback";
}
public void jsConstructor() {
//empty constructor on JS site; could work as setter
}
public void jsFunction_log(Object obj1) {
aapsLogger.debug(LTag.APS, obj1.toString().trim());
logBuffer.append(obj1.toString());
}
public void jsFunction_error(Object obj1) {
aapsLogger.error(LTag.APS, obj1.toString().trim());
errorBuffer.append(obj1.toString());
}
public static String getScriptDebug() {
String ret = "";
if (errorBuffer.length() > 0) {
ret += "e:\n" + errorBuffer.toString();
}
if (ret.length() > 0 && logBuffer.length() > 0) ret += '\n';
if (logBuffer.length() > 0) {
ret += "d:\n" + logBuffer.toString();
}
return ret;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.aps.logger
import info.nightscout.androidaps.db.StaticInjector
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import org.mozilla.javascript.ScriptableObject
import javax.inject.Inject
@Suppress("unused", "FunctionName")
class LoggerCallback : ScriptableObject() {
@Inject lateinit var aapsLogger: AAPSLogger
override fun getClassName(): String = "LoggerCallback"
fun jsConstructor() {
//empty constructor on JS site; could work as setter
}
fun jsFunction_log(obj1: Any) {
aapsLogger.debug(LTag.APS, obj1.toString().trim { it <= ' ' })
logBuffer.append(obj1.toString())
}
fun jsFunction_error(obj1: Any) {
aapsLogger.error(LTag.APS, obj1.toString().trim { it <= ' ' })
errorBuffer.append(obj1.toString())
}
companion object {
private var errorBuffer = StringBuffer()
private var logBuffer = StringBuffer()
val scriptDebug: String
get() {
var ret = ""
if (errorBuffer.isNotEmpty()) {
ret += """
e:
$errorBuffer
""".trimIndent()
}
if (ret.isNotEmpty() && logBuffer.isNotEmpty()) ret += '\n'
if (logBuffer.isNotEmpty()) {
ret += """
d:
$logBuffer
""".trimIndent()
}
return ret
}
}
init {
//empty constructor needed for Rhino
errorBuffer = StringBuffer()
logBuffer = StringBuffer()
@Suppress("DEPRECATION")
StaticInjector.Companion.getInstance().androidInjector().inject(this)
}
}

View file

@ -1,880 +0,0 @@
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 org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Lazy;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.BuildConfig;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainActivity;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
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.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventTempTargetChange;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.LoopInterface;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpDescription;
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.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.ActionStringHandler;
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;
@Singleton
public class LoopPlugin extends PluginBase implements LoopInterface {
private final HasAndroidInjector injector;
private final SP sp;
private final RxBusWrapper rxBus;
private final AapsSchedulers aapsSchedulers;
private final ConstraintChecker constraintChecker;
private final ResourceHelper resourceHelper;
private final ProfileFunction profileFunction;
private final Context context;
private final CommandQueueProvider commandQueue;
private final ActivePluginProvider activePlugin;
private final TreatmentsPlugin treatmentsPlugin;
private final VirtualPumpPlugin virtualPumpPlugin;
private final Lazy<ActionStringHandler> actionStringHandler;
private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
private final ReceiverStatusStore receiverStatusStore;
private final FabricPrivacy fabricPrivacy;
private final NSUpload nsUpload;
private final HardLimits hardLimits;
private final CompositeDisposable disposable = new CompositeDisposable();
private static final String CHANNEL_ID = "AndroidAPS-Openloop";
private long lastBgTriggeredRun = 0;
private long loopSuspendedTill; // end of manual loop suspend
private boolean isSuperBolus;
private boolean isDisconnected;
private long carbsSuggestionsSuspendedUntil = 0;
private int prevCarbsreq = 0;
@Nullable private LastRun lastRun = null;
@Nullable @Override public LastRun getLastRun() {
return lastRun;
}
@Override public void setLastRun(@Nullable LastRun lastRun) {
this.lastRun = lastRun;
}
@Inject
public LoopPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
AapsSchedulers aapsSchedulers,
RxBusWrapper rxBus,
SP sp,
Config config,
ConstraintChecker constraintChecker,
ResourceHelper resourceHelper,
ProfileFunction profileFunction,
Context context,
CommandQueueProvider commandQueue,
ActivePluginProvider activePlugin,
TreatmentsPlugin treatmentsPlugin,
VirtualPumpPlugin virtualPumpPlugin,
Lazy<ActionStringHandler> 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.aapsSchedulers = aapsSchedulers;
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(aapsSchedulers.getIo())
.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.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
// Autosens calculation not triggered by a new BG
if (!(event.getCause() instanceof EventNewBG)) return;
GlucoseValue glucoseValue = iobCobCalculatorPlugin.actualBg();
// BG outdated
if (glucoseValue == null) return;
// already looped with that value
if (glucoseValue.getTimestamp() <= lastBgTriggeredRun) return;
lastBgTriggeredRun = glucoseValue.getTimestamp();
invoke("AutosenseCalculation for " + glucoseValue, true);
}, fabricPrivacy::logException)
);
}
private void createNotificationChannel() {
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 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<Boolean> 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<Boolean> 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.setUsePercent(true);
}
result.setPercent((int) (result.getRate() / profile.getBasal() * 100));
// check rate for constraints
final APSResult resultAfterConstraints = result.newAndClone(injector);
resultAfterConstraints.setRateConstraint(new Constraint<>(resultAfterConstraints.getRate()));
resultAfterConstraints.setRate(constraintChecker.applyBasalConstraints(resultAfterConstraints.getRateConstraint(), profile).value());
resultAfterConstraints.setPercentConstraint(new Constraint<>(resultAfterConstraints.getPercent()));
resultAfterConstraints.setPercent(constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.getPercentConstraint(), profile).value());
resultAfterConstraints.setSmbConstraint(new Constraint<>(resultAfterConstraints.getSmb()));
resultAfterConstraints.setSmb(constraintChecker.applyBolusConstraints(resultAfterConstraints.getSmbConstraint()).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 requested but still in 3 min interval");
resultAfterConstraints.setSmb(0);
}
if (lastRun != null && lastRun.getConstraintsProcessed() != null) {
prevCarbsreq = lastRun.getConstraintsProcessed().getCarbsReq();
}
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<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
if (closedLoopEnabled.value()) {
if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired()
&& resultAfterConstraints.getCarbsReq() >= 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)) {
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, true)) {
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, true)) {
// 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.getTempBasalRequested())
lastRun.setTbrSetByPump(waiting);
if (resultAfterConstraints.getBolusRequested())
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 durationMinutes) {
carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + (durationMinutes * 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.getTempBasalRequested()) {
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.getUsePercent() && allowPercentage()) {
if (request.getPercent() == 100 && request.getDuration() == 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.getPercent()).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
}
}
} else if (activeTemp != null
&& activeTemp.getPlannedRemainingMinutes() > 5
&& request.getDuration() - activeTemp.getPlannedRemainingMinutes() < 30
&& request.getPercent() == activeTemp.percentRate) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).percent(request.getPercent())
.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.getPercent(), request.getDuration(), false, profile, callback);
}
} else {
if ((request.getRate() == 0 && request.getDuration() == 0) || Math.abs(request.getRate() - 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.getRate()).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
}
}
} else if (activeTemp != null
&& activeTemp.getPlannedRemainingMinutes() > 5
&& request.getDuration() - activeTemp.getPlannedRemainingMinutes() < 30
&& Math.abs(request.getRate() - 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.getRate(), request.getDuration(), false, profile, callback);
}
}
}
private void applySMBRequest(APSResult request, Callback callback) {
if (!request.getBolusRequested()) {
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.getSmb();
detailedBolusInfo.isSMB = true;
detailedBolusInfo.source = Source.USER;
detailedBolusInfo.deliverAt = request.getDeliverAt();
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);
}
}

View file

@ -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.
<p>
*/
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"
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -1,300 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA;
import androidx.annotation.Nullable;
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.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;
}
}

View file

@ -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<IobTotal?>?,
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<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> 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
}
}

View file

@ -114,7 +114,7 @@ class OpenAPSAMAFragment : DaggerFragment() {
if (openAPSAMAPlugin.lastAPSRun != 0L) {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
}
openAPSAMAPlugin.lastAutosensResult?.let {
openAPSAMAPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
}
}

View file

@ -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.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
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.APSResult;
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.iob.iobCobCalculator.data.AutosensData;
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.getRate() == 0d && determineBasalResultAMA.getDuration() == 0 && !treatmentsPlugin.isTempBasalInProgress())
determineBasalResultAMA.setTempBasalRequested(false);
determineBasalResultAMA.setIob(iobArray[0]);
long now = System.currentTimeMillis();
try {
determineBasalResultAMA.getJson().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;
}
}

View file

@ -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;
}
}

View file

@ -1,373 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB;
import androidx.annotation.Nullable;
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.inject.Inject;
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.ProfileFunction;
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.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;
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 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: " + mAutosensData.toString());
else
aapsLogger.debug(LTag.APS, "Autosens data: " + "undefined");
aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined");
aapsLogger.debug(LTag.APS, "MicroBolusAllowed: " + mMicrobolusAllowed);
aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: " + mSMBAlwaysAllowed);
aapsLogger.debug(LTag.APS, "CurrentTime: " + 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,
Boolean.valueOf(mMicrobolusAllowed),
makeParam(null, rhino, scope), // reservoir data as undefined
Long.valueOf(mCurrentTime),
Boolean.valueOf(mIsSaveCgmSource)
};
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 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 {
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;
return NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
}
private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) {
//Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() {
return NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
}
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;
}
}

View file

@ -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<IobTotal>,
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<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> 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)
}
}

View file

@ -117,7 +117,7 @@ class OpenAPSSMBFragment : DaggerFragment() {
if (openAPSSMBPlugin.lastAPSRun != 0L) {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
}
openAPSSMBPlugin.lastAutosensResult?.let {
openAPSSMBPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
}
}

View file

@ -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.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
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.APSResult;
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.iob.iobCobCalculator.data.AutosensData;
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<Double> inputConstraints = new Constraint<>(0d); // fake. only for collecting all results
Constraint<Double> 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<Double> 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<Boolean> smbAllowed = new Constraint<>(!tempBasalFallback);
constraintChecker.isSMBModeEnabled(smbAllowed);
inputConstraints.copyReasons(smbAllowed);
Constraint<Boolean> advancedFiltering = new Constraint<>(!tempBasalFallback);
constraintChecker.isAdvancedFilteringEnabled(advancedFiltering);
inputConstraints.copyReasons(advancedFiltering);
Constraint<Boolean> 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.getRate() == 0d && determineBasalResultSMB.getDuration() == 0 && !treatmentsPlugin.isTempBasalInProgress())
determineBasalResultSMB.setTempBasalRequested(false);
determineBasalResultSMB.setIob(iobArray[0]);
try {
determineBasalResultSMB.getJson().put("timestamp", DateUtil.toISOString(now));
} catch (JSONException e) {
getAapsLogger().error(LTag.APS, "Unhandled exception", e);
}
determineBasalResultSMB.setInputConstraints(inputConstraints);
lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS;
lastAPSResult = determineBasalResultSMB;
lastAPSRun = now;
}
rxBus.send(new EventOpenAPSUpdateGui());
//deviceStatus.suggested = determineBasalResultAMA.json;
}
@NotNull
@Override
public Constraint<Boolean> isSuperBolusEnabled(Constraint<Boolean> value) {
value.set(getAapsLogger(), false);
return value;
}
}

View file

@ -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<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_COB))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_temptarget))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(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<Boolean>): Constraint<Boolean> {
value[aapsLogger] = false
return value
}
}

View file

@ -196,19 +196,19 @@ class ObjectivesFragment : DaggerFragment() {
if (task.shouldBeIgnored()) continue
// name
val name = TextView(holder.binding.progress.context)
name.text = resourceHelper.gs(task.task) + ":"
name.text = "${resourceHelper.gs(task.task)}:"
name.setTextColor(-0x1)
holder.binding.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
// hint
task.hints.forEach { h ->
if (!task.isCompleted)
holder.binding.progress.addView(h.generate(context))
if (!task.isCompleted())
context?.let { holder.binding.progress.addView(h.generate(it)) }
}
// state
val state = TextView(holder.binding.progress.context)
state.setTextColor(-0x1)
val basicHTML = "<font color=\"%1\$s\"><b>%2\$s</b></font>"
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.binding.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
@ -307,7 +307,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.unstart.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable {
uel.log("OBJECTVE UNSTARTED", i1 = position + 1)
uel.log("OBJECTIVE UNSTARTED", i1 = position + 1)
objective.startedOn = 0
scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui())
@ -332,7 +332,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.inputhint.visibility = View.VISIBLE
holder.binding.enterbutton.setOnClickListener {
val input = holder.binding.input.text.toString()
objective.specialAction(activity, input)
activity?.let { activity -> objective.specialAction(activity, input) }
rxBus.send(EventObjectivesUpdateGui())
}
} else {

View file

@ -80,6 +80,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
// Options
binding.examOptions.removeAllViews()
task.options.forEach {
context?.let { context ->
val cb = it.generate(context)
if (task.answered) {
cb.isEnabled = false
@ -88,20 +89,21 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
}
binding.examOptions.addView(cb)
}
}
// Hints
binding.examHints.removeAllViews()
for (h in task.hints) {
binding.examHints.addView(h.generate(context))
context?.let { binding.examHints.addView(h.generate(it)) }
}
// Disabled to
binding.examDisabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo))
binding.examDisabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE
binding.examDisabledto.visibility = if (task.isEnabledAnswer()) View.GONE else View.VISIBLE
// Buttons
binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer
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
@ -133,14 +135,14 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
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

View file

@ -1,295 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
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<Task> 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<Task> tasks) {
}
public List<Task> 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<Hint> 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<Hint> 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.days, days, days);
else if (hours > 0) return resourceHelper.gq(R.plurals.hours, hours, hours);
else return resourceHelper.gq(R.plurals.minutes, minutes, minutes);
}
}
public class ExamTask extends Task {
@StringRes
int question;
ArrayList<Option> options = new ArrayList<>();
private final String spIdentifier;
private boolean answered;
private long disabledTo;
ExamTask(@StringRes int task, @StringRes int question, String spIdentifier) {
super(task);
this.question = question;
this.spIdentifier = spIdentifier;
answered = sp.getBoolean("ExamTask_" + spIdentifier, false);
disabledTo = sp.getLong("DisabledTo_" + spIdentifier, 0L);
}
public void setDisabledTo(long newState) {
disabledTo = newState;
sp.putLong("DisabledTo_" + spIdentifier, disabledTo);
}
public long getDisabledTo() {
return disabledTo;
}
public boolean isEnabledAnswer() {
return disabledTo < DateUtil.now();
}
public void setAnswered(boolean newState) {
answered = newState;
sp.putBoolean("ExamTask_" + spIdentifier, answered);
}
public boolean getAnswered() {
return answered;
}
ExamTask option(Option option) {
options.add(option);
return this;
}
public @StringRes int getQuestion() {
return question;
}
public List<Objective.Option> getOptions() {
return options;
}
@Override
public boolean isCompleted() {
return answered;
}
}
public class Option {
@StringRes int option;
boolean isCorrect;
CheckBox cb; // TODO: change it, this will block releasing memeory
Option(@StringRes int option, boolean isCorrect) {
this.option = option;
this.isCorrect = isCorrect;
}
public boolean isCorrect() {
return isCorrect;
}
public CheckBox generate(Context context) {
cb = new CheckBox(context);
cb.setText(option);
return cb;
}
public boolean evaluate() {
boolean selection = cb.isChecked();
if (selection && isCorrect) return true;
return !selection && !isCorrect;
}
}
public class Hint {
@StringRes int hint;
Hint(@StringRes int hint) {
this.hint = hint;
}
public TextView generate(Context context) {
TextView textView = new TextView(context);
textView.setText(hint);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setLinksClickable(true);
textView.setLinkTextColor(Color.YELLOW);
Linkify.addLinks(textView, Linkify.WEB_URLS);
return textView;
}
}
}

View file

@ -0,0 +1,183 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
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 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
import java.util.*
import javax.inject.Inject
import kotlin.math.floor
abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRes objective: Int, @StringRes gate: Int) {
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
private val spName: String
@StringRes val objective: Int
@StringRes val gate: Int
var startedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_started", startedOn)
}
var accomplishedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_accomplished", value)
}
var tasks: MutableList<Task> = ArrayList()
var hasSpecialInput = false
val isCompleted: Boolean
get() {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted()) return false
}
return true
}
init {
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
}
}
fun isCompleted(trueTime: Long): Boolean {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted(trueTime)) return false
}
return true
}
val isAccomplished: Boolean
get() = accomplishedOn != 0L && accomplishedOn < DateUtil.now()
val isStarted: Boolean
get() = startedOn != 0L
open fun specialActionEnabled(): Boolean {
return true
}
open fun specialAction(activity: FragmentActivity, input: String) {}
abstract inner class Task(var objective: Objective, @StringRes val task: Int) {
var hints = ArrayList<Hint>()
abstract fun isCompleted(): Boolean
open fun isCompleted(trueTime: Long): Boolean = isCompleted
open val progress: String
get() = resourceHelper.gs(if (isCompleted) R.string.completed_well_done else R.string.not_completed_yet)
fun hint(hint: Hint): Task {
hints.add(hint)
return this
}
open fun shouldBeIgnored(): Boolean = false
}
inner class MinimumDurationTask internal constructor(objective: Objective, private val minimumDuration: Long) : Task(objective, R.string.time_elapsed) {
override fun isCompleted(): Boolean =
objective.isStarted && System.currentTimeMillis() - objective.startedOn >= minimumDuration
override fun isCompleted(trueTime: Long): Boolean {
return objective.isStarted && trueTime - objective.startedOn >= minimumDuration
}
override val progress: String
get() = (getDurationText(System.currentTimeMillis() - objective.startedOn)
+ " / " + getDurationText(minimumDuration))
private fun getDurationText(duration: Long): String {
val days = floor(duration.toDouble() / T.days(1).msecs()).toInt()
val hours = floor(duration.toDouble() / T.hours(1).msecs()).toInt()
val minutes = floor(duration.toDouble() / T.mins(1).msecs()).toInt()
return when {
days > 0 -> resourceHelper.gq(R.plurals.days, days, days)
hours > 0 -> resourceHelper.gq(R.plurals.hours, hours, hours)
else -> resourceHelper.gq(R.plurals.minutes, minutes, minutes)
}
}
}
inner class ExamTask internal constructor(objective: Objective, @StringRes task: Int, @StringRes val question: Int, private val spIdentifier: String) : Task(objective, task) {
var options = ArrayList<Option>()
var answered: Boolean = false
set(value) {
field = value
sp.putBoolean("ExamTask_$spIdentifier", value)
}
var disabledTo: Long = 0
set(value) {
field = value
sp.putLong("DisabledTo_$spIdentifier", value)
}
init {
answered = sp.getBoolean("ExamTask_$spIdentifier", false)
disabledTo = sp.getLong("DisabledTo_$spIdentifier", 0L)
}
override fun isCompleted(): Boolean = answered
fun isEnabledAnswer(): Boolean = disabledTo < DateUtil.now()
fun option(option: Option): ExamTask {
options.add(option)
return this
}
}
inner class Option internal constructor(@StringRes var option: Int, var isCorrect: Boolean) {
var cb: CheckBox? = null // TODO: change it, this will block releasing memory
fun generate(context: Context): CheckBox {
cb = CheckBox(context)
cb?.setText(option)
return cb!!
}
fun evaluate(): Boolean {
val selection = cb!!.isChecked
return if (selection && isCorrect) true else !selection && !isCorrect
}
}
inner class Hint internal constructor(@StringRes var hint: Int) {
fun generate(context: Context): TextView {
val textView = TextView(context)
textView.setText(hint)
textView.autoLinkMask = Linkify.WEB_URLS
textView.linksClickable = true
textView.setLinkTextColor(Color.YELLOW)
Linkify.addLinks(textView, Linkify.WEB_URLS)
return textView
}
}
}

View file

@ -1,91 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective0 extends Objective {
@Inject SP sp;
@Inject ActivePluginProvider activePlugin;
@Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject TreatmentsPlugin treatmentsPlugin;
@Inject LoopPlugin loopPlugin;
@Inject NSClientPlugin nsClientPlugin;
@Inject IobCobCalculatorPlugin iobCobCalculatorPlugin;
public Objective0(HasAndroidInjector injector) {
super(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_bgavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.nsclienthaswritepermission) {
@Override
public boolean isCompleted() {
return nsClientPlugin.hasWritePermission();
}
});
tasks.add(new Task(R.string.virtualpump_uploadstatus_title) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false);
}
@Override
public boolean shouldBeIgnored() {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP);
}
});
tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.hasbgdata) {
@Override
public boolean isCompleted() {
return iobCobCalculatorPlugin.lastBg() != null;
}
});
tasks.add(new Task(R.string.loopenabled) {
@Override
public boolean isCompleted() {
return loopPlugin.isEnabled(PluginType.LOOP);
}
});
tasks.add(new Task(R.string.apsselected) {
@Override
public boolean isCompleted() {
APSInterface usedAPS = activePlugin.getActiveAPS();
return ((PluginBase) usedAPS).isEnabled(PluginType.APS);
}
});
tasks.add(new Task(R.string.activate_profile) {
@Override
public boolean isCompleted() {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null;
}
});
}
}

View file

@ -0,0 +1,72 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import javax.inject.Inject
class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate) {
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
init {
tasks.add(object : Task(this, R.string.objectives_bgavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.nsclienthaswritepermission) {
override fun isCompleted(): Boolean {
return nsClientPlugin.hasWritePermission()
}
})
tasks.add(object : Task(this, R.string.virtualpump_uploadstatus_title) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false)
}
override fun shouldBeIgnored(): Boolean {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP)
}
})
tasks.add(object : Task(this, R.string.objectives_pumpstatusavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.hasbgdata) {
override fun isCompleted(): Boolean {
return iobCobCalculatorPlugin.lastBg() != null
}
})
tasks.add(object : Task(this, R.string.loopenabled) {
override fun isCompleted(): Boolean {
return loopPlugin.isEnabled(PluginType.LOOP)
}
})
tasks.add(object : Task(this, R.string.apsselected) {
override fun isCompleted(): Boolean {
val usedAPS = activePlugin.activeAPS
return (usedAPS as PluginBase).isEnabled(PluginType.APS)
}
})
tasks.add(object : Task(this, R.string.activate_profile) {
override fun isCompleted(): Boolean {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null
}
})
}
}

View file

@ -1,67 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective1 extends Objective {
@Inject SP sp;
@Inject ActionsPlugin actionsPlugin;
@Inject
public Objective1(HasAndroidInjector injector) {
super(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_useprofileswitch) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false);
}
});
tasks.add(new Task(R.string.objectives_usedisconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usereconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusereconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usetemptarget) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false);
}
}.hint(new Hint(R.string.usetemptarget_hint)));
tasks.add(new Task(R.string.objectives_useactions) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible();
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_useloop) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseloop, false);
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_usescale) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusescale, false);
}
}.hint(new Hint(R.string.usescale_hint)));
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
import javax.inject.Inject
class Objective1 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate) {
@Inject lateinit var actionsPlugin: ActionsPlugin
init {
tasks.add(object : Task(this, R.string.objectives_useprofileswitch) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false)
}
})
tasks.add(object : Task(this, R.string.objectives_usedisconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usereconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusereconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usetemptarget) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false)
}
}.hint(Hint(R.string.usetemptarget_hint)))
tasks.add(object : Task(this, R.string.objectives_useactions) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible()
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_useloop) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseloop, false)
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_usescale) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusescale, false)
}
}.hint(Hint(R.string.usescale_hint)))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective10 extends Objective {
public Objective10(HasAndroidInjector injector) {
super(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective10(injector: HasAndroidInjector) : Objective(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,222 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.Collections;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Objective2 extends Objective {
public Objective2(HasAndroidInjector injector) {
super(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate);
for (Task task : tasks) {
if (!task.isCompleted()) setAccomplishedOn(0);
}
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new ExamTask(R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(new Option(R.string.prerequisites_nightscout, true))
.option(new Option(R.string.prerequisites_computer, true))
.option(new Option(R.string.prerequisites_pump, true))
.option(new Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(new Hint(R.string.prerequisites_hint1))
);
tasks.add(new ExamTask(R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(new Option(R.string.prerequisites2_profile, true))
.option(new Option(R.string.prerequisites2_device, true))
.option(new Option(R.string.prerequisites2_internet, false))
.option(new Option(R.string.prerequisites2_supportedcgm, true))
.hint(new Hint(R.string.prerequisites2_hint1))
);
tasks.add(new ExamTask(R.string.basaltest_label, R.string.basaltest_when,"basaltest")
.option(new Option(R.string.basaltest_fixed, false))
.option(new Option(R.string.basaltest_havingregularhighlow, true))
.option(new Option(R.string.basaltest_weekly, false))
.option(new Option(R.string.basaltest_beforeloop, true))
.hint(new Hint(R.string.basaltest_hint1))
);
tasks.add(new ExamTask(R.string.dia_label_exam, R.string.dia_whatmeansdia,"dia")
.option(new Option(R.string.dia_profile, true))
.option(new Option(R.string.dia_minimumis5h, true))
.option(new Option(R.string.dia_meaningisequaltodiapump, false))
.option(new Option(R.string.dia_valuemustbedetermined, true))
.hint(new Hint(R.string.dia_hint1))
);
tasks.add(new ExamTask(R.string.isf_label_exam, R.string.blank,"isf")
.option(new Option(R.string.isf_decreasingvalue, true))
.option(new Option(R.string.isf_preferences, false))
.option(new Option(R.string.isf_increasingvalue, false))
.option(new Option(R.string.isf_noeffect, false))
.hint(new Hint(R.string.isf_hint1))
.hint(new Hint(R.string.isf_hint2))
);
tasks.add(new ExamTask(R.string.ic_label_exam, R.string.blank,"ic")
.option(new Option(R.string.ic_increasingvalue, true))
.option(new Option(R.string.ic_decreasingvalue, false))
.option(new Option(R.string.ic_multiple, true))
.option(new Option(R.string.ic_isf, false))
.hint(new Hint(R.string.ic_hint1))
);
tasks.add(new ExamTask(R.string.hypott_label, R.string.hypott_whenhypott,"hypott")
.option(new Option(R.string.hypott_preventoversmb, true))
.option(new Option(R.string.hypott_exercise, false))
.option(new Option(R.string.hypott_wrongbasal, false))
.option(new Option(R.string.hypott_0basal, false))
.hint(new Hint(R.string.hypott_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch_label, R.string.profileswitch_pctwillchange,"profileswitch")
.option(new Option(R.string.profileswitch_basallower, true))
.option(new Option(R.string.profileswitch_isfhigher, true))
.option(new Option(R.string.profileswitch_iclower, false))
.option(new Option(R.string.profileswitch_unchanged, false))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange,"profileswitch2")
.option(new Option(R.string.profileswitch2_bghigher, false))
.option(new Option(R.string.profileswitch2_basalhigher, true))
.option(new Option(R.string.profileswitch2_bgunchanged, true))
.option(new Option(R.string.profileswitch2_isfhigher, false))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.profileswitchtime_label, R.string.profileswitchtime_iwant,"profileswitchtime")
.option(new Option(R.string.profileswitchtime_2, false))
.option(new Option(R.string.profileswitchtime__2, true))
.option(new Option(R.string.profileswitchtime_tt, false))
.option(new Option(R.string.profileswitchtime_100, false))
.hint(new Hint(R.string.profileswitchtime_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch4_label, R.string.blank,"profileswitch4")
.option(new Option(R.string.profileswitch4_rates, true))
.option(new Option(R.string.profileswitch4_internet, true))
.option(new Option(R.string.profileswitch4_sufficient, false))
.option(new Option(R.string.profileswitch4_multi, true))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo,"exercise")
.option(new Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(new Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(new Option(R.string.exerciseprofile_suspendloop, false))
.option(new Option(R.string.exerciseprofile_leaveat100, false))
.hint(new Hint(R.string.exerciseprofile_hint1))
);
tasks.add(new ExamTask(R.string.exercise_label, R.string.exercise_whattodo,"exercise2")
.option(new Option(R.string.exercise_settt, true))
.option(new Option(R.string.exercise_setfinished, false))
.option(new Option(R.string.exercise_setunchanged, false))
.option(new Option(R.string.exercise_15g, false))
.hint(new Hint(R.string.exercise_hint1))
);
tasks.add(new ExamTask(R.string.noisycgm_label, R.string.noisycgm_whattodo,"noisycgm")
.option(new Option(R.string.noisycgm_nothing, false))
.option(new Option(R.string.noisycgm_pause, true))
.option(new Option(R.string.noisycgm_replacesensor, true))
.option(new Option(R.string.noisycgm_checksmoothing, true))
.hint(new Hint(R.string.noisycgm_hint1))
);
tasks.add(new ExamTask(R.string.pumpdisconnect_label, R.string.blank,"pumpdisconnect")
.option(new Option(R.string.pumpdisconnect_unnecessary, false))
.option(new Option(R.string.pumpdisconnect_missinginsulin, true))
.option(new Option(R.string.pumpdisconnect_notstop, false))
.option(new Option(R.string.pumpdisconnect_openloop, false))
.hint(new Hint(R.string.pumpdisconnect_hint1))
);
tasks.add(new ExamTask(R.string.insulin_label, R.string.insulin_ultrarapid,"insulin")
.option(new Option(R.string.insulin_novorapid, false))
.option(new Option(R.string.insulin_humalog, false))
.option(new Option(R.string.insulin_actrapid, false))
.option(new Option(R.string.insulin_fiasp, true))
.hint(new Hint(R.string.insulin_hint1))
);
tasks.add(new ExamTask(R.string.sensitivity_label, R.string.blank,"sensitivity")
.option(new Option(R.string.sensitivity_adjust, true))
.option(new Option(R.string.sensitivity_edit, false))
.option(new Option(R.string.sensitivity_cannula, true))
.option(new Option(R.string.sensitivity_time, true))
.hint(new Hint(R.string.sensitivity_hint1))
);
tasks.add(new ExamTask(R.string.objectives_label, R.string.objectives_howtosave,"objectives")
.option(new Option(R.string.objectives_notesettings, false))
.option(new Option(R.string.objectives_afterobjective, true))
.option(new Option(R.string.objectives_afterchange, true))
.option(new Option(R.string.objectives_afterinitialsetup, true))
.hint(new Hint(R.string.objectives_hint1))
.hint(new Hint(R.string.objectives_hint2))
);
tasks.add(new ExamTask(R.string.objectives2_label, R.string.objectives_howtosave,"objectives2")
.option(new Option(R.string.objectives2_maintenance, true))
.option(new Option(R.string.objectives2_internalstorage, true))
.option(new Option(R.string.objectives2_cloud, true))
.option(new Option(R.string.objectives2_easyrestore, false))
.hint(new Hint(R.string.objectives_hint1))
.hint(new Hint(R.string.objectives_hint2))
);
tasks.add(new ExamTask(R.string.update_label, R.string.blank,"update")
.option(new Option(R.string.update_git, true))
.option(new Option(R.string.update_askfriend, false))
.option(new Option(R.string.update_keys, true))
.option(new Option(R.string.update_asap, true))
.hint(new Hint(R.string.update_hint1))
);
tasks.add(new ExamTask(R.string.troubleshooting_label, R.string.troubleshooting_wheretoask,"troubleshooting")
.option(new Option(R.string.troubleshooting_fb, true))
.option(new Option(R.string.troubleshooting_wiki, true))
.option(new Option(R.string.troubleshooting_gitter, true))
.option(new Option(R.string.troubleshooting_yourendo, false))
.hint(new Hint(R.string.troubleshooting_hint1))
.hint(new Hint(R.string.troubleshooting_hint2))
.hint(new Hint(R.string.troubleshooting_hint3))
);
tasks.add(new ExamTask(R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo,"wrongcarbs")
.option(new Option(R.string.wrongcarbs_addinsulin, false))
.option(new Option(R.string.wrongcarbs_treatmentstab, true))
.option(new Option(R.string.wrongcarbs_donothing, false))
.option(new Option(R.string.wrongcarbs_bolus, false))
);
tasks.add(new ExamTask(R.string.wronginsulin_label, R.string.wronginsulin_whattodo,"wronginsulin")
.option(new Option(R.string.wronginsulin_careportal, false))
.option(new Option(R.string.wronginsulin_compare, true))
.option(new Option(R.string.wronginsulin_prime, true))
.option(new Option(R.string.wrongcarbs_donothing, false))
);
tasks.add(new ExamTask(R.string.iob_label, R.string.blank,"iob")
.option(new Option(R.string.iob_value, true))
.option(new Option(R.string.iob_hightemp, false))
.option(new Option(R.string.iob_negiob, true))
.option(new Option(R.string.iob_posiob, true))
);
tasks.add(new ExamTask(R.string.breadgrams_label, R.string.blank,"breadgrams")
.option(new Option(R.string.breadgrams_grams, true))
.option(new Option(R.string.breadgrams_exchange, false))
.option(new Option(R.string.breadgrams_decay, true))
.option(new Option(R.string.breadgrams_calc, true))
.hint(new Hint(R.string.breadgrams_hint1))
);
tasks.add(new ExamTask(R.string.extendedcarbs_label, R.string.extendedcarbs_handling,"extendedcarbs")
.option(new Option(R.string.extendedcarbs_future, true))
.option(new Option(R.string.extendedcarbs_free, false))
.option(new Option(R.string.extendedcarbs_fat, true))
.option(new Option(R.string.extendedcarbs_rescue, false))
.hint(new Hint(R.string.extendedcarbs_hint1))
);
tasks.add(new ExamTask(R.string.nsclient_label, R.string.nsclient_howcanyou,"nsclient")
.option(new Option(R.string.nsclient_nightscout, true))
.option(new Option(R.string.nsclient_dexcomfollow, true))
.option(new Option(R.string.nsclient_data, true))
.option(new Option(R.string.nsclient_fullcontrol, false))
.hint(new Hint(R.string.nsclient_hint1))
);
tasks.add(new ExamTask(R.string.other_medication_label, R.string.other_medication_text,"otherMedicationWarning")
.option(new Option(R.string.yes, true))
.option(new Option(R.string.no, false))
);
for (Task task : tasks)
Collections.shuffle(((ExamTask)task).options);
}
}

View file

@ -0,0 +1,211 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective2(injector: HasAndroidInjector) : Objective(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate) {
init {
tasks.add(ExamTask(this, R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(Option(R.string.prerequisites_nightscout, true))
.option(Option(R.string.prerequisites_computer, true))
.option(Option(R.string.prerequisites_pump, true))
.option(Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(Hint(R.string.prerequisites_hint1))
)
tasks.add(ExamTask(this, R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(Option(R.string.prerequisites2_profile, true))
.option(Option(R.string.prerequisites2_device, true))
.option(Option(R.string.prerequisites2_internet, false))
.option(Option(R.string.prerequisites2_supportedcgm, true))
.hint(Hint(R.string.prerequisites2_hint1))
)
tasks.add(ExamTask(this, R.string.basaltest_label, R.string.basaltest_when, "basaltest")
.option(Option(R.string.basaltest_fixed, false))
.option(Option(R.string.basaltest_havingregularhighlow, true))
.option(Option(R.string.basaltest_weekly, false))
.option(Option(R.string.basaltest_beforeloop, true))
.hint(Hint(R.string.basaltest_hint1))
)
tasks.add(ExamTask(this, R.string.dia_label_exam, R.string.dia_whatmeansdia, "dia")
.option(Option(R.string.dia_profile, true))
.option(Option(R.string.dia_minimumis5h, true))
.option(Option(R.string.dia_meaningisequaltodiapump, false))
.option(Option(R.string.dia_valuemustbedetermined, true))
.hint(Hint(R.string.dia_hint1))
)
tasks.add(ExamTask(this, R.string.isf_label_exam, R.string.blank, "isf")
.option(Option(R.string.isf_decreasingvalue, true))
.option(Option(R.string.isf_preferences, false))
.option(Option(R.string.isf_increasingvalue, false))
.option(Option(R.string.isf_noeffect, false))
.hint(Hint(R.string.isf_hint1))
.hint(Hint(R.string.isf_hint2))
)
tasks.add(ExamTask(this, R.string.ic_label_exam, R.string.blank, "ic")
.option(Option(R.string.ic_increasingvalue, true))
.option(Option(R.string.ic_decreasingvalue, false))
.option(Option(R.string.ic_multiple, true))
.option(Option(R.string.ic_isf, false))
.hint(Hint(R.string.ic_hint1))
)
tasks.add(ExamTask(this, R.string.hypott_label, R.string.hypott_whenhypott, "hypott")
.option(Option(R.string.hypott_preventoversmb, true))
.option(Option(R.string.hypott_exercise, false))
.option(Option(R.string.hypott_wrongbasal, false))
.option(Option(R.string.hypott_0basal, false))
.hint(Hint(R.string.hypott_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch_label, R.string.profileswitch_pctwillchange, "profileswitch")
.option(Option(R.string.profileswitch_basallower, true))
.option(Option(R.string.profileswitch_isfhigher, true))
.option(Option(R.string.profileswitch_iclower, false))
.option(Option(R.string.profileswitch_unchanged, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange, "profileswitch2")
.option(Option(R.string.profileswitch2_bghigher, false))
.option(Option(R.string.profileswitch2_basalhigher, true))
.option(Option(R.string.profileswitch2_bgunchanged, true))
.option(Option(R.string.profileswitch2_isfhigher, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitchtime_label, R.string.profileswitchtime_iwant, "profileswitchtime")
.option(Option(R.string.profileswitchtime_2, false))
.option(Option(R.string.profileswitchtime__2, true))
.option(Option(R.string.profileswitchtime_tt, false))
.option(Option(R.string.profileswitchtime_100, false))
.hint(Hint(R.string.profileswitchtime_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch4_label, R.string.blank, "profileswitch4")
.option(Option(R.string.profileswitch4_rates, true))
.option(Option(R.string.profileswitch4_internet, true))
.option(Option(R.string.profileswitch4_sufficient, false))
.option(Option(R.string.profileswitch4_multi, true))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo, "exercise")
.option(Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(Option(R.string.exerciseprofile_suspendloop, false))
.option(Option(R.string.exerciseprofile_leaveat100, false))
.hint(Hint(R.string.exerciseprofile_hint1))
)
tasks.add(ExamTask(this, R.string.exercise_label, R.string.exercise_whattodo, "exercise2")
.option(Option(R.string.exercise_settt, true))
.option(Option(R.string.exercise_setfinished, false))
.option(Option(R.string.exercise_setunchanged, false))
.option(Option(R.string.exercise_15g, false))
.hint(Hint(R.string.exercise_hint1))
)
tasks.add(ExamTask(this, R.string.noisycgm_label, R.string.noisycgm_whattodo, "noisycgm")
.option(Option(R.string.noisycgm_nothing, false))
.option(Option(R.string.noisycgm_pause, true))
.option(Option(R.string.noisycgm_replacesensor, true))
.option(Option(R.string.noisycgm_checksmoothing, true))
.hint(Hint(R.string.noisycgm_hint1))
)
tasks.add(ExamTask(this, R.string.pumpdisconnect_label, R.string.blank, "pumpdisconnect")
.option(Option(R.string.pumpdisconnect_unnecessary, false))
.option(Option(R.string.pumpdisconnect_missinginsulin, true))
.option(Option(R.string.pumpdisconnect_notstop, false))
.option(Option(R.string.pumpdisconnect_openloop, false))
.hint(Hint(R.string.pumpdisconnect_hint1))
)
tasks.add(ExamTask(this, R.string.insulin_label, R.string.insulin_ultrarapid, "insulin")
.option(Option(R.string.insulin_novorapid, false))
.option(Option(R.string.insulin_humalog, false))
.option(Option(R.string.insulin_actrapid, false))
.option(Option(R.string.insulin_fiasp, true))
.hint(Hint(R.string.insulin_hint1))
)
tasks.add(ExamTask(this, R.string.sensitivity_label, R.string.blank, "sensitivity")
.option(Option(R.string.sensitivity_adjust, true))
.option(Option(R.string.sensitivity_edit, false))
.option(Option(R.string.sensitivity_cannula, true))
.option(Option(R.string.sensitivity_time, true))
.hint(Hint(R.string.sensitivity_hint1))
)
tasks.add(ExamTask(this, R.string.objectives_label, R.string.objectives_howtosave, "objectives")
.option(Option(R.string.objectives_notesettings, false))
.option(Option(R.string.objectives_afterobjective, true))
.option(Option(R.string.objectives_afterchange, true))
.option(Option(R.string.objectives_afterinitialsetup, true))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.objectives2_label, R.string.objectives_howtosave, "objectives2")
.option(Option(R.string.objectives2_maintenance, true))
.option(Option(R.string.objectives2_internalstorage, true))
.option(Option(R.string.objectives2_cloud, true))
.option(Option(R.string.objectives2_easyrestore, false))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.update_label, R.string.blank, "update")
.option(Option(R.string.update_git, true))
.option(Option(R.string.update_askfriend, false))
.option(Option(R.string.update_keys, true))
.option(Option(R.string.update_asap, true))
.hint(Hint(R.string.update_hint1))
)
tasks.add(ExamTask(this, R.string.troubleshooting_label, R.string.troubleshooting_wheretoask, "troubleshooting")
.option(Option(R.string.troubleshooting_fb, true))
.option(Option(R.string.troubleshooting_wiki, true))
.option(Option(R.string.troubleshooting_gitter, true))
.option(Option(R.string.troubleshooting_yourendo, false))
.hint(Hint(R.string.troubleshooting_hint1))
.hint(Hint(R.string.troubleshooting_hint2))
.hint(Hint(R.string.troubleshooting_hint3))
)
tasks.add(ExamTask(this, R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo, "wrongcarbs")
.option(Option(R.string.wrongcarbs_addinsulin, false))
.option(Option(R.string.wrongcarbs_treatmentstab, true))
.option(Option(R.string.wrongcarbs_donothing, false))
.option(Option(R.string.wrongcarbs_bolus, false))
)
tasks.add(ExamTask(this, R.string.wronginsulin_label, R.string.wronginsulin_whattodo, "wronginsulin")
.option(Option(R.string.wronginsulin_careportal, false))
.option(Option(R.string.wronginsulin_compare, true))
.option(Option(R.string.wronginsulin_prime, true))
.option(Option(R.string.wrongcarbs_donothing, false))
)
tasks.add(ExamTask(this, R.string.iob_label, R.string.blank, "iob")
.option(Option(R.string.iob_value, true))
.option(Option(R.string.iob_hightemp, false))
.option(Option(R.string.iob_negiob, true))
.option(Option(R.string.iob_posiob, true))
)
tasks.add(ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams")
.option(Option(R.string.breadgrams_grams, true))
.option(Option(R.string.breadgrams_exchange, false))
.option(Option(R.string.breadgrams_decay, true))
.option(Option(R.string.breadgrams_calc, true))
.hint(Hint(R.string.breadgrams_hint1))
)
tasks.add(ExamTask(this, R.string.extendedcarbs_label, R.string.extendedcarbs_handling, "extendedcarbs")
.option(Option(R.string.extendedcarbs_future, true))
.option(Option(R.string.extendedcarbs_free, false))
.option(Option(R.string.extendedcarbs_fat, true))
.option(Option(R.string.extendedcarbs_rescue, false))
.hint(Hint(R.string.extendedcarbs_hint1))
)
tasks.add(ExamTask(this, R.string.nsclient_label, R.string.nsclient_howcanyou, "nsclient")
.option(Option(R.string.nsclient_nightscout, true))
.option(Option(R.string.nsclient_dexcomfollow, true))
.option(Option(R.string.nsclient_data, true))
.option(Option(R.string.nsclient_fullcontrol, false))
.hint(Hint(R.string.nsclient_hint1))
)
tasks.add(ExamTask(this, R.string.other_medication_label, R.string.other_medication_text, "otherMedicationWarning")
.option(Option(R.string.yes, true))
.option(Option(R.string.no, false))
)
for (task in tasks) (task as ExamTask).options.shuffle()
for (task in tasks) {
if (!task.isCompleted()) accomplishedOn = 0
}
}
}

View file

@ -1,61 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import androidx.fragment.app.FragmentActivity;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective3 extends Objective {
@Inject SP sp;
@Inject ObjectivesPlugin objectivesPlugin;
@Inject ResourceHelper resourceHelper;
@Inject NSClientPlugin nsClientPlugin;
private final int MANUAL_ENACTS_NEEDED = 20;
@Inject
public Objective3(HasAndroidInjector injector) {
super(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate);
// disable option for skipping objectives for now
// hasSpecialInput = true;
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
tasks.add(new Task(R.string.objectives_manualenacts) {
@Override
public boolean isCompleted() {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED;
}
@Override
public String getProgress() {
if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED)
return resourceHelper.gs(R.string.completed_well_done);
else
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) + " / " + MANUAL_ENACTS_NEEDED;
}
});
}
@Override
public boolean specialActionEnabled() {
return NSClientService.isConnected && NSClientService.hasWriteAuth;
}
@Override
public void specialAction(FragmentActivity activity, String input) {
objectivesPlugin.completeObjectives(activity, input);
}
}

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import androidx.fragment.app.FragmentActivity
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate) {
@Inject lateinit var objectivesPlugin: ObjectivesPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
tasks.add(object : Task(this, R.string.objectives_manualenacts) {
override fun isCompleted(): Boolean {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED
}
override val progress: String
get() = if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) resourceHelper.gs(R.string.completed_well_done) else sp.getInt(R.string.key_ObjectivesmanualEnacts, 0).toString() + " / " + MANUAL_ENACTS_NEEDED
})
}
override fun specialActionEnabled(): Boolean =
NSClientService.isConnected && NSClientService.hasWriteAuth
override fun specialAction(activity: FragmentActivity, input: String) {
objectivesPlugin.completeObjectives(activity, input)
}
companion object {
private const val MANUAL_ENACTS_NEEDED = 20
}
}

View file

@ -1,11 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
public class Objective4 extends Objective {
public Objective4(HasAndroidInjector injector) {
super(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective4(injector: HasAndroidInjector) : Objective(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate)

View file

@ -1,32 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
import info.nightscout.androidaps.utils.T;
public class Objective5 extends Objective {
@Inject SafetyPlugin safetyPlugin;
public Objective5(HasAndroidInjector injector) {
super(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(5).msecs()));
tasks.add(new Task(R.string.closedmodeenabled) {
@Override
public boolean isCompleted() {
Constraint<Boolean> closedLoopEnabled = new Constraint<>(true);
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled);
return closedLoopEnabled.value();
}
});
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective5(injector: HasAndroidInjector) : Objective(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate) {
@Inject lateinit var safetyPlugin: SafetyPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(5).msecs()))
tasks.add(object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean {
val closedLoopEnabled = Constraint(true)
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled)
return closedLoopEnabled.value()
}
})
}
}

View file

@ -1,30 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.utils.T;
public class Objective6 extends Objective {
@Inject ConstraintChecker constraintChecker;
public Objective6(HasAndroidInjector injector) {
super(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(1).msecs()));
tasks.add(new Task(R.string.maxiobset) {
@Override
public boolean isCompleted() {
double maxIOB = constraintChecker.getMaxIOBAllowed().value();
return maxIOB > 0;
}
});
}
}

View file

@ -0,0 +1,23 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective6(injector: HasAndroidInjector) : Objective(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate) {
@Inject lateinit var constraintChecker: ConstraintChecker
init {
tasks.add(MinimumDurationTask(this, T.days(1).msecs()))
tasks.add(object : Task(this, R.string.maxiobset) {
override fun isCompleted(): Boolean {
val maxIOB = constraintChecker.getMaxIOBAllowed().value()
return maxIOB > 0
}
})
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective7 extends Objective {
public Objective7(HasAndroidInjector injector) {
super(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective7(injector: HasAndroidInjector) : Objective(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective8 extends Objective {
public Objective8(HasAndroidInjector injector) {
super(injector, "ama", R.string.objectives_ama_objective, 0);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective8(injector: HasAndroidInjector) : Objective(injector, "ama", R.string.objectives_ama_objective, 0) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective9 extends Objective {
public Objective9(HasAndroidInjector injector) {
super(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective9(injector: HasAndroidInjector) : Objective(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,289 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.safety;
import androidx.annotation.NonNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.BgSourceInterface;
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.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.HardLimits;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
private final SP sp;
private final RxBusWrapper rxBus;
private final ConstraintChecker constraintChecker;
private final OpenAPSAMAPlugin openAPSAMAPlugin;
private final OpenAPSSMBPlugin openAPSSMBPlugin;
private final SensitivityOref1Plugin sensitivityOref1Plugin;
private final ActivePluginProvider activePlugin;
private final HardLimits hardLimits;
private final BuildHelper buildHelper;
private final TreatmentsPlugin treatmentsPlugin;
private final Config config;
@Inject
public SafetyPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
ResourceHelper resourceHelper,
SP sp,
RxBusWrapper rxBus,
ConstraintChecker constraintChecker,
OpenAPSAMAPlugin openAPSAMAPlugin,
OpenAPSSMBPlugin openAPSSMBPlugin,
SensitivityOref1Plugin sensitivityOref1Plugin,
ActivePluginProvider activePlugin,
HardLimits hardLimits,
BuildHelper buildHelper,
TreatmentsPlugin treatmentsPlugin,
Config config
) {
super(new PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
);
this.sp = sp;
this.rxBus = rxBus;
this.constraintChecker = constraintChecker;
this.openAPSAMAPlugin = openAPSAMAPlugin;
this.openAPSSMBPlugin = openAPSSMBPlugin;
this.sensitivityOref1Plugin = sensitivityOref1Plugin;
this.activePlugin = activePlugin;
this.hardLimits = hardLimits;
this.buildHelper = buildHelper;
this.treatmentsPlugin = treatmentsPlugin;
this.config = config;
}
/**
* Constraints interface
**/
@NonNull @Override
public Constraint<Boolean> isLoopInvocationAllowed(@NonNull Constraint<Boolean> value) {
if (!activePlugin.getActivePump().getPumpDescription().isTempBasalCapable)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.pumpisnottempbasalcapable), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isClosedLoopAllowed(@NonNull Constraint<Boolean> value) {
String mode = sp.getString(R.string.key_aps_mode, "open");
if ((mode.equals("open")))
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closedmodedisabledinpreferences), this);
if (!buildHelper.isEngineeringModeOrRelease()) {
if (value.value()) {
Notification n = new Notification(Notification.TOAST_ALARM, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL);
rxBus.send(new EventNewNotification(n));
}
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), this);
}
PumpInterface pump = activePlugin.getActivePump();
if (!pump.isFakingTempsByExtendedBoluses() && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) {
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_with_eb), this);
}
return value;
}
@NonNull @Override
public Constraint<Boolean> isAutosensModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_openapsama_useautosens, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.autosensdisabledinpreferences), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isSMBModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_smb, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbdisabledinpreferences), this);
Constraint<Boolean> closedLoop = constraintChecker.isClosedLoopAllowed();
if (!closedLoop.value())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbnotallowedinopenloopmode), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isUAMEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_uam, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledinpreferences), this);
boolean oref1Enabled = sensitivityOref1Plugin.isEnabled(PluginType.SENSITIVITY);
if (!oref1Enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledoref1notselected), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isAdvancedFilteringEnabled(@NonNull Constraint<Boolean> value) {
BgSourceInterface bgSource = activePlugin.getActiveBgSource();
if (!bgSource.advancedFilteringSupported())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbalwaysdisabled), this);
return value;
}
@NonNull @Override
public Constraint<Double> applyBasalConstraints(Constraint<Double> absoluteRate, @NonNull Profile profile) {
absoluteRate.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbasalratio), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
if (config.getAPS()) {
double maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1d);
if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal();
absoluteRate.addReason(getResourceHelper().gs(R.string.increasingmaxbasal), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), maxBasal, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxBasal, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
// Check percentRate but absolute rate too, because we know real current basal in pump
double maxBasalMult = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d);
double maxFromBasalMult = Math.floor(maxBasalMult * profile.getBasal() * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromBasalMult, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromBasalMult, getResourceHelper().gs(R.string.maxbasalmultiplier)), this);
double maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3d);
double maxFromDaily = Math.floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromDaily, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromDaily, getResourceHelper().gs(R.string.maxdailybasalmultiplier)), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), hardLimits.maxBasal(), String.format(getResourceHelper().gs(R.string.limitingbasalratio), hardLimits.maxBasal(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
// check for pump max
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
absoluteRate.setIfSmaller(getAapsLogger(), pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
// do rounding
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
absoluteRate.set(getAapsLogger(), Round.roundTo(absoluteRate.value(), pump.getPumpDescription().tempAbsoluteStep));
}
return absoluteRate;
}
@NonNull @Override
public Constraint<Integer> applyBasalPercentConstraints(Constraint<Integer> percentRate, Profile profile) {
Double currentBasal = profile.getBasal();
double absoluteRate = currentBasal * ((double) percentRate.originalValue() / 100);
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this);
Constraint<Double> absoluteConstraint = new Constraint<>(absoluteRate);
applyBasalConstraints(absoluteConstraint, profile);
percentRate.copyReasons(absoluteConstraint);
PumpInterface pump = activePlugin.getActivePump();
int percentRateAfterConst = Double.valueOf(absoluteConstraint.value() / currentBasal * 100).intValue();
if (percentRateAfterConst < 100)
percentRateAfterConst = Round.ceilTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
else
percentRateAfterConst = Round.floorTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
percentRate.set(getAapsLogger(), percentRateAfterConst, String.format(getResourceHelper().gs(R.string.limitingpercentrate), percentRateAfterConst, getResourceHelper().gs(R.string.pumplimit)), this);
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
percentRate.setIfSmaller(getAapsLogger(), (int) pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
return percentRate;
}
@NonNull @Override
public Constraint<Double> applyBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Double> applyExtendedBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectExtendedBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Integer> applyCarbsConstraints(Constraint<Integer> carbs) {
carbs.setIfGreater(getAapsLogger(), 0, String.format(getResourceHelper().gs(R.string.limitingcarbs), 0, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Integer maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48);
carbs.setIfSmaller(getAapsLogger(), maxCarbs, String.format(getResourceHelper().gs(R.string.limitingcarbs), maxCarbs, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
return carbs;
}
@NonNull @Override
public Constraint<Double> applyMaxIOBConstraints(@NonNull Constraint<Double> maxIob) {
double maxIobPref;
String apsmode = sp.getString(R.string.key_aps_mode, "open");
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIobPref = sp.getDouble(R.string.key_openapssmb_max_iob, 3d);
else
maxIobPref = sp.getDouble(R.string.key_openapsma_max_iob, 1.5d);
maxIob.setIfSmaller(getAapsLogger(), maxIobPref, String.format(getResourceHelper().gs(R.string.limitingiob), maxIobPref, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
if (openAPSAMAPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobAMA(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobAMA(), getResourceHelper().gs(R.string.hardlimit)), this);
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobSMB(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobSMB(), getResourceHelper().gs(R.string.hardlimit)), this);
if ((apsmode.equals("lgs")))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.getMAXIOB_LGS(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.getMAXIOB_LGS(), getResourceHelper().gs(R.string.lowglucosesuspend)), this);
return maxIob;
}
}

View file

@ -0,0 +1,195 @@
package info.nightscout.androidaps.plugins.constraints.safety
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
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.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.floor
@Singleton
class SafetyPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val constraintChecker: ConstraintChecker,
private val openAPSAMAPlugin: OpenAPSAMAPlugin,
private val openAPSSMBPlugin: OpenAPSSMBPlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val activePlugin: ActivePluginProvider,
private val hardLimits: HardLimits,
private val buildHelper: BuildHelper,
private val treatmentsPlugin: TreatmentsPlugin,
private val config: Config
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
/**
* Constraints interface
*/
override fun isLoopInvocationAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!activePlugin.activePump.pumpDescription.isTempBasalCapable) value[aapsLogger, false, resourceHelper.gs(R.string.pumpisnottempbasalcapable)] = this
return value
}
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
val mode = sp.getString(R.string.key_aps_mode, "open")
if (mode == "open") value[aapsLogger, false, resourceHelper.gs(R.string.closedmodedisabledinpreferences)] = this
if (!buildHelper.isEngineeringModeOrRelease()) {
if (value.value()) {
val n = Notification(Notification.TOAST_ALARM, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL)
rxBus.send(EventNewNotification(n))
}
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch)] = this
}
val pump = activePlugin.activePump
if (!pump.isFakingTempsByExtendedBoluses && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_with_eb)] = this
}
return value
}
override fun isAutosensModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_openapsama_useautosens, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.autosensdisabledinpreferences)] = this
return value
}
override fun isSMBModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_use_smb, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.smbdisabledinpreferences)] = this
val closedLoop = constraintChecker.isClosedLoopAllowed()
if (!closedLoop.value()) value[aapsLogger, false, resourceHelper.gs(R.string.smbnotallowedinopenloopmode)] = this
return value
}
override fun isUAMEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_use_uam, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.uamdisabledinpreferences)] = this
val oref1Enabled = sensitivityOref1Plugin.isEnabled(PluginType.SENSITIVITY)
if (!oref1Enabled) value[aapsLogger, false, resourceHelper.gs(R.string.uamdisabledoref1notselected)] = this
return value
}
override fun isAdvancedFilteringEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val bgSource = activePlugin.activeBgSource
if (!bgSource.advancedFilteringSupported()) value[aapsLogger, false, resourceHelper.gs(R.string.smbalwaysdisabled)] = this
return value
}
override fun applyBasalConstraints(absoluteRate: Constraint<Double>, profile: Profile): Constraint<Double> {
absoluteRate.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingbasalratio), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
if (config.APS) {
var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0)
if (maxBasal < profile.maxDailyBasal) {
maxBasal = profile.maxDailyBasal
absoluteRate.addReason(resourceHelper.gs(R.string.increasingmaxbasal), this)
}
absoluteRate.setIfSmaller(aapsLogger, maxBasal, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxBasal, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
// Check percentRate but absolute rate too, because we know real current basal in pump
val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)
val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.basal * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromBasalMultiplier, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromBasalMultiplier, resourceHelper.gs(R.string.maxbasalmultiplier)), this)
val maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3.0)
val maxFromDaily = floor(profile.maxDailyBasal * maxBasalFromDaily * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromDaily, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromDaily, resourceHelper.gs(R.string.maxdailybasalmultiplier)), this)
}
absoluteRate.setIfSmaller(aapsLogger, hardLimits.maxBasal(), String.format(resourceHelper.gs(R.string.limitingbasalratio), hardLimits.maxBasal(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
// check for pump max
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
absoluteRate.setIfSmaller(aapsLogger, pumpLimit, String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
// do rounding
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
absoluteRate[aapsLogger] = Round.roundTo(absoluteRate.value(), pump.pumpDescription.tempAbsoluteStep)
}
return absoluteRate
}
override fun applyBasalPercentConstraints(percentRate: Constraint<Int>, profile: Profile): Constraint<Int> {
val currentBasal = profile.basal
val absoluteRate = currentBasal * (percentRate.originalValue().toDouble() / 100)
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this)
val absoluteConstraint = Constraint(absoluteRate)
applyBasalConstraints(absoluteConstraint, profile)
percentRate.copyReasons(absoluteConstraint)
val pump = activePlugin.activePump
var percentRateAfterConst = java.lang.Double.valueOf(absoluteConstraint.value() / currentBasal * 100).toInt()
percentRateAfterConst = if (percentRateAfterConst < 100) Round.ceilTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() else Round.floorTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt()
percentRate[aapsLogger, percentRateAfterConst, String.format(resourceHelper.gs(R.string.limitingpercentrate), percentRateAfterConst, resourceHelper.gs(R.string.pumplimit))] = this
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
return percentRate
}
override fun applyBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingbolus), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(resourceHelper.gs(R.string.limitingbolus), maxBolus, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(resourceHelper.gs(R.string.limitingbolus), hardLimits.maxBolus(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, resourceHelper.gs(R.string.pumplimit), this)
return insulin
}
override fun applyExtendedBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingextendedbolus), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(resourceHelper.gs(R.string.limitingextendedbolus), maxBolus, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(resourceHelper.gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectExtendedBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, resourceHelper.gs(R.string.pumplimit), this)
return insulin
}
override fun applyCarbsConstraints(carbs: Constraint<Int>): Constraint<Int> {
carbs.setIfGreater(aapsLogger, 0, String.format(resourceHelper.gs(R.string.limitingcarbs), 0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48)
carbs.setIfSmaller(aapsLogger, maxCarbs, String.format(resourceHelper.gs(R.string.limitingcarbs), maxCarbs, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
return carbs
}
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
val apsMode = sp.getString(R.string.key_aps_mode, "open")
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled(PluginType.APS)) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5)
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(resourceHelper.gs(R.string.limitingiob), maxIobPref, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
if (openAPSAMAPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this)
if (openAPSSMBPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this)
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, hardLimits.MAXIOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.MAXIOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
return maxIob
}
}

View file

@ -33,7 +33,6 @@ import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
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
@ -42,6 +41,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.ui.SingleClickButton
import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
@ -158,12 +158,7 @@ class ActionsFragment : DaggerFragment() {
commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), R.raw.boluserror)
}
}
})
@ -178,12 +173,7 @@ class ActionsFragment : DaggerFragment() {
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
}
}
})

View file

@ -1,12 +1,10 @@
package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.StringRes
import com.dpro.widgets.WeekdaysPicker
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.ui.WeekdayPicker
import java.util.*
class InputWeekDay(injector: HasAndroidInjector) : Element(injector) {
@ -78,13 +76,10 @@ class InputWeekDay(injector: HasAndroidInjector) : Element(injector) {
}
override fun addToLayout(root: LinearLayout) {
val weekdaysPicker = WeekdaysPicker(root.context)
weekdaysPicker.setEditable(true)
weekdaysPicker.selectedDays = getSelectedDays()
weekdaysPicker.setOnWeekdaysChangeListener { _: View?, i: Int, list: List<Int?> -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)) }
weekdaysPicker.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
weekdaysPicker.sundayFirstDay = Calendar.getInstance().firstDayOfWeek == Calendar.SUNDAY
weekdaysPicker.redrawDays()
root.addView(weekdaysPicker)
WeekdayPicker(root.context).apply {
setSelectedDays(getSelectedDays())
setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) }
root.addView(this)
}
}
}

View file

@ -129,7 +129,7 @@ class NSSettingsStatus @Inject constructor(
fun handleNewData(nightscoutVersionName: String, nightscoutVersionCode: Int, status: JSONObject) {
this.nightscoutVersionName = nightscoutVersionName
aapsLogger.debug(LTag.NSCLIENT, "Got versions: Nightscout: $nightscoutVersionName")
if (nightscoutVersionCode < config.SUPPORTEDNSVERSION) {
if (nightscoutVersionCode != 0 && nightscoutVersionCode < config.SUPPORTEDNSVERSION) {
val notification = Notification(Notification.OLD_NS, resourceHelper.gs(R.string.unsupportednsversion), Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
} else {

View file

@ -43,7 +43,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
@ -99,7 +99,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator
@Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var notificationStore: NotificationStore
@Inject lateinit var actionStringHandler: ActionStringHandler
@Inject lateinit var quickWizard: QuickWizard
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var commandQueue: CommandQueue
@ -346,7 +345,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
uel.log("ACCEPT TEMP BASAL")
binding.buttonsLayout.acceptTempButton.visibility = View.GONE
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID)
actionStringHandler.handleInitiate("cancelChangeRequest")
rxBus.send(EventWearDoAction("cancelChangeRequest"))
loopPlugin.acceptChangeRequest()
})
})

View file

@ -8,6 +8,10 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.dana.DanaPump
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.CareportalEvent
@ -18,23 +22,23 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
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.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
@ -46,6 +50,7 @@ import javax.inject.Singleton
class ActionStringHandler @Inject constructor(
private val sp: SP,
private val rxBus: RxBusWrapper,
private val aapsSchedulers: AapsSchedulers,
private val resourceHelper: ResourceHelper,
private val injector: HasAndroidInjector,
private val context: Context,
@ -53,6 +58,7 @@ class ActionStringHandler @Inject constructor(
private val profileFunction: ProfileFunction,
private val loopPlugin: LoopPlugin,
private val wearPlugin: WearPlugin,
private val fabricPrivacy: FabricPrivacy,
private val commandQueue: CommandQueueProvider,
private val activePlugin: ActivePluginProvider,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
@ -73,10 +79,18 @@ class ActionStringHandler @Inject constructor(
private var lastConfirmActionString: String? = null
private var lastBolusWizard: BolusWizard? = null
// TODO Adrian use RxBus instead of Lazy + cross dependency
private val disposable = CompositeDisposable()
init {
disposable += rxBus
.toObservable(EventWearDoAction::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ handleInitiate(it.action) }, fabricPrivacy::logException)
}
@Synchronized
fun handleInitiate(actionString: String) {
if (!sp.getBoolean("wearcontrol", false)) return
private fun handleInitiate(actionString: String) {
if (!sp.getBoolean(R.string.key_wear_control, false)) return
lastBolusWizard = null
var rTitle = "CONFIRM" //TODO: i18n
var rMessage = ""
@ -459,7 +473,7 @@ class ActionStringHandler @Inject constructor(
@Synchronized
fun handleConfirmation(actionString: String) {
if (!sp.getBoolean("wearcontrol", false)) return
if (!sp.getBoolean(R.string.key_wear_control, false)) return
//Guard from old or duplicate confirmations
if (lastConfirmActionString == null) return
if (lastConfirmActionString != actionString) return
@ -614,13 +628,5 @@ class ActionStringHandler @Inject constructor(
lastSentTimestamp = System.currentTimeMillis()
lastConfirmActionString = null
lastBolusWizard = null
} /*
public synchronized static void expectNotificationAction(String message, int id) {
String actionstring = "dismissoverviewnotification " + id;
WearPlugin.getPlugin().requestActionConfirmation("DISMISS", message, actionstring);
lastSentTimestamp = System.currentTimeMillis();
lastConfirmActionString = actionstring;
lastBolusWizard = null;
}
*/
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.general.wear.events
import info.nightscout.androidaps.events.Event
class EventWearDoAction (val action: String) : Event()

View file

@ -45,9 +45,10 @@ import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
@ -65,6 +66,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
@Inject public WearPlugin wearPlugin;
@Inject public ResourceHelper resourceHelper;
@Inject public SP sp;
@Inject public RxBusWrapper rxBus;
@Inject public ProfileFunction profileFunction;
@Inject public DefaultValueHelper defaultValueHelper;
@Inject public NSDeviceStatus nsDeviceStatus;
@ -72,7 +74,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
@Inject public LoopPlugin loopPlugin;
@Inject public IobCobCalculatorPlugin iobCobCalculatorPlugin;
@Inject public TreatmentsPlugin treatmentsPlugin;
@Inject public ActionStringHandler actionStringHandler;
@Inject public AppRepository repository;
@Inject ReceiverStatusStore receiverStatusStore;
@Inject Config config;
@ -260,13 +261,13 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (event != null && event.getPath().equals(WEARABLE_INITIATE_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData());
aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring);
actionStringHandler.handleInitiate(actionstring);
rxBus.send(new EventWearDoAction(actionstring));
}
if (event != null && event.getPath().equals(WEARABLE_CONFIRM_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData());
aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring);
actionStringHandler.handleConfirmation(actionstring);
rxBus.send(new EventWearDoAction(actionstring));
}
}
}
@ -288,10 +289,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (wearIntegration()) {
final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus);
if (dataMap == null) {
ToastUtils.showToastInUiThread(this, resourceHelper.gs(R.string.noprofile));
return;
}
(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataMap);
}
@ -743,12 +740,12 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendPreferences() {
if (googleApiClient != null && googleApiClient.isConnected()) {
boolean wearcontrol = sp.getBoolean("wearcontrol", false);
boolean wearcontrol = sp.getBoolean(R.string.key_wear_control, false);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putBoolean("wearcontrol", wearcontrol);
dataMapRequest.getDataMap().putBoolean(resourceHelper.gs(R.string.key_wear_control), wearcontrol);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.treatments
import android.content.Context
import android.content.Intent
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
@ -47,12 +46,7 @@ class CarbsGenerator @Inject constructor(
commandQueue.bolus(carbInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.treatments;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
@ -679,12 +678,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
String status = String.format(resourceHelper.gs(R.string.error_adding_treatment_message), treatment.insulin, (int) treatment.carbs, dateUtil.dateAndTimeString(treatment.date));
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", R.raw.error);
i.putExtra("title", resourceHelper.gs(R.string.error_adding_treatment_title));
i.putExtra("status", status);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
ErrorHelperActivity.Companion.runAlarm(context, status, resourceHelper.gs(R.string.error_adding_treatment_title), R.raw.error);
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_LIST_ID, "TreatmentClash");

View file

@ -116,12 +116,7 @@ class CommandQueue @Inject constructor(
setProfile(it, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.failedupdatebasalprofile))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.failedupdatebasalprofile), R.raw.boluserror)
}
if (result.enacted) rxBus.send(EventNewBasalProfile())
}

View file

@ -74,11 +74,11 @@ class SWDefinition @Inject constructor(
}
private val screenSetupWizard = SWScreen(injector, R.string.nav_setupwizard)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.welcometosetupwizard))
private val screenEula = SWScreen(injector, R.string.end_user_license_agreement)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.end_user_license_agreement_text))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -112,7 +112,7 @@ class SWDefinition @Inject constructor(
.comment(R.string.high_mark_comment))
private val screenPermissionBattery = SWScreen(injector, R.string.permission)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(resourceHelper.gs(R.string.needwhitelisting, resourceHelper.gs(R.string.app_name))))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -123,7 +123,7 @@ class SWDefinition @Inject constructor(
.validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) }
private val screenPermissionBt = SWScreen(injector, R.string.permission)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(resourceHelper.gs(R.string.needlocationpermission)))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -134,7 +134,7 @@ class SWDefinition @Inject constructor(
.validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION) }
private val screenPermissionStore = SWScreen(injector, R.string.permission)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(resourceHelper.gs(R.string.needstoragepermission)))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -144,7 +144,7 @@ class SWDefinition @Inject constructor(
.visibility { androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }
.validator { !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }
private val screenImport = SWScreen(injector, R.string.nav_import)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.storedsettingsfound))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -153,7 +153,7 @@ class SWDefinition @Inject constructor(
.visibility { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) }
private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title)
.skippable(true)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.nsclientinfotext))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -183,19 +183,19 @@ class SWDefinition @Inject constructor(
.visibility { !(nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth) }
private val screenPatientName = SWScreen(injector, R.string.patient_name)
.skippable(true)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.patient_name_summary))
.add(SWEditString(injector)
.validator(SWTextValidator(String::isNotEmpty))
.preferenceId(R.string.key_patient_name))
private val screenMasterPassword = SWScreen(injector, R.string.master_password)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.master_password))
.add(SWEditEncryptedPassword(injector, cryptoUtil)
.preferenceId(R.string.key_master_password))
.add(SWBreak(injector))
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.master_password_summary))
.validator { !cryptoUtil.checkPassword("", sp.getString(R.string.key_master_password, "")) }
private val screenAge = SWScreen(injector, R.string.patientage)
@ -206,7 +206,20 @@ class SWDefinition @Inject constructor(
.preferenceId(R.string.key_age)
.label(R.string.patientage)
.comment(R.string.patientage_summary))
.validator { sp.contains(R.string.key_age) }
.add(SWBreak(injector))
.add(SWEditNumber(injector, 3.0, 0.1, 25.0)
.preferenceId(R.string.key_treatmentssafety_maxbolus)
.updateDelay(5)
.label(R.string.treatmentssafety_maxbolus_title)
.comment(R.string.common_values))
.add(SWEditNumber(injector, 48.0, 1.0, 100.0)
.preferenceId(R.string.key_treatmentssafety_maxcarbs)
.updateDelay(5)
.label(R.string.treatmentssafety_maxcarbs_title)
.comment(R.string.common_values))
.validator { sp.contains(R.string.key_age)
&& sp.getDouble(R.string.key_treatmentssafety_maxbolus, 0.0) > 0
&& sp.getDouble(R.string.key_treatmentssafety_maxcarbs, 0.0) > 0 }
private val screenInsulin = SWScreen(injector, R.string.configbuilder_insulin)
.skippable(false)
.add(SWPlugin(injector, this)
@ -214,7 +227,7 @@ class SWDefinition @Inject constructor(
.makeVisible(false)
.label(R.string.configbuilder_insulin))
.add(SWBreak(injector))
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.diawarning))
private val screenBgSource = SWScreen(injector, R.string.configbuilder_bgsource)
.skippable(false)
@ -224,7 +237,7 @@ class SWDefinition @Inject constructor(
.add(SWBreak(injector))
private val screenProfile = SWScreen(injector, R.string.configbuilder_profile)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.setupwizard_profile_description))
.add(SWBreak(injector))
.add(SWPlugin(injector, this)
@ -232,7 +245,7 @@ class SWDefinition @Inject constructor(
.label(R.string.configbuilder_profile))
private val screenNsProfile = SWScreen(injector, R.string.nsprofile)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.adjustprofileinns))
.add(SWFragment(injector, this)
.add(NSProfileFragment()))
@ -246,7 +259,7 @@ class SWDefinition @Inject constructor(
.visibility { localProfilePlugin.isEnabled(PluginType.PROFILE) }
private val screenProfileSwitch = SWScreen(injector, R.string.careportal_profileswitch)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.profileswitch_ismissing))
.add(SWButton(injector)
.text(R.string.doprofileswitch)
@ -259,11 +272,11 @@ class SWDefinition @Inject constructor(
.option(PluginType.PUMP, R.string.configbuilder_pump_description)
.label(R.string.configbuilder_pump))
.add(SWBreak(injector))
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.setupwizard_pump_pump_not_initialized)
.visibility { !isPumpInitialized() })
.add( // Omnipod only
SWInfotext(injector)
SWInfoText(injector)
.label(R.string.setupwizard_pump_waiting_for_riley_link_connection)
.visibility {
val activePump = activePlugin.activePump
@ -295,7 +308,7 @@ class SWDefinition @Inject constructor(
private val screenAps = SWScreen(injector, R.string.configbuilder_aps)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.setupwizard_aps_description))
.add(SWBreak(injector))
.add(SWPlugin(injector, this)
@ -314,7 +327,7 @@ class SWDefinition @Inject constructor(
.validator { sp.contains(R.string.key_aps_mode) }
private val screenLoop = SWScreen(injector, R.string.configbuilder_loop)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.setupwizard_loop_description))
.add(SWBreak(injector))
.add(SWButton(injector)
@ -328,7 +341,7 @@ class SWDefinition @Inject constructor(
.visibility { !loopPlugin.isEnabled(PluginType.LOOP) && config.APS }
private val screenSensitivity = SWScreen(injector, R.string.configbuilder_sensitivity)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.setupwizard_sensitivity_description))
.add(SWHtmlLink(injector)
.label(R.string.setupwizard_sensitivity_url))
@ -338,7 +351,7 @@ class SWDefinition @Inject constructor(
.label(R.string.configbuilder_sensitivity))
private val getScreenObjectives = SWScreen(injector, R.string.objectives)
.skippable(false)
.add(SWInfotext(injector)
.add(SWInfoText(injector)
.label(R.string.startobjective))
.add(SWBreak(injector))
.add(SWFragment(injector, this)

View file

@ -6,7 +6,7 @@ import android.widget.TextView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.setupwizard.SWValidator
class SWBreak(injector: HasAndroidInjector) : SWItem(injector, Type.TEXT) {
class SWBreak(injector: HasAndroidInjector) : SWItem(injector, Type.BREAK) {
private var l: TextView? = null
private var visibilityValidator: SWValidator? = null

View file

@ -0,0 +1,60 @@
package info.nightscout.androidaps.setupwizard.elements
import android.graphics.Typeface
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.setupwizard.SWNumberValidator
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ui.NumberPicker
import java.text.DecimalFormat
class SWEditNumber(injector: HasAndroidInjector, private val init: Double, private val min: Double, private val max: Double) : SWItem(injector, Type.DECIMAL_NUMBER) {
private val validator: SWNumberValidator = SWNumberValidator { value -> value in min..max }
private var updateDelay = 0
override fun generateDialog(layout: LinearLayout) {
val context = layout.context
val watcher: TextWatcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (validator.isValid(SafeParse.stringToDouble(s.toString())))
save(s.toString(), updateDelay.toLong())
}
override fun afterTextChanged(s: Editable) {}
}
val l = TextView(context)
l.id = View.generateViewId()
label?.let { l.setText(it) }
l.setTypeface(l.typeface, Typeface.BOLD)
layout.addView(l)
val initValue = sp.getDouble(preferenceId, init)
val numberPicker = NumberPicker(context)
numberPicker.setParams(initValue, min, max, 0.1, DecimalFormat("0.0"), false, null, watcher)
layout.addView(numberPicker)
val c = TextView(context)
c.id = View.generateViewId()
comment?.let { c.setText(it) }
c.setTypeface(c.typeface, Typeface.ITALIC)
layout.addView(c)
super.generateDialog(layout)
}
fun preferenceId(preferenceId: Int): SWEditNumber {
this.preferenceId = preferenceId
return this
}
fun updateDelay(updateDelay: Int): SWEditNumber {
this.updateDelay = updateDelay
return this
}
}

View file

@ -16,11 +16,11 @@ import info.nightscout.androidaps.utils.SafeParse
import java.text.DecimalFormat
import javax.inject.Inject
class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Double, private val min: Double, private val max: Double) : SWItem(injector, Type.UNITNUMBER) {
class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Double, private val min: Double, private val max: Double) : SWItem(injector, Type.UNIT_NUMBER) {
@Inject lateinit var profileFunction: ProfileFunction
private val validator: SWNumberValidator? = SWNumberValidator { value -> value >= min && value <= max }
private val validator: SWNumberValidator = SWNumberValidator { value -> value in min..max }
private var updateDelay = 0
override fun generateDialog(layout: LinearLayout) {
@ -28,7 +28,7 @@ class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Doub
val watcher: TextWatcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (validator != null && validator.isValid(SafeParse.stringToDouble(s.toString())))
if (validator.isValid(SafeParse.stringToDouble(s.toString())))
save(s.toString(), updateDelay.toLong())
}

View file

@ -38,7 +38,7 @@ class SWEditUrl(injector: HasAndroidInjector) : SWItem(injector, Type.URL) {
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (Patterns.WEB_URL.matcher(s).matches()) save(s.toString(), updateDelay.toLong()) else rxBus.send(EventSWLabel(resourceHelper.gs(R.string.error_url_not_valid)))
if (Patterns.WEB_URL.matcher(s).matches()) save(s.toString(), updateDelay) else rxBus.send(EventSWLabel(resourceHelper.gs(R.string.error_url_not_valid)))
}
override fun afterTextChanged(s: Editable) {}

View file

@ -8,7 +8,7 @@ import androidx.annotation.StringRes
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.setupwizard.SWValidator
class SWHtmlLink(injector: HasAndroidInjector) : SWItem(injector, Type.HTMLLINK) {
class SWHtmlLink(injector: HasAndroidInjector) : SWItem(injector, Type.HTML_LINK) {
private var textLabel: String? = null
private var l: TextView? = null

View file

@ -6,22 +6,22 @@ import android.widget.TextView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.setupwizard.SWValidator
class SWInfotext(injector: HasAndroidInjector) : SWItem(injector, Type.TEXT) {
class SWInfoText(injector: HasAndroidInjector) : SWItem(injector, Type.TEXT) {
private var textLabel: String? = null
private var l: TextView? = null
private var visibilityValidator: SWValidator? = null
override fun label(label: Int): SWInfotext {
override fun label(label: Int): SWInfoText {
this.label = label
return this
}
fun label(newLabel: String): SWInfotext {
fun label(newLabel: String): SWInfoText {
textLabel = newLabel
return this
}
fun visibility(visibilityValidator: SWValidator): SWInfotext {
fun visibility(visibilityValidator: SWValidator): SWInfoText {
this.visibilityValidator = visibilityValidator
return this
}

View file

@ -27,25 +27,18 @@ open class SWItem(val injector: HasAndroidInjector, var type: Type) {
private var scheduledEventPost: ScheduledFuture<*>? = null
init {
@Suppress("LeakingThis")
injector.androidInjector().inject(this)
}
enum class Type {
NONE, TEXT, HTMLLINK, BREAK, LISTENER, URL, STRING, NUMBER, DECIMALNUMBER, CHECKBOX, RADIOBUTTON, PLUGIN, BUTTON, FRAGMENT, UNITNUMBER
NONE, TEXT, HTML_LINK, BREAK, LISTENER, URL, STRING, NUMBER, DECIMAL_NUMBER, RADIOBUTTON, PLUGIN, BUTTON, FRAGMENT, UNIT_NUMBER
}
var label: Int? = null
var comment: Int? = null
var preferenceId = 0
fun getLabel(): String {
return label?.let { resourceHelper.gs(it) } ?: ""
}
fun getComment(): String {
return comment?.let { resourceHelper.gs(it) } ?: ""
}
open fun label(@StringRes label: Int): SWItem {
this.label = label
return this
@ -72,6 +65,7 @@ open class SWItem(val injector: HasAndroidInjector, var type: Type) {
fun scheduleChange(updateDelay: Long) {
class PostRunnable : Runnable {
override fun run() {
aapsLogger.debug(LTag.CORE, "Firing EventPreferenceChange")
rxBus.send(EventPreferenceChange(resourceHelper, preferenceId))

View file

@ -89,7 +89,7 @@ class SWPlugin(injector: HasAndroidInjector, val definition: SWDefinition) : SWI
super.generateDialog(layout)
}
fun addConfiguration(layout: LinearLayout, plugin: PluginBase) {
private fun addConfiguration(layout: LinearLayout, plugin: PluginBase) {
if (plugin.preferencesId != -1) {
fragment = MyPreferenceFragment()
fragment?.arguments = Bundle().also { it.putInt("id", plugin.preferencesId) }

View file

@ -29,12 +29,12 @@ class SWRadioButton(injector: HasAndroidInjector) : SWItem(injector, Type.RADIOB
override fun generateDialog(layout: LinearLayout) {
val context = layout.context
val pdesc = TextView(context)
pdesc.text = getComment()
val desc = TextView(context)
comment?.let { desc.setText(it) }
val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.setMargins(0, 0, 0, 40)
pdesc.layoutParams = params
layout.addView(pdesc)
desc.layoutParams = params
layout.addView(desc)
// Get if there is already value in SP
val previousValue = sp.getString(preferenceId, "none")
@ -43,12 +43,12 @@ class SWRadioButton(injector: HasAndroidInjector) : SWItem(injector, Type.RADIOB
radioGroup?.orientation = LinearLayout.VERTICAL
radioGroup?.visibility = View.VISIBLE
for (i in labels().indices) {
val rdbtn = RadioButton(context)
rdbtn.id = View.generateViewId()
rdbtn.text = labels()[i]
if (previousValue == values()[i]) rdbtn.isChecked = true
rdbtn.tag = i
radioGroup!!.addView(rdbtn)
val rdBtn = RadioButton(context)
rdBtn.id = View.generateViewId()
rdBtn.text = labels()[i]
if (previousValue == values()[i]) rdBtn.isChecked = true
rdBtn.tag = i
radioGroup!!.addView(rdBtn)
}
radioGroup!!.setOnCheckedChangeListener { group: RadioGroup, checkedId: Int ->
val i = group.findViewById<View>(checkedId).tag as Int

View file

@ -351,12 +351,7 @@ class BolusWizard @Inject constructor(
commandQueue.bolus(this, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
} else
carbTimer.scheduleEatReminder()
}
@ -384,12 +379,7 @@ class BolusWizard @Inject constructor(
commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
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)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
}
}
})
@ -399,9 +389,9 @@ class BolusWizard @Inject constructor(
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.putExtra(ErrorHelperActivity.SOUND_ID, R.raw.boluserror)
i.putExtra(ErrorHelperActivity.STATUS, result.comment)
i.putExtra(ErrorHelperActivity.TITLE, resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
}
@ -425,12 +415,7 @@ class BolusWizard @Inject constructor(
commandQueue.bolus(this, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
}
}
})

View file

@ -420,6 +420,7 @@
<string name="basal_shortname">BAZ</string>
<string name="deviation_shortname">ODCH</string>
<string name="activity_shortname">AKT</string>
<string name="bgi_shortname">-BGI</string>
<string name="abs_insulin_shortname">ABS</string>
<string name="devslope_shortname">DEVSLOPE</string>
<string name="nav_about">O aplikaci</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Automaticky doplňovat chybějící glykémie z NS</string>
<string name="loop_smbsetbypump_label">SMB provedené pumpou</string>
<string name="overview_show_activity">Aktivita</string>
<string name="overview_show_bgi">Vliv na hladinu glukózy</string>
<string name="overview_show_sensitivity">Citlivost</string>
<string name="overview_show_deviations">Odchylky</string>
<string name="overview_show_cob">Zbývající sacharidy</string>
@ -1156,4 +1158,7 @@
<string name="graph_menu_divider_header">Graf</string>
<string name="chart_menu">Možnosti grafu</string>
<string name="clear_filter">Vymazat filtr</string>
<string name="trend_arrow">Šipka trendu</string>
<string name="cannula">Kanyla</string>
<string name="userentry">Vstup uživatele</string>
</resources>

View file

@ -420,6 +420,7 @@
<string name="basal_shortname">BAS</string>
<string name="deviation_shortname">DEV</string>
<string name="activity_shortname">ACT</string>
<string name="bgi_shortname">-BGI</string>
<string name="abs_insulin_shortname">ABS</string>
<string name="devslope_shortname">DEVSLOPE</string>
<string name="nav_about">Über</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Lade fehlende Blutzuckerwerte automatisch aus Nightscout nach.</string>
<string name="loop_smbsetbypump_label">SMB von der Pumpe abgegeben</string>
<string name="overview_show_activity">Aktivität</string>
<string name="overview_show_bgi">Blutzuckerwirkung</string>
<string name="overview_show_sensitivity">Sensitivität</string>
<string name="overview_show_deviations">Abweichungen</string>
<string name="overview_show_cob">Aktive Kohlenhydrate</string>
@ -1157,4 +1159,7 @@ Unerwartetes Verhalten.</string>
<string name="graph_menu_divider_header">Diagramm</string>
<string name="chart_menu">Diagrammmenü</string>
<string name="clear_filter">Filter löschen</string>
<string name="trend_arrow">Trendpfeil</string>
<string name="cannula">Kanüle</string>
<string name="userentry">Benutzereingabe</string>
</resources>

View file

@ -421,6 +421,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S
<string name="basal_shortname">BAS</string>
<string name="deviation_shortname">DEV</string>
<string name="activity_shortname">ACT</string>
<string name="bgi_shortname">-IGly</string>
<string name="abs_insulin_shortname">ABS</string>
<string name="devslope_shortname">PENTEDEV</string>
<string name="nav_about">À propos</string>
@ -628,6 +629,7 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S
<string name="ns_autobackfill_summary">Remplissage automatique des glycémies manquantes en utilisant NS</string>
<string name="loop_smbsetbypump_label">SMB défini par la pompe</string>
<string name="overview_show_activity">Activité</string>
<string name="overview_show_bgi">Impact glycémique</string>
<string name="overview_show_sensitivity">Sensibilité</string>
<string name="overview_show_deviations">Déviations</string>
<string name="overview_show_cob">Glucides actifs</string>
@ -1159,4 +1161,5 @@ L\'ENSEMBLE DES RISQUES LIÉS À LA QUALITÉ ET À LA PERFORMANCE DU PROGRAMME S
<string name="clear_filter">Effacer le filtre</string>
<string name="trend_arrow">Flèche de tendance</string>
<string name="cannula">Canule</string>
<string name="userentry">Entrées utilisateur</string>
</resources>

View file

@ -420,6 +420,7 @@
<string name="basal_shortname">BAS</string>
<string name="deviation_shortname">DEV</string>
<string name="activity_shortname">ATT</string>
<string name="bgi_shortname">-BGI</string>
<string name="abs_insulin_shortname">ASS</string>
<string name="devslope_shortname">PENDEV</string>
<string name="nav_about">Informazioni su</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Riempimento automatico glicemie mancanti da NS</string>
<string name="loop_smbsetbypump_label">SMB impostato dal micro</string>
<string name="overview_show_activity">Attività</string>
<string name="overview_show_bgi">Impatto glicemia (BGI)</string>
<string name="overview_show_sensitivity">Sensibilità</string>
<string name="overview_show_deviations">Deviazioni</string>
<string name="overview_show_cob">CHO attivi</string>
@ -1158,4 +1160,5 @@
<string name="clear_filter">Cancella filtro</string>
<string name="trend_arrow">Freccia trend</string>
<string name="cannula">Cannula</string>
<string name="userentry">Inserimento utente</string>
</resources>

View file

@ -16,7 +16,7 @@
<string name="objectives_maxiob_objective">כוונון הלולאה הסגורה, תוך העלאה של ערך ה- IOB מעל 0 ובסופו של דבר הורדת ערכי המטרה של רמת הסוכר</string>
<string name="objectives_maxiob_gate">לפני הורדת ערך רמת הסוכר, הפעל במשך מס\' ימים, עם לפחות לילה אחד ללא התרעת רמת סוכר נמוכה</string>
<string name="objectives_autosens_objective">במידת הצורך, בצעו התאמת בזאלי ויחסים, ולאחר מכן הפעילו את Autosens</string>
<string name="objectives_autosens_gate">שבוע של הפעלה מוצלחת של הלולאה במשך היום, עם הוספת ערכי פחמימות רגילים</string>
<string name="objectives_autosens_gate">שבוע של הפעלה מוצלחת של הלולאה במשך שעות היום, עם הוספת ערכי פחמימות רגילים</string>
<string name="objectives_ama_objective">הפעלת פונקציות נוספות לשימוש במשך היום, לדוגמה AMA (סיוע ארוחות מתקדם)</string>
<string name="objectives_smb_objective">הפעלת פונקציות נוספות לשימוש במשך היום, כגון סופר מיקרו בולוסים (SMB)</string>
<string name="objectives_auto_objective">הפעלת אוטומציה</string>

View file

@ -156,7 +156,7 @@
<string name="nav_import">יבא הגדרות</string>
<string name="openapsma_maxbasal_title">מינון בזאלי זמני מקסימלי (יח\'\\שעה)</string>
<string name="openapsma_maxbasal_summary">ערך זה נקרא בזאלי מרבי ב-OpenAPS</string>
<string name="openapsma_maxiob_title">בזאלי פעיל מרבי ש-OpenAPS יכול לספק [U]</string>
<string name="openapsma_maxiob_title">בזאלי פעיל מרבי ש-OpenAPS יכול לספק (יחידות) </string>
<string name="openapsma_maxiob_summary">ערך זה נקרא אינסולין פעיל מקסימלי (Max IOB) ב-OpenAPS. זהו מקסימום האינסולין הפעיל ב-[U] ש-APS מאפשר.</string>
<string name="password_preferences_encrypt_prompt">אתם תתבקשו למלא סיסמה ראשית שתשמש להצפנת ההעדפות המיוצאות.</string>
<string name="password_preferences_decrypt_prompt">אתם תתבקשו למלא סיסמה ראשית שתשמש לפיענוח ההעדפות המיובאות.</string>
@ -217,7 +217,7 @@
<string name="smscommunicator_remotecommandnotallowed">פקודה מרוחקת אינה מותרת</string>
<string name="smscommunicator_remotebolusnotallowed">בולוס מרחוק אינו זמין. נסו שוב מאוחר יותר.</string>
<string name="smscommunicator_basalreplywithcode">להפעלת בזאלי %1$.2f יח\' לשעה למשך %2$d דקות יש להשיב עם הקוד %3$s</string>
<string name="smscommunicator_profilereplywithcode">להחלפת פרופיל ל- %1$s %2$d % הקש קוד %3$s</string>
<string name="smscommunicator_profilereplywithcode">להחלפת פרופיל ל- %1$s %2$d %% הקש את הקוד %3$s</string>
<string name="smscommunicator_extendedreplywithcode">למתן בולוס ממושך %1$.2f יח\' למשך %2$d דקות יש להשיב עם הקוד %3$s</string>
<string name="smscommunicator_carbsreplywithcode">למתן %1$d גר\' ב-%2$s יש להשיב עם הקוד %3$s</string>
<string name="smscommunicator_basalpctreplywithcode">להפעלת בזאלי %1$d%% למשך %2$d דקות הקש קוד %3$s</string>
@ -312,7 +312,7 @@
<string name="openapsama_current_basal_safety_multiplier_summary">ערך ברירת מחדל: 4 זהו החצי השני של מנגנון הגנה עיקרי של OpenAPS, והחצי השני של \"3x max daily; 4x current\" במנגנוני ההגנה. המשמעות היא שהמינון הבזאלי, ללא קשר למקסימום שנקבע במשאבה, אינו יכול להיות גבוה יותר ממספר זה כפול הקצב הבזאלי הנוכחי. מיועד למנוע כניסה לתחומים מסוכנים ע\"י קביעת קצב בזאלי גבוה לפני הבנה של פעולת האלגוריתם. שוב: ברירת המחדל היא x4, רוב המשתמשים לעולם אינם צריכים לשנות ערך זה ואם הם מרגישים שמנגנון הגנה זה מפריע להם, הפתרון הוא בשינוי של הגדרות אחרות.</string>
<string name="openapsama_autosens_max_summary">ערך ברירת מחדל: 1.2\nזוהי מגבלת המכפיל של autosens (ובקרוב autotune) שמגדירה 20%% כיחס מקסימלי של autosens, אשר קובע בתורו את גובה הבזאלי המקסימלי, גובהו המינימלי של יחס התיקון, ואת המינימום של ערכי המטרה האפשריים.</string>
<string name="openapsama_autosens_min_summary">ערך ברירת מחדל: 0.7\nבצד השני של מגבלות הבטיחות של autosens, ערך זה מגביל את יכולתו של autosens להוריד את הערכים הבזאליים ועד כמה יכול להעלות את יחס התיקון ואת ערכי מטרת הסוכר בדם.</string>
<string name="openapsama_autosens_adjusttargets">Autosens מווסת גם את ערכי המטרה</string>
<string name="openapsama_autosens_adjusttargets">וויסות ערכי מטרה ע\"י Autosens</string>
<string name="openapsama_autosens_adjusttargets_summary">ערך ברירת מחדל: התאמה מדויקת\n נועד לאפשר ל-Autosens לשנות את המטרה של ערכי הסוכר, בנוסף ליחס התיקון והבזאלים.</string>
<string name="openapsama_bolussnooze_dia_divisor_summary">ערך ברירת מחדל: 2\n מעכב בולוס מופעל אחרי שאתם מזריקים בולוס ארוחה, כך שהלולאה לא תפצה ע\"י בזאלי זמני נמוך אחרי הארוחה. הדוגמה כאן וברירת המחדל היא 2; כך שהגדרה של משך פעילות אינסולין של 3 שעות משמעה שהעיכוב יחלוף בהדרגה בתוך 1.5 שעות (3 ש\' לחלק ל-2).</string>
<string name="openapsama_min_5m_carbimpact_summary">ערך ברירת מחדל: 3.0 (AMA) או 8.0 (SMB). זוהי הגדרת ברירת מחדל לספיגת פחמימות ב-5 דקות. ברירת המחדל היא 3mg/dL/5min. פעולה זו משפיעה על קצב הדעיכה של פחמ\' פעילות, ועל הנחת קצב ספיגת הפחמ\' בחישוב רמות סוכר עתידיות כשהן בירידה מהירה מהצפוי או עליה איטית מהצפוי.</string>
@ -420,6 +420,7 @@
<string name="basal_shortname">בזאל\'</string>
<string name="deviation_shortname">סטייה</string>
<string name="activity_shortname">פעילות</string>
<string name="bgi_shortname">השפעה</string>
<string name="abs_insulin_shortname">אבס\'</string>
<string name="devslope_shortname">שיפוע</string>
<string name="nav_about">אודות</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">לטעינת הנתונים האוטומטית חסרים נתוני סוכר מ-Nightscout</string>
<string name="loop_smbsetbypump_label">SMB מוגדר באמצעות משאבה</string>
<string name="overview_show_activity">פעילות</string>
<string name="overview_show_bgi">השפעת הסוכר בדם</string>
<string name="overview_show_sensitivity">רגישות</string>
<string name="overview_show_deviations">חריגות</string>
<string name="overview_show_cob">פחמימות פעילות</string>
@ -676,7 +678,7 @@
<string name="smbnotallowedinopenloopmode">SMB אינו פעיל במצב לולאה פתוחה</string>
<string name="food_short">מזון</string>
<string name="reset">איפוס</string>
<string name="openapssmb_maxiob_title">מינון אינסולין פעיל מרבי ממנו OpenAPS לא יחרוג [U]</string>
<string name="openapssmb_maxiob_title">מינון אינסולין פעיל מרבי ממנו OpenAPS לא יחרוג (יחידות) </string>
<string name="openapssmb_maxiob_summary">ב-OpenAPS ערך זה נקרא מקסימום אינסולין פעיל (maxIOB). \nלא יוזרק עוד אינסולין אם כמות האינסולין הפעיל הנוכחי גדול מערך זה</string>
<string name="pump_stopped">המשאבה נעצרה</string>
<string name="pump_started">המשאבה הופעלה</string>
@ -702,8 +704,8 @@
<string name="connectionsettings_title">הגדרות חיבור</string>
<string name="ns_wifi_allowedssids">SSID מורשים (מופרדים בנקודה-פסיק)</string>
<string name="ns_allowroaming">אפשר חיבור בנדידה</string>
<string name="openapsama_autosens_max">יחס autosens מקסימלי</string>
<string name="openapsama_autosens_min">יחס autosens מינימלי</string>
<string name="openapsama_autosens_max">יחס Autosens מקסימלי</string>
<string name="openapsama_autosens_min">יחס Autosens מינימלי</string>
<string name="openapsama_bolussnooze_dia_divisor">נמנום בולוס - מחלק משך פעילות אינסולין</string>
<string name="openapsama_max_daily_safety_multiplier">מכפלת בטיחות בזאלי יומי מירבי</string>
<string name="openapsama_current_basal_safety_multiplier">מכפלת בטיחות בזאלי נוכחי</string>
@ -771,7 +773,7 @@
<string name="delete_logs">מחק רישומים</string>
<string name="error_adding_treatment_message">לא ניתן להוסיף טיפול (אינסולין: %1$.2f, פחמ\': %2$d, בשעה: %3$s) לטיפולים. נא לבדוק ולהוסיף רשומה באופן ידני כנדרש.</string>
<string name="generated_ecarbs_note">פחמימות ממושכות: %1$d גר\' (%2$d ש\'), עיכוב %3$d דק\'</string>
<string name="openaps_noasdata">אין נתוני autosens זמינים</string>
<string name="openaps_noasdata">אין נתוני Autosens זמינים</string>
<string name="nav_logsettings">הגדרות יומן רישום</string>
<string name="resettodefaults">אפס לברירת המחדל</string>
<string name="nsmalfunction">תקלה ב-NSClient. שקלו להפעיל את Nightscout ו-NSClient מחדש.</string>
@ -1158,4 +1160,5 @@
<string name="clear_filter">נקה סינון</string>
<string name="trend_arrow">חץ מגמה</string>
<string name="cannula">צינורית</string>
<string name="userentry">קלט המשתמש</string>
</resources>

View file

@ -420,6 +420,7 @@
<string name="basal_shortname">BAS</string>
<string name="deviation_shortname">DEV</string>
<string name="activity_shortname">AKT</string>
<string name="bgi_shortname">-BGI</string>
<string name="abs_insulin_shortname">ABS</string>
<string name="devslope_shortname">DEVSLOPE</string>
<string name="nav_about">Om</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Hente manglende BS data fra NS</string>
<string name="loop_smbsetbypump_label">SMB satt med pumpe</string>
<string name="overview_show_activity">Aktivitet</string>
<string name="overview_show_bgi">Blodsukkerpåvirkning</string>
<string name="overview_show_sensitivity">Sensitivitet</string>
<string name="overview_show_deviations">Avvik</string>
<string name="overview_show_cob">Aktive KH (COB)</string>
@ -1158,4 +1160,5 @@
<string name="clear_filter">Nullstill filtre</string>
<string name="trend_arrow">Trend pil</string>
<string name="cannula">Kanyle</string>
<string name="userentry">Bruker registrering</string>
</resources>

View file

@ -96,7 +96,7 @@
<string name="whatistrue">Seleccione todas as respostas correctas.</string>
<string name="update_git">Precisa ter Git instalado e configurado no computador.</string>
<string name="update_asap">Quando as versões actualizadas do AndroidAPS são lançadas, as versões anteriores podem ser remotamente limitadas após um tempo específico.</string>
<string name="update_keys">Deve guardar e anotar a localização da sua keystore e usar a mesma chave de assinatura para actualizações como para a instalação anterior.</string>
<string name="update_keys">Deve guardar e anotar a localização da sua keystore e utilizar a mesma chave de assinatura para actualizações como para a instalação anterior.</string>
<string name="update_neverupdate">Nunca actualize se o sistema estiver a funcionar bem.</string>
<string name="update_askfriend">Se tiver dificuldade em construir o apk, pode instalar um apk que foi construído por um amigo.</string>
<string name="update_hint1">https://androidaps.readthedocs.io/en/latest/EN/Installing-AndroidAPS/Update-to-new-version.html#update-to-a-new-version-or-branch</string>
@ -110,7 +110,7 @@
<string name="troubleshooting_hint2">https://www.facebook.com/groups/AndroidAPSUsers/</string>
<string name="troubleshooting_hint3">https://gitter.im/MilosKozak/AndroidAPS</string>
<string name="insulin_label">Plugins de Insulina</string>
<string name="insulin_ultrarapid">Qual insulina deve usar com o plugin Ultra-Rapid Oref?</string>
<string name="insulin_ultrarapid">Qual insulina deve utilizar com o plugin Ultra-Rapid Oref?</string>
<string name="insulin_fiasp">Fiasp®</string>
<string name="insulin_novorapid">NovoRapid®/Novolog®</string>
<string name="insulin_humalog">Humalog ®</string>
@ -148,7 +148,7 @@
<string name="breadgrams_calc">Se os níveis de glicose no sangue estiverem fora dos valores aceitáveis (muito baixos ou muito altos) a calculadora de bólus pode ser usada para fornecer sugestões para correções de hidratos ou insulina.</string>
<string name="breadgrams_hint1">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#insulin-to-carb-ratio-ic-g-u</string>
<string name="extendedcarbs_label">hidratos-e</string>
<string name="extendedcarbs_handling">Para que poderia usar os hidratos-e (hidratos estendidos)?</string>
<string name="extendedcarbs_handling">Para que poderia utilizar os hidratos-e (hidratos estendidos)?</string>
<string name="extendedcarbs_future">Para agendar hidratos no futuro, possivelmente distribuídos num intervalo (semelhante a um bólus estendido que distribui insulina por um intervalo).</string>
<string name="extendedcarbs_free">Para registar hidratos \'livres\' ingeridos em exercícios que deseja esconder do AndroidAPS.</string>
<string name="extendedcarbs_fat">Os hidratos-e (distribuídos no futuro) podem ajudar o AndroidAPS a lidar com comidas gorduras/proteínas elevadas.</string>
@ -169,7 +169,7 @@
<string name="isf_profile">A alteração do valor do FSI no seu perfil é suficiente para aplicar a mudança.</string>
<string name="isf_hint1">https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/FAQ.html#insulin-sensitivity-factor-isf-mmol-l-u-or-mg-dl-u</string>
<string name="isf_hint2">https://androidaps.readthedocs.io/en/latest/EN/Usage/Profiles.html</string>
<string name="ic_multiple">Pode usar mais de um valor para o rácio I:HC no seu perfil.</string>
<string name="ic_multiple">Pode utilizar mais de um valor para o rácio I:HC no seu perfil.</string>
<string name="ic_isf">Se alterar o seu FSI no perfil, também deve mudar a taxa de I:HC.</string>
<string name="ic_label_exam">Rácio Insulina por Hidratos de Carbono (Rácio I:HC)</string>
<string name="ic_increasingvalue">Rácios I:HC mais altos levam a menos insulina administrada para uma dada quantidade de hidratos.</string>
@ -211,5 +211,5 @@
<string name="basalhelp_facebook">Facebook</string>
<string name="other_medication_label">Outra Medicação. Por favor, leia declaração abaixo e marque a caixa para aceitá-la.</string>
<string name="other_medication_text">AndroidAPS reduz as taxas de basal ou suspende a administração de insulina para aumentar a quantidade de açúcar no sangue. Os medicamentos da classe inibidores SGLT2 (gliflozins) podem evitar aumentos da glicose no sangue e, por conseguinte, podem produzir uma perigosa deficiência de insulina que leva à DKA.
\nOs nomes comuns são: Invokana®, Forxiga®, Jardiance®, Steglatro®, Suglat®, Apleway®, Deberza®, Synjardy®, Vokanamet®, Xigduo®,\n\nPrometo pelo presente que não tomarei tal medicação ao usar o AndroidAPS ou que desactivarei o loop antes de usar tais medicamentos.</string>
\nOs nomes comuns são: Invokana®, Forxiga®, Jardiance®, Steglatro®, Suglat®, Apleway®, Deberza®, Synjardy®, Vokanamet®, Xigduo®,\n\nPrometo pelo presente que não tomarei tal medicação ao U«ytilizar o AndroidAPS ou que desactivarei o loop antes de usar tais medicamentos.</string>
</resources>

View file

@ -13,7 +13,7 @@
<string name="nav_resetdb">Reinicializar Base de Dados</string>
<string name="reset_db_confirm">Quer realmente reiniciar a base de dados?</string>
<string name="nav_exit">Sair</string>
<string name="ns_sync_use_absolute_title">Usar sempre valores absolutos de basal</string>
<string name="ns_sync_use_absolute_title">Utilizar sempre valores absolutos de basal</string>
<string name="alert_dialog_permission_battery_optimization_failed">Este dispositivo não parece suportar a optimização de bateria na lista de permissões - pode ter problemas de desempenho.</string>
<string name="description_actions">Alguns botões para aceder rapidamente a funções comuns</string>
<string name="description_config_builder">Usado para configurar os plugins ativos</string>
@ -86,7 +86,7 @@
<string name="virtualpump">Bomba virtual</string>
<string name="careportal">Careportal</string>
<string name="configbuilder_pump">Bomba</string>
<string name="configbuilder_pump_description">Qual a bomba que gostaria de usar com AndroidAPS?</string>
<string name="configbuilder_pump_description">Qual a bomba que gostaria de utilizar com AndroidAPS?</string>
<string name="configbuilder_treatments">Tratamentos</string>
<string name="configbuilder_treatments_description">Qual o plugin que deve ser usado para os tratamentos?</string>
<string name="configbuilder_profile">Perfil</string>
@ -285,7 +285,7 @@
<string name="array_of_elements">Array de %1$d elementos.\nValor actual:</string>
<string name="openapsma_autosensdata_label">Dados Autosens</string>
<string name="openapsma_scriptdebugdata_label">Script debug</string>
<string name="openapsama_useautosens">Usar função Autosens</string>
<string name="openapsama_useautosens">Utilizar função Autosens</string>
<string name="refresheventsfromnightscout">Actualizar tratamentos do NS</string>
<string name="deletefuturetreatments">Apagar tratamentos do futuro</string>
<string name="eatingsoon">Comer Brevemente</string>
@ -305,7 +305,7 @@
<string name="wear_shortname">WEAR</string>
<string name="smscommunicator_shortname">SMS</string>
<string name="short_tabtitles">Abreviar títulos dos separadores</string>
<string name="always_use_shortavg">Usar sempre delta médio curto em vez de delta simples</string>
<string name="always_use_shortavg">Utilizar sempre delta médio curto em vez de delta simples</string>
<string name="always_use_shortavg_summary">Útil quando há ruído nos dados de fontes sem filtro como xDrip+.</string>
<string name="profile">Perfil</string>
<string name="openapsama_max_daily_safety_multiplier_summary">Valor padrão: 3 Por segurança é o valor limite estabelecido por OpenAPS. O que faz é limitar a basal a x3 a basal mázima. Se necessário modificar este valor, por favor ter em conta que os dados apontam para que os limites de segurança sejam - 3 x max diario ou 4x valor actual (qual seja menor) como valores máximos.</string>
@ -398,7 +398,7 @@
<string name="ultrafastactinginsulincomment">Fiasp</string>
<string name="insulin_shortname">INS</string>
<string name="enablesuperbolus">Activar superbólus no assistente</string>
<string name="enablesuperbolus_summary">Habilite a funcionalidade de superbolus no assistente. Não habilite até que aprenda o funcionamento. PODE CAUSAR OVERDOSE DE INSULINA SE USAR INDISCRIMINADAMENTE!</string>
<string name="enablesuperbolus_summary">Active a funcionalidade de superbolus no assistente. Não active até que aprenda o funcionamento. PODE CAUSAR OVERDOSE DE INSULINA SE UTILIZAR INDISCRIMINADAMENTE!</string>
<string name="show_statuslights">Mostrar luzes de estado no ecrã principal</string>
<string name="statuslights_cage_warning">Limite aviso idade canula [h]</string>
<string name="statuslights_cage_critical">Limite Crítico Idade Canula [h]</string>
@ -420,6 +420,7 @@
<string name="basal_shortname">BAS</string>
<string name="deviation_shortname">DESV</string>
<string name="activity_shortname">ACT</string>
<string name="bgi_shortname">-IG</string>
<string name="abs_insulin_shortname">ABS</string>
<string name="devslope_shortname">DESVINCLI</string>
<string name="nav_about">Acerca</string>
@ -508,7 +509,7 @@
<string name="executingrightnow">Comando será executado agora</string>
<string name="pump_unreachable">Bomba inacessível</string>
<string name="missed_bg_readings">Leituras Glicose perdidas</string>
<string name="raise_notifications_as_android_notifications">Usar as notificações do sistema para alertas e notificações</string>
<string name="raise_notifications_as_android_notifications">Utilizar as notificações do sistema para alertas e notificações</string>
<string name="gradually_increase_notification_volume">Aumentar gradualmente o volume de alertas e notificações</string>
<string name="localalertsettings_title">Alertas local</string>
<string name="enable_missed_bg_readings_alert">Alerta caso nenhuma Glicose seja recebida</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Preencher Glicose em falta com NS</string>
<string name="loop_smbsetbypump_label">SMB definido pela bomba</string>
<string name="overview_show_activity">Actividade</string>
<string name="overview_show_bgi">Impacto Glicose</string>
<string name="overview_show_sensitivity">Sensibilidade</string>
<string name="overview_show_deviations">Desvios</string>
<string name="overview_show_cob">Hidratos a Bordo (COB)</string>
@ -727,7 +729,7 @@
<string name="nsclientinfotext">ClienteNS gere a ligação ao Nightscout. Pode saltar esta parte mas não será possível passar os objectivos até que o configure.</string>
<string name="diawarning">Lembre-se: novos perfis de insulina requerem diâmetro de pelo menos 5h. DIA 56h no novo perfil é igual ao diâmetro 3h nos antigos perfis de insulina.</string>
<string name="setupwizard_profile_description">Por favor seleccione a fonte do perfil. Se o paciente é uma criança deverá utilizar perfil NS. Se ninguém o está a seguir no Nightscout provavelmente preferirá um perfil Local. Lembre-se que apenas está a escolher a fonte de perfil. Para o utilizar terá que o activar executando \"Troca Perfil\"</string>
<string name="setupwizard_aps_description">Seleccione um dos algoritmos disponíveis. Eles são classificados do mais antigo para o mais recente. Algoritmo mais recente é geralmente mais forte e mais agressivo. Assim, se é novo looper, poderá provavelmente começar com AMA e não com a versão mais recente. Não se esqueça de ler a documentação de OpenAPS e configurá-lo antes de usar.</string>
<string name="setupwizard_aps_description">Seleccione um dos algoritmos disponíveis. Eles são classificados do mais antigo para o mais recente. Algoritmo mais recente é geralmente mais forte e mais agressivo. Assim, se é novo looper, poderá provavelmente começar com AMA e não com a versão mais recente. Não se esqueça de ler a documentação de OpenAPS e configurá-lo antes de utilizar.</string>
<string name="setupwizard_pump_waiting_for_riley_link_connection">Por favor, configure o seu RileyLink abaixo. Depois de seleccionar um RileyLink, será possível continuar a configuração quando o estado do RileyLink estiver \"Conectado\". Isso pode levar um minuto.\n</string>
<string name="setupwizard_pump_pump_not_initialized"><b>Nota:</b> Pode continuar confguração uma vez que a bomba foi configurada.\n</string>
<string name="startobjective">Iniciar primeiro objectivo</string>
@ -801,7 +803,7 @@
<string name="log_operating_mode_changes">Alterações do modo de funcionamento de registo</string>
<string name="log_alerts">Alertas de registo</string>
<string name="enable_tbr_emulation">Activar a emulação DBT</string>
<string name="enable_tbr_emulation_summary">Usar bólus prolongados em vez de basais temporárias para contornar o limite de 250%%</string>
<string name="enable_tbr_emulation_summary">Utilizar bólus prolongados em vez de basais temporárias para contornar o limite de 250%%</string>
<string name="disable_vibration">Desactivar vibrações na entrega manual de bólus</string>
<string name="disable_vibration_summary">Para bólus e bólus estendidos (só disponível com o firmware Insight 3.x)</string>
<string name="disable_vibration_auto">Desactivar vibrações na entrega automática de bólus</string>
@ -883,9 +885,9 @@
<string name="or">Ou</string>
<string name="xor">Exclusivo ou</string>
<string name="atspecifiedtime">Às %1$s</string>
<string name="use_network_location">Usar localização da rede</string>
<string name="use_gps_location">Usar localização GPS</string>
<string name="use_passive_location">Usar localização passiva</string>
<string name="use_network_location">Utilizar localização da rede</string>
<string name="use_gps_location">Utilizar localização GPS</string>
<string name="use_passive_location">Utilizar localização passiva</string>
<string name="locationservice">Serviço de localização</string>
<string name="automation_short">Auto</string>
<string name="automation">Automatização</string>
@ -900,7 +902,7 @@
<string name="title_tidepool_password">Senha Login</string>
<string name="title_tidepool_test_login">Teste de Conta Tidepool</string>
<string name="summary_tidepool_dev_servers">Se activado, envio será para https://int-app.tidepool.org em vez do habitual https://app.tidepool.org/</string>
<string name="title_tidepool_dev_servers">Usar servidores de integração (teste)</string>
<string name="title_tidepool_dev_servers">Utilizar servidores de integração (teste)</string>
<string name="tidepool">Tidepool</string>
<string name="tidepool_shortname">TDP</string>
<string name="description_tidepool">Enviar dados para Tidepool</string>
@ -1147,7 +1149,7 @@
<string name="advisoralarm">Executar alarme quando for tempo de comer</string>
<string name="alarminxmin">Executar alarme em %1$d min</string>
<string name="bolusadvisor">Guia de Bólus</string>
<string name="bolusadvisormessage">Tem glicemia alta. Em vez de comer agora é recomendado esperar por uma melhor glicemia. Quer fazer um bólus de correção agora e lembrá-lo quando fôr hora de comer? Neste caso nenhum hidrato será registado e deve usar o assistente novamente quando for lembrado.</string>
<string name="bolusadvisormessage">Tem glicemia alta. Em vez de comer agora é recomendado esperar por uma melhor glicemia. Quer fazer um bólus de correção agora e lembrá-lo quando fôr hora de comer? Neste caso nenhum hidrato será registado e deve utilizar o assistente novamente quando for lembrado.</string>
<string name="enablebolusadvisor">Activar Guia de Bólus</string>
<string name="enablebolusadvisor_summary">Use lembrete para começar a comer mais tarde em vez di resultado do assistente durante a glicemia alta (\"pré-bolus\")</string>
<string name="time_to_eat">Hora de comer!\nExecutar assistente de Bólus e fazer cálculo novamente.</string>
@ -1158,4 +1160,5 @@
<string name="clear_filter">Limpar filtros</string>
<string name="trend_arrow">Seta de tendência</string>
<string name="cannula">Cânula</string>
<string name="userentry">Entrada de Utilizador</string>
</resources>

View file

@ -420,6 +420,7 @@
<string name="basal_shortname">БАЗ</string>
<string name="deviation_shortname">ОТКЛН</string>
<string name="activity_shortname">НАГР</string>
<string name="bgi_shortname">-BGI</string>
<string name="abs_insulin_shortname">НАДО</string>
<string name="devslope_shortname">ЛИНОТКЛН</string>
<string name="nav_about">о приложении</string>
@ -627,6 +628,7 @@
<string name="ns_autobackfill_summary">Заполнять пропущенные данные из NS</string>
<string name="loop_smbsetbypump_label">Супер микро болюс SMB задан помпой</string>
<string name="overview_show_activity">Нагрузка</string>
<string name="overview_show_bgi">Влияние глюкозы</string>
<string name="overview_show_sensitivity">Чувствительность</string>
<string name="overview_show_deviations">Отклонение</string>
<string name="overview_show_cob">Активные углеводы COB</string>
@ -1160,4 +1162,5 @@ Context | Edit Context</string>
<string name="clear_filter">Очистить фильтр</string>
<string name="trend_arrow">Стрелка тренда</string>
<string name="cannula">Катетер помпы</string>
<string name="userentry">Запись пользователя</string>
</resources>

View file

@ -1381,5 +1381,6 @@
<string name="trend_arrow">Trend arrow</string>
<string name="cannula">Cannula</string>
<string name="userentry">User entry</string>
<string name="common_values">Use values of your largest food you usually eat\n</string>
</resources>

View file

@ -8,18 +8,18 @@
<SwitchPreference
android:defaultValue="false"
android:key="wearcontrol"
android:key="@string/key_wear_control"
android:summary="@string/wearcontrol_summary"
android:title="@string/wearcontrol_title" />
<PreferenceCategory
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:summary="@string/wear_wizard_settings_summary"
android:title="@string/wear_wizard_settings">
<CheckBoxPreference
android:defaultValue="true"
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:key="@string/key_wearwizard_bg"
android:title="@string/treatments_wizard_bg_label" />
@ -31,25 +31,25 @@
<CheckBoxPreference
android:defaultValue="false"
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:key="@string/key_wearwizard_trend"
android:title="@string/treatments_wizard_bgtrend_label" />
<CheckBoxPreference
android:defaultValue="true"
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:key="@string/key_wearwizard_cob"
android:title="@string/treatments_wizard_cob_label" />
<CheckBoxPreference
android:defaultValue="true"
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:key="@string/key_wearwizard_bolusiob"
android:title="@string/treatments_wizard_bolusiob_label" />
<CheckBoxPreference
android:defaultValue="true"
android:dependency="wearcontrol"
android:dependency="@string/key_wear_control"
android:key="@string/key_wearwizard_basaliob"
android:title="@string/treatments_wizard_basaliob_label" />

View file

@ -122,7 +122,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
danaRPlugin = DanaRPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, resourceHelper, constraintChecker, activePlugin, sp, commandQueue, danaPump, dateUtil, fabricPrivacy)
danaRSPlugin = DanaRSPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, resourceHelper, constraintChecker, profileFunction, activePluginProvider, sp, commandQueue, danaPump, detailedBolusInfoStorage, fabricPrivacy, dateUtil)
insightPlugin = LocalInsightPlugin(injector, aapsLogger, rxBus, resourceHelper, treatmentsPlugin, sp, commandQueue, profileFunction, nsUpload, context, uploadQueue, Config(), dateUtil)
openAPSSMBPlugin = OpenAPSSMBPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits, profiler, fabricPrivacy, sp)
openAPSSMBPlugin = OpenAPSSMBPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits, profiler, sp)
openAPSAMAPlugin = OpenAPSAMAPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits, profiler, fabricPrivacy)
safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, buildHelper, treatmentsPlugin, Config())
val constraintsPluginsList = ArrayList<PluginBase>()

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.aps.loop
import android.app.NotificationManager
import android.content.Context
import dagger.Lazy
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
@ -16,7 +15,6 @@ import info.nightscout.androidaps.interfaces.PumpDescription
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.wear.ActionStringHandler
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
@ -48,7 +46,6 @@ class LoopPluginTest : TestBase() {
@Mock lateinit var activePlugin: ActivePluginProvider
@Mock lateinit var treatmentsPlugin: TreatmentsPlugin
@Mock lateinit var virtualPumpPlugin: VirtualPumpPlugin
@Mock lateinit var actionStringHandler: Lazy<ActionStringHandler>
@Mock lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var receiverStatusStore: ReceiverStatusStore
@ -62,7 +59,7 @@ class LoopPluginTest : TestBase() {
@Before fun prepareMock() {
hardLimits = HardLimits(aapsLogger, rxBus, sp, resourceHelper, context, nsUpload)
loopPlugin = LoopPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, Config(), constraintChecker, resourceHelper, profileFunction, context, commandQueue, activePlugin, treatmentsPlugin, virtualPumpPlugin, actionStringHandler, iobCobCalculatorPlugin, receiverStatusStore, fabricPrivacy, nsUpload, hardLimits)
loopPlugin = LoopPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, Config(), constraintChecker, resourceHelper, profileFunction, context, commandQueue, activePlugin, treatmentsPlugin, virtualPumpPlugin, iobCobCalculatorPlugin, receiverStatusStore, fabricPrivacy, nsUpload, hardLimits)
`when`(activePlugin.activePump).thenReturn(virtualPumpPlugin)
`when`(context.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(notificationManager)
}

View file

@ -20,7 +20,7 @@ import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.powermock.core.classloader.annotations.PrepareForTest
@PrepareForTest(VirtualPumpPlugin::class, RxBusWrapper::class, LocalProfilePlugin::class, SmsCommunicatorPlugin::class, ConfigBuilderPlugin::class)
@PrepareForTest(VirtualPumpPlugin::class, RxBusWrapper::class, LocalProfilePlugin::class, SmsCommunicatorPlugin::class, ConfigBuilderPlugin::class, LoopPlugin::class)
open class ActionsTestBase : TestBaseWithProfile() {
@Mock lateinit var sp: SP

View file

@ -51,7 +51,7 @@ import org.powermock.modules.junit4.PowerMockRunner
import java.util.*
@RunWith(PowerMockRunner::class)
@PrepareForTest(ConstraintChecker::class, FabricPrivacy::class, VirtualPumpPlugin::class, XdripCalibrations::class, SmsManager::class, CommandQueue::class, LocalProfilePlugin::class, DateUtil::class, IobCobCalculatorPlugin::class, OneTimePassword::class, UserEntryLogger::class)
@PrepareForTest(ConstraintChecker::class, FabricPrivacy::class, VirtualPumpPlugin::class, XdripCalibrations::class, SmsManager::class, CommandQueue::class, LocalProfilePlugin::class, DateUtil::class, IobCobCalculatorPlugin::class, OneTimePassword::class, UserEntryLogger::class, LoopPlugin::class)
class SmsCommunicatorPluginTest : TestBaseWithProfile() {
@Mock lateinit var context: Context

View file

@ -4,7 +4,7 @@ buildscript {
ext {
kotlin_version = '1.4.30'
coreVersion = '1.3.2'
rxjava_version = '2.2.20'
rxjava_version = '2.2.21'
rxandroid_version = '2.1.1'
rxkotlin_version = '2.4.0'
room_version = '2.2.6'
@ -24,7 +24,7 @@ buildscript {
jodatime_version = '2.10.10'
work_version = '2.5.0'
junit_version = '4.13.1'
junit_version = '4.13.2'
mockitoVersion = '3.7.7'
powermockVersion = '2.0.9'
dexmakerVersion = "1.2"

View file

@ -1,6 +1,9 @@
package info.nightscout.androidaps.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.annotation.RawRes
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.dialogs.ErrorDialog
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
@ -16,13 +19,28 @@ class ErrorHelperActivity : DialogAppCompatActivity() {
super.onCreate(savedInstanceState)
val errorDialog = ErrorDialog()
errorDialog.helperActivity = this
errorDialog.status = intent.getStringExtra("status")
errorDialog.sound = intent.getIntExtra("soundid", R.raw.error)
errorDialog.title = intent.getStringExtra("title")
errorDialog.status = intent.getStringExtra(STATUS)
errorDialog.sound = intent.getIntExtra(SOUND_ID, R.raw.error)
errorDialog.title = intent.getStringExtra(TITLE)
errorDialog.show(supportFragmentManager, "Error")
if (sp.getBoolean(R.string.key_ns_create_announcements_from_errors, true)) {
nsUpload.uploadError(intent.getStringExtra("status"))
nsUpload.uploadError(intent.getStringExtra(STATUS))
}
}
companion object {
const val SOUND_ID = "soundId"
const val STATUS = "status"
const val TITLE = "title"
fun runAlarm(ctx: Context, status: String, title : String, @RawRes soundId: Int = 0) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra(SOUND_ID, soundId)
i.putExtra(STATUS, status)
i.putExtra(TITLE, title)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
}
}
}

View file

@ -1,528 +0,0 @@
package info.nightscout.androidaps.activities;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TDD;
import info.nightscout.androidaps.events.EventDanaRSyncStatus;
import info.nightscout.androidaps.events.EventPumpStatusChanged;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.SafeParse;
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;
public class TDDStatsActivity extends NoSplashAppCompatActivity {
@Inject AAPSLogger aapsLogger;
@Inject ResourceHelper resourceHelper;
@Inject RxBusWrapper rxBus;
@Inject SP sp;
@Inject ProfileFunction profileFunction;
@Inject ActivePluginProvider activePlugin;
@Inject CommandQueueProvider commandQueue;
@Inject DatabaseHelperInterface databaseHelper;
@Inject FabricPrivacy fabricPrivacy;
@Inject AapsSchedulers aapsSchedulers;
private final CompositeDisposable disposable = new CompositeDisposable();
TextView statusView, statsMessage, totalBaseBasal2;
EditText totalBaseBasal;
Button reloadButton;
LinearLayoutManager llm;
TableLayout tl, ctl, etl;
String TBB;
double magicNumber;
DecimalFormat decimalFormat;
List<TDD> historyList = new ArrayList<>();
List<TDD> dummies;
public TDDStatsActivity() {
super();
}
@Override
protected void onResume() {
super.onResume();
disposable.add(rxBus
.toObservable(EventPumpStatusChanged.class)
.observeOn(aapsSchedulers.getMain())
.subscribe(event -> statusView.setText(event.getStatus(resourceHelper)), exception -> fabricPrivacy.logException(exception))
);
disposable.add(rxBus
.toObservable(EventDanaRSyncStatus.class)
.observeOn(aapsSchedulers.getMain())
.subscribe(event -> {
aapsLogger.debug("EventDanaRSyncStatus: " + event.getMessage());
statusView.setText(event.getMessage());
}, exception -> fabricPrivacy.logException(exception))
);
}
@Override
protected void onPause() {
super.onPause();
disposable.clear();
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View myView = getCurrentFocus();
if (myView instanceof EditText) {
Rect rect = new Rect();
myView.getGlobalVisibleRect(rect);
if (!rect.contains((int) event.getRawX(), (int) event.getRawY())) {
myView.clearFocus();
}
}
}
return super.dispatchTouchEvent(event);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.danar_statsactivity);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
statusView = findViewById(R.id.danar_stats_connection_status);
reloadButton = findViewById(R.id.danar_statsreload);
totalBaseBasal = findViewById(R.id.danar_stats_editTotalBaseBasal);
totalBaseBasal2 = findViewById(R.id.danar_stats_editTotalBaseBasal2);
statsMessage = findViewById(R.id.danar_stats_Message);
statusView.setVisibility(View.GONE);
statsMessage.setVisibility(View.GONE);
totalBaseBasal2.setEnabled(false);
totalBaseBasal2.setClickable(false);
totalBaseBasal2.setFocusable(false);
totalBaseBasal2.setInputType(0);
decimalFormat = new DecimalFormat("0.000");
llm = new LinearLayoutManager(this);
TBB = sp.getString("TBB", "10.00");
Profile profile = profileFunction.getProfile();
if (profile != null) {
double cppTBB = profile.baseBasalSum();
TBB = decimalFormat.format(cppTBB);
sp.putString("TBB", TBB);
}
totalBaseBasal.setText(TBB);
if (!activePlugin.getActivePump().getPumpDescription().needsManualTDDLoad)
reloadButton.setVisibility(View.GONE);
// stats table
tl = findViewById(R.id.main_table);
TableRow tr_head = new TableRow(this);
tr_head.setBackgroundColor(Color.DKGRAY);
tr_head.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
TextView label_date = new TextView(this);
label_date.setText(resourceHelper.gs(R.string.date));
label_date.setTextColor(Color.WHITE);
tr_head.addView(label_date);
TextView label_basalrate = new TextView(this);
label_basalrate.setText(resourceHelper.gs(R.string.basalrate));
label_basalrate.setTextColor(Color.WHITE);
tr_head.addView(label_basalrate);
TextView label_bolus = new TextView(this);
label_bolus.setText(resourceHelper.gs(R.string.bolus));
label_bolus.setTextColor(Color.WHITE);
tr_head.addView(label_bolus);
TextView label_tdd = new TextView(this);
label_tdd.setText(resourceHelper.gs(R.string.tdd));
label_tdd.setTextColor(Color.WHITE);
tr_head.addView(label_tdd);
TextView label_ratio = new TextView(this);
label_ratio.setText(resourceHelper.gs(R.string.ratio));
label_ratio.setTextColor(Color.WHITE);
tr_head.addView(label_ratio);
// add stats headers to tables
tl.addView(tr_head, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
// cumulative table
ctl = findViewById(R.id.cumulative_table);
TableRow ctr_head = new TableRow(this);
ctr_head.setBackgroundColor(Color.DKGRAY);
ctr_head.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
TextView label_cum_amount_days = new TextView(this);
label_cum_amount_days.setText(resourceHelper.gs(R.string.amount_days));
label_cum_amount_days.setTextColor(Color.WHITE);
ctr_head.addView(label_cum_amount_days);
TextView label_cum_tdd = new TextView(this);
label_cum_tdd.setText(resourceHelper.gs(R.string.tdd));
label_cum_tdd.setTextColor(Color.WHITE);
ctr_head.addView(label_cum_tdd);
TextView label_cum_ratio = new TextView(this);
label_cum_ratio.setText(resourceHelper.gs(R.string.ratio));
label_cum_ratio.setTextColor(Color.WHITE);
ctr_head.addView(label_cum_ratio);
// add cummulative headers to tables
ctl.addView(ctr_head, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
// expontial table
etl = findViewById(R.id.expweight_table);
TableRow etr_head = new TableRow(this);
etr_head.setBackgroundColor(Color.DKGRAY);
etr_head.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
TextView label_exp_weight = new TextView(this);
label_exp_weight.setText(resourceHelper.gs(R.string.weight));
label_exp_weight.setTextColor(Color.WHITE);
etr_head.addView(label_exp_weight);
TextView label_exp_tdd = new TextView(this);
label_exp_tdd.setText(resourceHelper.gs(R.string.tdd));
label_exp_tdd.setTextColor(Color.WHITE);
etr_head.addView(label_exp_tdd);
TextView label_exp_ratio = new TextView(this);
label_exp_ratio.setText(resourceHelper.gs(R.string.ratio));
label_exp_ratio.setTextColor(Color.WHITE);
etr_head.addView(label_exp_ratio);
// add expontial headers to tables
etl.addView(etr_head, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
reloadButton.setOnClickListener(v -> {
runOnUiThread(() -> {
reloadButton.setVisibility(View.GONE);
statusView.setVisibility(View.VISIBLE);
statsMessage.setVisibility(View.VISIBLE);
statsMessage.setText(resourceHelper.gs(R.string.warning_Message));
});
commandQueue.loadTDDs(new Callback() {
@Override
public void run() {
loadDataFromDB();
runOnUiThread(() -> {
reloadButton.setVisibility(View.VISIBLE);
statusView.setVisibility(View.GONE);
statsMessage.setVisibility(View.GONE);
});
}
});
});
totalBaseBasal.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
totalBaseBasal.clearFocus();
return true;
}
return false;
});
totalBaseBasal.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
totalBaseBasal.getText().clear();
} else {
sp.putString("TBB", totalBaseBasal.getText().toString());
TBB = sp.getString("TBB", "");
loadDataFromDB();
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(totalBaseBasal.getWindowToken(), 0);
}
});
loadDataFromDB();
}
private void loadDataFromDB() {
historyList = databaseHelper.getTDDs();
//only use newest 10
historyList = historyList.subList(0, Math.min(10, historyList.size()));
//fill single gaps
dummies = new LinkedList<>();
DateFormat df = new SimpleDateFormat("dd.MM.", Locale.getDefault());
for (int i = 0; i < historyList.size() - 1; i++) {
TDD elem1 = historyList.get(i);
TDD elem2 = historyList.get(i + 1);
if (!df.format(new Date(elem1.date)).equals(df.format(new Date(elem2.date + 25 * 60 * 60 * 1000)))) {
TDD dummy = new TDD();
dummy.date = elem1.date - 24 * 60 * 60 * 1000;
dummy.basal = elem1.basal / 2;
dummy.bolus = elem1.bolus / 2;
dummies.add(dummy);
elem1.basal /= 2;
elem1.bolus /= 2;
}
}
historyList.addAll(dummies);
Collections.sort(historyList, (lhs, rhs) -> (int) (rhs.date - lhs.date));
runOnUiThread(() -> {
cleanTable(tl);
cleanTable(ctl);
cleanTable(etl);
DateFormat df1 = new SimpleDateFormat("dd.MM.", Locale.getDefault());
if (TextUtils.isEmpty(TBB)) {
totalBaseBasal.setError("Please Enter Total Base Basal");
return;
} else {
magicNumber = SafeParse.stringToDouble(TBB);
}
magicNumber *= 2;
totalBaseBasal2.setText(decimalFormat.format(magicNumber));
int i = 0;
double sum = 0d;
double weighted03 = 0d;
double weighted05 = 0d;
double weighted07 = 0d;
//TDD table
for (TDD record : historyList) {
double tdd = record.getTotal();
// Create the table row
TableRow tr = new TableRow(TDDStatsActivity.this);
if (i % 2 != 0) tr.setBackgroundColor(Color.DKGRAY);
if (dummies.contains(record)) {
tr.setBackgroundColor(Color.argb(125, 255, 0, 0));
}
tr.setId(100 + i);
tr.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
// Here create the TextView dynamically
TextView labelDATE = new TextView(TDDStatsActivity.this);
labelDATE.setId(200 + i);
labelDATE.setText(df1.format(new Date(record.date)));
labelDATE.setTextColor(Color.WHITE);
tr.addView(labelDATE);
TextView labelBASAL = new TextView(TDDStatsActivity.this);
labelBASAL.setId(300 + i);
labelBASAL.setText(resourceHelper.gs(R.string.formatinsulinunits, record.basal));
labelBASAL.setTextColor(Color.WHITE);
tr.addView(labelBASAL);
TextView labelBOLUS = new TextView(TDDStatsActivity.this);
labelBOLUS.setId(400 + i);
labelBOLUS.setText(resourceHelper.gs(R.string.formatinsulinunits, record.bolus));
labelBOLUS.setTextColor(Color.WHITE);
tr.addView(labelBOLUS);
TextView labelTDD = new TextView(TDDStatsActivity.this);
labelTDD.setId(500 + i);
labelTDD.setText(resourceHelper.gs(R.string.formatinsulinunits, tdd));
labelTDD.setTextColor(Color.WHITE);
tr.addView(labelTDD);
TextView labelRATIO = new TextView(TDDStatsActivity.this);
labelRATIO.setId(600 + i);
labelRATIO.setText(Math.round(100 * tdd / magicNumber) + "%");
labelRATIO.setTextColor(Color.WHITE);
tr.addView(labelRATIO);
// add stats rows to tables
tl.addView(tr, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
i++;
}
i = 0;
//cumulative TDDs
for (TDD record : historyList) {
if (!historyList.isEmpty() && df1.format(new Date(record.date)).equals(df1.format(new Date()))) {
//Today should not be included
continue;
}
i++;
sum = sum + record.getTotal();
// Create the cumtable row
TableRow ctr = new TableRow(TDDStatsActivity.this);
if (i % 2 == 0) ctr.setBackgroundColor(Color.DKGRAY);
ctr.setId(700 + i);
ctr.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
// Here create the TextView dynamically
TextView labelDAYS = new TextView(TDDStatsActivity.this);
labelDAYS.setId(800 + i);
labelDAYS.setText("" + i);
labelDAYS.setTextColor(Color.WHITE);
ctr.addView(labelDAYS);
TextView labelCUMTDD = new TextView(TDDStatsActivity.this);
labelCUMTDD.setId(900 + i);
labelCUMTDD.setText(resourceHelper.gs(R.string.formatinsulinunits, sum / i));
labelCUMTDD.setTextColor(Color.WHITE);
ctr.addView(labelCUMTDD);
TextView labelCUMRATIO = new TextView(TDDStatsActivity.this);
labelCUMRATIO.setId(1000 + i);
labelCUMRATIO.setText(Math.round(100 * sum / i / magicNumber) + "%");
labelCUMRATIO.setTextColor(Color.WHITE);
ctr.addView(labelCUMRATIO);
// add cummulative rows to tables
ctl.addView(ctr, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
}
if (isOldData(historyList) && activePlugin.getActivePump().getPumpDescription().needsManualTDDLoad) {
statsMessage.setVisibility(View.VISIBLE);
statsMessage.setText(resourceHelper.gs(R.string.olddata_Message));
} else {
tl.setBackgroundColor(Color.TRANSPARENT);
}
if (!historyList.isEmpty() && df1.format(new Date(historyList.get(0).date)).equals(df1.format(new Date()))) {
//Today should not be included
historyList.remove(0);
}
Collections.reverse(historyList);
i = 0;
for (TDD record : historyList) {
double tdd = record.getTotal();
if (i == 0) {
weighted03 = tdd;
weighted05 = tdd;
weighted07 = tdd;
} else {
weighted07 = (weighted07 * 0.3 + tdd * 0.7);
weighted05 = (weighted05 * 0.5 + tdd * 0.5);
weighted03 = (weighted03 * 0.7 + tdd * 0.3);
}
i++;
}
// Create the exptable row
TableRow etr = new TableRow(TDDStatsActivity.this);
if (i % 2 != 0) etr.setBackgroundColor(Color.DKGRAY);
etr.setId(1100 + i);
etr.setLayoutParams(new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
// Here create the TextView dynamically
TextView labelWEIGHT = new TextView(TDDStatsActivity.this);
labelWEIGHT.setId(1200 + i);
labelWEIGHT.setText("0.3\n" + "0.5\n" + "0.7");
labelWEIGHT.setTextColor(Color.WHITE);
etr.addView(labelWEIGHT);
TextView labelEXPTDD = new TextView(TDDStatsActivity.this);
labelEXPTDD.setId(1300 + i);
labelEXPTDD.setText(resourceHelper.gs(R.string.formatinsulinunits, weighted03) + "\n" +
resourceHelper.gs(R.string.formatinsulinunits, weighted05) + "\n" +
resourceHelper.gs(R.string.formatinsulinunits, weighted07));
labelEXPTDD.setTextColor(Color.WHITE);
etr.addView(labelEXPTDD);
TextView labelEXPRATIO = new TextView(TDDStatsActivity.this);
labelEXPRATIO.setId(1400 + i);
labelEXPRATIO.setText(Math.round(100 * weighted03 / magicNumber) + "%\n"
+ Math.round(100 * weighted05 / magicNumber) + "%\n"
+ Math.round(100 * weighted07 / magicNumber) + "%");
labelEXPRATIO.setTextColor(Color.WHITE);
etr.addView(labelEXPRATIO);
// add exponentail rows to tables
etl.addView(etr, new TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
});
}
private void cleanTable(TableLayout table) {
int childCount = table.getChildCount();
// Remove all rows except the first one
if (childCount > 1) {
table.removeViews(1, childCount - 1);
}
}
public boolean isOldData(List<TDD> historyList) {
PumpType type = activePlugin.getActivePump().getPumpDescription().pumpType;
boolean startsYesterday = type == PumpType.DanaR || type == PumpType.DanaRS || type == PumpType.DanaRv2 || type == PumpType.DanaRKorean || type == PumpType.AccuChekInsight;
DateFormat df = new SimpleDateFormat("dd.MM.", Locale.getDefault());
return (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).date)).equals(df.format(new Date(System.currentTimeMillis() - (startsYesterday ? 1000 * 60 * 60 * 24 : 0))))));
}
}

View file

@ -0,0 +1,429 @@
package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle
import android.text.TextUtils
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.TableLayout
import android.widget.TableRow
import android.widget.TextView
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.core.databinding.ActivityTddStatsBinding
import info.nightscout.androidaps.db.TDD
import info.nightscout.androidaps.events.EventDanaRSyncStatus
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
import kotlin.math.min
import kotlin.math.roundToInt
class TDDStatsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers
private lateinit var binding: ActivityTddStatsBinding
private val disposable = CompositeDisposable()
lateinit var tbb: String
private var magicNumber = 0.0
private var decimalFormat: DecimalFormat = DecimalFormat("0.000")
private var historyList: MutableList<TDD> = mutableListOf()
private var dummies: MutableList<TDD> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTddStatsBinding.inflate(layoutInflater)
setContentView(binding.root)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
binding.connectionStatus.visibility = View.GONE
binding.message.visibility = View.GONE
binding.totalBaseBasal2.isEnabled = false
binding.totalBaseBasal2.isClickable = false
binding.totalBaseBasal2.isFocusable = false
binding.totalBaseBasal2.inputType = 0
tbb = sp.getString("TBB", "10.00")
val profile = profileFunction.getProfile()
if (profile != null) {
val cppTBB = profile.baseBasalSum()
tbb = decimalFormat.format(cppTBB)
sp.putString("TBB", tbb)
}
binding.totalBaseBasal.setText(tbb)
if (!activePlugin.activePump.pumpDescription.needsManualTDDLoad) binding.reload.visibility = View.GONE
// stats table
// add stats headers to tables
binding.mainTable.addView(
TableRow(this).also { trHead ->
trHead.setBackgroundColor(Color.DKGRAY)
trHead.layoutParams = TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
trHead.addView(TextView(this).also { labelDate ->
labelDate.text = resourceHelper.gs(R.string.date)
labelDate.setTextColor(Color.WHITE)
})
trHead.addView(TextView(this).also { labelBasalRate ->
labelBasalRate.text = resourceHelper.gs(R.string.basalrate)
labelBasalRate.setTextColor(Color.WHITE)
})
trHead.addView(TextView(this).also { labelBolus ->
labelBolus.text = resourceHelper.gs(R.string.bolus)
labelBolus.setTextColor(Color.WHITE)
})
trHead.addView(TextView(this).also { labelTdd ->
labelTdd.text = resourceHelper.gs(R.string.tdd)
labelTdd.setTextColor(Color.WHITE)
})
trHead.addView(TextView(this).also { labelRatio ->
labelRatio.text = resourceHelper.gs(R.string.ratio)
labelRatio.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
)
// cumulative table
binding.cumulativeTable.addView(
TableRow(this).also { ctrHead ->
ctrHead.setBackgroundColor(Color.DKGRAY)
ctrHead.layoutParams = TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
ctrHead.addView(TextView(this).also { labelCumAmountDays ->
labelCumAmountDays.text = resourceHelper.gs(R.string.amount_days)
labelCumAmountDays.setTextColor(Color.WHITE)
})
ctrHead.addView(TextView(this).also { labelCumTdd ->
labelCumTdd.text = resourceHelper.gs(R.string.tdd)
labelCumTdd.setTextColor(Color.WHITE)
})
ctrHead.addView(TextView(this).also { labelCumRatio ->
labelCumRatio.text = resourceHelper.gs(R.string.ratio)
labelCumRatio.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
)
// exponential table
binding.expweightTable.addView(
TableRow(this).also { etrHead ->
etrHead.setBackgroundColor(Color.DKGRAY)
etrHead.layoutParams = TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
etrHead.addView(TextView(this).also { labelExpWeight ->
labelExpWeight.text = resourceHelper.gs(R.string.weight)
labelExpWeight.setTextColor(Color.WHITE)
})
etrHead.addView(TextView(this).also { labelExpTdd ->
labelExpTdd.text = resourceHelper.gs(R.string.tdd)
labelExpTdd.setTextColor(Color.WHITE)
})
etrHead.addView(TextView(this).also { labelExpRatio ->
labelExpRatio.text = resourceHelper.gs(R.string.ratio)
labelExpRatio.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
)
binding.reload.setOnClickListener {
binding.reload.visibility = View.GONE
binding.connectionStatus.visibility = View.VISIBLE
binding.message.visibility = View.VISIBLE
binding.message.text = resourceHelper.gs(R.string.warning_Message)
commandQueue.loadTDDs(object : Callback() {
override fun run() {
loadDataFromDB()
runOnUiThread {
binding.reload.visibility = View.VISIBLE
binding.connectionStatus.visibility = View.GONE
binding.message.visibility = View.GONE
}
}
})
}
binding.totalBaseBasal.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
binding.totalBaseBasal.clearFocus()
return@setOnEditorActionListener true
}
false
}
binding.totalBaseBasal.setOnFocusChangeListener { _: View?, hasFocus: Boolean ->
if (hasFocus) {
binding.totalBaseBasal.text.clear()
} else {
sp.putString("TBB", binding.totalBaseBasal.text.toString())
tbb = sp.getString("TBB", "")
loadDataFromDB()
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(binding.totalBaseBasal.windowToken, 0)
}
}
loadDataFromDB()
}
override fun onResume() {
super.onResume()
disposable.add(rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ event -> binding.connectionStatus.text = event.getStatus(resourceHelper) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventDanaRSyncStatus::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ event ->
aapsLogger.debug("EventDanaRSyncStatus: " + event.message)
binding.connectionStatus.text = event.message
}, fabricPrivacy::logException)
)
}
override fun onPause() {
super.onPause()
disposable.clear()
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val myView = currentFocus
if (myView is EditText) {
val rect = Rect()
myView.getGlobalVisibleRect(rect)
if (!rect.contains(event.rawX.toInt(), event.rawY.toInt())) {
myView.clearFocus()
}
}
}
return super.dispatchTouchEvent(event)
}
@SuppressLint("SetTextI18n")
private fun loadDataFromDB() {
historyList.clear()
historyList.addAll(databaseHelper.getTDDs())
//only use newest 10
historyList = historyList.subList(0, min(10, historyList.size))
//fill single gaps
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
for (i in 0 until historyList.size - 1) {
val elem1 = historyList[i]
val elem2 = historyList[i + 1]
if (df.format(Date(elem1.date)) != df.format(Date(elem2.date + 25 * 60 * 60 * 1000))) {
val dummy = TDD()
dummy.date = elem1.date - 24 * 60 * 60 * 1000
dummy.basal = elem1.basal / 2
dummy.bolus = elem1.bolus / 2
dummies.add(dummy)
elem1.basal /= 2.0
elem1.bolus /= 2.0
}
}
historyList.addAll(dummies)
historyList.sortWith { lhs: TDD, rhs: TDD -> (rhs.date - lhs.date).toInt() }
runOnUiThread {
cleanTable(binding.mainTable)
cleanTable(binding.cumulativeTable)
cleanTable(binding.expweightTable)
val df1: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
if (TextUtils.isEmpty(tbb)) {
binding.totalBaseBasal.error = "Please Enter Total Base Basal"
return@runOnUiThread
} else {
magicNumber = SafeParse.stringToDouble(tbb)
}
magicNumber *= 2.0
binding.totalBaseBasal2.text = decimalFormat.format(magicNumber)
var i = 0
var sum = 0.0
var weighted03 = 0.0
var weighted05 = 0.0
var weighted07 = 0.0
//TDD table
for (record in historyList) {
val tdd = record.getTotal()
// Create the table row
binding.mainTable.addView(
TableRow(this@TDDStatsActivity).also { tr ->
if (i % 2 != 0) tr.setBackgroundColor(Color.DKGRAY)
if (dummies.contains(record))
tr.setBackgroundColor(Color.argb(125, 255, 0, 0))
tr.id = 100 + i
tr.layoutParams = TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT)
// Here create the TextView dynamically
tr.addView(TextView(this@TDDStatsActivity).also { labelDATE ->
labelDATE.id = 200 + i
labelDATE.text = df1.format(Date(record.date))
labelDATE.setTextColor(Color.WHITE)
})
tr.addView(TextView(this@TDDStatsActivity).also { labelBASAL ->
labelBASAL.id = 300 + i
labelBASAL.text = resourceHelper.gs(R.string.formatinsulinunits, record.basal)
labelBASAL.setTextColor(Color.WHITE)
})
tr.addView(TextView(this@TDDStatsActivity).also { labelBOLUS ->
labelBOLUS.id = 400 + i
labelBOLUS.text = resourceHelper.gs(R.string.formatinsulinunits, record.bolus)
labelBOLUS.setTextColor(Color.WHITE)
})
tr.addView(TextView(this@TDDStatsActivity).also { labelTDD ->
labelTDD.id = 500 + i
labelTDD.text = resourceHelper.gs(R.string.formatinsulinunits, tdd)
labelTDD.setTextColor(Color.WHITE)
})
tr.addView(TextView(this@TDDStatsActivity).also { labelRATIO ->
labelRATIO.id = 600 + i
labelRATIO.text = (100 * tdd / magicNumber).roundToInt().toString() + "%"
labelRATIO.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT))
i++
}
i = 0
//cumulative TDDs
for (record in historyList) {
if (historyList.isNotEmpty() && df1.format(Date(record.date)) == df1.format(Date()))
//Today should not be included
continue
i++
sum += record.getTotal()
// Create the cumulative table row
binding.cumulativeTable.addView(
TableRow(this@TDDStatsActivity).also { ctr ->
if (i % 2 == 0) ctr.setBackgroundColor(Color.DKGRAY)
ctr.id = 700 + i
ctr.layoutParams = TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
// Here create the TextView dynamically
ctr.addView(TextView(this@TDDStatsActivity).also { labelDAYS ->
labelDAYS.id = 800 + i
labelDAYS.text = i.toString()
labelDAYS.setTextColor(Color.WHITE)
})
ctr.addView(TextView(this@TDDStatsActivity).also { labelCUMTDD ->
labelCUMTDD.id = 900 + i
labelCUMTDD.text = resourceHelper.gs(R.string.formatinsulinunits, sum / i)
labelCUMTDD.setTextColor(Color.WHITE)
})
ctr.addView(TextView(this@TDDStatsActivity).also { labelCUMRATIO ->
labelCUMRATIO.id = 1000 + i
labelCUMRATIO.text = (100 * sum / i / magicNumber).roundToInt().toString() + "%"
labelCUMRATIO.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
)
}
if (isOldData(historyList) && activePlugin.activePump.pumpDescription.needsManualTDDLoad) {
binding.message.visibility = View.VISIBLE
binding.message.text = resourceHelper.gs(R.string.olddata_Message)
} else binding.mainTable.setBackgroundColor(Color.TRANSPARENT)
if (historyList.isNotEmpty() && df1.format(Date(historyList[0].date)) == df1.format(Date())) {
//Today should not be included
historyList.removeAt(0)
}
historyList.reverse()
i = 0
for (record in historyList) {
val tdd = record.getTotal()
if (i == 0) {
weighted03 = tdd
weighted05 = tdd
weighted07 = tdd
} else {
weighted07 = weighted07 * 0.3 + tdd * 0.7
weighted05 = weighted05 * 0.5 + tdd * 0.5
weighted03 = weighted03 * 0.7 + tdd * 0.3
}
i++
}
// Create the exponential table row
binding.expweightTable.addView(
TableRow(this@TDDStatsActivity).also { etr ->
if (i % 2 != 0) etr.setBackgroundColor(Color.DKGRAY)
etr.id = 1100 + i
etr.layoutParams = TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
// Here create the TextView dynamically
etr.addView(TextView(this@TDDStatsActivity).also { labelWEIGHT ->
labelWEIGHT.id = 1200 + i
labelWEIGHT.text = "0.3\n0.5\n0.7"
labelWEIGHT.setTextColor(Color.WHITE)
})
etr.addView(TextView(this@TDDStatsActivity).also { labelEXPTDD ->
labelEXPTDD.id = 1300 + i
labelEXPTDD.text = """
${resourceHelper.gs(R.string.formatinsulinunits, weighted03)}
${resourceHelper.gs(R.string.formatinsulinunits, weighted05)}
${resourceHelper.gs(R.string.formatinsulinunits, weighted07)}
""".trimIndent()
labelEXPTDD.setTextColor(Color.WHITE)
})
etr.addView(TextView(this@TDDStatsActivity).also { labelEXPRATIO ->
labelEXPRATIO.id = 1400 + i
labelEXPRATIO.text = """
${(100 * weighted03 / magicNumber).roundToInt()}%
${(100 * weighted05 / magicNumber).roundToInt()}%
${(100 * weighted07 / magicNumber).roundToInt()}%
""".trimIndent()
labelEXPRATIO.setTextColor(Color.WHITE)
})
}, TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT)
)
}
}
private fun cleanTable(table: TableLayout) {
val childCount = table.childCount
// Remove all rows except the first one
if (childCount > 1) table.removeViews(1, childCount - 1)
}
private fun isOldData(historyList: List<TDD>): Boolean {
val type = activePlugin.activePump.pumpDescription.pumpType
val startsYesterday = type == PumpType.DanaR || type == PumpType.DanaRS || type == PumpType.DanaRv2 || type == PumpType.DanaRKorean || type == PumpType.AccuChekInsight
val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
return historyList.size < 3 || df.format(Date(historyList[0].date)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0))
}
}

View file

@ -13,7 +13,7 @@ class StaticInjector @Inject constructor(
companion object {
private var instance : StaticInjector? = null
@Deprecated("Only until DB is refactored")
@Deprecated("Use only for classes instantiated by 3rd party")
fun getInstance() : StaticInjector {
if (instance == null) throw IllegalStateException("StaticInjector not initialized")
return instance!!

View file

@ -1,15 +0,0 @@
package info.nightscout.androidaps.interfaces;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
/**
* Created by mike on 10.06.2016.
*/
public interface APSInterface {
APSResult getLastAPSResult();
long getLastAPSRun();
void invoke(String initiator, boolean tempBasalFallback);
}

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.interfaces
import info.nightscout.androidaps.plugins.aps.loop.APSResult
interface APSInterface {
val lastAPSResult: APSResult?
val lastAPSRun: Long
operator fun invoke(initiator: String, tempBasalFallback: Boolean)
}

View file

@ -8,6 +8,7 @@ import android.os.Binder
import android.os.Handler
import android.os.IBinder
import dagger.android.DaggerService
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.NotificationHolderInterface
import info.nightscout.androidaps.logging.AAPSLogger
@ -69,7 +70,7 @@ class AlarmSoundService : DaggerService() {
player?.let { if (it.isPlaying) it.stop() }
if (intent?.hasExtra("soundid") == true) resourceId = intent.getIntExtra("soundid", R.raw.error)
if (intent?.hasExtra(ErrorHelperActivity.SOUND_ID) == true) resourceId = intent.getIntExtra(ErrorHelperActivity.SOUND_ID, R.raw.error)
player = MediaPlayer()
try {
val afd = resourceHelper.openRawResourceFd(resourceId) ?: return START_STICKY

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