aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2025-11-08 20:56:50 -0500
committerSam Anthony <sam@samanthony.xyz>2025-11-08 20:56:50 -0500
commit2085a6b287d435052fada584cb3e623064ddafda (patch)
tree1c1d6a34a49cda46bc271237cf26ccb8c82332b0
parent4d4024312dad007a72844cc82d5fa550b12f050b (diff)
downloadcan-gauge-interface-2085a6b287d435052fada584cb3e623064ddafda.zip
cal: refactor
-rw-r--r--sw/cal/can.go60
-rw-r--r--sw/cal/csv.go54
-rw-r--r--sw/cal/err.go3
-rw-r--r--sw/cal/main.go18
-rw-r--r--sw/cal/signal.go82
-rw-r--r--sw/cal/table.go159
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
}