github.com/jshiv/can-go@v0.2.1-0.20210224011015-069e90e90bdf/internal/generate/compile.go (about)

     1  package generate
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"time"
     7  
     8  	"go.einride.tech/can/pkg/dbc"
     9  	"go.einride.tech/can/pkg/descriptor"
    10  )
    11  
    12  type CompileResult struct {
    13  	Database *descriptor.Database
    14  	Warnings []error
    15  }
    16  
    17  func Compile(sourceFile string, data []byte) (result *CompileResult, err error) {
    18  	p := dbc.NewParser(sourceFile, data)
    19  	if err := p.Parse(); err != nil {
    20  		return nil, fmt.Errorf("failed to parse DBC source file: %w", err)
    21  	}
    22  	defs := p.Defs()
    23  	c := &compiler{
    24  		db:   &descriptor.Database{SourceFile: sourceFile},
    25  		defs: defs,
    26  	}
    27  	c.collectDescriptors()
    28  	c.addMetadata()
    29  	c.sortDescriptors()
    30  	return &CompileResult{Database: c.db, Warnings: c.warnings}, nil
    31  }
    32  
    33  type compileError struct {
    34  	def    dbc.Def
    35  	reason string
    36  }
    37  
    38  func (e *compileError) Error() string {
    39  	return fmt.Sprintf("failed to compile: %v (%v)", e.reason, e.def)
    40  }
    41  
    42  type compiler struct {
    43  	db       *descriptor.Database
    44  	defs     []dbc.Def
    45  	warnings []error
    46  }
    47  
    48  func (c *compiler) addWarning(warning error) {
    49  	c.warnings = append(c.warnings, warning)
    50  }
    51  
    52  func (c *compiler) collectDescriptors() {
    53  	for _, def := range c.defs {
    54  		switch def := def.(type) {
    55  		case *dbc.VersionDef:
    56  			c.db.Version = def.Version
    57  		case *dbc.MessageDef:
    58  			if def.MessageID == dbc.IndependentSignalsMessageID {
    59  				continue // don't compile
    60  			}
    61  			message := &descriptor.Message{
    62  				Name:       string(def.Name),
    63  				ID:         def.MessageID.ToCAN(),
    64  				IsExtended: def.MessageID.IsExtended(),
    65  				Length:     uint8(def.Size),
    66  				SenderNode: string(def.Transmitter),
    67  			}
    68  			for _, signalDef := range def.Signals {
    69  				signal := &descriptor.Signal{
    70  					Name:             string(signalDef.Name),
    71  					IsBigEndian:      signalDef.IsBigEndian,
    72  					IsSigned:         signalDef.IsSigned,
    73  					IsMultiplexer:    signalDef.IsMultiplexerSwitch,
    74  					IsMultiplexed:    signalDef.IsMultiplexed,
    75  					MultiplexerValue: uint(signalDef.MultiplexerSwitch),
    76  					Start:            uint8(signalDef.StartBit),
    77  					Length:           uint8(signalDef.Size),
    78  					Scale:            signalDef.Factor,
    79  					Offset:           signalDef.Offset,
    80  					Min:              signalDef.Minimum,
    81  					Max:              signalDef.Maximum,
    82  					Unit:             signalDef.Unit,
    83  				}
    84  				for _, receiver := range signalDef.Receivers {
    85  					signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver))
    86  				}
    87  				message.Signals = append(message.Signals, signal)
    88  			}
    89  			c.db.Messages = append(c.db.Messages, message)
    90  		case *dbc.NodesDef:
    91  			for _, node := range def.NodeNames {
    92  				c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)})
    93  			}
    94  		}
    95  	}
    96  }
    97  
    98  func (c *compiler) addMetadata() {
    99  	for _, def := range c.defs {
   100  		switch def := def.(type) {
   101  		case *dbc.CommentDef:
   102  			switch def.ObjectType {
   103  			case dbc.ObjectTypeMessage:
   104  				if def.MessageID == dbc.IndependentSignalsMessageID {
   105  					continue // don't compile
   106  				}
   107  				message, ok := c.db.Message(def.MessageID.ToCAN())
   108  				if !ok {
   109  					c.addWarning(&compileError{def: def, reason: "no declared message"})
   110  					continue
   111  				}
   112  				message.Description = def.Comment
   113  			case dbc.ObjectTypeSignal:
   114  				if def.MessageID == dbc.IndependentSignalsMessageID {
   115  					continue // don't compile
   116  				}
   117  				signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
   118  				if !ok {
   119  					c.addWarning(&compileError{def: def, reason: "no declared signal"})
   120  					continue
   121  				}
   122  				signal.Description = def.Comment
   123  			case dbc.ObjectTypeNetworkNode:
   124  				node, ok := c.db.Node(string(def.NodeName))
   125  				if !ok {
   126  					c.addWarning(&compileError{def: def, reason: "no declared node"})
   127  					continue
   128  				}
   129  				node.Description = def.Comment
   130  			}
   131  		case *dbc.ValueDescriptionsDef:
   132  			if def.MessageID == dbc.IndependentSignalsMessageID {
   133  				continue // don't compile
   134  			}
   135  			if def.ObjectType != dbc.ObjectTypeSignal {
   136  				continue // don't compile
   137  			}
   138  			signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
   139  			if !ok {
   140  				c.addWarning(&compileError{def: def, reason: "no declared signal"})
   141  				continue
   142  			}
   143  			for _, valueDescription := range def.ValueDescriptions {
   144  				signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{
   145  					Description: valueDescription.Description,
   146  					Value:       int(valueDescription.Value),
   147  				})
   148  			}
   149  		case *dbc.AttributeValueForObjectDef:
   150  			switch def.ObjectType {
   151  			case dbc.ObjectTypeMessage:
   152  				msg, ok := c.db.Message(def.MessageID.ToCAN())
   153  				if !ok {
   154  					c.addWarning(&compileError{def: def, reason: "no declared message"})
   155  					continue
   156  				}
   157  				switch def.AttributeName {
   158  				case "GenMsgSendType":
   159  					if err := msg.SendType.UnmarshalString(def.StringValue); err != nil {
   160  						c.addWarning(&compileError{def: def, reason: err.Error()})
   161  						continue
   162  					}
   163  				case "GenMsgCycleTime":
   164  					msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond
   165  				case "GenMsgDelayTime":
   166  					msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond
   167  				}
   168  			case dbc.ObjectTypeSignal:
   169  				sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName))
   170  				if !ok {
   171  					c.addWarning(&compileError{def: def, reason: "no declared signal"})
   172  				}
   173  				if def.AttributeName == "GenSigStartValue" {
   174  					sig.DefaultValue = int(def.IntValue)
   175  				}
   176  			}
   177  		}
   178  	}
   179  }
   180  
   181  func (c *compiler) sortDescriptors() {
   182  	// Sort nodes by name
   183  	sort.Slice(c.db.Nodes, func(i, j int) bool {
   184  		return c.db.Nodes[i].Name < c.db.Nodes[j].Name
   185  	})
   186  	// Sort messages by ID
   187  	sort.Slice(c.db.Messages, func(i, j int) bool {
   188  		return c.db.Messages[i].ID < c.db.Messages[j].ID
   189  	})
   190  	for _, m := range c.db.Messages {
   191  		m := m
   192  		// Sort signals by start (and multiplexer value)
   193  		sort.Slice(m.Signals, func(j, k int) bool {
   194  			if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue {
   195  				return true
   196  			}
   197  			return m.Signals[j].Start < m.Signals[k].Start
   198  		})
   199  		// Sort value descriptions by value
   200  		for _, s := range m.Signals {
   201  			s := s
   202  			sort.Slice(s.ValueDescriptions, func(k, l int) bool {
   203  				return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value
   204  			})
   205  		}
   206  	}
   207  }