SMB Rhino

This commit is contained in:
AdrianLxM 2017-10-21 22:38:07 +02:00
parent 7ed7a2e8c4
commit 755c82898c
2 changed files with 189 additions and 182 deletions

View file

@ -5,12 +5,23 @@ import com.eclipsesource.v8.V8;
import com.eclipsesource.v8.V8Array; import com.eclipsesource.v8.V8Array;
import com.eclipsesource.v8.V8Object; import com.eclipsesource.v8.V8Object;
import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Date; import java.util.Date;
import info.nightscout.androidaps.Config; 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.IobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.plugins.Loop.ScriptReader;
import info.nightscout.androidaps.data.Profile; 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; import info.nightscout.utils.SP;
public class DetermineBasalAdapterSMBJS { public class DetermineBasalAdapterSMBJS {
@ -31,26 +44,17 @@ public class DetermineBasalAdapterSMBJS {
private ScriptReader mScriptReader = null; private ScriptReader mScriptReader = null;
V8 mV8rt; private JSONObject mProfile;
private V8Object mProfile; private JSONObject mGlucoseStatus;
private V8Object mGlucoseStatus; private JSONArray mIobData;
private V8Array mIobData; private JSONObject mMealData;
private V8Object mMealData; private JSONObject mCurrentTemp;
private V8Object mCurrentTemp; private JSONObject mAutosensData = null;
private V8Object mAutosensData = null; private boolean mMicrobolusAllowed;
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 String storedCurrentTemp = null; private String storedCurrentTemp = null;
private String storedIobData = null; private String storedIobData = null;
private String storedGlucoseStatus = null; private String storedGlucoseStatus = null;
private String storedProfile = null; private String storedProfile = null;
private String storedMeal_data = null; private String storedMeal_data = null;
@ -64,55 +68,108 @@ public class DetermineBasalAdapterSMBJS {
*/ */
public DetermineBasalAdapterSMBJS(ScriptReader scriptReader) throws IOException { public DetermineBasalAdapterSMBJS(ScriptReader scriptReader) throws IOException {
mV8rt = V8.createV8Runtime();
mScriptReader = scriptReader; mScriptReader = scriptReader;
initLogCallback();
initProcessExitCallback();
initModuleParent();
loadScript();
} }
public DetermineBasalResultSMB invoke() { public DetermineBasalResultSMB invoke() {
log.debug(">>> Invoking detemine_basal_oref1 <<<");
log.debug("Glucose status: " + (storedGlucoseStatus = mV8rt.executeStringScript("JSON.stringify(" + PARAM_glucoseStatus + ");"))); log.debug(">>> Invoking detemine_basal <<<");
log.debug("IOB data: " + (storedIobData = mV8rt.executeStringScript("JSON.stringify(" + PARAM_iobData + ");"))); log.debug("Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString()));
log.debug("Current temp: " + (storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");"))); log.debug("IOB data: " + (storedIobData = mIobData.toString()));
log.debug("Profile: " + (storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");"))); log.debug("Current temp: " + (storedCurrentTemp = mCurrentTemp.toString()));
log.debug("Meal data: " + (storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");"))); log.debug("Profile: " + (storedProfile = mProfile.toString()));
log.debug("Meal data: " + (storedMeal_data = mMealData.toString()));
if (mAutosensData != null) 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 else
log.debug("Autosens data: " + (storedAutosens_data = "undefined")); log.debug("Autosens data: " + (storedAutosens_data = "undefined"));
log.debug("Reservoir data: " + "undefined"); log.debug("Reservoir data: " + "undefined");
log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = mV8rt.executeStringScript("JSON.stringify(" + PARAM_microBolusAllowed + ");"))); log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed));
mV8rt.executeVoidScript( DetermineBasalResultSMB determineBasalResultSMB = null;
"var rT = determine_basal(" +
PARAM_glucoseStatus + ", " +
PARAM_currentTemp + ", " +
PARAM_iobData + ", " +
PARAM_profile + ", " +
PARAM_autosens_data + ", " +
PARAM_meal_data + ", " +
"tempBasalFunctions" + ", " +
PARAM_microBolusAllowed + ", " +
PARAM_reservoirData +
");");
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 { try {
result = new DetermineBasalResultSMB(new JSONObject(ret));
//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) { } catch (JSONException e) {
e.printStackTrace(); 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() { String getGlucoseStatusParam() {
@ -147,60 +204,6 @@ public class DetermineBasalAdapterSMBJS {
return scriptDebug; 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, public void setData(Profile profile,
double maxIob, double maxIob,
double maxBasal, double maxBasal,
@ -214,102 +217,104 @@ public class DetermineBasalAdapterSMBJS {
double autosensDataRatio, double autosensDataRatio,
boolean tempTargetSet, boolean tempTargetSet,
boolean microBolusAllowed boolean microBolusAllowed
) { ) throws JSONException {
String units = profile.getUnits(); String units = profile.getUnits();
mProfile = new V8Object(mV8rt); mProfile = new JSONObject();;
mProfile.add("max_iob", maxIob); mProfile.put("max_iob", maxIob);
mProfile.add("dia", profile.getDia()); mProfile.put("dia", profile.getDia());
mProfile.add("type", "current"); mProfile.put("type", "current");
mProfile.add("max_daily_basal", profile.getMaxDailyBasal()); mProfile.put("max_daily_basal", profile.getMaxDailyBasal());
mProfile.add("max_basal", maxBasal); mProfile.put("max_basal", maxBasal);
mProfile.add("min_bg", minBg); mProfile.put("min_bg", minBg);
mProfile.add("max_bg", maxBg); mProfile.put("max_bg", maxBg);
mProfile.add("target_bg", targetBg); mProfile.put("target_bg", targetBg);
mProfile.add("carb_ratio", profile.getIc()); mProfile.put("carb_ratio", profile.getIc());
mProfile.add("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units)); mProfile.put("sens", Profile.toMgdl(profile.getIsf().doubleValue(), units));
mProfile.add("max_daily_safety_multiplier", SP.getInt("openapsama_max_daily_safety_multiplier", 3)); mProfile.put("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.put("current_basal_safety_multiplier", SP.getInt("openapsama_current_basal_safety_multiplier", 4));
mProfile.add("skip_neutral_temps", true); mProfile.put("skip_neutral_temps", true);
mProfile.add("current_basal", pump.getBaseBasalRate()); mProfile.put("current_basal", pump.getBaseBasalRate());
mProfile.add("temptargetSet", tempTargetSet); mProfile.put("temptargetSet", tempTargetSet);
mProfile.add("autosens_adjust_targets", SP.getBoolean("openapsama_autosens_adjusttargets", true)); mProfile.put("autosens_adjust_targets", SP.getBoolean("openapsama_autosens_adjusttargets", true));
mProfile.add("min_5m_carbimpact", SP.getDouble("openapsama_min_5m_carbimpact", 3d)); mProfile.put("min_5m_carbimpact", SP.getDouble("openapsama_min_5m_carbimpact", 3d));
mProfile.add("enableSMB_with_bolus", SP.getBoolean(R.string.key_use_smb, false)); mProfile.put("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.put("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.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false));
mProfile.add("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("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.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. // 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. // (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) // Essentially, this just limits AMA as a safety cap against weird COB calculations)
mProfile.add("maxCOB", 120); mProfile.put("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.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.add("remainingCarbsFraction", 1.0d); // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption mProfile.put("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 mProfile.put("remainingCarbsCap", 90); // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
mV8rt.add(PARAM_profile, mProfile);
mCurrentTemp = new V8Object(mV8rt); mCurrentTemp = new JSONObject();;
mCurrentTemp.add("temp", "absolute"); mCurrentTemp.put("temp", "absolute");
mCurrentTemp.add("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); mCurrentTemp.put("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory());
mCurrentTemp.add("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()); mCurrentTemp.put("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory());
// as we have non default temps longer than 30 mintues // as we have non default temps longer than 30 mintues
TemporaryBasal tempBasal = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis()); TemporaryBasal tempBasal = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
if (tempBasal != null) { 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()); mGlucoseStatus = new JSONObject();;
mV8rt.add(PARAM_iobData, mIobData); mGlucoseStatus.put("glucose", glucoseStatus.glucose);
mGlucoseStatus = new V8Object(mV8rt);
mGlucoseStatus.add("glucose", glucoseStatus.glucose);
if (SP.getBoolean("always_use_shortavg", false)) { if (SP.getBoolean("always_use_shortavg", false)) {
mGlucoseStatus.add("delta", glucoseStatus.short_avgdelta); mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta);
} else { } else {
mGlucoseStatus.add("delta", glucoseStatus.delta); mGlucoseStatus.put("delta", glucoseStatus.delta);
} }
mGlucoseStatus.add("short_avgdelta", glucoseStatus.short_avgdelta); mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta);
mGlucoseStatus.add("long_avgdelta", glucoseStatus.long_avgdelta); mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta);
mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus);
mMealData = new V8Object(mV8rt); mMealData = new JSONObject();;
mMealData.add("carbs", mealData.carbs); mMealData.put("carbs", mealData.carbs);
mMealData.add("boluses", mealData.boluses); mMealData.put("boluses", mealData.boluses);
mMealData.add("mealCOB", mealData.mealCOB); mMealData.put("mealCOB", mealData.mealCOB);
mMealData.add("minDeviationSlope", mealData.minDeviationSlope); mMealData.put("minDeviationSlope", mealData.minDeviationSlope);
mMealData.add("lastBolusTime", mealData.lastBolusTime); mMealData.put("lastBolusTime", mealData.lastBolusTime);
mV8rt.add(PARAM_meal_data, mMealData);
if (MainApp.getConfigBuilder().isAMAModeEnabled()) { if (MainApp.getConfigBuilder().isAMAModeEnabled()) {
mAutosensData = new V8Object(mV8rt); mAutosensData = new JSONObject();;
mAutosensData.add("ratio", autosensDataRatio); mAutosensData.put("ratio", autosensDataRatio);
mV8rt.add(PARAM_autosens_data, mAutosensData);
} else { } else {
mV8rt.addUndefined(PARAM_autosens_data); mAutosensData = null;
} }
mMicrobolusAllowed = microBolusAllowed;
mV8rt.addUndefined(PARAM_reservoirData);
mV8rt.add(PARAM_microBolusAllowed, microBolusAllowed);
} }
public Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) {
public void release() { if(jsonObject == null) return Undefined.instance;
mProfile.release();
mCurrentTemp.release(); Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), new Callable() {
mIobData.release(); @Override
mMealData.release(); public Object call(Context context, Scriptable scriptable, Scriptable scriptable1, Object[] objects) {
mGlucoseStatus.release(); return objects[1];
if (mAutosensData != null) {
mAutosensData.release();
} }
mV8rt.release(); });
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 { public String readFile(String filename) throws IOException {

View file

@ -224,11 +224,16 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface {
Profiler.log(log, "AMA data gathering", start); Profiler.log(log, "AMA data gathering", start);
start = new Date(); start = new Date();
try {
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData, determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData,
lastAutosensResult.ratio, //autosensDataRatio lastAutosensResult.ratio, //autosensDataRatio
isTempTarget, isTempTarget,
true //microBolusAllowed true //microBolusAllowed
); );
} catch (JSONException e) {
log.error(e.getMessage());
return;
}
DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke(); DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke();
@ -245,9 +250,6 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface {
} }
determineBasalResultSMB.iob = iobArray[0]; determineBasalResultSMB.iob = iobArray[0];
determineBasalAdapterSMBJS.release();
Date now = new Date(); Date now = new Date();
try { try {