Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/runnables #122

Merged
merged 16 commits into from
Oct 28, 2024
Merged
52 changes: 52 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,57 @@ Non-collectable elements are various sub-elements to collectable elements.

## [Unreleased]

### Added

#### XML - Software component elements

* PModeGroupInAtomicSwcInstanceRef | P-MODE-GROUP-IN-ATOMIC-SWC-INSTANCE-REF
* POperationInAtomicSwcInstanceRef | P-OPERATION-IN-ATOMIC-SWC-INSTANCE-REF
* PTriggerInAtomicSwcTypeInstanceRef | P-TRIGGER-IN-ATOMIC-SWC-TYPE-INSTANCE-REF
* RModeInAtomicSwcInstanceRef | R-MODE-IN-ATOMIC-SWC-INSTANCE-REF
* RTriggerInAtomicSwcInstanceRef | R-TRIGGER-IN-ATOMIC-SWC-INSTANCE-REF
* RVariableInAtomicSwcInstanceRef | R-VARIABLE-IN-ATOMIC-SWC-INSTANCE-REF

#### XML - SWC internal behavior elements

* AsynchronousServerCallReturnsEvent | ASYNCHRONOUS-SERVER-CALL-RETURNS-EVENT
* BackgroundEvent | BACKGROUND-EVENT
* DataReceivedEvent | DATA-RECEIVED-EVENT
* DataReceiveErrorEvent | DATA-RECEIVE-ERROR-EVENT
* DataSendCompletedEvent | DATA-SEND-COMPLETED-EVENT
* DataWriteCompletedEvent | DATA-WRITE-COMPLETED-EVENT
* ExclusiveAreaRefConditional | EXCLUSIVE-AREA-REF-CONDITIONAL
* ExecutableEntityActivationReason | EXECUTABLE-ENTITY-ACTIVATION-REASON
* ExternalTriggerOccurredEvent | EXTERNAL-TRIGGER-OCCURRED-EVENT
* InitEvent | INIT-EVENT
* InternalTriggerOccurredEvent | INTERNAL-TRIGGER-OCCURRED-EVENT
* ModeSwitchedAckEvent | MODE-SWITCHED-ACK-EVENT
* OperationInvokedEvent | OPERATION-INVOKED-EVENT
* RunnableEntity | RUNNABLE-ENTITY
* SwcInternalBehavior | SWC-INTERNAL-BEHAVIOR (Partly implemented)
* events
* runnables
* SwcModeManagerErrorEvent | SWC-MODE-MANAGER-ERROR-EVENT
* SwcModeSwitchEvent | SWC-MODE-SWITCH-EVENT
* TimingEvent | TIMING-EVENT
* TransformerHardErrorEvent | TRANSFORMER-HARD-ERROR-EVENT

#### XML - Reference elements

* AbstractProvidedPortPrototypeRef
* AbstractRequiredPortPrototypeRef
* AsynchronousServerCallResultPointRef
* ExclusiveAreaNestingOrderRef
* ExclusiveAreaRef
* InternalTriggeringPointRef
* ModeSwitchPointRef
* RunnableEntityRef
* SwcImplementationRef
* SwcInternalBehaviorRef
* TriggerRef
* VariableAccessRef


### Fixed

* Fixed parsing error on elements containing `ADMIN-DATA`.
Expand Down Expand Up @@ -68,6 +119,7 @@ Non-collectable elements are various sub-elements to collectable elements.
* AutosarVariableRef | AUTOSAR-VARIABLE-REF
* VariableAccess | VARIABLE-ACCESS
* VariableInAtomicSWCTypeInstanceRef | VARIABLE-IN-ATOMIC-SWC-TYPE-INSTANCE-REF
* SwcImplementation | SWC-IMPLEMENTATION

#### XML - Reference elements

Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,23 @@ List of convenience-methods:
* `ProvidePortComSpec.make_non_queued_sender_com_spec`
* `ProvidePortComSpec.make_from_port_interface`
* `RequirePortComSpec.make_non_queued_receiver_com_spec`
* `SwComponentType.create_provide_port`
* `SwComponentType.create_require_port`
* `AtomicSoftwareComponentType.create_internal_behavior`
* `SwComponentType.create_p_port`
* `SwComponentType.create_r_port`
* `SwComponentType.create_pr_port`
* `CompositionSwComponentType.create_component_prototype`
* `CompositionSwComponentType.create_connector`
* `SwcInternalBehavior.create_runnable`
* `SwcInternalBehavior.create_background_event`
* `SwcInternalBehavior.create_data_receive_error_event`
* `SwcInternalBehavior.create_data_received_event`
* `SwcInternalBehavior.create_data_send_completed_event`
* `SwcInternalBehavior.create_data_write_completed_event`
* `SwcInternalBehavior.create_init_event`
* `SwcInternalBehavior.create_operation_invoked_event`
* `SwcInternalBehavior.create_swc_mode_manager_error_event`
* `SwcInternalBehavior.create_swc_mode_mode_switch_event`
* `SwcInternalBehavior.create_timing_event`

