package info.nightscout.androidaps.plugins.OpenAPSAMA; import android.content.SharedPreferences; import android.preference.PreferenceManager; import com.eclipsesource.v8.JavaVoidCallback; import com.eclipsesource.v8.V8; import com.eclipsesource.v8.V8Array; import com.eclipsesource.v8.V8Object; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.GlucoseStatus; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.db.TempBasal; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; public class DetermineBasalAdapterAMAJS { private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterAMAJS.class); 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 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 = ""; /** * Main code */ public DetermineBasalAdapterAMAJS(ScriptReader scriptReader) throws IOException { mV8rt = V8.createV8Runtime(); mScriptReader = scriptReader; initLogCallback(); initProcessExitCallback(); initModuleParent(); loadScript(); } public DetermineBasalResultAMA invoke() { log.debug(">>> Invoking detemine_basal <<<"); 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 + ");"))); if (mAutosensData != null) log.debug("Autosens data: " + (storedAutosens_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_autosens_data + ");"))); else log.debug("Autosens data: " + (storedAutosens_data = "undefined")); mV8rt.executeVoidScript( "var rT = determine_basal(" + PARAM_glucoseStatus + ", " + PARAM_currentTemp + ", " + PARAM_iobData + ", " + PARAM_profile + ", " + PARAM_autosens_data + ", " + PARAM_meal_data + ", " + "tempBasalFunctions" + ");"); String ret = mV8rt.executeStringScript("JSON.stringify(rT);"); log.debug("Result: " + ret); V8Object v8ObjectReuslt = mV8rt.getObject("rT"); DetermineBasalResultAMA result = null; try { result = new DetermineBasalResultAMA(v8ObjectReuslt, new JSONObject(ret)); } catch (JSONException e) { e.printStackTrace(); } return result; } 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; } 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("OpenAPSAMA/basal-set-temp.js"), "OpenAPSAMA/basal-set-temp.js ", 0); mV8rt.executeVoidScript("var tempBasalFunctions = module.exports;"); mV8rt.executeVoidScript( readFile("OpenAPSAMA/determine-basal.js"), "OpenAPSAMA/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(NSProfile profile, double maxIob, double maxBasal, double minBg, double maxBg, double targetBg, PumpInterface pump, IobTotal[] iobArray, GlucoseStatus glucoseStatus, MealData mealData, double autosensDataRatio, boolean tempTargetSet, double min_5m_carbimpact) { 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(profile.secondsFromMidnight())); mProfile.add("sens", NSProfile.toMgdl(profile.getIsf(NSProfile.secondsFromMidnight()).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)); mV8rt.add(PARAM_profile, mProfile); mCurrentTemp = new V8Object(mV8rt); mCurrentTemp.add("temp", "absolute"); mCurrentTemp.add("duration", pump.getTempBasalRemainingMinutes()); mCurrentTemp.add("rate", pump.getTempBasalAbsoluteRate()); // as we have non default temps longer than 30 mintues TempBasal tempBasal = pump.getTempBasal(); if(tempBasal != null){ mCurrentTemp.add("minutesrunning", tempBasal.getRealDuration()); } mV8rt.add(PARAM_currentTemp, mCurrentTemp); mIobData = mV8rt.executeArrayScript(IobTotal.convertToJSONArray(iobArray).toString()); mV8rt.add(PARAM_iobData, mIobData); mGlucoseStatus = new V8Object(mV8rt); mGlucoseStatus.add("glucose", glucoseStatus.glucose); mGlucoseStatus.add("delta", glucoseStatus.delta); mGlucoseStatus.add("short_avgdelta", glucoseStatus.short_avgdelta); mGlucoseStatus.add("long_avgdelta", glucoseStatus.long_avgdelta); mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus); mMealData = new V8Object(mV8rt); mMealData.add("carbs", mealData.carbs); mMealData.add("boluses", mealData.boluses); mMealData.add("mealCOB", mealData.mealCOB); mV8rt.add(PARAM_meal_data, mMealData); if (MainApp.getConfigBuilder().isAMAModeEnabled()) { mAutosensData = new V8Object(mV8rt); mAutosensData.add("ratio", autosensDataRatio); mV8rt.add(PARAM_autosens_data, mAutosensData); } else { mV8rt.addUndefined(PARAM_autosens_data); } } public void release() { mProfile.release(); mCurrentTemp.release(); mIobData.release(); mMealData.release(); mGlucoseStatus.release(); if (mAutosensData != null) { mAutosensData.release(); } mV8rt.release(); } public String readFile(String filename) throws IOException { byte[] bytes = mScriptReader.readFile(filename); String string = new String(bytes, "UTF-8"); if (string.startsWith("#!/usr/bin/env node")) { string = string.substring(20); } return string; } }