根据ARXML中报文信息生成SWC(二)
集成的时候只有dbcasw使用的时候需要手动一个个创建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 rd:\workspace\Demo\davinci\ImportFiles\Databases\CHA_CAN.arxml OUTPUT_DIR rd:\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, level0, spaces_per2): 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, textNone, **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, textshort_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, encodingutf-8) # ElementTree with register_namespace(, ...) uses ns0: prefix # Replace to get clean default namespace output raw raw.replace(bns0:, b).replace(b xmlns:ns0, b xmlns) # Write the file with open(filepath, wb) as f: f.write(b?xml version1.0 encodingutf-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, UUIDmake_uuid(fsri_{signal_name})) SubElement(sri, SHORT-NAME, textfPi{signal_name}) SubElement(sri, IS-SERVICE, textfalse) data_elems SubElement(sri, DATA-ELEMENTS) vdp SubElement(data_elems, VARIABLE-DATA-PROTOTYPE, UUIDmake_uuid(fvdp_{signal_name})) SubElement(vdp, SHORT-NAME, textfDe{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, textNOT-ACCESSIBLE) SubElement(vdp, TYPE-TREF, DESTIMPLEMENTATION-DATA-TYPE, textdata_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, UUIDmake_uuid(fswc_{ecu_name})) SubElement(swc, SHORT-NAME, textfCtAp{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, UUIDmake_uuid(fpp_{ecu_name}_{sig})) SubElement(pp, SHORT-NAME, textfPp{sig}) # COM-SPECS with init value pcs SubElement(pp, PROVIDED-COM-SPECS) nscs SubElement(pcs, NONQUEUED-SENDER-COM-SPEC) SubElement(nscs, DATA-ELEMENT-REF, DESTVARIABLE-DATA-PROTOTYPE, textf/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}) iv SubElement(nscs, INIT-VALUE) nvs SubElement(iv, NUMERICAL-VALUE-SPECIFICATION) SubElement(nvs, VALUE, text0) SubElement(pp, PROVIDED-INTERFACE-TREF, DESTSENDER-RECEIVER-INTERFACE, textf/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}) if sig in rx_signal_names: # R-PORT-PROTOTYPE (Require) rp SubElement(ports, R-PORT-PROTOTYPE, UUIDmake_uuid(frp_{ecu_name}_{sig})) SubElement(rp, SHORT-NAME, textfRp{sig}) # COM-SPECS with init value rcs SubElement(rp, REQUIRED-COM-SPECS) nrcs SubElement(rcs, NONQUEUED-RECEIVER-COM-SPEC) SubElement(nrcs, DATA-ELEMENT-REF, DESTVARIABLE-DATA-PROTOTYPE, textf/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}) SubElement(nrcs, ALIVE-TIMEOUT, text0) SubElement(nrcs, ENABLE-UPDATE, texttrue) flt SubElement(nrcs, FILTER) SubElement(flt, DATA-FILTER-TYPE, textALWAYS) SubElement(nrcs, HANDLE-NEVER-RECEIVED, textfalse) iv SubElement(nrcs, INIT-VALUE) nvs SubElement(iv, NUMERICAL-VALUE-SPECIFICATION) SubElement(nvs, VALUE, text0) SubElement(rp, REQUIRED-INTERFACE-TREF, DESTSENDER-RECEIVER-INTERFACE, textf/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}) # ── Internal Behavior ────────────────────────────────────────────────── ib SubElement(swc, INTERNAL-BEHAVIORS) swc_ib SubElement(ib, SWC-INTERNAL-BEHAVIOR, UUIDmake_uuid(fib_{ecu_name})) SubElement(swc_ib, SHORT-NAME, textfCtAp{ecu_name}_InternalBehavior) # ── Exclusive Areas (under SWC-INTERNAL-BEHAVIOR) ── eas SubElement(swc_ib, EXCLUSIVE-AREAS) ea SubElement(eas, EXCLUSIVE-AREA, UUIDmake_uuid(fea_beh_{ecu_name})) SubElement(ea, SHORT-NAME, textEaJ1939) # ── 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 PDUdirection 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 fRE_{pdu_name}_{direction}_{cycle_ms:04d}ms # Timing Event for this cyclic runnable tm_event SubElement(events, TIMING-EVENT, UUIDmake_uuid(ftm_evt_{ecu_name}_{pdu_name}_{direction})) SubElement(tm_event, SHORT-NAME, textfTMT_{run_name}) SubElement(tm_event, START-ON-EVENT-REF, DESTRUNNABLE-ENTITY, textf/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/CtAp{ecu_name}_InternalBehavior/{run_name}) SubElement(tm_event, PERIOD, textstr(float(cycle_sec))) re SubElement(runnables, RUNNABLE-ENTITY, UUIDmake_uuid(fre_{ecu_name}_{pdu_name}_{direction})) SubElement(re, SHORT-NAME, textrun_name) SubElement(re, MINIMUM-START-INTERVAL, textstr(float(cycle_sec))) SubElement(re, CAN-BE-INVOKED-CONCURRENTLY, textfalse) if direction Rx: drpba SubElement(re, DATA-RECEIVE-POINT-BY-ARGUMENTS) for sig in signals_in_pdu: va SubElement(drpba, VARIABLE-ACCESS, UUIDmake_uuid(fva_rx_{ecu_name}_{pdu_name}_{sig})) SubElement(va, SHORT-NAME, textfREC_Pi{sig}_De{sig}) av SubElement(va, ACCESSED-VARIABLE) avi SubElement(av, AUTOSAR-VARIABLE-IREF) SubElement(avi, PORT-PROTOTYPE-REF, DESTR-PORT-PROTOTYPE, textf/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/Rp{sig}) SubElement(avi, TARGET-DATA-PROTOTYPE-REF, DESTVARIABLE-DATA-PROTOTYPE, textf/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, UUIDmake_uuid(fva_tx_{ecu_name}_{pdu_name}_{sig})) SubElement(va, SHORT-NAME, textfSEND_Pi{sig}_De{sig}) av SubElement(va, ACCESSED-VARIABLE) avi SubElement(av, AUTOSAR-VARIABLE-IREF) SubElement(avi, PORT-PROTOTYPE-REF, DESTP-PORT-PROTOTYPE, textf/UserApplication/SwcJ1939/ComponentTypes/CtAp{ecu_name}/Pp{sig}) SubElement(avi, TARGET-DATA-PROTOTYPE-REF, DESTVARIABLE-DATA-PROTOTYPE, textf/UserApplication/SwcJ1939/PortInterfaces/Pi{sig}/De{sig}) # SYMBOL must be last child per AUTOSAR schema order SubElement(re, SYMBOL, textrun_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, textfalse) return swc def make_swc_implementation(ecu_name): Create SWC-IMPLEMENTATION element for one ECU. impl ET.Element(SWC-IMPLEMENTATION, UUIDmake_uuid(fimpl_{ecu_name})) SubElement(impl, SHORT-NAME, textfCtAp{ecu_name}_Implementation) SubElement(impl, BEHAVIOR-REF, DESTSWC-INTERNAL-BEHAVIOR, textf/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(fFound {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(fFound {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(fFound {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(fFound {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(fFound {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) # Well 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 PDUcycle ──────────────── 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_okTrue) 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, textSwcJ1939) pi_sub_pkgs SubElement(swc_pkg, AR-PACKAGES) pi_pkg SubElement(pi_sub_pkgs, AR-PACKAGE) SubElement(pi_pkg, SHORT-NAME, textPortInterfaces) 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 ECUs 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 ECUs 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, textSwcJ1939) swc_sub_pkgs SubElement(swc_pkg2, AR-PACKAGES) ct_pkg SubElement(swc_sub_pkgs, AR-PACKAGE) SubElement(ct_pkg, SHORT-NAME, textComponentTypes) 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, fCtAp{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(fInput: {INPUT_ARXML}) print(fOutput: {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, fCtAp{ecu}.arxml)}) print() if __name__ __main__: main()提示只需指定以下三个参数内容即可INPUT_ARXML 输入arxml文件路径OUTPUT_DIR 输出文件夹路径PORT_IF_DIR 输出port文件路径最终生成如下文件实际根据用户arxml中ECU节点生成对应的swc其中