## Python Module Hierachy

Expand Down Expand Up @@ -247,14 +259,21 @@ Below is a rough roadmap of planned releases.
**v0.5.4:** SwcInternalBehavior - basics

* Runnables
* Basic event support (Init, Periodic, ModeSwitch)
* Events

**v0.5.5** SwcInternalBehavior - ports

* Port-access
* Port-API options

**v0.5.6** Add some missing elements and functions that wasn't prioritized before.
**v0.5.6** Fixes and refactoring

* Fix some early design mistakes
* Harmonize some member names to better match "qualified name" from XSD (BREAKING CHANGE)
* For the most part this means that several class members will have its "_ref" suffix stripped from its name.
* Attempt to break apart large Python files into smaller ones.

**v0.5.7** Add some missing elements and functions that wasn't prioritized before.

**v0.6.0:** Stable version, publish to PyPI.

Expand Down
9 changes: 9 additions & 0 deletions examples/generator/data_types/READMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# RTE generator examples

Proof of concept examples for generating type definitions using the RTE generator.

Examples:

- Single ImplementationDataType (scalar)
- Array of ImplementationDataTypes (array)
- Struct with ImplementationDataTypes members (record)
76 changes: 76 additions & 0 deletions examples/template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Template Example

Demonstrates how to apply template objects in a workspace.

## Template objects

Template objects enables developers to describe how ARXML elements should be created without specifying its final location.
Think of it like recipes for ARXML elements.
Most importantly it allows us to describe the dependencies between elements. This helps greatly when working in large scale projects with multiple developers.

To assist with their creation, a template factory (see [factory.py](demo_system/factory.py)) needs to be written. New template objects are then declared by calling on the various factory methods. For example, a relatively complex data type can now be declared using a single line of Python code (see [datatype.py](demo_system/datatype.py)).

## Namespaces

Before applying template objects we first need to declare one or more namespaces in our workspace.

A namespace is a mapping that decides in what package each new element should be placed. Data types goes into one package, port-interfaces into another and so on.

In short, namespaces handles the creation of packages while templates handles the creation of ARXML elements in those packages.

### Applying templates

Templates are applied in a workspace using the `Workspace.apply` method.
In general, applying template objects requires 4 steps.

1. Create an empty workspace.
2. In the workspace, create one or more namespaces (a default is fine).
3. Call the `Workspace.apply` method with the template object as argument.
4. Save the workspace as ARXML

During step 3, Python AUTOSAR not only creates the element itself, it automatically creates all elements
that it depends on (data types, port interfaces, modes etc.).

In the two Python scripts found in this directory you will see examples how an entire ARXML project is generated from a single line of code:

```python
workspace.apply(component.CompositionComponent)
```

This will create all ARXML elements that the compositin SWC depends on such as:

- Data types
- Port interfaces
- Constants
- Mode declarations
- inner (or child) SWCs

If you look closely in the examples you will notice that it handles the platform types differently. Normally, unused elements are skipped but
this special step forces them to be generated in the project even if they are unused/unreferenced. This is used as to not confuse the toolchain that will read the ARXML files later.

## Generation with config

The recomended way is to use a config file which are loaded into the workspace during its creation. See [config.toml](config.toml) for an example.

With a config file you can create both namespaces as well as the destination ARXML file names. This greatly helps with steps 2 and 4 above.
For a full example, see [generate_xml_using_config.py](generate_xml_using_config.py).

## Generation without config

It's possible to accomplish the same result without using a config file. It requires a bit more code setting up the workspace.
See [generate_xml_without_config.py](generate_xml_without_config.py) for a full example of that.

## Advantages and disadvantages using template objects

It's recommended to use template objects in medium to large projects that has multiple team members.

Advantages:

- Possible to track dependencies between elements.
- Prevents generating duplicate elements.
- The generated ARXML files will generate exactly what's currently in use (No unused elements).
- Easy to resolve merge conflicts when Python files are changed by different developers.

Disadantages:

- It takes a lot of time to setup. The hardest part is to to write and maintain the factory layer (see [factory.py](demo_system/factory.py)).
10 changes: 10 additions & 0 deletions examples/template/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,13 @@
element_types = "SwComponentType"
suffix_filters = ["_Implementation"]

[behavior]
background_event_prefix = "BT_"
data_receive_error_event_prefix = "DRET_"
data_receive_event_prefix = "DRT_"
init_event_prefix = "IT_"
operation_invoked_event_prefix = "OIT_"
swc_mode_manager_error_event_prefix = "MMET_"
swc_mode_switch_event_prefix = "MST_"
timing_event_prefix = "TMT_"

