Skip to content

Commit

Permalink
importer backbone
Browse files Browse the repository at this point in the history
  • Loading branch information
FerroO2000 committed Apr 28, 2024
1 parent 75ee628 commit 1eb15de
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 10 deletions.
22 changes: 18 additions & 4 deletions dbc/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// It is based on the version 1.0.5 of the DBC file format.
package dbc

import "fmt"

// FileExtension is the extension of a DBC file.
const FileExtension = ".dbc"

Expand All @@ -15,6 +17,18 @@ type Location struct {
Col int
}

func (l *Location) String() string {
return fmt.Sprintf("%s:%d:%d", l.Filename, l.Line, l.Col)
}

type withLocation struct {
loc *Location
}

func (wl *withLocation) Location() *Location {
return wl.loc
}

// File definition:
//
// The DBC file describes the communication of a single CAN network.
Expand Down Expand Up @@ -93,7 +107,7 @@ type BitTiming struct {
//
// node_name = DBC_identifier ;
type Nodes struct {
Location *Location
withLocation

Names []string
}
Expand Down Expand Up @@ -125,7 +139,7 @@ type ValueTable struct {
//
// value_description = unsigned_integer char_string ;
type ValueDescription struct {
Location *Location
withLocation

ID uint32
Name string
Expand Down Expand Up @@ -194,7 +208,7 @@ type ValueEncoding struct {
// If the massage shall have no sender, the string 'Vector__XXX' has to be given
// here.
type Message struct {
Location *Location
withLocation

ID uint32
Name string
Expand Down Expand Up @@ -297,7 +311,7 @@ const (
// be defined in the set of node names in the node section. If the signal shall have no
// receiver, the string 'Vector__XXX' has to be given here.
type Signal struct {
Location *Location
withLocation

Name string
IsMultiplexor bool
Expand Down
8 changes: 4 additions & 4 deletions dbc/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func (p *Parser) parseNodes() (*Nodes, error) {
p.foundNode = true

node := new(Nodes)
node.Location = p.getLocation()
node.withLocation.loc = p.getLocation()

if err := p.expectPunct(punctColon); err != nil {
return nil, err
Expand All @@ -416,7 +416,7 @@ func (p *Parser) parseNodes() (*Nodes, error) {

func (p *Parser) parseValueDescription() (*ValueDescription, error) {
valDesc := new(ValueDescription)
valDesc.Location = p.getLocation()
valDesc.withLocation.loc = p.getLocation()

t := p.scan()
if !t.isNumber() {
Expand Down Expand Up @@ -485,7 +485,7 @@ func (p *Parser) parseMessageID() (uint32, error) {

func (p *Parser) parseMessage() (*Message, error) {
msg := new(Message)
msg.Location = p.getLocation()
msg.withLocation.loc = p.getLocation()

id, err := p.parseMessageID()
if err != nil {
Expand Down Expand Up @@ -544,7 +544,7 @@ func (p *Parser) parseSignalName() (string, error) {

func (p *Parser) parseSignal() (*Signal, error) {
sig := new(Signal)
sig.Location = p.getLocation()
sig.withLocation.loc = p.getLocation()

name, err := p.parseSignalName()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion dbc/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func isEOF(ch rune) bool {
}

func isSpace(ch rune) bool {
return ch == ' ' || ch == '\t' || ch == '\n'
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}

func isLetter(ch rune) bool {
Expand Down
5 changes: 4 additions & 1 deletion dbc/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ type writer struct {
}

func (w *writer) print(format string, a ...any) {
fmt.Fprintf(w.f, format, a...)
_, err := fmt.Fprintf(w.f, format, a...)
if err != nil {
panic(err)
}
}

func (w *writer) println(format string, a ...any) {
Expand Down
230 changes: 230 additions & 0 deletions importer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package acmelib

import (
"fmt"

"github.com/FerroO2000/acmelib/dbc"
)

func ImportDBCFile(dbcFile *dbc.File) (*Bus, error) {
importer := newImporter()
return importer.importFile(dbcFile)
}

type dbcFileLocator interface {
Location() *dbc.Location
}

type importer struct {
bus *Bus

busDesc string
nodeDesc map[string]string
msgDesc map[MessageCANID]string
sigDesc map[string]string

signalEnums map[string]*SignalEnum
}

func newImporter() *importer {
return &importer{
busDesc: "",
nodeDesc: make(map[string]string),
msgDesc: make(map[MessageCANID]string),
sigDesc: make(map[string]string),

signalEnums: make(map[string]*SignalEnum),
}
}

func (i *importer) errorf(dbcLoc dbcFileLocator, err error) error {
return fmt.Errorf("%s : %w", dbcLoc.Location(), err)
}

func (i *importer) getSignalKey(dbcMsgID uint32, sigName string) string {
return fmt.Sprintf("%d_%s", dbcMsgID, sigName)
}

func (i *importer) importFile(dbcFile *dbc.File) (*Bus, error) {
bus := NewBus(dbcFile.Location.Filename)
i.bus = bus

i.importComments(dbcFile.Comments)
bus.SetDesc(i.busDesc)

for _, dbcValEnc := range dbcFile.ValueEncodings {
if err := i.importValueEncoding(dbcValEnc); err != nil {
return nil, err
}
}

if err := i.importNodes(dbcFile.Nodes); err != nil {
return nil, err
}

for _, dbcMsg := range dbcFile.Messages {
if err := i.importMessage(dbcMsg); err != nil {
return nil, err
}
}

return bus, nil
}

func (i *importer) importComments(dbcComments []*dbc.Comment) {
for _, dbcComm := range dbcComments {
switch dbcComm.Kind {
case dbc.CommentGeneral:
i.busDesc = dbcComm.Text

case dbc.CommentNode:
i.nodeDesc[dbcComm.NodeName] = dbcComm.Text

case dbc.CommentMessage:
i.msgDesc[MessageCANID(dbcComm.MessageID)] = dbcComm.Text

case dbc.CommentSignal:
key := i.getSignalKey(dbcComm.MessageID, dbcComm.SignalName)
i.sigDesc[key] = dbcComm.Text
}
}
}

func (i *importer) importValueEncoding(dbcValEnc *dbc.ValueEncoding) error {
if dbcValEnc.Kind != dbc.ValueEncodingSignal {
return nil
}

sigName := dbcValEnc.SignalName
sigEnum := NewSignalEnum(fmt.Sprintf("%s_Enum", sigName))
for _, dbcVal := range dbcValEnc.Values {
if err := sigEnum.AddValue(NewSignalEnumValue(dbcVal.Name, int(dbcVal.ID))); err != nil {
return i.errorf(dbcVal, err)
}
}

i.signalEnums[i.getSignalKey(dbcValEnc.MessageID, sigName)] = sigEnum

return nil
}

func (i *importer) importNodes(dbcNodes *dbc.Nodes) error {
for idx, nodeName := range dbcNodes.Names {
tmpNode := NewNode(nodeName, NodeID(idx))

if desc, ok := i.nodeDesc[nodeName]; ok {
tmpNode.SetDesc(desc)
}

if err := i.bus.AddNode(tmpNode); err != nil {
return i.errorf(dbcNodes, err)
}
}

if err := i.bus.AddNode(NewNode(dbc.DummyNode, 1024)); err != nil {
return i.errorf(dbcNodes, err)
}

return nil
}

func (i *importer) importMessage(dbcMsg *dbc.Message) error {
msg := NewMessage(dbcMsg.Name, int(dbcMsg.Size))

msgID := MessageCANID(dbcMsg.ID)
msg.SetCANID(msgID)

if desc, ok := i.msgDesc[msgID]; ok {
msg.SetDesc(desc)
}

receivers := make(map[string]bool)
for _, dbcSig := range dbcMsg.Signals {
tmpSig, err := i.importSignal(dbcSig, dbcMsg.ID)
if err != nil {
return err
}

startBit := int(dbcSig.StartBit)
if tmpSig.ByteOrder() == SignalByteOrderBigEndian {
startBit -= (tmpSig.GetSize() - 1)
}

if err := msg.InsertSignal(tmpSig, startBit); err != nil {
return i.errorf(dbcSig, err)
}

for _, rec := range dbcSig.Receivers {
receivers[rec] = true
}
}

for recName := range receivers {
if recName == dbc.DummyNode {
continue
}

recNode, err := i.bus.GetNodeByName(recName)
if err != nil {
return i.errorf(dbcMsg, err)
}

msg.AddReceiver(recNode)
}

sendNode, err := i.bus.GetNodeByName(dbcMsg.Transmitter)
if err != nil {
return i.errorf(dbcMsg, err)
}

if err := sendNode.AddMessage(msg); err != nil {
return i.errorf(dbcMsg, err)
}

return nil
}

func (i *importer) importSignal(dbcSig *dbc.Signal, dbcMsgID uint32) (Signal, error) {
var sig Signal

sigName := dbcSig.Name
sigKey := i.getSignalKey(dbcMsgID, sigName)

if sigEnum, ok := i.signalEnums[sigKey]; ok {
enumSig, err := NewEnumSignal(sigName, sigEnum)
if err != nil {
return nil, i.errorf(dbcSig, err)
}
sig = enumSig

} else if dbcSig.IsMultiplexor {
// mux signal
} else {
signed := false
if dbcSig.ValueType == dbc.SignalSigned {
signed = true
}

sigType, err := NewIntegerSignalType(fmt.Sprintf("%s_Type", sigName), int(dbcSig.Size), signed)
if err != nil {
return nil, i.errorf(dbcSig, err)
}

stdSig, err := NewStandardSignal(sigName, sigType)
if err != nil {
return nil, i.errorf(dbcSig, err)
}

sig = stdSig
}

if desc, ok := i.sigDesc[sigKey]; ok {
sig.SetDesc(desc)
}

if dbcSig.ByteOrder == dbc.SignalBigEndian {
sig.SetByteOrder(SignalByteOrderBigEndian)
}

return sig, nil
}
27 changes: 27 additions & 0 deletions importer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package acmelib

import (
"os"
"testing"

"github.com/FerroO2000/acmelib/dbc"
"github.com/stretchr/testify/assert"
)

func Test_ImportDBCFile(t *testing.T) {
assert := assert.New(t)

file, err := os.ReadFile(cmpExportBusFilename)
assert.NoError(err)

parser := dbc.NewParser("test_ExportBus", file)
dbcFile, err := parser.Parse()
assert.NoError(err)

t.Log(dbcFile)

// bus, err := ImportDBCFile(dbcFile)
// assert.NoError(err)

// t.Log(bus)
}
2 changes: 2 additions & 0 deletions signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ type Signal interface {
EntityID() EntityID
// Name returns the name of the signal.
Name() string
// SetDesc stes the description of the signal.
SetDesc(desc string)
// Desc returns the description of the signal.
Desc() string
// CreateTime returns the creation time of the signal.
Expand Down

0 comments on commit 1eb15de

Please sign in to comment.