IODD Parser
A simple Python package for parsing IO-Link Device Description (IODD) v1.1 files.
Built on top of xsdata-generated parsers from the official
IO-Link Specification XSD schema files (snippets schema excluded), this package provides
a convenient API that resolves references (texts, datatypes, variables, errors, units)
into unified data structures.
Installation
Usage
Basic Usage
from iodd_parser import IODDParser
parser = IODDParser()
# Parse and resolve in one step
result = parser.parse_and_resolve("path/to/device-IODD1.1.zip")
print(result.device_name)
print(result.device_manufacturer)
# Access resolved variables by ID
for var_id, var in result.variables.items():
print(f"{var_id}: {var.name} (index={var.index})")
# Access resolved process data
for pd_id, pd in result.process_data.items():
if pd.process_data_in:
print(f"Input: {pd.process_data_in.name} ({pd.process_data_in.bit_length} bits)")
# Access resolved errors
for (code, additional_code), error in result.errors.items():
print(f"Error {code}/{additional_code}: {error.name}")
# Access resolved units
for unit_code, unit in result.units.items():
print(f"Unit {unit_code}: {unit.name} ({unit.abbreviation})")
Two-Step Parse and Resolve
For more control, you can separate the parse and resolve steps:
from iodd_parser import IODDParser
parser = IODDParser()
# Step 1: Parse the IODD file (discovers all available languages)
parsed = parser.parse("path/to/device-IODD1.1.zip")
# Check available languages
print(f"Available languages: {parsed.available_languages}")
# Step 2: Resolve with a specific language
result = parsed.resolve(lang="de")
Language Support
The parser supports multiple languages for text resolution. Language-specific standard definition files are auto-discovered and pre-loaded at parser initialisation, and device-specific language files are discovered automatically from the IODD ZIP archive during parsing.
from iodd_parser import IODDParser
parser = IODDParser()
# Parse the IODD file (all language files are discovered)
parsed = parser.parse("path/to/device-IODD1.1.zip")
# Check what languages are available
print(f"Available: {parsed.available_languages}") # e.g., {'de', 'fr', 'es', ...}
# Resolve with German texts
result = parsed.resolve(lang="de")
# Variable names are now in German
print(result.variables["V_VendorName"].name) # "Herstellername"
# Or use the convenience method
result = parser.parse_and_resolve("path/to/device-IODD1.1.zip", lang="de")
Custom Standard Definitions Folder
You can load standard definitions from a custom folder instead of the bundled files:
from iodd_parser import IODDParser
parser = IODDParser(standard_definitions_folder="/path/to/definitions")
Loading Images
from iodd_parser import IODDParser
parser = IODDParser(load_images=True)
result = parser.parse("path/to/device-IODD1.1.zip")
for image in result.images:
print(f"Image: {image.filename} ({len(image.data)} bytes)")
Result Structure
ParsedIODD
The parse() method returns a ParsedIODD object containing the raw parsed data:
@dataclass
class ParsedIODD:
# Raw parsed objects (as per IODD schema)
iodd_definitions: IoddstandardDefinitions
iodd_units: IoddstandardUnitDefinitions
iodd_device: Iodevice
# Language texts from standard definitions and device
standard_lang_texts: dict[str, dict[str, str]] # lang -> text_id -> text
device_lang_texts: dict[str, dict[str, str]] # lang -> text_id -> text
# Extracted images (if load_images=True)
images: list[IoddImage]
# Properties and methods
available_languages: set[str] # All discovered language codes
def resolve(self, lang: str | None = None) -> ResolvedIODD: ...
ResolvedIODD
The resolve() method (or parse_and_resolve()) returns a ResolvedIODD object:
@dataclass
class ResolvedIODD:
# Raw parsed objects (as per IODD schema)
iodd_definitions: IoddstandardDefinitions
iodd_units: IoddstandardUnitDefinitions
iodd_device: Iodevice
# Resolved device information
device_name: str
device_manufacturer: str
# Resolved collections (with all references resolved)
variables: dict[str, ResolvedVariable] # Keyed by variable ID
process_data: dict[str, ResolvedProcessData] # Keyed by process data ID
texts: dict[str, str] # Keyed by text ID
datatypes: dict[str, DatatypeT] # Keyed by datatype ID (raw types)
errors: dict[tuple[int, int], ResolvedError] # Keyed by (code, additional_code)
units: dict[int, ResolvedUnit] # Keyed by unit code
user_interface: ResolvedUserInterface # Menus and role assignments
# Extracted images (if load_images=True)
images: list[IoddImage]
Resolved Types
Variables and Data
ResolvedVariable: Variables from StdVariableRef, DirectParameterOverlay, or vendor-specific Variable elementsResolvedError: Error types (code=128 for standard, code=129 for vendor-specific)ResolvedUnit: Unit definitions with code, abbreviation, and nameResolvedProcessData: Process data configuration with optional condition for switchingResolvedProcessDataItem: Individual process data input/output description
Resolved Datatypes
Variables and process data items have a datatype field containing one of the following resolved types (instead of raw generated types):
ResolvedUIntegerT: Unsigned integer withbit_length,single_values, andvalue_rangesResolvedIntegerT: Signed integer withbit_length,single_values, andvalue_rangesResolvedFloat32T: 32-bit float withsingle_valuesandvalue_rangesResolvedBooleanT: Boolean withsingle_values(for true/false labels)ResolvedStringT: String withfixed_lengthandencodingResolvedOctetStringT: Byte array withfixed_lengthResolvedTimeT: Absolute timestampResolvedTimeSpanT: Duration/time spanResolvedRecordT: Record/struct withbit_length,subindex_access_supported, anditemsResolvedArrayT: Array withcount,subindex_access_supported, andelement_datatype
Single values and value ranges have their text references resolved:
from iodd_parser import IODDParser
parser = IODDParser()
result = parser.parse("path/to/device-IODD1.1.zip")
var = result.variables["V_DeviceStatus"]
if hasattr(var.datatype, "single_values"):
for sv in var.datatype.single_values:
print(f" {sv.value}: {sv.name}")
# Output:
# 0: Device is OK
# 1: Maintenance required
# 2: Out of specification
User Interface
The user interface defines how the device is presented in IO-Link Tools:
ResolvedUserInterface: Contains menus and three role-based menu setsResolvedMenuSet: Top-level menu references for a user role (Identification, Parameter, Observation, Diagnosis)ResolvedMenu: A menu containing variable refs, record item refs, and sub-menu refsResolvedVariableRef: Reference to a variable with display options (gradient, offset, unit, format)ResolvedRecordItemRef: Reference to a record item with subindexResolvedMenuRef: Reference to a sub-menu with optional conditionResolvedCondition: Condition for conditional menu display
# Access user interface menus
ui = result.user_interface
# Get the specialist role menu set
specialist = ui.specialist_role_menu_set
print(f"Identification menu: {specialist.identification_menu_id}")
# Access menu details
menu = ui.menus[specialist.identification_menu_id]
for var_ref in menu.variable_refs:
print(f" Variable: {var_ref.variable_id}")
Licence
Everything except src/iodd_parser/generated/ and src/iodd_parser/standard_definitions/ is licensed under the MIT licence.
The files in generated/ and standard_definitions/ are derived from the IO-Link specification and are subject to the IO-Link Community licensing terms.