forked from Jon-b-m/middleware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Dynamic_ISF_with_calculated_TDD.js
365 lines (311 loc) · 17.2 KB
/
Dynamic_ISF_with_calculated_TDD.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
function middleware(iob, currenttemp, glucose, profile, autosens, meal, reservoir, clock, pumphistory) {
//
const BG = glucose[0].glucose;
// Change to false to turn off Chris Wilson's formula
var chrisFormula = true;
const adjustmentFactor = 1;
const minLimitChris = profile.autosens_min;
const maxLimitChris = profile.autosens_max;
const currentMinTarget = profile.min_bg;
var exerciseSetting = false;
var pumpData = 0;
var log = "";
var logTDD = "";
var logBasal = "";
var logBolus = "";
var logTempBasal = "";
var dataLog = "";
var logOutPut = "";
var current = 0;
var TDD = 0;
var insulin = 0;
var tempInsulin = 0;
var bolusInsulin = 0;
var scheduledBasalInsulin = 0;
var quota = 0;
//
//
function round(value, precision) {
var multiplier = Math.pow(10, precision || 0);
return Math.round(value * multiplier) / multiplier;
}
function addTimeToDate(objDate, _hours) {
var ms = objDate.getTime();
var add_ms = _hours * 36e5;
var newDateObj = new Date(ms + add_ms);
return newDateObj;
}
function subtractTimeFromDate(date, hours_) {
var ms_ = date.getTime();
var add_ms_ = hours_ * 36e5;
var new_date = new Date(ms_ - add_ms_);
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 ms1 = time1.getTime();
var ms2 = time2.getTime();
var difference = (ms1 - ms2) / 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;
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 = addTimeToDate(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 = addTimeToDate(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 = addTimeToDate(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;
}
// Turn off Chris' formula when using a temp target >= 118 (6.5 mol/l) and if an exercise setting is enabled.
if (currentMinTarget >= 118 && exerciseSetting == true) {
chrisFormula = false;
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.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;
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();
}
pumpData = (startDate - endDate) / 36e5;
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 --------------------------------------
//Bolus:
for (let i = 0; i < pumphistory.length; i++) {
if (pumphistory[i]._type == "Bolus") {
bolusInsulin += pumphistory[i].amount;
}
}
// Temp basals:
for (let j = 1; j < pumphistory.length; j++) {
if (pumphistory[j]._type == "TempBasal" && pumphistory[j].rate > 0) {
current = j;
quota = pumphistory[j].rate;
var duration = pumphistory[j-1]['duration (min)'] / 60;
var origDur = duration;
var pastTime = new Date(pumphistory[j-1].timestamp);
// If temp basal hasn't yet ended, use now as end date for calculation
do {
j--;
if (j <= 0) {
morePresentTime = new Date();
break;
} else if (pumphistory[j]._type == "TempBasal" || pumphistory[j]._type == "PumpSuspend") {
morePresentTime = new Date(pumphistory[j].timestamp);
break;
}
}
while (j >= 0);
var diff = (morePresentTime - pastTime) / 36e5;
if (diff < origDur) {
duration = diff;
}
insulin = quota * duration;
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. 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?
//
for (let k = 0; k < pumphistory.length; k++) {
// Check for 0 temp basals with 0 min duration.
insulin = 0;
if (pumphistory[k]['duration (min)'] == 0 || pumphistory[k]._type == "PumpResume") {
let time1 = new Date(pumphistory[k].timestamp);
let time2 = time1;
let l = k;
do {
--l;
if (pumphistory[l]._type == "TempBasal" && l >= 0) {
time2 = new Date(pumphistory[l].timestamp);
break;
}
} while (l > 0);
// duration of current scheduled basal in h
let basDuration = (time2 - time1) / 36e5;
if (basDuration > 0) {
scheduledBasalInsulin += calcScheduledBasalInsulin(time2, time1);
}
}
}
// Check for temp basals that completes
for (let n = pumphistory.length -1; n > 0; n--) {
if (pumphistory[n]._type == "TempBasalDuration") {
// duration in hours
let oldBasalDuration = pumphistory[n]['duration (min)'] / 60;
// time of old temp basal
let oldTime = new Date(pumphistory[n].timestamp);
var newTime = oldTime;
let o = n;
do {
--o;
if (o >= 0) {
if (pumphistory[o]._type == "TempBasal" || pumphistory[o]._type == "PumpSuspend") {
// time of next (new) temp basal or a pump suspension
newTime = new Date(pumphistory[o].timestamp);
break;
}
}
} while (o > 0);
// When latest temp basal is index 0 in pump history
if (n == 0 && pumphistory[0]._type == "TempBasalDuration") {
newTime = new Date();
oldBasalDuration = pumphistory[n]['duration (min)'] / 60;
}
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 = addTimeToDate(oldTime, oldBasalDuration);
scheduledBasalInsulin += calcScheduledBasalInsulin(newTime, timeOfScheduledBasal);
}
}
}
TDD = bolusInsulin + tempInsulin + scheduledBasalInsulin;
logBolus = ". Bolus insulin: " + bolusInsulin.toPrecision(5) + " U";
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:
if (chrisFormula == true && TDD > 0) {
var newRatio = profile.sens / (277700 / (adjustmentFactor * TDD * BG));
log = "New ratio using Dynamic ISF is " + newRatio.toPrecision(3) + " with ISF: " + (profile.sens / newRatio).toPrecision(3) + " mg/dl/U (" + ((profile.sens / newRatio) * 0.0555).toPrecision(3) + " mmol/l/U)";
// Respect autosens.max and autosens.min limits
if (newRatio > maxLimitChris) {
log = "Dynamic ISF hit limit by autosens_max setting: " + maxLimitChris + " (" + newRatio.toPrecision(3) + ")" + ". ISF: " + (profile.sens / maxLimitChris).toPrecision(3) + " mg/dl/U (" + ((profile.sens / maxLimitChris) * 0.0555).toPrecision(3) + " mmol/l/U)";
newRatio = maxLimitChris;
} else if (newRatio < minLimitChris) {
log = "Dynamic ISF hit limit by autosens_min setting: " + minLimitChris + " (" + newRatio.toPrecision(3) + ")" + ". ISF: " + (profile.sens / minLimitChris).toPrecision(3) + " mg/dl/U (" + ((profile.sens / minLimitChris) * 0.0555).toPrecision(3) + " mmol/l/U)";
newRatio = minLimitChris;
}
// Set the new ratio
autosens.ratio = round(newRatio, 2);
// Set the new Dynamic CR (Test)
// Print to log
logOutPut = dataLog + log + logTDD + logBolus + logTempBasal + logBasal;
} else { logOutPut = "Dynamic ISF is off."; }
return logOutPut;
}