mirror of
https://github.com/bettercap/bettercap.git
synced 2024-11-08 06:30:13 -08:00
228 lines
6.6 KiB
Go
228 lines
6.6 KiB
Go
package can
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
"go.einride.tech/can/pkg/dbc"
|
|
"go.einride.tech/can/pkg/descriptor"
|
|
)
|
|
|
|
type CompileResult struct {
|
|
Database *descriptor.Database
|
|
Warnings []error
|
|
}
|
|
|
|
func dbcCompile(sourceFile string, data []byte) (result *CompileResult, err error) {
|
|
p := dbc.NewParser(sourceFile, data)
|
|
if err := p.Parse(); err != nil {
|
|
return nil, fmt.Errorf("failed to parse DBC source file: %w", err)
|
|
}
|
|
defs := p.Defs()
|
|
c := &compiler{
|
|
db: &descriptor.Database{SourceFile: sourceFile},
|
|
defs: defs,
|
|
}
|
|
c.collectDescriptors()
|
|
c.addMetadata()
|
|
c.sortDescriptors()
|
|
return &CompileResult{Database: c.db, Warnings: c.warnings}, nil
|
|
}
|
|
|
|
type compileError struct {
|
|
def dbc.Def
|
|
reason string
|
|
}
|
|
|
|
func (e *compileError) Error() string {
|
|
return fmt.Sprintf("failed to compile: %v (%v)", e.reason, e.def)
|
|
}
|
|
|
|
type compiler struct {
|
|
db *descriptor.Database
|
|
defs []dbc.Def
|
|
warnings []error
|
|
}
|
|
|
|
func (c *compiler) addWarning(warning error) {
|
|
c.warnings = append(c.warnings, warning)
|
|
}
|
|
|
|
func (c *compiler) collectDescriptors() {
|
|
for _, def := range c.defs {
|
|
switch def := def.(type) {
|
|
case *dbc.VersionDef:
|
|
c.db.Version = def.Version
|
|
case *dbc.MessageDef:
|
|
if def.MessageID == dbc.IndependentSignalsMessageID {
|
|
continue // don't compile
|
|
}
|
|
message := &descriptor.Message{
|
|
Name: string(def.Name),
|
|
ID: def.MessageID.ToCAN(),
|
|
IsExtended: def.MessageID.IsExtended(),
|
|
Length: uint8(def.Size),
|
|
SenderNode: string(def.Transmitter),
|
|
}
|
|
for _, signalDef := range def.Signals {
|
|
signal := &descriptor.Signal{
|
|
Name: string(signalDef.Name),
|
|
IsBigEndian: signalDef.IsBigEndian,
|
|
IsSigned: signalDef.IsSigned,
|
|
IsMultiplexer: signalDef.IsMultiplexerSwitch,
|
|
IsMultiplexed: signalDef.IsMultiplexed,
|
|
MultiplexerValue: uint(signalDef.MultiplexerSwitch),
|
|
Start: uint8(signalDef.StartBit),
|
|
Length: uint8(signalDef.Size),
|
|
Scale: signalDef.Factor,
|
|
Offset: signalDef.Offset,
|
|
Min: signalDef.Minimum,
|
|
Max: signalDef.Maximum,
|
|
Unit: signalDef.Unit,
|
|
}
|
|
for _, receiver := range signalDef.Receivers {
|
|
signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver))
|
|
}
|
|
message.Signals = append(message.Signals, signal)
|
|
}
|
|
c.db.Messages = append(c.db.Messages, message)
|
|
case *dbc.NodesDef:
|
|
for _, node := range def.NodeNames {
|
|
c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *compiler) addMetadata() {
|
|
for _, def := range c.defs {
|
|
switch def := def.(type) {
|
|
case *dbc.SignalValueTypeDef:
|
|
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared signal"})
|
|
continue
|
|
}
|
|
switch def.SignalValueType {
|
|
case dbc.SignalValueTypeInt:
|
|
signal.IsFloat = false
|
|
case dbc.SignalValueTypeFloat32:
|
|
if signal.Length == 32 {
|
|
signal.IsFloat = true
|
|
} else {
|
|
reason := fmt.Sprintf("incorrect float signal length: %d", signal.Length)
|
|
c.addWarning(&compileError{def: def, reason: reason})
|
|
}
|
|
default:
|
|
reason := fmt.Sprintf("unsupported signal value type: %v", def.SignalValueType)
|
|
c.addWarning(&compileError{def: def, reason: reason})
|
|
}
|
|
case *dbc.CommentDef:
|
|
switch def.ObjectType {
|
|
case dbc.ObjectTypeMessage:
|
|
if def.MessageID == dbc.IndependentSignalsMessageID {
|
|
continue // don't compile
|
|
}
|
|
message, ok := c.db.Message(def.MessageID.ToCAN())
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared message"})
|
|
continue
|
|
}
|
|
message.Description = def.Comment
|
|
case dbc.ObjectTypeSignal:
|
|
if def.MessageID == dbc.IndependentSignalsMessageID {
|
|
continue // don't compile
|
|
}
|
|
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared signal"})
|
|
continue
|
|
}
|
|
signal.Description = def.Comment
|
|
case dbc.ObjectTypeNetworkNode:
|
|
node, ok := c.db.Node(string(def.NodeName))
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared node"})
|
|
continue
|
|
}
|
|
node.Description = def.Comment
|
|
}
|
|
case *dbc.ValueDescriptionsDef:
|
|
if def.MessageID == dbc.IndependentSignalsMessageID {
|
|
continue // don't compile
|
|
}
|
|
if def.ObjectType != dbc.ObjectTypeSignal {
|
|
continue // don't compile
|
|
}
|
|
signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared signal"})
|
|
continue
|
|
}
|
|
for _, valueDescription := range def.ValueDescriptions {
|
|
signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{
|
|
Description: valueDescription.Description,
|
|
Value: int64(valueDescription.Value),
|
|
})
|
|
}
|
|
case *dbc.AttributeValueForObjectDef:
|
|
switch def.ObjectType {
|
|
case dbc.ObjectTypeMessage:
|
|
msg, ok := c.db.Message(def.MessageID.ToCAN())
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared message"})
|
|
continue
|
|
}
|
|
switch def.AttributeName {
|
|
case "GenMsgSendType":
|
|
if err := msg.SendType.UnmarshalString(def.StringValue); err != nil {
|
|
c.addWarning(&compileError{def: def, reason: err.Error()})
|
|
continue
|
|
}
|
|
case "GenMsgCycleTime":
|
|
msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond
|
|
case "GenMsgDelayTime":
|
|
msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond
|
|
}
|
|
case dbc.ObjectTypeSignal:
|
|
sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
|
|
if !ok {
|
|
c.addWarning(&compileError{def: def, reason: "no declared signal"})
|
|
}
|
|
if def.AttributeName == "GenSigStartValue" {
|
|
sig.DefaultValue = int(def.IntValue)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *compiler) sortDescriptors() {
|
|
// Sort nodes by name
|
|
sort.Slice(c.db.Nodes, func(i, j int) bool {
|
|
return c.db.Nodes[i].Name < c.db.Nodes[j].Name
|
|
})
|
|
// Sort messages by ID
|
|
sort.Slice(c.db.Messages, func(i, j int) bool {
|
|
return c.db.Messages[i].ID < c.db.Messages[j].ID
|
|
})
|
|
for _, m := range c.db.Messages {
|
|
m := m
|
|
// Sort signals by start (and multiplexer value)
|
|
sort.Slice(m.Signals, func(j, k int) bool {
|
|
if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue {
|
|
return true
|
|
}
|
|
return m.Signals[j].Start < m.Signals[k].Start
|
|
})
|
|
// Sort value descriptions by value
|
|
for _, s := range m.Signals {
|
|
s := s
|
|
sort.Slice(s.ValueDescriptions, func(k, l int) bool {
|
|
return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value
|
|
})
|
|
}
|
|
}
|
|
}
|