copy basic files oref0 dev 2017/01/04, make it running without crash
This commit is contained in:
parent
9d5c2e5dac
commit
8aad003311
7 changed files with 476 additions and 188 deletions
61
app/src/main/assets/OpenAPSAMA/basal-set-temp.js
Normal file
61
app/src/main/assets/OpenAPSAMA/basal-set-temp.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function reason(rT, msg) {
|
||||||
|
rT.reason = (rT.reason ? rT.reason + '. ' : '') + msg;
|
||||||
|
console.error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempBasalFunctions = {};
|
||||||
|
|
||||||
|
tempBasalFunctions.getMaxSafeBasal = function getMaxSafeBasal(profile) {
|
||||||
|
|
||||||
|
var max_daily_safety_multiplier = (isNaN(profile.max_daily_safety_multiplier) || profile.max_daily_safety_multiplier == null) ? 3 : profile.max_daily_safety_multiplier;
|
||||||
|
var current_basal_safety_multiplier = (isNaN(profile.current_basal_safety_multiplier) || profile.current_basal_safety_multiplier == null) ? 4 : profile.current_basal_safety_multiplier;
|
||||||
|
|
||||||
|
return Math.min(profile.max_basal, max_daily_safety_multiplier * profile.max_daily_basal, current_basal_safety_multiplier * profile.current_basal);
|
||||||
|
};
|
||||||
|
|
||||||
|
tempBasalFunctions.setTempBasal = function setTempBasal(rate, duration, profile, rT, currenttemp) {
|
||||||
|
//var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * profile.current_basal);
|
||||||
|
|
||||||
|
var maxSafeBasal = tempBasalFunctions.getMaxSafeBasal(profile);
|
||||||
|
var round_basal = require('./round-basal');
|
||||||
|
|
||||||
|
if (rate < 0) {
|
||||||
|
rate = 0;
|
||||||
|
} // if >30m @ 0 required, zero temp will be extended to 30m instead
|
||||||
|
else if (rate > maxSafeBasal) {
|
||||||
|
rate = maxSafeBasal;
|
||||||
|
}
|
||||||
|
|
||||||
|
var suggestedRate = round_basal(rate, profile);
|
||||||
|
if (typeof(currenttemp) !== 'undefined' && typeof(currenttemp.duration) !== 'undefined' && typeof(currenttemp.rate) !== 'undefined' && currenttemp.duration > 20 && suggestedRate <= currenttemp.rate * 1.2 && suggestedRate >= currenttemp.rate * 0.8) {
|
||||||
|
rT.reason += ", but "+currenttemp.duration+"m left and " + currenttemp.rate + " ~ req " + suggestedRate + "U/hr: no action required";
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestedRate === profile.current_basal) {
|
||||||
|
if (profile.skip_neutral_temps) {
|
||||||
|
if (typeof(currenttemp) !== 'undefined' && typeof(currenttemp.duration) !== 'undefined' && currenttemp.duration > 0) {
|
||||||
|
reason(rT, 'Suggested rate is same as profile rate, a temp basal is active, canceling current temp');
|
||||||
|
rT.duration = 0;
|
||||||
|
rT.rate = 0;
|
||||||
|
return rT;
|
||||||
|
} else {
|
||||||
|
reason(rT, 'Suggested rate is same as profile rate, no temp basal is active, doing nothing');
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reason(rT, 'Setting neutral temp basal of ' + profile.current_basal + 'U/hr');
|
||||||
|
rT.duration = duration;
|
||||||
|
rT.rate = suggestedRate;
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rT.duration = duration;
|
||||||
|
rT.rate = suggestedRate;
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = tempBasalFunctions;
|
|
@ -12,7 +12,42 @@
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, offline, meal_data, setTempBasal) {
|
|
||||||
|
|
||||||
|
var round_basal = require('../round-basal')
|
||||||
|
|
||||||
|
// Rounds value to 'digits' decimal places
|
||||||
|
function round(value, digits)
|
||||||
|
{
|
||||||
|
var scale = Math.pow(10, digits);
|
||||||
|
return Math.round(value * scale) / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we expect BG to rise or fall at the rate of BGI,
|
||||||
|
// adjusted by the rate at which BG would need to rise /
|
||||||
|
// fall to get eventualBG to target over DIA/2 hours
|
||||||
|
function calculate_expected_delta(dia, target_bg, eventual_bg, bgi) {
|
||||||
|
// (hours * mins_per_hour) / 5 = how many 5 minute periods in dia/2
|
||||||
|
var dia_in_5min_blocks = (dia/2 * 60) / 5;
|
||||||
|
var target_delta = target_bg - eventual_bg;
|
||||||
|
var expectedDelta = round(bgi + (target_delta / dia_in_5min_blocks), 1);
|
||||||
|
return expectedDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function convert_bg(value, profile)
|
||||||
|
{
|
||||||
|
if (profile.out_units == "mmol/L")
|
||||||
|
{
|
||||||
|
return round(value / 18, 1).toFixed(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return value.toFixed(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions) {
|
||||||
var rT = { //short for requestedTemp
|
var rT = { //short for requestedTemp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,17 +55,40 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
||||||
rT.error ='Error: could not get current basal rate';
|
rT.error ='Error: could not get current basal rate';
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
|
var basal = profile.current_basal;
|
||||||
|
if (typeof autosens_data !== 'undefined' ) {
|
||||||
|
basal = profile.current_basal * autosens_data.ratio;
|
||||||
|
basal = round_basal(basal, profile);
|
||||||
|
if (basal != profile.current_basal) {
|
||||||
|
console.error("Adjusting basal from "+profile.current_basal+" to "+basal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var bg = glucose_status.glucose;
|
var bg = glucose_status.glucose;
|
||||||
if (bg < 38) { //Dexcom is in ??? mode or calibrating, do nothing. Asked @benwest for raw data in iter_glucose
|
// TODO: figure out how to use raw isig data to estimate BG
|
||||||
rT.error = "CGM is calibrating or in ??? state";
|
if (bg < 39) { //Dexcom is in ??? mode or calibrating
|
||||||
return rT;
|
rT.reason = "CGM is calibrating or in ??? state";
|
||||||
|
if (basal <= currenttemp.rate * 1.2) { // high temp is running
|
||||||
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
|
} else { //do nothing.
|
||||||
|
rT.reason += ", temp " + currenttemp.rate + " <~ current basal " + basal + "U/hr";
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver
|
var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver
|
||||||
|
|
||||||
// if target_bg is set, great. otherwise, if min and max are set, then set target to their average
|
// if target_bg is set, great. otherwise, if min and max are set, then set target to their average
|
||||||
var target_bg;
|
var target_bg;
|
||||||
|
var min_bg;
|
||||||
|
var max_bg;
|
||||||
|
if (typeof profile.min_bg !== 'undefined') {
|
||||||
|
min_bg = profile.min_bg;
|
||||||
|
}
|
||||||
|
if (typeof profile.max_bg !== 'undefined') {
|
||||||
|
max_bg = profile.max_bg;
|
||||||
|
}
|
||||||
if (typeof profile.target_bg !== 'undefined') {
|
if (typeof profile.target_bg !== 'undefined') {
|
||||||
target_bg = profile.target_bg;
|
target_bg = profile.target_bg;
|
||||||
} else {
|
} else {
|
||||||
|
@ -42,61 +100,90 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120
|
||||||
|
if (typeof autosens_data !== 'undefined' && profile.autosens_adjust_targets) {
|
||||||
|
if (profile.temptargetSet) {
|
||||||
|
console.error("Temp Target set, not adjusting with autosens");
|
||||||
|
} else {
|
||||||
|
min_bg = Math.round((min_bg - 60) / autosens_data.ratio) + 60;
|
||||||
|
max_bg = Math.round((max_bg - 60) / autosens_data.ratio) + 60;
|
||||||
|
new_target_bg = Math.round((target_bg - 60) / autosens_data.ratio) + 60;
|
||||||
|
if (target_bg == new_target_bg) {
|
||||||
|
console.error("target_bg unchanged:", new_target_bg);
|
||||||
|
} else {
|
||||||
|
console.error("Adjusting target_bg from", target_bg, "to", new_target_bg);
|
||||||
|
}
|
||||||
|
target_bg = new_target_bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof iob_data === 'undefined' ) {
|
if (typeof iob_data === 'undefined' ) {
|
||||||
rT.error ='Error: iob_data undefined';
|
rT.error ='Error: iob_data undefined';
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' || typeof iob_data.activity === 'undefined') {
|
var iobArray = iob_data;
|
||||||
|
if (typeof(iob_data.length) && iob_data.length > 1) {
|
||||||
|
iob_data = iobArray[0];
|
||||||
|
//console.error(JSON.stringify(iob_data[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' ) {
|
||||||
rT.error ='Error: iob_data missing some property';
|
rT.error ='Error: iob_data missing some property';
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tick;
|
var tick;
|
||||||
|
|
||||||
if (glucose_status.delta >= 0) {
|
if (glucose_status.delta > -0.5) {
|
||||||
tick = "+" + glucose_status.delta;
|
tick = "+" + round(glucose_status.delta,0);
|
||||||
} else {
|
} else {
|
||||||
tick = glucose_status.delta;
|
tick = round(glucose_status.delta,0);
|
||||||
}
|
}
|
||||||
var minDelta = Math.min(glucose_status.delta, glucose_status.avgdelta);
|
var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta);
|
||||||
//var maxDelta = Math.max(glucose_status.delta, glucose_status.avgdelta);
|
var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta);
|
||||||
|
|
||||||
|
var sens = profile.sens;
|
||||||
|
if (typeof autosens_data !== 'undefined' ) {
|
||||||
|
sens = profile.sens / autosens_data.ratio;
|
||||||
|
sens = round(sens, 1);
|
||||||
|
if (sens != profile.sens) {
|
||||||
|
console.error("Adjusting sens from "+profile.sens+" to "+sens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone
|
//calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone
|
||||||
var bgi = Math.round(( -iob_data.activity * profile.sens * 5 )*100)/100;
|
var bgi = round(( -iob_data.activity * sens * 5 ), 2);
|
||||||
// project positive deviations for 15 minutes
|
// project deviations for 30 minutes
|
||||||
var deviation = Math.round( 15 / 5 * ( glucose_status.avgdelta - bgi ) );
|
var deviation = Math.round( 30 / 5 * ( minDelta - bgi ) );
|
||||||
// project negative deviations for 30 minutes
|
// don't overreact to a big negative delta: use minAvgDelta if deviation is negative
|
||||||
if (deviation < 0) {
|
if (deviation < 0) {
|
||||||
deviation = Math.round( 30 / 5 * ( glucose_status.avgdelta - bgi ) );
|
deviation = Math.round( (30 / 5) * ( minAvgDelta - bgi ) );
|
||||||
}
|
}
|
||||||
//console.log("Avg.Delta: " + glucose_status.avgdelta.toFixed(1) + ", BGI: " + bgi.toFixed(1) + " 15m activity projection: " + deviation.toFixed(0));
|
|
||||||
|
|
||||||
// calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity
|
// calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity
|
||||||
var naive_eventualBG = Math.round( bg - (iob_data.iob * profile.sens) );
|
if (iob_data.iob > 0) {
|
||||||
|
var naive_eventualBG = Math.round( bg - (iob_data.iob * sens) );
|
||||||
|
} else { // if IOB is negative, be more conservative and use the lower of sens, profile.sens
|
||||||
|
var naive_eventualBG = Math.round( bg - (iob_data.iob * Math.min(sens, profile.sens) ) );
|
||||||
|
}
|
||||||
// and adjust it for the deviation above
|
// and adjust it for the deviation above
|
||||||
var eventualBG = naive_eventualBG + deviation;
|
var eventualBG = naive_eventualBG + deviation;
|
||||||
// calculate what portion of that is due to bolussnooze
|
// calculate what portion of that is due to bolussnooze
|
||||||
var bolusContrib = iob_data.bolussnooze * profile.sens;
|
var bolusContrib = iob_data.bolussnooze * sens;
|
||||||
// and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime
|
// and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime
|
||||||
var naive_snoozeBG = Math.round( naive_eventualBG + 1.5 * bolusContrib );
|
var naive_snoozeBG = Math.round( naive_eventualBG + 1.5 * bolusContrib );
|
||||||
// adjust that for deviation like we did eventualBG
|
// adjust that for deviation like we did eventualBG
|
||||||
var snoozeBG = naive_snoozeBG + deviation;
|
var snoozeBG = naive_snoozeBG + deviation;
|
||||||
|
|
||||||
//console.log("BG: " + bg +"(" + tick + ","+glucose_status.avgdelta.toFixed(1)+")"+ " -> " + eventualBG + "-" + snoozeBG + " (Unadjusted: " + naive_eventualBG + "-" + naive_snoozeBG + "), BGI: " + bgi);
|
var expectedDelta = calculate_expected_delta(profile.dia, target_bg, eventualBG, bgi);
|
||||||
|
|
||||||
var expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10;
|
|
||||||
//console.log("expectedDelta: " + expectedDelta);
|
|
||||||
|
|
||||||
if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) {
|
if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) {
|
||||||
rT.error ='Error: could not calculate eventualBG';
|
rT.error ='Error: could not calculate eventualBG';
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// min_bg of 90 -> threshold of 70, 110 -> 80, and 130 -> 90
|
// min_bg of 90 -> threshold of 70, 110 -> 80, and 130 -> 90
|
||||||
var threshold = profile.min_bg - 0.5*(profile.min_bg-50);
|
var threshold = min_bg - 0.5*(min_bg-50);
|
||||||
|
|
||||||
rT = {
|
rT = {
|
||||||
'temp': 'absolute'
|
'temp': 'absolute'
|
||||||
|
@ -109,208 +196,292 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
||||||
var basaliob;
|
var basaliob;
|
||||||
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
|
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
|
||||||
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
|
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
|
||||||
// allow meal assist to run when carbs are just barely covered
|
|
||||||
if (minDelta > Math.max(3, bgi) && ( (meal_data.carbs > 0 && (1.1 * meal_data.carbs/profile.carb_ratio > meal_data.boluses + basaliob)) || ( deviation > 25 && minDelta > 7 ) ) ) {
|
// generate predicted future BGs based on IOB, COB, and current absortpion rate
|
||||||
// ignore all covered IOB, and just set eventualBG to the current bg
|
|
||||||
eventualBG = Math.max(bg,eventualBG) + deviation;
|
var COBpredBGs = [];
|
||||||
rT.eventualBG = eventualBG;
|
var aCOBpredBGs = [];
|
||||||
profile.min_bg = 80;
|
var IOBpredBGs = [];
|
||||||
target_bg = (profile.min_bg + profile.max_bg) / 2;
|
COBpredBGs.push(bg);
|
||||||
expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10;
|
aCOBpredBGs.push(bg);
|
||||||
rT.mealAssist = "On: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi;
|
IOBpredBGs.push(bg);
|
||||||
} else {
|
//console.error(meal_data);
|
||||||
rT.mealAssist = "Off: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi;
|
// carb impact and duration are 0 unless changed below
|
||||||
|
var ci = 0;
|
||||||
|
var cid = 0;
|
||||||
|
// calculate current carb absorption rate, and how long to absorb all carbs
|
||||||
|
// CI = current carb impact on BG in mg/dL/5m
|
||||||
|
ci = Math.round((minDelta - bgi)*10)/10;
|
||||||
|
if (meal_data.mealCOB * 2 > meal_data.carbs) {
|
||||||
|
// set ci to a minimum of 3mg/dL/5m (default) if less than half of carbs have absorbed
|
||||||
|
ci = Math.max(profile.min_5m_carbimpact, ci);
|
||||||
}
|
}
|
||||||
if (bg < threshold) { // low glucose suspend mode: BG is < ~80
|
aci = 10;
|
||||||
rT.reason = "BG " + bg + "<" + threshold;
|
//5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m)
|
||||||
if ((glucose_status.delta <= 0 && glucose_status.avgdelta <= 0) || (glucose_status.delta < expectedDelta && glucose_status.avgdelta < expectedDelta)) {
|
cid = meal_data.mealCOB * ( sens / profile.carb_ratio ) / ci;
|
||||||
// BG is still falling / rising slower than predicted
|
acid = meal_data.mealCOB * ( sens / profile.carb_ratio ) / aci;
|
||||||
return setTempBasal(0, 30, profile, rT, offline);
|
console.error("Carb Impact:",ci,"mg/dL per 5m; CI Duration:",Math.round(10*cid/6)/10,"hours");
|
||||||
|
console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",Math.round(10*acid/6)/10,"hours");
|
||||||
|
var minPredBG = 999;
|
||||||
|
var maxPredBG = bg;
|
||||||
|
var eventualPredBG = bg;
|
||||||
|
try {
|
||||||
|
iobArray.forEach(function(iobTick) {
|
||||||
|
//console.error(iobTick);
|
||||||
|
predBGI = round(( -iobTick.activity * sens * 5 ), 2);
|
||||||
|
// predicted deviation impact drops linearly from current deviation down to zero
|
||||||
|
// over 60 minutes (data points every 5m)
|
||||||
|
predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) );
|
||||||
|
IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev;
|
||||||
|
//IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI;
|
||||||
|
// predicted carb impact drops linearly from current carb impact down to zero
|
||||||
|
// eventually accounting for all carbs (if they can be absorbed over DIA)
|
||||||
|
predCI = Math.max(0, ci * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) );
|
||||||
|
predACI = Math.max(0, aci * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) );
|
||||||
|
COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI;
|
||||||
|
aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI;
|
||||||
|
//console.error(predBGI, predCI, predBG);
|
||||||
|
IOBpredBGs.push(IOBpredBG);
|
||||||
|
COBpredBGs.push(COBpredBG);
|
||||||
|
aCOBpredBGs.push(aCOBpredBG);
|
||||||
|
// wait 45m before setting minPredBG
|
||||||
|
if ( COBpredBGs.length > 9 && (COBpredBG < minPredBG) ) { minPredBG = COBpredBG; }
|
||||||
|
if ( COBpredBG > maxPredBG ) { maxPredBG = COBpredBG; }
|
||||||
|
});
|
||||||
|
// set eventualBG to include effect of carbs
|
||||||
|
//console.error("PredBGs:",JSON.stringify(predBGs));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled.");
|
||||||
|
}
|
||||||
|
rT.predBGs = {};
|
||||||
|
IOBpredBGs.forEach(function(p, i, theArray) {
|
||||||
|
theArray[i] = Math.round(Math.min(401,Math.max(39,p)));
|
||||||
|
});
|
||||||
|
for (var i=IOBpredBGs.length-1; i > 12; i--) {
|
||||||
|
if (IOBpredBGs[i-1] != IOBpredBGs[i]) { break; }
|
||||||
|
else { IOBpredBGs.pop(); }
|
||||||
|
}
|
||||||
|
rT.predBGs.IOB = IOBpredBGs;
|
||||||
|
if (meal_data.mealCOB > 0) {
|
||||||
|
aCOBpredBGs.forEach(function(p, i, theArray) {
|
||||||
|
theArray[i] = Math.round(Math.min(401,Math.max(39,p)));
|
||||||
|
});
|
||||||
|
for (var i=aCOBpredBGs.length-1; i > 12; i--) {
|
||||||
|
if (aCOBpredBGs[i-1] != aCOBpredBGs[i]) { break; }
|
||||||
|
else { aCOBpredBGs.pop(); }
|
||||||
}
|
}
|
||||||
if (glucose_status.delta > glucose_status.avgdelta) {
|
rT.predBGs.aCOB = aCOBpredBGs;
|
||||||
|
}
|
||||||
|
if (meal_data.mealCOB > 0 && ci > 0 ) {
|
||||||
|
COBpredBGs.forEach(function(p, i, theArray) {
|
||||||
|
theArray[i] = Math.round(Math.min(401,Math.max(39,p)));
|
||||||
|
});
|
||||||
|
for (var i=COBpredBGs.length-1; i > 12; i--) {
|
||||||
|
if (COBpredBGs[i-1] != COBpredBGs[i]) { break; }
|
||||||
|
else { COBpredBGs.pop(); }
|
||||||
|
}
|
||||||
|
rT.predBGs.COB = COBpredBGs;
|
||||||
|
eventualBG = Math.max(eventualBG, Math.round(COBpredBGs[COBpredBGs.length-1]) );
|
||||||
|
rT.eventualBG = eventualBG;
|
||||||
|
minPredBG = Math.min(minPredBG, eventualBG);
|
||||||
|
// set snoozeBG to minPredBG
|
||||||
|
snoozeBG = Math.round(Math.max(snoozeBG,minPredBG));
|
||||||
|
rT.snoozeBG = snoozeBG;
|
||||||
|
}
|
||||||
|
|
||||||
|
rT.COB=meal_data.mealCOB;
|
||||||
|
rT.IOB=iob_data.iob;
|
||||||
|
rT.reason="COB: " + meal_data.mealCOB + ", Dev: " + deviation + ", BGI: " + bgi + ", ISF: " + convert_bg(sens, profile) + ", Target: " + convert_bg(target_bg, profile) + "; ";
|
||||||
|
if (bg < threshold) { // low glucose suspend mode: BG is < ~80
|
||||||
|
rT.reason += "BG " + convert_bg(bg, profile) + "<" + convert_bg(threshold, profile);
|
||||||
|
if ((glucose_status.delta <= 0 && minDelta <= 0) || (glucose_status.delta < expectedDelta && minDelta < expectedDelta) || bg < 60 ) {
|
||||||
|
// BG is still falling / rising slower than predicted
|
||||||
|
return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp);
|
||||||
|
}
|
||||||
|
if (glucose_status.delta > minDelta) {
|
||||||
rT.reason += ", delta " + glucose_status.delta + ">0";
|
rT.reason += ", delta " + glucose_status.delta + ">0";
|
||||||
} else {
|
} else {
|
||||||
rT.reason += ", avg delta " + glucose_status.avgdelta.toFixed(2) + ">0";
|
rT.reason += ", min delta " + minDelta.toFixed(2) + ">0";
|
||||||
}
|
}
|
||||||
if (currenttemp.rate > profile.current_basal) { // if a high-temp is running
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
rT.reason += ", cancel high temp";
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel high temp
|
return rT;
|
||||||
} else if (currenttemp.duration && eventualBG > profile.max_bg) { // if low-temped and predicted to go high from negative IOB
|
|
||||||
rT.reason += ", cancel low temp";
|
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel low temp
|
|
||||||
}
|
|
||||||
rT.reason += "; no high-temp to cancel";
|
|
||||||
return rT;
|
|
||||||
}
|
|
||||||
if (eventualBG < profile.min_bg) { // if eventual BG is below target:
|
|
||||||
if (rT.mealAssist.indexOf("On") == 0) {
|
|
||||||
rT.reason = "Meal assist: " + meal_data.carbs + "g, " + meal_data.boluses + "U";
|
|
||||||
} else {
|
} else {
|
||||||
rT.reason = "Eventual BG " + eventualBG + "<" + profile.min_bg;
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
// if 5m or 15m avg BG is rising faster than expected delta
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
if (minDelta > expectedDelta && minDelta > 0) {
|
}
|
||||||
if (glucose_status.delta > glucose_status.avgdelta) {
|
}
|
||||||
rT.reason += ", but Delta " + tick + " > Exp. Delta " + expectedDelta;
|
|
||||||
} else {
|
if (eventualBG < min_bg) { // if eventual BG is below target:
|
||||||
rT.reason += ", but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " > Exp. Delta " + expectedDelta;
|
rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " < " + convert_bg(min_bg, profile);
|
||||||
}
|
// if 5m or 30m avg BG is rising faster than expected delta
|
||||||
if (currenttemp.duration > 0) { // if there is currently any temp basal running
|
if (minDelta > expectedDelta && minDelta > 0) {
|
||||||
rT.reason = rT.reason += "; cancel";
|
if (glucose_status.delta > minDelta) {
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
rT.reason += ", but Delta " + tick + " > Exp. Delta " + expectedDelta;
|
||||||
} else {
|
} else {
|
||||||
rT.reason = rT.reason += "; no temp to cancel";
|
rT.reason += ", but Min. Delta " + minDelta.toFixed(2) + " > Exp. Delta " + expectedDelta;
|
||||||
|
}
|
||||||
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
return rT;
|
return rT;
|
||||||
}
|
} else {
|
||||||
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventualBG < profile.min_bg) {
|
if (eventualBG < min_bg) {
|
||||||
// if this is just due to boluses, we can snooze until the bolus IOB decays (at double speed)
|
// if we've bolused recently, we can snooze until the bolus IOB decays (at double speed)
|
||||||
if (snoozeBG > profile.min_bg) { // if adding back in the bolus contribution BG would be above min
|
if (snoozeBG > min_bg) { // if adding back in the bolus contribution BG would be above min
|
||||||
// if BG is falling and high-temped, or rising and low-temped, cancel
|
rT.reason += ", bolus snooze: eventual BG range " + convert_bg(eventualBG, profile) + "-" + convert_bg(snoozeBG, profile);
|
||||||
// compare against zero here, not BGI, because BGI will be highly negative from boluses and no carbs
|
//console.error(currenttemp, basal );
|
||||||
if (glucose_status.delta < 0 && currenttemp.duration > 0 && currenttemp.rate > profile.current_basal) {
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
rT.reason += tick + ", and temp " + currenttemp.rate + " > basal " + profile.current_basal;
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
return rT;
|
||||||
} else if (glucose_status.delta > 0 && currenttemp.duration > 0 && currenttemp.rate < profile.current_basal) {
|
} else {
|
||||||
rT.reason += tick + ", and temp " + currenttemp.rate + " < basal " + profile.current_basal;
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
rT.reason += ", bolus snooze: eventual BG range " + eventualBG + "-" + snoozeBG;
|
|
||||||
return rT;
|
|
||||||
} else {
|
} else {
|
||||||
// calculate 30m low-temp required to get projected BG up to target
|
// calculate 30m low-temp required to get projected BG up to target
|
||||||
// use snoozeBG to more gradually ramp in any counteraction of the user's boluses
|
// use snoozeBG to more gradually ramp in any counteraction of the user's boluses
|
||||||
// multiply by 2 to low-temp faster for increased hypo safety
|
// multiply by 2 to low-temp faster for increased hypo safety
|
||||||
var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / profile.sens);
|
var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens);
|
||||||
if (minDelta < 0 && minDelta > expectedDelta) {
|
if (minDelta < 0 && minDelta > expectedDelta) {
|
||||||
// if we're barely falling, newinsulinReq should be barely negative
|
// if we're barely falling, newinsulinReq should be barely negative
|
||||||
rT.reason += ", Snooze BG " + snoozeBG;
|
rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile);
|
||||||
var newinsulinReq = Math.round(( insulinReq * (minDelta / expectedDelta) ) * 100)/100;
|
var newinsulinReq = round(( insulinReq * (minDelta / expectedDelta) ), 2);
|
||||||
//console.log("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq);
|
//console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq);
|
||||||
insulinReq = newinsulinReq;
|
insulinReq = newinsulinReq;
|
||||||
}
|
}
|
||||||
// rate required to deliver insulinReq less insulin over 30m:
|
// rate required to deliver insulinReq less insulin over 30m:
|
||||||
var rate = profile.current_basal + (2 * insulinReq);
|
var rate = basal + (2 * insulinReq);
|
||||||
rate = Math.round( rate * 1000 ) / 1000;
|
rate = round_basal(rate, profile);
|
||||||
// if required temp < existing temp basal
|
// if required temp < existing temp basal
|
||||||
var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60;
|
var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60;
|
||||||
if (insulinScheduled < insulinReq - 0.2) { // if current temp would deliver >0.2U less than the required insulin, raise the rate
|
if (insulinScheduled < insulinReq - basal*0.3) { // if current temp would deliver a lot (30% of basal) less than the required insulin, raise the rate
|
||||||
rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " < req " + insulinReq + "-0.2U";
|
rT.reason += ", "+currenttemp.duration + "m@" + (currenttemp.rate - basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " < req " + insulinReq + "-" + basal*0.3;
|
||||||
return setTempBasal(rate, 30, profile, rT, offline);
|
return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate > currenttemp.rate - 0.1)) {
|
if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate >= currenttemp.rate * 0.8)) {
|
||||||
rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr";
|
rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr";
|
||||||
return rT;
|
return rT;
|
||||||
} else {
|
} else {
|
||||||
rT.reason += ", setting " + rate + "U/hr";
|
rT.reason += ", setting " + rate + "U/hr";
|
||||||
return setTempBasal(rate, 30, profile, rT, offline);
|
return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there is a low-temp running, and eventualBG would be below min_bg without it, let it run
|
||||||
|
if (round_basal(currenttemp.rate, profile) < round_basal(basal, profile) ) {
|
||||||
|
var lowtempimpact = (currenttemp.rate - basal) * (currenttemp.duration/60) * sens;
|
||||||
|
var adjEventualBG = eventualBG + lowtempimpact;
|
||||||
|
if ( adjEventualBG < min_bg ) {
|
||||||
|
rT.reason += "letting low temp of " + currenttemp.rate + " run.";
|
||||||
|
return rT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if eventual BG is above min but BG is falling faster than expected Delta
|
// if eventual BG is above min but BG is falling faster than expected Delta
|
||||||
if (minDelta < expectedDelta) {
|
if (minDelta < expectedDelta) {
|
||||||
if (glucose_status.delta < glucose_status.avgdelta) {
|
if (glucose_status.delta < minDelta) {
|
||||||
rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Delta " + tick + " < Exp. Delta " + expectedDelta;
|
rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Delta " + tick + " < Exp. Delta " + expectedDelta;
|
||||||
} else {
|
} else {
|
||||||
rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " < Exp. Delta " + expectedDelta;
|
rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Min. Delta " + minDelta.toFixed(2) + " < Exp. Delta " + expectedDelta;
|
||||||
}
|
}
|
||||||
if (currenttemp.duration > 0) { // if there is currently any temp basal running
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
rT.reason = rT.reason += "; cancel";
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
return rT;
|
||||||
} else {
|
} else {
|
||||||
rT.reason = rT.reason += "; no temp to cancel";
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eventualBG or snoozeBG (from minPredBG) is below max_bg
|
||||||
|
if (eventualBG < max_bg || snoozeBG < max_bg) {
|
||||||
|
// if there is a high-temp running and eventualBG > max_bg, let it run
|
||||||
|
if (eventualBG > max_bg && round_basal(currenttemp.rate, profile) > round_basal(basal, profile) ) {
|
||||||
|
rT.reason += ", " + eventualBG + " > " + max_bg + ": no action required (letting high temp of " + currenttemp.rate + " run)."
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (eventualBG < profile.max_bg) {
|
rT.reason += convert_bg(eventualBG, profile)+"-"+convert_bg(snoozeBG, profile)+" in range: no temp required";
|
||||||
rT.reason = eventualBG + " is in range. No temp required";
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
if (currenttemp.duration > 0) { // if there is currently any temp basal running
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
rT.reason = rT.reason += "; cancel";
|
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
|
||||||
}
|
|
||||||
if (offline == 'Offline') {
|
|
||||||
// if no temp is running or required, set the current basal as a temp, so you can see on the pump that the loop is working
|
|
||||||
if ((!currenttemp.duration || (currenttemp.rate == profile.current_basal)) && !rT.duration) {
|
|
||||||
rT.reason = rT.reason + "; setting current basal of " + profile.current_basal + " as temp";
|
|
||||||
return setTempBasal(profile.current_basal, 30, profile, rT, offline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snoozeBG < profile.max_bg) {
|
|
||||||
rT.reason = snoozeBG + " < " + profile.max_bg;
|
|
||||||
if (currenttemp.duration > 0) { // if there is currently any temp basal running
|
|
||||||
rT.reason = rT.reason += "; cancel";
|
|
||||||
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
|
|
||||||
} else {
|
|
||||||
rT.reason = rT.reason += "; no temp to cancel";
|
|
||||||
return rT;
|
return rT;
|
||||||
|
} else {
|
||||||
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// eventual BG is at/above target:
|
// eventual BG is at/above target:
|
||||||
// if iob is over max, just cancel any temps
|
// if iob is over max, just cancel any temps
|
||||||
var basaliob;
|
var basaliob;
|
||||||
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
|
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
|
||||||
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
|
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
|
||||||
rT.reason = "Eventual BG " + eventualBG + ">=" + profile.max_bg + ", ";
|
rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", ";
|
||||||
if (basaliob > max_iob) {
|
if (basaliob > max_iob) {
|
||||||
rT.reason = "basaliob " + basaliob + " > max_iob " + max_iob;
|
rT.reason += "basaliob " + round(basaliob,2) + " > max_iob " + max_iob;
|
||||||
return setTempBasal(0, 0, profile, rT, offline);
|
if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) {
|
||||||
|
rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr";
|
||||||
|
return rT;
|
||||||
|
} else {
|
||||||
|
rT.reason += "; setting current basal of " + basal + " as temp";
|
||||||
|
return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp);
|
||||||
|
}
|
||||||
} else { // otherwise, calculate 30m high-temp required to get projected BG down to target
|
} else { // otherwise, calculate 30m high-temp required to get projected BG down to target
|
||||||
|
|
||||||
// insulinReq is the additional insulin required to get down to max bg:
|
// insulinReq is the additional insulin required to get down to max bg:
|
||||||
// if in meal assist mode, check if snoozeBG is lower, as eventualBG is not dependent on IOB
|
// if in meal assist mode, check if snoozeBG is lower, as eventualBG is not dependent on IOB
|
||||||
var insulinReq = (Math.min(snoozeBG,eventualBG) - target_bg) / profile.sens;
|
var insulinReq = round( (Math.min(snoozeBG,eventualBG) - target_bg) / sens, 2);
|
||||||
if (minDelta < 0 && minDelta > expectedDelta) {
|
if (minDelta < 0 && minDelta > expectedDelta) {
|
||||||
var newinsulinReq = Math.round(( insulinReq * (1 - (minDelta / expectedDelta)) ) * 100)/100;
|
var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2);
|
||||||
//console.log("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq);
|
//console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq);
|
||||||
insulinReq = newinsulinReq;
|
insulinReq = newinsulinReq;
|
||||||
}
|
}
|
||||||
// if that would put us over max_iob, then reduce accordingly
|
// if that would put us over max_iob, then reduce accordingly
|
||||||
if (insulinReq > max_iob-basaliob) {
|
if (insulinReq > max_iob-basaliob) {
|
||||||
rT.reason = "max_iob " + max_iob + ", ";
|
rT.reason += "max_iob " + max_iob + ", ";
|
||||||
insulinReq = max_iob-basaliob;
|
insulinReq = max_iob-basaliob;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rate required to deliver insulinReq more insulin over 30m:
|
// rate required to deliver insulinReq more insulin over 30m:
|
||||||
var rate = profile.current_basal + (2 * insulinReq);
|
var rate = basal + (2 * insulinReq);
|
||||||
rate = Math.round( rate * 1000 ) / 1000;
|
rate = round_basal(rate, profile);
|
||||||
|
|
||||||
|
// var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * basal);
|
||||||
|
|
||||||
|
var maxSafeBasal = tempBasalFunctions.getMaxSafeBasal(profile);
|
||||||
|
|
||||||
var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * profile.current_basal);
|
|
||||||
if (rate > maxSafeBasal) {
|
if (rate > maxSafeBasal) {
|
||||||
rT.reason += "adj. req. rate:"+rate.toFixed(1) +" to maxSafeBasal:"+maxSafeBasal.toFixed(1)+", ";
|
rT.reason += "adj. req. rate: "+rate+" to maxSafeBasal: "+maxSafeBasal+", ";
|
||||||
rate = maxSafeBasal;
|
rate = round_basal(maxSafeBasal, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60;
|
var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60;
|
||||||
if (insulinScheduled > insulinReq + 0.2) { // if current temp would deliver >0.2U more than the required insulin, lower the rate
|
if (insulinScheduled >= insulinReq * 2) { // if current temp would deliver >2x more than the required insulin, lower the rate
|
||||||
rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " > req " + insulinReq + "+0.2U";
|
rT.reason += currenttemp.duration + "m@" + (currenttemp.rate - basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " > 2 * req " + insulinReq + ". Setting temp basal of " + rate + "U/hr";
|
||||||
return setTempBasal(rate, 30, profile, rT, offline);
|
return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set
|
if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set
|
||||||
rT.reason += "no temp, setting " + rate + "U/hr";
|
rT.reason += "no temp, setting " + rate + "U/hr";
|
||||||
return setTempBasal(rate, 30, profile, rT, offline);
|
return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currenttemp.duration > 5 && rate < currenttemp.rate + 0.1) { // if required temp <~ existing temp basal
|
if (currenttemp.duration > 5 && (round_basal(rate, profile) <= round_basal(currenttemp.rate, profile))) { // if required temp <~ existing temp basal
|
||||||
rT.reason += "temp " + currenttemp.rate + " >~ req " + rate + "U/hr";
|
rT.reason += "temp " + currenttemp.rate + " >~ req " + rate + "U/hr";
|
||||||
return rT;
|
return rT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// required temp > existing temp basal
|
// required temp > existing temp basal
|
||||||
rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr";
|
rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr";
|
||||||
return setTempBasal(rate, 30, profile, rT, offline);
|
return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = determine_basal;
|
module.exports = determine_basal;
|
||||||
|
|
46
app/src/main/assets/OpenAPSAMA/round-basal.js
Normal file
46
app/src/main/assets/OpenAPSAMA/round-basal.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
var endsWith = function endsWith(text, val) {
|
||||||
|
return text.indexOf(val, text.length - val.length) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var round_basal = function round_basal(basal, profile) {
|
||||||
|
|
||||||
|
/* x23 and x54 pumps change basal increment depending on how much basal is being delivered:
|
||||||
|
0.025u for 0.025 < x < 0.975
|
||||||
|
0.05u for 1 < x < 9.95
|
||||||
|
0.1u for 10 < x
|
||||||
|
To round numbers nicely for the pump, use a scale factor of (1 / increment). */
|
||||||
|
|
||||||
|
var lowest_rate_scale = 20;
|
||||||
|
|
||||||
|
// Has profile even been passed in?
|
||||||
|
if (typeof profile !== 'undefined')
|
||||||
|
{
|
||||||
|
// Make sure optional model has been set
|
||||||
|
if (typeof profile.model == 'string')
|
||||||
|
{
|
||||||
|
if (endsWith(profile.model, "54") || endsWith(profile.model, "23"))
|
||||||
|
{
|
||||||
|
lowest_rate_scale = 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rounded_result = basal;
|
||||||
|
// Shouldn't need to check against 0 as pumps can't deliver negative basal anyway?
|
||||||
|
if (basal < 1)
|
||||||
|
{
|
||||||
|
rounded_basal = Math.round(basal * lowest_rate_scale) / lowest_rate_scale;
|
||||||
|
}
|
||||||
|
else if (basal < 10)
|
||||||
|
{
|
||||||
|
rounded_basal = Math.round(basal * 20) / 20;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rounded_basal = Math.round(basal * 10) / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rounded_basal;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports = module.exports = round_basal
|
|
@ -209,10 +209,12 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||||
/*
|
/*
|
||||||
* Returns glucose_status for openAPS or null if no actual data available
|
* Returns glucose_status for openAPS or null if no actual data available
|
||||||
*/
|
*/
|
||||||
public static class GlucoseStatus implements Parcelable {
|
public static class GlucoseStatus {
|
||||||
public double glucose = 0d;
|
public double glucose = 0d;
|
||||||
public double delta = 0d;
|
public double delta = 0d;
|
||||||
public double avgdelta = 0d;
|
public double avgdelta = 0d;
|
||||||
|
public double short_avgdelta = 0d; // TODO: add calculation for AMA
|
||||||
|
public double long_avgdelta = 0d; // TODO: add calculation for AMA
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -227,34 +229,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||||
"<b>" + MainApp.sResources.getString(R.string.avgdelta) + "</b>: " + DecimalFormatter.to2Decimal(avgdelta) + " mg/dl");
|
"<b>" + MainApp.sResources.getString(R.string.avgdelta) + "</b>: " + DecimalFormatter.to2Decimal(avgdelta) + " mg/dl");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeDouble(avgdelta);
|
|
||||||
dest.writeDouble(delta);
|
|
||||||
dest.writeDouble(glucose);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Parcelable.Creator<GlucoseStatus> CREATOR = new Parcelable.Creator<GlucoseStatus>() {
|
|
||||||
public GlucoseStatus createFromParcel(Parcel in) {
|
|
||||||
return new GlucoseStatus(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlucoseStatus[] newArray(int size) {
|
|
||||||
return new GlucoseStatus[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private GlucoseStatus(Parcel in) {
|
|
||||||
avgdelta = in.readDouble();
|
|
||||||
delta = in.readDouble();
|
|
||||||
glucose = in.readDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlucoseStatus() {
|
public GlucoseStatus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,21 +34,24 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
private V8Object mIobData;
|
private V8Object mIobData;
|
||||||
private V8Object mMealData;
|
private V8Object mMealData;
|
||||||
private V8Object mCurrentTemp;
|
private V8Object mCurrentTemp;
|
||||||
|
private V8Object mAutosensData = null;
|
||||||
|
|
||||||
private final String PARAM_currentTemp = "currentTemp";
|
private final String PARAM_currentTemp = "currentTemp";
|
||||||
private final String PARAM_iobData = "iobData";
|
private final String PARAM_iobData = "iobData";
|
||||||
private final String PARAM_glucoseStatus = "glucose_status";
|
private final String PARAM_glucoseStatus = "glucose_status";
|
||||||
private final String PARAM_profile = "profile";
|
private final String PARAM_profile = "profile";
|
||||||
private final String PARAM_meal_data = "meal_data";
|
private final String PARAM_meal_data = "meal_data";
|
||||||
|
private final String PARAM_autosens_data = "autosens_data";
|
||||||
|
|
||||||
private String storedCurrentTemp = null;
|
private String storedCurrentTemp = null;
|
||||||
public 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;
|
||||||
|
private String storedAutosens_data = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main code
|
* Main code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public DetermineBasalAdapterAMAJS(ScriptReader scriptReader) throws IOException {
|
public DetermineBasalAdapterAMAJS(ScriptReader scriptReader) throws IOException {
|
||||||
|
@ -102,17 +105,22 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
mMealData = new V8Object(mV8rt);
|
mMealData = new V8Object(mV8rt);
|
||||||
mMealData.add("carbs", 0);
|
mMealData.add("carbs", 0);
|
||||||
mMealData.add("boluses", 0);
|
mMealData.add("boluses", 0);
|
||||||
|
mMealData.add("mealCOB", 0.0d);
|
||||||
mV8rt.add(PARAM_meal_data, mMealData);
|
mV8rt.add(PARAM_meal_data, mMealData);
|
||||||
|
// Autosens data
|
||||||
|
mV8rt.executeVoidScript("autosens_data = undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
public DetermineBasalResultAMA invoke() {
|
public DetermineBasalResultAMA invoke() {
|
||||||
|
|
||||||
mV8rt.executeVoidScript(
|
mV8rt.executeVoidScript(
|
||||||
"console.error(\"determine_basal(\"+\n" +
|
"console.error(\"determine_basal(\"+\n" +
|
||||||
"JSON.stringify(" + PARAM_glucoseStatus + ")+ \", \" +\n" +
|
"JSON.stringify(" + PARAM_glucoseStatus + ")+ \", \" +\n" +
|
||||||
"JSON.stringify(" + PARAM_currentTemp + ")+ \", \" +\n" +
|
"JSON.stringify(" + PARAM_currentTemp + ")+ \", \" +\n" +
|
||||||
"JSON.stringify(" + PARAM_iobData + ")+ \", \" +\n" +
|
"JSON.stringify(" + PARAM_iobData + ")+ \", \" +\n" +
|
||||||
"JSON.stringify(" + PARAM_profile + ")+ \", \" +\n" +
|
"JSON.stringify(" + PARAM_profile + ")+ \", \" +\n" +
|
||||||
"JSON.stringify(" + PARAM_meal_data + ")+ \") \");"
|
"JSON.stringify(" + PARAM_autosens_data + ")+ \", \" +\n" +
|
||||||
|
"JSON.stringify(" + PARAM_meal_data + ")+ \") \");"
|
||||||
);
|
);
|
||||||
mV8rt.executeVoidScript(
|
mV8rt.executeVoidScript(
|
||||||
"var rT = determine_basal(" +
|
"var rT = determine_basal(" +
|
||||||
|
@ -120,9 +128,9 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
PARAM_currentTemp + ", " +
|
PARAM_currentTemp + ", " +
|
||||||
PARAM_iobData + ", " +
|
PARAM_iobData + ", " +
|
||||||
PARAM_profile + ", " +
|
PARAM_profile + ", " +
|
||||||
"undefined, " +
|
PARAM_autosens_data + ", " +
|
||||||
PARAM_meal_data + ", " +
|
PARAM_meal_data + ", " +
|
||||||
"setTempBasal" +
|
"tempBasalFunctions" +
|
||||||
");");
|
");");
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,6 +152,8 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");");
|
storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");");
|
||||||
storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");");
|
storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");");
|
||||||
storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");");
|
storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");");
|
||||||
|
if (mAutosensData != null)
|
||||||
|
storedAutosens_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_autosens_data + ");");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -168,10 +178,21 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
return storedMeal_data;
|
return storedMeal_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getAutosensDataParam() {
|
||||||
|
return storedAutosens_data;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadScript() throws IOException {
|
private void loadScript() throws IOException {
|
||||||
|
mV8rt.executeVoidScript(readFile("OpenAPSAMA/round-basal.js"), "OpenAPSAMA/round-basal.js", 0);
|
||||||
|
mV8rt.executeVoidScript("var round_basal = module.exports;");
|
||||||
|
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(
|
mV8rt.executeVoidScript(
|
||||||
readFile("OpenAPSAMA/determine-basal.js"),
|
readFile("OpenAPSAMA/determine-basal.js"),
|
||||||
"OpenAPSAMA/bin/oref0-determine-basal.js",
|
"OpenAPSAMA/determine-basal.js",
|
||||||
0);
|
0);
|
||||||
mV8rt.executeVoidScript("var determine_basal = module.exports;");
|
mV8rt.executeVoidScript("var determine_basal = module.exports;");
|
||||||
mV8rt.executeVoidScript(
|
mV8rt.executeVoidScript(
|
||||||
|
@ -228,7 +249,8 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
PumpInterface pump,
|
PumpInterface pump,
|
||||||
IobTotal iobData,
|
IobTotal iobData,
|
||||||
DatabaseHelper.GlucoseStatus glucoseStatus,
|
DatabaseHelper.GlucoseStatus glucoseStatus,
|
||||||
TreatmentsPlugin.MealData mealData) {
|
TreatmentsPlugin.MealData mealData,
|
||||||
|
JSONObject autosensData) {
|
||||||
|
|
||||||
String units = profile.getUnits();
|
String units = profile.getUnits();
|
||||||
|
|
||||||
|
@ -258,18 +280,31 @@ public class DetermineBasalAdapterAMAJS {
|
||||||
mGlucoseStatus.add("glucose", glucoseStatus.glucose);
|
mGlucoseStatus.add("glucose", glucoseStatus.glucose);
|
||||||
mGlucoseStatus.add("delta", glucoseStatus.delta);
|
mGlucoseStatus.add("delta", glucoseStatus.delta);
|
||||||
mGlucoseStatus.add("avgdelta", glucoseStatus.avgdelta);
|
mGlucoseStatus.add("avgdelta", glucoseStatus.avgdelta);
|
||||||
|
mGlucoseStatus.add("short_avgdelta", glucoseStatus.short_avgdelta);
|
||||||
|
mGlucoseStatus.add("long_avgdelta", glucoseStatus.long_avgdelta);
|
||||||
|
|
||||||
mMealData.add("carbs", mealData.carbs);
|
mMealData.add("carbs", mealData.carbs);
|
||||||
mMealData.add("boluses", mealData.boluses);
|
mMealData.add("boluses", mealData.boluses);
|
||||||
|
mMealData.add("mealCOB", mealData.mealCOB);
|
||||||
|
|
||||||
|
if (autosensData != null) {
|
||||||
|
mAutosensData = new V8Object(mV8rt);
|
||||||
|
// TODO: add autosens data here for AMA
|
||||||
|
} else {
|
||||||
|
mV8rt.executeVoidScript("autosens_data = undefined");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
mProfile.release();
|
mProfile.release();
|
||||||
mCurrentTemp.release();
|
mCurrentTemp.release();
|
||||||
mIobData.release();
|
mIobData.release();
|
||||||
mMealData.release();
|
mMealData.release();
|
||||||
mGlucoseStatus.release();
|
mGlucoseStatus.release();
|
||||||
|
if (mAutosensData != null) {
|
||||||
|
mAutosensData.release();
|
||||||
|
}
|
||||||
mV8rt.release();
|
mV8rt.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ public class OpenAPSAMAPlugin implements PluginBase, APSInterface {
|
||||||
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return;
|
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return;
|
||||||
if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return;
|
if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return;
|
||||||
|
|
||||||
determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobTotal, glucoseStatus, mealData);
|
determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobTotal, glucoseStatus, mealData, null);
|
||||||
|
|
||||||
|
|
||||||
DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke();
|
DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke();
|
||||||
|
|
|
@ -141,6 +141,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface {
|
||||||
public class MealData {
|
public class MealData {
|
||||||
public double boluses = 0d;
|
public double boluses = 0d;
|
||||||
public double carbs = 0d;
|
public double carbs = 0d;
|
||||||
|
public double mealCOB = 0.0d; // TODO: add calculation for AMA
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue