Refactor kafka to pure Go (franz-go), fix DBC stubs, update Dockerfile
This commit is contained in:
231
pkg/can-go/internal/generate/compile.go
Normal file
231
pkg/can-go/internal/generate/compile.go
Normal file
@@ -0,0 +1,231 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user