Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions pkg/bluetooth/bluetooth.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ func (b *Ble) expectCommand(expected Packet) {

func (b *Ble) writeMessage(msg *message.Message) {
var buf bytes.Buffer
var index byte = 0
var index = 0

b.WriteCmd(CmdRTS)
b.expectCommand(CmdCTS) // TODO figure out what to do if !CTS
Expand All @@ -344,7 +344,7 @@ func (b *Ble) writeMessage(msg *message.Message) {
log.Tracef("pkg bluetooth; Sending message: %x", bytes)
sum := crc32.ChecksumIEEE(bytes)
if len(bytes) <= 18 {
buf.WriteByte(index) // index
buf.WriteByte(byte(index))
buf.WriteByte(0) // fragments

buf.WriteByte(byte(sum >> 24))
Expand All @@ -360,7 +360,7 @@ func (b *Ble) writeMessage(msg *message.Message) {
b.writeDataBuffer(&buf)

if len(bytes) > 14 {
buf.WriteByte(index)
buf.WriteByte(byte(index))
buf.WriteByte(byte(len(bytes) - 14))
buf.Write(bytes[14:])
b.writeDataBuffer(&buf)
Expand All @@ -369,16 +369,16 @@ func (b *Ble) writeMessage(msg *message.Message) {
}

size := len(bytes)
fullFragments := (byte)((size - 18) / 19)
rest := (byte)((size - (int(fullFragments) * 19)) - 18)
buf.WriteByte(index)
buf.WriteByte(fullFragments + 1)
fullFragments := (size - 18) / 19
rest := (size - (fullFragments * 19)) - 18
buf.WriteByte(byte(index))
buf.WriteByte(byte(fullFragments + 1))
buf.Write(bytes[:18])

b.writeDataBuffer(&buf)

for index = 1; index <= fullFragments; index++ {
buf.WriteByte(index)
buf.WriteByte(byte(index))
if index == 1 {
buf.Write(bytes[18:37])
} else {
Expand All @@ -387,8 +387,8 @@ func (b *Ble) writeMessage(msg *message.Message) {
b.writeDataBuffer(&buf)
}

buf.WriteByte(index)
buf.WriteByte(rest)
buf.WriteByte(byte(index))
buf.WriteByte(byte(rest))
buf.WriteByte(byte(sum >> 24))
buf.WriteByte(byte(sum >> 16))
buf.WriteByte(byte(sum >> 8))
Expand All @@ -401,8 +401,8 @@ func (b *Ble) writeMessage(msg *message.Message) {
b.writeDataBuffer(&buf)
if rest > 14 {
index++
buf.WriteByte(index)
buf.WriteByte(rest - 14)
buf.WriteByte(byte(index))
buf.WriteByte(byte(rest - 14))
buf.Write(bytes[fullFragments*19+18+14:])
for buf.Len() < 20 {
buf.WriteByte(0)
Expand Down
9 changes: 5 additions & 4 deletions pkg/command/getstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ func (g *GetStatus) GetSeq() uint8 {
}

func (g *GetStatus) IsResponseHardcoded() bool {
if g.RequestType == 0 || g.RequestType == 7 || g.RequestType == 2 {
if g.RequestType == 0 || g.RequestType == 1 || g.RequestType == 2 ||
g.RequestType == 3 || g.RequestType == 5 || g.RequestType == 7 {
// These status types all return dynamic information based on changing pod values
return false
} else {
// 0x46, 0x50 & 0x51 and the Nack response for other request types are all hardcoded values
return true
}
}
Expand All @@ -40,9 +43,7 @@ func (g *GetStatus) DoesMutatePodState() bool {
// TODO remove this once all other message types return something other than
// Hardcoded for GetResponseType()
func (g *GetStatus) GetResponse() (response.Response, error) {
if g.RequestType == 0x2 {
return &response.DetailedStatusResponse{}, nil
} else if g.RequestType == 0x46 {
if g.RequestType == 0x46 {
return &response.Type46StatusResponse{}, nil
} else if g.RequestType == 0x50 {
return &response.Type50StatusResponse{}, nil
Expand Down
22 changes: 19 additions & 3 deletions pkg/command/programalerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,30 @@ import (
)

type ProgramAlerts struct {
Seq uint8
ID []byte
Seq uint8
ID []byte
AlertMask uint8
}

func UnmarshalProgramAlerts(data []byte) (*ProgramAlerts, error) {
ret := &ProgramAlerts{}
// TODO deserialize this command
log.Debugf("ProgramAlerts, 0x19, received, data %x", data)

// 19 LL NNNNNNNN IVXX YYYY 0J0K IVXX YYYY 0J0K IVXX YYYY 0J0K IVXX YYYY 0J0K 11 05 NNNNNNNN MM
// 1c 494e532e 2800 125e 060f 3800 0b56 030f 4c00 01ea 010f 79a4 10ba 050f 11 05 494e532e ff
// 0 1 2 3 4 5 6 7 8 910 1112 1314 1516 1718 1920 2122 2324 2526 2728
const bytesPerAlert = 6
const offsetAlert0 = 5
ret.AlertMask = 0
var nAlerts = int((data[0] + 1 - offsetAlert0) / bytesPerAlert)
for i := 0; i < nAlerts; i++ {
// IVXX = 0iiiabcx xxxxxxxx, extract 3 bit iii value and
// turn into mask to be used to clear any triggered alerts.
// We don't (yet) emulate alert triggers based on alert programming though.
var alertNum = (data[offsetAlert0 + (i * bytesPerAlert)] & 0x70) >> 4
ret.AlertMask |= (1 << alertNum)
}
log.Debugf("ProgramAlerts, AlertMask 0x%x", ret.AlertMask)
return ret, nil
}

Expand Down
137 changes: 115 additions & 22 deletions pkg/pod/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (p *Pod) StartActivation() {
pair := &pair.Pair{}
msg, _ := p.ble.ReadMessage()
if err := pair.ParseSP1SP2(msg); err != nil {
log.Fatalf("pkg pod; pkg pod; error parsing SP1SP2 %s", err)
log.Fatalf("pkg pod; error parsing SP1SP2 %s", err)
}
// read PDM public key and nonce
msg, _ = p.ble.ReadMessage()
Expand Down Expand Up @@ -342,7 +342,7 @@ func (p *Pod) makeGeneralStatusResponse() response.Response {

var now = time.Now()

return &response.GeneralStatusResponse{
return &response.GeneralStatusResponse {
LastProgSeqNum: p.state.LastProgSeqNum,
Reservoir: p.state.Reservoir,
Alerts: p.state.ActiveAlertSlots,
Expand All @@ -361,7 +361,7 @@ func (p *Pod) makeDetailedStatusResponse() response.Response {

var now = time.Now()

return &response.DetailedStatusResponse{
return &response.DetailedStatusResponse {
LastProgSeqNum: p.state.LastProgSeqNum,
Reservoir: p.state.Reservoir,
Alerts: p.state.ActiveAlertSlots,
Expand All @@ -378,29 +378,120 @@ func (p *Pod) makeDetailedStatusResponse() response.Response {
}
}

func (p *Pod) makeType1StatusResponse() response.Response {

return &response.Type1StatusResponse {
TriggeredAlerts: p.state.TriggerTimes,
}
}

func (p *Pod) makeType3StatusResponse() response.Response {

return &response.Type3StatusResponse {
FaultEvent: p.state.FaultEvent,
FaultEventTime: p.state.FaultTime,
MinutesActive: p.state.MinutesActive(),
}
}

func (p *Pod) makeType5StatusResponse() response.Response {

var activationTime = p.state.ActivationTime

return &response.Type5StatusResponse {
FaultEvent: p.state.FaultEvent,
FaultEventTime: p.state.FaultTime,
Year: uint8(activationTime.Year() - 2000),
Month: uint8(activationTime.Month()),
Day: uint8(activationTime.Day()),
Hour: uint8(activationTime.Hour()),
Minute: uint8(activationTime.Minute()),
}
}

func (p *Pod) getResponse(cmd command.Command) response.Response {
var rsp response.Response

// If explicit request for detail, or we have a fault, return detail status.
getStatus, ok := cmd.(*command.GetStatus)
if (ok && getStatus.RequestType == 2) || p.state.FaultEvent != 0 {
rsp = p.makeDetailedStatusResponse()
getStatus, isStatusRequest := cmd.(*command.GetStatus)
if !isStatusRequest || getStatus.RequestType == 0 {
// Not a get status command or a type 0 get status
if p.state.FaultEvent == 0 {
// Pod is not faulted, return a general status response
rsp = p.makeGeneralStatusResponse()
} else {
// Pod is faulted, return a detailed status response
rsp = p.makeDetailedStatusResponse()
}
} else {
rsp = p.makeGeneralStatusResponse()
// Return the requested status type independent of the pod fault state
switch getStatus.RequestType {
case 1:
rsp = p.makeType1StatusResponse()
case 2:
rsp = p.makeDetailedStatusResponse()
case 3:
rsp = p.makeType3StatusResponse()
case 5:
rsp = p.makeType5StatusResponse()
default:
// Includes 0x46, 0x50, 0x51 and the nack responses that are all hardcoded
log.Fatal("pkg pod; getStatus: unexpected type 0x%x", getStatus.RequestType)
}
}

return rsp
}

// clear the alert bit mask and the trigger times array for alerts in the mask
func (p *Pod) clearAlerts(alertMask uint8) {
p.state.ActiveAlertSlots = p.state.ActiveAlertSlots &^ alertMask

for i := 0; i < 8; i++ {
if ((1 << i) & alertMask) != 0 {
p.state.TriggerTimes[i] = 0
}
}
}

func (p *Pod) handleCommand(cmd command.Command) {
if crashBeforeProcessingCommand && cmd.DoesMutatePodState() {
log.Fatalf("pkg pod; Crashing before processing command with sequence %d", cmd.GetSeq())
}

switch c := cmd.(type) {
case *command.GetVersion:
case *command.GetVersion: // 0x03
p.state.PodProgress = response.PodProgressReminderInitialized
case *command.SetUniqueID:

case *command.SetUniqueID: // 0x07
p.state.PodProgress = response.PodProgressPairingCompleted
case *command.ProgramInsulin:

case *command.GetStatus: // 0x0E
now := time.Now()
if p.state.PodProgress == response.PodProgressPriming {
// if enough time has passed for priming to finish, advance PodProgress
if p.state.BolusEnd.Before(now) {
log.Infof("*** Advancing progress to PodProgressPrimingCompleted as prime bolus has ended")
p.state.PodProgress = response.PodProgressPrimingCompleted
}
}
if p.state.PodProgress == response.PodProgressInsertingCannula && !p.state.BolusEnd.After(now) {
// if enough time has passed for cannula insert bolus to finish, advance PodProgress
if p.state.BolusEnd.Before(now) {
log.Infof("*** Advancing progress to PodProgressRunningAbove50U as cannula insert bolus has ended")
p.state.PodProgress = response.PodProgressRunningAbove50U
}
}

case *command.SilenceAlerts: // 0x11
// clears the ActiveAlertSlots bits and Trigger Times for the specified alerts
p.clearAlerts(c.AlertMask)

case *command.ProgramAlerts: // 0x19
// For now just clears the ActiveAlertSlots bits and Trigger Times for alerts being programmed
// Later could add code to manage timers for configured alerts to make the sim more pod-like.
p.clearAlerts(c.AlertMask)

case *command.ProgramInsulin: // 0x1A
log.Debugf("pkg pod; ProgramInsulin: PodProgress = %d", p.state.PodProgress)

if p.state.PodProgress < response.PodProgressPriming {
Expand Down Expand Up @@ -438,14 +529,7 @@ func (p *Pod) handleCommand(cmd command.Command) {
}
}

case *command.GetStatus:
if p.state.PodProgress == response.PodProgressPriming {
p.state.PodProgress = response.PodProgressPrimingCompleted
}
if p.state.PodProgress == response.PodProgressInsertingCannula {
p.state.PodProgress = response.PodProgressRunningAbove50U
}
case *command.StopDelivery:
case *command.StopDelivery: // 0x1F
if c.StopBolus {
p.state.ExtendedBolusActive = false
}
Expand All @@ -455,11 +539,11 @@ func (p *Pod) handleCommand(cmd command.Command) {
if c.StopBasal {
p.state.BasalActive = false
}
case *command.SilenceAlerts:
p.state.ActiveAlertSlots = p.state.ActiveAlertSlots &^ c.AlertMask
default:

default: // includes 0x08, 0x1C, 0x1E
// No action
}

if cmd.DoesMutatePodState() {
seq := cmd.GetSeq()
log.Debugf("pkg pod; Updating LastProgSeqNum = %d", seq)
Expand All @@ -481,6 +565,15 @@ func (p *Pod) SetReservoir(newVal float32) {
func (p *Pod) SetAlerts(newVal uint8) {
p.mtx.Lock()
p.state.ActiveAlertSlots = newVal

// Save the current pod time in alert trigger
// time array for any alerts slots going active
var podTime = p.state.MinutesActive()
for i := 0; i < 8; i++ {
if ((1 << i) & newVal) != 0 {
p.state.TriggerTimes[i] = podTime
}
}
p.state.Save()
p.mtx.Unlock()
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/pod/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type PODState struct {
FaultTime uint16 `toml:"fault_time"`
Delivered uint16 `toml:"delivered"`

TriggerTimes [8]uint16 `toml:"trigger_times"`

// At some point these could be replaced with details
// of each kind of delivery (volume, start time, schedule, etc)
BolusEnd time.Time `toml:"bolus_end"`
Expand Down
23 changes: 23 additions & 0 deletions pkg/response/type1statusresponse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package response

import (
"encoding/hex"
)

type Type1StatusResponse struct {
TriggeredAlerts [8]uint16
}

// CMD 1 2 3 4 5 6 7 8 910 1112 1314 1516 1718 1920
// 02 13 01 XXXX VVVV VVVV VVVV VVVV VVVV VVVV VVVV VVVV

func (r *Type1StatusResponse) Marshal() ([]byte, error) {

response, _ := hex.DecodeString("021301000000000000000000000000000000000000")

for i := 0; i < 8; i++ {
response[(2 * i) + 5] = byte(r.TriggeredAlerts[i] >> 8)
response[(2 * i) + 6] = byte(r.TriggeredAlerts[i] & 0xff)
}
return response, nil
}
31 changes: 31 additions & 0 deletions pkg/response/type3statusresponse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package response

import (
"encoding/hex"
)

type Type3StatusResponse struct {
FaultEvent uint8
FaultEventTime uint16
MinutesActive uint16
}

// OFF 1 2 3 4 5 6 7 8 9 10
// 02 LL 03 PP QQQQ SSSS 04 3c XXXXXXXX ...

func (r *Type3StatusResponse) Marshal() ([]byte, error) {
response, _ := hex.DecodeString("02f8030000000d4c043c287331002d733b003072320035733b00387232003d733b004072320045723d00487232004d723c005072330055723e00587235805d733d806073348001713e800450338009503c800c50338011513b801475328019723a801c72328021723b002472330029733f002c7334003172400034723500397240003c733400417241004473340049733e004c73330051733d005473330059733f805c73348061713e800074338005723d80087233800d723d801073338015733c80187334801d723d802073340025724000287336002d7241003072370035734100387337003d7243004072380045724400487238004d714300")

// Fault PP
response[3] = r.FaultEvent

// Fault Time QQQQ
response[4] = byte(r.FaultEventTime >> 8)
response[5] = byte(r.FaultEventTime & 0xff)

// Minutes Since Activation SSSS
response[6] = byte(r.MinutesActive >> 8)
response[7] = byte(r.MinutesActive & 0xff)

return response, nil
}
Loading