集成的时候只有dbc,asw使用的时候需要手动一个个创建port interface、runnable、runnable的event、每个runnable对应的rport、pport、port initvalue太麻烦了,于是自动化脚本实现,提高开发效率。
1、gen_swc_from_can.py
#!/usr/bin/env python3 """ Script: gen_swc_from_can.py Parse J1939 CAN database ARXML and generate DaVinci Developer SWC files. For each ECU node, generates: - One SENDER-RECEIVER-INTERFACE per signal (in shared PortInterfaces file) - One APPLICATION-SW-COMPONENT-TYPE with: - PPort for TX signals, RPort for RX signals - One Runnable per PDU/direction based on cycle time """ import xml.etree.ElementTree as ET import uuid as uuid_lib import os import sys from collections import defaultdict # ─── Configuration ─────────────────────────────────────────────────────────── INPUT_ARXML = r"d:\workspace\Demo\davinci\ImportFiles\Databases\CHA_CAN.arxml" OUTPUT_DIR = r"d:\workspace\Demo\davinci\ImportFiles\Swcs" PORT_IF_DIR = os.path.join(OUTPUT_DIR, "PortInterfaces_J1939.arxml") # ARXML settings AR_SCHEMA = "http://autosar.org/schema/r4.0" AR_XSI = "http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" # ─── Helpers ────────────────────────────────────────────────────────────────── _new_uuids = {} # cache deterministic UUIDs by key def make_uuid(key): """Generate a deterministic UUID based on a string key.""" if key not in _new_uuids: _new_uuids[key] = str(uuid_lib.uuid5(uuid_lib.NAMESPACE_DNS, key + "@j1939swc")).upper() return _new_uuids[key] def indent(elem, level=0, spaces_per=2): """Pretty-print indentation for ElementTree (in-place).""" s = "\n" + " " * (level * spaces_per) if len(elem): if not elem.text or not elem.text.strip(): elem.text = s + " " * spaces_per if not elem.tail or not elem.tail.strip(): elem.tail = s for child in elem: indent(child, level + 1, spaces_per) if not child.tail or not child.tail.strip(): child.tail = s else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = s def SubElement(parent, tag, text=None, **attribs): """Create a sub-element and return it, with optional text and attribs.""" elem = ET.SubElement(parent, tag, **attribs) if text is not None: elem.text = text return elem def arxml_preamble(): """Return a minimal root ET.Element for a new ARXML file.""" root = ET.Element( "AUTOSAR", attrib={ "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation": AR_XSI, }, ) root.set("xmlns", AR_SCHEMA) # xmlns:xsi is already defined by the xsi:schemaLocation attribute above return root def arxml_element(root, short_name, uuid_key): """Build the standard AR-PACKAGES > AR-PACKAGE > ... structure. Returns (ar_package_root, elements_parent) tuple. """ ar_packages = SubElement(root, "AR-PACKAGES") ar_package = SubElement(ar_packages, "AR-PACKAGE") SubElement(ar_package, "SHORT-NAME", text=short_name) elements = SubElement(ar_package, "ELEMENTS") return ar_package, elements def _write_arxml(root_elem, filepath): """Write an ARXML file from an ElementTree root element. Handles namespace formatting to match DaVinci Developer convention. """ ET.register_namespace("", AR_SCHEMA) raw = ET.tostring(root_elem, encoding="utf-8") # ElementTree with register_namespace("", ...) uses "ns0:" prefix # Replace to get clean default namespace output raw = raw.replace(b"ns0:", b"").replace(b" xmlns:ns0=", b" xmlns=") # Write the file with open(filepath, "wb") as f: f.write(b'<?xml version="1.0" encoding="utf-8"?>\n') f.write(raw) def data_type_for_length(length_bits): """Map signal length (bits) to AUTOSAR ImplementationDataType reference.""" if length_bits <= 8: return "/AUTOSAR_Platform/ImplementationDataTypes/uint8" elif length_bits <= 16: return "/AUTOSAR_Platform/ImplementationDataTypes/uint16" else: return "/AUTOSAR_Platform/ImplementationDataTypes/uint32" def make_sender_receiver_interface(signal_name, data_type_ref): """Create a SENDER-RECEIVER-INTERFACE element for one signal.""" sri = ET.Element("SENDER-RECEIVER-INTERFACE", UUID=make_uuid(f"sri_{signal_name}")) SubElement(sri, "SHORT-NAME", text=f"Pi{signal_name}") SubElement(sri, "IS-SERVICE", text="false") data_elems = SubElement(sri, "DATA-ELEMENTS") vdp = SubElement(data_elems, "VARIABLE-DATA-PROTOTYPE", UUID=make_uuid(f"vdp_{signal_name}")) SubElement(vdp, "SHORT-NAME", text=f"De{signal_name}") sdv = SubElement(vdp, "SW-DATA-DEF-PROPS") sdvv = SubElement(sdv, "SW-DATA-DEF-PROPS-VARIANTS") sdvc = SubElement(sdvv, "SW-DATA-DEF-PROPS-CONDITIONAL") SubElement(sdvc, "SW-CALIBRATION-ACCESS", text="NOT-ACCESSIBLE") SubElement(vdp, "TYPE-TREF", DEST="IMPLEMENTATION-DATA-TYPE", text=data_type_ref) return sri def make_swc(ecu_name, tx_signal_names, rx_signal_names, signal_pdu_cycle_map): """Create APPLICATION-SW-COMPONENT-TYPE for one ECU. signal_pdu_cycle_map: dict { signal_name -> (pdu_name, cycle_sec, direction:'Tx'|'Rx') } """ swc = ET.Element("APPLICATION-SW-COMPONENT-TYPE", UUID=make_uuid(f"swc_{ecu_name}")) SubElement(swc, "SHORT-NAME", text=f"CtAp{ecu_name}") # ── Ports ────────────────────────────────────────────────────────────── ports = SubElement(swc, "PORTS") # All unique signal names (both TX and RX) for this ECU all_signals = list(dict.fromkeys(tx_signal_names + rx_signal_names)) # preserve order, deduplicate all_signals_set = set(all_signals) for sig in all_signals: if sig in tx_signal_names: # P-PORT-PROTOTYPE (Provider) pp = SubElement(ports, "P-PORT-PROTOTYPE", UUID=make_uuid(f"pp_{ecu_name}_{sig}")) SubElement(pp, "SHORT-NAME", text=f"Pp{sig}") # COM-SPECS with init value pcs = SubElement(pp, "PROVIDED-COM-SPECS") nscs = SubElement(pcs, "NONQUEUED-SENDER-COM-SPEC") SubElement(nscs, "DATA-ELEMENT-REF", DEST="VARIABLE-DATA-PROTOTYPE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}") iv = SubElement(nscs, "INIT-VALUE") nvs = SubElement(iv, "NUMERICAL-VALUE-SPECIFICATION") SubElement(nvs, "VALUE", text="0") SubElement(pp, "PROVIDED-INTERFACE-TREF", DEST="SENDER-RECEIVER-INTERFACE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}") if sig in rx_signal_names: # R-PORT-PROTOTYPE (Require) rp = SubElement(ports, "R-PORT-PROTOTYPE", UUID=make_uuid(f"rp_{ecu_name}_{sig}")) SubElement(rp, "SHORT-NAME", text=f"Rp{sig}") # COM-SPECS with init value rcs = SubElement(rp, "REQUIRED-COM-SPECS") nrcs = SubElement(rcs, "NONQUEUED-RECEIVER-COM-SPEC") SubElement(nrcs, "DATA-ELEMENT-REF", DEST="VARIABLE-DATA-PROTOTYPE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}") SubElement(nrcs, "ALIVE-TIMEOUT", text="0") SubElement(nrcs, "ENABLE-UPDATE", text="true") flt = SubElement(nrcs, "FILTER") SubElement(flt, "DATA-FILTER-TYPE", text="ALWAYS") SubElement(nrcs, "HANDLE-NEVER-RECEIVED", text="false") iv = SubElement(nrcs, "INIT-VALUE") nvs = SubElement(iv, "NUMERICAL-VALUE-SPECIFICATION") SubElement(nvs, "VALUE", text="0") SubElement(rp, "REQUIRED-INTERFACE-TREF", DEST="SENDER-RECEIVER-INTERFACE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}") # ── Internal Behavior ────────────────────────────────────────────────── ib = SubElement(swc, "INTERNAL-BEHAVIORS") swc_ib = SubElement(ib, "SWC-INTERNAL-BEHAVIOR", UUID=make_uuid(f"ib_{ecu_name}")) SubElement(swc_ib, "SHORT-NAME", text=f"CtAp{ecu_name}_InternalBehavior") # ── Exclusive Areas (under SWC-INTERNAL-BEHAVIOR) ── eas = SubElement(swc_ib, "EXCLUSIVE-AREAS") ea = SubElement(eas, "EXCLUSIVE-AREA", UUID=make_uuid(f"ea_beh_{ecu_name}")) SubElement(ea, "SHORT-NAME", text="EaJ1939") # ── Events (before RUNNABLES per AUTOSAR schema) ───────────────────── events = SubElement(swc_ib, "EVENTS") # ── Runnables ────────────────────────────────────────────────────────── runnables = SubElement(swc_ib, "RUNNABLES") # Build runnables grouped by (PDU, direction) to avoid duplicates # Each runnable handles all signals of one PDU in one direction pdu_dir_signals = defaultdict(set) # (pdu_name, direction) -> set of signal_name pdu_dir_cycle = {} # (pdu_name, direction) -> cycle_sec for sig, entries in signal_pdu_cycle_map.items(): for (pdu, cycle_sec, direction) in entries: key = (pdu, direction) pdu_dir_signals[key].add(sig) # Use the shortest cycle for this PDU+direction if key not in pdu_dir_cycle or cycle_sec < pdu_dir_cycle[key]: pdu_dir_cycle[key] = cycle_sec for (pdu_name, direction), signals_in_pdu in pdu_dir_signals.items(): cycle_sec = pdu_dir_cycle[(pdu_name, direction)] cycle_ms = int(float(cycle_sec) * 1000) run_name = f"RE_{pdu_name}_{direction}_{cycle_ms:04d}ms" # Timing Event for this cyclic runnable tm_event = SubElement(events, "TIMING-EVENT", UUID=make_uuid(f"tm_evt_{ecu_name}_{pdu_name}_{direction}")) SubElement(tm_event, "SHORT-NAME", text=f"TMT_{run_name}") SubElement(tm_event, "START-ON-EVENT-REF", DEST="RUNNABLE-ENTITY", text=f"/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/CtAp{ecu_name}_InternalBehavior/{run_name}") SubElement(tm_event, "PERIOD", text=str(float(cycle_sec))) re = SubElement(runnables, "RUNNABLE-ENTITY", UUID=make_uuid(f"re_{ecu_name}_{pdu_name}_{direction}")) SubElement(re, "SHORT-NAME", text=run_name) SubElement(re, "MINIMUM-START-INTERVAL", text=str(float(cycle_sec))) SubElement(re, "CAN-BE-INVOKED-CONCURRENTLY", text="false") if direction == "Rx": drpba = SubElement(re, "DATA-RECEIVE-POINT-BY-ARGUMENTS") for sig in signals_in_pdu: va = SubElement(drpba, "VARIABLE-ACCESS", UUID=make_uuid(f"va_rx_{ecu_name}_{pdu_name}_{sig}")) SubElement(va, "SHORT-NAME", text=f"REC_Pi{sig}_De{sig}") av = SubElement(va, "ACCESSED-VARIABLE") avi = SubElement(av, "AUTOSAR-VARIABLE-IREF") SubElement(avi, "PORT-PROTOTYPE-REF", DEST="R-PORT-PROTOTYPE", text=f"/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/Rp{sig}") SubElement(avi, "TARGET-DATA-PROTOTYPE-REF", DEST="VARIABLE-DATA-PROTOTYPE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}") else: # Tx dsp = SubElement(re, "DATA-SEND-POINTS") for sig in signals_in_pdu: va = SubElement(dsp, "VARIABLE-ACCESS", UUID=make_uuid(f"va_tx_{ecu_name}_{pdu_name}_{sig}")) SubElement(va, "SHORT-NAME", text=f"SEND_Pi{sig}_De{sig}") av = SubElement(va, "ACCESSED-VARIABLE") avi = SubElement(av, "AUTOSAR-VARIABLE-IREF") SubElement(avi, "PORT-PROTOTYPE-REF", DEST="P-PORT-PROTOTYPE", text=f"/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/Pp{sig}") SubElement(avi, "TARGET-DATA-PROTOTYPE-REF", DEST="VARIABLE-DATA-PROTOTYPE", text=f"/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}") # SYMBOL must be last child per AUTOSAR schema order SubElement(re, "SYMBOL", text=run_name) # If no runnables were created (edge case), ensure RUNNABLES exists if len(pdu_dir_signals) == 0: # Remove the empty RUNNABLES swc_ib.remove(runnables) # SUPPORTS-MULTIPLE-INSTANTIATION must come after RUNNABLES per AUTOSAR schema order SubElement(swc_ib, "SUPPORTS-MULTIPLE-INSTANTIATION", text="false") return swc def make_swc_implementation(ecu_name): """Create SWC-IMPLEMENTATION element for one ECU.""" impl = ET.Element("SWC-IMPLEMENTATION", UUID=make_uuid(f"impl_{ecu_name}")) SubElement(impl, "SHORT-NAME", text=f"CtAp{ecu_name}_Implementation") SubElement(impl, "BEHAVIOR-REF", DEST="SWC-INTERNAL-BEHAVIOR", text=f"/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/CtAp{ecu_name}_InternalBehavior") return impl # ─── Main parsing logic ─────────────────────────────────────────────────────── def parse_arxml(filepath): """Parse the CAN ARXML and extract ECU/Signal/PDU relationships.""" tree = ET.parse(filepath) root = tree.getroot() ns = {"ar": AR_SCHEMA} # ── 1. Collect ECUs ──────────────────────────────────────────────────── ecus = set() # Get ECUs from NM nodes for nm_node in root.iter("{http://autosar.org/schema/r4.0}J-1939-NM-NODE"): sn = nm_node.find("ar:SHORT-NAME", ns) if sn is not None and sn.text: ecus.add(sn.text) print(f"Found {len(ecus)} ECUs: {', '.join(sorted(ecus))}") # ── 2. I-SIGNAL definitions: ISignalName -> (length_bits, base_type_ref) ── isignal_info = {} # ISignalShortName -> length (bits) for isig in root.iter("{http://autosar.org/schema/r4.0}I-SIGNAL"): sn = isig.find("ar:SHORT-NAME", ns) length_el = isig.find("ar:LENGTH", ns) if sn is not None and length_el is not None: isignal_info[sn.text] = int(length_el.text) print(f"Found {len(isignal_info)} I-SIGNALs") # ── 3. I-SIGNAL-TRIGGERING: signal -> {Tx: ecu, Rx: [ecus]} ── sig_triggering = {} # ST_name -> (isignal_ref, [(ecu, direction)]) sig_to_ecu = defaultdict(lambda: {"Tx": [], "Rx": []}) # ISignalShortName -> {Tx:[ecu], Rx:[ecus]} for ist in root.iter("{http://autosar.org/schema/r4.0}I-SIGNAL-TRIGGERING"): st_name = ist.find("ar:SHORT-NAME", ns) isig_ref = ist.find("ar:I-SIGNAL-REF", ns) if st_name is None or isig_ref is None: continue isig_name = isig_ref.text.split("/")[-1] # Get the ISignal short name port_refs = ist.find("ar:I-SIGNAL-PORT-REFS", ns) if port_refs is None: continue ecu_dirs = [] for port_ref in port_refs.findall("ar:I-SIGNAL-PORT-REF", ns): port_path = port_ref.text # /ECU/{ECU}/CN_CHA_CAN/SP_{signal}_{Tx|Rx} if port_path: parts = port_path.split("/") ecu_name = parts[2] # /ECU/{ECU}/... port_name = parts[4] # SP_{signal}_{Tx|Rx} direction = "Tx" if port_name.endswith("_Tx") else "Rx" ecu_dirs.append((ecu_name, direction)) sig_to_ecu[isig_name][direction].append(ecu_name) sig_triggering[st_name.text] = { "isignal_ref": isig_name, "ecu_dirs": ecu_dirs, } print(f"Found {len(sig_triggering)} I-SIGNAL-TRIGGERINGs") # ── 4. PDU-TRIGGERING: PDU -> {Tx: ecu, Rx: [ecus], signals: [ST_refs], PDU_ref} ── pdu_triggering = {} for pt in root.iter("{http://autosar.org/schema/r4.0}PDU-TRIGGERING"): pt_name = pt.find("ar:SHORT-NAME", ns) pdu_ref = pt.find("ar:I-PDU-REF", ns) if pt_name is None or pdu_ref is None: continue pdu_short = pdu_ref.text.split("/")[-1] # e.g. ABS_DM1_oCHA_CAN # PDU port refs port_refs = pt.find("ar:I-PDU-PORT-REFS", ns) ecu_dirs = [] if port_refs is not None: for port_ref in port_refs.findall("ar:I-PDU-PORT-REF", ns): port_path = port_ref.text if port_path: parts = port_path.split("/") ecu_name = parts[2] port_name = parts[4] direction = "Tx" if port_name.endswith("_Tx") else "Rx" ecu_dirs.append((ecu_name, direction)) # Signal triggering refs sig_refs = [] sig_trig_container = pt.find("ar:I-SIGNAL-TRIGGERINGS", ns) if sig_trig_container is not None: for strc in sig_trig_container.findall("ar:I-SIGNAL-TRIGGERING-REF-CONDITIONAL", ns): str_ref = strc.find("ar:I-SIGNAL-TRIGGERING-REF", ns) if str_ref is not None and str_ref.text: sig_refs.append(str_ref.text) pdu_triggering[pt_name.text] = { "pdu_ref": pdu_short, "ecu_dirs": ecu_dirs, "sig_triggering_refs": sig_refs, } print(f"Found {len(pdu_triggering)} PDU-TRIGGERINGs") # ── 5. I-SIGNAL-I-PDU: PDU timing ────────────────────────────────────── pdu_cycle_time = {} # pdu_short_name -> cycle_time_seconds for pdu in root.iter("{http://autosar.org/schema/r4.0}I-SIGNAL-I-PDU"): pdu_sn = pdu.find("ar:SHORT-NAME", ns) if pdu_sn is None: continue # Get the first TIME-PERIOD.VALUE for tp in pdu.iter("{http://autosar.org/schema/r4.0}TIME-PERIOD"): val = tp.find("ar:VALUE", ns) if val is not None and val.text: pdu_cycle_time[pdu_sn.text] = float(val.text) break # use the first cycle time (usually both are the same) print(f"Found {len(pdu_cycle_time)} PDUs with cycle time") return { "ecus": ecus, "isignal_info": isignal_info, "sig_to_ecu": sig_to_ecu, "sig_triggering": sig_triggering, "pdu_triggering": pdu_triggering, "pdu_cycle_time": pdu_cycle_time, } def build_ecu_data(parsed): """Build per-ECU data from parsed structures.""" ecus = parsed["ecus"] isignal_info = parsed["isignal_info"] sig_to_ecu = parsed["sig_to_ecu"] pdu_triggering = parsed["pdu_triggering"] pdu_cycle_time = parsed["pdu_cycle_time"] # ── Build ISignal name -> system signal name mapping ────────────────── # ISignal names look like: ABS_Active_oABS_EBC1_oCHA_CAN # System signal names: ABS_Active # We need a shorter name for the port interface. # Extract signal name from ISignal triggering name. # The ISignal SHORT-NAME format is: {SYS_SIGNAL}_{originPDU}_{bus} # Simpler: from sig_triggering -> isignal_ref (ISignal short name) # We'll map ISignal -> port interface signal name (the system signal part) # Build: isignal_name -> port_signal_name isig_to_portsig = {} for isig_name in isignal_info: # Pattern: SIGNALNAME_oPDU_oBUS -> SIGNALNAME # Most J1939 signals follow this pattern parts = isig_name.split("_o") if len(parts) >= 2: portsig = parts[0] else: portsig = isig_name isig_to_portsig[isig_name] = portsig # ── Build PDU -> signals (ISignal names) mapping ────────────────────── # From ST refs -> ST name -> ISignal name st_to_isig = {} # ST (Signal Triggering) name -> ISignal name for st_name, st_data in parsed["sig_triggering"].items(): st_to_isig[st_name] = st_data["isignal_ref"] # ── For each ECU, collect TX/RX signals with PDU+cycle ──────────────── ecu_signals = defaultdict(lambda: {"tx": set(), "rx": set()}) # signal_name -> set of (pdu_name, cycle_sec, direction) (no duplicates) signal_pdu_cycle = defaultdict(set) for pt_name, pt_data in pdu_triggering.items(): pdu_ref = pt_data["pdu_ref"] cycle_sec = pdu_cycle_time.get(pdu_ref, 1.0) # default 1s if not found # Which ECUs are TX/RX for this PDU pdu_tx_ecu = None pdu_rx_ecus = [] for ecu, direction in pt_data["ecu_dirs"]: if direction == "Tx": pdu_tx_ecu = ecu else: pdu_rx_ecus.append(ecu) # Get unique ISignal names for this PDU (deduplicate) isig_names = set() for st_ref in pt_data["sig_triggering_refs"]: # ST ref looks like: /Cluster/CHA_CAN/CHNL/ST_ABS_Active_oABS_EBC1 st_name = st_ref.split("/")[-1] isig_name = st_to_isig.get(st_name) if isig_name: isig_names.add(isig_name) # Map ISignal names -> port signal names for isig_name in isig_names: portsig = isig_to_portsig.get(isig_name, isig_name) if pdu_tx_ecu: ecu_signals[pdu_tx_ecu]["tx"].add(portsig) signal_pdu_cycle[portsig].add((pdu_ref, cycle_sec, "Tx")) for rx_ecu in pdu_rx_ecus: ecu_signals[rx_ecu]["rx"].add(portsig) signal_pdu_cycle[portsig].add((pdu_ref, cycle_sec, "Rx")) return { "ecu_signals": ecu_signals, "signal_pdu_cycle": signal_pdu_cycle, "isignal_info": isignal_info, "isig_to_portsig": isig_to_portsig, } def generate_output(parsed, ecu_data): """Generate ARXML output files.""" os.makedirs(OUTPUT_DIR, exist_ok=True) ecus = sorted(parsed["ecus"]) isignal_info = parsed["isignal_info"] isig_to_portsig = ecu_data["isig_to_portsig"] ecu_signals = ecu_data["ecu_signals"] signal_pdu_cycle = ecu_data["signal_pdu_cycle"] # ── Collect all unique signal names across all ECUs ──────────────────── all_signals = set() for ecu in ecus: all_signals.update(ecu_signals[ecu]["tx"]) all_signals.update(ecu_signals[ecu]["rx"]) all_signals = sorted(all_signals) # Build ISignal name -> port signal -> ISignal length mapping portsig_to_isig = {} for isig_name, portsig in isig_to_portsig.items(): portsig_to_isig[portsig] = isig_name # ── 1. Generate PortInterfaces file ──────────────────────────────────── pi_root = arxml_preamble() mxl_pkg, mxl_elements = arxml_element(pi_root, "UserApplication", "mxl") mxl_sub_pkgs = SubElement(mxl_pkg, "AR-PACKAGES") swc_pkg = SubElement(mxl_sub_pkgs, "AR-PACKAGE") SubElement(swc_pkg, "SHORT-NAME", text="SwcJ1939") pi_sub_pkgs = SubElement(swc_pkg, "AR-PACKAGES") pi_pkg = SubElement(pi_sub_pkgs, "AR-PACKAGE") SubElement(pi_pkg, "SHORT-NAME", text="PortInterfaces") pi_elements = SubElement(pi_pkg, "ELEMENTS") # Remove empty ELEMENTS from UserApplication package mxl_pkg.remove(mxl_elements) for sig in all_signals: isig_name = portsig_to_isig.get(sig, sig) length = isignal_info.get(isig_name, 8) dt_ref = data_type_for_length(length) sri = make_sender_receiver_interface(sig, dt_ref) pi_elements.append(sri) indent(pi_root) # Write PortInterfaces file _write_arxml(pi_root, PORT_IF_DIR) print(f"[OK] Written: {PORT_IF_DIR}") print(f" - {len(all_signals)} SENDER-RECEIVER-INTERFACEs") # ── 2. Generate ComponentType file for each ECU ──────────────────────── for ecu in ecus: tx_sigs = sorted(ecu_signals[ecu]["tx"]) rx_sigs = sorted(ecu_signals[ecu]["rx"]) # Filter signal_pdu_cycle_map for this ECU's signals only ecu_sig_pdu = {} for sig in tx_sigs + rx_sigs: entries = signal_pdu_cycle.get(sig, set()) # Only include entries where direction matches this ECU's role filtered = [] for (pdu, cycle, direction) in entries: # For TX: only TX entries where this ECU was TX # For RX: only RX entries keep = False if sig in tx_sigs and direction == "Tx": keep = True if sig in rx_sigs and direction == "Rx": keep = True if keep: filtered.append((pdu, cycle, direction)) if filtered: ecu_sig_pdu[sig] = filtered swc_root = arxml_preamble() # Package: UserApplication -> SwcJ1939 -> ComponentTypes mxl_pkg2, mxl_elem2 = arxml_element(swc_root, "UserApplication", "mxl_ct_" + ecu) mxl_sub_pkgs2 = SubElement(mxl_pkg2, "AR-PACKAGES") swc_pkg2 = SubElement(mxl_sub_pkgs2, "AR-PACKAGE") SubElement(swc_pkg2, "SHORT-NAME", text="SwcJ1939") swc_sub_pkgs = SubElement(swc_pkg2, "AR-PACKAGES") ct_pkg = SubElement(swc_sub_pkgs, "AR-PACKAGE") SubElement(ct_pkg, "SHORT-NAME", text="ComponentTypes") ct_elements = SubElement(ct_pkg, "ELEMENTS") swc = make_swc(ecu, tx_sigs, rx_sigs, ecu_sig_pdu) ct_elements.append(swc) impl = make_swc_implementation(ecu) ct_elements.append(impl) # Remove empty ELEMENTS from UserApplication package mxl_pkg2.remove(mxl_elem2) indent(swc_root) comp_file = os.path.join(OUTPUT_DIR, f"CtAp{ecu}.arxml") _write_arxml(swc_root, comp_file) print(f"[OK] Written: {comp_file}") print(f" - TX signals: {len(tx_sigs)}, RX signals: {len(rx_sigs)}") # Count runnables run_count = 0 if len(ecu_sig_pdu) > 0: # collect unique (pdu, direction) pairs seen = set() for entries in ecu_sig_pdu.values(): for (pdu, cycle, direction) in entries: seen.add((pdu, direction)) run_count = len(seen) print(f" - Runnables: {run_count}") # ─── Entry point ────────────────────────────────────────────────────────────── def main(): print("=" * 60) print("J1939 SWC Generator") print("=" * 60) print(f"Input: {INPUT_ARXML}") print(f"Output: {OUTPUT_DIR}") print() parsed = parse_arxml(INPUT_ARXML) ecu_data = build_ecu_data(parsed) generate_output(parsed, ecu_data) print() print("Done! Files generated:") print(f" 1. {PORT_IF_DIR}") for ecu in sorted(parsed["ecus"]): print(f" {os.path.join(OUTPUT_DIR, f'CtAp{ecu}.arxml')}") print() if __name__ == "__main__": main()提示:
只需指定以下三个参数内容即可:
INPUT_ARXML 输入arxml文件路径
OUTPUT_DIR 输出文件夹路径
PORT_IF_DIR 输出port文件路径
最终生成如下文件,实际根据用户arxml中ECU节点生成对应的swc,其中: