From 82d32896029472d1a5d2b280099ff2a638bce584 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 14 Aug 2017 12:20:12 +0200 Subject: [PATCH 001/171] add oref1 asssets --- .../main/assets/OpenAPSSMB/basal-set-temp.js | 61 ++ .../main/assets/OpenAPSSMB/determine-basal.js | 885 ++++++++++++++++++ 2 files changed, 946 insertions(+) create mode 100644 app/src/main/assets/OpenAPSSMB/basal-set-temp.js create mode 100644 app/src/main/assets/OpenAPSSMB/determine-basal.js diff --git a/app/src/main/assets/OpenAPSSMB/basal-set-temp.js b/app/src/main/assets/OpenAPSSMB/basal-set-temp.js new file mode 100644 index 0000000000..9745869194 --- /dev/null +++ b/app/src/main/assets/OpenAPSSMB/basal-set-temp.js @@ -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; diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js new file mode 100644 index 0000000000..e185aad962 --- /dev/null +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -0,0 +1,885 @@ +/* + Determine Basal + + Released under MIT license. See the accompanying LICENSE.txt file for + full terms and conditions + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +var round_basal = require('../round-basal') + +// Rounds value to 'digits' decimal places +function round(value, digits) +{ + if (! digits) { digits = 0; } + 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 Math.round(value); + } +} + +var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data) { + var rT = {}; //short for requestedTemp + + var deliverAt = new Date(); + + if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { + rT.error ='Error: could not get current basal rate'; + return rT; + } + var profile_current_basal = round_basal(profile.current_basal, profile); + 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("Autosens adjusting basal from "+profile.current_basal+" to "+basal+"; "); + } else { + console.error("Basal unchanged: "+basal+"; "); + } + } + + var bg = glucose_status.glucose; + if (bg < 39) { //Dexcom is in ??? mode or calibrating + 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. "; + rT.deliverAt = deliverAt; + rT.temp = 'absolute'; + 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 + + // if min and max are set, then set target to their average + 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.min_bg !== 'undefined' && typeof profile.max_bg !== 'undefined') { + target_bg = (profile.min_bg + profile.max_bg) / 2; + } else { + rT.error ='Error: could not determine target_bg. '; + return rT; + } + + // 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 { + // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range + min_bg = round((min_bg - 60) / autosens_data.ratio) + 60; + max_bg = round((max_bg - 60) / autosens_data.ratio) + 60; + new_target_bg = 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("target_bg from "+target_bg+" to "+new_target_bg+"; "); + } + target_bg = new_target_bg; + } + } + + if (typeof iob_data === 'undefined' ) { + rT.error ='Error: iob_data undefined. '; + return rT; + } + + 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. '; + return rT; + } + + var tick; + + if (glucose_status.delta > -0.5) { + tick = "+" + round(glucose_status.delta,0); + } else { + tick = round(glucose_status.delta,0); + } + //var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta); + var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta); + var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta); + + var profile_sens = round(profile.sens,1) + 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("sens from "+profile_sens+" to "+sens); + } else { + console.error("sens unchanged: "+sens); + } + console.error(" (autosens ratio "+autosens_data.ratio+")"); + } + console.error(""); + + //calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone + var bgi = round(( -iob_data.activity * sens * 5 ), 2); + // project deviations for 30 minutes + var deviation = round( 30 / 5 * ( minDelta - bgi ) ); + // don't overreact to a big negative delta: use minAvgDelta if deviation is negative + if (deviation < 0) { + deviation = round( (30 / 5) * ( minAvgDelta - bgi ) ); + } + + // calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity + if (iob_data.iob > 0) { + var naive_eventualBG = 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 = round( bg - (iob_data.iob * Math.min(sens, profile.sens) ) ); + } + // and adjust it for the deviation above + var eventualBG = naive_eventualBG + deviation; + // calculate what portion of that is due to bolussnooze + var bolusContrib = iob_data.bolussnooze * sens; + // and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime + var naive_snoozeBG = round( naive_eventualBG + 1.5 * bolusContrib ); + // adjust that for deviation like we did eventualBG + var snoozeBG = naive_snoozeBG + deviation; + + // adjust target BG range if needed to safely bring down high BG faster without causing lows + if ( bg > max_bg && profile.adv_target_adjustments ) { + // with target=100, as BG rises from 100 to 160, adjustedTarget drops from 100 to 80 + var adjustedMinBG = round(Math.max(80, min_bg - (bg - min_bg)/3 ),0); + var adjustedTargetBG =round( Math.max(80, target_bg - (bg - target_bg)/3 ),0); + var adjustedMaxBG = round(Math.max(80, max_bg - (bg - max_bg)/3 ),0); + // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, don’t use it + //console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG); + if (eventualBG > adjustedMinBG && naive_eventualBG > adjustedMinBG && min_bg > adjustedMinBG) { + process.stderr.write("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); + min_bg = adjustedMinBG; + } else { + process.stderr.write("min_bg unchanged: "+min_bg+"; "); + } + // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, don’t use it + if (eventualBG > adjustedTargetBG && naive_eventualBG > adjustedTargetBG && target_bg > adjustedTargetBG) { + process.stderr.write("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); + target_bg = adjustedTargetBG; + } else { + process.stderr.write("target_bg unchanged: "+target_bg+"; "); + } + // if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, don’t use it + if (eventualBG > adjustedMaxBG && naive_eventualBG > adjustedMaxBG && max_bg > adjustedMaxBG) { + console.error("max_bg from "+max_bg+" to "+adjustedMaxBG); + max_bg = adjustedMaxBG; + } else { + console.error("max_bg unchanged: "+max_bg); + } + } + + var expectedDelta = calculate_expected_delta(profile.dia, target_bg, eventualBG, bgi); + if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) { + rT.error ='Error: could not calculate eventualBG. '; + return rT; + } + + // min_bg of 90 -> threshold of 65, 100 -> 70 110 -> 75, and 130 -> 85 + var threshold = min_bg - 0.5*(min_bg-40); + + //console.error(reservoir_data); + var deliverAt = new Date(); + + rT = { + 'temp': 'absolute' + , 'bg': bg + , 'tick': tick + , 'eventualBG': eventualBG + , 'snoozeBG': snoozeBG + , 'insulinReq': 0 + , 'reservoir' : reservoir_data // The expected reservoir volume at which to deliver the microbolus (the reservoir volume from immediately before the last pumphistory run) + , 'deliverAt' : deliverAt // The time at which the microbolus should be delivered + , 'minPredBG' : 999 + }; + + var basaliob = iob_data.basaliob; + //if (iob_data.basaliob) { basaliob = iob_data.basaliob; } + //else { basaliob = iob_data.iob - iob_data.bolussnooze; } + var bolusiob = iob_data.iob - basaliob; + + // generate predicted future BGs based on IOB, COB, and current absorption rate + + var COBpredBGs = []; + var aCOBpredBGs = []; + var IOBpredBGs = []; + var UAMpredBGs = []; + COBpredBGs.push(bg); + aCOBpredBGs.push(bg); + IOBpredBGs.push(bg); + UAMpredBGs.push(bg); + + // enable SMB whenever we have COB or UAM is enabled + // SMB is diabled by default, unless explicitly enabled in preferences.json + var enableSMB=false; + // disable SMB when a high temptarget is set + if (profile.temptargetSet && target_bg > 100) { + enableSMB=false; + // enable SMB (if enabled in preferences) for DIA hours after bolus + } else if (profile.enableSMB_with_bolus && bolusiob > 0.1) { + enableSMB=true; + // enable SMB (if enabled in preferences) while we have COB + } else if (profile.enableSMB_with_COB && meal_data.mealCOB) { + enableSMB=true; + // enable SMB (if enabled in preferences) if a low temptarget is set + } else if (profile.enableSMB_with_temptarget && (profile.temptargetSet && target_bg < 100)) { + enableSMB=true; + } + // enable UAM (if enabled in preferences) for DIA hours after bolus, or if SMB is enabled + var enableUAM=(profile.enableUAM && (bolusiob > 0.1 || enableSMB)); + + + //console.error(meal_data); + // 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 = round((minDelta - bgi),1); + uci = round((minAvgDelta - bgi),1); + // ISF (mg/dL/U) / CR (g/U) = CSF (mg/dL/g) + var csf = sens / profile.carb_ratio + // set meal_carbimpact high enough to absorb all meal carbs over 6 hours + // total_impact (mg/dL) = CSF (mg/dL/g) * carbs (g) + //console.error(csf * meal_data.carbs); + // meal_carbimpact (mg/dL/5m) = CSF (mg/dL/g) * carbs (g) / 6 (h) * (1h/60m) * 5 (m/5m) * 2 (for linear decay) + //var meal_carbimpact = round((csf * meal_data.carbs / 6 / 60 * 5 * 2),1) + // calculate the number of carbs absorbed over 4h at current CI + // CI (mg/dL/5m) * (5m)/5 (m) * 60 (min/hr) * 4 (h) / 2 (linear decay factor) = total carb impact (mg/dL) + var totalCI = Math.max(0, ci / 5 * 60 * 4 / 2); + // totalCI (mg/dL) / CSF (mg/dL/g) = total carbs absorbed (g) + var totalCA = totalCI / csf; + // exclude the last 1/3 of carbs from remainingCarbs, and then cap it at 90 + var remainingCarbsCap = 90; // default to 90 + var remainingCarbsFraction = 1; + if (profile.remainingCarbsCap) { remainingCarbsCap = Math.min(90,profile.remainingCarbsCap); } + if (profile.remainingCarbsFraction) { remainingCarbsFraction = Math.min(1,profile.remainingCarbsFraction); } + var remainingCarbsIgnore = 1 - remainingCarbsFraction; + var remainingCarbs = Math.max(0, meal_data.mealCOB - totalCA - meal_data.carbs*remainingCarbsIgnore); + remainingCarbs = Math.min(remainingCarbsCap,remainingCarbs); + // assume remainingCarbs will absorb over 4h + // remainingCI (mg/dL/5m) = remainingCarbs (g) * CSF (mg/dL/g) * 5 (m/5m) * 1h/60m / 4 (h) + var remainingCI = remainingCarbs * csf * 5 / 60 / 4; + //console.error(profile.min_5m_carbimpact,ci,totalCI,totalCA,remainingCarbs,remainingCI); + //if (meal_data.mealCOB * 3 > meal_data.carbs) { } + + // calculate peak deviation in last hour, and slope from that to current deviation + var minDeviationSlope = round(meal_data.minDeviationSlope,2); + //console.error(minDeviationSlope); + + aci = 10; + //5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m) + // duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m) + cid = Math.max(0, meal_data.mealCOB * csf / ci ); + acid = Math.max(0, meal_data.mealCOB * csf / aci ); + // duration (hours) = duration (5m) * 5 / 60 * 2 (to account for linear decay) + console.error("Carb Impact:",ci,"mg/dL per 5m; CI Duration:",round(cid*5/60*2,1),"hours; remaining 4h+ CI:",round(remainingCI,1),"mg/dL per 5m"); + console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",round(acid*5/60*2,1),"hours"); + var minIOBPredBG = 999; + var minCOBPredBG = 999; + var minUAMPredBG = 999; + var minPredBG; + var avgPredBG; + var IOBpredBG = eventualBG; + var maxIOBPredBG = bg; + var maxCOBPredBG = bg; + var maxUAMPredBG = bg; + //var maxPredBG = bg; + var eventualPredBG = bg; + var lastIOBpredBG; + var lastCOBpredBG; + var lastUAMpredBG; + var UAMduration = 0; + try { + iobArray.forEach(function(iobTick) { + //console.error(iobTick); + predBGI = round(( -iobTick.activity * sens * 5 ), 2); + // for IOBpredBGs, 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; + // for COBpredBGs, 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, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); + predACI = Math.max(0, Math.max(0,aci) * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) ); + // if any carbs aren't absorbed after 4 hours, assume they'll absorb at a constant rate for next 4h + COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI + remainingCI; + // stop adding remainingCI after 4h + if (COBpredBGs.length > 4 * 60 / 5) { remainingCI = 0; } + aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI; + // for UAMpredBGs, predicted carb impact drops at minDeviationSlope + // calculate predicted CI from UAM based on minDeviationSlope + predUCIslope = Math.max(0, uci + ( UAMpredBGs.length*minDeviationSlope ) ); + // if minDeviationSlope is too flat, predicted deviation impact drops linearly from + // current deviation down to zero over DIA (data points every 5m) + predUCIdia = Math.max(0, uci * ( 1 - UAMpredBGs.length/Math.max(profile.dia*60/5,1) ) ); + //console.error(predUCIslope, predUCIdia); + // predicted CI from UAM is the lesser of CI based on deviationSlope or DIA + predUCI = Math.min(predUCIslope, predUCIdia); + if(predUCI>0) { + //console.error(UAMpredBGs.length,minDeviationSlope, predUCI); + UAMduration=round((UAMpredBGs.length+1)*5/60,1); + } + UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI; + //console.error(predBGI, predCI, predUCI); + // truncate all BG predictions at 3.5 hours + if ( IOBpredBGs.length < 42) { IOBpredBGs.push(IOBpredBG); } + if ( COBpredBGs.length < 42) { COBpredBGs.push(COBpredBG); } + if ( aCOBpredBGs.length < 42) { aCOBpredBGs.push(aCOBpredBG); } + if ( UAMpredBGs.length < 42) { UAMpredBGs.push(UAMpredBG); } + // wait 90m before setting minIOBPredBG + if ( IOBpredBGs.length > 18 && (IOBpredBG < minIOBPredBG) ) { minIOBPredBG = round(IOBpredBG); } + if ( IOBpredBG > maxIOBPredBG ) { maxIOBPredBG = IOBpredBG; } + // wait 60m before setting COB and UAM minPredBGs + if ( (cid || remainingCI > 0) && COBpredBGs.length > 12 && (COBpredBG < minCOBPredBG) ) { minCOBPredBG = round(COBpredBG); } + if ( (cid || remainingCI > 0) && COBpredBG > maxIOBPredBG ) { maxCOBPredBG = COBpredBG; } + if ( enableUAM && UAMpredBGs.length > 12 && (UAMpredBG < minUAMPredBG) ) { minUAMPredBG = round(UAMpredBG); } + if ( enableUAM && UAMpredBG > maxIOBPredBG ) { maxUAMPredBG = UAMpredBG; } + }); + // 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:",e); + } + rT.predBGs = {}; + IOBpredBGs.forEach(function(p, i, theArray) { + theArray[i] = 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; + lastIOBpredBG=round(IOBpredBGs[IOBpredBGs.length-1]); + if (meal_data.mealCOB > 0) { + aCOBpredBGs.forEach(function(p, i, theArray) { + theArray[i] = 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(); } + } + rT.predBGs.aCOB = aCOBpredBGs; + } + if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCI > 0 )) { + COBpredBGs.forEach(function(p, i, theArray) { + theArray[i] = 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; + lastCOBpredBG=round(COBpredBGs[COBpredBGs.length-1]); + eventualBG = Math.max(eventualBG, round(COBpredBGs[COBpredBGs.length-1]) ); + } + if (ci > 0 || remainingCI > 0) { + if (enableUAM) { + UAMpredBGs.forEach(function(p, i, theArray) { + theArray[i] = round(Math.min(401,Math.max(39,p))); + }); + for (var i=UAMpredBGs.length-1; i > 12; i--) { + if (UAMpredBGs[i-1] != UAMpredBGs[i]) { break; } + else { UAMpredBGs.pop(); } + } + rT.predBGs.UAM = UAMpredBGs; + lastUAMpredBG=round(UAMpredBGs[UAMpredBGs.length-1]); + eventualBG = Math.max(eventualBG, round(UAMpredBGs[UAMpredBGs.length-1]) ); + } + + // set eventualBG and snoozeBG based on COB or UAM predBGs + rT.eventualBG = eventualBG; + } + + console.error("UAM Impact:",uci,"mg/dL per 5m; UAM Duration:",UAMduration,"hours"); + + minIOBPredBG = Math.max(39,minIOBPredBG); + minCOBPredBG = Math.max(39,minCOBPredBG); + minUAMPredBG = Math.max(39,minUAMPredBG); + minPredBG = round(minIOBPredBG); + + // if we have COB and UAM is enabled, average all three + if ( minUAMPredBG < 400 && minCOBPredBG < 400 ) { + avgPredBG = round( (IOBpredBG + UAMpredBG + COBpredBG)/3 ); + // if UAM is disabled, average IOB and COB + } else if ( minCOBPredBG < 400 ) { + avgPredBG = round( (IOBpredBG + COBpredBG)/2 ); + // if carbs are expired, use IOB instead of COB + } else if ( meal_data.carbs && minUAMPredBG < 400 ) { + avgPredBG = round( (2*IOBpredBG + UAMpredBG)/3 ); + // in pure UAM mode, just average IOB and UAM + } else if ( minUAMPredBG < 400 ) { + avgPredBG = round( (IOBpredBG + UAMpredBG)/2 ); + } else { + avgPredBG = round( IOBpredBG ); + } + + // if any carbs have been entered recently + if (meal_data.carbs) { + // average the minIOBPredBG and minUAMPredBG if available + if ( minUAMPredBG < 400 ) { + avgMinPredBG = round( (minIOBPredBG+minUAMPredBG)/2 ); + } else { + avgMinPredBG = minIOBPredBG; + } + + // if UAM is disabled, use max of minIOBPredBG, minCOBPredBG + if ( ! enableUAM && minCOBPredBG < 400 ) { + minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG)); + // if we have COB, use minCOBPredBG, or blendedMinPredBG if it's higher + } else if ( minCOBPredBG < 400 ) { + // calculate blendedMinPredBG based on how many carbs remain as COB + fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs; + blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*avgMinPredBG; + // if blendedMinPredBG > minCOBPredBG, use that instead + minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG, blendedMinPredBG)); + // if carbs have been entered, but have expired, use avg of minIOBPredBG and minUAMPredBG + } else { + minPredBG = avgMinPredBG; + } + // in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG + } else if ( enableUAM ) { + minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG)); + } + + // make sure minPredBG isn't higher than avgPredBG + minPredBG = Math.min( minPredBG, avgPredBG ); + + console.error("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG); + if (minCOBPredBG < 400) { + console.error(" minCOBPredBG: "+minCOBPredBG); + } + if (minUAMPredBG < 400) { + console.error(" minUAMPredBG: "+minUAMPredBG); + } + console.error(" avgPredBG:",avgPredBG,"COB:",meal_data.mealCOB,"carbs:",meal_data.carbs); + // But if the COB line falls off a cliff, don't trust UAM too much: + // use maxCOBPredBG if it's been set and lower than minPredBG + if ( maxCOBPredBG > bg ) { + minPredBG = Math.min(minPredBG, maxCOBPredBG); + } + // set snoozeBG to minPredBG if it's higher + if (minPredBG < 400) { + snoozeBG = round(Math.max(snoozeBG,minPredBG)); + } + rT.snoozeBG = snoozeBG; + //console.error(minPredBG, minIOBPredBG, minUAMPredBG, minCOBPredBG, maxCOBPredBG, 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) + ", minPredBG " + convert_bg(minPredBG, profile) + ", IOBpredBG " + convert_bg(lastIOBpredBG, profile); + if (lastCOBpredBG > 0) { + rT.reason += ", COBpredBG " + convert_bg(lastCOBpredBG, profile); + } + if (lastUAMpredBG > 0) { + rT.reason += ", UAMpredBG " + convert_bg(lastUAMpredBG, profile) + } + rT.reason += "; "; + var bgUndershoot = target_bg - Math.max( naive_eventualBG, eventualBG, lastIOBpredBG ); + // calculate how long until COB (or IOB) predBGs drop below min_bg + var minutesAboveMinBG = 240; + if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCI > 0 )) { + for (var i=0; i 0 ) { + rT.carbsReq = carbsReq; + rT.reason += carbsReq + " add'l carbs req + " + minutesAboveMinBG + "m zero temp; "; + } + // don't low glucose suspend if IOB is already super negative and BG is rising faster than predicted + if (bg < threshold && iob_data.iob < -profile.current_basal*20/60 && minDelta > 0 && minDelta > expectedDelta) { + rT.reason += "IOB "+iob_data.iob+" < " + round(-profile.current_basal*20/60,2); + rT.reason += " and minDelta " + minDelta + " > " + "expectedDelta " + expectedDelta + "; "; + } + // low glucose suspend mode: BG is < ~80 + else if (bg < threshold) { + 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 + rT.reason += ", minDelta " + minDelta + " < " + "expectedDelta " + expectedDelta + "; "; + return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); + } + if (glucose_status.delta > minDelta) { + rT.reason += ", delta " + glucose_status.delta + ">0"; + } else { + rT.reason += ", min delta " + minDelta.toFixed(2) + ">0"; + } + 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); + } + } + + if (eventualBG < min_bg) { // if eventual BG is below target: + 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 (minDelta > expectedDelta && minDelta > 0) { + // if naive_eventualBG < 40, set a 30m zero temp (oref0-pump-loop will let any longer SMB zero temp run) + if (naive_eventualBG < 40) { + rT.reason += ", naive_eventualBG < 40. "; + return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); + } + if (glucose_status.delta > minDelta) { + rT.reason += ", but Delta " + tick + " > expectedDelta " + expectedDelta; + } else { + 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; + } else { + rT.reason += "; setting current basal of " + basal + " as temp. "; + return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + } + } + + if (eventualBG < min_bg) { + // if we've bolused recently, we can snooze until the bolus IOB decays (at double speed) + if (snoozeBG > min_bg) { // if adding back in the bolus contribution BG would be above min + // If we're not in SMB mode with COB, or lastCOBpredBG > target_bg, bolus snooze + if (! (microBolusAllowed && rT.COB) || lastCOBpredBG > target_bg) { + rT.reason += ", bolus snooze: eventual BG range " + convert_bg(eventualBG, profile) + "-" + convert_bg(snoozeBG, profile); + //console.error(currenttemp, basal ); + 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 { + // 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 + // multiply by 2 to low-temp faster for increased hypo safety + var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens); + insulinReq = round( insulinReq , 2); + // calculate naiveInsulinReq based on naive_eventualBG + var naiveInsulinReq = Math.min(0, (naive_eventualBG - target_bg) / sens); + naiveInsulinReq = round( naiveInsulinReq , 2); + if (minDelta < 0 && minDelta > expectedDelta) { + // if we're barely falling, newinsulinReq should be barely negative + rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile); + var newinsulinReq = round(( insulinReq * (minDelta / expectedDelta) ), 2); + //console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); + insulinReq = newinsulinReq; + } + // rate required to deliver insulinReq less insulin over 30m: + var rate = basal + (2 * insulinReq); + rate = round_basal(rate, profile); + // if required temp < existing temp basal + var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; + // if current temp would deliver a lot (30% of basal) less than the required insulin, + // by both normal and naive calculations, then raise the rate + var minInsulinReq = Math.min(insulinReq,naiveInsulinReq); + if (insulinScheduled < minInsulinReq - basal*0.3) { + rT.reason += ", "+currenttemp.duration + "m@" + (currenttemp.rate).toFixed(2) + " is a lot less than needed. "; + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate >= currenttemp.rate * 0.8)) { + rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr. "; + return rT; + } else { + // calculate a long enough zero temp to eventually correct back up to target + if ( rate < 0 ) { + var bgUndershoot = target_bg - naive_eventualBG; + var worstCaseInsulinReq = bgUndershoot / sens; + var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + if (durationReq < 0) { + durationReq = 0; + // don't set a temp longer than 120 minutes + } else { + durationReq = round(durationReq/30)*30; + durationReq = Math.min(120,Math.max(0,durationReq)); + } + //console.error(durationReq); + //rT.reason += "insulinReq " + insulinReq + "; " + if (durationReq > 0) { + rT.reason += ", setting " + durationReq + "m zero temp. "; + return tempBasalFunctions.setTempBasal(rate, durationReq, profile, rT, currenttemp); + } + } else { + rT.reason += ", setting " + rate + "U/hr. "; + } + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + } + } + } + + /* + var minutes_running; + if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { + minutes_running = 30; + } else if (typeof currenttemp.minutesrunning !== 'undefined'){ + // If the time the current temp is running is not defined, use default request duration of 30 minutes. + minutes_running = currenttemp.minutesrunning; + } else { + minutes_running = 30 - currenttemp.duration; + } + + // 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) * ((30-minutes_running)/60) * sens; + var adjEventualBG = eventualBG + lowtempimpact; + // don't return early if microBolusAllowed etc. + if ( adjEventualBG < min_bg && ! (microBolusAllowed && enableSMB)) { + 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 (minDelta < expectedDelta) { + // if in SMB mode, don't cancel SMB zero temp + if (! (microBolusAllowed && enableSMB)) { + if (glucose_status.delta < minDelta) { + rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Delta " + tick + " < Exp. Delta " + expectedDelta; + } else { + 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 > 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); + } + } + } + // eventualBG, snoozeBG, or minPredBG is below max_bg + if (Math.min(eventualBG,snoozeBG,minPredBG) < 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) && currenttemp.duration > 5 ) { + rT.reason += eventualBG + " > " + max_bg + ": no temp required (letting high temp of " + currenttemp.rate + " run). " + return rT; + } + + // if in SMB mode, don't cancel SMB zero temp + if (! (microBolusAllowed && enableSMB )) { + rT.reason += convert_bg(eventualBG, profile)+"-"+convert_bg(Math.min(minPredBG,snoozeBG), profile)+" in range: no temp required"; + 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); + } + } + } + + // eventual BG is at/above target (or bolus snooze disabled for SMB) + // if iob is over max, just cancel any temps + var basaliob; + if (iob_data.basaliob) { basaliob = iob_data.basaliob; } + else { basaliob = iob_data.iob - iob_data.bolussnooze; } + // if we're not here because of SMB, eventual BG is at/above target + if (! (microBolusAllowed && rT.COB)) { + rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", "; + } + if (basaliob > max_iob) { + rT.reason += "basaliob " + round(basaliob,2) + " > max_iob " + max_iob; + 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 + + // insulinReq is the additional insulin required to get minPredBG down to target_bg + //console.error(minPredBG,snoozeBG,eventualBG); + var insulinReq = round( (Math.min(minPredBG,snoozeBG,eventualBG) - target_bg) / sens, 2); + // when dropping, but not as fast as expected, reduce insulinReq proportionally + // to the what fraction of expectedDelta we're dropping at + if (minDelta < 0 && minDelta > expectedDelta) { + var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2); + //console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq); + insulinReq = newinsulinReq; + } + // if that would put us over max_iob, then reduce accordingly + if (insulinReq > max_iob-basaliob) { + rT.reason += "max_iob " + max_iob + ", "; + insulinReq = max_iob-basaliob; + } + + // rate required to deliver insulinReq more insulin over 30m: + var rate = basal + (2 * insulinReq); + rate = round_basal(rate, profile); + insulinReq = round(insulinReq,3); + rT.insulinReq = insulinReq; + rT.minPredBG = minPredBG; + //console.error(iob_data.lastBolusTime); + // minutes since last bolus + var lastBolusAge = round(( new Date().getTime() - meal_data.lastBolusTime ) / 60000,1); + //console.error(lastBolusAge); + //console.error(profile.temptargetSet, target_bg, rT.COB); + // only allow microboluses with COB or low temp targets, or within DIA hours of a bolus + // only microbolus if 0.1U SMB represents 20m or less of basal (0.3U/hr or higher) + if (microBolusAllowed && enableSMB && profile.current_basal >= 0.3) { + // never bolus more than 30m worth of basal + maxBolus = round(profile.current_basal/2,1); + // bolus 1/3 the insulinReq, up to maxBolus + microBolus = round(Math.min(insulinReq/3,maxBolus),1); + + // calculate a long enough zero temp to eventually correct back up to target + var smbTarget = target_bg; + var worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; + var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + + // if no microBolus required, snoozeBG > target_bg, and lastCOBpredBG > target_bg, don't set a zero temp + if (microBolus < 0.1 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { + durationReq = 0; + } + + if (durationReq < 0) { + durationReq = 0; + // don't set a temp longer than 120 minutes + } else { + durationReq = round(durationReq/30)*30; + durationReq = Math.min(120,Math.max(0,durationReq)); + } + rT.reason += " insulinReq " + insulinReq; + if (microBolus >= maxBolus) { + rT.reason += "; maxBolus " + maxBolus; + } + if (durationReq > 0) { + rT.reason += "; setting " + durationReq + "m zero temp"; + } + rT.reason += ". "; + + //allow SMBs every 3 minutes + var nextBolusMins = round(3-lastBolusAge,1); + //console.error(naive_eventualBG, insulinReq, worstCaseInsulinReq, durationReq); + console.error("naive_eventualBG",naive_eventualBG+",",durationReq+"m zero temp needed; last bolus",lastBolusAge+"m ago; maxBolus: "+maxBolus); + if (lastBolusAge > 3) { + if (microBolus > 0) { + rT.units = microBolus; + rT.reason += "Microbolusing " + microBolus + "U. "; + } + } else { + rT.reason += "Waiting " + nextBolusMins + "m to microbolus again(" + microBolus + "). "; + } + //rT.reason += ". "; + + // if no zero temp is required, don't return yet; allow later code to set a high temp + if (durationReq > 0) { + rT.rate = 0; + rT.duration = durationReq; + return rT; + } + + // if insulinReq is negative, snoozeBG > target_bg, and lastCOBpredBG > target_bg, set a neutral temp + if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { + rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. "; + return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + } + } + + var maxSafeBasal = tempBasalFunctions.getMaxSafeBasal(profile); + + if (rate > maxSafeBasal) { + rT.reason += "adj. req. rate: "+rate+" to maxSafeBasal: "+maxSafeBasal+", "; + rate = round_basal(maxSafeBasal, profile); + } + + var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; + 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).toFixed(2) + " > 2 * insulinReq. Setting temp basal of " + rate + "U/hr. "; + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + + if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set + rT.reason += "no temp, setting " + rate + "U/hr. "; + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + + 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. "; + return rT; + } + + // required temp > existing temp basal + rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr. "; + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + +}; + +module.exports = determine_basal; From 898e162300240ec1b95bd036c7474ec256c718ab Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 15 Aug 2017 23:13:37 +0200 Subject: [PATCH 002/171] initial work on SMB --- app/build.gradle | 2 +- .../main/assets/OpenAPSSMB/determine-basal.js | 8 +- .../info/nightscout/androidaps/MainApp.java | 2 + .../androidaps/PreferencesActivity.java | 3 + .../nightscout/androidaps/data/IobTotal.java | 4 + .../nightscout/androidaps/data/MealData.java | 1 + .../IobCobCalculator/AutosensData.java | 2 + .../IobCobCalculatorPlugin.java | 42 ++- .../DetermineBasalAdapterSMBJS.java | 323 ++++++++++++++++++ .../OpenAPSSMB/DetermineBasalResultSMB.java | 180 ++++++++++ .../OpenAPSSMB/OpenAPSSMBFragment.java | 150 ++++++++ .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 279 +++++++++++++++ .../plugins/Treatments/TreatmentsPlugin.java | 3 + app/src/main/res/values/strings.xml | 8 + app/src/main/res/xml/pref_advanced.xml | 145 ++++---- 15 files changed, 1073 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java diff --git a/app/build.gradle b/app/build.gradle index c12c150513..cd11e97945 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,7 +44,7 @@ android { minSdkVersion 21 targetSdkVersion 23 versionCode 1500 - version "1.5g" + version "1.5smb" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", generateGitBuild() } diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index e185aad962..3eaee83b5b 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -194,17 +194,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, don’t use it //console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG); if (eventualBG > adjustedMinBG && naive_eventualBG > adjustedMinBG && min_bg > adjustedMinBG) { - process.stderr.write("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); + console.error("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); min_bg = adjustedMinBG; } else { - process.stderr.write("min_bg unchanged: "+min_bg+"; "); + console.error("min_bg unchanged: "+min_bg+"; "); } // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, don’t use it if (eventualBG > adjustedTargetBG && naive_eventualBG > adjustedTargetBG && target_bg > adjustedTargetBG) { - process.stderr.write("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); + console.error("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); target_bg = adjustedTargetBG; } else { - process.stderr.write("target_bg unchanged: "+target_bg+"; "); + console.error("target_bg unchanged: "+target_bg+"; "); } // if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, don’t use it if (eventualBG > adjustedMaxBG && naive_eventualBG > adjustedMaxBG && max_bg > adjustedMaxBG) { diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 817b0adafb..7914aa0cac 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -38,6 +38,7 @@ import info.nightscout.androidaps.plugins.NSClientInternal.NSClientInternalFragm import info.nightscout.androidaps.plugins.NSClientInternal.receivers.AckAlarmReceiver; import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAFragment; import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAFragment; +import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBFragment; import info.nightscout.androidaps.plugins.Overview.OverviewFragment; import info.nightscout.androidaps.plugins.Persistentnotification.PersistentNotificationPlugin; import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfileFragment; @@ -125,6 +126,7 @@ public class MainApp extends Application { if (Config.LOOPENABLED) pluginsList.add(LoopFragment.getPlugin()); if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSMAFragment.getPlugin()); if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSAMAFragment.getPlugin()); + if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSSMBFragment.getPlugin()); pluginsList.add(NSProfileFragment.getPlugin()); if (Config.OTHERPROFILES) pluginsList.add(SimpleProfileFragment.getPlugin()); if (Config.OTHERPROFILES) pluginsList.add(LocalProfileFragment.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index c5d8769639..e94aa9c9f4 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -14,6 +14,7 @@ import android.preference.PreferenceManager; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.BluetoothDevicePreference; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin; @@ -112,6 +113,8 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResource(R.xml.pref_openapsma); if (MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class) != null && MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class).isEnabled(PluginBase.APS)) addPreferencesFromResource(R.xml.pref_openapsama); + if (MainApp.getSpecificPlugin(OpenAPSSMBPlugin.class) != null && MainApp.getSpecificPlugin(OpenAPSSMBPlugin.class).isEnabled(PluginBase.APS)) + addPreferencesFromResource(R.xml.pref_openapsama); } if (MainApp.getSpecificPlugin(SensitivityAAPSPlugin.class) != null && MainApp.getSpecificPlugin(SensitivityAAPSPlugin.class).isEnabled(PluginBase.SENSITIVITY) || MainApp.getSpecificPlugin(SensitivityWeightedAveragePlugin.class) != null && MainApp.getSpecificPlugin(SensitivityWeightedAveragePlugin.class).isEnabled(PluginBase.SENSITIVITY)) diff --git a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java index bd924d35b5..bd569ceb97 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java +++ b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java @@ -19,6 +19,7 @@ public class IobTotal { // oref1 public double microBolusInsulin; public double microBolusIOB; + public long lastBolusTime; public double netInsulin = 0d; // for calculations from temp basals only public double netRatio = 0d; // net ratio at start of temp basal @@ -36,6 +37,7 @@ public class IobTotal { this.hightempinsulin = 0d; this.microBolusInsulin = 0d; this.microBolusIOB = 0d; + this.lastBolusTime = 0; this.time = time; } @@ -63,6 +65,7 @@ public class IobTotal { result.hightempinsulin = basalIob.hightempinsulin; result.microBolusInsulin = bolusIOB.microBolusInsulin + basalIob.microBolusInsulin; result.microBolusIOB = bolusIOB.microBolusIOB + basalIob.microBolusIOB; + result.lastBolusTime = bolusIOB.lastBolusTime; return result; } @@ -98,6 +101,7 @@ public class IobTotal { json.put("basaliob", basaliob); json.put("bolussnooze", bolussnooze); json.put("activity", activity); + json.put("lastBolusTime", lastBolusTime); json.put("time", DateUtil.toISOString(new Date(time))); } catch (JSONException e) { e.printStackTrace(); diff --git a/app/src/main/java/info/nightscout/androidaps/data/MealData.java b/app/src/main/java/info/nightscout/androidaps/data/MealData.java index 292d2272d5..f00bfe231e 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/MealData.java +++ b/app/src/main/java/info/nightscout/androidaps/data/MealData.java @@ -7,4 +7,5 @@ public class MealData { public double boluses = 0d; public double carbs = 0d; public double mealCOB = 0.0d; + public double minDeviationSlope; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 9939bd44d1..b43840ef8a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -59,6 +59,8 @@ public class AutosensData { public double autosensRatio = 1d; + public double minDeviationSlope; + public String log(long time) { return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " Bgi=" + bgi + " Deviation=" + deviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 445eda93cd..abe83a9513 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -227,7 +227,7 @@ public class IobCobCalculatorPlugin implements PluginBase { private BgReading findOlder(long time) { BgReading lastFound = bgReadings.get(bgReadings.size() - 1); if (lastFound.date > time) return null; - for (int i = bgReadings.size() - 2; i >=0 ; --i) { + for (int i = bgReadings.size() - 2; i >= 0; --i) { if (bgReadings.get(i).date < time) continue; lastFound = bgReadings.get(i); if (bgReadings.get(i).date > time) break; @@ -246,7 +246,7 @@ public class IobCobCalculatorPlugin implements PluginBase { long currentTime = bgReadings.get(0).date + 5 * 60 * 1000 - bgReadings.get(0).date % (5 * 60 * 1000) - 5 * 60 * 1000L; //log.debug("First reading: " + new Date(currentTime).toLocaleString()); - while (true) { + while (true) { // test if current value is older than current time BgReading newer = findNewer(currentTime); BgReading older = findOlder(currentTime); @@ -353,6 +353,11 @@ public class IobCobCalculatorPlugin implements PluginBase { long prevDataTime = roundUpTime(bucketed_data.get(bucketed_data.size() - 3).date); log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString()); AutosensData previous = autosensDataTable.get(prevDataTime); + + double currentDeviation = 0; + double minDeviationSlope = 0; + double maxDeviation = 0; + // start from oldest to be able sub cob for (int i = bucketed_data.size() - 4; i >= 0; i--) { // check if data already exists @@ -379,11 +384,13 @@ public class IobCobCalculatorPlugin implements PluginBase { double bg; double avgDelta; double delta; + bg = bucketed_data.get(i).value; if (bg < 39 || bucketed_data.get(i + 3).value < 39) { log.error("! value < 39"); continue; } + avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; delta = (bg - bucketed_data.get(i + 1).value); IobTotal iob = calculateFromTreatmentsAndTemps(bgTime); @@ -391,6 +398,32 @@ public class IobCobCalculatorPlugin implements PluginBase { double bgi = -iob.activity * sens * 5; double deviation = delta - bgi; + /* + TODO: SMB: i don't undestand this part + long ciTime = System.currentTimeMillis(); + + if (i == bucketed_data.size() - 4) { + currentDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; + } else if (ciTime > bgTime) { + double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; + double deviationSlope = (avgDeviation - currentDeviation) / (bgTime - ciTime) * 1000 * 60 * 5; + if (avgDeviation > maxDeviation) { + minDeviationSlope = Math.min(0, deviationSlope); + maxDeviation = avgDeviation; + } + //console.error("Deviations:",bgTime, avgDeviation, deviationSlope, minDeviationSlope); + } + + https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + + Scott's comment + that stuff is used in UAM (unannounced meal) mode. what we're doing there is calculating the currentDeviation (how fast carbs are absorbing right now), and comparing that to the highest deviation we've seen in the last 45m to calculate the deviationSlope: the rate at which deviations are currently decreasing. We then project deviations to continue into the future starting at the currentDeviation rate and declining at deviationSlope until it gets back down to zero + that gives us an independent way to estimate how long deviations (likely carb absorption) will continue, even if we don't have (or don't want to fully trust) the user's carb estimate + the complexity comes from reusing the same COB calculation code to calculate both current and historical carb absorption rates + + */ + + List recentTreatments = MainApp.getConfigBuilder().getTreatments5MinBackFromHistory(bgTime); for (int ir = 0; ir < recentTreatments.size(); ir++) { autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; @@ -420,6 +453,7 @@ public class IobCobCalculatorPlugin implements PluginBase { autosensData.deviation = deviation; autosensData.bgi = bgi; autosensData.delta = delta; + autosensData.minDeviationSlope = minDeviationSlope; // calculate autosens only without COB if (autosensData.cob <= 0) { @@ -669,8 +703,8 @@ public class IobCobCalculatorPlugin implements PluginBase { for (int index = iobTable.size() - 1; index >= 0; index--) { if (iobTable.keyAt(index) > time) { if (Config.logAutosensData) - if (Config.logAutosensData) - log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString()); + if (Config.logAutosensData) + log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString()); iobTable.removeAt(index); } else { break; 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 new file mode 100644 index 0000000000..261a2a4522 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -0,0 +1,323 @@ +package info.nightscout.androidaps.plugins.OpenAPSSMB; + +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 java.util.Date; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.GlucoseStatus; +import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.data.MealData; +import info.nightscout.androidaps.db.TemporaryBasal; +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.utils.SP; + +public class DetermineBasalAdapterSMBJS { + private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterSMBJS.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 final String PARAM_reservoirData = "reservoirData"; + private final String PARAM_microBolusAllowed = "microBolusAllowed"; + + + 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 storedMicroBolusAllowed = null; + + private String scriptDebug = ""; + + /** + * Main code + */ + + 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 + ");"))); + if (mAutosensData != null) + log.debug("Autosens data: " + (storedAutosens_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_autosens_data + ");"))); + else + log.debug("Autosens data: " + (storedAutosens_data = "undefined")); + log.debug("Reservoir data: " + "undefined"); + log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = mV8rt.executeStringScript("JSON.stringify(" + PARAM_microBolusAllowed + ");"))); + + 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 + + ");"); + + + 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(); + } + + 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 getMicroBolusAllowedParam() { + return storedMicroBolusAllowed; + } + + 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("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, + double minBg, + double maxBg, + double targetBg, + PumpInterface pump, + IobTotal[] iobArray, + GlucoseStatus glucoseStatus, + MealData mealData, + double autosensDataRatio, + boolean tempTargetSet, + boolean microBolusAllowed + ) { + + 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 + // 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); + + mCurrentTemp = new V8Object(mV8rt); + mCurrentTemp.add("temp", "absolute"); + mCurrentTemp.add("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); + mCurrentTemp.add("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()); + } + + mV8rt.add(PARAM_currentTemp, mCurrentTemp); + + mIobData = mV8rt.executeArrayScript(IobCobCalculatorPlugin.convertToJSONArray(iobArray).toString()); + mV8rt.add(PARAM_iobData, mIobData); + + mGlucoseStatus = new V8Object(mV8rt); + mGlucoseStatus.add("glucose", glucoseStatus.glucose); + + if (SP.getBoolean("always_use_shortavg", false)) { + mGlucoseStatus.add("delta", glucoseStatus.short_avgdelta); + } else { + 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); + mMealData.add("minDeviationSlope", mealData.minDeviationSlope); + 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); + } + + mV8rt.addUndefined(PARAM_reservoirData); + mV8rt.add(PARAM_microBolusAllowed, microBolusAllowed); + + } + + + 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; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java new file mode 100644 index 0000000000..8a6c29d4fe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -0,0 +1,180 @@ +package info.nightscout.androidaps.plugins.OpenAPSSMB; + +import com.eclipsesource.v8.V8Object; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.utils.DateUtil; + +public class DetermineBasalResultSMB extends APSResult { + public Date date; + public JSONObject json = new JSONObject(); + public double eventualBG; + public double snoozeBG; + public IobTotal iob; + public double smbValue; + public long deliverAt; + + public DetermineBasalResultSMB(JSONObject result) { + date = new Date(); + json = result; + try { + if (result.has("error")) { + reason = result.getString("error"); + changeRequested = false; + rate = -1; + duration = -1; + } else { + reason = result.getString("reason"); + if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG"); + if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); + if (result.has("rate")) { + rate = result.getDouble("rate"); + if (rate < 0d) rate = 0d; + changeRequested = true; + } else { + rate = -1; + changeRequested = false; + } + if (result.has("duration")) { + duration = result.getInt("duration"); + //changeRequested as above + } else { + duration = -1; + changeRequested = false; + } + if (result.has("units")) { + changeRequested = true; + smbValue = result.getDouble("units"); + } else { + smbValue = 0.0; + //changeRequested as above + } + if (result.has("deliverAt")) { + String date = result.getString("deliverAt"); + try { + deliverAt = DateUtil.fromISODateString(date).getTime(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public DetermineBasalResultSMB() { + } + + @Override + public DetermineBasalResultSMB clone() { + DetermineBasalResultSMB newResult = new DetermineBasalResultSMB(); + newResult.reason = new String(reason); + newResult.rate = rate; + newResult.duration = duration; + newResult.changeRequested = changeRequested; + newResult.rate = rate; + newResult.duration = duration; + newResult.smbValue = smbValue; + + try { + newResult.json = new JSONObject(json.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + newResult.eventualBG = eventualBG; + newResult.snoozeBG = snoozeBG; + newResult.date = date; + return newResult; + } + + @Override + public JSONObject json() { + try { + JSONObject ret = new JSONObject(this.json.toString()); + return ret; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + public List getPredictions() { + List array = new ArrayList<>(); + try { + long startTime = date.getTime(); + if (json.has("predBGs")) { + JSONObject predBGs = json.getJSONObject("predBGs"); + if (predBGs.has("IOB")) { + JSONArray iob = predBGs.getJSONArray("IOB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + if (predBGs.has("aCOB")) { + JSONArray iob = predBGs.getJSONArray("aCOB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + if (predBGs.has("COB")) { + JSONArray iob = predBGs.getJSONArray("COB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + return array; + } + + public long getLatestPredictionsTime() { + long latest = 0; + try { + long startTime = date.getTime(); + if (json.has("predBGs")) { + JSONObject predBGs = json.getJSONObject("predBGs"); + if (predBGs.has("IOB")) { + JSONArray iob = predBGs.getJSONArray("IOB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + if (predBGs.has("aCOB")) { + JSONArray iob = predBGs.getJSONArray("aCOB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + if (predBGs.has("COB")) { + JSONArray iob = predBGs.getJSONArray("COB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + + return latest; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java new file mode 100644 index 0000000000..b08d0dac9d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java @@ -0,0 +1,150 @@ +package info.nightscout.androidaps.plugins.OpenAPSSMB; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.crashlytics.android.answers.Answers; +import com.crashlytics.android.answers.CustomEvent; +import com.squareup.otto.Subscribe; + +import org.json.JSONArray; +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.Common.SubscriberFragment; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.utils.JSONFormatter; + +public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnClickListener { + private static Logger log = LoggerFactory.getLogger(OpenAPSSMBFragment.class); + + private static OpenAPSSMBPlugin openAPSSMBPlugin; + + public static OpenAPSSMBPlugin getPlugin() { + if (openAPSSMBPlugin == null) { + openAPSSMBPlugin = new OpenAPSSMBPlugin(); + } + return openAPSSMBPlugin; + } + + Button run; + TextView lastRunView; + TextView glucoseStatusView; + TextView currentTempView; + TextView iobDataView; + TextView profileView; + TextView mealDataView; + TextView autosensDataView; + TextView resultView; + TextView scriptdebugView; + TextView requestView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.openapsama_fragment, container, false); + + run = (Button) view.findViewById(R.id.openapsma_run); + run.setOnClickListener(this); + lastRunView = (TextView) view.findViewById(R.id.openapsma_lastrun); + glucoseStatusView = (TextView) view.findViewById(R.id.openapsma_glucosestatus); + currentTempView = (TextView) view.findViewById(R.id.openapsma_currenttemp); + iobDataView = (TextView) view.findViewById(R.id.openapsma_iobdata); + profileView = (TextView) view.findViewById(R.id.openapsma_profile); + mealDataView = (TextView) view.findViewById(R.id.openapsma_mealdata); + autosensDataView = (TextView) view.findViewById(R.id.openapsma_autosensdata); + scriptdebugView = (TextView) view.findViewById(R.id.openapsma_scriptdebugdata); + resultView = (TextView) view.findViewById(R.id.openapsma_result); + requestView = (TextView) view.findViewById(R.id.openapsma_request); + + updateGUI(); + return view; + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.openapsma_run: + getPlugin().invoke("OpenAPSAMA button"); + Answers.getInstance().logCustom(new CustomEvent("OpenAPS_AMA_Run")); + break; + } + + } + + @Subscribe + public void onStatusEvent(final EventOpenAPSUpdateGui ev) { + updateGUI(); + } + + @Subscribe + public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { + updateResultGUI(ev.text); + } + + @Override + protected void updateGUI() { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DetermineBasalResultSMB lastAPSResult = getPlugin().lastAPSResult; + if (lastAPSResult != null) { + resultView.setText(JSONFormatter.format(lastAPSResult.json)); + requestView.setText(lastAPSResult.toSpanned()); + } + DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = getPlugin().lastDetermineBasalAdapterSMBJS; + if (determineBasalAdapterSMBJS != null) { + glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam())); + currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam())); + try { + JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); + iobDataView.setText(String.format(MainApp.sResources.getString(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))); + } catch (JSONException e) { + e.printStackTrace(); + iobDataView.setText("JSONException"); + } + profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam())); + mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam())); + scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug()); + } + if (getPlugin().lastAPSRun != null) { + lastRunView.setText(getPlugin().lastAPSRun.toLocaleString()); + } + if (getPlugin().lastAutosensResult != null) { + autosensDataView.setText(JSONFormatter.format(getPlugin().lastAutosensResult.json())); + } + } + }); + } + + void updateResultGUI(final String text) { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + resultView.setText(text); + glucoseStatusView.setText(""); + currentTempView.setText(""); + iobDataView.setText(""); + profileView.setText(""); + mealDataView.setText(""); + autosensDataView.setText(""); + scriptdebugView.setText(""); + requestView.setText(""); + lastRunView.setText(""); + } + }); + } +} 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 new file mode 100644 index 0000000000..9ddbacd355 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java @@ -0,0 +1,279 @@ +package info.nightscout.androidaps.plugins.OpenAPSSMB; + +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Date; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.GlucoseStatus; +import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.data.MealData; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.interfaces.APSInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.androidaps.plugins.Loop.ScriptReader; +import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalResultSMB; +import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalAdapterSMBJS; +import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.NSUpload; +import info.nightscout.utils.Profiler; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; +import info.nightscout.utils.ToastUtils; + +/** + * Created by mike on 05.08.2016. + */ +public class OpenAPSSMBPlugin implements PluginBase, APSInterface { + private static Logger log = LoggerFactory.getLogger(OpenAPSSMBPlugin.class); + + // last values + DetermineBasalAdapterSMBJS lastDetermineBasalAdapterSMBJS = null; + Date lastAPSRun = null; + DetermineBasalResultSMB lastAPSResult = null; + AutosensResult lastAutosensResult = null; + + boolean fragmentEnabled = false; + boolean fragmentVisible = true; + + @Override + public String getName() { + return MainApp.instance().getString(R.string.openapssmb); + } + + @Override + public String getNameShort() { + String name = MainApp.sResources.getString(R.string.smb_shortname); + if (!name.trim().isEmpty()){ + //only if translation exists + return name; + } + // use long name as fallback + return getName(); + } + + @Override + public boolean isEnabled(int type) { + return type == APS && fragmentEnabled && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; + } + + @Override + public boolean isVisibleInTabs(int type) { + return type == APS && fragmentVisible && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; + } + + @Override + public boolean canBeHidden(int type) { + return true; + } + + @Override + public boolean hasFragment() { + return true; + } + + @Override + public boolean showInList(int type) { + return true; + } + + @Override + public void setFragmentVisible(int type, boolean fragmentVisible) { + if (type == APS) this.fragmentVisible = fragmentVisible; + } + + @Override + public void setFragmentEnabled(int type, boolean fragmentEnabled) { + if (type == APS) this.fragmentEnabled = fragmentEnabled; + } + + @Override + public int getType() { + return PluginBase.APS; + } + + @Override + public String getFragmentClass() { + return OpenAPSSMBFragment.class.getName(); + } + + @Override + public APSResult getLastAPSResult() { + return lastAPSResult; + } + + @Override + public Date getLastAPSRun() { + return lastAPSRun; + } + + @Override + public void invoke(String initiator) { + log.debug("invoke from " + initiator); + lastAPSResult = null; + DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = null; + try { + determineBasalAdapterSMBJS = new DetermineBasalAdapterSMBJS(new ScriptReader(MainApp.instance().getBaseContext())); + } catch (IOException e) { + log.error(e.getMessage(), e); + return; + } + + GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); + Profile profile = MainApp.getConfigBuilder().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder(); + + if (profile == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.noprofileselected))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.noprofileselected)); + return; + } + + if (!isEnabled(PluginBase.APS)) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_disabled))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_disabled)); + return; + } + + if (glucoseStatus == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noglucosedata))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_noglucosedata)); + return; + } + + String units = profile.getUnits(); + + double maxIob = SP.getDouble("openapsma_max_iob", 1.5d); + double maxBasal = SP.getDouble("openapsma_max_basal", 1d); + double minBg = Profile.toMgdl(profile.getTargetLow(), units); + double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); + double targetBg = (minBg + maxBg) / 2; + + minBg = Round.roundTo(minBg, 0.1d); + maxBg = Round.roundTo(maxBg, 0.1d); + + Date start = new Date(); + Date startPart = new Date(); + IobTotal[] iobArray = IobCobCalculatorPlugin.calculateIobArrayInDia(); + Profiler.log(log, "calculateIobArrayInDia()", startPart); + + startPart = new Date(); + MealData mealData = MainApp.getConfigBuilder().getMealData(); + Profiler.log(log, "getMealData()", startPart); + + maxIob = MainApp.getConfigBuilder().applyMaxIOBConstraints(maxIob); + + minBg = verifyHardLimits(minBg, "minBg", Constants.VERY_HARD_LIMIT_MIN_BG[0], Constants.VERY_HARD_LIMIT_MIN_BG[1]); + maxBg = verifyHardLimits(maxBg, "maxBg", Constants.VERY_HARD_LIMIT_MAX_BG[0], Constants.VERY_HARD_LIMIT_MAX_BG[1]); + targetBg = verifyHardLimits(targetBg, "targetBg", Constants.VERY_HARD_LIMIT_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TARGET_BG[1]); + + boolean isTempTarget = false; + TempTarget tempTarget = MainApp.getConfigBuilder().getTempTargetFromHistory(System.currentTimeMillis()); + if (tempTarget != null) { + isTempTarget = true; + minBg = verifyHardLimits(tempTarget.low, "minBg", Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[1]); + maxBg = verifyHardLimits(tempTarget.high, "maxBg", Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[1]); + targetBg = verifyHardLimits((tempTarget.low + tempTarget.high) / 2, "targetBg", Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[1]); + } + + + maxIob = verifyHardLimits(maxIob, "maxIob", 0, 7); + maxBasal = verifyHardLimits(maxBasal, "max_basal", 0.1, 10); + + if (!checkOnlyHardLimits(profile.getDia(), "dia", 2, 7)) return; + if (!checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", 2, 100)) + return; + if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf().doubleValue(), units), "sens", 2, 900)) + return; + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return; + if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return; + + startPart = new Date(); + if (MainApp.getConfigBuilder().isAMAModeEnabled()) { + lastAutosensResult = IobCobCalculatorPlugin.detectSensitivityWithLock(IobCobCalculatorPlugin.oldestDataAvailable(), System.currentTimeMillis()); + } else { + lastAutosensResult = new AutosensResult(); + } + Profiler.log(log, "detectSensitivityandCarbAbsorption()", startPart); + 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 + ); + + + DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke(); + Profiler.log(log, "SMB calculation", start); + // Fix bug determine basal + if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !MainApp.getConfigBuilder().isTempBasalInProgress()) + determineBasalResultSMB.changeRequested = false; + // limit requests on openloop mode + if (!MainApp.getConfigBuilder().isClosedModeEnabled()) { + if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()) < 0.1) + determineBasalResultSMB.changeRequested = false; + if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getBaseBasalRate()) < 0.1) + determineBasalResultSMB.changeRequested = false; + } + + determineBasalResultSMB.iob = iobArray[0]; + + determineBasalAdapterSMBJS.release(); + + Date now = new Date(); + + try { + determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now)); + } catch (JSONException e) { + e.printStackTrace(); + } + + lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS; + lastAPSResult = determineBasalResultSMB; + lastAPSRun = now; + MainApp.bus().post(new EventOpenAPSUpdateGui()); + + //deviceStatus.suggested = determineBasalResultAMA.json; + } + + // safety checks + public static boolean checkOnlyHardLimits(Double value, String valueName, double lowLimit, double highLimit) { + return value.equals(verifyHardLimits(value, valueName, lowLimit, highLimit)); + } + + public static Double verifyHardLimits(Double value, String valueName, double lowLimit, double highLimit) { + Double newvalue = value; + if (newvalue < lowLimit || newvalue > highLimit) { + newvalue = Math.max(newvalue, lowLimit); + newvalue = Math.min(newvalue, highLimit); + String msg = String.format(MainApp.sResources.getString(R.string.openapsma_valueoutofrange), valueName); + msg += ".\n"; + msg += String.format(MainApp.sResources.getString(R.string.openapsma_valuelimitedto), value, newvalue); + log.error(msg); + NSUpload.uploadError(msg); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), msg, R.raw.error); + } + return newvalue; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 34fca17d27..58de167297 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -187,6 +187,8 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { Iob bIOB = t.iobCalc(time, dia / SP.getDouble("openapsama_bolussnooze_dia_divisor", 2.0)); total.bolussnooze += bIOB.iobContrib; } else { + if (t.date > total.lastBolusTime) + total.lastBolusTime = t.date; total.basaliob += t.insulin; total.microBolusIOB += tIOB.iobContrib; } @@ -236,6 +238,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { AutosensData autosensData = IobCobCalculatorPlugin.getLastAutosensData(); if (autosensData != null) { result.mealCOB = autosensData.cob; + result.minDeviationSlope = autosensData.minDeviationSlope; } return result; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a422d85ca7..b690ecb8ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -689,5 +689,13 @@ PUMP Basal value [U/h] Duration [min] + OpenAPS SMB + SMB + use_smb + use_uam + Enable UAM + Enable SMB + Use Super Micro Boluses instead of temp basal for faster action + Detection of Unannounced meals diff --git a/app/src/main/res/xml/pref_advanced.xml b/app/src/main/res/xml/pref_advanced.xml index 5395a75f1e..0b284663f7 100644 --- a/app/src/main/res/xml/pref_advanced.xml +++ b/app/src/main/res/xml/pref_advanced.xml @@ -4,125 +4,130 @@ - - + + + android:summary="@string/ns_upload_only_summary" + android:title="@string/ns_upload_only" /> + android:summary="@string/ns_noupload_summary" + android:title="@string/ns_noupload" /> - + + android:summary="@string/enablesuperbolus_summary" + android:title="@string/enablesuperbolus" /> - + + android:summary="@string/always_use_shortavg_summary" + android:title="@string/always_use_shortavg" /> - - + + + + + + - + - + android:title="@string/openapsama_current_basal_safety_multiplier" + validate:maxNumber="10" + validate:minNumber="1" + validate:testType="numericRange" /> + + android:inputType="numberDecimal" + android:key="openapsama_autosens_min" + android:maxLines="20" + android:selectAllOnFocus="true" + android:singleLine="true" + android:title="@string/openapsama_autosens_min" + validate:floatmaxNumber="1.0" + validate:floatminNumber="0.1" + validate:testType="floatNumericRange" /> + android:summary="@string/openapsama_autosens_adjusttargets_summary" + android:title="@string/openapsama_autosens_adjusttargets" /> + validate:maxNumber="10" + validate:minNumber="1" + validate:testType="floatNumericRange" /> - + + android:summary="@string/do_not_track_profile_switch_summary" + android:title="@string/do_not_track_profile_switch" /> From a097bf51aa81dd86d585920d2b5e347684834a98 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 17 Aug 2017 23:37:59 +0200 Subject: [PATCH 003/171] minDeviationSlope 1st try --- .../IobCobCalculator/AutosensData.java | 5 +- .../IobCobCalculatorPlugin.java | 56 ++++++++----------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index b43840ef8a..9ce6b340e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -56,13 +56,14 @@ public class AutosensData { public double cob = 0; public double bgi = 0d; public double delta = 0d; + public double avgDelta = 0d; + public double avgDeviation = 0d; public double autosensRatio = 1d; - public double minDeviationSlope; public String log(long time) { - return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " Bgi=" + bgi + " Deviation=" + deviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio; + return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " minDeviationSlope=" + minDeviationSlope; } public int minOld() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index abe83a9513..7be4f3b929 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -354,10 +354,6 @@ public class IobCobCalculatorPlugin implements PluginBase { log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString()); AutosensData previous = autosensDataTable.get(prevDataTime); - double currentDeviation = 0; - double minDeviationSlope = 0; - double maxDeviation = 0; - // start from oldest to be able sub cob for (int i = bucketed_data.size() - 4; i >= 0; i--) { // check if data already exists @@ -381,49 +377,43 @@ public class IobCobCalculatorPlugin implements PluginBase { autosensData.activeCarbsList = new ArrayList<>(); //console.error(bgTime , bucketed_data[i].glucose); - double bg; - double avgDelta; - double delta; - bg = bucketed_data.get(i).value; + double bg = bucketed_data.get(i).value; if (bg < 39 || bucketed_data.get(i + 3).value < 39) { log.error("! value < 39"); continue; } - avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; - delta = (bg - bucketed_data.get(i + 1).value); + double avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; + double delta = (bg - bucketed_data.get(i + 1).value); IobTotal iob = calculateFromTreatmentsAndTemps(bgTime); double bgi = -iob.activity * sens * 5; double deviation = delta - bgi; + double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; - /* - TODO: SMB: i don't undestand this part - long ciTime = System.currentTimeMillis(); + double currentDeviation; + double minDeviationSlope = 0; + double maxDeviation = 0; - if (i == bucketed_data.size() - 4) { - currentDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; - } else if (ciTime > bgTime) { - double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; - double deviationSlope = (avgDeviation - currentDeviation) / (bgTime - ciTime) * 1000 * 60 * 5; - if (avgDeviation > maxDeviation) { - minDeviationSlope = Math.min(0, deviationSlope); - maxDeviation = avgDeviation; + // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope + long hourago = bgTime + 10 * 1000 - 60 * 60 * 1000L; + AutosensData hourAgoData = getAutosensData(hourago); + currentDeviation = hourAgoData.avgDeviation; + int initialIndex = autosensDataTable.indexOfKey(hourAgoData.time); + + for (int past = 1; past < 12; past++) { + AutosensData ad = autosensDataTable.valueAt(initialIndex + past); + double deviationSlope = (ad.avgDeviation - currentDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; + if (ad.avgDeviation > maxDeviation) { + minDeviationSlope = Math.min(0, deviationSlope); + maxDeviation = ad.avgDeviation; + } + log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " minDeviationSlope=" + minDeviationSlope); } - //console.error("Deviations:",bgTime, avgDeviation, deviationSlope, minDeviationSlope); } - https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 - - Scott's comment - that stuff is used in UAM (unannounced meal) mode. what we're doing there is calculating the currentDeviation (how fast carbs are absorbing right now), and comparing that to the highest deviation we've seen in the last 45m to calculate the deviationSlope: the rate at which deviations are currently decreasing. We then project deviations to continue into the future starting at the currentDeviation rate and declining at deviationSlope until it gets back down to zero - that gives us an independent way to estimate how long deviations (likely carb absorption) will continue, even if we don't have (or don't want to fully trust) the user's carb estimate - the complexity comes from reusing the same COB calculation code to calculate both current and historical carb absorption rates - - */ - - List recentTreatments = MainApp.getConfigBuilder().getTreatments5MinBackFromHistory(bgTime); for (int ir = 0; ir < recentTreatments.size(); ir++) { autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; @@ -453,6 +443,8 @@ public class IobCobCalculatorPlugin implements PluginBase { autosensData.deviation = deviation; autosensData.bgi = bgi; autosensData.delta = delta; + autosensData.avgDelta = avgDelta; + autosensData.avgDeviation = avgDeviation; autosensData.minDeviationSlope = minDeviationSlope; // calculate autosens only without COB From ec114c3b38bcb9128a9112b8012cdaf28a45c7e3 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 08:41:34 +0200 Subject: [PATCH 004/171] execute SMB --- .../interfaces/TreatmentsInterface.java | 1 + .../ConfigBuilder/ConfigBuilderPlugin.java | 25 +++++++++++++++++++ .../androidaps/plugins/Loop/APSResult.java | 8 ++++++ .../androidaps/plugins/Loop/LoopPlugin.java | 1 + .../OpenAPSAMA/DetermineBasalResultAMA.java | 1 - .../OpenAPSMA/DetermineBasalResultMA.java | 1 - .../OpenAPSSMB/DetermineBasalResultSMB.java | 10 +++----- .../plugins/Treatments/TreatmentsPlugin.java | 13 ++++++++++ .../java/info/nightscout/utils/NSUpload.java | 13 ++-------- 9 files changed, 54 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 5e8b4e52e8..698f0161e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -30,6 +30,7 @@ public interface TreatmentsInterface { List getTreatmentsFromHistory(); List getTreatments5MinBackFromHistory(long time); + long getLastSMBTime(); // real basals (not faked by extended bolus) boolean isInHistoryRealTempBasalInProgress(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 6bc0e1e807..30ee04de8a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.data.Intervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileIntervals; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.TempTarget; @@ -442,6 +443,8 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain bolusProgressDialog = new BolusProgressDialog(); bolusProgressDialog.setInsulin(detailedBolusInfo.insulin); bolusProgressDialog.show(((AppCompatActivity) detailedBolusInfo.context).getSupportFragmentManager(), "BolusProgress"); + } else if (detailedBolusInfo.isSMB) { + // do not show bolus progress dialog } else { Intent i = new Intent(); i.putExtra("insulin", detailedBolusInfo.insulin); @@ -588,6 +591,23 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain log.debug("applyAPSRequest: setTempBasalAbsolute()"); result = setTempBasalAbsolute(request.rate, request.duration); } + + if (request.smb != 0) { + long lastSMBTime = getLastSMBTime(); + if (lastSMBTime != 0 && lastSMBTime + 4.5 * 60 * 1000 > System.currentTimeMillis()) { + log.debug("SMS requsted but still in 5 min interval"); + } else { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; + detailedBolusInfo.insulin = request.smb; + detailedBolusInfo.isSMB = true; + PumpEnactResult smbResult = deliverTreatment(detailedBolusInfo); + if (smbResult.success) + return result; + else + return smbResult; + } + } return result; } @@ -797,6 +817,11 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return activeTreatments.getTreatments5MinBackFromHistory(time); } + @Override + public long getLastSMBTime() { + return activeTreatments.getLastSMBTime(); + } + @Override public boolean isInHistoryRealTempBasalInProgress() { return activeTreatments.isInHistoryRealTempBasalInProgress(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 69e161524f..5e89a5a0f3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -10,6 +10,7 @@ import org.json.JSONObject; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.utils.DecimalFormatter; @@ -21,6 +22,10 @@ public class APSResult { public double rate; public int duration; public boolean changeRequested = false; + public IobTotal iob; + public double smb = 0d; // super micro bolus in units + public long deliverAt = 0; + @Override public String toString() { final ConfigBuilderPlugin configBuilder = MainApp.getConfigBuilder(); @@ -31,6 +36,7 @@ public class APSResult { return MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + "(" + DecimalFormatter.to2Decimal(rate/configBuilder.getBaseBasalRate() *100) + "%)\n" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to0Decimal(duration) + " min\n" + + (smb != 0 ? ("SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + MainApp.sResources.getString(R.string.reason) + ": " + reason; } else return MainApp.sResources.getString(R.string.nochangerequested); @@ -45,6 +51,7 @@ public class APSResult { ret = "" + MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + "(" + DecimalFormatter.to2Decimal(rate/configBuilder.getBaseBasalRate() *100) + "%)
" + "" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to2Decimal(duration) + " min
" + + (smb != 0 ? ("" + "SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + "" + MainApp.sResources.getString(R.string.reason) + ": " + reason.replace("<", "<").replace(">", ">"); return Html.fromHtml(ret); } else @@ -60,6 +67,7 @@ public class APSResult { newResult.rate = rate; newResult.duration = duration; newResult.changeRequested = changeRequested; + newResult.iob = iob; return newResult; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index 6e459a03b2..54a9cd0795 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -260,6 +260,7 @@ public class LoopPlugin implements PluginBase { // check rate for constrais final APSResult resultAfterConstraints = result.clone(); resultAfterConstraints.rate = constraintsInterface.applyBasalConstraints(resultAfterConstraints.rate); + resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java index c2a55ff894..12c7e8ac22 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -19,7 +19,6 @@ public class DetermineBasalResultAMA extends APSResult { public JSONObject json = new JSONObject(); public double eventualBG; public double snoozeBG; - public IobTotal iob; public DetermineBasalResultAMA(V8Object result, JSONObject j) { date = new Date(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java index a0cd1a75b0..e5154a8fa2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java @@ -17,7 +17,6 @@ public class DetermineBasalResultMA extends APSResult { public double eventualBG; public double snoozeBG; public String mealAssist; - public IobTotal iob; public DetermineBasalResultMA(V8Object result, JSONObject j) { json = j; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java index 8a6c29d4fe..f512d44535 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -20,9 +20,6 @@ public class DetermineBasalResultSMB extends APSResult { public JSONObject json = new JSONObject(); public double eventualBG; public double snoozeBG; - public IobTotal iob; - public double smbValue; - public long deliverAt; public DetermineBasalResultSMB(JSONObject result) { date = new Date(); @@ -54,9 +51,9 @@ public class DetermineBasalResultSMB extends APSResult { } if (result.has("units")) { changeRequested = true; - smbValue = result.getDouble("units"); + smb = result.getDouble("units"); } else { - smbValue = 0.0; + smb = 0d; //changeRequested as above } if (result.has("deliverAt")) { @@ -85,7 +82,8 @@ public class DetermineBasalResultSMB extends APSResult { newResult.changeRequested = changeRequested; newResult.rate = rate; newResult.duration = duration; - newResult.smbValue = smbValue; + newResult.smb = smb; + newResult.deliverAt = deliverAt; try { newResult.json = new JSONObject(json.toString()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 75ee736a8b..608887b9ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -268,6 +268,19 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { return in5minback; } + @Override + public long getLastSMBTime() { + long last = 0; + for (Integer pos = 0; pos < treatments.size(); pos++) { + Treatment t = treatments.get(pos); + if (!t.isValid) + continue; + if (t.isSMB && t.date > last) + last = t.date; + } + return last; + } + @Override public boolean isInHistoryRealTempBasalInProgress() { return getRealTempBasalFromHistory(System.currentTimeMillis()) != null; diff --git a/app/src/main/java/info/nightscout/utils/NSUpload.java b/app/src/main/java/info/nightscout/utils/NSUpload.java index 09ecbc9370..9e28910def 100644 --- a/app/src/main/java/info/nightscout/utils/NSUpload.java +++ b/app/src/main/java/info/nightscout/utils/NSUpload.java @@ -193,17 +193,8 @@ public class NSUpload { apsResult.json().put("timestamp", DateUtil.toISOString(lastRun.lastAPSRun)); deviceStatus.suggested = apsResult.json(); - if (lastRun.request instanceof DetermineBasalResultMA) { - DetermineBasalResultMA result = (DetermineBasalResultMA) lastRun.request; - deviceStatus.iob = result.iob.json(); - deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.lastAPSRun)); - } - - if (lastRun.request instanceof DetermineBasalResultAMA) { - DetermineBasalResultAMA result = (DetermineBasalResultAMA) lastRun.request; - deviceStatus.iob = result.iob.json(); - deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.lastAPSRun)); - } + deviceStatus.iob = lastRun.request.iob.json(); + deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.lastAPSRun)); if (lastRun.setByPump != null && lastRun.setByPump.enacted) { // enacted deviceStatus.enacted = lastRun.request.json(); From f5143b2ec438a61090ba97560540d1faffe8543d Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 09:24:17 +0200 Subject: [PATCH 005/171] display UAM prediction --- .../androidaps/plugins/Loop/APSResult.java | 99 ++++++++++++++++++- .../OpenAPSAMA/DetermineBasalResultAMA.java | 77 +-------------- .../OpenAPSSMB/DetermineBasalResultSMB.java | 80 +-------------- .../plugins/Overview/OverviewFragment.java | 14 +-- 4 files changed, 103 insertions(+), 167 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 5e89a5a0f3..9b44bc40fd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -1,16 +1,20 @@ package info.nightscout.androidaps.plugins.Loop; -import android.os.Parcel; -import android.os.Parcelable; import android.text.Html; import android.text.Spanned; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.utils.DecimalFormatter; @@ -18,11 +22,14 @@ import info.nightscout.utils.DecimalFormatter; * Created by mike on 09.06.2016. */ public class APSResult { + public Date date; public String reason; public double rate; public int duration; public boolean changeRequested = false; public IobTotal iob; + public JSONObject json = new JSONObject(); + public boolean hasPredictions = false; public double smb = 0d; // super micro bolus in units public long deliverAt = 0; @@ -34,7 +41,7 @@ public class APSResult { return MainApp.sResources.getString(R.string.canceltemp); else return MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + - "(" + DecimalFormatter.to2Decimal(rate/configBuilder.getBaseBasalRate() *100) + "%)\n" + + "(" + DecimalFormatter.to2Decimal(rate / configBuilder.getBaseBasalRate() * 100) + "%)\n" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to0Decimal(duration) + " min\n" + (smb != 0 ? ("SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + MainApp.sResources.getString(R.string.reason) + ": " + reason; @@ -49,7 +56,7 @@ public class APSResult { if (rate == 0 && duration == 0) ret = MainApp.sResources.getString(R.string.canceltemp); else ret = "" + MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + - "(" + DecimalFormatter.to2Decimal(rate/configBuilder.getBaseBasalRate() *100) + "%)
" + + "(" + DecimalFormatter.to2Decimal(rate / configBuilder.getBaseBasalRate() * 100) + "%)
" + "" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to2Decimal(duration) + " min
" + (smb != 0 ? ("" + "SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + "" + MainApp.sResources.getString(R.string.reason) + ": " + reason.replace("<", "<").replace(">", ">"); @@ -84,4 +91,88 @@ public class APSResult { } return json; } + + public List getPredictions() { + List array = new ArrayList<>(); + try { + long startTime = date.getTime(); + if (json.has("predBGs")) { + JSONObject predBGs = json.getJSONObject("predBGs"); + if (predBGs.has("IOB")) { + JSONArray iob = predBGs.getJSONArray("IOB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + if (predBGs.has("aCOB")) { + JSONArray iob = predBGs.getJSONArray("aCOB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + if (predBGs.has("COB")) { + JSONArray iob = predBGs.getJSONArray("COB"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + if (predBGs.has("UAM")) { + JSONArray iob = predBGs.getJSONArray("UAM"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isPrediction = true; + array.add(bg); + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + return array; + } + + public long getLatestPredictionsTime() { + long latest = 0; + try { + long startTime = date.getTime(); + if (json.has("predBGs")) { + JSONObject predBGs = json.getJSONObject("predBGs"); + if (predBGs.has("IOB")) { + JSONArray iob = predBGs.getJSONArray("IOB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + if (predBGs.has("aCOB")) { + JSONArray iob = predBGs.getJSONArray("aCOB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + if (predBGs.has("COB")) { + JSONArray iob = predBGs.getJSONArray("COB"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + if (predBGs.has("UAM")) { + JSONArray iob = predBGs.getJSONArray("UAM"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + + return latest; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java index 12c7e8ac22..a9574a0d6f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -2,25 +2,19 @@ package info.nightscout.androidaps.plugins.OpenAPSAMA; import com.eclipsesource.v8.V8Object; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.plugins.Loop.APSResult; -import info.nightscout.androidaps.data.IobTotal; public class DetermineBasalResultAMA extends APSResult { - public Date date; - public JSONObject json = new JSONObject(); public double eventualBG; public double snoozeBG; public DetermineBasalResultAMA(V8Object result, JSONObject j) { + this(); date = new Date(); json = j; if (result.contains("error")) { @@ -52,6 +46,7 @@ public class DetermineBasalResultAMA extends APSResult { } public DetermineBasalResultAMA() { + hasPredictions = true; } @Override @@ -87,72 +82,4 @@ public class DetermineBasalResultAMA extends APSResult { return null; } - public List getPredictions() { - List array = new ArrayList<>(); - try { - long startTime = date.getTime(); - if (json.has("predBGs")) { - JSONObject predBGs = json.getJSONObject("predBGs"); - if (predBGs.has("IOB")) { - JSONArray iob = predBGs.getJSONArray("IOB"); - for (int i = 1; i < iob.length(); i ++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - if (predBGs.has("aCOB")) { - JSONArray iob = predBGs.getJSONArray("aCOB"); - for (int i = 1; i < iob.length(); i ++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - if (predBGs.has("COB")) { - JSONArray iob = predBGs.getJSONArray("COB"); - for (int i = 1; i < iob.length(); i ++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - return array; - } - - public long getLatestPredictionsTime() { - long latest = 0; - try { - long startTime = date.getTime(); - if (json.has("predBGs")) { - JSONObject predBGs = json.getJSONObject("predBGs"); - if (predBGs.has("IOB")) { - JSONArray iob = predBGs.getJSONArray("IOB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - if (predBGs.has("aCOB")) { - JSONArray iob = predBGs.getJSONArray("aCOB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - if (predBGs.has("COB")) { - JSONArray iob = predBGs.getJSONArray("COB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - - return latest; - } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java index f512d44535..62aa9ed247 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -1,27 +1,19 @@ package info.nightscout.androidaps.plugins.OpenAPSSMB; -import com.eclipsesource.v8.V8Object; - -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.utils.DateUtil; public class DetermineBasalResultSMB extends APSResult { - public Date date; - public JSONObject json = new JSONObject(); public double eventualBG; public double snoozeBG; public DetermineBasalResultSMB(JSONObject result) { + this(); date = new Date(); json = result; try { @@ -71,6 +63,7 @@ public class DetermineBasalResultSMB extends APSResult { } public DetermineBasalResultSMB() { + hasPredictions = true; } @Override @@ -106,73 +99,4 @@ public class DetermineBasalResultSMB extends APSResult { } return null; } - - public List getPredictions() { - List array = new ArrayList<>(); - try { - long startTime = date.getTime(); - if (json.has("predBGs")) { - JSONObject predBGs = json.getJSONObject("predBGs"); - if (predBGs.has("IOB")) { - JSONArray iob = predBGs.getJSONArray("IOB"); - for (int i = 1; i < iob.length(); i++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - if (predBGs.has("aCOB")) { - JSONArray iob = predBGs.getJSONArray("aCOB"); - for (int i = 1; i < iob.length(); i++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - if (predBGs.has("COB")) { - JSONArray iob = predBGs.getJSONArray("COB"); - for (int i = 1; i < iob.length(); i++) { - BgReading bg = new BgReading(); - bg.value = iob.getInt(i); - bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; - array.add(bg); - } - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - return array; - } - - public long getLatestPredictionsTime() { - long latest = 0; - try { - long startTime = date.getTime(); - if (json.has("predBGs")) { - JSONObject predBGs = json.getJSONObject("predBGs"); - if (predBGs.has("IOB")) { - JSONArray iob = predBGs.getJSONArray("IOB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - if (predBGs.has("aCOB")) { - JSONArray iob = predBGs.getJSONArray("aCOB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - if (predBGs.has("COB")) { - JSONArray iob = predBGs.getJSONArray("COB"); - latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - - return latest; - } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index f50da429d7..abcfbfc7f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -103,8 +103,6 @@ import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastAckAlarm; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; -import info.nightscout.androidaps.plugins.OpenAPSAMA.DetermineBasalResultAMA; -import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.CalibrationDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; @@ -126,9 +124,6 @@ import info.nightscout.utils.Profiler; import info.nightscout.utils.Round; import info.nightscout.utils.SP; import info.nightscout.utils.ToastUtils; -//Added By Rumen for staledata alarm -import info.nightscout.androidaps.plugins.Overview.Notification; -import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; public class OverviewFragment extends Fragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { private static Logger log = LoggerFactory.getLogger(OverviewFragment.class); @@ -1189,8 +1184,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, cobView.setText(cobText); } - boolean showPrediction = showPredictionView.isChecked() && finalLastRun != null && finalLastRun.constraintsProcessed.getClass().equals(DetermineBasalResultAMA.class); - if (MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class) != null && MainApp.getSpecificPlugin(OpenAPSAMAPlugin.class).isEnabled(PluginBase.APS)) { + boolean showPrediction = showPredictionView.isChecked() && finalLastRun != null && finalLastRun.request.hasPredictions; + if (showPrediction) { showPredictionView.setVisibility(View.VISIBLE); getActivity().findViewById(R.id.overview_showprediction_label).setVisibility(View.VISIBLE); } else { @@ -1247,7 +1242,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, long fromTime; long endTime; if (showPrediction) { - int predHours = (int) (Math.ceil(((DetermineBasalResultAMA) finalLastRun.constraintsProcessed).getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); + int predHours = (int) (Math.ceil(finalLastRun.constraintsProcessed.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); predHours = Math.min(2, predHours); predHours = Math.max(0, predHours); hoursToFetch = rangeToDisplay - predHours; @@ -1573,8 +1568,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, bgListArray.add(bg); } if (showPrediction) { - DetermineBasalResultAMA amaResult = (DetermineBasalResultAMA) finalLastRun.constraintsProcessed; - List predArray = amaResult.getPredictions(); + List predArray = finalLastRun.constraintsProcessed.getPredictions(); bgListArray.addAll(predArray); } From ebfc4e307572c99dc133053cfa7e74fc01398884 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 15:36:52 +0200 Subject: [PATCH 006/171] colored predictions --- .../nightscout/androidaps/db/BgReading.java | 30 ++++++++++++++++--- .../androidaps/db/CareportalEvent.java | 5 ++++ .../androidaps/db/ExtendedBolus.java | 5 ++++ .../androidaps/db/ProfileSwitch.java | 5 ++++ .../nightscout/androidaps/db/Treatment.java | 5 ++++ .../androidaps/plugins/Loop/APSResult.java | 8 ++--- .../DataPointWithLabelInterface.java | 1 + .../PointsWithLabelGraphSeries.java | 23 +++++++------- app/src/main/res/values/colors.xml | 1 + 9 files changed, 65 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index cbe1850958..725094ae21 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -17,7 +17,6 @@ import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv; import info.nightscout.androidaps.plugins.Overview.OverviewPlugin; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; -import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.SP; @@ -43,7 +42,10 @@ public class BgReading implements DataPointWithLabelInterface { @DatabaseField public String _id = null; // NS _id - public boolean isPrediction = false; // true when drawing predictions as bg points + public boolean isCOBPrediction = false; // true when drawing predictions as bg points (COB) + public boolean isaCOBPrediction = false; // true when drawing predictions as bg points (aCOB) + public boolean isIOBPrediction = false; // true when drawing predictions as bg points (IOB) + public boolean isUAMPrediction = false; // true when drawing predictions as bg points (UAM) public BgReading() { } @@ -181,7 +183,10 @@ public class BgReading implements DataPointWithLabelInterface { @Override public PointsWithLabelGraphSeries.Shape getShape() { - return PointsWithLabelGraphSeries.Shape.POINT; + if (isPrediction()) + return PointsWithLabelGraphSeries.Shape.PREDICTION; + else + return PointsWithLabelGraphSeries.Shape.BG; } @Override @@ -202,7 +207,7 @@ public class BgReading implements DataPointWithLabelInterface { highLine = Profile.fromMgdlToUnits(OverviewPlugin.bgTargetHigh, units); } int color = MainApp.sResources.getColor(R.color.inrange); - if (isPrediction) + if (isPrediction()) color = MainApp.sResources.getColor(R.color.prediction); else if (valueToUnits(units) < lowLine) color = MainApp.sResources.getColor(R.color.low); @@ -211,4 +216,21 @@ public class BgReading implements DataPointWithLabelInterface { return color; } + @Override + public int getSecondColor() { + if (isIOBPrediction) + return MainApp.sResources.getColor(R.color.iob); + if (isCOBPrediction) + return MainApp.sResources.getColor(R.color.cob); + if (isaCOBPrediction) + return 0x80FFFFFF & MainApp.sResources.getColor(R.color.cob); + if (isUAMPrediction) + return MainApp.sResources.getColor(R.color.uam); + return R.color.mdtp_white; + } + + private boolean isPrediction() { + return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index f65da51176..b88b3e1009 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -242,4 +242,9 @@ public class CareportalEvent implements DataPointWithLabelInterface { return Color.GRAY; return Color.GRAY; } + + @Override + public int getSecondColor() { + return 0; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index 32a9eca24c..1c3d7a6ce6 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -280,4 +280,9 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { public int getColor() { return Color.CYAN; } + + @Override + public int getSecondColor() { + return 0; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java index fb99934382..cbdf86b5ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java @@ -202,6 +202,11 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { return Color.CYAN; } + @Override + public int getSecondColor() { + return 0; + } + public String toString() { return "ProfileSwitch{" + "date=" + date + diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index 971e1d55f1..c89dc4e71c 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -173,6 +173,11 @@ public class Treatment implements DataPointWithLabelInterface { return Color.CYAN; } + @Override + public int getSecondColor() { + return 0; + } + @Override public void setY(double y) { yValue = y; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 9b44bc40fd..8a6bf74aed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -104,7 +104,7 @@ public class APSResult { BgReading bg = new BgReading(); bg.value = iob.getInt(i); bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; + bg.isIOBPrediction = true; array.add(bg); } } @@ -114,7 +114,7 @@ public class APSResult { BgReading bg = new BgReading(); bg.value = iob.getInt(i); bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; + bg.isaCOBPrediction = true; array.add(bg); } } @@ -124,7 +124,7 @@ public class APSResult { BgReading bg = new BgReading(); bg.value = iob.getInt(i); bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; + bg.isCOBPrediction = true; array.add(bg); } } @@ -134,7 +134,7 @@ public class APSResult { BgReading bg = new BgReading(); bg.value = iob.getInt(i); bg.date = startTime + i * 5 * 60 * 1000L; - bg.isPrediction = true; + bg.isUAMPrediction = true; array.add(bg); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DataPointWithLabelInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DataPointWithLabelInterface.java index 20d478692a..3f6280431c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DataPointWithLabelInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/DataPointWithLabelInterface.java @@ -55,4 +55,5 @@ public interface DataPointWithLabelInterface extends DataPointInterface{ PointsWithLabelGraphSeries.Shape getShape(); float getSize(); int getColor(); + int getSecondColor(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java index 970aaa8385..b8575a5f7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java @@ -50,14 +50,8 @@ public class PointsWithLabelGraphSeries e * You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape} */ public enum Shape { - /** - * draws a point / circle - */ - POINT, - - /** - * draws a triangle - */ + BG, + PREDICTION, TRIANGLE, RECTANGLE, BOLUS, @@ -191,9 +185,19 @@ public class PointsWithLabelGraphSeries e // draw data point if (!overdraw) { - if (value.getShape() == Shape.POINT) { + if (value.getShape() == Shape.BG) { + mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(0); canvas.drawCircle(endX, endY, value.getSize(), mPaint); + } else if (value.getShape() == Shape.PREDICTION) { + mPaint.setColor(value.getColor()); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setStrokeWidth(0); + canvas.drawCircle(endX, endY, value.getSize(), mPaint); + mPaint.setColor(value.getSecondColor()); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setStrokeWidth(0); + canvas.drawCircle(endX, endY, value.getSize() / 3, mPaint); } else if (value.getShape() == Shape.RECTANGLE) { canvas.drawRect(endX-value.getSize(), endY-value.getSize(), endX+value.getSize(), endY+value.getSize(), mPaint); } else if (value.getShape() == Shape.TRIANGLE) { @@ -244,7 +248,6 @@ public class PointsWithLabelGraphSeries e } else if (value.getShape() == Shape.MBG) { mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); - float w = mPaint.getStrokeWidth(); canvas.drawCircle(endX, endY, value.getSize(), mPaint); } else if (value.getShape() == Shape.BGCHECK) { mPaint.setStyle(Paint.Style.FILL_AND_STROKE); diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5b1d364563..33943af398 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,6 +4,7 @@ #00ffff #FFFB8C00 #8BC34A + #ffea00 #FFFFFF #00FF00 #FF0000 From 4f16700dc084cc92033846618f4f8c1cfe54a336 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 15:47:37 +0200 Subject: [PATCH 007/171] provide lastBolusTime --- app/src/main/java/info/nightscout/androidaps/data/MealData.java | 1 + .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 1 + .../androidaps/plugins/Treatments/TreatmentsPlugin.java | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/data/MealData.java b/app/src/main/java/info/nightscout/androidaps/data/MealData.java index f00bfe231e..4e1014431c 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/MealData.java +++ b/app/src/main/java/info/nightscout/androidaps/data/MealData.java @@ -8,4 +8,5 @@ public class MealData { public double carbs = 0d; public double mealCOB = 0.0d; public double minDeviationSlope; + public long lastBolusTime; } 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 261a2a4522..60803c03b3 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 @@ -283,6 +283,7 @@ public class DetermineBasalAdapterSMBJS { 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); if (MainApp.getConfigBuilder().isAMAModeEnabled()) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 608887b9ac..67ab500205 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -247,6 +247,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { result.mealCOB = autosensData.cob; result.minDeviationSlope = autosensData.minDeviationSlope; } + result.lastBolusTime = getLastSMBTime(); return result; } From 204c39880f1456d7b7fcb191edd81429d746ad1b Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 17:22:25 +0200 Subject: [PATCH 008/171] mark SMB boluses in treatments tab --- .../plugins/Treatments/fragments/TreatmentsBolusFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java index b47cd3a6bf..5a13be1bbe 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java @@ -82,7 +82,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. Iob iob = t.iobCalc(System.currentTimeMillis(), profile.getDia()); holder.iob.setText(DecimalFormatter.to2Decimal(iob.iobContrib) + " U"); holder.activity.setText(DecimalFormatter.to3Decimal(iob.activityContrib) + " U"); - holder.mealOrCorrection.setText(t.mealBolus ? MainApp.sResources.getString(R.string.mealbolus) : MainApp.sResources.getString(R.string.correctionbous)); + holder.mealOrCorrection.setText(t.isSMB ? "SMB" : t.mealBolus ? MainApp.sResources.getString(R.string.mealbolus) : MainApp.sResources.getString(R.string.correctionbous)); holder.ph.setVisibility(t.source == Source.PUMP ? View.VISIBLE : View.GONE); holder.ns.setVisibility(t._id != null ? View.VISIBLE : View.GONE); holder.invalid.setVisibility(t.isValid ? View.GONE : View.VISIBLE); From aad168c50fca5e33f489a4bc9b8f11568e876ddc Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 21 Aug 2017 17:42:26 +0200 Subject: [PATCH 009/171] visualize SMB --- .../nightscout/androidaps/db/Treatment.java | 11 +++++++++-- .../plugins/Overview/OverviewFragment.java | 17 ++++++++++------- .../PointsWithLabelGraphSeries.java | 9 +++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index c89dc4e71c..e099f08086 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -14,6 +14,7 @@ import java.util.Objects; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; @@ -160,7 +161,10 @@ public class Treatment implements DataPointWithLabelInterface { @Override public PointsWithLabelGraphSeries.Shape getShape() { - return PointsWithLabelGraphSeries.Shape.BOLUS; + if (isSMB) + return PointsWithLabelGraphSeries.Shape.SMB; + else + return PointsWithLabelGraphSeries.Shape.BOLUS; } @Override @@ -170,7 +174,10 @@ public class Treatment implements DataPointWithLabelInterface { @Override public int getColor() { - return Color.CYAN; + if (isSMB) + return MainApp.sResources.getColor(R.color.tempbasal); + else + return Color.CYAN; } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index abcfbfc7f2..70185826f9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1595,7 +1595,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (!t.isValid) continue; if (t.getX() < fromTime || t.getX() > endTime) continue; - t.setY(getNearestBg((long) t.getX(), bgReadingsArray)); + if (t.isSMB) + t.setY(lowLine); + else + t.setY(getNearestBg((long) t.getX(), bgReadingsArray)); filteredTreatments.add(t); } @@ -1734,7 +1737,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, public void onBindViewHolder(NotificationsViewHolder holder, int position) { Notification notification = notificationsList.get(position); holder.dismiss.setTag(notification); - if(notification.text == MainApp.sResources.getString(R.string.nsalarm_staledata)) + if (notification.text == MainApp.sResources.getString(R.string.nsalarm_staledata)) holder.dismiss.setText("snooze"); holder.text.setText(notification.text); holder.time.setText(DateUtil.timeString(notification.date)); @@ -1785,12 +1788,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, BroadcastAckAlarm.handleClearAlarm(notification.nsAlarm, MainApp.instance().getApplicationContext(), 60 * 60 * 1000L); } // Adding current time to snooze if we got staleData - log.debug("Notification text is: "+notification.text); - if(notification.text.equals(MainApp.sResources.getString(R.string.nsalarm_staledata))){ + log.debug("Notification text is: " + notification.text); + if (notification.text.equals(MainApp.sResources.getString(R.string.nsalarm_staledata))) { NotificationStore nstore = getPlugin().notificationStore; - long msToSnooze = SP.getInt("nsalarm_staledatavalue",15)*60*1000L; - log.debug("snooze nsalarm_staledatavalue in minutes is "+SP.getInt("nsalarm_staledatavalue",15)+"\n in ms is: "+msToSnooze+" currentTimeMillis is: "+System.currentTimeMillis()); - nstore.snoozeTo(System.currentTimeMillis()+(SP.getInt("nsalarm_staledatavalue",15)*60*1000L)); + long msToSnooze = SP.getInt("nsalarm_staledatavalue", 15) * 60 * 1000L; + log.debug("snooze nsalarm_staledatavalue in minutes is " + SP.getInt("nsalarm_staledatavalue", 15) + "\n in ms is: " + msToSnooze + " currentTimeMillis is: " + System.currentTimeMillis()); + nstore.snoozeTo(System.currentTimeMillis() + (SP.getInt("nsalarm_staledatavalue", 15) * 60 * 1000L)); } break; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java index b8575a5f7c..f4d1d8aeda 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java @@ -55,6 +55,7 @@ public class PointsWithLabelGraphSeries e TRIANGLE, RECTANGLE, BOLUS, + SMB, EXTENDEDBOLUS, PROFILE, MBG, @@ -218,6 +219,14 @@ public class PointsWithLabelGraphSeries e if (value.getLabel() != null) { drawLabel45(endX, endY, value, canvas); } + } else if (value.getShape() == Shape.SMB) { + mPaint.setStrokeWidth(2); + Point[] points = new Point[3]; + points[0] = new Point((int)endX, (int)(endY-value.getSize())); + points[1] = new Point((int)(endX+value.getSize()), (int)(endY+value.getSize()*0.67)); + points[2] = new Point((int)(endX-value.getSize()), (int)(endY+value.getSize()*0.67)); + mPaint.setStyle(Paint.Style.FILL_AND_STROKE); + drawArrows(points, canvas, mPaint); } else if (value.getShape() == Shape.EXTENDEDBOLUS) { mPaint.setStrokeWidth(0); if (value.getLabel() != null) { From 1a8edc837278cb91b2ff8c6634b613d1d65fd95b Mon Sep 17 00:00:00 2001 From: Roumen Georgiev Date: Mon, 11 Sep 2017 13:09:57 +0000 Subject: [PATCH 010/171] Update ConfigBuilderPlugin.java - Typo fix on line 598 --- .../androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 30ee04de8a..9a43e3a33f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -595,7 +595,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain if (request.smb != 0) { long lastSMBTime = getLastSMBTime(); if (lastSMBTime != 0 && lastSMBTime + 4.5 * 60 * 1000 > System.currentTimeMillis()) { - log.debug("SMS requsted but still in 5 min interval"); + log.debug("SMB requsted but still in 5 min interval"); } else { DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; From 936184be6094b9089b667cfa78a75dfe466e385d Mon Sep 17 00:00:00 2001 From: Paul Andrel Date: Tue, 12 Sep 2017 15:03:48 -0400 Subject: [PATCH 011/171] Update determine-basal.js and basal-set-temp.js from Current oref0 dev. --- .../main/assets/OpenAPSSMB/determine-basal.js | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 3eaee83b5b..cb6dcd71fb 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -1,9 +1,9 @@ /* Determine Basal - + Released under MIT license. See the accompanying LICENSE.txt file for full terms and conditions - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -50,8 +50,8 @@ function convert_bg(value, profile) var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data) { var rT = {}; //short for requestedTemp - - var deliverAt = new Date(); + + var deliverAt = new Date(); if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { rT.error ='Error: could not get current basal rate'; @@ -63,7 +63,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ basal = profile.current_basal * autosens_data.ratio; basal = round_basal(basal, profile); if (basal != profile_current_basal) { - console.error("Autosens adjusting basal from "+profile.current_basal+" to "+basal+"; "); + console.error("Autosens adjusting basal from "+profile_current_basal+" to "+basal+"; "); } else { console.error("Basal unchanged: "+basal+"; "); } @@ -84,7 +84,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver - + // if min and max are set, then set target to their average var target_bg; var min_bg; @@ -101,18 +101,20 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.error ='Error: could not determine target_bg. '; return rT; } - + // 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"); + console.error("Temp Target set, not adjusting with autosens; "); } else { - // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range + // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range min_bg = round((min_bg - 60) / autosens_data.ratio) + 60; max_bg = round((max_bg - 60) / autosens_data.ratio) + 60; new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60; + // don't allow target_bg below 80 + new_target_bg = Math.max(80, new_target_bg); if (target_bg == new_target_bg) { - console.error("target_bg unchanged:", new_target_bg); + console.error("target_bg unchanged: "+new_target_bg+"; "); } else { console.error("target_bg from "+target_bg+" to "+new_target_bg+"; "); } @@ -144,7 +146,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ tick = round(glucose_status.delta,0); } //var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta); - var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta); + var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta); var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta); var profile_sens = round(profile.sens,1) @@ -157,7 +159,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { console.error("sens unchanged: "+sens); } - console.error(" (autosens ratio "+autosens_data.ratio+")"); + console.error(" (autosens ratio "+autosens_data.ratio+")"); } console.error(""); @@ -185,7 +187,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // adjust that for deviation like we did eventualBG var snoozeBG = naive_snoozeBG + deviation; - // adjust target BG range if needed to safely bring down high BG faster without causing lows + // adjust target BG range if needed to safely bring down high BG faster without causing lows if ( bg > max_bg && profile.adv_target_adjustments ) { // with target=100, as BG rises from 100 to 160, adjustedTarget drops from 100 to 80 var adjustedMinBG = round(Math.max(80, min_bg - (bg - min_bg)/3 ),0); @@ -215,7 +217,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } - var expectedDelta = calculate_expected_delta(profile.dia, target_bg, eventualBG, bgi); + var expectedDelta = calculate_expected_delta(profile.dia, target_bg, eventualBG, bgi); if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) { rT.error ='Error: could not calculate eventualBG. '; return rT; @@ -243,9 +245,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //if (iob_data.basaliob) { basaliob = iob_data.basaliob; } //else { basaliob = iob_data.iob - iob_data.bolussnooze; } var bolusiob = iob_data.iob - basaliob; - + // generate predicted future BGs based on IOB, COB, and current absorption rate - + var COBpredBGs = []; var aCOBpredBGs = []; var IOBpredBGs = []; @@ -261,20 +263,24 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // disable SMB when a high temptarget is set if (profile.temptargetSet && target_bg > 100) { enableSMB=false; - // enable SMB (if enabled in preferences) for DIA hours after bolus + // enable SMB/UAM (if enabled in preferences) for DIA hours after bolus } else if (profile.enableSMB_with_bolus && bolusiob > 0.1) { enableSMB=true; - // enable SMB (if enabled in preferences) while we have COB + // enable SMB/UAM (if enabled in preferences) while we have COB } else if (profile.enableSMB_with_COB && meal_data.mealCOB) { enableSMB=true; - // enable SMB (if enabled in preferences) if a low temptarget is set + // enable SMB/UAM (if enabled in preferences) if a low temptarget is set } else if (profile.enableSMB_with_temptarget && (profile.temptargetSet && target_bg < 100)) { enableSMB=true; + // enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry + // (6 hours is defined in carbWindow in lib/meal/total.js) + } else if (profile.enableSMB_after_carbs && meal_data.carbs) { + enableSMB=true; } // enable UAM (if enabled in preferences) for DIA hours after bolus, or if SMB is enabled var enableUAM=(profile.enableUAM && (bolusiob > 0.1 || enableSMB)); - + //console.error(meal_data); // carb impact and duration are 0 unless changed below var ci = 0; @@ -311,11 +317,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // calculate peak deviation in last hour, and slope from that to current deviation var minDeviationSlope = round(meal_data.minDeviationSlope,2); - //console.error(minDeviationSlope); - + //console.error(minDeviationSlope); + aci = 10; //5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m) - // duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m) + // duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m) cid = Math.max(0, meal_data.mealCOB * csf / ci ); acid = Math.max(0, meal_data.mealCOB * csf / aci ); // duration (hours) = duration (5m) * 5 / 60 * 2 (to account for linear decay) @@ -349,7 +355,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // eventually accounting for all carbs (if they can be absorbed over DIA) predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); predACI = Math.max(0, Math.max(0,aci) * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) ); - // if any carbs aren't absorbed after 4 hours, assume they'll absorb at a constant rate for next 4h + // if any carbs aren't absorbed after 4 hours, assume they'll absorb at a constant rate for next 4h COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI + remainingCI; // stop adding remainingCI after 4h if (COBpredBGs.length > 4 * 60 / 5) { remainingCI = 0; } @@ -381,7 +387,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if ( (cid || remainingCI > 0) && COBpredBGs.length > 12 && (COBpredBG < minCOBPredBG) ) { minCOBPredBG = round(COBpredBG); } if ( (cid || remainingCI > 0) && COBpredBG > maxIOBPredBG ) { maxCOBPredBG = COBpredBG; } if ( enableUAM && UAMpredBGs.length > 12 && (UAMpredBG < minUAMPredBG) ) { minUAMPredBG = round(UAMpredBG); } - if ( enableUAM && UAMpredBG > maxIOBPredBG ) { maxUAMPredBG = UAMpredBG; } + if ( enableUAM && UAMpredBG > maxIOBPredBG ) { maxUAMPredBG = UAMpredBG; } }); // set eventualBG to include effect of carbs //console.error("PredBGs:",JSON.stringify(predBGs)); @@ -466,7 +472,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // average the minIOBPredBG and minUAMPredBG if available if ( minUAMPredBG < 400 ) { avgMinPredBG = round( (minIOBPredBG+minUAMPredBG)/2 ); - } else { + } else { avgMinPredBG = minIOBPredBG; } @@ -487,8 +493,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG } else if ( enableUAM ) { minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG)); - } - + } + // make sure minPredBG isn't higher than avgPredBG minPredBG = Math.min( minPredBG, avgPredBG ); @@ -511,7 +517,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } rT.snoozeBG = snoozeBG; //console.error(minPredBG, minIOBPredBG, minUAMPredBG, minCOBPredBG, maxCOBPredBG, 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) + ", minPredBG " + convert_bg(minPredBG, profile) + ", IOBpredBG " + convert_bg(lastIOBpredBG, profile); @@ -523,7 +529,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } rT.reason += "; "; var bgUndershoot = target_bg - Math.max( naive_eventualBG, eventualBG, lastIOBpredBG ); - // calculate how long until COB (or IOB) predBGs drop below min_bg + // calculate how long until COB (or IOB) predBGs drop below min_bg var minutesAboveMinBG = 240; if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCI > 0 )) { for (var i=0; i minDelta) { @@ -593,7 +601,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.reason += ", naive_eventualBG < 40. "; return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); } - if (glucose_status.delta > minDelta) { + if (glucose_status.delta > minDelta) { rT.reason += ", but Delta " + tick + " > expectedDelta " + expectedDelta; } else { rT.reason += ", but Min. Delta " + minDelta.toFixed(2) + " > Exp. Delta " + expectedDelta; @@ -612,16 +620,16 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (snoozeBG > min_bg) { // if adding back in the bolus contribution BG would be above min // If we're not in SMB mode with COB, or lastCOBpredBG > target_bg, bolus snooze if (! (microBolusAllowed && rT.COB) || lastCOBpredBG > target_bg) { - rT.reason += ", bolus snooze: eventual BG range " + convert_bg(eventualBG, profile) + "-" + convert_bg(snoozeBG, profile); - //console.error(currenttemp, basal ); - 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); + rT.reason += ", bolus snooze: eventual BG range " + convert_bg(eventualBG, profile) + "-" + convert_bg(snoozeBG, profile); + //console.error(currenttemp, basal ); + 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 { // 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 @@ -680,7 +688,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } } - + /* var minutes_running; if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { @@ -691,7 +699,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { minutes_running = 30 - currenttemp.duration; } - + // 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) * ((30-minutes_running)/60) * sens; @@ -703,11 +711,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } */ - + // if eventual BG is above min but BG is falling faster than expected Delta if (minDelta < expectedDelta) { // if in SMB mode, don't cancel SMB zero temp - if (! (microBolusAllowed && enableSMB)) { + if (! (microBolusAllowed && enableSMB)) { if (glucose_status.delta < minDelta) { rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Delta " + tick + " < Exp. Delta " + expectedDelta; } else { @@ -748,10 +756,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var basaliob; if (iob_data.basaliob) { basaliob = iob_data.basaliob; } else { basaliob = iob_data.iob - iob_data.bolussnooze; } - // if we're not here because of SMB, eventual BG is at/above target + // if we're not here because of SMB, eventual BG is at/above target if (! (microBolusAllowed && rT.COB)) { - rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", "; - } + rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", "; + } if (basaliob > max_iob) { rT.reason += "basaliob " + round(basaliob,2) + " > max_iob " + max_iob; if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) { @@ -787,12 +795,12 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.minPredBG = minPredBG; //console.error(iob_data.lastBolusTime); // minutes since last bolus - var lastBolusAge = round(( new Date().getTime() - meal_data.lastBolusTime ) / 60000,1); + var lastBolusAge = round(( new Date().getTime() - iob_data.lastBolusTime ) / 60000,1); //console.error(lastBolusAge); //console.error(profile.temptargetSet, target_bg, rT.COB); // only allow microboluses with COB or low temp targets, or within DIA hours of a bolus // only microbolus if 0.1U SMB represents 20m or less of basal (0.3U/hr or higher) - if (microBolusAllowed && enableSMB && profile.current_basal >= 0.3) { + if (microBolusAllowed && enableSMB && profile.current_basal >= 0.3 && bg > threshold) { // never bolus more than 30m worth of basal maxBolus = round(profile.current_basal/2,1); // bolus 1/3 the insulinReq, up to maxBolus @@ -830,11 +838,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ console.error("naive_eventualBG",naive_eventualBG+",",durationReq+"m zero temp needed; last bolus",lastBolusAge+"m ago; maxBolus: "+maxBolus); if (lastBolusAge > 3) { if (microBolus > 0) { - rT.units = microBolus; + rT.units = microBolus; rT.reason += "Microbolusing " + microBolus + "U. "; } } else { - rT.reason += "Waiting " + nextBolusMins + "m to microbolus again(" + microBolus + "). "; + rT.reason += "Waiting " + nextBolusMins + "m to microbolus again. "; } //rT.reason += ". "; @@ -844,7 +852,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.duration = durationReq; return rT; } - + // if insulinReq is negative, snoozeBG > target_bg, and lastCOBpredBG > target_bg, set a neutral temp if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. "; @@ -882,4 +890,4 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ }; -module.exports = determine_basal; +module.exports = determine_basal; \ No newline at end of file From 88c1248c41993ffcffa000e041d38eb1d8488c2d Mon Sep 17 00:00:00 2001 From: Paul Andrel Date: Tue, 12 Sep 2017 15:04:04 -0400 Subject: [PATCH 012/171] Update determine-basal.js and basal-set-temp.js from Current oref0 dev. --- app/src/main/assets/OpenAPSSMB/basal-set-temp.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/basal-set-temp.js b/app/src/main/assets/OpenAPSSMB/basal-set-temp.js index 9745869194..1686ea2c47 100644 --- a/app/src/main/assets/OpenAPSSMB/basal-set-temp.js +++ b/app/src/main/assets/OpenAPSSMB/basal-set-temp.js @@ -29,8 +29,8 @@ var round_basal = require('./round-basal'); } 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"; + if (typeof(currenttemp) !== 'undefined' && typeof(currenttemp.duration) !== 'undefined' && typeof(currenttemp.rate) !== 'undefined' && currenttemp.duration > (duration-10) && currenttemp.duration <= 120 && suggestedRate <= currenttemp.rate * 1.2 && suggestedRate >= currenttemp.rate * 0.8) { + rT.reason += " "+currenttemp.duration+"m left and " + currenttemp.rate + " ~ req " + suggestedRate + "U/hr: no temp required"; return rT; } @@ -58,4 +58,4 @@ var round_basal = require('./round-basal'); } }; -module.exports = tempBasalFunctions; +module.exports = tempBasalFunctions; \ No newline at end of file From 87c993cde134c2ce3cae6820facba53c801d0841 Mon Sep 17 00:00:00 2001 From: Roumen Georgiev Date: Wed, 13 Sep 2017 06:49:48 +0000 Subject: [PATCH 013/171] Temp fix for not adding SMB boluses to treatments and IOB calculations --- .../androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 9a43e3a33f..9684aa837b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -457,7 +457,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); result = activePump.deliverTreatment(detailedBolusInfo); - + if(result.success) addToHistoryTreatment(detailedBolusInfo); BolusProgressDialog.bolusEnded = true; MainApp.bus().post(new EventDismissBolusprogressIfRunning(result)); From e795a8cd9916e414bf372818d1af1da0d1de5e66 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 13 Sep 2017 07:05:47 +0000 Subject: [PATCH 014/171] Revert "Temp fix for not adding SMB boluses to treatments and IOB calculations" This reverts commit 87c993cde134c2ce3cae6820facba53c801d0841 --- .../androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 9684aa837b..9a43e3a33f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -457,7 +457,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); result = activePump.deliverTreatment(detailedBolusInfo); - if(result.success) addToHistoryTreatment(detailedBolusInfo); + BolusProgressDialog.bolusEnded = true; MainApp.bus().post(new EventDismissBolusprogressIfRunning(result)); From a4ebd15dbf2d265f3e3534b3b4a6766aa3063b7d Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Wed, 13 Sep 2017 13:18:57 +0200 Subject: [PATCH 015/171] Set Source.USER for SMB to deliver. --- .../androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 30ee04de8a..45d82f46de 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -30,6 +30,7 @@ import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; +import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.Treatment; @@ -601,6 +602,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; detailedBolusInfo.insulin = request.smb; detailedBolusInfo.isSMB = true; + detailedBolusInfo.source = Source.USER; PumpEnactResult smbResult = deliverTreatment(detailedBolusInfo); if (smbResult.success) return result; From 6ef4b0c2a9b7db0cdaa938419892dd7bb9c6a395 Mon Sep 17 00:00:00 2001 From: Roumen Georgiev Date: Fri, 15 Sep 2017 09:33:07 +0000 Subject: [PATCH 016/171] Only do SMB if invoke is from EventNewBG --- .../nightscout/androidaps/plugins/Loop/LoopPlugin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index 54a9cd0795..83a8e9d5b2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -260,7 +260,12 @@ public class LoopPlugin implements PluginBase { // check rate for constrais final APSResult resultAfterConstraints = result.clone(); resultAfterConstraints.rate = constraintsInterface.applyBasalConstraints(resultAfterConstraints.rate); - resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); + if(initiator == "EventNewBG") { + resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); + } else { + log.debug("Loop is not invoked by EventNewBG so SMB is 0("+constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb)+")"); + resultAfterConstraints.smb = 0.0; + } if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; From 60990e500ef53515836a6c6b57df82ffabbeddd3 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 18 Sep 2017 21:28:43 +0000 Subject: [PATCH 017/171] Revert "Only do SMB if invoke is from EventNewBG" This reverts commit 6ef4b0c2a9b7db0cdaa938419892dd7bb9c6a395 --- .../nightscout/androidaps/plugins/Loop/LoopPlugin.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index 83a8e9d5b2..54a9cd0795 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -260,12 +260,7 @@ public class LoopPlugin implements PluginBase { // check rate for constrais final APSResult resultAfterConstraints = result.clone(); resultAfterConstraints.rate = constraintsInterface.applyBasalConstraints(resultAfterConstraints.rate); - if(initiator == "EventNewBG") { - resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); - } else { - log.debug("Loop is not invoked by EventNewBG so SMB is 0("+constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb)+")"); - resultAfterConstraints.smb = 0.0; - } + resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; From 2750ccbf26bf3153b0663a4ea9c8c08a82c0bf44 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Thu, 21 Sep 2017 21:23:34 +0200 Subject: [PATCH 018/171] Fix APSResult.toSpanned for SMBs. Adds a missing newline after "SMB: xxx" (cherry picked from commit d4de9ce) --- .../info/nightscout/androidaps/plugins/Loop/APSResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 862f4687f4..82eef8d6cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -60,9 +60,9 @@ public class APSResult { if (rate == 0 && duration == 0) ret = MainApp.sResources.getString(R.string.canceltemp); else ret = "" + MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + - "(" + DecimalFormatter.to2Decimal(rate / configBuilder.getBaseBasalRate() * 100) + "%)
" + + "(" + DecimalFormatter.to2Decimal(rate / configBuilder.getBaseBasalRate() * 100) + "%)
" + "" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to2Decimal(duration) + " min
" + - (smb != 0 ? ("" + "SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + + (smb != 0 ? ("" + "SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U
") : "") + "" + MainApp.sResources.getString(R.string.reason) + ": " + reason.replace("<", "<").replace(">", ">"); return Html.fromHtml(ret); } else From 7e8e7f0daec9d29a329f857aedb297a4a1c74dc2 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Fri, 22 Sep 2017 15:51:33 +0200 Subject: [PATCH 019/171] Fix APSResult.toString as well. (cherry picked from commit d485e5b) --- .../java/info/nightscout/androidaps/plugins/Loop/APSResult.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 82eef8d6cc..3e1e79d462 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -47,7 +47,7 @@ public class APSResult { return MainApp.sResources.getString(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + "(" + DecimalFormatter.to2Decimal(rate / configBuilder.getBaseBasalRate() * 100) + "%)\n" + MainApp.sResources.getString(R.string.duration) + ": " + DecimalFormatter.to0Decimal(duration) + " min\n" + - (smb != 0 ? ("SMB" + ": " + DecimalFormatter.to2Decimal(smb) + " U") : "") + + (smb != 0 ? ("SMB: " + DecimalFormatter.to2Decimal(smb) + " U\n") : "") + MainApp.sResources.getString(R.string.reason) + ": " + reason; } else return MainApp.sResources.getString(R.string.nochangerequested); From c6c5624b85b915298bd8855fa86ee6a531073d41 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sat, 7 Oct 2017 21:15:34 +0200 Subject: [PATCH 020/171] Singleton OpenAPSSMBPlugin. --- .../info/nightscout/androidaps/MainApp.java | 1 + .../OpenAPSSMB/OpenAPSSMBFragment.java | 24 +++++++------------ .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 16 +++++++++---- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index ce55d7ef8b..f85c3ea124 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -131,6 +131,7 @@ public class MainApp extends Application { if (Config.LOOPENABLED) pluginsList.add(LoopPlugin.getPlugin()); if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSMAPlugin.getPlugin()); if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSAMAPlugin.getPlugin()); + if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSSMBPlugin.getPlugin()); pluginsList.add(NSProfilePlugin.getPlugin()); if (Config.OTHERPROFILES) pluginsList.add(SimpleProfilePlugin.getPlugin()); if (Config.OTHERPROFILES) pluginsList.add(LocalProfileFragment.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java index b08d0dac9d..1c0af1a67d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java @@ -27,15 +27,6 @@ import info.nightscout.utils.JSONFormatter; public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnClickListener { private static Logger log = LoggerFactory.getLogger(OpenAPSSMBFragment.class); - private static OpenAPSSMBPlugin openAPSSMBPlugin; - - public static OpenAPSSMBPlugin getPlugin() { - if (openAPSSMBPlugin == null) { - openAPSSMBPlugin = new OpenAPSSMBPlugin(); - } - return openAPSSMBPlugin; - } - Button run; TextView lastRunView; TextView glucoseStatusView; @@ -74,7 +65,7 @@ public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnCli public void onClick(View view) { switch (view.getId()) { case R.id.openapsma_run: - getPlugin().invoke("OpenAPSAMA button"); + OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSAMA button"); Answers.getInstance().logCustom(new CustomEvent("OpenAPS_AMA_Run")); break; } @@ -98,12 +89,13 @@ public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnCli activity.runOnUiThread(new Runnable() { @Override public void run() { - DetermineBasalResultSMB lastAPSResult = getPlugin().lastAPSResult; + OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); + DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; if (lastAPSResult != null) { resultView.setText(JSONFormatter.format(lastAPSResult.json)); requestView.setText(lastAPSResult.toSpanned()); } - DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = getPlugin().lastDetermineBasalAdapterSMBJS; + DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; if (determineBasalAdapterSMBJS != null) { glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam())); currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam())); @@ -118,11 +110,11 @@ public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnCli mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam())); scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug()); } - if (getPlugin().lastAPSRun != null) { - lastRunView.setText(getPlugin().lastAPSRun.toLocaleString()); + if (plugin.lastAPSRun != null) { + lastRunView.setText(plugin.lastAPSRun.toLocaleString()); } - if (getPlugin().lastAutosensResult != null) { - autosensDataView.setText(JSONFormatter.format(getPlugin().lastAutosensResult.json())); + if (plugin.lastAutosensResult != null) { + autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json())); } } }); 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 9ddbacd355..44490d6151 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 @@ -23,9 +23,6 @@ import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.ScriptReader; -import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalResultSMB; -import info.nightscout.androidaps.plugins.OpenAPSSMB.DetermineBasalAdapterSMBJS; -import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; import info.nightscout.utils.DateUtil; @@ -33,7 +30,6 @@ import info.nightscout.utils.NSUpload; import info.nightscout.utils.Profiler; import info.nightscout.utils.Round; import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; import info.nightscout.utils.ToastUtils; /** @@ -51,6 +47,18 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { boolean fragmentEnabled = false; boolean fragmentVisible = true; + private static OpenAPSSMBPlugin openAPSSMBPlugin; + + private OpenAPSSMBPlugin() { + } + + public static OpenAPSSMBPlugin getPlugin() { + if (openAPSSMBPlugin == null) { + openAPSSMBPlugin = new OpenAPSSMBPlugin(); + } + return openAPSSMBPlugin; + } + @Override public String getName() { return MainApp.instance().getString(R.string.openapssmb); From 755c82898cf8b5633f5c5ec6028bd7b9078c9494 Mon Sep 17 00:00:00 2001 From: AdrianLxM Date: Sat, 21 Oct 2017 22:38:07 +0200 Subject: [PATCH 021/171] 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 { From 4e5a374b9cfeb5d897e344963d35bcd1e065e34c Mon Sep 17 00:00:00 2001 From: AdrianLxM Date: Sun, 22 Oct 2017 02:20:11 +0200 Subject: [PATCH 022/171] include smb into basal iob --- .../main/java/info/nightscout/androidaps/data/IobTotal.java | 6 +++--- .../androidaps/plugins/Treatments/TreatmentsPlugin.java | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java index 666b3191c6..dd00978d04 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java +++ b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java @@ -64,9 +64,9 @@ public class IobTotal { result.iob = bolusIOB.iob + basalIob.basaliob; result.activity = bolusIOB.activity + basalIob.activity; result.bolussnooze = bolusIOB.bolussnooze; - result.basaliob = basalIob.basaliob; - result.netbasalinsulin = basalIob.netbasalinsulin; - result.hightempinsulin = basalIob.hightempinsulin; + result.basaliob = bolusIOB.basaliob + basalIob.basaliob; + result.netbasalinsulin = bolusIOB.netbasalinsulin + basalIob.netbasalinsulin; + result.hightempinsulin = basalIob.hightempinsulin + bolusIOB.hightempinsulin; result.microBolusInsulin = bolusIOB.microBolusInsulin + basalIob.microBolusInsulin; result.microBolusIOB = bolusIOB.microBolusIOB + basalIob.microBolusIOB; result.lastBolusTime = bolusIOB.lastBolusTime; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 44330ac7e6..8a14bd5645 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -202,8 +202,11 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { } else { if (t.date > total.lastBolusTime) total.lastBolusTime = t.date; - total.basaliob += t.insulin; + total.basaliob += tIOB.iobContrib; total.microBolusIOB += tIOB.iobContrib; + total.hightempinsulin += t.insulin; + total.netbasalinsulin += t.insulin; + total.microBolusInsulin += t.insulin; } } From 29b075e7001db03cb491452a932bd41eb32ce357 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 24 Oct 2017 17:15:49 +0200 Subject: [PATCH 023/171] display minDeviationSlope --- .../info/nightscout/androidaps/Config.java | 2 ++ .../plugins/Overview/OverviewFragment.java | 7 ++++- .../plugins/Overview/graphData/GraphData.java | 30 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index abda0ca941..408cd64122 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -28,6 +28,8 @@ public class Config { public static final boolean ALLPREFERENCES = !BuildConfig.NSCLIENTOLNY; + public static final boolean displayDeviationSlope = true; + public static final boolean detailedLog = true; public static final boolean logFunctionCalls = true; public static final boolean logIncommingData = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index bed4aadba9..d2e5a6943d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1281,6 +1281,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, boolean useCobForScale = false; boolean useDevForScale = false; boolean useRatioForScale = false; + boolean useDSForScale = false; if (showIobView.isChecked()) { useIobForScale = true; @@ -1290,6 +1291,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, useDevForScale = true; } else if (showRatiosView.isChecked()) { useRatioForScale = true; + } else if (Config.displayDeviationSlope) { + useDSForScale = true; } if (showIobView.isChecked()) @@ -1300,8 +1303,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, secondGraphData.addDeviations(iobGraph, fromTime, now, useDevForScale, 1d); if (showRatiosView.isChecked()) secondGraphData.addRatio(iobGraph, fromTime, now, useRatioForScale, 1d); + if (Config.displayDeviationSlope) + secondGraphData.addDeviationSlope(iobGraph, fromTime, now, useDSForScale, 1d); - if (showIobView.isChecked() || showCobView.isChecked() || showDeviationsView.isChecked() || showRatiosView.isChecked()) { + if (showIobView.isChecked() || showCobView.isChecked() || showDeviationsView.isChecked() || showRatiosView.isChecked() || Config.displayDeviationSlope) { iobGraph.setVisibility(View.VISIBLE); } else { iobGraph.setVisibility(View.GONE); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index f4be1fd351..dec8da3334 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -428,6 +428,36 @@ public class GraphData { addSeriesWithoutInvalidate(graph, ratioSeries); } + // scale in % of vertical size (like 0.3) + public void addDeviationSlope(GraphView graph, long fromTime, long toTime, boolean useForScale, double scale) { + LineGraphSeries dsSeries; + List dsArray = new ArrayList<>(); + Double maxDSValueFound = 0d; + Scale dsScale = new Scale(); + + for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { + AutosensData autosensData = IobCobCalculatorPlugin.getAutosensData(time); + if (autosensData != null) { + dsArray.add(new DataPoint(time, autosensData.minDeviationSlope)); + maxDSValueFound = Math.max(maxDSValueFound, Math.abs(autosensData.minDeviationSlope)); + } + } + + // RATIOS + DataPoint[] ratioData = new DataPoint[dsArray.size()]; + ratioData = dsArray.toArray(ratioData); + dsSeries = new LineGraphSeries<>(ratioData); + dsSeries.setColor(Color.MAGENTA); + dsSeries.setThickness(3); + + if (useForScale) + maxY = maxDSValueFound; + + dsScale.setMultiplier(maxY * scale / maxDSValueFound); + + addSeriesWithoutInvalidate(graph, dsSeries); + } + // scale in % of vertical size (like 0.3) public void addNowLine(GraphView graph, long now) { LineGraphSeries seriesNow; From 2925c16fb9c1b951e12e4df7273b8ca82e4da79a Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 24 Oct 2017 17:50:44 +0200 Subject: [PATCH 024/171] comment minDeviationSlope --- .../androidaps/plugins/IobCobCalculator/AutosensData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 9ce6b340e2..ae4de096f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -60,7 +60,7 @@ public class AutosensData { public double avgDeviation = 0d; public double autosensRatio = 1d; - public double minDeviationSlope; + public double minDeviationSlope; // renamed to slopeFromMaxDeviation in 0.6.0 public String log(long time) { return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " minDeviationSlope=" + minDeviationSlope; From 551ca92201e3fceb622e4750d0578b900adff9df Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 24 Oct 2017 23:13:29 +0200 Subject: [PATCH 025/171] Remove cast to DetermineBasalResultAMA for predictions. --- .../plugins/Overview/OverviewFragment.java | 2 +- .../plugins/Overview/graphData/GraphData.java | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index d2e5a6943d..a7567508a8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1323,7 +1323,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, // **** BG **** if (showPrediction) - graphData.addBgReadings(bgGraph, fromTime, toTime, lowLine, highLine, (DetermineBasalResultAMA) finalLastRun.constraintsProcessed); + graphData.addBgReadings(bgGraph, fromTime, toTime, lowLine, highLine, finalLastRun.constraintsProcessed); else graphData.addBgReadings(bgGraph, fromTime, toTime, lowLine, highLine, null); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index dec8da3334..10e7179483 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -12,7 +12,6 @@ import com.jjoe64.graphview.series.LineGraphSeries; import com.jjoe64.graphview.series.Series; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import info.nightscout.androidaps.Constants; @@ -27,7 +26,7 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.BasalData; -import info.nightscout.androidaps.plugins.OpenAPSAMA.DetermineBasalResultAMA; +import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Overview.graphExtensions.AreaGraphSeries; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DoubleDataPoint; @@ -52,7 +51,7 @@ public class GraphData { private List bgReadingsArray; private String units; - public void addBgReadings(GraphView bgGraph, long fromTime, long toTime, double lowLine, double highLine, DetermineBasalResultAMA amaResult) { + public void addBgReadings(GraphView bgGraph, long fromTime, long toTime, double lowLine, double highLine, APSResult apsResult) { double maxBgValue = 0d; bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true); List bgListArray = new ArrayList<>(); @@ -61,14 +60,12 @@ public class GraphData { return; } - Iterator it = bgReadingsArray.iterator(); - while (it.hasNext()) { - BgReading bg = it.next(); + for (BgReading bg : bgReadingsArray) { if (bg.value > maxBgValue) maxBgValue = bg.value; bgListArray.add(bg); } - if (amaResult != null) { - List predArray = amaResult.getPredictions(); + if (apsResult != null) { + List predArray = apsResult.getPredictions(); bgListArray.addAll(predArray); } @@ -266,7 +263,7 @@ public class GraphData { } } - double getNearestBg(long date) { + private double getNearestBg(long date) { double bg = 0; for (int r = bgReadingsArray.size() - 1; r >= 0; r--) { BgReading reading = bgReadingsArray.get(r); From dfd657ce2dda940260341e72ddefc67084a11502 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 26 Oct 2017 21:29:38 +0200 Subject: [PATCH 026/171] remove wrong imports --- .../DetermineBasalAdapterSMBJS.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 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 7bdd0178d6..2078d24fc3 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 @@ -1,10 +1,5 @@ package info.nightscout.androidaps.plugins.OpenAPSSMB; -import com.eclipsesource.v8.JavaVoidCallback; -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; @@ -128,11 +123,10 @@ public class DetermineBasalAdapterSMBJS { makeParam(mMealData, rhino, scope), setTempBasalFunctionsObj, new Boolean(mMicrobolusAllowed), - makeParam(null,rhino,scope) // reservoir data as undefined + makeParam(null, rhino, scope) // reservoir data as undefined }; - NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params); scriptDebug = LoggerCallback.getScriptDebug(); @@ -217,11 +211,12 @@ public class DetermineBasalAdapterSMBJS { double autosensDataRatio, boolean tempTargetSet, boolean microBolusAllowed - ) throws JSONException { + ) throws JSONException { String units = profile.getUnits(); - mProfile = new JSONObject();; + mProfile = new JSONObject(); + ; mProfile.put("max_iob", maxIob); mProfile.put("dia", profile.getDia()); mProfile.put("type", "current"); @@ -252,7 +247,8 @@ public class DetermineBasalAdapterSMBJS { 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 JSONObject();; + mCurrentTemp = new JSONObject(); + ; mCurrentTemp.put("temp", "absolute"); mCurrentTemp.put("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); mCurrentTemp.put("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()); @@ -265,7 +261,8 @@ public class DetermineBasalAdapterSMBJS { mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray); - mGlucoseStatus = new JSONObject();; + mGlucoseStatus = new JSONObject(); + ; mGlucoseStatus.put("glucose", glucoseStatus.glucose); if (SP.getBoolean("always_use_shortavg", false)) { @@ -276,7 +273,8 @@ public class DetermineBasalAdapterSMBJS { mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta); mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta); - mMealData = new JSONObject();; + mMealData = new JSONObject(); + ; mMealData.put("carbs", mealData.carbs); mMealData.put("boluses", mealData.boluses); mMealData.put("mealCOB", mealData.mealCOB); @@ -284,7 +282,8 @@ public class DetermineBasalAdapterSMBJS { mMealData.put("lastBolusTime", mealData.lastBolusTime); if (MainApp.getConfigBuilder().isAMAModeEnabled()) { - mAutosensData = new JSONObject();; + mAutosensData = new JSONObject(); + ; mAutosensData.put("ratio", autosensDataRatio); } else { mAutosensData = null; @@ -295,7 +294,7 @@ public class DetermineBasalAdapterSMBJS { public Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { - if(jsonObject == null) return Undefined.instance; + if (jsonObject == null) return Undefined.instance; Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), new Callable() { @Override From b5f843d901080dd148ae2ad3089a2ccb87891a0d Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 26 Oct 2017 22:12:15 +0200 Subject: [PATCH 027/171] getLastSBMTime -> getLastBolusTime --- .../androidaps/interfaces/TreatmentsInterface.java | 2 +- .../plugins/ConfigBuilder/ConfigBuilderPlugin.java | 6 +++--- .../androidaps/plugins/Treatments/TreatmentsPlugin.java | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 2eae150cda..134b419240 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -30,7 +30,7 @@ public interface TreatmentsInterface { List getTreatmentsFromHistory(); List getTreatments5MinBackFromHistory(long time); - long getLastSMBTime(); + long getLastBolusTime(); // real basals (not faked by extended bolus) boolean isInHistoryRealTempBasalInProgress(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index fe0145a15f..8be6a5475c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -594,7 +594,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } if (request.smb != 0) { - long lastSMBTime = getLastSMBTime(); + long lastSMBTime = getLastBolusTime(); if (lastSMBTime != 0 && lastSMBTime + 4.5 * 60 * 1000 > System.currentTimeMillis()) { log.debug("SMB requsted but still in 5 min interval"); } else { @@ -820,8 +820,8 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain } @Override - public long getLastSMBTime() { - return activeTreatments.getLastSMBTime(); + public long getLastBolusTime() { + return activeTreatments.getLastBolusTime(); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 8a14bd5645..441f6eec88 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -8,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Date; import java.util.List; import info.nightscout.androidaps.Config; @@ -256,7 +257,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { result.mealCOB = autosensData.cob; result.minDeviationSlope = autosensData.minDeviationSlope; } - result.lastBolusTime = getLastSMBTime(); + result.lastBolusTime = getLastBolusTime(); return result; } @@ -279,15 +280,16 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { } @Override - public long getLastSMBTime() { + public long getLastBolusTime() { long last = 0; for (Integer pos = 0; pos < treatments.size(); pos++) { Treatment t = treatments.get(pos); if (!t.isValid) continue; - if (t.isSMB && t.date > last) + if (t.date > last) last = t.date; } + log.debug("Last bolus time: " + new Date(last).toLocaleString()); return last; } From 2a04505862478c795441b193b591ddfe91361c26 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 1 Dec 2017 16:18:46 +0100 Subject: [PATCH 028/171] fix compile --- .../java/info/nightscout/androidaps/PreferencesActivity.java | 4 +--- .../androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index cde687b3a6..c72bce73d7 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -16,12 +16,9 @@ import android.text.TextUtils; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; -<<<<<<< HEAD import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; -======= import info.nightscout.androidaps.plugins.Careportal.CareportalPlugin; import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; ->>>>>>> dev2 import info.nightscout.androidaps.plugins.Insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.NSClientInternalPlugin; @@ -144,6 +141,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(LoopPlugin.getPlugin(), PluginBase.LOOP); addPreferencesFromResourceIfEnabled(OpenAPSMAPlugin.getPlugin(), PluginBase.APS); addPreferencesFromResourceIfEnabled(OpenAPSAMAPlugin.getPlugin(), PluginBase.APS); + addPreferencesFromResourceIfEnabled(OpenAPSSMBPlugin.getPlugin(), PluginBase.APS); } addPreferencesFromResourceIfEnabled(SensitivityAAPSPlugin.getPlugin(), PluginBase.SENSITIVITY); 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 7d5a8c7458..54376b2f10 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 @@ -105,6 +105,11 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { if (type == APS) this.fragmentVisible = fragmentVisible; } + @Override + public int getPreferencesId() { + return R.xml.pref_openapsama; + } + @Override public void setFragmentEnabled(int type, boolean fragmentEnabled) { if (type == APS) this.fragmentEnabled = fragmentEnabled; From 67c89f5d9802ae984952db0c176223fc3579cafe Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 1 Dec 2017 18:41:24 +0100 Subject: [PATCH 029/171] distinguish between common and SMB bolus in queue --- .../androidaps/queue/CommandQueue.java | 17 +++++--- .../androidaps/queue/commands/Command.java | 1 + .../queue/commands/CommandSMBBolus.java | 39 +++++++++++++++++++ 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index 2b085007e3..45efe4722d 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -31,6 +31,7 @@ import info.nightscout.androidaps.queue.commands.CommandExtendedBolus; import info.nightscout.androidaps.queue.commands.CommandLoadEvents; import info.nightscout.androidaps.queue.commands.CommandLoadHistory; import info.nightscout.androidaps.queue.commands.CommandReadStatus; +import info.nightscout.androidaps.queue.commands.CommandSMBBolus; import info.nightscout.androidaps.queue.commands.CommandSetProfile; import info.nightscout.androidaps.queue.commands.CommandTempBasalAbsolute; import info.nightscout.androidaps.queue.commands.CommandTempBasalPercent; @@ -65,7 +66,6 @@ import info.nightscout.androidaps.queue.commands.CommandTempBasalPercent; * connect() is called again * * when queue is empty, disconnect is called - * */ public class CommandQueue { @@ -141,17 +141,22 @@ public class CommandQueue { // returns true if command is queued public boolean bolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { - if (isRunning(Command.CommandType.BOLUS)) { + Command.CommandType type = detailedBolusInfo.isSMB ? Command.CommandType.SMB_BOLUS : Command.CommandType.BOLUS; + + if (isRunning(type)) { if (callback != null) callback.result(executingNowError()).run(); return false; } // remove all unfinished boluses - removeAll(Command.CommandType.BOLUS); + removeAll(type); // add new command to queue - add(new CommandBolus(detailedBolusInfo, callback)); + if (detailedBolusInfo.isSMB) + add(new CommandSMBBolus(detailedBolusInfo, callback)); + else + add(new CommandBolus(detailedBolusInfo, callback)); notifyAboutNewCommand(); @@ -162,7 +167,7 @@ public class CommandQueue { detailedBolusInfo.insulin = MainApp.getConfigBuilder().applyBolusConstraints(detailedBolusInfo.insulin); detailedBolusInfo.carbs = MainApp.getConfigBuilder().applyCarbsConstraints((int) detailedBolusInfo.carbs); - BolusProgressDialog bolusProgressDialog = null; + BolusProgressDialog bolusProgressDialog; if (detailedBolusInfo.context != null) { bolusProgressDialog = new BolusProgressDialog(); bolusProgressDialog.setInsulin(detailedBolusInfo.insulin); @@ -228,7 +233,7 @@ public class CommandQueue { return false; } - Double rateAfterConstraints = MainApp.getConfigBuilder().applyBolusConstraints(insulin); + Double rateAfterConstraints = MainApp.getConfigBuilder().applyBolusConstraints(insulin); // remove all unfinished removeAll(Command.CommandType.EXTENDEDBOLUS); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java index 4624e2385f..9c5a8cf6ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java @@ -11,6 +11,7 @@ import info.nightscout.androidaps.queue.Callback; public abstract class Command { public enum CommandType { BOLUS, + SMB_BOLUS, TEMPBASAL, EXTENDEDBOLUS, BASALPROFILE, diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java new file mode 100644 index 0000000000..4d3fc92883 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.queue.commands; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; +import info.nightscout.androidaps.plugins.Overview.events.EventDismissBolusprogressIfRunning; +import info.nightscout.androidaps.queue.Callback; +import info.nightscout.utils.DecimalFormatter; + +/** + * Created by mike on 09.11.2017. + */ + +public class CommandSMBBolus extends Command { + DetailedBolusInfo detailedBolusInfo; + + public CommandSMBBolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { + commandType = CommandType.SMB_BOLUS; + this.detailedBolusInfo = detailedBolusInfo; + this.callback = callback; + } + + @Override + public void execute() { + PumpEnactResult r = ConfigBuilderPlugin.getActivePump().deliverTreatment(detailedBolusInfo); + + BolusProgressDialog.bolusEnded = true; + MainApp.bus().post(new EventDismissBolusprogressIfRunning(r)); + + if (callback != null) + callback.result(r).run(); + } + + public String status() { + return "SMBBOLUS " + DecimalFormatter.to1Decimal(detailedBolusInfo.insulin) + "U"; + } +} From ef16174e5b09890775c3b7df779373c16c8010a4 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 4 Dec 2017 23:31:35 +0100 Subject: [PATCH 030/171] fix NPE --- .../androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 ff42d0d79a..d449b30a29 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 @@ -78,12 +78,14 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { @Override public boolean isEnabled(int type) { - return type == APS && fragmentEnabled && ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable; + boolean pumpCapable = ConfigBuilderPlugin.getActivePump() == null || ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable; + return type == APS && fragmentEnabled && pumpCapable; } @Override public boolean isVisibleInTabs(int type) { - return type == APS && fragmentVisible && ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable; + boolean pumpCapable = ConfigBuilderPlugin.getActivePump() == null || ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable; + return type == APS && fragmentVisible && pumpCapable; } @Override From 1986594877cd56d7292bc950e248557a1df79e2b Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 4 Dec 2017 23:34:55 +0100 Subject: [PATCH 031/171] hide deviation slope graph --- app/src/main/java/info/nightscout/androidaps/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index 874f75d2bf..3365f8a28e 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -23,7 +23,7 @@ public class Config { public static final boolean SMSCOMMUNICATORENABLED = !BuildConfig.NSCLIENTOLNY && !BuildConfig.G5UPLOADER; - public static final boolean displayDeviationSlope = true; + public static final boolean displayDeviationSlope = false; public static final boolean detailedLog = true; public static final boolean logFunctionCalls = true; From d21a2bd69141edc5e7a57ba7e9a718f4acce6193 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sat, 18 Nov 2017 18:44:59 +0100 Subject: [PATCH 032/171] Overview: fix predictions checkbox disappearing after unchecking it. --- .../androidaps/plugins/Overview/OverviewFragment.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 0e3fa03c29..e2a8c1d1bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1170,8 +1170,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, cobView.setText(cobText); } - boolean showPrediction = showPredictionView.isChecked() && finalLastRun != null && finalLastRun.request.hasPredictions; - if (showPrediction) { + boolean predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + if (predictionsAvailable) { showPredictionView.setVisibility(View.VISIBLE); getActivity().findViewById(R.id.overview_showprediction_label).setVisibility(View.VISIBLE); } else { @@ -1214,7 +1214,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, // ****** GRAPH ******* - // allign to hours + // align to hours Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.MILLISECOND, 0); @@ -1226,7 +1226,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, long toTime; long fromTime; long endTime; - if (showPrediction) { + if (predictionsAvailable && showPredictionView.isChecked()) { int predHours = (int) (Math.ceil(finalLastRun.constraintsProcessed.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); predHours = Math.min(2, predHours); predHours = Math.max(0, predHours); @@ -1295,7 +1295,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, graphData.addInRangeArea(bgGraph, fromTime, endTime, lowLine, highLine); // **** BG **** - if (showPrediction) + if (predictionsAvailable && showPredictionView.isChecked()) graphData.addBgReadings(bgGraph, fromTime, toTime, lowLine, highLine, finalLastRun.constraintsProcessed); else graphData.addBgReadings(bgGraph, fromTime, toTime, lowLine, highLine, null); From 8822dcecd3906650d24fb605c0ec7ecb4ed0c1e4 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 10 Dec 2017 15:48:28 +0100 Subject: [PATCH 033/171] update smb execution in configbuilder --- .../ConfigBuilder/ConfigBuilderPlugin.java | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 748fab2e9d..1d53c0b79f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -356,9 +356,8 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr * expect absolute request and allow both absolute and percent response based on pump capabilities * * @param request - * @return - * true if command is going to be executed - * false if error + * @return true if command is going to be executed + * false if error */ public boolean applyAPSRequest(APSResult request, Callback callback) { @@ -381,20 +380,6 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr return false; } - if (request.smb != 0) { - long lastSMBTime = getLastBolusTime(); - if (lastSMBTime != 0 && lastSMBTime + 4.5 * 60 * 1000 > System.currentTimeMillis()) { - log.debug("SMB requsted but still in 5 min interval"); - } else { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = request.smb; - detailedBolusInfo.isSMB = true; - detailedBolusInfo.source = Source.USER; - getCommandQueue().bolus(detailedBolusInfo, callback); - } - } - if (Config.logCongigBuilderActions) log.debug("applyAPSRequest: " + request.toString()); if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) { @@ -402,14 +387,16 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr if (Config.logCongigBuilderActions) log.debug("applyAPSRequest: cancelTempBasal()"); getCommandQueue().cancelTempBasal(false, callback); - return true; + if (request.smb == 0) + return true; } else { if (Config.logCongigBuilderActions) log.debug("applyAPSRequest: Basal set correctly"); if (callback != null) { callback.result(new PumpEnactResult().absolute(request.rate).duration(0).enacted(false).success(true).comment("Basal set correctly")).run(); } - return false; + if (request.smb == 0) + return false; } } else if (isTempBasalInProgress() && getTempBasalRemainingMinutesFromHistory() > 5 @@ -419,13 +406,34 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr if (callback != null) { callback.result(new PumpEnactResult().absolute(getTempBasalAbsoluteRateHistory()).duration(getTempBasalFromHistory(System.currentTimeMillis()).getPlannedRemainingMinutes()).enacted(false).success(true).comment("Temp basal set correctly")).run(); } - return false; + if (request.smb == 0) + return false; } else { if (Config.logCongigBuilderActions) log.debug("applyAPSRequest: setTempBasalAbsolute()"); getCommandQueue().tempBasalAbsolute(request.rate, request.duration, false, callback); - return true; + + if (request.smb == 0) return true; } + log.debug("SMB requested in config is: " + request.smb); + if (request.smb != 0) { + long lastBolusTime = getLastBolusTime(); + if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { + log.debug("SMB requsted but still in 3 min interval"); + } else { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; + detailedBolusInfo.insulin = request.smb; + detailedBolusInfo.isSMB = true; + detailedBolusInfo.source = Source.USER; + boolean smbDelivered = getCommandQueue().bolus(detailedBolusInfo, callback); + if (smbDelivered) + return true; + else + return false; + } + } + return true; } /* From 20414c81c9046f7af0ba4207a9ca87064ca3ac7b Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 10 Dec 2017 16:14:10 +0100 Subject: [PATCH 034/171] update minDeviationSlope default val --- .../androidaps/plugins/IobCobCalculator/AutosensData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 33cb5271a7..4e5400cbc0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -61,7 +61,7 @@ public class AutosensData { public double avgDeviation = 0d; public double autosensRatio = 1d; - public double minDeviationSlope; // renamed to slopeFromMaxDeviation in 0.6.0 + public double minDeviationSlope = 999; public String log(long time) { return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " minDeviationSlope=" + minDeviationSlope; From 890ef5978b7f51dc8458f6e33c7ae2474ffd37b5 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 10 Dec 2017 16:25:56 +0100 Subject: [PATCH 035/171] safety check for multiple SMBs --- .../nightscout/androidaps/plugins/Loop/LoopPlugin.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index faf4fde613..b1fb6dd893 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -33,6 +33,7 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventLoopSetLastRunGui; import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -297,6 +298,13 @@ public class LoopPlugin implements PluginBase { resultAfterConstraints.rate = constraintsInterface.applyBasalConstraints(resultAfterConstraints.rate); resultAfterConstraints.smb = constraintsInterface.applyBolusConstraints(resultAfterConstraints.smb); + // safety check for multiple SMBs + long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(); + if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { + log.debug("SMB requsted but still in 3 min interval"); + resultAfterConstraints.smb = 0; + } + if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; lastRun.constraintsProcessed = resultAfterConstraints; From c714fead249311a0e4b2379f7f50ce8f3d7a289d Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 10 Dec 2017 17:04:22 +0100 Subject: [PATCH 036/171] add deliverAt safety --- .../androidaps/data/DetailedBolusInfo.java | 4 +++- .../plugins/ConfigBuilder/ConfigBuilderPlugin.java | 1 + .../androidaps/queue/commands/CommandSMBBolus.java | 13 ++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java index 7720b03d3e..7d1dcac9ad 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java +++ b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java @@ -29,6 +29,7 @@ public class DetailedBolusInfo { public Context context = null; // context for progress dialog public long pumpId = 0; // id of record if comming from pump history (not a newly created treatment) public boolean isSMB = false; // is a Super-MicroBolus + public long deliverAt = 0; // SMB should be delivered within 1 min from this time @Override public String toString() { @@ -37,6 +38,7 @@ public class DetailedBolusInfo { " carbs: " + carbs + " isValid: " + isValid + " carbTime: " + carbTime + - " isSMB: " + isSMB; + " isSMB: " + isSMB + + " deliverAt: " + new Date(deliverAt).toLocaleString(); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 1d53c0b79f..3b72712b3b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -426,6 +426,7 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr detailedBolusInfo.insulin = request.smb; detailedBolusInfo.isSMB = true; detailedBolusInfo.source = Source.USER; + detailedBolusInfo.deliverAt = request.deliverAt; boolean smbDelivered = getCommandQueue().bolus(detailedBolusInfo, callback); if (smbDelivered) return true; diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java index 4d3fc92883..d8fae1df32 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.queue.commands; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.PumpEnactResult; @@ -14,6 +17,7 @@ import info.nightscout.utils.DecimalFormatter; */ public class CommandSMBBolus extends Command { + private static Logger log = LoggerFactory.getLogger(CommandSMBBolus.class); DetailedBolusInfo detailedBolusInfo; public CommandSMBBolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { @@ -24,7 +28,14 @@ public class CommandSMBBolus extends Command { @Override public void execute() { - PumpEnactResult r = ConfigBuilderPlugin.getActivePump().deliverTreatment(detailedBolusInfo); + PumpEnactResult r; + if (detailedBolusInfo.deliverAt != 0 && detailedBolusInfo.deliverAt + 60 * 1000L > System.currentTimeMillis()) + r = ConfigBuilderPlugin.getActivePump().deliverTreatment(detailedBolusInfo); + else { + r = new PumpEnactResult().enacted(false).success(false).comment("SMB request too old"); + log.debug("SMB bolus canceled. delivetAt=" + detailedBolusInfo.deliverAt + " now=" + System.currentTimeMillis()); + } + BolusProgressDialog.bolusEnded = true; MainApp.bus().post(new EventDismissBolusprogressIfRunning(r)); From b1c4f28eb749e2e6502fcd78c2ddb7bf959426eb Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 11 Dec 2017 18:45:04 +0100 Subject: [PATCH 037/171] OpenAPS 0.6.0 1st part --- .../main/assets/OpenAPSSMB/determine-basal.js | 864 +++++++++++------- .../info/nightscout/androidaps/Config.java | 2 +- .../androidaps/data/GlucoseStatus.java | 2 + .../nightscout/androidaps/data/IobTotal.java | 43 + .../nightscout/androidaps/data/MealData.java | 4 +- .../nightscout/androidaps/db/BgReading.java | 5 +- .../interfaces/InsulinInterface.java | 12 +- .../IobCobCalculator/AutosensData.java | 5 +- .../IobCobCalculatorPlugin.java | 54 +- .../androidaps/plugins/Loop/APSResult.java | 14 + .../DetermineBasalAdapterAMAJS.java | 6 +- .../plugins/OpenAPSAMA/OpenAPSAMAPlugin.java | 3 +- .../DetermineBasalAdapterSMBJS.java | 54 +- .../OpenAPSSMB/OpenAPSSMBFragment.java | 4 +- .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 8 +- .../plugins/OpenAPSSMB/SMBDefaults.java | 64 ++ .../plugins/Overview/graphData/GraphData.java | 52 +- .../plugins/Treatments/TreatmentsPlugin.java | 16 +- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/pref_advanced.xml | 17 - app/src/main/res/xml/pref_openapsama.xml | 5 + app/src/main/res/xml/pref_openapssmb.xml | 49 + 23 files changed, 891 insertions(+), 396 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java create mode 100644 app/src/main/res/xml/pref_openapssmb.xml diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index cb6dcd71fb..19ebbb4e70 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -26,12 +26,12 @@ function round(value, digits) // 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; +// fall to get eventualBG to target over 2 hours +function calculate_expected_delta(target_bg, eventual_bg, bgi) { + // (hours * mins_per_hour) / 5 = how many 5 minute periods in 2h = 24 + var five_min_blocks = (2 * 60) / 5; var target_delta = target_bg - eventual_bg; - var expectedDelta = round(bgi + (target_delta / dia_in_5min_blocks), 1); + var expectedDelta = round(bgi + (target_delta / five_min_blocks), 1); return expectedDelta; } @@ -59,26 +59,37 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } var profile_current_basal = round_basal(profile.current_basal, profile); 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("Autosens adjusting basal from "+profile_current_basal+" to "+basal+"; "); - } else { - console.error("Basal unchanged: "+basal+"; "); - } - } + + var systemTime = new Date(); + var bgTime = new Date(glucose_status.date); + var minAgo = round( (systemTime - bgTime) / 60 / 1000 ,1); var bg = glucose_status.glucose; if (bg < 39) { //Dexcom is in ??? mode or calibrating 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. "; + } + if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future + rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; + } + if (bg < 39 || minAgo > 12 || minAgo < -5) { + if (currenttemp.rate >= basal) { // high temp is running + rT.reason += ". Canceling high temp basal of "+currenttemp.rate; rT.deliverAt = deliverAt; rT.temp = 'absolute'; - return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + rT.duration = 0; + rT.rate = 0; + return rT; + //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + } else if ( currenttemp.rate == 0 && currenttemp.duration > 30 ) { //shorten long zero temps to 30m + rT.reason += ". Shortening " + currenttemp.duration + "m long zero temp to 30m. "; + rT.deliverAt = deliverAt; + rT.temp = 'absolute'; + rT.duration = 30; + rT.rate = 0; + return rT; + //return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); } else { //do nothing. - rT.reason += ", temp " + currenttemp.rate + " <~ current basal " + basal + "U/hr. "; + rT.reason += ". Temp " + currenttemp.rate + " <= current basal " + basal + "U/hr; doing nothing. "; return rT; } } @@ -102,24 +113,58 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ return rT; } - // 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 { - // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range - min_bg = round((min_bg - 60) / autosens_data.ratio) + 60; - max_bg = round((max_bg - 60) / autosens_data.ratio) + 60; - new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60; - // don't allow target_bg below 80 - new_target_bg = Math.max(80, new_target_bg); - if (target_bg == new_target_bg) { - console.error("target_bg unchanged: "+new_target_bg+"; "); + var sensitivityRatio; + var high_temptarget_raises_sensitivity = profile.exercise_mode || profile.high_temptarget_raises_sensitivity; + var normalTarget = 100; // evaluate high/low temptarget against 100, not scheduled basal (which might change) + if ( profile.half_basal_exercise_target ) { + var halfBasalTarget = profile.half_basal_exercise_target; + } else { + var halfBasalTarget = 160; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%) + // 80 mg/dL with low_temptarget_lowers_sensitivity would give 1.5x basal, but is limited to autosens_max (1.2x by default) + } + if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget + 10 + || profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) { + // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 + // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 + //sensitivityRatio = 2/(2+(target_bg-normalTarget)/40); + var c = halfBasalTarget - normalTarget; + sensitivityRatio = c/(c+target_bg-normalTarget); + // limit sensitivityRatio to profile.autosens_max (1.2x by default) + sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max); + sensitivityRatio = round(sensitivityRatio,2); + console.error("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; "); + } else if (typeof autosens_data !== 'undefined' ) { + sensitivityRatio = autosens_data.ratio; + console.error("Autosens ratio: "+sensitivityRatio+"; "); + } + if (sensitivityRatio) { + basal = profile.current_basal * sensitivityRatio; + basal = round_basal(basal, profile); + if (basal != profile_current_basal) { + console.error("Adjusting basal from "+profile_current_basal+" to "+basal+"; "); } else { - console.error("target_bg from "+target_bg+" to "+new_target_bg+"; "); + console.error("Basal unchanged: "+basal+"; "); + } + } + + // adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120 + if (profile.temptargetSet) { + //console.error("Temp Target set, not adjusting with autosens; "); + } else if (typeof autosens_data !== 'undefined' ) { + if ( profile.sensitivity_raises_target && autosens_data.ratio < 1 || profile.resistance_lowers_target && autosens_data.ratio > 1 ) { + // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range + min_bg = round((min_bg - 60) / autosens_data.ratio) + 60; + max_bg = round((max_bg - 60) / autosens_data.ratio) + 60; + new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60; + // don't allow target_bg below 80 + new_target_bg = Math.max(80, new_target_bg); + if (target_bg == new_target_bg) { + console.error("target_bg unchanged: "+new_target_bg+"; "); + } else { + console.error("target_bg from "+target_bg+" to "+new_target_bg+"; "); + } + target_bg = new_target_bg; } - target_bg = new_target_bg; - } } if (typeof iob_data === 'undefined' ) { @@ -148,20 +193,56 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta); var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta); var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta); + var maxDelta = Math.max(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta); var profile_sens = round(profile.sens,1) var sens = profile.sens; if (typeof autosens_data !== 'undefined' ) { - sens = profile.sens / autosens_data.ratio; + sens = profile.sens / sensitivityRatio; sens = round(sens, 1); if (sens != profile_sens) { - console.error("sens from "+profile_sens+" to "+sens); + console.error("ISF from "+profile_sens+" to "+sens); } else { - console.error("sens unchanged: "+sens); + console.error("ISF unchanged: "+sens); } - console.error(" (autosens ratio "+autosens_data.ratio+")"); + //console.error(" (autosens ratio "+sensitivityRatio+")"); + } + console.error("; CR:",profile.carb_ratio); + + // compare currenttemp to iob_data.lastTemp and cancel temp if they don't match + var lastTempAge; + if (typeof iob_data.lastTemp !== 'undefined' ) { + lastTempAge = round(( new Date().getTime() - iob_data.lastTemp.date ) / 60000); // in minutes + } + //console.error("currenttemp:",currenttemp,"lastTemp:",JSON.stringify(iob_data.lastTemp),"lastTempAge:",lastTempAge,"m"); + tempModulus = (lastTempAge + currenttemp.duration) % 30; + console.error("currenttemp:",currenttemp,"lastTempAge:",lastTempAge,"m","tempModulus:",tempModulus,"m"); + rT.temp = 'absolute'; + rT.deliverAt = deliverAt; + if ( microBolusAllowed && currenttemp && iob_data.lastTemp && currenttemp.rate != iob_data.lastTemp.rate ) { + rT.reason = "Warning: currenttemp rate "+currenttemp.rate+" != lastTemp rate "+iob_data.lastTemp.rate+" from pumphistory; setting neutral temp of "+basal+"."; + return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + } + if ( currenttemp && iob_data.lastTemp && currenttemp.duration > 0 ) { + // TODO: fix this (lastTemp.duration is how long it has run; currenttemp.duration is time left + //if ( currenttemp.duration < iob_data.lastTemp.duration - 2) { + //rT.reason = "Warning: currenttemp duration "+currenttemp.duration+" << lastTemp duration "+round(iob_data.lastTemp.duration,1)+" from pumphistory; setting neutral temp of "+basal+"."; + //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + //} + //console.error(lastTempAge, round(iob_data.lastTemp.duration,1), round(lastTempAge - iob_data.lastTemp.duration,1)); + var lastTempEnded = lastTempAge - iob_data.lastTemp.duration + if ( lastTempEnded > 5 ) { + rT.reason = "Warning: currenttemp running but lastTemp from pumphistory ended "+lastTempEnded+"m ago; setting neutral temp of "+basal+"."; + //console.error(currenttemp, round(iob_data.lastTemp,1), round(lastTempAge,1)); + return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + } + // TODO: figure out a way to do this check that doesn't fail across basal schedule boundaries + //if ( tempModulus < 25 && tempModulus > 5 ) { + //rT.reason = "Warning: currenttemp duration "+currenttemp.duration+" + lastTempAge "+lastTempAge+" isn't a multiple of 30m; setting neutral temp of "+basal+"."; + //console.error(rT.reason); + //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + //} } - console.error(""); //calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone var bgi = round(( -iob_data.activity * sens * 5 ), 2); @@ -170,6 +251,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // don't overreact to a big negative delta: use minAvgDelta if deviation is negative if (deviation < 0) { deviation = round( (30 / 5) * ( minAvgDelta - bgi ) ); + // and if deviation is still negative, use long_avgdelta + if (deviation < 0) { + deviation = round( (30 / 5) * ( glucose_status.long_avgdelta - bgi ) ); + } } // calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity @@ -181,14 +266,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // and adjust it for the deviation above var eventualBG = naive_eventualBG + deviation; // calculate what portion of that is due to bolussnooze - var bolusContrib = iob_data.bolussnooze * sens; + //var bolusContrib = iob_data.bolussnooze * sens; // and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime - var naive_snoozeBG = round( naive_eventualBG + 1.5 * bolusContrib ); + //var naive_snoozeBG = round( naive_eventualBG + 1.5 * bolusContrib ); // adjust that for deviation like we did eventualBG - var snoozeBG = naive_snoozeBG + deviation; + //var snoozeBG = naive_snoozeBG + deviation; // adjust target BG range if needed to safely bring down high BG faster without causing lows - if ( bg > max_bg && profile.adv_target_adjustments ) { + if ( bg > max_bg && profile.adv_target_adjustments && ! profile.temptargetSet ) { // with target=100, as BG rises from 100 to 160, adjustedTarget drops from 100 to 80 var adjustedMinBG = round(Math.max(80, min_bg - (bg - min_bg)/3 ),0); var adjustedTargetBG =round( Math.max(80, target_bg - (bg - target_bg)/3 ),0); @@ -217,7 +302,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } - var expectedDelta = calculate_expected_delta(profile.dia, target_bg, eventualBG, bgi); + var expectedDelta = calculate_expected_delta(target_bg, eventualBG, bgi); if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) { rT.error ='Error: could not calculate eventualBG. '; return rT; @@ -227,58 +312,99 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var threshold = min_bg - 0.5*(min_bg-40); //console.error(reservoir_data); - var deliverAt = new Date(); rT = { 'temp': 'absolute' , 'bg': bg , 'tick': tick , 'eventualBG': eventualBG - , 'snoozeBG': snoozeBG + //, 'snoozeBG': snoozeBG , 'insulinReq': 0 - , 'reservoir' : reservoir_data // The expected reservoir volume at which to deliver the microbolus (the reservoir volume from immediately before the last pumphistory run) + , 'reservoir' : reservoir_data // The expected reservoir volume at which to deliver the microbolus (the reservoir volume from right before the last pumphistory run) , 'deliverAt' : deliverAt // The time at which the microbolus should be delivered - , 'minPredBG' : 999 + , 'sensitivityRatio' : sensitivityRatio // autosens ratio (fraction of normal basal) }; - var basaliob = iob_data.basaliob; - //if (iob_data.basaliob) { basaliob = iob_data.basaliob; } - //else { basaliob = iob_data.iob - iob_data.bolussnooze; } - var bolusiob = iob_data.iob - basaliob; - // generate predicted future BGs based on IOB, COB, and current absorption rate var COBpredBGs = []; var aCOBpredBGs = []; var IOBpredBGs = []; var UAMpredBGs = []; + var ZTpredBGs = []; COBpredBGs.push(bg); aCOBpredBGs.push(bg); IOBpredBGs.push(bg); + ZTpredBGs.push(bg); UAMpredBGs.push(bg); // enable SMB whenever we have COB or UAM is enabled - // SMB is diabled by default, unless explicitly enabled in preferences.json + // SMB is disabled by default, unless explicitly enabled in preferences.json var enableSMB=false; // disable SMB when a high temptarget is set - if (profile.temptargetSet && target_bg > 100) { + if (! microBolusAllowed) { + console.error("SMB disabled (!microBolusAllowed)") + } else if (! profile.allowSMB_with_high_temptarget && profile.temptargetSet && target_bg > 100) { + console.error("SMB disabled due to high temptarget of",target_bg); enableSMB=false; - // enable SMB/UAM (if enabled in preferences) for DIA hours after bolus - } else if (profile.enableSMB_with_bolus && bolusiob > 0.1) { - enableSMB=true; // enable SMB/UAM (if enabled in preferences) while we have COB - } else if (profile.enableSMB_with_COB && meal_data.mealCOB) { - enableSMB=true; - // enable SMB/UAM (if enabled in preferences) if a low temptarget is set - } else if (profile.enableSMB_with_temptarget && (profile.temptargetSet && target_bg < 100)) { - enableSMB=true; + } else if (profile.enableSMB_with_COB === true && meal_data.mealCOB) { + if (meal_data.bwCarbs) { + if (profile.A52_risk_enable) { + console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") + enableSMB=true; + } else { + console.error("SMB not enabled for Bolus Wizard COB"); + } + } else { + console.error("SMB enabled for COB of",meal_data.mealCOB); + enableSMB=true; + } // enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry // (6 hours is defined in carbWindow in lib/meal/total.js) - } else if (profile.enableSMB_after_carbs && meal_data.carbs) { - enableSMB=true; + } else if (profile.enableSMB_after_carbs === true && meal_data.carbs ) { + if (meal_data.bwCarbs) { + if (profile.A52_risk_enable) { + console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") + enableSMB=true; + } else { + console.error("SMB not enabled for Bolus Wizard carbs"); + } + } else { + console.error("SMB enabled for 6h after carb entry"); + enableSMB=true; + } + // enable SMB/UAM (if enabled in preferences) if a low temptarget is set + } else if (profile.enableSMB_with_temptarget === true && (profile.temptargetSet && target_bg < 100)) { + if (meal_data.bwFound) { + if (profile.A52_risk_enable) { + console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard") + enableSMB=true; + } else { + console.error("enableSMB_with_temptarget not supported within 6h of using Bolus Wizard"); + } + } else { + console.error("SMB enabled for temptarget of",convert_bg(target_bg, profile)); + enableSMB=true; + } + // enable SMB/UAM if always-on (unless previously disabled for high temptarget) + } else if (profile.enableSMB_always === true) { + if (meal_data.bwFound) { + if (profile.A52_risk_enable === true) { + console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard") + enableSMB=true; + } else { + console.error("enableSMB_always not supported within 6h of using Bolus Wizard"); + } + } else { + console.error("SMB enabled due to enableSMB_always"); + enableSMB=true; + } + } else { + console.error("SMB disabled (no enableSMB preferences active)"); } - // enable UAM (if enabled in preferences) for DIA hours after bolus, or if SMB is enabled - var enableUAM=(profile.enableUAM && (bolusiob > 0.1 || enableSMB)); + // enable UAM (if enabled in preferences) if SMB is enabled + var enableUAM=(profile.enableUAM && enableSMB); //console.error(meal_data); @@ -288,20 +414,54 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // calculate current carb absorption rate, and how long to absorb all carbs // CI = current carb impact on BG in mg/dL/5m ci = round((minDelta - bgi),1); - uci = round((minAvgDelta - bgi),1); + uci = round((minDelta - bgi),1); // ISF (mg/dL/U) / CR (g/U) = CSF (mg/dL/g) - var csf = sens / profile.carb_ratio + if (profile.temptargetSet) { + // if temptargetSet, use unadjusted profile.sens to allow activity mode sensitivityRatio to adjust CR + var csf = profile.sens / profile.carb_ratio; + } else { + // otherwise, use autosens-adjusted sens to counteract autosens meal insulin dosing adjustments + // so that autotuned CR is still in effect even when basals and ISF are being adjusted by autosens + var csf = sens / profile.carb_ratio; + } + var maxCarbAbsorptionRate = 30; // g/h; maximum rate to assume carbs will absorb if no CI observed + // limit Carb Impact to maxCarbAbsorptionRate * csf in mg/dL per 5m + maxCI = round(maxCarbAbsorptionRate*csf*5/60,1) + if (ci > maxCI) { + console.error("Limiting carb impact from",ci,"to",maxCI,"mg/dL/5m (",maxCarbAbsorptionRate,"g/h )"); + ci = maxCI; + } // set meal_carbimpact high enough to absorb all meal carbs over 6 hours // total_impact (mg/dL) = CSF (mg/dL/g) * carbs (g) //console.error(csf * meal_data.carbs); // meal_carbimpact (mg/dL/5m) = CSF (mg/dL/g) * carbs (g) / 6 (h) * (1h/60m) * 5 (m/5m) * 2 (for linear decay) //var meal_carbimpact = round((csf * meal_data.carbs / 6 / 60 * 5 * 2),1) - // calculate the number of carbs absorbed over 4h at current CI + var remainingCATimeMin = 3; // h; before carb absorption starts + // adjust remainingCATime (instead of CR) for autosens + remainingCATimeMin = remainingCATimeMin / sensitivityRatio; + // 20 g/h means that anything <= 60g will get a remainingCATimeMin, 80g will get 4h, and 120g 6h + // when actual absorption ramps up it will take over from remainingCATime + var assumedCarbAbsorptionRate = 20; // g/h; maximum rate to assume carbs will absorb if no CI observed + var remainingCATime; + if (meal_data.carbs) { + // if carbs * assumedCarbAbsorptionRate > remainingCATimeMin, raise it + // so <= 90g is assumed to take 3h, and 120g=4h + remainingCATimeMin = Math.max(remainingCATimeMin, meal_data.mealCOB/assumedCarbAbsorptionRate); + var lastCarbAge = round(( new Date().getTime() - meal_data.lastCarbTime ) / 60000); + //console.error(meal_data.lastCarbTime, lastCarbAge); + + fractionCOBAbsorbed = ( meal_data.carbs - meal_data.mealCOB ) / meal_data.carbs; + remainingCATime = remainingCATimeMin + 1.5 * lastCarbAge/60; + remainingCATime = round(remainingCATime,1); + //console.error(fractionCOBAbsorbed, remainingCATimeAdjustment, remainingCATime) + console.error("Last carbs",lastCarbAge,"minutes ago; remainingCATime:",remainingCATime,"hours;",round(fractionCOBAbsorbed*100)+"% carbs absorbed"); + } + + // calculate the number of carbs absorbed over remainingCATime hours at current CI // CI (mg/dL/5m) * (5m)/5 (m) * 60 (min/hr) * 4 (h) / 2 (linear decay factor) = total carb impact (mg/dL) - var totalCI = Math.max(0, ci / 5 * 60 * 4 / 2); + var totalCI = Math.max(0, ci / 5 * 60 * remainingCATime / 2); // totalCI (mg/dL) / CSF (mg/dL/g) = total carbs absorbed (g) var totalCA = totalCI / csf; - // exclude the last 1/3 of carbs from remainingCarbs, and then cap it at 90 var remainingCarbsCap = 90; // default to 90 var remainingCarbsFraction = 1; if (profile.remainingCarbsCap) { remainingCarbsCap = Math.min(90,profile.remainingCarbsCap); } @@ -309,27 +469,44 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var remainingCarbsIgnore = 1 - remainingCarbsFraction; var remainingCarbs = Math.max(0, meal_data.mealCOB - totalCA - meal_data.carbs*remainingCarbsIgnore); remainingCarbs = Math.min(remainingCarbsCap,remainingCarbs); - // assume remainingCarbs will absorb over 4h - // remainingCI (mg/dL/5m) = remainingCarbs (g) * CSF (mg/dL/g) * 5 (m/5m) * 1h/60m / 4 (h) - var remainingCI = remainingCarbs * csf * 5 / 60 / 4; - //console.error(profile.min_5m_carbimpact,ci,totalCI,totalCA,remainingCarbs,remainingCI); + // assume remainingCarbs will absorb in a /\ shaped bilinear curve + // peaking at remainingCATime / 2 and ending at remainingCATime hours + // area of the /\ triangle is the same as a remainingCIpeak-height rectangle out to remainingCATime/2 + // remainingCIpeak (mg/dL/5m) = remainingCarbs (g) * CSF (mg/dL/g) * 5 (m/5m) * 1h/60m / (remainingCATime/2) (h) + var remainingCIpeak = remainingCarbs * csf * 5 / 60 / (remainingCATime/2); + //console.error(profile.min_5m_carbimpact,ci,totalCI,totalCA,remainingCarbs,remainingCI,remainingCATime); //if (meal_data.mealCOB * 3 > meal_data.carbs) { } // calculate peak deviation in last hour, and slope from that to current deviation - var minDeviationSlope = round(meal_data.minDeviationSlope,2); - //console.error(minDeviationSlope); + var slopeFromMaxDeviation = round(meal_data.slopeFromMaxDeviation,2); + // calculate lowest deviation in last hour, and slope from that to current deviation + var slopeFromMinDeviation = round(meal_data.slopeFromMinDeviation,2); + // assume deviations will drop back down at least at 1/3 the rate they ramped up + var slopeFromDeviations = Math.min(slopeFromMaxDeviation,-slopeFromMinDeviation/3); + //console.error(slopeFromMaxDeviation); aci = 10; //5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m) // duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m) - cid = Math.max(0, meal_data.mealCOB * csf / ci ); + // limit cid to remainingCATime hours: the reset goes to remainingCI + if (ci == 0) { + // avoid divide by zero + cid = 0; + } else { + cid = Math.min(remainingCATime*60/5/2,Math.max(0, meal_data.mealCOB * csf / ci )); + } acid = Math.max(0, meal_data.mealCOB * csf / aci ); // duration (hours) = duration (5m) * 5 / 60 * 2 (to account for linear decay) - console.error("Carb Impact:",ci,"mg/dL per 5m; CI Duration:",round(cid*5/60*2,1),"hours; remaining 4h+ CI:",round(remainingCI,1),"mg/dL per 5m"); - console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",round(acid*5/60*2,1),"hours"); + console.error("Carb Impact:",ci,"mg/dL per 5m; CI Duration:",round(cid*5/60*2,1),"hours; remaining CI (~2h peak):",round(remainingCIpeak,1),"mg/dL per 5m"); + //console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",round(acid*5/60*2,1),"hours"); var minIOBPredBG = 999; var minCOBPredBG = 999; var minUAMPredBG = 999; + var minGuardBG = bg; + var minCOBGuardBG = 999; + var minUAMGuardBG = 999; + var minIOBGuardBG = 999; + var minZTGuardBG = 999; var minPredBG; var avgPredBG; var IOBpredBG = eventualBG; @@ -341,51 +518,78 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var lastIOBpredBG; var lastCOBpredBG; var lastUAMpredBG; + var lastZTpredBG; var UAMduration = 0; + var remainingCItotal = 0; + var remainingCIs = []; + var predCIs = []; try { iobArray.forEach(function(iobTick) { //console.error(iobTick); predBGI = round(( -iobTick.activity * sens * 5 ), 2); + predZTBGI = round(( -iobTick.iobWithZeroTemp.activity * sens * 5 ), 2); // for IOBpredBGs, 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; + // calculate predBGs with long zero temp without deviations + ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI; // for COBpredBGs, 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, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); predACI = Math.max(0, Math.max(0,aci) * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) ); - // if any carbs aren't absorbed after 4 hours, assume they'll absorb at a constant rate for next 4h + // if any carbs aren't absorbed after remainingCATime hours, assume they'll absorb in a /\ shaped + // bilinear curve peaking at remainingCIpeak at remainingCATime/2 hours (remainingCATime/2*12 * 5m) + // and ending at remainingCATime h (remainingCATime*12 * 5m intervals) + var intervals = Math.min( COBpredBGs.length, (remainingCATime*12)-COBpredBGs.length ); + var remainingCI = Math.max(0, intervals / (remainingCATime/2*12) * remainingCIpeak ); + remainingCItotal += predCI+remainingCI; + remainingCIs.push(round(remainingCI,0)); + predCIs.push(round(predCI,0)); + //console.error(round(predCI,1)+"+"+round(remainingCI,1)+" "); COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI + remainingCI; - // stop adding remainingCI after 4h - if (COBpredBGs.length > 4 * 60 / 5) { remainingCI = 0; } aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI; - // for UAMpredBGs, predicted carb impact drops at minDeviationSlope - // calculate predicted CI from UAM based on minDeviationSlope - predUCIslope = Math.max(0, uci + ( UAMpredBGs.length*minDeviationSlope ) ); - // if minDeviationSlope is too flat, predicted deviation impact drops linearly from - // current deviation down to zero over DIA (data points every 5m) - predUCIdia = Math.max(0, uci * ( 1 - UAMpredBGs.length/Math.max(profile.dia*60/5,1) ) ); - //console.error(predUCIslope, predUCIdia); + // for UAMpredBGs, predicted carb impact drops at slopeFromDeviations + // calculate predicted CI from UAM based on slopeFromDeviations + predUCIslope = Math.max(0, uci + ( UAMpredBGs.length*slopeFromDeviations ) ); + // if slopeFromDeviations is too flat, predicted deviation impact drops linearly from + // current deviation down to zero over 3h (data points every 5m) + predUCImax = Math.max(0, uci * ( 1 - UAMpredBGs.length/Math.max(3*60/5,1) ) ); + //console.error(predUCIslope, predUCImax); // predicted CI from UAM is the lesser of CI based on deviationSlope or DIA - predUCI = Math.min(predUCIslope, predUCIdia); + predUCI = Math.min(predUCIslope, predUCImax); if(predUCI>0) { - //console.error(UAMpredBGs.length,minDeviationSlope, predUCI); + //console.error(UAMpredBGs.length,slopeFromDeviations, predUCI); UAMduration=round((UAMpredBGs.length+1)*5/60,1); } UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI; //console.error(predBGI, predCI, predUCI); - // truncate all BG predictions at 3.5 hours - if ( IOBpredBGs.length < 42) { IOBpredBGs.push(IOBpredBG); } - if ( COBpredBGs.length < 42) { COBpredBGs.push(COBpredBG); } - if ( aCOBpredBGs.length < 42) { aCOBpredBGs.push(aCOBpredBG); } - if ( UAMpredBGs.length < 42) { UAMpredBGs.push(UAMpredBG); } + // truncate all BG predictions at 4 hours + if ( IOBpredBGs.length < 48) { IOBpredBGs.push(IOBpredBG); } + if ( COBpredBGs.length < 48) { COBpredBGs.push(COBpredBG); } + if ( aCOBpredBGs.length < 48) { aCOBpredBGs.push(aCOBpredBG); } + if ( UAMpredBGs.length < 48) { UAMpredBGs.push(UAMpredBG); } + if ( ZTpredBGs.length < 48) { ZTpredBGs.push(ZTpredBG); } + // calculate minGuardBGs without a wait from COB, UAM, IOB predBGs + if ( COBpredBG < minCOBGuardBG ) { minCOBGuardBG = round(COBpredBG); } + if ( UAMpredBG < minUAMGuardBG ) { minUAMGuardBG = round(UAMpredBG); } + if ( IOBpredBG < minIOBGuardBG ) { minIOBGuardBG = round(IOBpredBG); } + if ( ZTpredBG < minZTGuardBG ) { minZTGuardBG = round(ZTpredBG); } + + // set minPredBGs starting when currently-dosed insulin activity will peak + // look ahead 60m (regardless of insulin type) so as to be less aggressive on slower insulins + var insulinPeakTime = 60; + // add 30m to allow for insluin delivery (SMBs or temps) + insulinPeakTime = 90; + var insulinPeak5m = (insulinPeakTime/60)*12; + //console.error(insulinPeakTime, insulinPeak5m, profile.insulinPeakTime, profile.curve); + // wait 90m before setting minIOBPredBG - if ( IOBpredBGs.length > 18 && (IOBpredBG < minIOBPredBG) ) { minIOBPredBG = round(IOBpredBG); } + if ( IOBpredBGs.length > insulinPeak5m && (IOBpredBG < minIOBPredBG) ) { minIOBPredBG = round(IOBpredBG); } if ( IOBpredBG > maxIOBPredBG ) { maxIOBPredBG = IOBpredBG; } - // wait 60m before setting COB and UAM minPredBGs - if ( (cid || remainingCI > 0) && COBpredBGs.length > 12 && (COBpredBG < minCOBPredBG) ) { minCOBPredBG = round(COBpredBG); } - if ( (cid || remainingCI > 0) && COBpredBG > maxIOBPredBG ) { maxCOBPredBG = COBpredBG; } + // wait 85-105m before setting COB and 60m for UAM minPredBGs + if ( (cid || remainingCIpeak > 0) && COBpredBGs.length > insulinPeak5m && (COBpredBG < minCOBPredBG) ) { minCOBPredBG = round(COBpredBG); } + if ( (cid || remainingCIpeak > 0) && COBpredBG > maxIOBPredBG ) { maxCOBPredBG = COBpredBG; } if ( enableUAM && UAMpredBGs.length > 12 && (UAMpredBG < minUAMPredBG) ) { minUAMPredBG = round(UAMpredBG); } if ( enableUAM && UAMpredBG > maxIOBPredBG ) { maxUAMPredBG = UAMpredBG; } }); @@ -394,6 +598,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } catch (e) { console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled:",e); } + if (meal_data.mealCOB) { + console.error("predCIs (mg/dL/5m):",predCIs.join(" ")); + console.error("remainingCIs: ",remainingCIs.join(" ")); + } + //,"totalCA:",round(totalCA,2),"remainingCItotal/csf+totalCA:",round(remainingCItotal/csf+totalCA,2)); rT.predBGs = {}; IOBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); @@ -404,6 +613,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } rT.predBGs.IOB = IOBpredBGs; lastIOBpredBG=round(IOBpredBGs[IOBpredBGs.length-1]); + ZTpredBGs.forEach(function(p, i, theArray) { + theArray[i] = round(Math.min(401,Math.max(39,p))); + }); + for (var i=ZTpredBGs.length-1; i > 6; i--) { + //if (ZTpredBGs[i-1] != ZTpredBGs[i]) { break; } + // stop displaying ZTpredBGs once they're rising and above target + if (ZTpredBGs[i-1] >= ZTpredBGs[i] || ZTpredBGs[i] < target_bg) { break; } + else { ZTpredBGs.pop(); } + } + rT.predBGs.ZT = ZTpredBGs; + lastZTpredBG=round(ZTpredBGs[ZTpredBGs.length-1]); if (meal_data.mealCOB > 0) { aCOBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); @@ -412,9 +632,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (aCOBpredBGs[i-1] != aCOBpredBGs[i]) { break; } else { aCOBpredBGs.pop(); } } - rT.predBGs.aCOB = aCOBpredBGs; + // disable for now. may want to add a preference to re-enable + //rT.predBGs.aCOB = aCOBpredBGs; } - if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCI > 0 )) { + if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 )) { COBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); @@ -426,7 +647,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ lastCOBpredBG=round(COBpredBGs[COBpredBGs.length-1]); eventualBG = Math.max(eventualBG, round(COBpredBGs[COBpredBGs.length-1]) ); } - if (ci > 0 || remainingCI > 0) { + if (ci > 0 || remainingCIpeak > 0) { if (enableUAM) { UAMpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); @@ -437,7 +658,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } rT.predBGs.UAM = UAMpredBGs; lastUAMpredBG=round(UAMpredBGs[UAMpredBGs.length-1]); - eventualBG = Math.max(eventualBG, round(UAMpredBGs[UAMpredBGs.length-1]) ); + if (UAMpredBGs[UAMpredBGs.length-1]) { + eventualBG = Math.max(eventualBG, round(UAMpredBGs[UAMpredBGs.length-1]) ); + } } // set eventualBG and snoozeBG based on COB or UAM predBGs @@ -446,81 +669,117 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ console.error("UAM Impact:",uci,"mg/dL per 5m; UAM Duration:",UAMduration,"hours"); + minIOBPredBG = Math.max(39,minIOBPredBG); minCOBPredBG = Math.max(39,minCOBPredBG); minUAMPredBG = Math.max(39,minUAMPredBG); minPredBG = round(minIOBPredBG); - // if we have COB and UAM is enabled, average all three - if ( minUAMPredBG < 400 && minCOBPredBG < 400 ) { - avgPredBG = round( (IOBpredBG + UAMpredBG + COBpredBG)/3 ); + var fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs; + // if we have COB and UAM is enabled, average both + if ( minUAMPredBG < 999 && minCOBPredBG < 999 ) { + // weight COBpredBG vs. UAMpredBG based on how many carbs remain as COB + avgPredBG = round( (1-fractionCarbsLeft)*UAMpredBG + fractionCarbsLeft*COBpredBG ); // if UAM is disabled, average IOB and COB - } else if ( minCOBPredBG < 400 ) { + } else if ( minCOBPredBG < 999 ) { avgPredBG = round( (IOBpredBG + COBpredBG)/2 ); - // if carbs are expired, use IOB instead of COB - } else if ( meal_data.carbs && minUAMPredBG < 400 ) { - avgPredBG = round( (2*IOBpredBG + UAMpredBG)/3 ); - // in pure UAM mode, just average IOB and UAM - } else if ( minUAMPredBG < 400 ) { + // if we have UAM but no COB, average IOB and UAM + } else if ( minUAMPredBG < 999 ) { avgPredBG = round( (IOBpredBG + UAMpredBG)/2 ); } else { avgPredBG = round( IOBpredBG ); } + // if avgPredBG is below minZTGuardBG, bring it up to that level + if ( minZTGuardBG > avgPredBG ) { + avgPredBG = minZTGuardBG; + } + // if we have both minCOBGuardBG and minUAMGuardBG, blend according to fractionCarbsLeft + if ( (cid || remainingCIpeak > 0) ) { + if ( enableUAM ) { + minGuardBG = fractionCarbsLeft*minCOBGuardBG + (1-fractionCarbsLeft)*minUAMGuardBG; + } else { + minGuardBG = minCOBGuardBG; + } + } else if ( enableUAM ) { + minGuardBG = minUAMGuardBG; + } else { + minGuardBG = minIOBGuardBG; + } + minGuardBG = round(minGuardBG); + //console.error(minCOBGuardBG, minUAMGuardBG, minIOBGuardBG, minGuardBG); + + var minZTUAMPredBG = minUAMPredBG; + // if minZTGuardBG is below threshold, bring down any super-high minUAMPredBG by averaging + // this helps prevent UAM from giving too much insulin in case absorption falls off suddenly + if ( minZTGuardBG < threshold ) { + minZTUAMPredBG = (minUAMPredBG + minZTGuardBG) / 2; + // if minZTGuardBG is between threshold and target, blend in the averaging + } else if ( minZTGuardBG < target_bg ) { + // target 100, threshold 70, minZTGuardBG 85 gives 50%: (85-70) / (100-70) + var blendPct = (minZTGuardBG-threshold) / (target_bg-threshold); + var blendedMinZTGuardBG = minUAMPredBG*blendPct + minZTGuardBG*(1-blendPct); + minZTUAMPredBG = (minUAMPredBG + blendedMinZTGuardBG) / 2; + //minZTUAMPredBG = minUAMPredBG - target_bg + minZTGuardBG; + // if minUAMPredBG is below minZTGuardBG, bring minUAMPredBG up by averaging + // this allows more insulin if lastUAMPredBG is below target, but minZTGuardBG is still high + } else if ( minZTGuardBG > minUAMPredBG ) { + minZTUAMPredBG = (minUAMPredBG + minZTGuardBG) / 2; + } + minZTUAMPredBG = round(minZTUAMPredBG); + //console.error("minUAMPredBG:",minUAMPredBG,"minZTGuardBG:",minZTGuardBG,"minZTUAMPredBG:",minZTUAMPredBG); // if any carbs have been entered recently if (meal_data.carbs) { // average the minIOBPredBG and minUAMPredBG if available - if ( minUAMPredBG < 400 ) { + /* + if ( minUAMPredBG < 999 ) { avgMinPredBG = round( (minIOBPredBG+minUAMPredBG)/2 ); } else { avgMinPredBG = minIOBPredBG; } + */ // if UAM is disabled, use max of minIOBPredBG, minCOBPredBG - if ( ! enableUAM && minCOBPredBG < 400 ) { + if ( ! enableUAM && minCOBPredBG < 999 ) { minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG)); // if we have COB, use minCOBPredBG, or blendedMinPredBG if it's higher - } else if ( minCOBPredBG < 400 ) { + } else if ( minCOBPredBG < 999 ) { // calculate blendedMinPredBG based on how many carbs remain as COB - fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs; - blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*avgMinPredBG; + //blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minUAMPredBG; + blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minZTUAMPredBG; // if blendedMinPredBG > minCOBPredBG, use that instead minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG, blendedMinPredBG)); - // if carbs have been entered, but have expired, use avg of minIOBPredBG and minUAMPredBG + // if carbs have been entered, but have expired, use minUAMPredBG } else { - minPredBG = avgMinPredBG; + //minPredBG = minUAMPredBG; + minPredBG = minZTUAMPredBG; } // in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG } else if ( enableUAM ) { - minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG)); + //minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG)); + minPredBG = round(Math.max(minIOBPredBG,minZTUAMPredBG)); } // make sure minPredBG isn't higher than avgPredBG minPredBG = Math.min( minPredBG, avgPredBG ); - console.error("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG); - if (minCOBPredBG < 400) { + console.error("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG+" minZTGuardBG: "+minZTGuardBG); + if (minCOBPredBG < 999) { console.error(" minCOBPredBG: "+minCOBPredBG); } - if (minUAMPredBG < 400) { + if (minUAMPredBG < 999) { console.error(" minUAMPredBG: "+minUAMPredBG); } - console.error(" avgPredBG:",avgPredBG,"COB:",meal_data.mealCOB,"carbs:",meal_data.carbs); + console.error(" avgPredBG:",avgPredBG,"COB:",meal_data.mealCOB,"/",meal_data.carbs); // But if the COB line falls off a cliff, don't trust UAM too much: // use maxCOBPredBG if it's been set and lower than minPredBG if ( maxCOBPredBG > bg ) { minPredBG = Math.min(minPredBG, maxCOBPredBG); } - // set snoozeBG to minPredBG if it's higher - if (minPredBG < 400) { - snoozeBG = round(Math.max(snoozeBG,minPredBG)); - } - rT.snoozeBG = snoozeBG; - //console.error(minPredBG, minIOBPredBG, minUAMPredBG, minCOBPredBG, maxCOBPredBG, 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) + ", minPredBG " + convert_bg(minPredBG, profile) + ", IOBpredBG " + convert_bg(lastIOBpredBG, profile); + rT.reason="COB: " + meal_data.mealCOB + ", Dev: " + convert_bg(deviation, profile) + ", BGI: " + convert_bg(bgi, profile) + ", ISF: " + convert_bg(sens, profile) + ", CR: " + round(profile.carb_ratio, 2) + ", Target: " + convert_bg(target_bg, profile) + ", minPredBG " + convert_bg(minPredBG, profile) + ", minGuardBG " + convert_bg(minGuardBG, profile) + ", IOBpredBG " + convert_bg(lastIOBpredBG, profile); if (lastCOBpredBG > 0) { rT.reason += ", COBpredBG " + convert_bg(lastCOBpredBG, profile); } @@ -528,10 +787,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.reason += ", UAMpredBG " + convert_bg(lastUAMpredBG, profile) } rT.reason += "; "; - var bgUndershoot = target_bg - Math.max( naive_eventualBG, eventualBG, lastIOBpredBG ); + //var bgUndershoot = threshold - Math.min(minGuardBG, Math.max( naive_eventualBG, eventualBG )); + // use naive_eventualBG if above 40, but switch to minGuardBG if both eventualBGs hit floor of 39 + //var carbsReqBG = Math.max( naive_eventualBG, eventualBG ); + var carbsReqBG = naive_eventualBG; + if ( carbsReqBG < 40 ) { + carbsReqBG = Math.min( minGuardBG, carbsReqBG ); + } + var bgUndershoot = threshold - carbsReqBG; // calculate how long until COB (or IOB) predBGs drop below min_bg var minutesAboveMinBG = 240; - if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCI > 0 )) { + var minutesAboveThreshold = 240; + if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 )) { for (var i=0; i 0.20 * bg ) { + console.error("maxDelta",convert_bg(maxDelta, profile),"> 20% of BG",convert_bg(bg, profile),"- disabling SMB"); + rT.reason += "maxDelta "+convert_bg(maxDelta, profile)+" > 20% of BG "+convert_bg(bg, profile)+": SMB disabled; "; + enableSMB = false; + } + + console.error("BG projected to remain above",convert_bg(min_bg, profile),"for",minutesAboveMinBG,"minutes"); + if ( minutesAboveThreshold < 240 || minutesAboveMinBG < 60 ) { + console.error("BG projected to remain above",convert_bg(threshold,profile),"for",minutesAboveThreshold,"minutes"); + } // include at least minutesAboveMinBG worth of zero temps in calculating carbsReq // always include at least 30m worth of zero temp (carbs to 80, low temp up to target) - var zeroTempDuration = Math.max(30,minutesAboveMinBG); + //var zeroTempDuration = Math.max(30,minutesAboveMinBG); + var zeroTempDuration = minutesAboveThreshold; // BG undershoot, minus effect of zero temps until hitting min_bg, converted to grams, minus COB var zeroTempEffect = profile.current_basal*sens*zeroTempDuration/60; - var carbsReq = (bgUndershoot - zeroTempEffect) / csf - meal_data.mealCOB; + // don't count the last 25% of COB against carbsReq + var COBforCarbsReq = Math.max(0, meal_data.mealCOB - 0.25*meal_data.carbs); + var carbsReq = (bgUndershoot - zeroTempEffect) / csf - COBforCarbsReq; zeroTempEffect = round(zeroTempEffect); carbsReq = round(carbsReq); - console.error("bgUndershoot:",bgUndershoot,"zeroTempDuration:",zeroTempDuration,"zeroTempEffect:",zeroTempEffect,"carbsReq:",carbsReq); - if ( carbsReq > 0 ) { + console.error("naive_eventualBG:",naive_eventualBG,"bgUndershoot:",bgUndershoot,"zeroTempDuration:",zeroTempDuration,"zeroTempEffect:",zeroTempEffect,"carbsReq:",carbsReq); + if ( carbsReq >= profile.carbsReqThreshold && minutesAboveThreshold <= 45 ) { rT.carbsReq = carbsReq; - rT.reason += carbsReq + " add'l carbs req + " + minutesAboveMinBG + "m zero temp; "; + rT.reason += carbsReq + " add'l carbs req w/in " + minutesAboveThreshold + "m; "; } // don't low glucose suspend if IOB is already super negative and BG is rising faster than predicted if (bg < threshold && iob_data.iob < -profile.current_basal*20/60 && minDelta > 0 && minDelta > expectedDelta) { rT.reason += "IOB "+iob_data.iob+" < " + round(-profile.current_basal*20/60,2); - rT.reason += " and minDelta " + minDelta + " > " + "expectedDelta " + expectedDelta + "; "; - } - // low glucose suspend mode: BG is < ~80 - else if (bg < threshold) { - 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 - if ( minDelta < expectedDelta ) { - rT.reason += ", minDelta " + minDelta + " < " + "expectedDelta " + expectedDelta + "; "; - } - return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); - } - if (glucose_status.delta > minDelta) { - rT.reason += ", delta " + glucose_status.delta + ">0"; - } else { - rT.reason += ", min delta " + minDelta.toFixed(2) + ">0"; - } - 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); - } + rT.reason += " and minDelta " + convert_bg(minDelta, profile) + " > " + "expectedDelta " + convert_bg(expectedDelta, profile) + "; "; + // predictive low glucose suspend mode: BG is / is projected to be < threshold + } else if ( bg < threshold || minGuardBG < threshold ) { + rT.reason += "minGuardBG " + convert_bg(minGuardBG, profile) + "<" + convert_bg(threshold, profile); + var bgUndershoot = target_bg - minGuardBG; + var worstCaseInsulinReq = bgUndershoot / sens; + var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + durationReq = round(durationReq/30)*30; + // always set a 30-120m zero temp (oref0-pump-loop will let any longer SMB zero temp run) + durationReq = Math.min(120,Math.max(30,durationReq)); + return tempBasalFunctions.setTempBasal(0, durationReq, profile, rT, currenttemp); } if (eventualBG < min_bg) { // if eventual BG is below target: 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 (minDelta > expectedDelta && minDelta > 0) { + if ( minDelta > expectedDelta && minDelta > 0 && !carbsReq ) { // if naive_eventualBG < 40, set a 30m zero temp (oref0-pump-loop will let any longer SMB zero temp run) if (naive_eventualBG < 40) { rT.reason += ", naive_eventualBG < 40. "; return tempBasalFunctions.setTempBasal(0, 30, profile, rT, currenttemp); } if (glucose_status.delta > minDelta) { - rT.reason += ", but Delta " + tick + " > expectedDelta " + expectedDelta; + rT.reason += ", but Delta " + convert_bg(tick, profile) + " > expectedDelta " + convert_bg(expectedDelta, profile); } else { - rT.reason += ", but Min. Delta " + minDelta.toFixed(2) + " > Exp. Delta " + expectedDelta; + rT.reason += ", but Min. Delta " + minDelta.toFixed(2) + " > Exp. Delta " + convert_bg(expectedDelta, profile); } if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) { rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr. "; @@ -615,111 +900,71 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } - if (eventualBG < min_bg) { - // if we've bolused recently, we can snooze until the bolus IOB decays (at double speed) - if (snoozeBG > min_bg) { // if adding back in the bolus contribution BG would be above min - // If we're not in SMB mode with COB, or lastCOBpredBG > target_bg, bolus snooze - if (! (microBolusAllowed && rT.COB) || lastCOBpredBG > target_bg) { - rT.reason += ", bolus snooze: eventual BG range " + convert_bg(eventualBG, profile) + "-" + convert_bg(snoozeBG, profile); - //console.error(currenttemp, basal ); - 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); - } + // 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 + // multiply by 2 to low-temp faster for increased hypo safety + //var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens); + var insulinReq = 2 * Math.min(0, (eventualBG - target_bg) / sens); + insulinReq = round( insulinReq , 2); + // calculate naiveInsulinReq based on naive_eventualBG + var naiveInsulinReq = Math.min(0, (naive_eventualBG - target_bg) / sens); + naiveInsulinReq = round( naiveInsulinReq , 2); + if (minDelta < 0 && minDelta > expectedDelta) { + // if we're barely falling, newinsulinReq should be barely negative + //rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile); + var newinsulinReq = round(( insulinReq * (minDelta / expectedDelta) ), 2); + //console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); + insulinReq = newinsulinReq; + } + // rate required to deliver insulinReq less insulin over 30m: + var rate = basal + (2 * insulinReq); + rate = round_basal(rate, profile); + // if required temp < existing temp basal + var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; + // if current temp would deliver a lot (30% of basal) less than the required insulin, + // by both normal and naive calculations, then raise the rate + var minInsulinReq = Math.min(insulinReq,naiveInsulinReq); + if (insulinScheduled < minInsulinReq - basal*0.3) { + rT.reason += ", "+currenttemp.duration + "m@" + (currenttemp.rate).toFixed(2) + " is a lot less than needed. "; + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); + } + if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate >= currenttemp.rate * 0.8)) { + rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr. "; + return rT; + } else { + // calculate a long enough zero temp to eventually correct back up to target + if ( rate <=0 ) { + var bgUndershoot = target_bg - naive_eventualBG; + var worstCaseInsulinReq = bgUndershoot / sens; + var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + if (durationReq < 0) { + durationReq = 0; + // don't set a temp longer than 120 minutes + } else { + durationReq = round(durationReq/30)*30; + durationReq = Math.min(120,Math.max(0,durationReq)); + } + //console.error(durationReq); + //rT.reason += "insulinReq " + insulinReq + "; " + if (durationReq > 0) { + rT.reason += ", setting " + durationReq + "m zero temp. "; + return tempBasalFunctions.setTempBasal(rate, durationReq, profile, rT, currenttemp); } } else { - // 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 - // multiply by 2 to low-temp faster for increased hypo safety - var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens); - insulinReq = round( insulinReq , 2); - // calculate naiveInsulinReq based on naive_eventualBG - var naiveInsulinReq = Math.min(0, (naive_eventualBG - target_bg) / sens); - naiveInsulinReq = round( naiveInsulinReq , 2); - if (minDelta < 0 && minDelta > expectedDelta) { - // if we're barely falling, newinsulinReq should be barely negative - rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile); - var newinsulinReq = round(( insulinReq * (minDelta / expectedDelta) ), 2); - //console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); - insulinReq = newinsulinReq; - } - // rate required to deliver insulinReq less insulin over 30m: - var rate = basal + (2 * insulinReq); - rate = round_basal(rate, profile); - // if required temp < existing temp basal - var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; - // if current temp would deliver a lot (30% of basal) less than the required insulin, - // by both normal and naive calculations, then raise the rate - var minInsulinReq = Math.min(insulinReq,naiveInsulinReq); - if (insulinScheduled < minInsulinReq - basal*0.3) { - rT.reason += ", "+currenttemp.duration + "m@" + (currenttemp.rate).toFixed(2) + " is a lot less than needed. "; - return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); - } - if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate >= currenttemp.rate * 0.8)) { - rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr. "; - return rT; - } else { - // calculate a long enough zero temp to eventually correct back up to target - if ( rate < 0 ) { - var bgUndershoot = target_bg - naive_eventualBG; - var worstCaseInsulinReq = bgUndershoot / sens; - var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); - if (durationReq < 0) { - durationReq = 0; - // don't set a temp longer than 120 minutes - } else { - durationReq = round(durationReq/30)*30; - durationReq = Math.min(120,Math.max(0,durationReq)); - } - //console.error(durationReq); - //rT.reason += "insulinReq " + insulinReq + "; " - if (durationReq > 0) { - rT.reason += ", setting " + durationReq + "m zero temp. "; - return tempBasalFunctions.setTempBasal(rate, durationReq, profile, rT, currenttemp); - } - } else { - rT.reason += ", setting " + rate + "U/hr. "; - } - return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); - } + rT.reason += ", setting " + rate + "U/hr. "; } + return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); } } - /* - var minutes_running; - if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { - minutes_running = 30; - } else if (typeof currenttemp.minutesrunning !== 'undefined'){ - // If the time the current temp is running is not defined, use default request duration of 30 minutes. - minutes_running = currenttemp.minutesrunning; - } else { - minutes_running = 30 - currenttemp.duration; - } - - // 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) * ((30-minutes_running)/60) * sens; - var adjEventualBG = eventualBG + lowtempimpact; - // don't return early if microBolusAllowed etc. - if ( adjEventualBG < min_bg && ! (microBolusAllowed && enableSMB)) { - 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 (minDelta < expectedDelta) { // if in SMB mode, don't cancel SMB zero temp if (! (microBolusAllowed && enableSMB)) { if (glucose_status.delta < minDelta) { - rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Delta " + tick + " < Exp. Delta " + expectedDelta; + rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Delta " + convert_bg(tick, profile) + " < Exp. Delta " + convert_bg(expectedDelta, profile); } else { - rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " > " + convert_bg(min_bg, profile) + " but Min. Delta " + minDelta.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 " + convert_bg(expectedDelta, profile); } if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) { rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr. "; @@ -730,17 +975,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } } - // eventualBG, snoozeBG, or minPredBG is below max_bg - if (Math.min(eventualBG,snoozeBG,minPredBG) < 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) && currenttemp.duration > 5 ) { - rT.reason += eventualBG + " > " + max_bg + ": no temp required (letting high temp of " + currenttemp.rate + " run). " - return rT; - } - + // eventualBG or minPredBG is below max_bg + if (Math.min(eventualBG,minPredBG) < max_bg) { // if in SMB mode, don't cancel SMB zero temp if (! (microBolusAllowed && enableSMB )) { - rT.reason += convert_bg(eventualBG, profile)+"-"+convert_bg(Math.min(minPredBG,snoozeBG), profile)+" in range: no temp required"; + rT.reason += convert_bg(eventualBG, profile)+"-"+convert_bg(minPredBG, profile)+" in range: no temp required"; if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) { rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr. "; return rT; @@ -751,17 +990,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } - // eventual BG is at/above target (or bolus snooze disabled for SMB) + // eventual BG is at/above target // if iob is over max, just cancel any temps - var basaliob; - if (iob_data.basaliob) { basaliob = iob_data.basaliob; } - else { basaliob = iob_data.iob - iob_data.bolussnooze; } // if we're not here because of SMB, eventual BG is at/above target if (! (microBolusAllowed && rT.COB)) { rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", "; } - if (basaliob > max_iob) { - rT.reason += "basaliob " + round(basaliob,2) + " > max_iob " + max_iob; + if (iob_data.iob > max_iob) { + rT.reason += "IOB " + round(iob_data.iob,2) + " > max_iob " + max_iob; if (currenttemp.duration > 15 && (round_basal(basal, profile) === round_basal(currenttemp.rate, profile))) { rT.reason += ", temp " + currenttemp.rate + " ~ req " + basal + "U/hr. "; return rT; @@ -772,19 +1008,20 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { // otherwise, calculate 30m high-temp required to get projected BG down to target // insulinReq is the additional insulin required to get minPredBG down to target_bg - //console.error(minPredBG,snoozeBG,eventualBG); - var insulinReq = round( (Math.min(minPredBG,snoozeBG,eventualBG) - target_bg) / sens, 2); + //console.error(minPredBG,eventualBG); + //var insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2); + var insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2); // when dropping, but not as fast as expected, reduce insulinReq proportionally // to the what fraction of expectedDelta we're dropping at - if (minDelta < 0 && minDelta > expectedDelta) { - var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2); - //console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq); - insulinReq = newinsulinReq; - } + //if (minDelta < 0 && minDelta > expectedDelta) { + //var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2); + //console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq + " for minDelta " + minDelta + " vs. expectedDelta " + expectedDelta); + //insulinReq = newinsulinReq; + //} // if that would put us over max_iob, then reduce accordingly - if (insulinReq > max_iob-basaliob) { + if (insulinReq > max_iob-iob_data.iob) { rT.reason += "max_iob " + max_iob + ", "; - insulinReq = max_iob-basaliob; + insulinReq = max_iob-iob_data.iob; } // rate required to deliver insulinReq more insulin over 30m: @@ -792,50 +1029,63 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rate = round_basal(rate, profile); insulinReq = round(insulinReq,3); rT.insulinReq = insulinReq; - rT.minPredBG = minPredBG; //console.error(iob_data.lastBolusTime); // minutes since last bolus var lastBolusAge = round(( new Date().getTime() - iob_data.lastBolusTime ) / 60000,1); //console.error(lastBolusAge); //console.error(profile.temptargetSet, target_bg, rT.COB); // only allow microboluses with COB or low temp targets, or within DIA hours of a bolus - // only microbolus if 0.1U SMB represents 20m or less of basal (0.3U/hr or higher) - if (microBolusAllowed && enableSMB && profile.current_basal >= 0.3 && bg > threshold) { - // never bolus more than 30m worth of basal - maxBolus = round(profile.current_basal/2,1); - // bolus 1/3 the insulinReq, up to maxBolus - microBolus = round(Math.min(insulinReq/3,maxBolus),1); - + if (microBolusAllowed && enableSMB && bg > threshold) { + // never bolus more than maxSMBBasalMinutes worth of basal + mealInsulinReq = round( meal_data.mealCOB / profile.carb_ratio ,3); + if (typeof profile.maxSMBBasalMinutes == 'undefined' ) { + maxBolus = round( profile.current_basal * 30 / 60 ,1); + console.error("profile.maxSMBBasalMinutes undefined: defaulting to 30m"); + // if IOB covers more than COB, limit maxBolus to 30m of basal + } else if ( iob_data.iob > mealInsulinReq && iob_data.iob > 0 ) { + console.error("IOB",iob_data.iob,"> COB",meal_data.mealCOB+"; mealInsulinReq =",mealInsulinReq); + maxBolus = round( profile.current_basal * 30 / 60 ,1); + } else { + console.error("profile.maxSMBBasalMinutes:",profile.maxSMBBasalMinutes,"profile.current_basal:",profile.current_basal); + maxBolus = round( profile.current_basal * profile.maxSMBBasalMinutes / 60 ,1); + } + // bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest 0.1U + microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*10)/10; // calculate a long enough zero temp to eventually correct back up to target var smbTarget = target_bg; var worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); - // if no microBolus required, snoozeBG > target_bg, and lastCOBpredBG > target_bg, don't set a zero temp - if (microBolus < 0.1 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { + // if insulinReq > 0 but not enough for a microBolus, don't set an SMB zero temp + if (insulinReq > 0 && microBolus < 0.1) { durationReq = 0; } - if (durationReq < 0) { + var smbLowTempReq = 0; + if (durationReq <= 0) { durationReq = 0; // don't set a temp longer than 120 minutes - } else { + } else if (durationReq >= 30) { durationReq = round(durationReq/30)*30; durationReq = Math.min(120,Math.max(0,durationReq)); + } else { + // if SMB durationReq is less than 30m, set a nonzero low temp + smbLowTempReq = round( basal * durationReq/30 ,2); + durationReq = 30; } rT.reason += " insulinReq " + insulinReq; if (microBolus >= maxBolus) { rT.reason += "; maxBolus " + maxBolus; } if (durationReq > 0) { - rT.reason += "; setting " + durationReq + "m zero temp"; + rT.reason += "; setting " + durationReq + "m low temp of " + smbLowTempReq + "U/h"; } rT.reason += ". "; //allow SMBs every 3 minutes var nextBolusMins = round(3-lastBolusAge,1); //console.error(naive_eventualBG, insulinReq, worstCaseInsulinReq, durationReq); - console.error("naive_eventualBG",naive_eventualBG+",",durationReq+"m zero temp needed; last bolus",lastBolusAge+"m ago; maxBolus: "+maxBolus); + console.error("naive_eventualBG",naive_eventualBG+",",durationReq+"m "+smbLowTempReq+"U/h temp needed; last bolus",lastBolusAge+"m ago; maxBolus: "+maxBolus); if (lastBolusAge > 3) { if (microBolus > 0) { rT.units = microBolus; @@ -848,16 +1098,16 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if no zero temp is required, don't return yet; allow later code to set a high temp if (durationReq > 0) { - rT.rate = 0; + rT.rate = smbLowTempReq; rT.duration = durationReq; return rT; } // if insulinReq is negative, snoozeBG > target_bg, and lastCOBpredBG > target_bg, set a neutral temp - if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { - rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. "; - return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); - } + //if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { + //rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. "; + //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + //} } var maxSafeBasal = tempBasalFunctions.getMaxSafeBasal(profile); @@ -890,4 +1140,4 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ }; -module.exports = determine_basal; \ No newline at end of file +module.exports = determine_basal; diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index 3365f8a28e..874f75d2bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -23,7 +23,7 @@ public class Config { public static final boolean SMSCOMMUNICATORENABLED = !BuildConfig.NSCLIENTOLNY && !BuildConfig.G5UPLOADER; - public static final boolean displayDeviationSlope = false; + public static final boolean displayDeviationSlope = true; public static final boolean detailedLog = true; public static final boolean logFunctionCalls = true; diff --git a/app/src/main/java/info/nightscout/androidaps/data/GlucoseStatus.java b/app/src/main/java/info/nightscout/androidaps/data/GlucoseStatus.java index 1623028a82..4fd4bcca6a 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/GlucoseStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/data/GlucoseStatus.java @@ -35,6 +35,7 @@ public class GlucoseStatus { public double avgdelta = 0d; public double short_avgdelta = 0d; public double long_avgdelta = 0d; + public long date = 0L; @Override @@ -126,6 +127,7 @@ public class GlucoseStatus { GlucoseStatus status = new GlucoseStatus(); status.glucose = now.value; + status.date = now_date; status.short_avgdelta = average(short_deltas); diff --git a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java index dd00978d04..34df937d14 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java +++ b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java @@ -24,6 +24,10 @@ public class IobTotal { public double microBolusInsulin; public double microBolusIOB; public long lastBolusTime; + public long lastTempDate; + public int lastTempDuration; + public double lastTempRate; + public IobTotal iobWithZeroTemp; public double netInsulin = 0d; // for calculations from temp basals only public double netRatio = 0d; // net ratio at start of temp basal @@ -32,6 +36,25 @@ public class IobTotal { long time; + + public IobTotal clone() { + IobTotal copy = new IobTotal(time); + copy.iob = iob; + copy.activity = activity; + copy.bolussnooze = bolussnooze; + copy.basaliob = basaliob; + copy.netbasalinsulin = netbasalinsulin; + copy.hightempinsulin = hightempinsulin; + copy.microBolusInsulin = microBolusInsulin; + copy.microBolusIOB = microBolusIOB; + copy.lastBolusTime = lastBolusTime; + copy.lastTempDate = lastTempDate; + copy.lastTempDuration = lastTempDuration; + copy.lastTempRate = lastTempRate; + copy.iobWithZeroTemp = iobWithZeroTemp; + return copy; + } + public IobTotal(long time) { this.iob = 0d; this.activity = 0d; @@ -70,6 +93,10 @@ public class IobTotal { result.microBolusInsulin = bolusIOB.microBolusInsulin + basalIob.microBolusInsulin; result.microBolusIOB = bolusIOB.microBolusIOB + basalIob.microBolusIOB; result.lastBolusTime = bolusIOB.lastBolusTime; + result.lastTempDate = basalIob.lastTempDate; + result.lastTempRate = basalIob.lastTempRate; + result.lastTempDuration = basalIob.lastTempDuration; + result.iobWithZeroTemp = basalIob.iobWithZeroTemp; return result; } @@ -107,6 +134,22 @@ public class IobTotal { json.put("activity", activity); json.put("lastBolusTime", lastBolusTime); json.put("time", DateUtil.toISOString(new Date(time))); + /* + + This is requested by SMB determine_basal but by based on Scott's info + it's MDT specific safety check only + It's causing rounding issues in determine_basal + + JSONObject lastTemp = new JSONObject(); + lastTemp.put("date", lastTempDate); + lastTemp.put("rate", lastTempRate); + lastTemp.put("duration", lastTempDuration); + json.put("lastTemp", lastTemp); + */ + if (iobWithZeroTemp != null) { + JSONObject iwzt = iobWithZeroTemp.determineBasalJson(); + json.put("iobWithZeroTemp", iwzt); + } } catch (JSONException e) { log.error("Unhandled exception", e); } diff --git a/app/src/main/java/info/nightscout/androidaps/data/MealData.java b/app/src/main/java/info/nightscout/androidaps/data/MealData.java index 4e1014431c..054c76d602 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/MealData.java +++ b/app/src/main/java/info/nightscout/androidaps/data/MealData.java @@ -7,6 +7,8 @@ public class MealData { public double boluses = 0d; public double carbs = 0d; public double mealCOB = 0.0d; - public double minDeviationSlope; + public double slopeFromMaxDeviation = 0; + public double slopeFromMinDeviation = 999; public long lastBolusTime; + public long lastCarbTime = 0L; } diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index 58442cadd1..9109963182 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -46,6 +46,7 @@ public class BgReading implements DataPointWithLabelInterface { public boolean isaCOBPrediction = false; // true when drawing predictions as bg points (aCOB) public boolean isIOBPrediction = false; // true when drawing predictions as bg points (IOB) public boolean isUAMPrediction = false; // true when drawing predictions as bg points (UAM) + public boolean isZTPrediction = false; // true when drawing predictions as bg points (ZT) public BgReading() { } @@ -228,11 +229,13 @@ public class BgReading implements DataPointWithLabelInterface { return 0x80FFFFFF & MainApp.sResources.getColor(R.color.cob); if (isUAMPrediction) return MainApp.sResources.getColor(R.color.uam); + if (isZTPrediction) + return MainApp.sResources.getColor(R.color.zt); return R.color.mdtp_white; } private boolean isPrediction() { - return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction; + return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction || isZTPrediction; } } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java index 0803f65373..7a6ebbd701 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java @@ -10,16 +10,16 @@ import info.nightscout.androidaps.db.Treatment; */ public interface InsulinInterface { - final int FASTACTINGINSULIN = 0; - final int FASTACTINGINSULINPROLONGED = 1; - final int OREF_RAPID_ACTING = 2; - final int OREF_ULTRA_RAPID_ACTING = 3; - final int OREF_FREE_PEAK = 4; + int FASTACTINGINSULIN = 0; + int FASTACTINGINSULINPROLONGED = 1; + int OREF_RAPID_ACTING = 2; + int OREF_ULTRA_RAPID_ACTING = 3; + int OREF_FREE_PEAK = 4; int getId(); String getFriendlyName(); String getComment(); double getDia(); - public Iob iobCalcForTreatment(Treatment treatment, long time, Double dia); + Iob iobCalcForTreatment(Treatment treatment, long time, Double dia); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 4e5400cbc0..dddea570be 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -61,10 +61,11 @@ public class AutosensData { public double avgDeviation = 0d; public double autosensRatio = 1d; - public double minDeviationSlope = 999; + public double slopeFromMaxDeviation = 0; + public double slopeFromMinDeviation = 999; public String log(long time) { - return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " minDeviationSlope=" + minDeviationSlope; + return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation =" + slopeFromMinDeviation ; } public int minOld() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index e669f57068..6a12d77c30 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -34,6 +34,7 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.BasalData; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; +import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; /** * Created by mike on 24.04.2017. @@ -402,8 +403,10 @@ public class IobCobCalculatorPlugin implements PluginBase { double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; double currentDeviation; - double minDeviationSlope = 0; + double slopeFromMaxDeviation = 0; + double slopeFromMinDeviation = 999; double maxDeviation = 0; + double minDeviation = 999; // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope @@ -416,10 +419,16 @@ public class IobCobCalculatorPlugin implements PluginBase { AutosensData ad = autosensDataTable.valueAt(initialIndex + past); double deviationSlope = (ad.avgDeviation - currentDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; if (ad.avgDeviation > maxDeviation) { - minDeviationSlope = Math.min(0, deviationSlope); + slopeFromMaxDeviation = Math.min(0, deviationSlope); maxDeviation = ad.avgDeviation; } - log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " minDeviationSlope=" + minDeviationSlope); + if (avgDeviation < minDeviation) { + slopeFromMinDeviation = Math.max(0, deviationSlope); + minDeviation = avgDeviation; + } + + //if (Config.logAutosensData) + // log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation=" + slopeFromMinDeviation); } } @@ -454,7 +463,8 @@ public class IobCobCalculatorPlugin implements PluginBase { autosensData.delta = delta; autosensData.avgDelta = avgDelta; autosensData.avgDeviation = avgDeviation; - autosensData.minDeviationSlope = minDeviationSlope; + autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation; + autosensData.slopeFromMinDeviation = slopeFromMinDeviation; // calculate autosens only without COB if (autosensData.cob <= 0) { @@ -477,8 +487,8 @@ public class IobCobCalculatorPlugin implements PluginBase { previous = autosensData; autosensDataTable.put(bgTime, autosensData); autosensData.autosensRatio = detectSensitivity(oldestTimeWithData, bgTime).ratio; - if (Config.logAutosensData) - log.debug(autosensData.log(bgTime)); + //if (Config.logAutosensData) + // log.debug(autosensData.log(bgTime)); } } MainApp.bus().post(new EventAutosensCalculationFinished()); @@ -511,6 +521,21 @@ public class IobCobCalculatorPlugin implements PluginBase { } IobTotal bolusIob = MainApp.getConfigBuilder().getCalculationToTimeTreatments(time).round(); IobTotal basalIob = MainApp.getConfigBuilder().getCalculationToTimeTempBasals(time).round(); + if (OpenAPSSMBPlugin.getPlugin().isEnabled(PluginBase.APS)) { + // Add expected zere temp basal for next 240 mins + IobTotal basalIobWithZeroTemp = basalIob.clone(); + TemporaryBasal t = new TemporaryBasal(); + t.date = now + 60 * 1000L; + t.durationInMinutes = 240; + t.isAbsolute = true; + t.absoluteRate = 0; + if (t.date < time) { + IobTotal calc = t.iobCalc(time); + basalIobWithZeroTemp.plus(calc); + } + + basalIob.iobWithZeroTemp = basalIobWithZeroTemp; + } IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); if (time < System.currentTimeMillis()) { @@ -605,6 +630,23 @@ public class IobCobCalculatorPlugin implements PluginBase { return array; } + public static IobTotal[] calculateIobArrayForSMB() { + Profile profile = MainApp.getConfigBuilder().getProfile(); + // predict IOB out to DIA plus 30m + long time = System.currentTimeMillis(); + time = roundUpTime(time); + int len = (4 * 60) / 5; + IobTotal[] array = new IobTotal[len]; + int pos = 0; + for (int i = 0; i < len; i++) { + long t = time + i * 5 * 60000; + IobTotal iob = calculateFromTreatmentsAndTempsSynchronized(t); + array[pos] = iob; + pos++; + } + return array; + } + public static AutosensResult detectSensitivityWithLock(long fromTime, long toTime) { synchronized (dataLock) { return detectSensitivity(fromTime, toTime); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 867fb20a92..6e81c1a619 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -143,6 +143,16 @@ public class APSResult { array.add(bg); } } + if (predBGs.has("ZT")) { + JSONArray iob = predBGs.getJSONArray("ZT"); + for (int i = 1; i < iob.length(); i++) { + BgReading bg = new BgReading(); + bg.value = iob.getInt(i); + bg.date = startTime + i * 5 * 60 * 1000L; + bg.isZTPrediction = true; + array.add(bg); + } + } } } catch (JSONException e) { e.printStackTrace(); @@ -172,6 +182,10 @@ public class APSResult { JSONArray iob = predBGs.getJSONArray("UAM"); latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); } + if (predBGs.has("ZT")) { + JSONArray iob = predBGs.getJSONArray("ZT"); + latest = Math.max(latest, startTime + (iob.length() - 1) * 5 * 60 * 1000L); + } } } catch (JSONException e) { e.printStackTrace(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java index 466026e986..66d3ab2b96 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java @@ -29,6 +29,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.plugins.OpenAPSMA.LoggerCallback; +import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; import info.nightscout.utils.SP; public class DetermineBasalAdapterAMAJS { @@ -189,8 +190,7 @@ public class DetermineBasalAdapterAMAJS { GlucoseStatus glucoseStatus, MealData mealData, double autosensDataRatio, - boolean tempTargetSet, - double min_5m_carbimpact) throws JSONException { + boolean tempTargetSet) throws JSONException { String units = profile.getUnits(); @@ -211,7 +211,7 @@ public class DetermineBasalAdapterAMAJS { mProfile.put("current_basal", basalrate); 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("min_5m_carbimpact", SP.getInt("openapsama_min_5m_carbimpact", SMBDefaults.min_5m_carbimpact)); if (units.equals(Constants.MMOL)) { mProfile.put("out_units", "mmol/L"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java index 4dcb174209..c318dce0d7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java @@ -233,8 +233,7 @@ public class OpenAPSAMAPlugin implements PluginBase, APSInterface { try { determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, ConfigBuilderPlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData, lastAutosensResult.ratio, //autosensDataRatio - isTempTarget, - SafeParse.stringToDouble(SP.getString("openapsama_min_5m_carbimpact", "3.0"))//min_5m_carbimpact + isTempTarget ); } catch (JSONException e) { log.error("Unable to set data: " + e.toString()); 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 2663ea1e1c..05ed4d92f4 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 @@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.Date; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; @@ -25,14 +24,13 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.interfaces.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; +import info.nightscout.utils.SafeParse; public class DetermineBasalAdapterSMBJS { private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterSMBJS.class); @@ -216,7 +214,7 @@ public class DetermineBasalAdapterSMBJS { String units = profile.getUnits(); mProfile = new JSONObject(); - ; + mProfile.put("max_iob", maxIob); mProfile.put("dia", profile.getDia()); mProfile.put("type", "current"); @@ -229,26 +227,32 @@ public class DetermineBasalAdapterSMBJS { 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("high_temptarget_raises_sensitivity", SMBDefaults.high_temptarget_raises_sensitivity); + mProfile.put("low_temptarget_lowers_sensitivity", SMBDefaults.low_temptarget_lowers_sensitivity); + mProfile.put("sensitivity_raises_target", SMBDefaults.sensitivity_raises_target); + mProfile.put("resistance_lowers_target", SMBDefaults.resistance_lowers_target); + mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments); + mProfile.put("exercise_mode", SMBDefaults.exercise_mode); + mProfile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target); + mProfile.put("maxCOB", SMBDefaults.maxCOB); + mProfile.put("skip_neutral_temps", SMBDefaults.skip_neutral_temps); + mProfile.put("min_5m_carbimpact", SP.getInt("openapsama_min_5m_carbimpact", SMBDefaults.min_5m_carbimpact)); + mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); + mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); + mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); + mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); + mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); + mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_after_carbs); + mProfile.put("allowSMB_with_high_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.allowSMB_with_high_temptarget); + mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); + mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); + mProfile.put("current_basal", basalrate); 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.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 + mProfile.put("autosens_max", SafeParse.stringToDouble(SP.getString("openapsama_autosens_max", "1.2"))); mCurrentTemp = new JSONObject(); - ; mCurrentTemp.put("temp", "absolute"); mCurrentTemp.put("duration", MainApp.getConfigBuilder().getTempBasalRemainingMinutesFromHistory()); mCurrentTemp.put("rate", MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()); @@ -262,7 +266,6 @@ public class DetermineBasalAdapterSMBJS { mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray); mGlucoseStatus = new JSONObject(); - ; mGlucoseStatus.put("glucose", glucoseStatus.glucose); if (SP.getBoolean("always_use_shortavg", false)) { @@ -272,14 +275,17 @@ public class DetermineBasalAdapterSMBJS { } mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta); mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta); + mGlucoseStatus.put("date", glucoseStatus.date); mMealData = new JSONObject(); - ; mMealData.put("carbs", mealData.carbs); mMealData.put("boluses", mealData.boluses); mMealData.put("mealCOB", mealData.mealCOB); - mMealData.put("minDeviationSlope", mealData.minDeviationSlope); + mMealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation); + mMealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation); mMealData.put("lastBolusTime", mealData.lastBolusTime); + mMealData.put("lastCarbTime", mealData.lastCarbTime); + if (MainApp.getConfigBuilder().isAMAModeEnabled()) { mAutosensData = new JSONObject(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java index 1c0af1a67d..ef515c68f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java @@ -65,8 +65,8 @@ public class OpenAPSSMBFragment extends SubscriberFragment implements View.OnCli public void onClick(View view) { switch (view.getId()) { case R.id.openapsma_run: - OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSAMA button"); - Answers.getInstance().logCustom(new CustomEvent("OpenAPS_AMA_Run")); + OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSSMB button"); + Answers.getInstance().logCustom(new CustomEvent("OpenAPS_SMB_Run")); break; } 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 d449b30a29..a0f7483f34 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 @@ -110,7 +110,7 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { @Override public int getPreferencesId() { - return R.xml.pref_openapsama; + return R.xml.pref_openapssmb; } @Override @@ -188,7 +188,7 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { Date start = new Date(); Date startPart = new Date(); - IobTotal[] iobArray = IobCobCalculatorPlugin.calculateIobArrayInDia(); + IobTotal[] iobArray = IobCobCalculatorPlugin.calculateIobArrayForSMB(); Profiler.log(log, "calculateIobArrayInDia()", startPart); startPart = new Date(); @@ -229,7 +229,7 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { lastAutosensResult = new AutosensResult(); } Profiler.log(log, "detectSensitivityandCarbAbsorption()", startPart); - Profiler.log(log, "AMA data gathering", start); + Profiler.log(log, "SMB data gathering", start); start = new Date(); try { @@ -263,7 +263,7 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { try { determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now)); } catch (JSONException e) { - e.printStackTrace(); + log.error("Unhandled exception", e); } lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java new file mode 100644 index 0000000000..57fecd43f6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.plugins.OpenAPSSMB; + +/** + * Created by mike on 10.12.2017. + */ + +public class SMBDefaults { + // CALCULATED OR FROM PREFS + + // max_iob: 0 // if max_iob is not provided, will default to zero + // max_daily_safety_multiplier:3 + // current_basal_safety_multiplier:4 + // autosens_max:1.2 + // autosens_min:0.7 + + // USED IN AUTOSENS + public final static boolean rewind_resets_autosens = true; // reset autosensitivity to neutral for awhile after each pump rewind + + // USED IN TARGETS + // by default the higher end of the target range is used only for avoiding bolus wizard overcorrections + // use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs + public final static boolean wide_bg_target_range = false; // by default use only the low end of the pump's BG target range as OpenAPS target + + // USED IN AUTOTUNE + public final static double autotune_isf_adjustmentFraction = 1.0; // 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. + public final static double remainingCarbsFraction = 1.0; // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption + + // USED IN DETERMINE_BASAL + public final static boolean low_temptarget_lowers_sensitivity = false; // lower sensitivity for temptargets <= 99. + public final static boolean high_temptarget_raises_sensitivity = false; // raise sensitivity for temptargets >= 111. synonym for exercise_mode + public final static boolean sensitivity_raises_target = true; // raise BG target when autosens detects sensitivity + public final static boolean resistance_lowers_target = false; // lower BG target when autosens detects resistance + public final static boolean adv_target_adjustments = false; // lower target automatically when BG and eventualBG are high + public final static boolean exercise_mode = false; // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonmym for high_temptarget_raises_sensitivity + public final static int half_basal_exercise_target = 160; // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%) + // 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/SMB as a safety cap against excessive COB entry) + public final static int maxCOB = 120; + public final static boolean skip_neutral_temps = true; // ***** default false in oref1 ***** if true, don't set neutral temps + // unsuspend_if_no_temp:false // if true, pump will un-suspend after a zero temp finishes + // bolussnooze_dia_divisor:2 // bolus snooze decays after 1/2 of DIA + public final static int min_5m_carbimpact = 8; // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4)) + public final static int remainingCarbsCap = 90; // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption + // WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin + // enableUAM:true // enable detection of unannounced meal carb absorption + public final static boolean A52_risk_enable = false; + public final static boolean enableSMB_with_COB = true; // ***** default false in oref1 ***** enable supermicrobolus while COB is positive + public final static boolean enableSMB_with_temptarget = true; // ***** default false in oref1 ***** enable supermicrobolus for eating soon temp targets + // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar + // xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs + // Using SMB overnight with such data sources risks causing a dangerous overdose of insulin + // if the CGM sensor reads falsely high and doesn't come down as actual BG does + public final static boolean enableSMB_always = false; // always enable supermicrobolus (unless disabled by high temptarget) + // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar + public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB + public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets + public final static int maxSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB with uncovered COB + // curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp) + // useCustomPeakTime:false // allows changing insulinPeakTime + // insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false + public final static int carbsReqThreshold = 1; // grams of carbsReq to trigger a pushover + // offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index a0c2167391..7a27546c19 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -398,21 +398,21 @@ public class GraphData { // scale in % of vertical size (like 0.3) public void addRatio(GraphView graph, long fromTime, long toTime, boolean useForScale, double scale) { - LineGraphSeries ratioSeries; - List ratioArray = new ArrayList<>(); + LineGraphSeries ratioSeries; + List ratioArray = new ArrayList<>(); Double maxRatioValueFound = 0d; Scale ratioScale = new Scale(-1d); for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { AutosensData autosensData = IobCobCalculatorPlugin.getAutosensData(time); if (autosensData != null) { - ratioArray.add(new DataPoint(time, autosensData.autosensRatio)); + ratioArray.add(new ScaledDataPoint(time, autosensData.autosensRatio, ratioScale)); maxRatioValueFound = Math.max(maxRatioValueFound, Math.abs(autosensData.autosensRatio)); } } // RATIOS - DataPoint[] ratioData = new DataPoint[ratioArray.size()]; + ScaledDataPoint[] ratioData = new ScaledDataPoint[ratioArray.size()]; ratioData = ratioArray.toArray(ratioData); ratioSeries = new LineGraphSeries<>(ratioData); ratioSeries.setColor(MainApp.sResources.getColor(R.color.ratio)); @@ -428,32 +428,46 @@ public class GraphData { // scale in % of vertical size (like 0.3) public void addDeviationSlope(GraphView graph, long fromTime, long toTime, boolean useForScale, double scale) { - LineGraphSeries dsSeries; - List dsArray = new ArrayList<>(); - Double maxDSValueFound = 0d; - Scale dsScale = new Scale(); + LineGraphSeries dsMaxSeries; + LineGraphSeries dsMinSeries; + List dsMaxArray = new ArrayList<>(); + List dsMinArray = new ArrayList<>(); + Double maxFromMaxValueFound = 0d; + Double maxFromMinValueFound = 0d; + Scale dsMaxScale = new Scale(); + Scale dsMinScale = new Scale(); for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { AutosensData autosensData = IobCobCalculatorPlugin.getAutosensData(time); if (autosensData != null) { - dsArray.add(new DataPoint(time, autosensData.minDeviationSlope)); - maxDSValueFound = Math.max(maxDSValueFound, Math.abs(autosensData.minDeviationSlope)); + dsMaxArray.add(new ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale)); + dsMinArray.add(new ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale)); + maxFromMaxValueFound = Math.max(maxFromMaxValueFound, Math.abs(autosensData.slopeFromMaxDeviation)); + maxFromMinValueFound = Math.max(maxFromMinValueFound, Math.abs(autosensData.slopeFromMinDeviation)); } } - // RATIOS - DataPoint[] ratioData = new DataPoint[dsArray.size()]; - ratioData = dsArray.toArray(ratioData); - dsSeries = new LineGraphSeries<>(ratioData); - dsSeries.setColor(Color.MAGENTA); - dsSeries.setThickness(3); + // Slopes + ScaledDataPoint[] ratioMaxData = new ScaledDataPoint[dsMaxArray.size()]; + ratioMaxData = dsMaxArray.toArray(ratioMaxData); + dsMaxSeries = new LineGraphSeries<>(ratioMaxData); + dsMaxSeries.setColor(Color.MAGENTA); + dsMaxSeries.setThickness(3); + + ScaledDataPoint[] ratioMinData = new ScaledDataPoint[dsMinArray.size()]; + ratioMinData = dsMinArray.toArray(ratioMinData); + dsMinSeries = new LineGraphSeries<>(ratioMinData); + dsMinSeries.setColor(Color.YELLOW); + dsMinSeries.setThickness(3); if (useForScale) - maxY = maxDSValueFound; + maxY = Math.max(maxFromMaxValueFound, maxFromMinValueFound); - dsScale.setMultiplier(maxY * scale / maxDSValueFound); + dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound); + dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound); - addSeriesWithoutInvalidate(graph, dsSeries); + addSeriesWithoutInvalidate(graph, dsMaxSeries); + addSeriesWithoutInvalidate(graph, dsMinSeries); } // scale in % of vertical size (like 0.3) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index da34488116..05c90d27c4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -250,6 +250,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { if (t > dia_ago && t <= now) { if (treatment.carbs >= 1) { result.carbs += treatment.carbs; + result.lastCarbTime = t; } if (treatment.insulin > 0 && treatment.mealBolus) { result.boluses += treatment.insulin; @@ -260,7 +261,8 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { AutosensData autosensData = IobCobCalculatorPlugin.getLastAutosensData(); if (autosensData != null) { result.mealCOB = autosensData.cob; - result.minDeviationSlope = autosensData.minDeviationSlope; + result.slopeFromMinDeviation = autosensData.slopeFromMinDeviation; + result.slopeFromMaxDeviation = autosensData.slopeFromMaxDeviation; } result.lastBolusTime = getLastBolusTime(); return result; @@ -349,6 +351,12 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { IobTotal calc = t.iobCalc(time); //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); total.plus(calc); + if (!t.isEndingEvent()) { + total.lastTempDate = t.date; + total.lastTempDuration = t.durationInMinutes; + total.lastTempRate = t.tempBasalConvertedToAbsolute(t.date); + } + } } if (ConfigBuilderPlugin.getActivePump().isFakingTempsByExtendedBoluses()) { @@ -359,6 +367,12 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { if (e.date > time) continue; IobTotal calc = e.iobCalc(time); totalExt.plus(calc); + TemporaryBasal t = new TemporaryBasal(e); + if (!t.isEndingEvent() && t.date > total.lastTempDate) { + total.lastTempDate = t.date; + total.lastTempDuration = t.durationInMinutes; + total.lastTempRate = t.tempBasalConvertedToAbsolute(t.date); + } } } // Convert to basal iob diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d068062ece..67def00b04 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,6 +6,7 @@ #FFFFCC03 #8BC34A #ffea00 + #ff9500 #FFFFFF #00FF00 #FF0000 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c01030d43..d8452d480a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -792,6 +792,9 @@ Customized APK for download Show detailed delta Show delta with one more decimal place + 45 60 75 90 105 120 + Max minutes of basal to limit SMB to + smbmaxminutes Unsupported pump firmware diff --git a/app/src/main/res/xml/pref_advanced.xml b/app/src/main/res/xml/pref_advanced.xml index 8056db4e3a..4a617c29ec 100644 --- a/app/src/main/res/xml/pref_advanced.xml +++ b/app/src/main/res/xml/pref_advanced.xml @@ -35,18 +35,6 @@ android:summary="@string/always_use_shortavg_summary" android:title="@string/always_use_shortavg" />
- - - - - + diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml new file mode 100644 index 0000000000..c8cca93a63 --- /dev/null +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + \ No newline at end of file From 2955dd7ec6b2c29c92bd797f4a4cc913e49718ca Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Wed, 20 Dec 2017 17:35:39 +0100 Subject: [PATCH 038/171] Only show progress dialog for normal boluses, not SMBs. (cherry picked from commit b3d4607) --- .../androidaps/queue/CommandQueue.java | 33 +++++++++++-------- .../queue/commands/CommandSMBBolus.java | 4 --- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index 16e3946fc5..c0ba6f9b55 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -160,28 +160,33 @@ public class CommandQueue { detailedBolusInfo.carbs = MainApp.getConfigBuilder().applyCarbsConstraints((int) detailedBolusInfo.carbs); // add new command to queue - if (detailedBolusInfo.isSMB) + if (detailedBolusInfo.isSMB) { add(new CommandSMBBolus(detailedBolusInfo, callback)); - else + } else { add(new CommandBolus(detailedBolusInfo, callback)); + // Bring up bolus progress dialog (start here, so the dialog is shown when the bolus is requested, + // not when the Bolus command is starting. The command closes the dialog upon completion). + if (detailedBolusInfo.context != null) { + BolusProgressDialog bolusProgressDialog = new BolusProgressDialog(); + bolusProgressDialog.setInsulin(detailedBolusInfo.insulin); + bolusProgressDialog.show(((AppCompatActivity) detailedBolusInfo.context).getSupportFragmentManager(), "BolusProgress"); + } else { + Intent i = new Intent(); + i.putExtra("insulin", detailedBolusInfo.insulin); + i.setClass(MainApp.instance(), BolusProgressHelperActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + // Notify Wear about upcoming bolus + MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); + } notifyAboutNewCommand(); // Notify Wear about upcoming bolus MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); - // Bring up bolus progress dialog - if (detailedBolusInfo.context != null) { - BolusProgressDialog bolusProgressDialog = new BolusProgressDialog(); - bolusProgressDialog.setInsulin(detailedBolusInfo.insulin); - bolusProgressDialog.show(((AppCompatActivity) detailedBolusInfo.context).getSupportFragmentManager(), "BolusProgress"); - } else { - Intent i = new Intent(); - i.putExtra("insulin", detailedBolusInfo.insulin); - i.setClass(MainApp.instance(), BolusProgressHelperActivity.class); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } + notifyAboutNewCommand(); return true; } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java index d8fae1df32..e1b268106a 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.java @@ -36,10 +36,6 @@ public class CommandSMBBolus extends Command { log.debug("SMB bolus canceled. delivetAt=" + detailedBolusInfo.deliverAt + " now=" + System.currentTimeMillis()); } - - BolusProgressDialog.bolusEnded = true; - MainApp.bus().post(new EventDismissBolusprogressIfRunning(r)); - if (callback != null) callback.result(r).run(); } From fb12c0bc96a491b0b2f80c5d9ab4d7eba6f7230c Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 21 Dec 2017 09:02:49 +0100 Subject: [PATCH 039/171] enable SMB for G5 --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 05ed4d92f4..a11ba438ce 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 @@ -26,9 +26,11 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.plugins.OpenAPSMA.LoggerCallback; +import info.nightscout.androidaps.plugins.SourceDexcomG5.SourceDexcomG5Plugin; import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; @@ -243,7 +245,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); - mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_after_carbs); + mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.APS) || SMBDefaults.enableSMB_after_carbs)); mProfile.put("allowSMB_with_high_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.allowSMB_with_high_temptarget); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); From e50c6fc8ed51a8db40b52e2a7541e03f54a5165b Mon Sep 17 00:00:00 2001 From: AdrianLxM Date: Mon, 25 Dec 2017 20:39:14 +0100 Subject: [PATCH 040/171] RS BLE modifications --- .../plugins/PumpDanaRS/services/BLEComm.java | 2 ++ .../androidaps/queue/QueueThread.java | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java index f8d18a4d22..15b57231ea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java @@ -164,6 +164,8 @@ public class BLEComm { scheduledDisconnection = null; if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("disconnect not possible: (mBluetoothAdapter == null) " + (mBluetoothAdapter == null)); + log.debug("disconnect not possible: (mBluetoothGatt == null) " + (mBluetoothGatt == null)); return; } setCharacteristicNotification(getUARTReadBTGattChar(), false); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java index dd02c5e973..c0915d96e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java @@ -16,6 +16,8 @@ import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventDismissBolusprogressIfRunning; +import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.androidaps.queue.events.EventQueueChanged; import info.nightscout.utils.SP; @@ -52,12 +54,6 @@ public class QueueThread extends Thread { while (true) { PumpInterface pump = ConfigBuilderPlugin.getActivePump(); long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000; - if (pump.isConnecting()) { - log.debug("QUEUE: connecting " + secondsElapsed); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); - SystemClock.sleep(1000); - continue; - } if (!pump.isConnected() && secondsElapsed > Constants.PUMP_MAX_CONNECTION_TIME_IN_SECONDS) { MainApp.bus().post(new EventDismissBolusprogressIfRunning(new PumpEnactResult())); @@ -74,19 +70,37 @@ public class QueueThread extends Thread { //write time SP.putLong(R.string.key_btwatchdog_lastbark, System.currentTimeMillis()); //toggle BT + pump.stopConnecting(); + pump.disconnect("watchdog"); + SystemClock.sleep(1000); BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter.disable(); SystemClock.sleep(1000); mBluetoothAdapter.enable(); SystemClock.sleep(1000); //start over again once after watchdog barked + //Notification notification = new Notification(Notification.OLD_NSCLIENT, "Watchdog", Notification.URGENT); + //MainApp.bus().post(new EventNewNotification(notification)); connectionStartTime = lastCommandTime = System.currentTimeMillis(); + pump.connect("watchdog"); } else { queue.clear(); + log.debug("QUEUE: no connection possible"); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + pump.disconnect("Queue empty"); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); return; } } + if (pump.isConnecting()) { + log.debug("QUEUE: connecting " + secondsElapsed); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); + SystemClock.sleep(1000); + continue; + } + + if (!pump.isConnected()) { log.debug("QUEUE: connect"); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); From a08a6cbb70aa6b446f96adac6ae8579ec3af7af2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Wed, 3 Jan 2018 22:59:26 +0100 Subject: [PATCH 041/171] patch SMB determine_basal to not produce error --- app/src/main/assets/OpenAPSSMB/determine-basal.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 19ebbb4e70..fb80fa7963 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -213,6 +213,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var lastTempAge; if (typeof iob_data.lastTemp !== 'undefined' ) { lastTempAge = round(( new Date().getTime() - iob_data.lastTemp.date ) / 60000); // in minutes + // } ---- added to not produce errors + } else { + lastTempAge = 0; } //console.error("currenttemp:",currenttemp,"lastTemp:",JSON.stringify(iob_data.lastTemp),"lastTempAge:",lastTempAge,"m"); tempModulus = (lastTempAge + currenttemp.duration) % 30; From 144315ba616ca3bd8583a70ebe7c601afc55b889 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 8 Jan 2018 14:19:21 +0100 Subject: [PATCH 042/171] pass enableSMB_always to determinebasal --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 1 + 1 file changed, 1 insertion(+) 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 a11ba438ce..69671ea70a 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 @@ -243,6 +243,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); + mProfile.put("enableSMB_always", SMBDefaults.enableSMB_always); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.APS) || SMBDefaults.enableSMB_after_carbs)); From 70e22487b256dd7741b9b537536565d9642e3c7f Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 9 Jan 2018 19:47:32 +0100 Subject: [PATCH 043/171] fix canceling temp in open loop mode. credits to @warstar2187 --- .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 a0f7483f34..be600ee9e2 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 @@ -68,7 +68,7 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { @Override public String getNameShort() { String name = MainApp.sResources.getString(R.string.smb_shortname); - if (!name.trim().isEmpty()){ + if (!name.trim().isEmpty()) { //only if translation exists return name; } @@ -179,8 +179,8 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { double maxIob = SP.getDouble("openapsma_max_iob", 1.5d); double maxBasal = SP.getDouble("openapsma_max_basal", 1d); - double minBg = Profile.toMgdl(profile.getTargetLow(), units); - double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); + double minBg = Profile.toMgdl(profile.getTargetLow(), units); + double maxBg = Profile.toMgdl(profile.getTargetHigh(), units); double targetBg = (minBg + maxBg) / 2; minBg = Round.roundTo(minBg, 0.1d); @@ -251,10 +251,13 @@ public class OpenAPSSMBPlugin implements PluginBase, APSInterface { determineBasalResultSMB.changeRequested = false; // limit requests on openloop mode if (!MainApp.getConfigBuilder().isClosedModeEnabled()) { - if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()) < 0.1) + if (MainApp.getConfigBuilder().isTempBasalInProgress() && determineBasalResultSMB.rate == 0 && determineBasalResultSMB.duration == 0) { + // going to cancel + } else if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory()) < 0.1) { determineBasalResultSMB.changeRequested = false; - if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - pump.getBaseBasalRate()) < 0.1) + } else if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultSMB.rate - pump.getBaseBasalRate()) < 0.1) { determineBasalResultSMB.changeRequested = false; + } } determineBasalResultSMB.iob = iobArray[0]; From d4365a235216d8e93cd07637e343df287ac0f143 Mon Sep 17 00:00:00 2001 From: "Markus M. May" Date: Wed, 10 Jan 2018 22:11:20 +0100 Subject: [PATCH 044/171] Add a unit test for Insulin Plugin --- .../info/nightscout/androidaps/data/Iob.java | 22 +++ .../Insulin/InsulinOrefBasePlugin.java | 31 ++++- .../Insulin/InsulinOrefBasePluginTest.java | 131 ++++++++++++++++++ 3 files changed, 177 insertions(+), 7 deletions(-) create mode 100644 app/src/test/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePluginTest.java diff --git a/app/src/main/java/info/nightscout/androidaps/data/Iob.java b/app/src/main/java/info/nightscout/androidaps/data/Iob.java index ee70699604..762352782b 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Iob.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Iob.java @@ -12,4 +12,26 @@ public class Iob { activityContrib += iob.activityContrib; return this; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Iob iob = (Iob) o; + + if (Double.compare(iob.iobContrib, iobContrib) != 0) return false; + return Double.compare(iob.activityContrib, activityContrib) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(iobContrib); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(activityContrib); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java index cbe5f2ff64..b91eefdc9c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.plugins.Insulin; +import com.squareup.otto.Bus; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Iob; @@ -44,38 +46,53 @@ public abstract class InsulinOrefBasePlugin implements PluginBase, InsulinInterf return true; } + public Bus getBus() { + return MainApp.bus(); + } + @Override public double getDia() { double dia = getUserDefinedDia(); if(dia >= MIN_DIA){ return dia; } else { - if((System.currentTimeMillis() - lastWarned) > 60*1000) { - lastWarned = System.currentTimeMillis(); - Notification notification = new Notification(Notification.SHORT_DIA, String.format(MainApp.sResources.getString(R.string.dia_too_short), dia, MIN_DIA), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); - } + sendShortDiaNotification(dia); return MIN_DIA; } } + void sendShortDiaNotification(double dia) { + if((System.currentTimeMillis() - lastWarned) > 60*1000) { + lastWarned = System.currentTimeMillis(); + Notification notification = new Notification(Notification.SHORT_DIA, String.format(this.getNotificationPattern(), dia, MIN_DIA), Notification.URGENT); + this.getBus().post(new EventNewNotification(notification)); + } + } + + public String getNotificationPattern() { + return MainApp.sResources.getString(R.string.dia_too_short); + } + public double getUserDefinedDia() { return MainApp.getConfigBuilder().getProfile() != null ? MainApp.getConfigBuilder().getProfile().getDia() : MIN_DIA; } + public Iob iobCalcForTreatment(Treatment treatment, long time) { + return this.iobCalcForTreatment(treatment, time, 0d); + } + @Override public Iob iobCalcForTreatment(Treatment treatment, long time, double dia) { Iob result = new Iob(); int peak = getPeak(); - if (treatment.insulin != 0d) { long bolusTime = treatment.date; double t = (time - bolusTime) / 1000d / 60d; - double td = getDia()*60; //getDIA() always > 5 + double td = getDia()*60; //getDIA() always >= MIN_DIA double tp = peak; // force the IOB to 0 if over DIA hours have passed diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePluginTest.java new file mode 100644 index 0000000000..de128175cc --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePluginTest.java @@ -0,0 +1,131 @@ +package info.nightscout.androidaps.plugins.Insulin; + +import org.junit.Before; +import org.junit.Test; + +import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.Treatment; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Created by triplem on 07.01.18. + */ + +public class InsulinOrefBasePluginTest extends InsulinOrefBasePlugin { + + private int peak; + private double dia; + + private boolean shortDiaNotificationSend; + + @Before + public void setUp() throws Exception { + this.peak = 0; + this.dia = InsulinOrefBasePlugin.MIN_DIA; + this.shortDiaNotificationSend = false; + } + + @Test + public void testGetDia() throws Exception { + assertEquals(InsulinOrefBasePlugin.MIN_DIA, this.getDia(), 0); + + this.dia = InsulinOrefBasePlugin.MIN_DIA + 1; + assertEquals(InsulinOrefBasePlugin.MIN_DIA + 1, this.getDia(), 0); + + this.dia = InsulinOrefBasePlugin.MIN_DIA - 1; + assertEquals(InsulinOrefBasePlugin.MIN_DIA, this.getDia(), 0); + assertTrue(this.shortDiaNotificationSend); + } + + @Test + public void testIobCalcForTreatment() { + Treatment treatment = new Treatment(); + Iob expected = new Iob(); + + assertEquals(expected, this.iobCalcForTreatment(treatment, 0, 0d)); + + this.peak = 30; + long time = System.currentTimeMillis(); + treatment.date = time - 1 * 60 * 60 * 1000; // 1 hour + treatment.insulin = 10d; + + assertEquals(3.92, this.iobCalcForTreatment(treatment, time).iobContrib, 0.1); + } + + + /** + * this method is implemented to allow tests of the iobCalcForTreatment calculation + * @return + */ + @Override + int getPeak() { + return this.peak; + } + + /** + * Userdefined Dia is implemented to allow tests of the getDia method + * + * @return + */ + public double getUserDefinedDia() { + return this.dia; + } + + void sendShortDiaNotification(double dia) { + this.shortDiaNotificationSend = true; + } + + + // the following methods are implemented, but not needed... + @Override + String commentStandardText() { + return null; + } + + @Override + public String getFragmentClass() { + return null; + } + + @Override + public int getId() { + return 0; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getFriendlyName() { + return null; + } + + @Override + public boolean isEnabled(int type) { + return false; + } + + @Override + public boolean isVisibleInTabs(int type) { + return false; + } + + @Override + public void setFragmentEnabled(int type, boolean fragmentEnabled) { + + } + + @Override + public void setFragmentVisible(int type, boolean fragmentVisible) { + + } + + @Override + public int getPreferencesId() { + return 0; + } +} From 390cef51748a39104d749a77e4d637ba55439684 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 13 Jan 2018 22:49:58 +0100 Subject: [PATCH 045/171] remainingCATime fix --- app/src/main/assets/OpenAPSSMB/determine-basal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 19ebbb4e70..33718325b5 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -442,7 +442,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // 20 g/h means that anything <= 60g will get a remainingCATimeMin, 80g will get 4h, and 120g 6h // when actual absorption ramps up it will take over from remainingCATime var assumedCarbAbsorptionRate = 20; // g/h; maximum rate to assume carbs will absorb if no CI observed - var remainingCATime; + var remainingCATime = remainingCATimeMin; // added by mike https://github.com/openaps/oref0/issues/884 if (meal_data.carbs) { // if carbs * assumedCarbAbsorptionRate > remainingCATimeMin, raise it // so <= 90g is assumed to take 3h, and 120g=4h From 0e4c72e790a8dbac5e8403f1093f0b29ae09a9fd Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sat, 13 Jan 2018 01:11:27 +0100 Subject: [PATCH 046/171] Fix check for DexcomG5 Bg source. --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 69671ea70a..369df21f59 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 @@ -246,7 +246,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_always", SMBDefaults.enableSMB_always); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); - mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.APS) || SMBDefaults.enableSMB_after_carbs)); + mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_after_carbs)); mProfile.put("allowSMB_with_high_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.allowSMB_with_high_temptarget); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); From 0569addc55ada51850281cff0e156a35d48abd72 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Mon, 15 Jan 2018 14:03:37 +0100 Subject: [PATCH 047/171] Set enableSMB_always if using a non-noisy BG source. --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 369df21f59..7656bb0245 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 @@ -243,7 +243,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); - mProfile.put("enableSMB_always", SMBDefaults.enableSMB_always); + mProfile.put("enableSMB_always", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_always); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_after_carbs)); From 54ac28bdf983d7da58c027470c8930d81ebfd2ed Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 15 Jan 2018 16:56:30 +0100 Subject: [PATCH 048/171] typo fix --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7656bb0245..7021710d0c 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 @@ -243,7 +243,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); - mProfile.put("enableSMB_always", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_always); + mProfile.put("enableSMB_always", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_always)); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_COB); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_use_smb, false) && SMBDefaults.enableSMB_with_temptarget); mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_use_smb, false) && (SourceDexcomG5Plugin.getPlugin().isEnabled(PluginBase.BGSOURCE) || SMBDefaults.enableSMB_after_carbs)); From 3962808626f657529d0072ac35327286b40dd71b Mon Sep 17 00:00:00 2001 From: AdrianLxM Date: Sun, 21 Jan 2018 19:05:04 +0100 Subject: [PATCH 049/171] minimum of 30 min for unreachable alert --- .../info/nightscout/androidaps/MainActivity.java | 15 ++++++++++++++- app/src/main/res/xml/pref_others.xml | 11 ++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index b702897ba4..e77be36ba6 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -82,7 +82,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe Manifest.permission.WRITE_EXTERNAL_STORAGE}, CASE_STORAGE); } askForBatteryOptimizationPermission(); - checkUpgradeToProfileTarget(); + doMigrations(); if (Config.logFunctionCalls) log.debug("onCreate"); @@ -163,6 +163,19 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } } + private void doMigrations() { + + checkUpgradeToProfileTarget(); + + // guarantee that the unreachable threshold is at least 30 and of type String + // Added in 1.57 at 21.01.2018 + Integer unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30); + SP.remove(R.string.key_pump_unreachable_threshold); + if(unreachable_threshold < 30) unreachable_threshold = 30; + SP.putString(R.string.key_pump_unreachable_threshold, unreachable_threshold.toString()); + } + + private void checkUpgradeToProfileTarget() { // TODO: can be removed in the future boolean oldKeyExists = SP.contains("openapsma_min_bg"); if (oldKeyExists) { diff --git a/app/src/main/res/xml/pref_others.xml b/app/src/main/res/xml/pref_others.xml index 548b99e5b9..ba16f09c2f 100644 --- a/app/src/main/res/xml/pref_others.xml +++ b/app/src/main/res/xml/pref_others.xml @@ -1,5 +1,6 @@ - + @@ -76,12 +77,16 @@ android:defaultValue="true" android:key="@string/key_enable_pump_unreachable_alert" android:title="@string/enable_pump_unreachable_alert" /> - + android:title="@string/pump_unreachable_threshold"> + Date: Mon, 22 Jan 2018 17:18:18 +0100 Subject: [PATCH 050/171] more autosens logging --- .../plugins/IobCobCalculator/IobCobCalculatorPlugin.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index fbbbe43ccc..3522d3fc1e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -33,6 +33,7 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; +import info.nightscout.utils.DateUtil; /** * Created by mike on 24.04.2017. @@ -580,11 +581,11 @@ public class IobCobCalculatorPlugin implements PluginBase { return null; } if (data.time < System.currentTimeMillis() - 11 * 60 * 1000) { - log.debug("AUTOSENSDATA null: data is old (" + reason + ")"); + log.debug("AUTOSENSDATA null: data is old (" + reason + ") size()=" + autosensDataTable.size() + " lastdata=" + DateUtil.dateAndTimeString(data.time)); return null; } else { if (data == null) - log.debug("AUTOSENSDATA null: data == null (" + " " + reason + ")"); + log.debug("AUTOSENSDATA null: data == null (" + " " + reason + ") size()=" + autosensDataTable.size() + " lastdata=" + DateUtil.dateAndTimeString(data.time)); return data; } } From d033407ba75865dd191d2c4af72419335b7da6d6 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 22 Jan 2018 19:12:22 +0100 Subject: [PATCH 051/171] fix rendering treatments with duration --- .../plugins/Overview/graphData/GraphData.java | 2 +- .../PointsWithLabelGraphSeries.java | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index e76714c5b2..5d5a0832d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -253,7 +253,7 @@ public class GraphData { } // Careportal - List careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, true); + List careportalEvents = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true); for (int tx = 0; tx < careportalEvents.size(); tx++) { DataPointWithLabelInterface t = careportalEvents.get(tx); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java index 7606a71f17..26892372d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java @@ -141,9 +141,6 @@ public class PointsWithLabelGraphSeries e Iterator values = getValues(minX, maxX); // draw background - double lastEndY = 0; - double lastEndX = 0; - // draw data double diffY = maxY - minY; @@ -154,9 +151,8 @@ public class PointsWithLabelGraphSeries e float graphLeft = graphView.getGraphContentLeft(); float graphTop = graphView.getGraphContentTop(); - lastEndY = 0; - lastEndX = 0; - float firstX = 0; + float scaleX = (float) (graphWidth / diffX); + int i=0; while (values.hasNext()) { E value = values.next(); @@ -171,9 +167,6 @@ public class PointsWithLabelGraphSeries e double ratX = valX / diffX; double x = graphWidth * ratX; - double orgX = x; - double orgY = y; - // overdraw boolean overdraw = false; if (x > graphWidth) { // end right @@ -185,6 +178,14 @@ public class PointsWithLabelGraphSeries e if (y > graphHeight) { // end top overdraw = true; } + + long duration = value.getDuration(); + float endWithDuration = (float) (x + duration * scaleX + graphLeft + 1); + // cut off to graph start if needed + if (x < 0 && endWithDuration > 0) { + x = 0; + } + /* Fix a bug that continue to show the DOT after Y axis */ if(x < 0) { overdraw = true; @@ -195,8 +196,8 @@ public class PointsWithLabelGraphSeries e registerDataPoint(endX, endY, value); float xpluslength = 0; - if (value.getDuration() > 0) { - xpluslength = endX + Math.min((float) (value.getDuration() * graphWidth / diffX), graphLeft + graphWidth); + if (duration > 0) { + xpluslength = Math.min(endWithDuration, graphLeft + graphWidth); } // draw data point From 52e4496add447eea216d1d66b2ef6f3c56d744cc Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 22 Jan 2018 21:49:01 +0100 Subject: [PATCH 052/171] Careportal DB browser --- .../nightscout/androidaps/data/Profile.java | 1 + .../androidaps/db/CareportalEvent.java | 11 + .../androidaps/db/DatabaseHelper.java | 16 +- .../Treatments/TreatmentsFragment.java | 9 + .../TreatmentsCareportalFragment.java | 192 ++++++++++++++++++ .../layout/treatments_careportal_fragment.xml | 28 +++ .../res/layout/treatments_careportal_item.xml | 104 ++++++++++ .../main/res/layout/treatments_fragment.xml | 10 + 8 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java create mode 100644 app/src/main/res/layout/treatments_careportal_fragment.xml create mode 100644 app/src/main/res/layout/treatments_careportal_item.xml diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 5235545362..7bb693ef23 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -239,6 +239,7 @@ public class Profile { // if pump not available (at start) // do not store converted array basal_v = null; + isValidated = false; } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index b80f4f932e..8c55034c9f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -191,6 +191,17 @@ public class CareportalEvent implements DataPointWithLabelInterface { return Translator.translate(eventType); } + public String getNotes() { + try { + JSONObject object = new JSONObject(json); + if (object.has("notes")) + return object.getString("notes"); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + return ""; + } + @Override public long getDuration() { try { diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 69bc542dae..8613f18ab5 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -1473,7 +1473,21 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } catch (SQLException e) { log.error("Unhandled exception", e); } - return new ArrayList(); + return new ArrayList<>(); + } + + public List getCareportalEventsFromTime(boolean ascending) { + try { + List careportalEvents; + QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); + queryBuilder.orderBy("date", ascending); + PreparedQuery preparedQuery = queryBuilder.prepare(); + careportalEvents = getDaoCareportalEvents().query(preparedQuery); + return careportalEvents; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); } public void deleteCareportalEventById(String _id) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index 6fcbcfe951..b10ad96697 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -20,6 +20,7 @@ import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Treatments.fragments.TreatmentsBolusFragment; +import info.nightscout.androidaps.plugins.Treatments.fragments.TreatmentsCareportalFragment; import info.nightscout.androidaps.plugins.Treatments.fragments.TreatmentsExtendedBolusesFragment; import info.nightscout.androidaps.plugins.Treatments.fragments.TreatmentsProfileSwitchFragment; import info.nightscout.androidaps.plugins.Treatments.fragments.TreatmentsTempTargetFragment; @@ -33,6 +34,7 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli TextView tempBasalsTab; TextView tempTargetTab; TextView profileSwitchTab; + TextView careportalTab; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -45,11 +47,13 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli tempBasalsTab = (TextView) view.findViewById(R.id.treatments_tempbasals); tempTargetTab = (TextView) view.findViewById(R.id.treatments_temptargets); profileSwitchTab = (TextView) view.findViewById(R.id.treatments_profileswitches); + careportalTab = (TextView) view.findViewById(R.id.treatments_careportal); treatmentsTab.setOnClickListener(this); extendedBolusesTab.setOnClickListener(this); tempBasalsTab.setOnClickListener(this); tempTargetTab.setOnClickListener(this); profileSwitchTab.setOnClickListener(this); + careportalTab.setOnClickListener(this); setFragment(new TreatmentsBolusFragment()); setBackgroundColorOnSelected(treatmentsTab); @@ -87,6 +91,10 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli setFragment(new TreatmentsProfileSwitchFragment()); setBackgroundColorOnSelected(profileSwitchTab); break; + case R.id.treatments_careportal: + setFragment(new TreatmentsCareportalFragment()); + setBackgroundColorOnSelected(careportalTab); + break; } } @@ -104,6 +112,7 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli tempBasalsTab.setBackgroundColor(MainApp.sResources.getColor(R.color.defaultbackground)); tempTargetTab.setBackgroundColor(MainApp.sResources.getColor(R.color.defaultbackground)); profileSwitchTab.setBackgroundColor(MainApp.sResources.getColor(R.color.defaultbackground)); + careportalTab.setBackgroundColor(MainApp.sResources.getColor(R.color.defaultbackground)); selected.setBackgroundColor(MainApp.sResources.getColor(R.color.tabBgColorSelected)); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java new file mode 100644 index 0000000000..7218198b43 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java @@ -0,0 +1,192 @@ +package info.nightscout.androidaps.plugins.Treatments.fragments; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Paint; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.CardView; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.Services.Intents; +import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.events.EventCareportalEventChange; +import info.nightscout.androidaps.plugins.Common.SubscriberFragment; +import info.nightscout.androidaps.plugins.NSClientInternal.UploadQueue; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.NSUpload; +import info.nightscout.utils.SP; +import info.nightscout.utils.Translator; + +/** + * Created by mike on 13/01/17. + */ + +public class TreatmentsCareportalFragment extends SubscriberFragment implements View.OnClickListener { + + RecyclerView recyclerView; + LinearLayoutManager llm; + Button refreshFromNS; + + Context context; + + public class RecyclerViewAdapter extends RecyclerView.Adapter { + + List careportalEventList; + + RecyclerViewAdapter(List careportalEventList) { + this.careportalEventList = careportalEventList; + } + + @Override + public CareportalEventsViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.treatments_careportal_item, viewGroup, false); + CareportalEventsViewHolder CareportalEventsViewHolder = new CareportalEventsViewHolder(v); + return CareportalEventsViewHolder; + } + + @Override + public void onBindViewHolder(CareportalEventsViewHolder holder, int position) { + CareportalEvent careportalEvent = careportalEventList.get(position); + holder.ns.setVisibility(NSUpload.isIdValid(careportalEvent._id) ? View.VISIBLE : View.GONE); + holder.date.setText(DateUtil.dateAndTimeString(careportalEvent.date)); + holder.note.setText(careportalEvent.getNotes()); + holder.type.setText(Translator.translate(careportalEvent.eventType)); + holder.remove.setTag(careportalEvent); + } + + @Override + public int getItemCount() { + return careportalEventList.size(); + } + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + } + + public class CareportalEventsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + CardView cv; + TextView date; + TextView type; + TextView note; + TextView remove; + TextView ns; + + CareportalEventsViewHolder(View itemView) { + super(itemView); + cv = (CardView) itemView.findViewById(R.id.careportal_cardview); + date = (TextView) itemView.findViewById(R.id.careportal_date); + type = (TextView) itemView.findViewById(R.id.careportal_type); + note = (TextView) itemView.findViewById(R.id.careportal_note); + ns = (TextView) itemView.findViewById(R.id.ns_sign); + remove = (TextView) itemView.findViewById(R.id.careportal_remove); + remove.setOnClickListener(this); + remove.setPaintFlags(remove.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + } + + @Override + public void onClick(View v) { + final CareportalEvent careportalEvent = (CareportalEvent) v.getTag(); + switch (v.getId()) { + case R.id.careportal_remove: + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(MainApp.sResources.getString(R.string.confirmation)); + builder.setMessage(MainApp.sResources.getString(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(careportalEvent.date)); + builder.setPositiveButton(MainApp.sResources.getString(R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + MainApp.getDbHelper().delete(careportalEvent); + } + }); + builder.setNegativeButton(MainApp.sResources.getString(R.string.cancel), null); + builder.show(); + break; + } + } + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.treatments_careportal_fragment, container, false); + + recyclerView = (RecyclerView) view.findViewById(R.id.careportal_recyclerview); + recyclerView.setHasFixedSize(true); + llm = new LinearLayoutManager(view.getContext()); + recyclerView.setLayoutManager(llm); + + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)); + recyclerView.setAdapter(adapter); + + refreshFromNS = (Button) view.findViewById(R.id.careportal_refreshfromnightscout); + refreshFromNS.setOnClickListener(this); + + context = getContext(); + + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); + if (nsUploadOnly) + refreshFromNS.setVisibility(View.GONE); + + updateGUI(); + return view; + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.careportal_refreshfromnightscout: + AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); + builder.setTitle(this.getContext().getString(R.string.confirmation)); + builder.setMessage(this.getContext().getString(R.string.refresheventsfromnightscout) + " ?"); + builder.setPositiveButton(this.getContext().getString(R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + MainApp.getDbHelper().resetCareportalEvents(); + Intent restartNSClient = new Intent(Intents.ACTION_RESTART); + MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + } + }); + builder.setNegativeButton(this.getContext().getString(R.string.cancel), null); + builder.show(); + break; + } + + } + + @Subscribe + public void onStatusEvent(final EventCareportalEventChange ev) { + updateGUI(); + } + + @Override + protected void updateGUI() { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)), false); + } + }); + } +} diff --git a/app/src/main/res/layout/treatments_careportal_fragment.xml b/app/src/main/res/layout/treatments_careportal_fragment.xml new file mode 100644 index 0000000000..710afed7bb --- /dev/null +++ b/app/src/main/res/layout/treatments_careportal_fragment.xml @@ -0,0 +1,28 @@ + + + + +