DetermineBasalAdapterSMBJS -> kt
This commit is contained in:
parent
af1ee97d83
commit
cfa1fc42d7
3 changed files with 298 additions and 386 deletions
|
@ -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.Companion.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -265,19 +265,14 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr
|
||||||
profiler.log(LTag.APS, "SMB data gathering", start);
|
profiler.log(LTag.APS, "SMB data gathering", start);
|
||||||
|
|
||||||
start = System.currentTimeMillis();
|
start = System.currentTimeMillis();
|
||||||
try {
|
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
|
||||||
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
|
lastAutosensResult.ratio, //autosensDataRatio
|
||||||
lastAutosensResult.ratio, //autosensDataRatio
|
isTempTarget,
|
||||||
isTempTarget,
|
smbAllowed.value(),
|
||||||
smbAllowed.value(),
|
uam.value(),
|
||||||
uam.value(),
|
advancedFiltering.value(),
|
||||||
advancedFiltering.value(),
|
activePlugin.getActiveBgSource().getClass().getSimpleName().equals("DexcomPlugin")
|
||||||
activePlugin.getActiveBgSource().getClass().getSimpleName().equals("DexcomPlugin")
|
);
|
||||||
);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
fabricPrivacy.logException(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue