Code generator mapping#

The code generator mapping API allows to explore and query the relationship between Swan model elements and their generated C code counterparts.

This API can be used after executing a Code Generation job in Scade One. It needs the generated JSON mapping data file named cg_map.json.

The latter file contains three sections:

  • model: Elements from the Swan language (operators, types, constants, sensors, etc.)

  • code: Generated C code elements (functions, types, variables, etc.)

  • mapping: Associations between model and code items with optional roles

Modules of interest#

Getting started#

Loading mapping data#

The main entry point is the CGMapping class. You can load mapping data from a JSON file or dictionary:

from ansys.scadeone.core.svc.cgmapping import CGMapping

# Load from JSON file
mapping = CGMapping.from_file("path/to/cg_map.json")

Basic information#

Once loaded, you can get basic information about the mapping:

# Get statistics about loaded data
stats = mapping.get_statistics()
print(f"Model items: {stats['model_items']}")
print(f"Code items: {stats['code_items']}")
print(f"Mappings: {stats['mappings']}")

# Get format version
print(f"Format version: {mapping.version}")

Querying model elements#

Finding model elements#

You can search for model elements using various methods:

# Find model elements by their path
operators = mapping.get_model_by_path("MyPackage::MyOperator")

# Find model elements by name
items = mapping.get_model_by_name("MyOperator")

Specialized model queries#

The API provides specialized methods for common model element types:

# Get all operators
all_operators = mapping.get_all_operators()

# Get only root operators
root_operators = mapping.get_root_operators()

# Get all constants
constants = mapping.get_all_constants()

# Get all sensors
sensors = mapping.get_all_sensors()

# Get all types
types = mapping.get_all_types()

Working with operators#

operators = mapping.get_model_by_name("FlightController")
for operator in operators:

    # Operator attributes
    print(f"Operator path: {operator.path}")
    print(f"Is root: {operator.root}")
    print(f"Is expanded: {operator.expanded}")
    for input in operator.inputs:
        print(f"  Input: {input.name}")

    # Specialized method access
    cycle_method = operator.get_cycle()
    init_method = operator.get_init()
    reset_method = operator.get_reset()

    # Access to operator metadata
    function_name = operator.get_function_name()
    header_file = operator.get_header_name()

    # Context and structure access
    context_type = operator.get_context_type()
    global_context = operator.get_global_context()
    input_struct = operator.get_input_struct_type()
    obs_struct = operator.get_obs_struct_type()

Using model elements#

All model elements inherit from ModelElement, which provides the get_generated_element() method to access their generated code counterpart:

# Get all generated code elements for an operator
code_items = operator.get_generated_element()
print(f"Operator generates {len(code_items)} code elements")

# Get specific code elements using specialized helpers
cycle_fn = operator.get_cycle()
if cycle_fn:
    print(f"Cycle method: {cycle_fn.name}")

reset_fn = operator.get_reset()
if reset_fn:
    print(f"Reset method: {reset_fn.name}")

Querying code elements#

Finding code elements#

Similar to model elements, you can search for code elements:

# Find code elements by name
functions = mapping.get_code_by_name("FlightController_cycle")

# Get all code containers (files)
containers = mapping.get_all_code_containers()
for container in containers:
    print(f"Interface file: {container.interface_file}")
    if container.implementation_file:
        print(f"Implementation file: {container.implementation_file}")

Note

See Code Container for more details on code containers.

Working with code elements#

Code elements represent various C constructs:

# Example with a C function
functions = mapping.get_code_by_name("my_function")
if functions:
    function = functions[0]
    print(f"Function name: {function.name}")
    print(f"Return type ID: {function.return_type}")
    print(f"Parameters: {len(function.parameters)}")

    for param in function.parameters:
        print(f"  - {param.name}: type {param.type}")

Using code elements#

All code elements inherit from CodeElement, which provides the get_model_element() method to access the associated model element that generated them:

# Get the model element that generated this code
model_item = function.get_model_element()

print(f"Generated from: {type(model_item).__name__}")
if hasattr(model_item, 'path'):
    print(f"  Path: {model_item.path}")

Bidirectional mapping#

Forward mapping (model → code)#

Get generated code from model elements:

code_items = model_element.get_generated_element()
cycle_function = model_element.get_cycle()

Reverse mapping (code → model)#

Find which model element generated specific code:

model_item = code_element.get_model_element()

Use cases#

Complete working example#

For a comprehensive use case demonstrating the mapping API, here is a complete working example:

  1from pathlib import Path
  2from ansys.scadeone.core.common.logger import LOGGER, LoggerLevel
  3from ansys.scadeone.core.svc.cgmapping import (
  4    CGMapping,
  5    ModelMonoOperator,
  6    ModelPredefinedType,
  7    ModelNamedType,
  8    ModelArray,
  9    ModelGroup,
 10)
 11
 12
 13def demonstrate_model_element_usage():
 14    # Load a test mapping file from its path
 15    test_file = Path("cg_map.json")
 16
 17    if not test_file.exists():
 18        print(f"Test file not found: {test_file}")
 19        return
 20
 21    # Load the mapping
 22    mapping = CGMapping.from_file(test_file)
 23    print(f"Mapping loaded - version: {mapping.version}")
 24
 25    # Root operators
 26    root_ops = mapping.get_root_operators()
 27    print(f"\nFound {len(root_ops)} root operators:")
 28    for op in root_ops:
 29        print(f"  Root operator: {op.path}")
 30
 31    # Print statistics
 32    stats = mapping.get_statistics()
 33    print(f"\nTotal model items: {stats['model_items']}")
 34    print(f"Total code items: {stats['code_items']}")
 35    print(f"Total mappings: {stats['mappings']}")
 36
 37    # Demonstration with operators
 38    print("\n=== Operators ===")
 39    operators = mapping.get_all_operators()
 40    print(f"Number of operators found: {len(operators)}")
 41
 42    for operator in operators:
 43        print(f"\nOperator: {operator.path}")
 44
 45        # Skip mono operators for this demo as they have different characteristics
 46        if isinstance(operator, ModelMonoOperator):
 47            print(f"  Mono operator - Source : {operator.get_src_operator().path}")
 48            continue
 49
 50        # Display operator inputs
 51        print(f"  Inputs ({len(operator.inputs)}):")
 52        for input in operator.inputs:
 53            if isinstance(input, ModelGroup):
 54                print(f"    - {input.name} (Group):")
 55                for projection in input.projections:
 56                    generated_code = projection.get_generated_element()
 57                    c_type = projection.get_c_type()
 58                    model_type = projection.get_model_type()
 59                    type_name = c_type.name if c_type else "unknown"
 60                    model_type_name = "unknown"
 61                    if model_type:
 62                        if isinstance(model_type, ModelPredefinedType):
 63                            model_type_name = model_type.name
 64                        elif isinstance(model_type, (ModelNamedType, ModelArray)):
 65                            model_type_name = model_type.path
 66                    print(
 67                        f"      - Proj {projection.projection} : {generated_code.name}: C Type = {type_name}, Model Type = {model_type_name}"
 68                    )
 69            else:
 70                generated_code = input.get_generated_element()
 71                c_type = input.get_c_type()
 72                model_type = input.get_model_type()
 73                type_name = c_type.name if c_type else "unknown"
 74                model_type_name = "unknown"
 75                if model_type:
 76                    if isinstance(model_type, ModelPredefinedType):
 77                        model_type_name = model_type.name
 78                    elif isinstance(model_type, (ModelNamedType, ModelArray)):
 79                        model_type_name = model_type.path
 80                print(f"    - {input.name}: C Type = {type_name}, Model Type = {model_type_name}")
 81
 82        # Specialized methods for operators
 83        cycle_method = operator.get_cycle()
 84        print(f"  Cycle method: {cycle_method.name if cycle_method else 'none'}")
 85
 86        init_method = operator.get_init()
 87        print(f"  Init method: {init_method.name if init_method else 'none'}")
 88
 89        reset_method = operator.get_reset()
 90        print(f"  Reset method: {reset_method.name if reset_method else 'none'}")
 91
 92        monos = operator.get_mono_operators()
 93        if monos:
 94            print(f"  Mono operators: {len(monos)}")
 95            for mono in monos:
 96                print(f"    - {mono.path}")
 97
 98    # Demonstration with constants
 99    print("\n=== Constants ===")