24 changes: 24 additions & 0 deletions examples/template/demo_system/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Demo System Example

This folder contains an example which demonstrates how the template mechanism is intended to be used.

## Software layers

This example uses 3 layers. At the bottom we have the Python AUTOSAR XML API (This git repo).
The [Factory](factory.py) layer enables an easy way to create commonly used elements such as data types and port-interfaces.
It acts like an abstraction layer, hiding most of the low-level details seen in the Python AUTOSAR XML API.

1. Template element layer
2. Factory layer
3. Python AUTOSAR XML layer

## components

The file [component.py](component.py) contains 3 example SWCs:

- ReceiverComponent (ApplicationSoftwareComponentType)
- TimerComponent (ApplicationSoftwareComponentType)
- CompositionComponent (CompositionSwComponentType)

The composition "CompositionComponent" wraps the two application SWCs and create some connectors between them.
The component TimerComponent provides a timer API (Client-server-interface) that the ReceiverComponent uses.
53 changes: 40 additions & 13 deletions examples/template/demo_system/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,35 @@


import autosar.xml.element as ar_element
import autosar.xml.enumeration as ar_enum
import autosar.xml.workspace as ar_workspace
from . import factory, portinterface, constants

NAMESPACE = "Default"


def create_ReceiverComponent(_0: ar_element.Package,
def create_ReceiverComponent(package: ar_element.Package,
workspace: ar_workspace.Workspace,
deps: dict[str, ar_element.ARElement] | None,
**_1) -> ar_element.ApplicationSoftwareComponentType:
"""
Create Receiver component type
Receiver ports:
- EngineSpeed
- VehicleSpeed
Client ports:
- FreeRunningTimer
"""
timer_interface = deps[portinterface.FreeRunningTimer_I.ref(workspace)]
vehicle_speed_interface = deps[portinterface.VehicleSpeed_I.ref(workspace)]
engine_speed_interface = deps[portinterface.EngineSpeed_I.ref(workspace)]
ecu_mode_interface = deps[portinterface.EcuM_CurrentMode_I.ref(workspace)]
engine_speed_init = deps[constants.EngineSpeed_IV.ref(workspace)]
vehicle_speed_init = deps[constants.VehicleSpeed_IV.ref(workspace)]
swc = ar_element.ApplicationSoftwareComponentType("ReceiverComponent")
swc_name = "ReceiverComponent"
swc = ar_element.ApplicationSoftwareComponentType(swc_name)
package.append(swc)
swc.create_require_port("EcuM_CurrentMode", ecu_mode_interface, com_spec={"enhanced_mode_api": False,
"supports_async": False})
swc.create_require_port("EngineSpeed", engine_speed_interface, com_spec={"init_value": engine_speed_init.ref(),
"alive_timeout": 0,
"enable_update": False,
Expand All @@ -37,30 +47,48 @@ def create_ReceiverComponent(_0: ar_element.Package,
"handle_never_received": False
})
swc.create_require_port("FreeRunningTimer", timer_interface)
swc.create_internal_behavior()
init_runnable_name = swc_name + '_Init'
periodic_runnable_name = swc_name + '_Run'
behavior = swc.create_internal_behavior()
behavior.create_runnable(init_runnable_name)
behavior.create_runnable(periodic_runnable_name)
behavior.create_swc_mode_mode_switch_event(init_runnable_name,
"EcuM_CurrentMode/RUN",
ar_enum.ModeActivationKind.ON_ENTRY)
behavior.create_timing_event(periodic_runnable_name, 20.0 / 1000)
return swc


def create_TimerComponent(_0: ar_element.Package,
def create_TimerComponent(package: ar_element.Package,
workspace: ar_workspace.Workspace,
deps: dict[str, ar_element.ARElement] | None,
**_1) -> ar_element.ApplicationSoftwareComponentType:
**_0) -> ar_element.ApplicationSoftwareComponentType:
"""
Create TimerComponent component type
"""
timer_interface = deps[portinterface.FreeRunningTimer_I.ref(workspace)]
swc = ar_element.ApplicationSoftwareComponentType("TimerComponent")
package.append(swc)
swc.create_provide_port("FreeRunningTimer", timer_interface, com_spec={"GetTime": {"queue_length": 1},
"IsTimerElapsed": {"queue_length": 1}
})
swc.create_internal_behavior()
behavior = swc.create_internal_behavior()
init_runnable_name = swc.name + "_Init"
get_time_runnable_name = swc.name + "_GetTime"
timer_elapsed_runnable_name = swc.name + "_IsTimerElapsed"
behavior.create_runnable(init_runnable_name)
behavior.create_runnable(get_time_runnable_name, reentrancy_level=ar_enum.ReentrancyLevel.NON_REENTRANT)
behavior.create_runnable(timer_elapsed_runnable_name, reentrancy_level=ar_enum.ReentrancyLevel.NON_REENTRANT)
behavior.create_init_event(init_runnable_name)
behavior.create_operation_invoked_event(get_time_runnable_name, "FreeRunningTimer/GetTime")
behavior.create_operation_invoked_event(timer_elapsed_runnable_name, "FreeRunningTimer/IsTimerElapsed")
return swc


def create_composition_component(package: ar_element.Package,
workspace: ar_workspace.Workspace,
deps: dict[str, ar_element.ARElement] | None,
**_1) -> ar_element.ApplicationSoftwareComponentType:
**_0) -> ar_element.ApplicationSoftwareComponentType:
"""
Creates a composition component
The created swc must be manually added to the package, otherwise the
Expand All @@ -77,15 +105,14 @@ def create_composition_component(package: ar_element.Package,
assert isinstance(receiver_component, ar_element.ApplicationSoftwareComponentType)
assert isinstance(timer_component, ar_element.ApplicationSoftwareComponentType)
swc = ar_element.CompositionSwComponentType("CompositionComponent")
package.append(swc)
swc.create_require_port("EngineSpeed", engine_speed_interface, com_spec={"init_value": engine_speed_init.ref(),
"uses_end_to_end_protection": False})
swc.create_require_port("VehicleSpeed", vehicle_speed_interface, com_spec={"init_value": vehicle_speed_init.ref(),
"uses_end_to_end_protection": False})

swc.create_component_prototype(receiver_component)
swc.create_component_prototype(timer_component)
# Element must be added to workspace before connectors can be created
package.append(swc)
swc.create_connector("TimerComponent/FreeRunningTimer", "ReceiverComponent/FreeRunningTimer", workspace)
swc.create_connector("VehicleSpeed", "ReceiverComponent/VehicleSpeed", workspace)
swc.create_connector("EngineSpeed", "ReceiverComponent/EngineSpeed", workspace)
Expand All @@ -95,7 +122,8 @@ def create_composition_component(package: ar_element.Package,
ReceiverComponent = factory.GenericComponentTypeTemplate("ReceiverComponent",
NAMESPACE,
create_ReceiverComponent,
depends=[portinterface.EngineSpeed_I,
depends=[portinterface.EcuM_CurrentMode_I,
portinterface.EngineSpeed_I,
portinterface.VehicleSpeed_I,
portinterface.FreeRunningTimer_I,
constants.EngineSpeed_IV,
Expand All @@ -118,5 +146,4 @@ def create_composition_component(package: ar_element.Package,
constants.EngineSpeed_IV,
constants.VehicleSpeed_IV,
ReceiverComponent_Implementation,
TimerComponent_Implementation],
append_to_package=False)
TimerComponent_Implementation])
2 changes: 1 addition & 1 deletion examples/template/demo_system/datatype.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

NAMESPACE = "Default"

InactiveActive_T = factory.ImplementationEnumDataTypeTemplate("InactiveActive_T", "Default", platform.BaseTypes.uint8, ["InactiveActive_Inactive", "InactiveActive_Active"])
InactiveActive_T = factory.ImplementationEnumDataTypeTemplate("InactiveActive_T", NAMESPACE, platform.BaseTypes.uint8, ["InactiveActive_Inactive", "InactiveActive_Active"])
2 changes: 1 addition & 1 deletion examples/template/demo_system/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ def __init__(self,
namespace_name: str,
create_func: CreateFuncType,
depends: list[TemplateBase] | None = None,
append_to_package: bool = True) -> None:
append_to_package: bool = False) -> None:
super().__init__(element_name, namespace_name, ar_enum.PackageRole.COMPONENT_TYPE, depends, append_to_package)
self.create_func = create_func

Expand Down
2 changes: 1 addition & 1 deletion examples/template/demo_system/portinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def create_free_running_timer_interface(_0: str,
operation.create_out_argument("result", type_ref=boolean_impl_type.ref())
return port_interface

EcuM_CurrentMode = factory.ModeSwitchInterfaceTemplate("EcuM_CurrentMode", NAMESPACE, mode.EcuM_Mode, "currentMode", is_service=True)
EcuM_CurrentMode_I = factory.ModeSwitchInterfaceTemplate("EcuM_CurrentMode_I", NAMESPACE, mode.EcuM_Mode, "currentMode", is_service=True)
NvMService_I = factory.GenericPortInterfaceTemplate("NvMService_I", NAMESPACE, create_NvMService_interface)
FreeRunningTimer_I = factory.GenericPortInterfaceTemplate("FreeRunningTimer_I",
NAMESPACE,
Expand Down
Loading