forked from ecc1/medtronic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbasal.go
135 lines (120 loc) · 3.31 KB
/
basal.go
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
package medtronic
import (
"fmt"
"log"
"time"
)
// BasalRate represents an entry in a basal rate schedule.
type BasalRate struct {
Start TimeOfDay
Rate Insulin
}
// BasalRateSchedule represents a basal rate schedule.
type BasalRateSchedule []BasalRate
func decodeBasalRateSchedule(data []byte) BasalRateSchedule {
var sched []BasalRate
for i := 0; i < len(data)-2; i += 3 {
rate := twoByteInsulinLE(data[i : i+2])
t := data[i+2]
// Don't stop if the 00:00 rate happens to be zero.
if i > 1 && rate == 0 && t == 0 {
break
}
start := halfHoursToTimeOfDay(t)
sched = append(sched, BasalRate{Start: start, Rate: rate})
}
return sched
}
func encodeBasalRateSchedule(s BasalRateSchedule, family Family) ([]byte, error) {
data := make([]byte, 0, len(s)*3)
for _, v := range s {
r, err := encodeBasalRate("basal", v.Rate, family)
if err != nil {
return nil, err
}
b := append(marshalUint16LE(r), v.Start.HalfHours())
data = append(data, b...)
}
return data, nil
}
func (pump *Pump) basalSchedule(cmd Command) BasalRateSchedule {
data := pump.ExtendedResponse(cmd)
if pump.Error() != nil {
return BasalRateSchedule{}
}
return decodeBasalRateSchedule(data)
}
// BasalRates returns the pump's basal rate schedule.
func (pump *Pump) BasalRates() BasalRateSchedule {
return pump.basalSchedule(basalRates)
}
// BasalPatternA returns the pump's basal pattern A.
func (pump *Pump) BasalPatternA() BasalRateSchedule {
return pump.basalSchedule(basalPatternA)
}
// BasalPatternB returns the pump's basal pattern B.
func (pump *Pump) BasalPatternB() BasalRateSchedule {
return pump.basalSchedule(basalPatternB)
}
// BasalRateAt returns the basal rate in effect at the given time.
func (s BasalRateSchedule) BasalRateAt(t time.Time) BasalRate {
d := SinceMidnight(t)
last := BasalRate{}
for _, v := range s {
if v.Start > d {
break
}
last = v
}
return last
}
func (pump *Pump) setBasalSchedule(cmd Command, s BasalRateSchedule) {
if len(s) == 0 {
pump.SetError(fmt.Errorf("%v: empty schedule", cmd))
return
}
data, err := encodeBasalRateSchedule(s, pump.Family())
if err != nil {
pump.SetError(err)
return
}
pump.ExtendedRequest(setBasalRates, data...)
}
// SetBasalRates sets the pump's basal rate schedule.
func (pump *Pump) SetBasalRates(s BasalRateSchedule) {
pump.setBasalSchedule(setBasalRates, s)
}
// SetBasalPatternA sets the pump's basal pattern A.
func (pump *Pump) SetBasalPatternA(s BasalRateSchedule) {
pump.setBasalSchedule(setBasalPatternA, s)
}
// SetBasalPatternB sets the pump's basal pattern B.
func (pump *Pump) SetBasalPatternB(s BasalRateSchedule) {
pump.setBasalSchedule(setBasalPatternB, s)
}
func encodeBasalRate(kind string, rate Insulin, family Family) (uint16, error) {
if rate < 0 {
return 0, fmt.Errorf("%s rate (%d) is negative", kind, rate)
}
if rate > maxBasal {
return 0, fmt.Errorf("%s rate (%d) is too large", kind, rate)
}
// Round the rate to the pump's delivery resolution.
var res Insulin
if family <= 22 {
res = 50
} else if rate < 1000 {
res = 25
} else if rate < 10000 {
res = 50
} else {
res = 100
}
actual := (rate / res) * res
if actual != rate {
log.Printf("rounding %s rate from %v to %v", kind, rate, actual)
}
// Encode the rounded value using 25 milliUnits/stroke.
m := milliUnitsPerStroke(23)
return uint16(actual / m), nil
}