@yinyinfurong_zmr/dbc-can
Version:
A general purpose CAN (Controller Area Network) toolbox with support for .dbc file parsing, CAN message decoding, and more
537 lines • 22.3 kB
JavaScript
import { ASTKinds, Parser, } from '../parser/parser';
import { computeDataType } from '../shared/DataTypes';
import Can from '../can/Can';
export default class DbcParser extends Parser {
constructor(rawString) {
super(rawString);
this.parseResult = this.parse();
}
updateData(data) {
if (this.hasKindProp(this.parseResult.ast)) {
const ast = this.parseResult.ast;
switch (this.parseResult.ast.kind) {
case ASTKinds.CanMessage:
this.addMessage(data, this.parseResult.ast);
break;
case ASTKinds.SignalMultiplexVal:
this.addSignalMultiplexValue(data, this.parseResult.ast);
break;
case ASTKinds.CanSignal:
this.addSignal(data, this.parseResult.ast);
break;
case ASTKinds.Version:
this.addVersion(data, this.parseResult.ast);
break;
case ASTKinds.NewSymbolValue:
this.addNewSymbolValue(data, this.parseResult.ast);
break;
case ASTKinds.Val:
this.addVal(data, this.parseResult.ast);
break;
case ASTKinds.ValTable:
this.addValTable(data, this.parseResult.ast);
break;
case ASTKinds.AttributeValue:
this.addAttributeValue(data, this.parseResult.ast);
break;
case ASTKinds.AttributeDefault:
this.addAttributeDefaultValue(data, this.parseResult.ast);
break;
case ASTKinds.GlobalAttribute:
this.addGlobalAttribute(data, this.parseResult.ast);
break;
case ASTKinds.MessageAttribute:
this.addMessageAttribute(data, this.parseResult.ast);
break;
case ASTKinds.SignalAttribute:
this.addSignalAttribute(data, this.parseResult.ast);
break;
case ASTKinds.NodeAttribute:
this.addNodeAttribute(data, this.parseResult.ast);
break;
case ASTKinds.CanNode:
this.addNode(data, this.parseResult.ast);
break;
case ASTKinds.SignalComment:
this.addSignalComment(data, this.parseResult.ast);
break;
case ASTKinds.MessageComment:
this.addMessageComment(data, this.parseResult.ast);
break;
case ASTKinds.NodeComment:
this.addNodeComment(data, this.parseResult.ast);
break;
case ASTKinds.Comment:
this.addComment(data, this.parseResult.ast);
break;
case ASTKinds.CanEnvironmentVariable:
this.addEnvironmentVariable(data, this.parseResult.ast);
break;
case ASTKinds.EnvironmentAttribute:
this.addEnvironmentAttribute(data, this.parseResult.ast);
break;
case ASTKinds.EnvironmentVal:
this.addEnvironmentVal(data, this.parseResult.ast);
break;
case ASTKinds.EnvironmentVariableComment:
this.addEnvironmentVariableComment(data, this.parseResult.ast);
break;
case ASTKinds.EnvironmentVarData:
this.addEnvironmentVariableData(data, this.parseResult.ast);
break;
case ASTKinds.MessageTransmitter:
this.addMessageTransmitter(data, this.parseResult.ast);
break;
case ASTKinds.CanSignalGroup:
this.addSignalGroup(data, this.parseResult.ast);
break;
case ASTKinds.SigValType:
this.addSignalValType(data, this.parseResult.ast);
break;
}
}
return data;
}
/* START OF HELPER FUNCTIONS FOR UPDATEDATA CALLBACK */
addComment(dbc, data) {
dbc.description = data.comment;
}
addNode(dbc, data) {
data.node_names.forEach((nodeName) => {
const node = {};
node.name = nodeName;
node.description = null;
node.attributes = new Map();
if (node.name !== '') {
dbc.nodes.set(nodeName, node);
}
});
}
addEnvironmentVariableData(dbc, data) {
const envVar = dbc.environmentVariables.get(data.name);
if (envVar) {
envVar.dataBytesLength = data.value;
}
}
addEnvironmentVariable(dbc, data) {
const envVar = {};
envVar.name = data.name;
envVar.type = this.convert2EnvType(data.type);
envVar.initialValue = data.initial_value;
envVar.min = data.min;
envVar.max = data.max;
envVar.evId = data.ev_id;
envVar.accessNode = data.node;
envVar.accessType = this.convert2AccessType(data.access_type);
envVar.attributes = new Map();
envVar.valueTable = null;
envVar.dataBytesLength = null;
envVar.description = null;
envVar.unit = data.unit;
dbc.environmentVariables.set(envVar.name, envVar);
}
addMessage(dbc, data) {
const can = new Can();
const message = {};
message.id = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
message.extended = can.isIdExtended(data.id);
message.dlc = data.dlc;
message.name = data.name;
message.sendingNode = data.node;
message.signals = new Map();
message.baseSignals = new Map();
message.multiplexSignals = new Map();
message.description = null;
message.attributes = new Map();
message.signalGroups = new Map();
dbc.messages.set(message.name, message);
}
addSignalMultiplexValue(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const message = Array.from(dbc.messages.values()).find((value) => value.id === canId);
if (message) {
const mulPlexSig = message.multiplexSignals.get(data.switch_name);
const mulPlexSigBase = message.multiplexSignals.get(data.name);
const dataSignal = message.signals.get(data.name);
if (mulPlexSig && dataSignal) {
let multiplexSignal = {};
if (mulPlexSigBase) {
multiplexSignal = mulPlexSigBase;
message.multiplexSignals.delete(data.name);
}
else {
multiplexSignal = {};
multiplexSignal.signal = dataSignal;
multiplexSignal.children = new Map();
}
data.value_ranges.forEach((valRange) => {
const start = parseInt(valRange[0], 10);
const end = parseInt(valRange[1], 10);
for (let i = start; i <= end; i++) {
let children = mulPlexSig.children.get(i);
if (!children) {
mulPlexSig.children.set(i, []);
children = mulPlexSig.children.get(i);
}
children === null || children === void 0 ? void 0 : children.push(multiplexSignal);
}
});
}
}
}
addSignal(dbc, data) {
var _a;
const signal = {};
signal.name = data.name;
signal.endian = data.endian === 'Motorola' ? 'Motorola' : 'Intel';
signal.startBit = data.start_bit;
signal.length = data.length;
signal.signed = data.signed;
signal.max = data.max;
signal.min = data.min;
signal.factor = data.factor;
signal.offset = data.offset;
signal.multiplex = data.multiplex;
signal.multiplexer = data.multiplexer;
signal.receivingNodes = data.nodes;
signal.unit = data.unit;
signal.valueTable = null;
signal.description = null;
signal.attributes = new Map();
signal.dataType = computeDataType(signal.length, signal.signed);
/* Signals come directly after a message tag, so we can just append
the current signal instance to the last message found in the array */
const messageList = Array.from(dbc.messages.keys());
const lastKey = messageList[messageList.length - 1];
let msg;
if (dbc.messages.has(lastKey)) {
msg = dbc.messages.get(lastKey);
msg === null || msg === void 0 ? void 0 : msg.signals.set(signal.name, signal);
if (!signal.multiplexer && ((_a = signal.multiplex) !== null && _a !== void 0 ? _a : '').length === 0) {
msg === null || msg === void 0 ? void 0 : msg.baseSignals.set(signal.name, signal);
}
else if (signal.multiplexer) {
const multiplexSignal = {};
multiplexSignal.signal = signal;
multiplexSignal.children = new Map();
msg === null || msg === void 0 ? void 0 : msg.multiplexSignals.set(multiplexSignal.signal.name, multiplexSignal);
}
}
}
addSignalComment(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const msgName = this.getMessageNameFromId(dbc, canId);
if (msgName) {
const msg = dbc.messages.get(msgName);
const signal = msg === null || msg === void 0 ? void 0 : msg.signals.get(data.name);
if (signal) {
signal.description = data.comment;
}
}
else {
// TODO: Throw error that message does not exist in data
}
}
addEnvironmentVariableComment(dbc, data) {
const envVars = dbc.environmentVariables;
const envVar = envVars.get(data.name);
if (envVar) {
envVar.description = data.comment;
}
}
addMessageComment(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const msgName = this.getMessageNameFromId(dbc, canId);
if (msgName) {
const msg = dbc.messages.get(msgName);
if (msg) {
msg.description = data.comment;
}
}
else {
// TODO: Throw error that message does not exist in data
}
}
addNodeComment(dbc, data) {
const node = dbc.nodes.get(data.name);
if (node) {
node.description = data.comment;
}
}
addVersion(dbc, data) {
dbc.version = data.version;
}
addNewSymbolValue(dbc, data) {
dbc.newSymbols.push(data.symbol);
}
addEnvironmentVal(dbc, data) {
const envVar = dbc.environmentVariables.get(data.name);
if (envVar) {
envVar.valueTable = data.enum;
}
}
addVal(dbc, data) {
// Need to find specific signal in dataset to append signal table to
const messageName = null;
for (const [key, value] of dbc.messages) {
const msg = dbc.messages.get(key);
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
if (msg && msg.id === canId) {
const signals = msg.signals;
const signal = signals.get(data.name);
const table = data.enum;
if (signal) {
signal.valueTable = table;
}
return;
}
}
}
addValTable(dbc, data) {
var _a;
const table = data.enum;
(_a = dbc.valueTables) === null || _a === void 0 ? void 0 : _a.set(data.name, table);
}
convert2AttributeType(str) {
switch (str) {
case 'INT':
return 'INT';
case 'STRING':
return 'STRING';
case 'FLOAT':
return 'FLOAT';
case 'ENUM':
return 'ENUM';
case 'HEX':
return 'HEX';
default:
// Uknown type encountered, convert to string so the remaining props are not parsed
return 'STRING';
}
}
addGlobalAttribute(dbc, data) {
const attribute = {};
const dataType = this.convert2AttributeType(data.type);
attribute.name = data.name;
attribute.type = 'Global';
attribute.dataType = dataType;
attribute.options = new Array();
attribute.defaultValue = null;
attribute.value = null;
attribute.min = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.min : null;
attribute.max = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.max : null;
attribute.options = dataType === 'ENUM' ? data.enum : null;
if (attribute.name && attribute.name !== '') {
dbc.attributes.set(attribute.name, attribute);
}
}
addMessageAttribute(dbc, data) {
const attribute = {};
const dataType = this.convert2AttributeType(data.type);
attribute.name = data.name;
attribute.type = 'Message';
attribute.dataType = this.convert2AttributeType(data.type);
attribute.options = new Array();
attribute.defaultValue = null;
attribute.value = null;
attribute.min = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.min : null;
attribute.max = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.max : null;
attribute.options = dataType === 'ENUM' ? data.enum : null;
if (attribute.name && attribute.name !== '') {
dbc.attributes.set(attribute.name, attribute);
}
}
addSignalAttribute(dbc, data) {
const attribute = {};
const dataType = this.convert2AttributeType(data.type);
attribute.name = data.name;
attribute.type = 'Signal';
attribute.dataType = this.convert2AttributeType(data.type);
attribute.options = new Array();
attribute.defaultValue = null;
attribute.value = null;
attribute.min = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.min : null;
attribute.max = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.max : null;
attribute.options = dataType === 'ENUM' ? data.enum : null;
if (attribute.name && attribute.name !== '') {
dbc.attributes.set(attribute.name, attribute);
}
}
addNodeAttribute(dbc, data) {
const attribute = {};
const dataType = this.convert2AttributeType(data.type);
attribute.name = data.name;
attribute.type = 'Node';
attribute.dataType = this.convert2AttributeType(data.type);
attribute.options = new Array();
attribute.defaultValue = null;
attribute.value = null;
attribute.min = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.min : null;
attribute.max = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.max : null;
attribute.options = dataType === 'ENUM' ? data.enum : null;
if (attribute.name && attribute.name !== '') {
dbc.attributes.set(attribute.name, attribute);
}
}
addEnvironmentAttribute(dbc, data) {
const attribute = {};
const dataType = this.convert2AttributeType(data.type);
attribute.name = data.name;
attribute.type = 'EnvironmentVariable';
attribute.dataType = this.convert2AttributeType(data.type);
attribute.options = new Array();
attribute.defaultValue = null;
attribute.value = null;
attribute.min = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.min : null;
attribute.max = dataType === 'FLOAT' || dataType === 'INT' || dataType === 'HEX' ? data.max : null;
attribute.options = dataType === 'ENUM' ? data.enum : null;
if (attribute.name && attribute.name !== '') {
dbc.attributes.set(attribute.name, attribute);
}
}
addAttributeDefaultValue(dbc, data) {
const attr = dbc.attributes.get(data.name);
if (attr) {
attr.defaultValue = data.value;
attr.value = data.value;
}
}
addAttributeValue(dbc, data) {
if (!dbc.attributes.has(data.name))
return;
const attr = Object.assign({}, dbc.attributes.get(data.name), { value: data.value });
attr.value = data.value;
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const msgName = this.getMessageNameFromId(dbc, canId);
switch (data.type) {
// Add existing attribute to proper type
case 'Signal':
if (msgName) {
const msg = dbc.messages.get(msgName);
if (msg) {
const signal = msg.signals.get(data.signal);
if (signal) {
signal.attributes.set(attr.name, attr);
}
}
}
break;
case 'Message':
if (msgName) {
const msg = dbc.messages.get(msgName);
if (msg) {
msg.attributes.set(attr.name, attr);
}
}
break;
case 'Node':
const node = dbc.nodes.get(data.node);
if (node) {
node.attributes.set(attr.name, attr);
}
break;
case 'Global':
dbc.attributes.set(data.name, attr);
break;
case 'EnvironmentVariable':
const ev = dbc.environmentVariables.get(data.node);
if (ev) {
ev.attributes.set(attr.name, attr);
}
break;
default:
break;
}
}
addMessageTransmitter(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
dbc.networkBridges.set(canId, data.nodes);
}
addSignalGroup(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const name = this.getMessageNameFromId(dbc, canId);
if (name) {
const msg = dbc.messages.get(name);
if (msg) {
const groupData = {
name: data.name,
id: canId,
groupId: data.group_number,
signals: data.signals,
};
msg.signalGroups.set(data.name, groupData);
}
}
}
addSignalValType(dbc, data) {
const can = new Can();
const canId = can.isIdExtended(data.id) ? can.unsetExtendedFlag(data.id) : data.id;
const msgName = this.getMessageNameFromId(dbc, canId);
if (msgName) {
const msg = dbc.messages.get(msgName);
if (msg) {
const signal = msg.signals.get(data.name);
if (signal) {
switch (data.type) {
// TODO: Should we enforce that the data type is float/double even if bits dont match?
case 1:
signal.dataType = computeDataType(signal.length, signal.signed, true);
break;
case 2:
signal.dataType = computeDataType(signal.length, signal.signed, true);
break;
}
}
}
}
}
convert2EnvType(type) {
switch (type) {
case '0':
return 'Integer';
case '1':
return 'Float';
case '2':
return 'String';
default:
return 'String';
}
}
convert2AccessType(type) {
switch (type.trim().slice(-1)) {
case '0':
return 'Unrestricted';
case '1':
return 'Read';
case '2':
return 'Write';
case '3':
return 'ReadWrite';
default:
return 'Read';
}
}
getMessageNameFromId(dbc, id) {
const msgNames = Array.from(dbc.messages.keys());
let msgName = null;
for (const name of msgNames) {
const msg = dbc.messages.get(name);
if (msg && msg.id === id) {
msgName = name;
break;
}
}
return msgName;
}
hasKindProp(obj) {
return typeof obj === 'object' && obj !== null && 'kind' in obj;
}
}
//# sourceMappingURL=DbcParser.js.map