Files
cloud-services/pkg/can-go/README.md

169 lines
5.6 KiB
Markdown

# :electric_plug: CAN Go
[![PkgGoDev][pkg-badge]][pkg]
[![GoReportCard][report-badge]][report]
[![Codecov][codecov-badge]][codecov]
[pkg-badge]: https://pkg.go.dev/badge/github.com/Fisker-Inc/project-ai-can-go
[pkg]: https://pkg.go.dev/github.com/Fisker-Inc/project-ai-can-go
[report-badge]: https://goreportcard.com/badge/github.com/Fisker-Inc/project-ai-can-go
[report]: https://goreportcard.com/report/github.com/Fisker-Inc/project-ai-can-go
[codecov-badge]: https://codecov.io/gh/einride/can-go/branch/master/graph/badge.svg
[codecov]: https://codecov.io/gh/einride/can-go
CAN toolkit for Go programmers.
can-go makes use of the Linux SocketCAN abstraction for CAN communication.
(See the [SocketCAN][socketcan] documentation for more details).
[socketcan]: https://www.kernel.org/doc/Documentation/networking/can.txt
## Modifications to the Original Repo
Original repo: `https://github.com/jshiv/can-go/tree/623b1140d54a845026249a5bbbaf23f44c614173`.
Comment out the value descriptor generation code within `can-go/internal/generate/file.go`, lines 128-141. This code creates compile errors due to duplicate values within the `.dbc` file provided.
## DBC Go Code Library Generation
1. Place `.dbc` file into `orig/` folder.
2. Run `scripts/main.go`:
```
go run scripts/main.go
```
This will translate all Chinese variable values within the file to English. Any non-alphanumeric characters are also removed. **NOTE** The default read file is labelled `orig/121-N60AB_ADASBUS_Matrix_CANFD_V2.3.dbc` and the default write file is labelled `dbc/n60.dbc`.
3. There will still be leftover Chinese characters in the `.dbc` file, I chose to just remove them.
4. Generate Go code from cleaned `.dbc` file:
```
can-go> go run cmd/cantool/main.go generate data/dbc dbc
```
## Examples
### Decoding CAN messages
Decoding CAN messages from byte arrays can be done using `can.Payload`
```go
func main() {
// DBC file
var dbcFile = []byte(`
VERSION ""
NS_ :
BS_:
BU_: DBG DRIVER IO MOTOR SENSOR
BO_ 1530 DisconnectState: 14 MOTOR
SG_ LockCountRearRight : 91|20@0+ (1,0) [0|1048575] "" IO
SG_ DisconnectStateRearRight : 95|4@0+ (1,0) [0|5] "" IO
SG_ CurrentRearRight : 79|16@0+ (1,0) [0|65535] "" IO
SG_ DisconnectStateRearRightTarget : 64|1@0+ (1,0) [0|1] "" IO
SG_ TargetSpeedRearRight : 63|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO
SG_ LockCountRearLeft : 35|20@0+ (1,0) [0|1048575] "" IO
SG_ DisconnectStateRearLeft : 39|4@0+ (1,0) [0|5] "" IO
SG_ CurrentRearLeft : 23|16@0+ (1,0) [0|65535] "" IO
SG_ DisconnectStateRearLeftTarget : 8|1@0+ (1,0) [0|1] "" IO
SG_ TargetSpeedRearLeft : 7|15@0+ (0.125,-2048) [-2048|2047.875] "rad/s" IO
VAL_ 1530 DisconnectStateRearRight 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ;
VAL_ 1530 DisconnectStateRearLeft 0 "Undefined" 1 "Locked" 2 "Unlocked" 3 "Locking" 4 "Unlocking" 5 "Faulted" ;
`)
// Create payload from hex string
byteStringHex := "8000000420061880000005200600"
p, _ := can.PayloadFromHex(byteStringHex)
// Load example dbc file
c, _ := generate.Compile("test.dbc", dbcFile)
db := *c.Database
// Decode message frame ID 1530
message, _ := db.Message(uint32(1530))
decodedSignals := message.Decode(&p)
for _, signal := range decodedSignals {
fmt.Printf("Signal: %s, Value: %f, Description: %s\n", signal.Signal.Name, signal.Value, signal.Description)
}
}
```
```
Signal: TargetSpeedRearLeft, Value: 0.000000, Description:
Signal: DisconnectStateRearLeftTarget, Value: 0.000000, Description:
Signal: CurrentRearLeft, Value: 4.000000, Description:
Signal: LockCountRearLeft, Value: 1560.000000, Description:
Signal: DisconnectStateRearLeft, Value: 2.000000, Description: Unlocked
Signal: TargetSpeedRearRight, Value: 0.000000, Description:
Signal: DisconnectStateRearRightTarget, Value: 0.000000, Description:
Signal: CurrentRearRight, Value: 5.000000, Description:
Signal: LockCountRearRight, Value: 1536.000000, Description:
Signal: DisconnectStateRearRight, Value: 2.000000, Description: Unlocked
```
### Receiving CAN frames
Receiving CAN frames from a socketcan interface.
```go
func main() {
// Error handling omitted to keep example simple
conn, _ := socketcan.DialContext(context.Background(), "can", "can0")
recv := socketcan.NewReceiver(conn)
for recv.Receive() {
frame := recv.Frame()
fmt.Println(frame.String())
}
}
```
### Sending CAN frames/messages
Sending CAN frames to a socketcan interface.
```go
func main() {
// Error handling omitted to keep example simple
conn, _ := socketcan.DialContext(context.Background(), "can", "can0")
frame := can.Frame{}
tx := socketcan.NewTransmitter(conn)
_ = tx.TransmitFrame(context.Background(), frame)
}
```
### Generating Go code from a DBC file
It is possible to generate Go code from a `.dbc` file.
```
$ go run github.com/Fisker-Inc/project-ai-can-go/cmd/cantool generate <dbc file root folder> <output folder>
```
In order to generate Go code that makes sense, we currently perform some
validations when parsing the DBC file so there may need to be some changes
on the DBC file to make it work
After generating Go code we can marshal a message to a frame:
```go
// import etruckcan "github.com/myproject/myrepo/gen"
auxMsg := etruckcan.NewAuxiliary().SetHeadLights(etruckcan.Auxiliary_HeadLights_LowBeam)
frame := auxMsg.Frame()
```
Or unmarshal a frame to a message:
```go
// import etruckcan "github.com/myproject/myrepo/gen"
// Error handling omitted for simplicity
_ := recv.Receive()
frame := recv.Frame()
var auxMsg *etruckcan.Auxiliary
_ = auxMsg.UnmarshalFrame(frame)
```