集成的时候只有dbc,asw使用的时候需要手动一个个创建port interface、runnable、runnable的event、每个runnable对应的rport、pport、port initvalue太麻烦了,于是自动化脚本实现,提高开发效率。
1、gen_swc_from_can.py
python
#!/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,其中:
