Working with IF_DATA#
IF_DATA sections in A2L files carry vendor-specific transport-layer and
protocol information (XCP, CCP, KWP2000, …). pyA2L parses these blocks
automatically when an AML description is available and wraps the result
in the IfData dataclass, which gives you
simultaneous access to the parsed (structured) and raw (text)
representation.
The IfData class#
Every inspect object that can contain IF_DATA blocks
(Module, Measurement, Characteristic, AxisPts,
Function, Group, Frame, Instance, Blob, and the
MemoryLayout / MemorySegment entries inside ModPar)
exposes an if_data attribute of type IfData.
from dataclasses import dataclass, field
from typing import Any, Dict, List, Union
@dataclass
class IfData:
"""Parsed + raw IF_DATA container.
Parameters
----------
if_data_parsed : List[Any]
Structured representation produced by the AML-based parser.
Each list element corresponds to one ``/begin IF_DATA … /end IF_DATA``
block in the source file.
if_data_raw : list
The original model objects (``pya2l.model.IfData``) that hold the
raw text. Useful when you need the verbatim A2L source, e.g. for
re-export or manual inspection.
Attributes
----------
flatmap : Dict[str, List[Any]]
A lazily built dictionary that flattens the nested parsed structure
into a key → [values…] mapping. Handy for quick look-ups when
you know the tag name but not the nesting depth.
"""
if_data_parsed: List[Any]
if_data_raw: list
items: Dict[str, List[Any]] = field(default_factory=dict)
@property
def flatmap(self) -> Dict[str, List[Any]]: ...
Quick start#
from pya2l import DB
from pya2l.api.inspect import Measurement
db = DB()
session = db.open_create("ASAP2_Demo_V161.a2l")
meas = Measurement.get(session, "ASAM.M.SCALAR.UBYTE.IDENTICAL")
# IfData instance — always present, may be empty
ifd = meas.if_data
# Parsed data (list of dicts/nested structures)
print(ifd.if_data_parsed) # e.g. [{'XCP': { ... }}]
# Raw model objects (for re-export or text inspection)
print(len(ifd.if_data_raw)) # number of IF_DATA blocks
# Quick key-based look-up via flatmap
if "DAQ_LIST" in ifd.flatmap:
print(ifd.flatmap["DAQ_LIST"])
Parsed data vs. raw data#
Why both? The parsed representation is the fast path for programmatic
access — you get Python dicts you can index into. The raw representation
is essential when you need the original text for diagnostics, logging,
or when the AML schema is unavailable (in which case if_data_parsed
is empty but the raw text is still there).
Using the flatmap#
Deeply nested IF_DATA trees can be tedious to traverse. flatmap
flattens every key it encounters into a dictionary of lists:
from pya2l import DB
from pya2l.api.inspect import Module
session = DB().open_create("xcp_demo_autodetect.a2l")
module = Module(session)
ifd = module.if_data # IfData instance
# Iterate all keys the parser found
for key, values in ifd.flatmap.items():
print(f"{key}: {len(values)} occurrence(s)")
# Direct look-up of a known tag
if "PROTOCOL_LAYER" in ifd.flatmap:
proto = ifd.flatmap["PROTOCOL_LAYER"]
print("Protocol layer info:", proto)
if "DAQ" in ifd.flatmap:
daq = ifd.flatmap["DAQ"]
print("DAQ configuration:", daq)
Note
flatmap is built lazily on first access. The cost is proportional
to the depth of the parsed tree and is paid only once.
Accessing IF_DATA across all entity types#
Every entity that can carry IF_DATA in the ASAP2 standard exposes it:
from pya2l import DB
from pya2l.api.inspect import (
Module, Measurement, Characteristic, AxisPts,
Function, Group, Frame,
)
session = DB().open_create("my_ecu.a2l")
module = Module(session)
# --- MODULE level ---
print("Module IF_DATA:", module.if_data.if_data_parsed)
# --- MEASUREMENT ---
for meas in module.measurement.query():
if meas.if_data.if_data_parsed:
print(f" {meas.name}: {meas.if_data.if_data_parsed}")
# --- CHARACTERISTIC ---
for char in module.characteristic.query():
if char.if_data.if_data_parsed:
print(f" {char.name}: {char.if_data.if_data_parsed}")
# --- AXIS_PTS ---
for ax in module.axis_pts.query():
if ax.if_data.if_data_parsed:
print(f" {ax.name}: {ax.if_data.if_data_parsed}")
# --- FUNCTION, GROUP, FRAME ---
for fn in module.function.query():
if fn.if_data.if_data_parsed:
print(f" FUNCTION {fn.name}: {fn.if_data.if_data_parsed}")
for grp in module.group.query():
if grp.if_data.if_data_parsed:
print(f" GROUP {grp.name}: {grp.if_data.if_data_parsed}")
for fr in module.frame.query():
if fr.if_data.if_data_parsed:
print(f" FRAME {fr.name}: {fr.if_data.if_data_parsed}")
# --- MEMORY_LAYOUT / MEMORY_SEGMENT (inside ModPar) ---
mp = module.mod_par
if mp:
for layout in mp.memoryLayouts:
if layout.if_data.if_data_parsed:
print(f" MEMORY_LAYOUT: {layout.if_data.if_data_parsed}")
for seg in mp.memorySegments:
if seg.if_data.if_data_parsed:
print(f" MEMORY_SEGMENT {seg.name}: {seg.if_data.if_data_parsed}")
Practical examples#
Extracting XCP transport-layer parameters#
Many ECU projects use XCP on Ethernet or CAN. The transport configuration lives in the module-level IF_DATA:
from pya2l import DB
from pya2l.api.inspect import Module
session = DB().open_create("xcp_demo_autodetect.a2l")
module = Module(session)
ifd = module.if_data
# Walk the parsed tree for XCP specifics
for block in ifd.if_data_parsed:
if not isinstance(block, dict):
continue
if "XCP" not in block:
continue
xcp = block["XCP"]
# Protocol layer
if "PROTOCOL_LAYER" in xcp:
proto = xcp["PROTOCOL_LAYER"]
print("XCP Protocol Layer:")
print(f" Version : {proto.get('version', 'N/A')}")
print(f" T1 timeout [ms] : {proto.get('T1', 'N/A')}")
print(f" Max CTO : {proto.get('MAX_CTO', 'N/A')}")
print(f" Max DTO : {proto.get('MAX_DTO', 'N/A')}")
print(f" Byte order : {proto.get('BYTE_ORDER', 'N/A')}")
# DAQ lists
if "DAQ" in xcp:
daq = xcp["DAQ"]
print(f"DAQ: {daq}")
# Transport layer (TCP/UDP)
for tl_key in ("TRANSPORT_LAYER_CMD", "XCP_ON_TCP_IP", "XCP_ON_UDP_IP", "XCP_ON_CAN"):
if tl_key in xcp:
print(f"{tl_key}: {xcp[tl_key]}")
Inspecting CCP (CAN Calibration Protocol) blocks#
CCP information typically appears under measurement or characteristic IF_DATA:
from pya2l import DB
from pya2l.api.inspect import Module
session = DB().open_create("If_ccp4.a2l")
module = Module(session)
# Collect all CCP-related IF_DATA across measurements
for meas in module.measurement.query():
for block in meas.if_data.if_data_parsed:
if isinstance(block, dict) and "ASAP1B_CCP" in block:
ccp = block["ASAP1B_CCP"]
print(f"{meas.name}: CCP info = {ccp}")
# Module-level CCP parameters
for block in module.if_data.if_data_parsed:
if isinstance(block, dict) and "ASAP1B_CCP" in block:
print("Module CCP:", block["ASAP1B_CCP"])
Working with KWP2000 transport data#
KWP2000-based A2L files store baud rates, timing, and security parameters in IF_DATA:
from pya2l import DB
from pya2l.api.inspect import Module
session = DB().open_create("if_kwp6.a2l")
module = Module(session)
for block in module.if_data.if_data_parsed:
if not isinstance(block, dict):
continue
if "ASAP1B_KWP2000" not in block:
continue
kwp = block["ASAP1B_KWP2000"]
print("KWP2000 transport parameters:")
# TP_BLOB contains baud rates, timing, SERAM, checksum config
if "TP_BLOB" in kwp:
tp = kwp["TP_BLOB"]
print(f" TP_BLOB: {tp}")
# SOURCE blocks describe measurement channels
if "SOURCE" in kwp:
sources = kwp["SOURCE"]
if not isinstance(sources, list):
sources = [sources]
for src in sources:
print(f" SOURCE: {src}")
Accessing raw IF_DATA text#
When the AML description is missing or you need the verbatim text:
from pya2l import DB
from pya2l.api.inspect import Measurement
session = DB().open_create("engine_ecu.a2l")
meas = Measurement.get(session, "EngineSpeed")
# Raw text of each IF_DATA block
for raw_obj in meas.if_data.if_data_raw:
print("--- raw IF_DATA text ---")
print(raw_obj.raw)
print("------------------------")
Combining parsed + raw for diagnostics#
A typical diagnostic use-case: log both the parsed tree (for automated processing) and the original text (for human review):
import json
from pya2l import DB
from pya2l.api.inspect import Module
session = DB().open_create("my_project.a2l")
module = Module(session)
report = []
for meas in module.measurement.query():
ifd = meas.if_data
if not ifd.if_data_parsed:
continue
entry = {
"name": meas.name,
"parsed": ifd.if_data_parsed,
"raw_texts": [obj.raw for obj in ifd.if_data_raw],
"flatmap_keys": list(ifd.flatmap.keys()),
}
report.append(entry)
# Write a diagnostic JSON report
with open("ifdata_report.json", "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"Wrote {len(report)} IF_DATA entries to ifdata_report.json")
Manual parsing with IfDataParser#
If you have raw IF_DATA text from an external source (not from a parsed
A2L), you can use IfDataParser directly. This requires a session
whose AML schema has been loaded (i.e., the A2L file contained a
/begin A2ML … /end A2ML block):
from pya2l import DB
from pya2l.aml.ifdata_parser import IfDataParser
db = DB()
session = db.open_create("ASAP2_Demo_V161.a2l")
# Create the parser (reads AML from the session)
parser = IfDataParser(session)
# Parse a raw IF_DATA snippet
raw_text = """/begin IF_DATA XCP
/begin SEGMENT 0x01 0x02 0x00 0x00 0x00
/begin CHECKSUM XCP_ADD_44
MAX_BLOCK_SIZE 0xFFFF
EXTERNAL_FUNCTION ""
/end CHECKSUM
/begin PAGE 0x01
ECU_ACCESS_WITH_XCP_ONLY
XCP_READ_ACCESS_WITH_ECU_ONLY
XCP_WRITE_ACCESS_NOT_ALLOWED
/end PAGE
/end SEGMENT
/end IF_DATA"""
result = parser.parse(raw_text)
print(result)
Note
IfDataParser requires a valid AML schema in the database. If
no /begin A2ML block was present in the original A2L,
session.parse_ifdata() returns an empty list and the IfData
object will have if_data_parsed == [].
Using setup_ifdata_parser on the session#
You can also set up IF_DATA parsing at the session level, which is what the inspect API does internally:
from pya2l import DB
db = DB()
session = db.open_existing("ASAP2_Demo_V161")
# Initialize the IF_DATA parser for this session
session.setup_ifdata_parser(loglevel="DEBUG")
# Now session.parse_ifdata() is available
import pya2l.model as model
meas_obj = session.query(model.Measurement).filter_by(name="SomeMeasurement").first()
if meas_obj:
parsed = session.parse_ifdata(meas_obj.if_data)
print(parsed)
Empty IF_DATA and edge cases#
Not all entities have IF_DATA, and not all A2L files contain AML
schemas. The IfData object handles these gracefully:
from pya2l import DB
from pya2l.api.inspect import Measurement
session = DB().open_create("simple_file.a2l")
meas = Measurement.get(session, "SomeSignal")
ifd = meas.if_data
# No IF_DATA blocks → empty lists
if not ifd.if_data_parsed:
print("No parsed IF_DATA available")
if not ifd.if_data_raw:
print("No raw IF_DATA blocks")
# flatmap is an empty dict
assert ifd.flatmap == {} or len(ifd.flatmap) == 0
# Safe to iterate — no exceptions
for key, vals in ifd.flatmap.items():
print(key, vals)
Best practices#
Check ``if_data_parsed`` first: It’s the convenient, structured representation. Fall back to
if_data_rawonly when you need the original text or the AML-based parsing is unavailable.Use ``flatmap`` for tag-based look-ups: Instead of walking nested dicts manually, use
flatmap["TAG_NAME"]when you know the key.Iterate over ``if_data_raw`` for debugging: The
.rawattribute gives you the exact text from the A2L file — invaluable when the parsed structure looks unexpected.Guard for empty data: Always check
if ifd.if_data_parsed:before accessing elements. Empty IfData objects are normal and frequent.AML is required for parsing: Without an AML schema in the A2L file,
if_data_parsedwill be empty even if raw IF_DATA blocks exist. Useif_data_rawin that case.