diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2025-11-08 18:11:23 -0500 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2025-11-08 18:11:23 -0500 |
| commit | c6573ad10a8d96d744324937195cd50bb1442c26 (patch) | |
| tree | 4d87e7cee19dd1c5fe3bdd9db5e128c605c9ef64 /sw/cal/canbus | |
| parent | 8f0fc9ba2a8efe870f3584af2c439b2c2fd899e5 (diff) | |
| download | can-gauge-interface-c6573ad10a8d96d744324937195cd50bb1442c26.zip | |
cal: verify signal encoding after writing
Diffstat (limited to 'sw/cal/canbus')
| -rw-r--r-- | sw/cal/canbus/canbus.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/sw/cal/canbus/canbus.go b/sw/cal/canbus/canbus.go new file mode 100644 index 0000000..031c456 --- /dev/null +++ b/sw/cal/canbus/canbus.go @@ -0,0 +1,140 @@ +package canbus + +import ( + "context" + "net" + "strings" + "time" + + "go.einride.tech/can" + "go.einride.tech/can/pkg/socketcan" +) + +const timeout = 1 * time.Second + +type Bus struct { + conn net.Conn + receiver *socketcan.Receiver + transmitter *socketcan.Transmitter + + tx <-chan txChan + + rx <-chan can.Frame + rxErr <-chan error + + cancel context.CancelFunc +} + +type txChan struct { + request chan<- transmission + err <-chan error +} + +type transmission struct { + context.Context + can.Frame +} + +func Connect(dev string) (Bus, error) { + conn, err := socketcan.Dial("can", dev) + if err != nil { + return Bus{}, err + } + + receiver := socketcan.NewReceiver(conn) + transmitter := socketcan.NewTransmitter(conn) + tx := make(chan txChan) + rx := make(chan can.Frame) + rxErr := make(chan error) + ctx, cancel := context.WithCancel(context.Background()) + + go transmit(ctx, transmitter, tx) + go receive(ctx, receiver, rx, rxErr) + + return Bus{conn, receiver, transmitter, tx, rx, rxErr, cancel}, nil +} + +func transmit(ctx context.Context, transmitter *socketcan.Transmitter, tx chan<- txChan) { + defer close(tx) + + for { + reqc := make(chan transmission) + errc := make(chan error) + select { + case tx <- txChan{reqc, errc}: + req := <-reqc + errc <- req.transmit(transmitter) + close(reqc) + close(errc) + case <-ctx.Done(): + close(reqc) + close(errc) + return + } + } +} + +func (t transmission) transmit(transmitter *socketcan.Transmitter) error { + ctx, _ := context.WithTimeout(context.Background(), timeout) + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + err := transmitter.TransmitFrame(t.Context, t.Frame) + if err == nil { + return nil // success + } else if strings.HasSuffix(err.Error(), "no buffer space available") { + continue // retry + } else { + return err + } + } +} + +func receive(ctx context.Context, receiver *socketcan.Receiver, rx chan<- can.Frame, rxErr chan<- error) { + defer close(rx) + defer close(rxErr) + + for receiver.Receive() { + frame := receiver.Frame() + select { + case rx <- frame: + case <-ctx.Done(): + return + } + } + rxErr <- receiver.Err() +} + +func (b Bus) Close() { + b.cancel() + b.transmitter.Close() + b.receiver.Close() + b.conn.Close() +} + +func (b Bus) Send(ctx context.Context, frame can.Frame) error { + c := <-b.tx + c.request <- transmission{ctx, frame} + return <-c.err +} + +func (b Bus) Receive(ctx context.Context) (can.Frame, error) { + select { + case frame := <-b.rx: + return frame, nil + case <-ctx.Done(): + return can.Frame{}, ctx.Err() + } +} + +func (b Bus) TryReceive() (can.Frame, bool) { + select { + case frame := <-b.rx: + return frame, true + default: + return can.Frame{}, false + } +} |