tools/cloud/dbc-to-decoders.py (97 lines of code) (raw):

#!/usr/bin/env python3 # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 import argparse import json import sys import cantools default_interface_id = "1" parser = argparse.ArgumentParser( description=( "Converts a DBC file to AWS IoT FleetWise 'decoders' format for use with " "CreateDecoderManifest" ) ) parser.add_argument( "-p", "--permissive", action="store_true", help="Apply the cantools strict=False option when loading the DBC file", ) parser.add_argument( "-i", "--interface-id", default=default_interface_id, help=( f'Network interface ID, default "{default_interface_id}". This must match the ID used in ' "the static config file." ), ) parser.add_argument( "infile", nargs="?", type=argparse.FileType("r"), default=sys.stdin, help="Input DBC file, default stdin", ) parser.add_argument( "outfile", nargs="?", type=argparse.FileType("w"), default=sys.stdout, help="Output filename, default stdout", ) args = parser.parse_args() db = cantools.database.load(args.infile, strict=not args.permissive) signal_decoders_to_add = [] processed_messages = set() for message in db.messages: message_text = message.name if message.name else message.frame_id if message_text in processed_messages: message_text = f"{message_text}_{message.frame_id}" if message_text in processed_messages: print( f"Message {message.frame_id} occurs multiple times, only the first occurrence " "will be used", file=sys.stderr, ) continue processed_messages.add(message_text) processed_signals = set() for signal in message.signals: signal_fqn = f"Vehicle.{message_text}.{signal.name}" if signal.name in processed_signals: print( f"Signal {signal.name} occurs multiple times in the message {message_text}, only" " the first occurrence will be used", file=sys.stderr, ) continue processed_signals.add(signal.name) signal_to_add = {} signal_to_add["name"] = signal.name signal_to_add["factor"] = signal.scale signal_to_add["isBigEndian"] = signal.byte_order == "big_endian" signal_to_add["isSigned"] = signal.is_signed signal_to_add["length"] = signal.length signal_to_add["offset"] = signal.offset signal_to_add["messageId"] = message.frame_id signal_to_add["signalValueType"] = "FLOATING_POINT" if signal.is_float else "INTEGER" # In a DBC file, the start bit indicates the LSB for little endian and MSB for big endian # signals. AWS IoT FleetWise considers start bit to always be the LSB regardless of # endianess. That is why we need to convert the value obtained from DBC. if signal.byte_order == "big_endian": pos = 7 - (signal.start % 8) + (signal.length - 1) if pos < 8: signal_to_add["startBit"] = signal.start - signal.length + 1 else: byte_count = int(pos / 8) signal_to_add["startBit"] = int( 7 - (pos % 8) + (byte_count * 8) + int(signal.start / 8) * 8 ) else: signal_to_add["startBit"] = signal.start signal_decoders_to_add.append( { "type": "CAN_SIGNAL", "canSignal": signal_to_add, "fullyQualifiedName": signal_fqn, "interfaceId": args.interface_id, } ) out = json.dumps(signal_decoders_to_add, indent=4, sort_keys=True) args.outfile.write(out)