232 lines
6.4 KiB
Go
232 lines
6.4 KiB
Go
package generate
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc"
|
|
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor"
|
|
)
|
|
|
|
type CompileResult struct {
|
|
Hash string
|
|
Database *descriptor.Database
|
|
Warnings []error
|
|
}
|
|
|
|
func Compile(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, ECUs: make(map[string]bool)},
|
|
defs: defs,
|
|
}
|
|
c.collectDescriptors()
|
|
c.addMetadata()
|
|
c.sortDescriptors()
|
|
|
|
hash := ComputeHash(data)
|
|
return &CompileResult{Hash: hash, 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() {
|
|
// find ECU names from messages
|
|
re := regexp.MustCompile(`^[0-9A-Za-z]+`)
|
|
|
|
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
|
|
}
|
|
|
|
// add ECU names to set
|
|
ecu := re.FindString(string(def.Name))
|
|
if ecu != "" {
|
|
c.db.ECUs[ecu] = true
|
|
}
|
|
|
|
message := &descriptor.Message{
|
|
Name: string(def.Name),
|
|
ID: def.MessageID.ToCAN(),
|
|
IsExtended: def.MessageID.IsExtended(),
|
|
Length: uint16(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: uint16(signalDef.StartBit),
|
|
Length: uint16(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[message.ID] = 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.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: int(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 {
|
|
if m == nil {
|
|
continue
|
|
}
|
|
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
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func ComputeHash(input []byte) string {
|
|
h := sha256.New()
|
|
h.Write(input)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|