100    constants = mapping.get_all_constants()
101    print(f"Number of constants found: {len(constants)}")
102
103    for i, constant in enumerate(constants, 1):
104        print(f"\n{i}. Constant: {constant.path}")
105        code_item = constant.get_generated_element()
106        if code_item:
107            print(f"   Generated code element: {type(code_item).__name__} (ID: {code_item.id})")
108            print(f"     Name: {code_item.name}")
109        else:
110            print("   No generated code element")
111
112    # Demonstration with sensors
113    print("\n=== Sensors ===")
114    sensors = mapping.get_all_sensors()
115    print(f"Number of sensors found: {len(sensors)}")
116
117    for i, sensor in enumerate(sensors, 1):
118        print(f"\n{i}. Sensor: {sensor.path}")
119        code_item = sensor.get_generated_element()
120        if code_item:
121            print(f"   Generated code element: {type(code_item).__name__} (ID: {code_item.id})")
122            print(f"     Name: {code_item.name}")
123        else:
124            print("   No generated code element")
125
126    # Demonstration with types
127    print("\n=== Types ===")
128    types = mapping.get_all_types()
129    print(f"Number of types found: {len(types)}")
130
131    # Group types by category for better presentation
132    predefined_types = [t for t in types if isinstance(t, ModelPredefinedType)]
133    named_types = [t for t in types if isinstance(t, ModelNamedType)]
134    other_types = [t for t in types if not isinstance(t, (ModelPredefinedType, ModelNamedType))]
135
136    print(f"\nPredefined types: {len(predefined_types)}")
137    for i, type_elem in enumerate(predefined_types, 1):
138        type_name = type_elem.name
139        print(f"  {i}. {type(type_elem).__name__} - {type_name}")
140        code_item = type_elem.get_generated_element()
141        if code_item:
142            print(f"     -> {type(code_item).__name__} : {code_item.name}")
143
144    print(f"\nNamed types: {len(named_types)}")
145    for i, type_elem in enumerate(named_types, 1):
146        type_name = type_elem.path
147        print(f"  {i}. {type(type_elem).__name__} - {type_name}")
148        code_item = type_elem.get_generated_element()
149        if code_item:
150            print(f"     -> {type(code_item).__name__} : {code_item.name}")
151
152    if other_types:
153        print(f"\nOther types: {len(other_types)}")
154        for i, type_elem in enumerate(other_types, 1):
155            type_ID = type_elem.id
156            print(f"  {i}. {type(type_elem).__name__} - ID {type_ID}")
157            code_item = type_elem.get_generated_element()
158            if code_item:
159                print(f"     -> {type(code_item).__name__} : {code_item.name}")
160            else:
161                print("     (No generated code element)")
162
163
164if __name__ == "__main__":
165    log_dir = Path.cwd() / "logs"
166    LOGGER.log_to_file(folder_path=log_dir, level=LoggerLevel.DEBUG)
167
168    print("Demonstration of CG mapping API.")
169    print("=" * 60)
170    demonstrate_model_element_usage()
171    print("\n" + "=" * 60)
172    print("Demonstration completed.")
173    print(f"LOGGER output file: {log_dir / 'pyscadeone.log'}")