From 755c82898cf8b5633f5c5ec6028bd7b9078c9494 Mon Sep 17 00:00:00 2001 From: AdrianLxM Date: Sat, 21 Oct 2017 22:38:07 +0200 Subject: [PATCH] SMB Rhino --- .../DetermineBasalAdapterSMBJS.java | 353 +++++++++--------- .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 18 +- 2 files changed, 189 insertions(+), 182 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 60803c03b3..7bdd0178d6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -5,12 +5,23 @@ import com.eclipsesource.v8.V8; import com.eclipsesource.v8.V8Array; import com.eclipsesource.v8.V8Object; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.javascript.Callable; +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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.Date; import info.nightscout.androidaps.Config; @@ -24,6 +35,8 @@ import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.plugins.OpenAPSAMA.DetermineBasalResultAMA; +import info.nightscout.androidaps.plugins.OpenAPSMA.LoggerCallback; import info.nightscout.utils.SP; public class DetermineBasalAdapterSMBJS { @@ -31,26 +44,17 @@ public class DetermineBasalAdapterSMBJS { private ScriptReader mScriptReader = null; - V8 mV8rt; - private V8Object mProfile; - private V8Object mGlucoseStatus; - private V8Array mIobData; - private V8Object mMealData; - private V8Object mCurrentTemp; - private V8Object mAutosensData = null; - - private final String PARAM_currentTemp = "currentTemp"; - private final String PARAM_iobData = "iobData"; - private final String PARAM_glucoseStatus = "glucose_status"; - private final String PARAM_profile = "profile"; - private final String PARAM_meal_data = "meal_data"; - private final String PARAM_autosens_data = "autosens_data"; - private final String PARAM_reservoirData = "reservoirData"; - private final String PARAM_microBolusAllowed = "microBolusAllowed"; - + private JSONObject mProfile; + private JSONObject mGlucoseStatus; + private JSONArray mIobData; + private JSONObject mMealData; + private JSONObject mCurrentTemp; + private JSONObject mAutosensData = null; + private boolean mMicrobolusAllowed; private String storedCurrentTemp = null; private String storedIobData = null; + private String storedGlucoseStatus = null; private String storedProfile = null; private String storedMeal_data = null; @@ -64,55 +68,108 @@ public class DetermineBasalAdapterSMBJS { */ public DetermineBasalAdapterSMBJS(ScriptReader scriptReader) throws IOException { - mV8rt = V8.createV8Runtime(); mScriptReader = scriptReader; - - initLogCallback(); - initProcessExitCallback(); - initModuleParent(); - loadScript(); } + public DetermineBasalResultSMB invoke() { - log.debug(">>> Invoking detemine_basal_oref1 <<<"); - log.debug("Glucose status: " + (storedGlucoseStatus = mV8rt.executeStringScript("JSON.stringify(" + PARAM_glucoseStatus + ");"))); - log.debug("IOB data: " + (storedIobData = mV8rt.executeStringScript("JSON.stringify(" + PARAM_iobData + ");"))); - log.debug("Current temp: " + (storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");"))); - log.debug("Profile: " + (storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");"))); - log.debug("Meal data: " + (storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");"))); + + log.debug(">>> Invoking detemine_basal <<<"); + log.debug("Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString())); + log.debug("IOB data: " + (storedIobData = mIobData.toString())); + log.debug("Current temp: " + (storedCurrentTemp = mCurrentTemp.toString())); + log.debug("Profile: " + (storedProfile = mProfile.toString())); + log.debug("Meal data: " + (storedMeal_data = mMealData.toString())); if (mAutosensData != null) - log.debug("Autosens data: " + (storedAutosens_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_autosens_data + ");"))); + log.debug("Autosens data: " + (storedAutosens_data = mAutosensData.toString())); else log.debug("Autosens data: " + (storedAutosens_data = "undefined")); log.debug("Reservoir data: " + "undefined"); - log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = mV8rt.executeStringScript("JSON.stringify(" + PARAM_microBolusAllowed + ");"))); + log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed)); - mV8rt.executeVoidScript( - "var rT = determine_basal(" + - PARAM_glucoseStatus + ", " + - PARAM_currentTemp + ", " + - PARAM_iobData + ", " + - PARAM_profile + ", " + - PARAM_autosens_data + ", " + - PARAM_meal_data + ", " + - "tempBasalFunctions" + ", " + - PARAM_microBolusAllowed + ", " + - PARAM_reservoirData + - ");"); + DetermineBasalResultSMB determineBasalResultSMB = null; + Context rhino = Context.enter(); + Scriptable scope = rhino.initStandardObjects(); + // Turn off optimization to make Rhino Android compatible + rhino.setOptimizationLevel(-1); - String ret = mV8rt.executeStringScript("JSON.stringify(rT);"); - log.debug("Result: " + ret); - - DetermineBasalResultSMB result = null; try { - result = new DetermineBasalResultSMB(new JSONObject(ret)); - } catch (JSONException e) { - e.printStackTrace(); + + //register logger callback for console.log and console.error + ScriptableObject.defineClass(scope, LoggerCallback.class); + Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null); + scope.put("console2", scope, myLogger); + rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null); + + //set module parent + rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null); + rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null); + rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null); + + //generate functions "determine_basal" and "setTempBasal" + rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null); + rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null); + Object determineBasalObj = scope.get("determine_basal", scope); + Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope); + + //call determine-basal + if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) { + Function determineBasalJS = (Function) determineBasalObj; + + //prepare parameters + Object[] params = new Object[]{ + makeParam(mGlucoseStatus, rhino, scope), + makeParam(mCurrentTemp, rhino, scope), + makeParamArray(mIobData, rhino, scope), + makeParam(mProfile, rhino, scope), + makeParam(mAutosensData, rhino, scope), + makeParam(mMealData, rhino, scope), + setTempBasalFunctionsObj, + new Boolean(mMicrobolusAllowed), + makeParam(null,rhino,scope) // reservoir data as undefined + }; + + + + 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(); + if (Config.logAPSResult) + log.debug("Result: " + result); + try { + determineBasalResultSMB = new DetermineBasalResultSMB(new JSONObject(result)); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + } else { + log.debug("Problem loading JS Functions"); + } + } catch (IOException e) { + log.debug("IOException"); + } catch (RhinoException e) { + log.error("RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()); + } catch (IllegalAccessException e) { + log.error(e.toString()); + } catch (InstantiationException e) { + log.error(e.toString()); + } catch (InvocationTargetException e) { + log.error(e.toString()); + } finally { + Context.exit(); } - return result; + storedGlucoseStatus = mGlucoseStatus.toString(); + storedIobData = mIobData.toString(); + storedCurrentTemp = mCurrentTemp.toString(); + storedProfile = mProfile.toString(); + storedMeal_data = mMealData.toString(); + + return determineBasalResultSMB; + } String getGlucoseStatusParam() { @@ -147,60 +204,6 @@ public class DetermineBasalAdapterSMBJS { return scriptDebug; } - private void loadScript() throws IOException { - mV8rt.executeVoidScript("var round_basal = function round_basal(basal, profile) { return basal; };"); - mV8rt.executeVoidScript("require = function() {return round_basal;};"); - - mV8rt.executeVoidScript(readFile("OpenAPSSMB/basal-set-temp.js"), "OpenAPSSMB/basal-set-temp.js ", 0); - mV8rt.executeVoidScript("var tempBasalFunctions = module.exports;"); - - mV8rt.executeVoidScript( - readFile("OpenAPSSMB/determine-basal.js"), - "OpenAPSSMB/determine-basal.js", - 0); - mV8rt.executeVoidScript("var determine_basal = module.exports;"); - } - - private void initModuleParent() { - mV8rt.executeVoidScript("var module = {\"parent\":Boolean(1)};"); - } - - private void initProcessExitCallback() { - JavaVoidCallback callbackProccessExit = new JavaVoidCallback() { - @Override - public void invoke(V8Object arg0, V8Array parameters) { - if (parameters.length() > 0) { - Object arg1 = parameters.get(0); - log.error("ProccessExit " + arg1); - } - } - }; - mV8rt.registerJavaMethod(callbackProccessExit, "proccessExit"); - mV8rt.executeVoidScript("var process = {\"exit\": function () { proccessExit(); } };"); - } - - private void initLogCallback() { - JavaVoidCallback callbackLog = new JavaVoidCallback() { - @Override - public void invoke(V8Object arg0, V8Array parameters) { - int i = 0; - String s = ""; - while (i < parameters.length()) { - Object arg = parameters.get(i); - s += arg + " "; - i++; - } - if (!s.equals("") && Config.logAPSResult) { - log.debug("Script debug: " + s); - scriptDebug += s + "\n"; - } - } - }; - mV8rt.registerJavaMethod(callbackLog, "log"); - mV8rt.executeVoidScript("var console = {\"log\":log, \"error\":log};"); - } - - public void setData(Profile profile, double maxIob, double maxBasal, @@ -214,102 +217,104 @@ public class DetermineBasalAdapterSMBJS { double autosensDataRatio, boolean tempTargetSet, boolean microBolusAllowed - ) { + ) throws JSONException { String units = profile.getUnits(); - mProfile = new V8Object(mV8rt); - mProfile.add("max_iob", maxIob); - mProfile.add("dia", profile.getDia()); - mProfile.add("type", "current"); - mProfile.add("max_daily_basal", profile.getMaxDailyBasal()); - mProfile.add("max_basal", maxBasal); - mProfile.add("min_bg", minBg); - mProfile.add("max_bg", maxBg); - mProfile.add("target_bg", targetBg); - mProfile.add("carb_ratio", profile.getIc()); - mProfile.add("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units)); - mProfile.add("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3)); - mProfile.add("current_basal_safety_multiplier", SP.getInt("openapsama_current_basal_safety_multiplier", 4)); - mProfile.add("skip_neutral_temps", true); - mProfile.add("current_basal", pump.getBaseBasalRate()); - mProfile.add("temptargetSet", tempTargetSet); - mProfile.add("autosens_adjust_targets", SP.getBoolean("openapsama_autosens_adjusttargets", true)); - mProfile.add("min_5m_carbimpact", SP.getDouble("openapsama_min_5m_carbimpact", 3d)); - mProfile.add("enableSMB_with_bolus", SP.getBoolean(R.string.key_use_smb, false)); - mProfile.add("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false)); - mProfile.add("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false)); - mProfile.add("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); - mProfile.add("adv_target_adjustments", true); // lower target automatically when BG and eventualBG are high + 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.toMgdl(profile.getIsf().doubleValue(), units)); + mProfile.put("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3)); + mProfile.put("current_basal_safety_multiplier", SP.getInt("openapsama_current_basal_safety_multiplier", 4)); + mProfile.put("skip_neutral_temps", true); + mProfile.put("current_basal", pump.getBaseBasalRate()); + mProfile.put("temptargetSet", tempTargetSet); + mProfile.put("autosens_adjust_targets", SP.getBoolean("openapsama_autosens_adjusttargets", true)); + mProfile.put("min_5m_carbimpact", SP.getDouble("openapsama_min_5m_carbimpact", 3d)); + mProfile.put("enableSMB_with_bolus", SP.getBoolean(R.string.key_use_smb, false)); + mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false)); + mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false)); + mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); + mProfile.put("adv_target_adjustments", true); // lower target automatically when BG and eventualBG are high // create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours. // (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120. // Essentially, this just limits AMA as a safety cap against weird COB calculations) - mProfile.add("maxCOB", 120); - mProfile.add("autotune_isf_adjustmentFraction", 0.5); // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF. - mProfile.add("remainingCarbsFraction", 1.0d); // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption - mProfile.add("remainingCarbsCap", 90); // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption - mV8rt.add(PARAM_profile, mProfile); + mProfile.put("maxCOB", 120); + mProfile.put("autotune_isf_adjustmentFraction", 0.5); // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF. + mProfile.put("remainingCarbsFraction", 1.0d); // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption + mProfile.put("remainingCarbsCap", 90); // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption - mCurrentTemp = new V8Object(mV8rt); - mCurrentTemp.add("temp", "absolute"); - mCurrentTemp.add("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); - mCurrentTemp.add("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()); + mCurrentTemp = new JSONObject();; + mCurrentTemp.put("temp", "absolute"); + mCurrentTemp.put("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); + mCurrentTemp.put("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()); // as we have non default temps longer than 30 mintues TemporaryBasal tempBasal = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); if (tempBasal != null) { - mCurrentTemp.add("minutesrunning", tempBasal.getRealDuration()); + mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration()); } - mV8rt.add(PARAM_currentTemp, mCurrentTemp); + mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray); - mIobData = mV8rt.executeArrayScript(IobCobCalculatorPlugin.convertToJSONArray(iobArray).toString()); - mV8rt.add(PARAM_iobData, mIobData); - - mGlucoseStatus = new V8Object(mV8rt); - mGlucoseStatus.add("glucose", glucoseStatus.glucose); + mGlucoseStatus = new JSONObject();; + mGlucoseStatus.put("glucose", glucoseStatus.glucose); if (SP.getBoolean("always_use_shortavg", false)) { - mGlucoseStatus.add("delta", glucoseStatus.short_avgdelta); + mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta); } else { - mGlucoseStatus.add("delta", glucoseStatus.delta); + mGlucoseStatus.put("delta", glucoseStatus.delta); } - mGlucoseStatus.add("short_avgdelta", glucoseStatus.short_avgdelta); - mGlucoseStatus.add("long_avgdelta", glucoseStatus.long_avgdelta); - mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus); + mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta); + mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta); - mMealData = new V8Object(mV8rt); - mMealData.add("carbs", mealData.carbs); - mMealData.add("boluses", mealData.boluses); - mMealData.add("mealCOB", mealData.mealCOB); - mMealData.add("minDeviationSlope", mealData.minDeviationSlope); - mMealData.add("lastBolusTime", mealData.lastBolusTime); - mV8rt.add(PARAM_meal_data, mMealData); + mMealData = new JSONObject();; + mMealData.put("carbs", mealData.carbs); + mMealData.put("boluses", mealData.boluses); + mMealData.put("mealCOB", mealData.mealCOB); + mMealData.put("minDeviationSlope", mealData.minDeviationSlope); + mMealData.put("lastBolusTime", mealData.lastBolusTime); if (MainApp.getConfigBuilder().isAMAModeEnabled()) { - mAutosensData = new V8Object(mV8rt); - mAutosensData.add("ratio", autosensDataRatio); - mV8rt.add(PARAM_autosens_data, mAutosensData); + mAutosensData = new JSONObject();; + mAutosensData.put("ratio", autosensDataRatio); } else { - mV8rt.addUndefined(PARAM_autosens_data); + mAutosensData = null; } - - mV8rt.addUndefined(PARAM_reservoirData); - mV8rt.add(PARAM_microBolusAllowed, microBolusAllowed); + mMicrobolusAllowed = microBolusAllowed; } + public Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { - public void release() { - mProfile.release(); - mCurrentTemp.release(); - mIobData.release(); - mMealData.release(); - mGlucoseStatus.release(); - if (mAutosensData != null) { - mAutosensData.release(); - } - mV8rt.release(); + if(jsonObject == null) return Undefined.instance; + + Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), new Callable() { + @Override + public Object call(Context context, Scriptable scriptable, Scriptable scriptable1, Object[] objects) { + return objects[1]; + } + }); + return param; + } + + public 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(), new Callable() { + @Override + public Object call(Context context, Scriptable scriptable, Scriptable scriptable1, Object[] objects) { + return objects[1]; + } + }); + return param; } public String readFile(String filename) throws IOException { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java index 44490d6151..7d5a8c7458 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java @@ -224,11 +224,16 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { Profiler.log(log, "AMA data gathering", start); start = new Date(); - determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData, - lastAutosensResult.ratio, //autosensDataRatio - isTempTarget, - true //microBolusAllowed - ); + try { + determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData, + lastAutosensResult.ratio, //autosensDataRatio + isTempTarget, + true //microBolusAllowed + ); + } catch (JSONException e) { + log.error(e.getMessage()); + return; + } DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke(); @@ -245,9 +250,6 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { } determineBasalResultSMB.iob = iobArray[0]; - - determineBasalAdapterSMBJS.release(); - Date now = new Date(); try {