Просмотр исходного кода

Update Middleware. Now accurate with scheduled basal rates. Added scheduled basal rate insulin if not enough pump data available (< 23.5 h).

(cherry picked from commit 0d574d9a47927f16772f9d5834f96780f5659a9d)
Jon Mårtensson 4 лет назад
Родитель
Сommit
577ad0910a
1 измененных файлов с 199 добавлено и 110 удалено
  1. 199 110
      FreeAPS/Resources/javascript/middleware/determine_basal.js

+ 199 - 110
FreeAPS/Resources/javascript/middleware/determine_basal.js

@@ -10,24 +10,191 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
     const adjustmentFactor = preferences.adjustmentFactor;
     const currentMinTarget = profile.min_bg;
     var exerciseSetting = false;
-    var enoughData = false;
     var pumpData = 0;
     var log = "";
     var logTDD = "";
     var logBasal = "";
     var logBolus = "";
     var logTempBasal = "";
+    var dataLog = "";
+    var logOutPut = "";
     var current = 0;
-    // If you have not set this to 0.05 in FAX settings (Omnipod), this will be set to 0.1 in code.
-    var minimalDose = profile.bolus_increment;
     var TDD = 0;
     var insulin = 0;
     var tempInsulin = 0;
     var bolusInsulin = 0;
     var scheduledBasalInsulin = 0;
-    var incrementsRaw = 0;
-    var incrementsRounded = 0;
     var quota = 0;
+    //
+    //
+    function round(value, precision) {
+        var multiplier = Math.pow(10, precision || 0);
+        return Math.round(value * multiplier) / multiplier;
+    }
+    
+    function addTimeFromDate(objDate, _hours) {
+        console.log("objDate" + objDate);
+        var numberOfMlSeconds = objDate.getTime();
+        var addMlSeconds = _hours * 36e5;
+        var newDateObj = new Date(numberOfMlSeconds + addMlSeconds);
+        console.log("newDateObj" + newDateObj);
+        return newDateObj;
+    }
+      
+    function subtractTimeFromDate(date, hours_) {
+        var miSeconds = date.getTime();
+        console.log("old date put into function SubtractTimeFromDate: " + miSeconds);
+        var addMiSeconds = hours_ * 36e5;
+        var new_date = new Date(miSeconds - addMiSeconds);
+        console.log("new date: " + new_date);
+        return new_date;
+    }
+        
+    function accountForIncrements(insulin) {
+        // If you have not set this to 0.05 in FAX settings (Omnipod), this will be set to 0.1 (Medtronic) in code.
+        var minimalDose = profile.bolus_increment;
+        if (minimalDose != 0.05) {
+            minimalDose = 0.1;
+        }
+        var incrementsRaw = insulin / minimalDose;
+        if (incrementsRaw >= 1) {
+            var incrementsRounded = Math.floor(incrementsRaw);
+            return round(incrementsRounded * minimalDose, 5);
+        } else { return 0; }
+    }
+    
+    function addZero(i) {
+        if (i < 10) {i = "0" + i}
+        return i;
+      }
+       
+    function timeDifferenceOfString(string1, string2) {
+        //Base time strings are in "00:00:00" format
+        var time1 = new Date("1/1/1999 " + string1);
+        var time2 = new Date("1/1/1999 " + string2);
+        var miS1 = time1.getTime();
+        var miS2 = time2.getTime();
+        var difference = (miS1 - miS2) / 36e5;
+        
+        return difference;
+    }
+
+    function calcScheduledBasalInsulin(lastRealTempTime, addedLastTempTime) {
+        
+        var totalInsulin = 0;
+        var old = addedLastTempTime;
+        var totalDuration = (lastRealTempTime - addedLastTempTime) / 36e5;
+        var basDuration = 0;
+        var totalDurationCheck = totalDuration;
+        var durationCurrentSchedule = 0;
+        var pp = 0;
+        
+        do {
+
+            if (totalDuration > 0) {
+                
+                let hour = addZero(old.getHours());
+                let minutes = addZero(old.getMinutes());
+                let seconds = "00";
+                let string = hour + ":" + minutes + ":" + seconds;
+                var baseTime_ = string;
+                
+                //Default basalrate in case none is found...
+                var basalScheduledRate_ = profile.basalprofile[0].start;
+                for (let m = 0; m < profile.basalprofile.length; m++) {
+                    
+                    var timeToTest = profile.basalprofile[m].start;
+                    
+                    if (baseTime_ == timeToTest) {
+                        
+                        if (m + 1 < profile.basalprofile.length) {
+                            let end = profile.basalprofile[m+1].start;
+                            let start = profile.basalprofile[m].start;
+                                                        
+                            durationCurrentSchedule = timeDifferenceOfString(end, start);
+                            
+                            if (totalDuration >= durationCurrentSchedule) {
+                                basDuration = durationCurrentSchedule;
+                            } else if (totalDuration < durationCurrentSchedule) {
+                                basDuration = totalDuration;
+                            }
+                            
+                        }
+                        else if (m + 1 == profile.basalprofile.length) {
+                            let end = profile.basalprofile[0].start;
+                            let start = profile.basalprofile[m].start;
+                            // First schedule is 00:00:00. Changed places of start and end here.
+                            durationCurrentSchedule = 24 - (timeDifferenceOfString(start, end));
+                            
+                            if (totalDuration >= durationCurrentSchedule) {
+                                basDuration = durationCurrentSchedule;
+                            } else if (totalDuration < durationCurrentSchedule) {
+                                basDuration = totalDuration;
+                            }
+                        
+                        }
+                        basalScheduledRate_ = profile.basalprofile[m].rate;
+                        totalInsulin += accountForIncrements(basalScheduledRate_ * basDuration);
+                        totalDuration -= basDuration;
+                        console.log("scheduled insulin added: " + accountForIncrements(basalScheduledRate_ * basDuration) + ", . Bas duration: " + basDuration + " . Base Rate: " + basalScheduledRate_ + " U/h" + ". Time :" + baseTime_);
+                        // Move clock to new date
+                        old = addTimeFromDate(old, basDuration);
+                    }
+                    
+                    else if (baseTime_ > timeToTest) {
+
+                        if (m + 1 < profile.basalprofile.length) {
+                            var timeToTest2 = profile.basalprofile[m+1].start
+                         
+                            if (baseTime_ < timeToTest2) {
+                                
+                               //  durationCurrentSchedule = timeDifferenceOfString(end, start);
+                               durationCurrentSchedule = timeDifferenceOfString(timeToTest2, baseTime_);
+                            
+                                if (totalDuration >= durationCurrentSchedule) {
+                                    basDuration = durationCurrentSchedule;
+                                } else if (totalDuration < durationCurrentSchedule) {
+                                    basDuration = totalDuration;
+                                }
+                                 
+                                basalScheduledRate_ = profile.basalprofile[m].rate;
+                                totalInsulin += accountForIncrements(basalScheduledRate_ * basDuration);
+                                totalDuration -= basDuration;
+                                console.log("scheduled insulin added: " + accountForIncrements(basalScheduledRate_ * basDuration) + ", . Bas duration: " + basDuration + " . Base Rate: " + basalScheduledRate_ + " U/h" + ". Time :" + baseTime_);
+                                // Move clock to new date
+                                old = addTimeFromDate(old, basDuration);
+                            }
+                        }
+                    
+                        else if (m == profile.basalprofile.length - 1) {
+                            // let start = profile.basalprofile[m].start;
+                            let start = baseTime_;
+                            // First schedule is 00:00:00. Changed places of start and end here.
+                            durationCurrentSchedule = timeDifferenceOfString("23:59:59", start);
+                            
+                            if (totalDuration >= durationCurrentSchedule) {
+                                basDuration = durationCurrentSchedule;
+                            } else if (totalDuration < durationCurrentSchedule) {
+                                basDuration = totalDuration;
+                            }
+                            
+                            basalScheduledRate_ = profile.basalprofile[m].rate;
+                            totalInsulin += accountForIncrements(basalScheduledRate_ * basDuration);
+                            totalDuration -= basDuration;
+                            console.log("scheduled insulin added: " + accountForIncrements(basalScheduledRate_ * basDuration) + ", . Bas duration: " + basDuration + " . Base Rate: " + basalScheduledRate_ + " U/h" + ". Time :" + baseTime_);
+                            // Move clock to new date
+                            old = addTimeFromDate(old, basDuration);
+                        }
+                    }
+                }
+            }
+            //totalDurationCheck to avoid infinite loop
+        } while (totalDuration > 0 && totalDuration < totalDurationCheck);
+        
+        // amount of insulin according to pump basal rate schedules
+        return totalInsulin;
+    }
+    //------------- End of added functions ----------------------------------------------------
     
     if (profile.high_temptarget_raises_sensitivity == true || profile.exercise_mode == true) {
         exerciseSetting = true;
@@ -45,23 +212,25 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
         log = "Dynamic ISF temporarily off due to a high temp target/exercising. Current min target: " + currentMinTarget;
     }
     
-    // Check that there is enough pump history data (>23 hours) for TDD calculation, else end this middleware.
+    // Check that there is enough pump history data (>23.5 hours) for TDD calculation, else estimate a TDD using using missing hours with scheduled basal rates.
     if (chrisFormula == true) {
         let ph_length = pumphistory.length;
-        let endDate = new Date(pumphistory[ph_length-1].timestamp);
-        let startDate = new Date(pumphistory[0].timestamp);
+        var endDate = new Date(pumphistory[ph_length-1].timestamp);
+        var startDate = new Date(pumphistory[0].timestamp);
         // If latest pump event is a temp basal
         if (pumphistory[0]._type == "TempBasalDuration") {
             startDate = new Date();
         }
-        // > 23 hours
         pumpData = (startDate - endDate) / 36e5;
-        if (pumpData >= 23) {
-            enoughData = true;
-        } else {
-                chrisFormula = false;
-                return "Dynamic ISF is temporarily off. 24 hours of data is required for a correct TDD calculation. Currently only " + pumpData.toPrecision(3) + " hours of pump history data available.";
-        }
+        
+        if (pumpData < 23.5) {
+            var missingHours = 24 - pumpData;
+            // Makes new end date for a total time duration of exakt 24 hour.
+            var endDate_ = subtractTimeFromDate(endDate, missingHours);
+            // endDate - endDate_ = missingHours
+            scheduledBasalInsulin = calcScheduledBasalInsulin(endDate, endDate_);
+            dataLog = "24 hours of data is required for an accurate TDD calculation. Currently only " + pumpData.toPrecision(3) + " hours of pump history data are available. Using your pump scheduled basals to fill in the missing hours. Scheduled basals added: " + scheduledBasalInsulin.toPrecision(5) + " U. ";
+        } else { dataLog = ""; }
     }
     
     // Calculate TDD --------------------------------------
@@ -73,9 +242,6 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
     }
     
     // Temp basals:
-    if (minimalDose != 0.05) {
-        minimalDose = 0.1;
-    }
     for (let j = 1; j < pumphistory.length; j++) {
         if (pumphistory[j]._type == "TempBasal" && pumphistory[j].rate > 0) {
             current = j;
@@ -102,21 +268,14 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
             }
             
             insulin = quota * duration;
-                
-            // Account for smallest possible pump dosage
-            incrementsRaw = insulin / minimalDose;
-            if (incrementsRaw >= 1) {
-                incrementsRounded = Math.floor(incrementsRaw);
-                insulin = incrementsRounded * minimalDose;
-                tempInsulin += insulin;
-            } else { insulin = 0}
+            tempInsulin += accountForIncrements(insulin);
             j = current;
         }
     }
     //  Check and count for when basals are delivered with a scheduled basal rate or an Autotuned basal rate.
     //  1. Check for 0 temp basals with 0 min duration. This is for when ending a manual temp basal and (perhaps) continuing in open loop for a while.
     //  2. Check for temp basals that completes. This is for when disconected from link/iphone, or when in open loop.
-    //  3. Account for a punp suspension.
+    //  3. Account for a punp suspension. This is for when pod screams or MDT or pod is manually pump suspended.
     //  4. Account for a pump resume (in case pump/cgm is disconnected before next loop).
     //  To do: are there more circumstances when scheduled basal rates are used?
     //
@@ -136,41 +295,9 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
             } while (l > 0);
             // duration of current scheduled basal in h
             let basDuration = (time2 - time1) / 36e5;
+           
             if (basDuration > 0) {
-                let hour = time1.getHours();
-                let minutes = time1.getMinutes();
-                let seconds = "00";
-                let string = "" + hour + ":" + minutes + ":" + seconds;
-                let baseTime = new Date(string);
-                var basalScheduledRate = profile.basalprofile[0].start;
-                for (let m = 0; m < profile.basalprofile.length; m++) {
-                    if (profile.basalprofile[m].start == baseTime) {
-                        basalScheduledRate = profile.basalprofile[m].rate;
-                        insulin = basalScheduledRate * basDuration;
-                        break;
-                    }
-                    else if (m + 1 < profile.basalprofile.length) {
-                        if (profile.basalprofile[m].start < baseTime && profile.basalprofile[m+1].start > baseTime) {
-                            basalScheduledRate = profile.basalprofile[m].rate;
-                            insulin = basalScheduledRate * basDuration;
-                            break;
-                        }
-                    }
-                    else if (m == profile.basalprofile.length - 1) {
-                        basalScheduledRate = profile.basalprofile[m].rate;
-                        insulin = basalScheduledRate * basDuration;
-                        break;
-                    }
-                }
-                // Account for smallest possible pump dosage
-                incrementsRaw = insulin / minimalDose;
-                if (incrementsRaw >= 1) {
-                    incrementsRounded = Math.floor(incrementsRaw);
-                    insulin = incrementsRounded * minimalDose;
-                    scheduledBasalInsulin += insulin;
-                } else { insulin = 0;
-                    
-                }
+                scheduledBasalInsulin += calcScheduledBasalInsulin(time2, time1);
             }
         }
     }
@@ -183,7 +310,7 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
             // time of old temp basal
             let oldTime = new Date(pumphistory[n].timestamp);
                         
-            let newTime = oldTime;
+            var newTime = oldTime;
             let o = n;
             do {
                 --o;
@@ -202,52 +329,15 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
                 oldBasalDuration = pumphistory[n]['duration (min)'] / 60;
             }
             
-            // Time difference in hours, new - old
             let tempBasalTimeDifference = (newTime - oldTime) / 36e5;
-            
             let timeOfbasal = tempBasalTimeDifference - oldBasalDuration;
             
             // if duration of scheduled basal is more than 0
             if (timeOfbasal > 0) {
-                
+
                 // Timestamp after completed temp basal
-                let timeOfScheduledBasal = new Date(oldTime.getTime() + oldBasalDuration*36e5);
-                
-                let hour = timeOfScheduledBasal.getHours();
-                let minutes = timeOfScheduledBasal.getMinutes();
-                let seconds = "00";
-                // "hour:minutes:00"
-                let baseTime = "" + hour + ":" + minutes + ":" + seconds;
-                                
-                // Default if correct basal schedule rate not found
-                var basalScheduledRate = profile.basalprofile[0].rate;
-    
-                for (let p = 0; p < profile.basalprofile.length; ++p) {
-                    let basalRateTime = new Date(profile.basalprofile[p].start);
-                    if (basalRateTime == baseTime) {
-                        basalScheduledRate = profile.basalprofile[p].rate;
-                        break;
-                    }
-                    else if (p+1 < profile.basalprofile.length) {
-                        let nextBasalRateTime = new Date(profile.basalprofile[p+1].start);
-                        if (basalRateTime < baseTime && nextBasalRateTime > baseTime) {
-                            basalScheduledRate = profile.basalprofile[p].rate;
-                            break;
-                        }
-                    }
-                    else if (p == (profile.basalprofile.length - 1)) {
-                        basalScheduledRate = profile.basalprofile[p].rate;
-                        break;
-                    }
-                }
-                
-                insulin = basalScheduledRate * timeOfbasal;
-                // Account for smallest possible pump dosage
-                incrementsRaw = insulin / minimalDose;
-                if (incrementsRaw >= 1) {
-                    incrementsRounded = Math.floor(incrementsRaw);
-                    scheduledBasalInsulin += incrementsRounded * minimalDose;
-                } else { insulin = 0}
+                let timeOfScheduledBasal =  addTimeFromDate(oldTime, oldBasalDuration);
+                scheduledBasalInsulin += calcScheduledBasalInsulin(newTime, timeOfScheduledBasal);
             }
         }
     }
@@ -257,6 +347,8 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
     logTempBasal = ". Temporary basal insulin: " + tempInsulin.toPrecision(5) + " U";
     logBasal = ". Delivered scheduled basal insulin: " + scheduledBasalInsulin.toPrecision(5) + " U";
     logTDD = ". TDD past 24h is: " + TDD.toPrecision(5) + " U";
+
+    
     // ----------------------------------------------------
     
     // Chris' formula with added adjustmentFactor for tuning:
@@ -273,19 +365,16 @@ function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoi
             newRatio = minLimitChris;
         }
         
-        function round(value, precision) {
-            var multiplier = Math.pow(10, precision || 0);
-            return Math.round(value * multiplier) / multiplier;
-        }
-        
         // Set the new ratio
         autosens.ratio = round(newRatio, 2);
         // Set the new Dynamic CR (Test)
         if (useDynamicCR == true) {
             profile.carb_ratio = round(profile.carb_ratio/newRatio, 2);
         }
-        // Print to log
-        return log + logTDD + logBolus + logTempBasal + logBasal;
         
-    } else { return "Dynamic ISF is off." }
+        // Print to log
+        logOutPut = dataLog + log + logTDD + logBolus + logTempBasal + logBasal;
+    }   else { logOutPut = "Dynamic ISF is off."; }
+
+    return logOutPut;
 }