UNPKG

@yinyinfurong_zmr/dbc-can

Version:

A general purpose CAN (Controller Area Network) toolbox with support for .dbc file parsing, CAN message decoding, and more

433 lines 16.6 kB
import Can from '../can/Can'; class Writer { constructor() { this.dbcString = ''; } /** * Main constructor class that will organize and write all * attributes of DbcData structure * @param data dbc data loaded or created using main DBC class */ constructFile(data) { this.dbcString = ''; // Main file attributes this.writeVersion(data.version ? data.version : ''); this.writeNamespace(data.newSymbols); this.writeBusSpeed(data.busSpeed); this.writeNodes(Array.from(data.nodes.keys())); if (data.valueTables) { this.writeValTable(data.valueTables); } // Both messages and signals this.writeMessagesAndSignals(data.messages); this.writeMessageTransmitters(data.networkBridges); // Write all comments if (data.description) { this.writeBaseComment(data.description); } this.writeMessageAndSignalComments(data.messages); this.writeAttributeDefinitions(data); this.writeAttributeDefaults(data); this.writeAttributeValues(data); this.writeSignalGroups(data.messages); this.writeSignalTables(data.messages); this.writeEnvVarTables(data.environmentVariables); this.writeSignalDataType(data.messages); return this.dbcString; } /** * * @param version Version of the dbc file */ writeVersion(version) { const lineContent = `VERSION "${version}"`; this.writeLine(lineContent); this.writeLine(''); } writeMessageTransmitters(transmitters) { transmitters.forEach((transmitter, canId) => { const lineContent = `BO_TX_BU_ ${canId} : ${transmitter.join(',')};`; this.writeLine(lineContent); this.writeLine(''); }); this.writeLine(''); } writeNamespace(ns = null) { const lineContent = `NS_:`; this.writeLine(lineContent); if (ns) { ns.forEach((newSymbol) => { this.writeLine(` ${newSymbol}`); }); } this.writeLine(''); } /** * Speed of the CAN bus, typically expressed as 250, 500, etc * @param busConfiguration Speed of the CAN bus */ writeBusSpeed(busConfiguration) { let lineContent = ''; if (busConfiguration === null) { lineContent = `BS_:`; } else { lineContent = `BS_: ${busConfiguration}`; } this.writeLine(lineContent); this.writeLine(''); } /** * Generic list of nodes that exist for the CAN architecture * @param nodes List of nodes that are attached to messages and signals */ writeNodes(nodes) { let lineContent = ''; if (nodes === null || nodes.length === 0) { lineContent = `BU_:`; } else { // TODO: Actually enumerate list out lineContent = `BU_: ${nodes.join(' ')}`; } this.writeLine(lineContent); this.writeLine(''); } /** * Generic the signal groups as provided from the model * @param groups Map of available signal groups to write */ writeSignalGroups(messages) { messages.forEach((message) => { message.signalGroups.forEach((group) => { const signalList = group.signals.join(' '); const lineContent = `SIG_GROUP_ ${group.id} ${group.name} ${group.groupId} : ${signalList};`; this.writeLine(lineContent); }); }); this.writeLine(''); } /** * * @param message Individual message to be written to the file */ writeMessagesAndSignals(messages) { for (const [name, message] of messages) { let node; if (message.sendingNode) { node = message.sendingNode; } else { // Default that is typically generated from CANDB++ (from Vector) node = 'Vector___XXX'; } // Need to encode extended ID flag if message is extended const can = new Can(); const canId = message.extended ? can.setExtendedFlag(message.id) : message.id; const lineContent = `BO_ ${canId.toString()} ${message.name}: ${message.dlc.toString()} ${node}`; this.writeLine(lineContent); // Extract signals and exand in file for (const [signalName, signal] of message.signals) { this.writeSignal(signal); } this.writeLine(''); } } /** * * @param signal Signal to be writen to dbc file */ writeSignal(signal) { const endian = signal.endian === 'Motorola' ? '0' : '1'; const sign = signal.signed ? '-' : '+'; const nodes = signal.receivingNodes.length === 0 ? 'Vector___XXXX' : signal.receivingNodes.join(','); const name = signal.multiplex ? signal.name + ' ' + signal.multiplex : signal.name; // Format: SG_ Signal0 : 0|32@1- (1,0) [0|0] "" Node1 const lineContent = ` SG_ ` + `${name} : ${signal.startBit.toString()}|${signal.length.toString()}@${endian}${sign}` + ` (${signal.factor.toString()},${signal.offset.toString()}) [${signal.min.toString()}|${signal.max.toString()}] ` + `"${signal.unit}" ${nodes}`; this.writeLine(lineContent); } writeBaseComment(comment) { this.writeLine(`CM_ "${comment}" ;`); } writeMessageAndSignalComments(messages) { for (const [name, msg] of messages) { if (msg.description) { this.writeLine(`CM_ BO_ ${msg.id.toString()} "${msg.description}" ;`); } for (const [signalName, signal] of msg.signals) { if (signal.description) { this.writeLine(`CM_ SG_ ${msg.id.toString()} ${signalName} "${signal.description}" ;`); } } } } generateEnumTable(tableMembers) { let members = ''; for (const [enumVal, enumName] of tableMembers) { members = members + enumVal.toString() + ' ' + `"${enumName}"` + ' '; } return `${members}`; } writeValTable(valueTable) { for (const [name, tableMembers] of valueTable) { const members = this.generateEnumTable(tableMembers); const lineContent = `VAL_TABLE_ ${name} ${members};`; this.writeLine(lineContent); } this.writeLine(''); } writeSignalTables(messages) { for (const [name, msg] of messages) { for (const [signalName, signal] of msg.signals) { if (signal.valueTable) { const members = this.generateEnumTable(signal.valueTable); const lineContent = `VAL_ ${msg.id.toString()} ${signal.name} ${members};`; this.writeLine(lineContent); } } } this.writeLine(''); } writeEnvVarTables(environmentVariables) { environmentVariables.forEach((ev) => { if (ev.valueTable) { const members = this.generateEnumTable(ev.valueTable); const lineContent = `VAL_ ${ev.name} ${members};`; this.writeLine(lineContent); } }); } enumListToString(enumList) { return enumList.reduce((accumulator, currentValue, idx) => { let str = ''; if (idx === 0) { str = `"${currentValue.trim()}"`; } else { str = `, "${currentValue.trim()}"`; } return accumulator + str; }, ''); } createAttributeDefinitionStr(value, type) { let lineContent; if (type) { lineContent = `BA_DEF_ ${type} "${value.name}" ${value.dataType}`; } else { lineContent = `BA_DEF_ "${value.name}" ${value.dataType}`; } switch (value.dataType) { case 'INT': lineContent = lineContent + ` ${value.min} ${value.max};`; break; case 'FLOAT': lineContent = lineContent + ` ${value.min} ${value.max};`; break; case 'ENUM': if (value.options) { lineContent = lineContent + ' ' + this.enumListToString(value.options) + ';'; } break; case 'STRING': lineContent = lineContent + ';'; break; case 'HEX': lineContent = lineContent + ` ${value.min} ${value.max};`; break; } return lineContent; } writeAttributeDefinitions(data) { // Write global attributes data.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefinitionStr(value, null)); }); // Write node attributes data.nodes.forEach((node) => { node.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefinitionStr(value, 'BU_')); }); }); // Write message attributes const messages = data.messages; messages.forEach((message) => { message.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefinitionStr(value, 'BO_')); }); }); // Write signal attributes messages.forEach((message) => { const signals = message.signals; if (signals) { signals.forEach((signal, key) => { signal.attributes.forEach((value, name) => { this.writeLine(this.createAttributeDefinitionStr(value, 'SG_')); }); }); } }); this.writeLine(''); } createAttributeDefaultString(value) { let lineContent = `BA_DEF_DEF_ "${value.name}"`; switch (value.dataType) { case 'STRING': lineContent = lineContent + ` "${value.defaultValue}";`; break; case 'ENUM': lineContent = lineContent + ` "${value.defaultValue}";`; break; default: lineContent = lineContent + ` ${value.defaultValue};`; break; } return lineContent; } writeAttributeDefaults(data) { // Write global attributes data.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefaultString(value)); }); // Write node attributes data.nodes.forEach((node) => { node.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefaultString(value)); }); }); // Write message attributes const messages = data.messages; messages.forEach((message) => { message.attributes.forEach((value, key) => { this.writeLine(this.createAttributeDefaultString(value)); }); }); // Write signal attributes messages.forEach((message) => { const signals = message.signals; if (signals) { signals.forEach((signal, key) => { signal.attributes.forEach((value, name) => { this.writeLine(this.createAttributeDefaultString(value)); }); }); } }); this.writeLine(''); } createAttributeValueString(value, type, id, node, signal) { let lineContent = `BA_ "${value.name}"`; if (!type) { if (value.dataType === 'STRING') { lineContent = lineContent + ` "${value.value}";`; } else { lineContent = lineContent + ` ${value.value};`; } return lineContent; } // BA_ "BUIntAttribute" BU_ Node0 100; // BA_ "BOStringAttribute" BO_ 1234 "MessageAttribute"; // BA_ "SGEnumAttribute" SG_ 1234 Signal0 2; switch (type) { case 'BU_': if (value.dataType === 'STRING') { lineContent = lineContent + ` ${type} ${node} "${value.value}";`; } else { lineContent = lineContent + ` ${type} ${node} ${value.value};`; } break; case 'BO_': if (value.dataType === 'STRING') { lineContent = lineContent + ` ${type} ${id} "${value.value}";`; } else { lineContent = lineContent + ` ${type} ${id} ${value.value};`; } break; case 'SG_': if (value.dataType === 'STRING') { lineContent = lineContent + ` ${type} ${id} ${signal} "${value.value}";`; } else { lineContent = lineContent + ` ${type} ${id} ${signal} ${value.value};`; } break; } return lineContent; } writeAttributeValues(data) { // Write global attributes data.attributes.forEach((value, key) => { // skip if default = value so its no double defined in the dbc file output if (value.defaultValue !== value.value) { this.writeLine(this.createAttributeValueString(value, null, null, null, null)); } }); // Write node attributes data.nodes.forEach((node) => { node.attributes.forEach((value, key) => { if (value.defaultValue !== value.value) { this.writeLine(this.createAttributeValueString(value, 'BU_', null, node.name, null)); } }); }); // Write message attributes const messages = data.messages; messages.forEach((message) => { message.attributes.forEach((value, key) => { if (value.defaultValue !== value.value) { this.writeLine(this.createAttributeValueString(value, 'BO_', message.id, null, null)); } }); }); // Write signal attributes messages.forEach((message) => { const signals = message.signals; if (signals) { signals.forEach((signal, key) => { signal.attributes.forEach((value, name) => { if (value.defaultValue !== value.value) { this.writeLine(this.createAttributeValueString(value, 'SG_', message.id, null, signal.name)); } }); }); } }); this.writeLine(''); } writeSignalDataType(messages) { for (const [name, msg] of messages) { for (const [signalName, signal] of msg.signals) { if (signal.dataType && (signal.dataType === 'float' || signal.dataType === 'double')) { let type; if (signal.dataType === 'double') { type = '2'; } else { type = '1'; } const lineContent = `SIG_VALTYPE_ ${msg.id.toString()} ${signal.name} : ${type};`; this.writeLine(lineContent); } } } this.writeLine(''); } /** * Main writer function for class. New line character will be added automatically * to each line, so subsquent calls to this function * will automatically start on the next line. * * @param line Line content to write to file */ writeLine(line) { this.dbcString += `${line}\n`; } } export default Writer; //# sourceMappingURL=Writer.js.map