diff --git a/cantools/database/can/formats/dbc.py b/cantools/database/can/formats/dbc.py index 7692ae05..dd428d90 100644 --- a/cantools/database/can/formats/dbc.py +++ b/cantools/database/can/formats/dbc.py @@ -1,10 +1,10 @@ # Load and dump a CAN database in DBC format. import re -import logging from collections import OrderedDict as odict from collections import defaultdict from decimal import Decimal +from copy import deepcopy import textparser from textparser import Sequence @@ -102,15 +102,23 @@ 64: SIGNAL_TYPE_DOUBLE } -# Definitions for long symbol names in dbc files: -MAX_LEN_SHORT_SYMBOL_NAME = 32 -ATT_NAME_LONG_MESSAGE = 'SystemMessageLongSymbol' -ATT_NAME_LONG_SIGNAL = 'SystemSignalLongSymbol' -ATT_NAME_LONG_NODE = 'SystemNodeLongSymbol' -ATT_NAME_LONG_ENVVAR = 'SystemEnvVarLongSymbol' +ATTRIBUTE_DEFINITION_LONG_NODE_NAME = AttributeDefinition( + 'SystemNodeLongSymbol', + default_value='', + kind='BU_', + type_name='STRING') +ATTRIBUTE_DEFINITION_LONG_MESSAGE_NAME = AttributeDefinition( + 'SystemMessageLongSymbol', + default_value='', + kind='BO_', + type_name='STRING') -LOGGER = logging.getLogger(__name__) +ATTRIBUTE_DEFINITION_LONG_SIGNAL_NAME = AttributeDefinition( + 'SystemSignalLongSymbol', + default_value='', + kind='SG_', + type_name='STRING') def to_int(value): @@ -341,13 +349,21 @@ def __init__(self, attribute_definitions=None, environment_variables=None, value_tables=None): - self._attributes = attributes - self._attribute_definitions = attribute_definitions - self._environment_variables = environment_variables + if attributes is None: + attributes = odict() + + if attribute_definitions is None: + attribute_definitions = odict() + + if environment_variables is None: + environment_variables = odict() if value_tables is None: value_tables = odict() + self._attributes = attributes + self._attribute_definitions = attribute_definitions + self._environment_variables = environment_variables self._value_tables = value_tables @property @@ -392,47 +408,29 @@ def environment_variables(self): class LongNamesConverter(object): - def __init__(self, names): - self._long_to_short_names_dict = {} - next_index_per_base_name = {} - short_names = set() - - def basename(name): - return name[:27] - - for name in names: - if len(name) != 32: - continue + def __init__(self, database): + self._database = database + self._next_index_per_cut_name = defaultdict(int) + self._short_names = set() - next_index_per_base_name[basename(name)] = 0 - short_names.add(name) + def convert(self, name): + short_name = None - for name in names: - if len(name) <= 32: - continue - - base_name = basename(name) + if len(name) == 32: + self._short_names.add(name) + elif len(name) > 32: + cut_name = name[:27] short_name = name[:32] - if short_name in short_names: - index = next_index_per_base_name[base_name] - next_index_per_base_name[base_name] += 1 + if short_name in self._short_names: + index = self._next_index_per_cut_name[cut_name] + self._next_index_per_cut_name[cut_name] += 1 short_name = '{}_{:04d}'.format(name[:27], index) else: - next_index_per_base_name[base_name] = 0 - short_names.add(short_name) + self._next_index_per_cut_name[cut_name] = 0 + self._short_names.add(short_name) - self._long_to_short_names_dict[name] = short_name - - def lookup_name(self, name): - if name in self._long_to_short_names_dict: - name = self._long_to_short_names_dict[name] - - return name - - @property - def long_to_short_names_dict(self): - return self._long_to_short_names_dict + return short_name def get_dbc_frame_id(message): @@ -446,14 +444,14 @@ def get_dbc_frame_id(message): def _get_node_name(attributes, name): try: - return attributes['node'][name][ATT_NAME_LONG_NODE].value + return attributes['node'][name]['SystemNodeLongSymbol'].value except (KeyError, TypeError): return name -def _get_envvar_name(attributes, name): +def _get_environment_variable_name(attributes, name): try: - return attributes['envvar'][name][ATT_NAME_LONG_ENVVAR].value + return attributes['envvar'][name]['SystemEnvVarLongSymbol'].value except (KeyError, TypeError): return name @@ -462,11 +460,11 @@ def _dump_version(database): return '' if database.version is None else database.version -def _dump_nodes(database, nodes_dict): +def _dump_nodes(database): bu = [] for node in database.nodes: - bu.append(nodes_dict[node.name]) + bu.append(node.name) return bu @@ -487,13 +485,9 @@ def _dump_value_tables(database): return val_table + [''] -def _dump_messages(database, name_dicts): +def _dump_messages(database): bo = [] - msg_dict = name_dicts['msgs'] - sig_dict = name_dicts['sigs'] - node_dict = name_dicts['nodes'] - def format_mux(signal): if signal.is_multiplexer: return ' M' @@ -504,19 +498,13 @@ def format_mux(signal): def format_receivers(signal): if signal.receivers: - return ' ' + ','.join([node_dict[rec] for rec in signal.receivers]) + return ' ' + ','.join(signal.receivers) else: return 'Vector__XXX' def format_senders(message): if message.senders: - try: - return node_dict[message.senders[0]] - except KeyError: - LOGGER.warning( - "message '{}' related to unknown sender '{}'." - .format(message.name, message.senders[0])) - return message.senders[0] + return message.senders[0] else: return 'Vector__XXX' @@ -524,7 +512,7 @@ def format_senders(message): msg = [] fmt = 'BO_ {frame_id} {name}: {length} {senders}' msg.append(fmt.format(frame_id=get_dbc_frame_id(message), - name=msg_dict[message.name], + name=message.name, length=message.length, senders=format_senders(message))) @@ -533,7 +521,7 @@ def format_senders(message): ' ({scale},{offset})' ' [{minimum}|{maximum}] "{unit}" {receivers}') msg.append(fmt.format( - name=sig_dict[signal.name], + name=signal.name, mux=format_mux(signal), start=signal.start, length=signal.length, @@ -551,26 +539,25 @@ def format_senders(message): return bo -def _dump_senders(database, node_dict): +def _dump_senders(database): bo_tx_bu = [] fmt = 'BO_TX_BU_ {frame_id} : {senders};' for message in database.messages: if len(message.senders) > 1: - senders_short = [node_dict[sender] for sender in message.senders] bo_tx_bu.append(fmt.format(frame_id=get_dbc_frame_id(message), - senders=','.join(senders_short))) + senders=','.join(message.senders))) return bo_tx_bu -def _dump_comments(database, name_dicts): +def _dump_comments(database): cm = [] for node in database.nodes: if node.comment is not None: fmt = 'CM_ BU_ {name} "{comment}";' - cm.append(fmt.format(name=name_dicts['nodes'][node.name], + cm.append(fmt.format(name=node.name, comment=node.comment.replace('"', '\\"'))) for message in database.messages: @@ -583,13 +570,13 @@ def _dump_comments(database, name_dicts): if signal.comment is not None: fmt = 'CM_ SG_ {frame_id} {name} "{comment}";' cm.append(fmt.format(frame_id=get_dbc_frame_id(message), - name=name_dicts['sigs'][signal.name], + name=signal.name, comment=signal.comment.replace('"', '\\"'))) return cm -def _dump_signal_types(database, sig_dict): +def _dump_signal_types(database): valtype = [] for message in database.messages: @@ -599,7 +586,7 @@ def _dump_signal_types(database, sig_dict): fmt = 'SIG_VALTYPE_ {} {} : {};' valtype.append(fmt.format(message.frame_id, - sig_dict[signal.name], + signal.name, FLOAT_LENGTH_TO_SIGNAL_TYPE[signal.length])) return valtype @@ -676,7 +663,7 @@ def _dump_attribute_definition_defaults(database): return ba_def_def -def _dump_attributes(database, name_dicts): +def _dump_attributes(database): ba = [] def get_value(attribute): @@ -701,7 +688,7 @@ def get_value(attribute): fmt = 'BA_ "{name}" {kind} {node_name} {value};' ba.append(fmt.format(name=attribute.definition.name, kind=attribute.definition.kind, - node_name=name_dicts['nodes'][node.name], + node_name=node.name, value=get_value(attribute))) for message in database.messages: @@ -722,13 +709,13 @@ def get_value(attribute): ba.append(fmt.format(name=attribute.definition.name, kind=attribute.definition.kind, frame_id=get_dbc_frame_id(message), - signal_name=name_dicts['sigs'][signal.name], + signal_name=signal.name, value=get_value(attribute))) return ba -def _dump_choices(database, sig_dict): +def _dump_choices(database): val = [] for message in database.messages: @@ -739,7 +726,7 @@ def _dump_choices(database, sig_dict): fmt = 'VAL_ {frame_id} {name} {choices} ;' val.append(fmt.format( frame_id=get_dbc_frame_id(message), - name=sig_dict[signal.name], + name=signal.name, choices=' '.join(['{value} "{text}"'.format(value=value, text=text) for value, text in signal.choices.items()]))) @@ -882,7 +869,7 @@ def _load_environment_variables(tokens, comments, attributes): environment_variables = odict() for env_var in tokens.get('EV_', []): - name = _get_envvar_name(attributes, env_var[1]) + name = _get_environment_variable_name(attributes, env_var[1]) environment_variables[name] = EnvironmentVariable( name=name, env_type=int(env_var[3]), @@ -1106,7 +1093,7 @@ def get_signal_name(frame_id_dbc, name): signal_attributes = get_attributes(frame_id_dbc, name) try: - return signal_attributes[ATT_NAME_LONG_SIGNAL].value + return signal_attributes['SystemSignalLongSymbol'].value except (KeyError, TypeError): return name @@ -1144,9 +1131,8 @@ def get_signal_initial_value(frame_id_dbc, name): unit=(None if signal[19] == '' else signal[19]), choices=get_choices(frame_id_dbc, signal[1][0]), - dbc_specifics=DbcSpecifics( - attributes=get_attributes(frame_id_dbc, signal[1][0]), - attribute_definitions=definitions), + dbc_specifics=DbcSpecifics(get_attributes(frame_id_dbc, signal[1][0]), + definitions), comment=get_comment(frame_id_dbc, signal[1][0]), is_multiplexer=get_is_multiplexer(signal), @@ -1253,7 +1239,7 @@ def get_message_name(frame_id_dbc, name): message_attributes = get_attributes(frame_id_dbc) try: - return message_attributes[ATT_NAME_LONG_MESSAGE].value + return message_attributes['SystemMessageLongSymbol'].value except (KeyError, TypeError): return name @@ -1311,9 +1297,8 @@ def get_message_name(frame_id_dbc, name): senders=senders, send_type=get_send_type(frame_id_dbc), cycle_time=get_cycle_time(frame_id_dbc), - dbc_specifics=DbcSpecifics( - attributes=get_attributes(frame_id_dbc), - attribute_definitions=definitions), + dbc_specifics=DbcSpecifics(get_attributes(frame_id_dbc), + definitions), signals=signals, comment=get_comment(frame_id_dbc), strict=strict, @@ -1347,157 +1332,139 @@ def _load_nodes(tokens, comments, attributes, definitions): for token in tokens.get('BU_', []): nodes = [Node(name=_get_node_name(attributes, node), comment=comments.get(node, None), - dbc_specifics=DbcSpecifics(attributes['node'].get(node, - None), + dbc_specifics=DbcSpecifics(attributes['node'].get(node, None), definitions)) for node in token[2]] return nodes -def create_one_unique_names_dict(names): - """Create a dict with short unique names for the given list. +def get_attribute_definition(database, name, default): + if database.dbc is None: + database.dbc = DbcSpecifics() - Map the objects' original names (short or long) to unique names with - up to 32 chars. - Use last five chars ('_0000' to '_9999' for unique enumaration of messages - with identical first 32 chars. - Skip numbers that are already used by a message in its original name with - exactly 32 chars. + if name not in database.dbc.attribute_definitions: + database.dbc.attribute_definitions[name] = default - """ + return database.dbc.attribute_definitions[name] + + +def get_long_node_name_attribute_definition(database): + return get_attribute_definition(database, + 'SystemNodeLongSymbol', + ATTRIBUTE_DEFINITION_LONG_NODE_NAME) + + +def get_long_message_name_attribute_definition(database): + return get_attribute_definition(database, + 'SystemMessageLongSymbol', + ATTRIBUTE_DEFINITION_LONG_MESSAGE_NAME) - result_dict = {} - # first step: just cut the name: - for name in names: - result_dict[name] = name[:MAX_LEN_SHORT_SYMBOL_NAME] +def get_long_signal_name_attribute_definition(database): + return get_attribute_definition(database, + 'SystemSignalLongSymbol', + ATTRIBUTE_DEFINITION_LONG_SIGNAL_NAME) - # for all messages that have the same cut name: - # change their cut names by replacing the last chars by enumeration: - # Skip numbers which are already used by messages with a "hard coded" - # number in the same format. - cut_names_set = set(result_dict.values()) +def make_node_names_unique(database): + converter = LongNamesConverter(database) - for cut_name in cut_names_set: - similar_obj_cnt = list(result_dict.values()).count(cut_name) - if similar_obj_cnt == 1: + for node in database.nodes: + name = converter.convert(node.name) + + if name is None: continue - i = 0 - for key in sorted([matching_dictkey - for matching_dictkey in result_dict - if result_dict[matching_dictkey] == cut_name]): - if cut_name == result_dict[key]: - while i <= 9999: - target_name = '{}_{:04d}'.format(cut_name[:27], i) - if target_name not in result_dict.keys(): - result_dict[key] = target_name - break - i += 1 - i += 1 + for message in database.messages: + for index, sender in enumerate(message.senders): + if sender == node.name: + message.senders[index] = name - return result_dict + for signal in message.signals: + for index, receiver in enumerate(signal.receivers): + if receiver == node.name: + signal.receivers[index] = name + if node.dbc is None: + node.dbc = DbcSpecifics() -def create_unique_names_dicts(database): - """Generate dicts with unique short object names. + node.dbc.attributes['SystemNodeLongSymbol'] = Attribute( + node.name, + get_long_node_name_attribute_definition(database)) + node.name = name - Create object names that are unique (per type) with a max len of 32 chars. - Add the related attribute definition for this object type's long symbol - name, if not defined yet. - Create one dict per object type (Message, Signal, Node, EnvVar) with their - long names as keys and the unique (short) names as values. - Add/update each object's attribute for its long symbol name if needed, and - delete that attribute if the name is short enough. +def make_message_names_unique(database): + converter = LongNamesConverter(database) - """ + for message in database.messages: + name = converter.convert(message.name) - def _refresh_one_object_type(obj_type, obj_list): - if not obj_list: - return dict() + if name is None: + continue - try: - att_name = {'BO': ATT_NAME_LONG_MESSAGE, - 'SG': ATT_NAME_LONG_SIGNAL, - 'BU': ATT_NAME_LONG_NODE, - 'EV': ATT_NAME_LONG_ENVVAR, - }[obj_type] - except KeyError: - return + if message.dbc is None: + message.dbc = DbcSpecifics() - att_def = AttributeDefinition( - att_name, - default_value='', - kind=obj_type + '_', - type_name='STRING') - unique_obj_names_dict = create_one_unique_names_dict( - [obj.name for obj in obj_list]) - for obj in obj_list: - if len(obj.name) > MAX_LEN_SHORT_SYMBOL_NAME: - if obj.dbc is None: - obj.dbc = DbcSpecifics(odict(), odict()) - obj.dbc.attributes[att_name] = Attribute(obj.name, att_def) - else: - try: - obj.dbc.attributes.pop(att_name) - except (KeyError, AttributeError): - pass - - # add attribute definition for long message names, if neccessary: - if max([len(obj.name) for obj in obj_list]) > MAX_LEN_SHORT_SYMBOL_NAME: - if database.dbc is None: - database.dbc = DbcSpecifics( - odict(), odict(), odict(), odict()) - if att_name not in database.dbc.attribute_definitions: - database.dbc.attribute_definitions[att_name] = att_def - return unique_obj_names_dict - - msg_dict = _refresh_one_object_type('BO', database.messages) - sig_dict = _refresh_one_object_type('SG', - [sig for msg in database.messages - for sig in msg.signals]) - node_dict = _refresh_one_object_type('BU', database.nodes) - - envvar_dict = {} - - # Note: - # Indepentend from short/long name handling, env_vars currently are only - # supported for import, not for export. - # Creating the env_var names dict is pereparation for when env_var export - # will be implemented. - try: - env_vars = database.dbc.environment_variables - envvar_dict = _refresh_one_object_type('EV', env_vars) - except AttributeError: - envvar_dict = {} + message.dbc.attributes['SystemMessageLongSymbol'] = Attribute( + message.name, + get_long_message_name_attribute_definition(database)) + message.name = name - if database.dbc is None: - database.dbc = DbcSpecifics(odict(), odict(), odict(), odict()) - return {'msgs': msg_dict, - 'sigs': sig_dict, - 'nodes': node_dict, - 'envvars': envvar_dict} + +def make_signal_names_unique(database): + converter = LongNamesConverter(database) + + for message in database.messages: + for signal in message.signals: + name = converter.convert(signal.name) + + if name is None: + continue + + if signal.dbc is None: + signal.dbc = DbcSpecifics() + + signal.dbc.attributes['SystemSignalLongSymbol'] = Attribute( + signal.name, + get_long_signal_name_attribute_definition(database)) + signal.name = name + + +def make_names_unique(database): + """Make message, signal and node names unique and add attributes for + their long names. + + """ + + # Make a deep copy of the database as names and attributes will be + # modified for items with long names. + database = deepcopy(database) + + make_node_names_unique(database) + make_message_names_unique(database) + make_signal_names_unique(database) + + return database def dump_string(database): """Format database in DBC file format. """ - name_dicts = create_unique_names_dicts(database) - bu = _dump_nodes(database, name_dicts['nodes']) + database = make_names_unique(database) + bu = _dump_nodes(database) val_table = _dump_value_tables(database) - bo = _dump_messages(database, name_dicts) - bo_tx_bu = _dump_senders(database, name_dicts['nodes']) - cm = _dump_comments(database, name_dicts) - signal_types = _dump_signal_types(database, name_dicts['sigs']) + bo = _dump_messages(database) + bo_tx_bu = _dump_senders(database) + cm = _dump_comments(database) + signal_types = _dump_signal_types(database) ba_def = _dump_attribute_definitions(database) ba_def_def = _dump_attribute_definition_defaults(database) - ba = _dump_attributes(database, name_dicts) - val = _dump_choices(database, name_dicts['sigs']) + ba = _dump_attributes(database) + val = _dump_choices(database) return DBC_FMT.format(version=_dump_version(database), bu=' '.join(bu), @@ -1583,10 +1550,10 @@ def load_string(string, strict=True): nodes = _load_nodes(tokens, comments, attributes, attribute_definitions) version = _load_version(tokens) environment_variables = _load_environment_variables(tokens, comments, attributes) - dbc_specifics = DbcSpecifics(attributes=attributes.get('database', None), - attribute_definitions=attribute_definitions, - environment_variables=environment_variables, - value_tables=value_tables) + dbc_specifics = DbcSpecifics(attributes.get('database', None), + attribute_definitions, + environment_variables, + value_tables) return InternalDatabase(messages, nodes, diff --git a/tests/files/dbc/long_names_multiple_relations.dbc b/tests/files/dbc/long_names_multiple_relations.dbc index 8957c121..b8ce5e70 100644 --- a/tests/files/dbc/long_names_multiple_relations.dbc +++ b/tests/files/dbc/long_names_multiple_relations.dbc @@ -1,98 +1,108 @@ -VERSION "" - - -NS_ : - NS_DESC_ - CM_ - BA_DEF_ - BA_ - VAL_ - CAT_DEF_ - CAT_ - FILTER - BA_DEF_DEF_ - EV_DATA_ - ENVVAR_DATA_ - SGTYPE_ - SGTYPE_VAL_ - BA_DEF_SGTYPE_ - BA_SGTYPE_ - SIG_TYPE_REF_ - VAL_TABLE_ - SIG_GROUP_ - SIG_VALTYPE_ - SIGTYPE_VALTYPE_ - BO_TX_BU_ - BA_DEF_REL_ - BA_REL_ - BA_DEF_DEF_REL_ - BU_SG_REL_ - BU_EV_REL_ - BU_BO_REL_ - SG_MUL_VAL_ - -BS_: - -BU_: Sender_2_aaaaaaaaaaaaaaaaaaaaaaa Receiver_2_zzzzzzzzzzzzzzzzzzzzz Receiver_1 Node_6789_123456789_123456789_12 -VAL_TABLE_ Value_Table_short 1 "Very long, long, long description for the value '0x1'" 0 "Very long, long, long description for the value '0x0'" ; -VAL_TABLE_ Value_Table_cdefghi_ABCDEFGHI_AB 13 "Description for the value '0xD'" 12 "Description for the value '0xC'" 11 "Description for the value '0xB'" 10 "Description for the value '0xA'" 9 "Description for the value '0x9'" 8 "Description for the value '0x8'" 7 "Description for the value '0x7'" 6 "Description for the value '0x6'" 5 "Description for the value '0x5'" 4 "Description for the value '0x4'" 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; -VAL_TABLE_ Value_Table_cdefghi_ABCDEFGHI_AB 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; - - -BO_ 6 TX_twice: 2 Node_6789_123456789_123456789_12 - SG_ rx_twice_long_yyyyyyyyyyyyyyyyyy : 8|8@1- (1,0) [0|0] "" Receiver_1,Receiver_2_zzzzzzzzzzzzzzzzzzzzz - SG_ rx_twice_short : 0|8@1- (1,0) [0|0] "" Receiver_2_zzzzzzzzzzzzzzzzzzzzz,Receiver_1 - -BO_ 5 RX_TX_1: 8 Node_6789_123456789_123456789_12 - -BO_ 4 MSG_CASE_TEST: 8 Vector__XXX - -BO_ 3 msg_case_test: 8 Vector__XXX - -BO_ 2 Msg_with_value_table_sigs: 3 Vector__XXX - SG_ Sig_with_short_val_table : 16|8@1- (1,0) [0|0] "" Vector__XXX - SG_ Sig_with_long_val_table_2 : 8|8@1- (1,0) [0|0] "" Vector__XXX - SG_ Sig_with_long_val_table_1 : 0|8@1- (1,0) [0|0] "" Vector__XXX - -BO_ 1 Msg_Long_Name_56789_123456789_12: 1 Vector__XXX - SG_ Sig_used_twice_efgh_abcdefghi_ab : 0|8@1- (1,0) [0|0] "" Vector__XXX - -BO_ 0 Msg_Long_Name_56789_1234567_0000: 1 Vector__XXX - SG_ Sig_used_twice_efgh_abcdefg_0000 : 0|8@1- (1,0) [0|0] "" Vector__XXX - -BO_TX_BU_ 6 : Sender_2_aaaaaaaaaaaaaaaaaaaaaaa,Node_6789_123456789_123456789_12; - - -BA_DEF_ SG_ "GenSigSendType" ENUM "Cyclic","OnWrite","OnWriteWithRepetition","OnChange","OnChangeWithRepetition","IfActive","IfActiveWithRepetition","NoSigSendType"; -BA_DEF_ SG_ "GenSigInactiveValue" INT 0 0; -BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; -BA_DEF_ BO_ "GenMsgSendType" ENUM "Cyclic","not_used","not_used","not_used","not_used","Cyclic","not_used","IfActive","NoMsgSendType"; -BA_DEF_ BU_ "NmStationAddress" HEX 0 0; -BA_DEF_ "DBName" STRING ; -BA_DEF_ "BusType" STRING ; -BA_DEF_ SG_ "SystemSignalLongSymbol" STRING ; -BA_DEF_ BO_ "SystemMessageLongSymbol" STRING ; -BA_DEF_ BU_ "SystemNodeLongSymbol" STRING ; -BA_DEF_DEF_ "GenSigSendType" "Cyclic"; -BA_DEF_DEF_ "GenSigInactiveValue" 0; -BA_DEF_DEF_ "GenMsgCycleTime" 0; -BA_DEF_DEF_ "GenMsgSendType" "NoMsgSendType"; -BA_DEF_DEF_ "NmStationAddress" 0; -BA_DEF_DEF_ "DBName" ""; -BA_DEF_DEF_ "BusType" "CAN"; -BA_DEF_DEF_ "SystemSignalLongSymbol" ""; -BA_DEF_DEF_ "SystemMessageLongSymbol" ""; -BA_DEF_DEF_ "SystemNodeLongSymbol" ""; -BA_ "DBName" "long_names_multiple_relations"; -BA_ "SystemNodeLongSymbol" BU_ Sender_2_aaaaaaaaaaaaaaaaaaaaaaa "Sender_2_aaaaaaaaaaaaaaaaaaaaaaaAAAAAA"; -BA_ "SystemNodeLongSymbol" BU_ Receiver_2_zzzzzzzzzzzzzzzzzzzzz "Receiver_2_zzzzzzzzzzzzzzzzzzzzzZZZ"; -BA_ "SystemNodeLongSymbol" BU_ Node_6789_123456789_123456789_12 "Node_6789_123456789_123456789_123456789"; -BA_ "SystemMessageLongSymbol" BO_ 1 "Msg_Long_Name_56789_123456789_123456789_Copy_1"; -BA_ "SystemMessageLongSymbol" BO_ 0 "Msg_Long_Name_56789_123456789_123456789"; -BA_ "SystemSignalLongSymbol" SG_ 6 rx_twice_long_yyyyyyyyyyyyyyyyyy "rx_twice_long_yyyyyyyyyyyyyyyyyyYYY"; -BA_ "SystemSignalLongSymbol" SG_ 1 Sig_used_twice_efgh_abcdefghi_ab "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; -BA_ "SystemSignalLongSymbol" SG_ 0 Sig_used_twice_efgh_abcdefg_0000 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; -VAL_ 2 Sig_with_short_val_table 1 "Very long, long, long description for the value '0x1'" 0 "Very long, long, long description for the value '0x0'" ; -VAL_ 2 Sig_with_long_val_table_2 13 "value '0xD'" 12 "Dvalue '0xC'" 11 "value '0xB'" 10 "value '0xA'" 9 "value '0x9'" 8 "value '0x8'" 7 "value '0x7'" 6 "value '0x6'" 5 "value '0x5'" 4 "value '0x4'" 3 "value '0x3'" 2 "value '0x2'" 1 "value '0x1'" 0 "value '0x0'" ; -VAL_ 2 Sig_with_long_val_table_1 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; - +VERSION "" + + +NS_ : + NS_DESC_ + CM_ + BA_DEF_ + BA_ + VAL_ + CAT_DEF_ + CAT_ + FILTER + BA_DEF_DEF_ + EV_DATA_ + ENVVAR_DATA_ + SGTYPE_ + SGTYPE_VAL_ + BA_DEF_SGTYPE_ + BA_SGTYPE_ + SIG_TYPE_REF_ + VAL_TABLE_ + SIG_GROUP_ + SIG_VALTYPE_ + SIGTYPE_VALTYPE_ + BO_TX_BU_ + BA_DEF_REL_ + BA_REL_ + BA_DEF_DEF_REL_ + BU_SG_REL_ + BU_EV_REL_ + BU_BO_REL_ + SG_MUL_VAL_ + +BS_: + +BU_: Sender_2_aaaaaaaaaaaaaaaaaaaaaaa Receiver_2_zzzzzzzzzzzzzzzzzzzzz Receiver_1 Node_6789_123456789_123456789_12 +VAL_TABLE_ Value_Table_short 1 "Very long, long, long description for the value '0x1'" 0 "Very long, long, long description for the value '0x0'" ; +VAL_TABLE_ Value_Table_cdefghi_ABCDEFGHI_AB 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; + + +BO_ 85 Msg_Long_Name_56789_123456789_12: 8 Vector__XXX + SG_ rx_twice_11111111111111111111111 : 8|8@1- (1,0) [0|0] "" Vector__XXX + SG_ rx_twice_short : 16|18@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefghi_ab : 0|6@1- (1,0) [0|0] "" Vector__XXX + +BO_ 6 TX_twice: 2 Node_6789_123456789_123456789_12 + SG_ rx_twice_long_yyyyyyyyyyyyyyyyyy : 8|8@1- (1,0) [0|0] "" Receiver_1,Receiver_2_zzzzzzzzzzzzzzzzzzzzz + SG_ rx_twice_short : 0|8@1- (1,0) [0|0] "" Receiver_2_zzzzzzzzzzzzzzzzzzzzz,Receiver_1 + +BO_ 5 RX_TX_1: 8 Node_6789_123456789_123456789_12 + SG_ Sig_used_twice_efgh_abcdefg_0000 : 0|9@1- (1,0) [0|0] "" Vector__XXX + +BO_ 4 MSG_CASE_TEST: 8 Vector__XXX + +BO_ 3 msg_case_test: 8 Vector__XXX + +BO_ 2 Msg_with_value_table_sigs: 3 Vector__XXX + SG_ Sig_with_short_val_table : 16|8@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_with_long_val_table_2 : 8|8@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_with_long_val_table_1 : 0|8@1- (1,0) [0|0] "" Vector__XXX + +BO_ 1 Msg_Long_Name_56789_1234567_0000: 8 Vector__XXX + SG_ rx_twice_11111111111111111111111 : 8|2@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefg_0001 : 0|8@1- (1,0) [0|0] "" Vector__XXX + +BO_ 0 Msg_Long_Name_56789_1234567_0001: 8 Vector__XXX + SG_ rx_twice_short : 8|8@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefg_0002 : 0|6@1- (1,0) [0|0] "" Vector__XXX + +BO_TX_BU_ 6 : Node_6789_123456789_123456789_12,Sender_2_aaaaaaaaaaaaaaaaaaaaaaa; + + +BA_DEF_ SG_ "GenSigSendType" ENUM "Cyclic","OnWrite","OnWriteWithRepetition","OnChange","OnChangeWithRepetition","IfActive","IfActiveWithRepetition","NoSigSendType"; +BA_DEF_ SG_ "GenSigInactiveValue" INT 0 0; +BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; +BA_DEF_ BO_ "GenMsgSendType" ENUM "Cyclic","not_used","not_used","not_used","not_used","Cyclic","not_used","IfActive","NoMsgSendType"; +BA_DEF_ BU_ "NmStationAddress" HEX 0 0; +BA_DEF_ "DBName" STRING ; +BA_DEF_ "BusType" STRING ; +BA_DEF_ SG_ "SystemSignalLongSymbol" STRING ; +BA_DEF_ BO_ "SystemMessageLongSymbol" STRING ; +BA_DEF_ BU_ "SystemNodeLongSymbol" STRING ; +BA_DEF_DEF_ "GenSigSendType" "Cyclic"; +BA_DEF_DEF_ "GenSigInactiveValue" 0; +BA_DEF_DEF_ "GenMsgCycleTime" 0; +BA_DEF_DEF_ "GenMsgSendType" "NoMsgSendType"; +BA_DEF_DEF_ "NmStationAddress" 0; +BA_DEF_DEF_ "DBName" ""; +BA_DEF_DEF_ "BusType" "CAN"; +BA_DEF_DEF_ "SystemSignalLongSymbol" ""; +BA_DEF_DEF_ "SystemMessageLongSymbol" ""; +BA_DEF_DEF_ "SystemNodeLongSymbol" ""; +BA_ "DBName" "long_names_multiple_relations"; +BA_ "SystemNodeLongSymbol" BU_ Sender_2_aaaaaaaaaaaaaaaaaaaaaaa "Sender_2_aaaaaaaaaaaaaaaaaaaaaaaAAAAAA"; +BA_ "SystemNodeLongSymbol" BU_ Receiver_2_zzzzzzzzzzzzzzzzzzzzz "Receiver_2_zzzzzzzzzzzzzzzzzzzzzZZZ"; +BA_ "SystemNodeLongSymbol" BU_ Node_6789_123456789_123456789_12 "Node_6789_123456789_123456789_123456789"; +BA_ "SystemMessageLongSymbol" BO_ 85 "Msg_Long_Name_56789_123456789_123456789_Copy_2"; +BA_ "SystemMessageLongSymbol" BO_ 1 "Msg_Long_Name_56789_123456789_123456789_Copy_1"; +BA_ "SystemMessageLongSymbol" BO_ 0 "Msg_Long_Name_56789_123456789_123456789"; +BA_ "SystemSignalLongSymbol" SG_ 85 Sig_used_twice_efgh_abcdefghi_ab "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +BA_ "SystemSignalLongSymbol" SG_ 6 rx_twice_long_yyyyyyyyyyyyyyyyyy "rx_twice_long_yyyyyyyyyyyyyyyyyyYYY"; +BA_ "SystemSignalLongSymbol" SG_ 5 Sig_used_twice_efgh_abcdefg_0000 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +BA_ "SystemSignalLongSymbol" SG_ 1 Sig_used_twice_efgh_abcdefg_0001 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +BA_ "SystemSignalLongSymbol" SG_ 0 Sig_used_twice_efgh_abcdefg_0002 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +VAL_ 2 Sig_with_short_val_table 1 "Very long, long, long description for the value '0x1'" 0 "Very long, long, long description for the value '0x0'" ; +VAL_ 2 Sig_with_long_val_table_2 13 "value '0xD'" 12 "Dvalue '0xC'" 11 "value '0xB'" 10 "value '0xA'" 9 "value '0x9'" 8 "value '0x8'" 7 "value '0x7'" 6 "value '0x6'" 5 "value '0x5'" 4 "value '0x4'" 3 "value '0x3'" 2 "value '0x2'" 1 "value '0x1'" 0 "value '0x0'" ; +VAL_ 2 Sig_with_long_val_table_1 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; + diff --git a/tests/files/dbc/long_names_multiple_relations_dumped.dbc b/tests/files/dbc/long_names_multiple_relations_dumped.dbc index 8f04ad0a..8963d0f2 100644 --- a/tests/files/dbc/long_names_multiple_relations_dumped.dbc +++ b/tests/files/dbc/long_names_multiple_relations_dumped.dbc @@ -38,11 +38,17 @@ VAL_TABLE_ Value_Table_short 1 "Very long, long, long description for the value VAL_TABLE_ Value_Table_cdefghi_ABCDEFGHI_AB 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; +BO_ 85 Msg_Long_Name_56789_123456789_12: 8 Vector__XXX + SG_ rx_twice_short : 16|18@1- (1,0) [0|0] "" Vector__XXX + SG_ rx_twice_11111111111111111111111 : 8|8@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefghi_ab : 0|6@1- (1,0) [0|0] "" Vector__XXX + BO_ 6 TX_twice: 2 Node_6789_123456789_123456789_12 SG_ rx_twice_long_yyyyyyyyyyyyyyyyyy : 8|8@1- (1,0) [0|0] "" Receiver_1,Receiver_2_zzzzzzzzzzzzzzzzzzzzz SG_ rx_twice_short : 0|8@1- (1,0) [0|0] "" Receiver_2_zzzzzzzzzzzzzzzzzzzzz,Receiver_1 BO_ 5 RX_TX_1: 8 Node_6789_123456789_123456789_12 + SG_ Sig_used_twice_efgh_abcdefg_0000 : 0|9@1- (1,0) [0|0] "" Vector__XXX BO_ 4 MSG_CASE_TEST: 8 Vector__XXX @@ -53,11 +59,13 @@ BO_ 2 Msg_with_value_table_sigs: 3 Vector__XXX SG_ Sig_with_long_val_table_2 : 8|8@1- (1,0) [0|0] "" Vector__XXX SG_ Sig_with_long_val_table_1 : 0|8@1- (1,0) [0|0] "" Vector__XXX -BO_ 1 Msg_Long_Name_56789_1234567_0001: 1 Vector__XXX - SG_ Sig_used_twice_efgh_abcdefghi_ab : 0|8@1- (1,0) [0|0] "" Vector__XXX +BO_ 1 Msg_Long_Name_56789_1234567_0000: 8 Vector__XXX + SG_ rx_twice_11111111111111111111111 : 8|2@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefg_0001 : 0|8@1- (1,0) [0|0] "" Vector__XXX -BO_ 0 Msg_Long_Name_56789_1234567_0000: 1 Vector__XXX - SG_ Sig_used_twice_efgh_abcdefghi_ab : 0|8@1- (1,0) [0|0] "" Vector__XXX +BO_ 0 Msg_Long_Name_56789_1234567_0001: 8 Vector__XXX + SG_ rx_twice_short : 8|8@1- (1,0) [0|0] "" Vector__XXX + SG_ Sig_used_twice_efgh_abcdefg_0002 : 0|6@1- (1,0) [0|0] "" Vector__XXX BO_TX_BU_ 6 : Node_6789_123456789_123456789_12,Sender_2_aaaaaaaaaaaaaaaaaaaaaaa; @@ -87,11 +95,14 @@ BA_ "DBName" "long_names_multiple_relations"; BA_ "SystemNodeLongSymbol" BU_ Sender_2_aaaaaaaaaaaaaaaaaaaaaaa "Sender_2_aaaaaaaaaaaaaaaaaaaaaaaAAAAAA"; BA_ "SystemNodeLongSymbol" BU_ Receiver_2_zzzzzzzzzzzzzzzzzzzzz "Receiver_2_zzzzzzzzzzzzzzzzzzzzzZZZ"; BA_ "SystemNodeLongSymbol" BU_ Node_6789_123456789_123456789_12 "Node_6789_123456789_123456789_123456789"; +BA_ "SystemMessageLongSymbol" BO_ 85 "Msg_Long_Name_56789_123456789_123456789_Copy_2"; +BA_ "SystemSignalLongSymbol" SG_ 85 Sig_used_twice_efgh_abcdefghi_ab "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; BA_ "SystemSignalLongSymbol" SG_ 6 rx_twice_long_yyyyyyyyyyyyyyyyyy "rx_twice_long_yyyyyyyyyyyyyyyyyyYYY"; +BA_ "SystemSignalLongSymbol" SG_ 5 Sig_used_twice_efgh_abcdefg_0000 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; BA_ "SystemMessageLongSymbol" BO_ 1 "Msg_Long_Name_56789_123456789_123456789_Copy_1"; -BA_ "SystemSignalLongSymbol" SG_ 1 Sig_used_twice_efgh_abcdefghi_ab "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +BA_ "SystemSignalLongSymbol" SG_ 1 Sig_used_twice_efgh_abcdefg_0001 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; BA_ "SystemMessageLongSymbol" BO_ 0 "Msg_Long_Name_56789_123456789_123456789"; -BA_ "SystemSignalLongSymbol" SG_ 0 Sig_used_twice_efgh_abcdefghi_ab "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; +BA_ "SystemSignalLongSymbol" SG_ 0 Sig_used_twice_efgh_abcdefg_0002 "Sig_used_twice_efgh_abcdefghi_abcdefghi_abcdefghi"; VAL_ 2 Sig_with_short_val_table 1 "Very long, long, long description for the value '0x1'" 0 "Very long, long, long description for the value '0x0'" ; VAL_ 2 Sig_with_long_val_table_2 13 "value '0xD'" 12 "Dvalue '0xC'" 11 "value '0xB'" 10 "value '0xA'" 9 "value '0x9'" 8 "value '0x8'" 7 "value '0x7'" 6 "value '0x6'" 5 "value '0x5'" 4 "value '0x4'" 3 "value '0x3'" 2 "value '0x2'" 1 "value '0x1'" 0 "value '0x0'" ; VAL_ 2 Sig_with_long_val_table_1 3 "Description for the value '0x3'" 2 "Description for the value '0x2'" 1 "Description for the value '0x1'" 0 "Description for the value '0x0'" ; diff --git a/tests/test_database.py b/tests/test_database.py index 1281db18..6a450761 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -4216,45 +4216,6 @@ def test_long_names_dbc(self): self.assertFalse('E12345678901234567890123456_0000' in envvar_names) self.assertTrue('E12345678901234567890123456789012' in envvar_names) - def test_dbc_long_names_converter(self): - long_names = [ - # 32 characters. - 'M1234567890123456789012345678901', - 'SS123456789012345678901234577890', - # More than 32 characters. - 'SS12345678901234567890123458789012345', - 'SS1234567890123456789012345778901', - 'SS1234567890123456789012345878901234', - 'SS12345678901234567890123456789012', - 'S12345678901234567890123456789012', - 'M123456789012345678901234567890123', - 'M12345678901234567890123456789012', - 'MM12345678901234567890123456789012' - ] - - short_names = [ - # 32 characters. - 'M1234567890123456789012345678901', - 'SS123456789012345678901234577890', - # More than 32 characters. - 'SS123456789012345678901234587890', - 'SS1234567890123456789012345_0000', - 'SS1234567890123456789012345_0001', - 'SS123456789012345678901234567890', - 'S1234567890123456789012345678901', - 'M12345678901234567890123456_0000', - 'M12345678901234567890123456_0001', - 'MM123456789012345678901234567890' - ] - - converter = dbc.LongNamesConverter(long_names) - - for long_name, short_name in zip(long_names, short_names): - self.assertEqual(converter.lookup_name(long_name), short_name) - - self.assertEqual(converter.long_to_short_names_dict, - dict(zip(long_names[2:], short_names[2:]))) - def test_system_arxml(self): db = cantools.db.load_file('tests/files/arxml/system-4.2.arxml') @@ -4689,41 +4650,63 @@ def test_issue_163_dbc_newlines(self): self.assertFalse(re.search(br'[^\r]\n', dumped_content)) os.remove(filename_dump) - def test_issue_167_long_names_dict(self): - """Test the base function of mapping long names to unique short names. + def test_issue_167_long_names_from_scratch(self): + """Test dbc export with mixed short and long symbol names. Create the + database by code, i.e. start with an empty database object, add + nodes, messages and signals, dump that to dbc format. Reload + the dumped data and check that it matches the original data. """ - datas = [ - { - }, - { - 'OBJ_long9_123456789_123456789_ABC' : 'OBJ_long9_123456789_123456789_AB', - 'OBJ_long9_123456789_123456789_DEF' : 'OBJ_long9_123456789_123456789_DE', - 'OBJ_short_123456789': 'OBJ_short_123456789', - 'OBJ_56789_123456789_123456789_GH' : 'OBJ_56789_123456789_1234567_0000', - 'OBJ_56789_123456789_123456789_GHI': 'OBJ_56789_123456789_1234567_0001', - 'OBJ_56789_123456789_123456789_GHIJ': 'OBJ_56789_123456789_1234567_0002' - }, - { - 'OBJ_56789_123456789_1234567_0000' : 'OBJ_56789_123456789_1234567_0000', - 'OBJ_56789_123456789_123456789_ABC': 'OBJ_56789_123456789_1234567_0001', - 'OBJ_56789_123456789_123456789_ABD': 'OBJ_56789_123456789_1234567_0002' - }, - { - 'OBJ_56789_123456789_1234567_0001' : 'OBJ_56789_123456789_1234567_0001', - 'OBJ_56789_123456789_123456789_ABC': 'OBJ_56789_123456789_1234567_0000', - 'OBJ_56789_123456789_123456789_ABD': 'OBJ_56789_123456789_1234567_0002' - } - ] + can = cantools.database.can + db = cantools.database.Database( + messages=[ + can.message.Message( + frame_id=1, + name='MSG456789_123456789_123456789_ABC', + length=8, + signals=[ + can.signal.Signal(name='SIG456789_123456789_123456789_ABC', + start=9, + length=8) + ], + senders=['NODE56789_abcdefghi_ABCDEFGHI_XYZ']), + can.message.Message( + frame_id=2, + name='MSG_short', + length=8, + signals=[ + can.signal.Signal(name='SIG_short', start=1, length=8) + ], + senders=['NODE_short']) + ], + nodes=[ + can.node.Node('NODE_short', None), + can.node.Node('NODE56789_abcdefghi_ABCDEFGHI_XYZ', None) + ], + version='') - for data in datas: - result = cantools.database.can.formats.dbc.create_one_unique_names_dict(data) - self.assertEqual(result, data) + db.refresh() + db = cantools.database.load_string(db.as_dbc_string()) + + self.assertEqual(len(db.nodes), 2) + self.assertEqual(db.nodes[0].name, 'NODE_short') + self.assertEqual(db.nodes[1].name, 'NODE56789_abcdefghi_ABCDEFGHI_XYZ') + + self.assertEqual(len(db.messages), 2) + + message = db.messages[0] + self.assertEqual(message.name, 'MSG456789_123456789_123456789_ABC') + self.assertEqual(message.senders, ['NODE56789_abcdefghi_ABCDEFGHI_XYZ']) + self.assertEqual(message.signals[0].name, 'SIG456789_123456789_123456789_ABC') - def test_long_names_from_file_multiple_relations(self): - """Test if long names are resolved correctly when message has more - than 1 sender. + message = db.messages[1] + self.assertEqual(message.name, 'MSG_short') + self.assertEqual(message.senders, ['NODE_short']) + self.assertEqual(message.signals[0].name, 'SIG_short') + + def test_long_names_multiple_relations(self): + """Test if long names are resolved correctly. """ @@ -4738,24 +4721,6 @@ def test_long_names_from_file_multiple_relations(self): else: self.assertEqual(db.as_dbc_string(), fin.read()) - def test_unknown_sender(self): - """Test warning if message has a sender not listed in the node list. - - """ - - node_name = 'Node_not_in_list' - db = cantools.database.Database() - msg = cantools.database.can.message.Message( - frame_id=1, - name='msg_dummy', - length=8, - signals=[], - senders=[node_name]) - db.messages.append(msg) - dump = db.as_dbc_string() - db_readback = cantools.database.load_string(dump, 'dbc') - self.assertEqual(db_readback.messages[0].senders, [node_name]) - def test_database_version(self): # default value if db created from scratch (map None to ''): db = cantools.database.Database()