diff options
Diffstat (limited to 'sw/cal')
| -rw-r--r-- | sw/cal/can.go | 60 | ||||
| -rw-r--r-- | sw/cal/csv.go | 54 | ||||
| -rw-r--r-- | sw/cal/err.go | 3 | ||||
| -rw-r--r-- | sw/cal/main.go | 18 | ||||
| -rw-r--r-- | sw/cal/signal.go | 82 | ||||
| -rw-r--r-- | sw/cal/table.go | 159 |
6 files changed, 184 insertions, 192 deletions
diff --git a/sw/cal/can.go b/sw/cal/can.go new file mode 100644 index 0000000..c1397b0 --- /dev/null +++ b/sw/cal/can.go @@ -0,0 +1,60 @@ +package main + +import ( + "context" + "time" + + "go.einride.tech/can" + + "git.samanthony.xyz/can_gauge_interface/sw/cal/canbus" +) + +const ( + stdMask = 0x7FF + extMask = 0x1FFFFFFF + + timeout = 1 * time.Second + maxRetries = 8 +) + +func sendCtrlFrame[C can.FrameMarshaler, R can.FrameUnmarshaler](cmd C, req can.FrameMarshaler, reply R, bus canbus.Bus, isReply func(R) bool, verify func(C, R) bool) error { + cmdFrame, err := cmd.MarshalFrame() + if err != nil { + return err + } + + for retry := 0; retry < maxRetries; retry++ { + // Write + ctx, _ := context.WithTimeout(context.Background(), timeout) + if err := bus.Send(ctx, cmdFrame); err != nil { + return err + } + + // Read back + reqFrame, err := req.MarshalFrame() + if err != nil { + return err + } + ctx, _ = context.WithTimeout(context.Background(), timeout) + if err := bus.Send(ctx, reqFrame); err != nil { + return err + } + ctx, _ = context.WithTimeout(context.Background(), timeout) + replyFrame, err := bus.Receive(ctx) + if err != nil { + return err + } + err = reply.UnmarshalFrame(replyFrame) + if err == errWrongId || !isReply(reply) { + continue + } else if err != nil { + return err + } + + // Verify + if verify(cmd, reply) { + return nil + } + } + return errVerifyFail +} diff --git a/sw/cal/csv.go b/sw/cal/csv.go deleted file mode 100644 index cdd5b44..0000000 --- a/sw/cal/csv.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "encoding/csv" - "fmt" - "io" - "os" - "strconv" -) - -func parseTable(filename string) (Table, error) { - f, err := os.Open(filename) - if err != nil { - eprintf("%v\n", err) - } - defer f.Close() - - var tbl Table - rdr := csv.NewReader(f) - for { - err := parseRow(rdr, &tbl) - if err == io.EOF { - return tbl, nil - } else if err != nil { - return Table{}, fmt.Errorf("%s:%v", filename, err) - } - } -} - -func parseRow(rdr *csv.Reader, tbl *Table) error { - row, err := rdr.Read() - if err != nil { - return err - } - if len(row) != 2 { - line, _ := rdr.FieldPos(0) - return fmt.Errorf("%d: malformed row", line) - } - key, err := strconv.ParseInt(row[0], 10, 32) - if err != nil { - line, col := rdr.FieldPos(0) - return fmt.Errorf("%d:%d: %v", line, col, err) - } - val, err := strconv.ParseUint(row[1], 10, 16) - if err != nil { - line, col := rdr.FieldPos(1) - return fmt.Errorf("%d:%d: %v", line, col, err) - } - if err := tbl.Insert(int32(key), uint16(val)); err != nil { - line, col := rdr.FieldPos(0) - return fmt.Errorf("%d:%d: %v", line, col, err) - } - return nil -} diff --git a/sw/cal/err.go b/sw/cal/err.go index 4ceb1cc..4069384 100644 --- a/sw/cal/err.go +++ b/sw/cal/err.go @@ -9,7 +9,8 @@ import ( ) var ( - errWrongId = errors.New("wrong ID") + errWrongId = errors.New("wrong ID") + errVerifyFail = errors.New("verification failed") ) func eprintf(format string, a ...any) { diff --git a/sw/cal/main.go b/sw/cal/main.go index 503324f..6b6a6a8 100644 --- a/sw/cal/main.go +++ b/sw/cal/main.go @@ -5,22 +5,12 @@ import ( "fmt" "math" "os" - "time" "go.einride.tech/can/pkg/dbc" "git.samanthony.xyz/can_gauge_interface/sw/cal/canbus" ) -const ( - stdMask = 0x7FF - extMask = 0x1FFFFFFF - - timeout = 1 * time.Second - eepromWriteDelay = 5 * time.Millisecond - maxRetries = 8 -) - type Signals struct { tach, speed, an1, an2, an3, an4 *dbc.SignalDef } @@ -141,6 +131,7 @@ func sendEncodings(dbcFilename string, sigNames map[uint8]string, bus canbus.Bus if err := sig.SendEncoding(bus); err != nil { return err } + fmt.Printf("Signal encoding %d OK\n", sig.index) } return nil @@ -149,16 +140,17 @@ func sendEncodings(dbcFilename string, sigNames map[uint8]string, bus canbus.Bus // Parse each table and transmit them using Table Control frames. func sendTables(tblFilenames map[uint8]string, bus canbus.Bus) error { for k, filename := range tblFilenames { - fmt.Println("Parsing", filename) - tbl, err := parseTable(filename) + fmt.Printf("Parsing table %d: %s\n", k, filename) + tbl, err := parseTable(filename, k) if err != nil { return err } - fmt.Printf("Transmitting table %d: %s\n", k, filename) + fmt.Printf("Sending table %d\n", k) if err := tbl.Send(bus); err != nil { return err } + fmt.Printf("Table %d OK\n", k) } return nil } diff --git a/sw/cal/signal.go b/sw/cal/signal.go index 446afb7..368b848 100644 --- a/sw/cal/signal.go +++ b/sw/cal/signal.go @@ -1,7 +1,6 @@ package main import ( - "context" bin "encoding/binary" "fmt" "math" @@ -49,63 +48,21 @@ func NewSignalDef(index uint8, msg *dbc.MessageDef, sig dbc.SignalDef) (SignalDe }, nil } -// Transmit a signal's encoding in a Signal Control frame so the Interface can store it in its EEPROM. +// Transmit a signal's encoding in a Signal Control frame +// so the Interface can store it in its EEPROM. func (sig SignalDef) SendEncoding(bus canbus.Bus) error { - frame, err := sig.MarshalFrame() - if err != nil { - return err - } - - var retry int - for retry = 0; retry < maxRetries; retry++ { - // Write to EEPROM - ctx, _ := context.WithTimeout(context.Background(), timeout) - if err := bus.Send(ctx, frame); err != nil { - return err - } - - // Read back - request := sigCtrlRequest(sig.index) - ctx, _ = context.WithTimeout(context.Background(), timeout) - if err := bus.Send(ctx, request); err != nil { - return err - } - ctx, _ = context.WithTimeout(context.Background(), timeout) - reply, err := bus.Receive(ctx) - if err != nil { - return err - } - var rsig SignalDef - err = rsig.UnmarshalFrame(reply) - if err == errWrongId { - continue - } else if err != nil { - return err - } - - // Verify - rsig.index = sig.index - rsig.name = sig.name - fmt.Println("Received signal encoding", rsig) - if rsig == sig { - fmt.Printf("Signal %d encoding OK\n", sig.index) - return nil // success - } else { - weprintf("Warning: signal %d verification failed; rewriting...\n", sig.index) - continue - } - } - // Max retries exceeded - return fmt.Errorf("signal %d verification failed", sig.index) + req := SignalControlRequest{sig.index} + reply := &SignalDef{} + isReply := func(reply *SignalDef) bool { return true } + return sendCtrlFrame(sig, req, reply, bus, isReply, verifySigCtrlReply) } -// Construct a Signal Control REMOTE REQUEST frame. -func sigCtrlRequest(index uint8) can.Frame { - return can.Frame{ - ID: sigCtrlId | uint32(index&0xF), - IsRemote: true, - IsExtended: true, - } +// Verify that the response to a Signal Control REMOTE REQUEST +// is the same as what was commanded to be written. +func verifySigCtrlReply(cmd SignalDef, reply *SignalDef) bool { + reply.index = cmd.index // ignore index and name of reply + reply.name = cmd.name + return *reply == cmd } func (sig SignalDef) MarshalFrame() (can.Frame, error) { @@ -137,7 +94,7 @@ func (sig *SignalDef) UnmarshalFrame(frame can.Frame) error { return errWrongId } if frame.Length != 7 { - return fmt.Errorf("Signal Control frame has wrong DLC: %d", frame.Length) + return fmt.Errorf("wrong DLC for Signal Control frame: %d", frame.Length) } sig.index = uint8(frame.ID & 0xF) var id uint32 @@ -152,3 +109,16 @@ func (sig *SignalDef) UnmarshalFrame(frame can.Frame) error { sig.isSigned = frame.Data[6]&0x40 != 0 return nil } + +// SignalControlRequest is a Signal Control REMOTE REQUEST frame. +type SignalControlRequest struct { + sigIndex uint8 +} + +func (r SignalControlRequest) MarshalFrame() (can.Frame, error) { + return can.Frame{ + ID: sigCtrlId | uint32(r.sigIndex&0xF), + IsRemote: true, + IsExtended: true, + }, nil +} diff --git a/sw/cal/table.go b/sw/cal/table.go index 7d63d8d..3848bef 100644 --- a/sw/cal/table.go +++ b/sw/cal/table.go @@ -2,10 +2,13 @@ package main import ( "cmp" - "context" bin "encoding/binary" + "encoding/csv" "fmt" + "io" + "os" "slices" + "strconv" "go.einride.tech/can" @@ -25,20 +28,66 @@ type Table struct { } type Row struct { - key int32 - val uint16 + sigIndex, rowIndex uint8 + key int32 + val uint16 } -func (t *Table) Insert(key int32, val uint16) error { - if len(t.rows) >= maxTabRows { +func parseTable(filename string, sigIndex uint8) (Table, error) { + f, err := os.Open(filename) + if err != nil { + eprintf("%v\n", err) + } + defer f.Close() + + tbl := Table{sigIndex, nil} + rdr := csv.NewReader(f) + for { + err := parseRow(rdr, &tbl) + if err == io.EOF { + return tbl, nil + } else if err != nil { + return Table{}, fmt.Errorf("%s:%v", filename, err) + } + } +} + +func parseRow(rdr *csv.Reader, tbl *Table) error { + row, err := rdr.Read() + if err != nil { + return err + } + if len(row) != 2 { + line, _ := rdr.FieldPos(0) + return fmt.Errorf("%d: malformed row", line) + } + key, err := strconv.ParseInt(row[0], 10, 32) + if err != nil { + line, col := rdr.FieldPos(0) + return fmt.Errorf("%d:%d: %v", line, col, err) + } + val, err := strconv.ParseUint(row[1], 10, 16) + if err != nil { + line, col := rdr.FieldPos(1) + return fmt.Errorf("%d:%d: %v", line, col, err) + } + if err := tbl.Insert(int32(key), uint16(val)); err != nil { + line, col := rdr.FieldPos(0) + return fmt.Errorf("%d:%d: %v", line, col, err) + } + return nil +} + +func (tbl *Table) Insert(key int32, val uint16) error { + if len(tbl.rows) >= maxTabRows { return fmt.Errorf("too many rows") } - i, ok := slices.BinarySearchFunc(t.rows, key, cmpRowKey) + i, ok := slices.BinarySearchFunc(tbl.rows, key, cmpRowKey) if ok { return ErrDupKey{key} } - t.rows = slices.Insert(t.rows, i, Row{key, val}) + tbl.rows = slices.Insert(tbl.rows, i, Row{tbl.sigIndex, uint8(i), key, val}) return nil } @@ -52,7 +101,7 @@ func (tbl Table) Send(bus canbus.Bus) error { // Send populated rows var i int for i = 0; i < len(tbl.rows); i++ { - if err := sendRow(tbl.sigIndex, uint8(i), tbl.rows[i], bus); err != nil { + if err := tbl.rows[i].Send(bus); err != nil { return err } } @@ -60,7 +109,8 @@ func (tbl Table) Send(bus canbus.Bus) error { // Fill rest of table with last row lastRow := tbl.rows[len(tbl.rows)-1] for ; i < maxTabRows; i++ { - if err := sendRow(tbl.sigIndex, uint8(i), lastRow, bus); err != nil { + lastRow.rowIndex = uint8(i) + if err := lastRow.Send(bus); err != nil { return err } } @@ -69,52 +119,22 @@ func (tbl Table) Send(bus canbus.Bus) error { } // Transmit a Table Control frame containing one row of a table. -func sendRow(sigIndex, rowIndex uint8, row Row, bus canbus.Bus) error { - frame, err := marshalTblCtrlFrame(sigIndex, rowIndex, row) - if err != nil { - return err +func (row Row) Send(bus canbus.Bus) error { + req := TableControlRequest{row.sigIndex, row.rowIndex} + reply := &Row{} + isReply := func(reply *Row) bool { + return reply.sigIndex == row.sigIndex && reply.rowIndex == row.rowIndex } + return sendCtrlFrame(row, req, reply, bus, isReply, verifyTblCtrlReply) +} - var retry int - for retry = 0; retry < maxRetries; retry++ { - // Write to EEPROM - ctx, _ := context.WithTimeout(context.Background(), timeout) - if err := bus.Send(ctx, frame); err != nil { - return err - } - - // Read back - request := tblCtrlRequest(sigIndex, rowIndex) - ctx, _ = context.WithTimeout(context.Background(), timeout) - if err := bus.Send(ctx, request); err != nil { - return err - } - ctx, _ = context.WithTimeout(context.Background(), timeout) - reply, err := bus.Receive(ctx) - if err != nil { - return err - } - rRow, rSigIndex, rRowIndex, err := unmarshalTblCtrlFrame(reply) - if err == errWrongId || rSigIndex != sigIndex || rRowIndex != rowIndex { - continue - } else if err != nil { - return err - } - - // Verify - if rRow == row { - fmt.Printf("Table %d row %d OK\n", sigIndex, rowIndex) - return nil // success - } else { - weprintf("Warning: table %d row %d verification failed; rewriting...\n", sigIndex, rowIndex) - continue - } - } - // Max retries exceeded - return fmt.Errorf("table %d row %d verification failed", sigIndex, rowIndex) +// Verify that the response to a Table Control REMOTE REQUEST +// is the same as what was commanded to be written. +func verifyTblCtrlReply(cmd Row, reply *Row) bool { + return *reply == cmd } -func marshalTblCtrlFrame(sigIndex, rowIndex uint8, row Row) (can.Frame, error) { +func (row Row) MarshalFrame() (can.Frame, error) { var data [8]byte if _, err := bin.Encode(data[0:4], bin.BigEndian, row.key); err != nil { return can.Frame{}, err @@ -123,37 +143,40 @@ func marshalTblCtrlFrame(sigIndex, rowIndex uint8, row Row) (can.Frame, error) { return can.Frame{}, err } return can.Frame{ - ID: uint32(tblCtrlId) | uint32((sigIndex<<5)&0xE0) | uint32(rowIndex&0x1F), + ID: uint32(tblCtrlId) | uint32((row.sigIndex<<5)&0xE0) | uint32(row.rowIndex&0x1F), Length: 6, Data: data, IsExtended: true, }, nil } -func unmarshalTblCtrlFrame(frame can.Frame) (row Row, sigIndex, rowIndex uint8, err error) { +func (row *Row) UnmarshalFrame(frame can.Frame) error { if !frame.IsExtended || frame.ID&tblCtrlMask != tblCtrlId { - err = errWrongId - return + return errWrongId } if frame.Length != 6 { - err = fmt.Errorf("Table Control frame has wrong DLC: %d", frame.Length) - return + return fmt.Errorf("wrong DLC for Table Control frame: %d", frame.Length) } - sigIndex = uint8((frame.ID & 0xE0) >> 5) - rowIndex = uint8(frame.ID & 0x1F) - _, err = bin.Decode(frame.Data[0:4], bin.BigEndian, &row.key) - if err != nil { - return + row.sigIndex = uint8((frame.ID & 0xE0) >> 5) + row.rowIndex = uint8(frame.ID & 0x1F) + if _, err := bin.Decode(frame.Data[0:4], bin.BigEndian, &row.key); err != nil { + return err + } + if _, err := bin.Decode(frame.Data[4:6], bin.BigEndian, &row.val); err != nil { + return err } - _, err = bin.Decode(frame.Data[4:6], bin.BigEndian, &row.val) - return + return nil +} + +// TableControlRequest is a Table Control REMOTE REQUEST frame. +type TableControlRequest struct { + sigIndex, rowIndex uint8 } -// Construct a Table Control REMOTE REQUEST frame. -func tblCtrlRequest(sigIndex, rowIndex uint8) can.Frame { +func (r TableControlRequest) MarshalFrame() (can.Frame, error) { return can.Frame{ - ID: tblCtrlId | uint32((sigIndex<<5)&0xE0) | uint32(rowIndex&0x1F), + ID: tblCtrlId | uint32((r.sigIndex<<5)&0xE0) | uint32(r.rowIndex&0x1F), IsRemote: true, IsExtended: true, - } + }, nil } |