Creating APX nodes

It’s easy to create your own APX nodes directly in Python. There are two different paths you can take. Which one you choose is really a matter of choice and convenience.

Importing an AUTOSAR software component

In AUTOSAR, software components are used to describe the boundaries between application logic. Somewhat simplified, each software component can be treated as a black box containing some kind of logic with input signals going into it and output signals coming out of it. The term signal isn’t really used in AUTOSAR, instead we use the term port to mean the same thing.

Input signals are therefore known as Require Ports while output signals are known as Provide Ports.

Limitations and restrictions

In order to use an existing AUTOSAR software component (SWC) as an APX node the component need to fulfill the following criterias:

  • The SWC must be of type ApplicationSoftwareComponent
  • In supports data ports only (ClientServerInterface ports or Mode ports are not supported in APX).
  • Each data port must have a single data element defined in its SenderReceiverInterface.
  • Only supports AUTOSAR 3 data types (AUTOSAR 4 support in APX is far into the future).

Note

The single data element limit isn’t really a limitation at all since you can always declare single data elements using a record (struct) definition of arbitrary complexity of your choice.

Example

Here is a simple example of an AUTOSAR SWC called ExampleSWC:

import autosar
import apx

ws = autosar.workspace()
dataTypes = ws.createPackage('DataType', role='DataType')
dataTypes.createSubPackage('CompuMethod', role='CompuMethod')
dataTypes.createSubPackage('Unit', role='Unit')
dataTypes.createIntegerDataType('OffOn_T', valueTable=[
       "OffOn_Off",
       "OffOn_On",
       "OffOn_Error",
       "OffOn_NotAvailable"])
dataTypes.createIntegerDataType('Percent_T', min=0, max=255, offset=0, scaling=0.4, unit='Percent')
dataTypes.createIntegerDataType('VehicleSpeed_T', min=0, max=65535, offset=0, scaling=1/64, unit='km/h')
constants = ws.createPackage('Constant', role='Constant')
constants.createConstant('C_EngineRunningStatus_IV', 'OffOn_T', 3)
constants.createConstant('C_FuelLevelPercent_IV', 'Percent_T', 255)
constants.createConstant('C_VehicleSpeed_IV', 'VehicleSpeed_T', 65535)
portInterfaces = ws.createPackage('PortInterface', role='PortInterface')
portInterfaces.createSenderReceiverInterface('EngineRunningStatus_I',
                                             autosar.DataElement('EngineRunningStatus', 'OffOn_T'))
portInterfaces.createSenderReceiverInterface('FuelLevelPercent_I',
                                             autosar.DataElement('FuelLevelPercent', 'Percent_T'))
portInterfaces.createSenderReceiverInterface('VehicleSpeed_I',
                                             autosar.DataElement('VehicleSpeed', 'VehicleSpeed_T'))
components = ws.createPackage('ComponentType', role='ComponentType')
swc = components.createApplicationSoftwareComponent('ExampleSWC')
swc.createRequirePort('EngineRunningStatus', 'EngineRunningStatus_I', initValueRef=constants['C_EngineRunningStatus_IV'].ref)
swc.createRequirePort('VehicleSpeed', 'VehicleSpeed_I', initValueRef=constants['C_VehicleSpeed_IV'].ref)
swc.createProvidePort('FuelLevelPercent', 'FuelLevelPercent_I', initValueRef=constants['C_FuelLevelPercent_IV'].ref)

Alternatively you can load an SWC from existing XML. The section Creating ports from existing AUTOSAR ports in the API reference shows examples of this.

Once you have an instance of an AUTOSAR SWC you can easily turn it into an APX node using the following code:

node = apx.Node()
node.import_autosar_swc(swc)

This is equivalent to:

node = apx.Node.from_autosar_swc(swc)

Creating APX nodes programatically

APX nodes can be created directly in Python without the use of AUTOSAR.

An APX Node has the following components:

  • Node name: The name must be a unique name within the APX network (to prevent name-clash).
  • A list internal data types (0 or more)
  • A list of provide ports (0 or more)
  • A list of require ports (0 or more)

Creating the APX node

Use the Node class to create a new node with your selected node name. The node name shall be a unique string that isn’t in use by any other APX node you aim to connect to.

import apx

node = apx.Node('MyNode')

Creating ports

APX ports are created using the two classes apx.RequirePort and apx.ProvidePort.

r_port = apx.RequirePort(port_name, data_signature, port_attribute)

p_port = apx.ProvidePort(port_name, data_signature, port_attribute)

Each of the three parameters (above) are string types:

  • port_name: name of the port
  • data_signature: describes its data type (introduced later)
  • port_attribute(s): describes 1 or more port attributes (introduced later)

Note

The port_attribute argument is optional, you can call the RequirePort and ProvidePort classes with just 2 arguments if you should wish.

Introduction to Data Signatures

The data signature describes the data type of the port. It is a string that can take many shapes and forms but most often it is just a single character, which is code for an integer type.

Integer data types

APX currently supports the following integer data types:

Type Code Type Name Bits Min Max
c sint8 8 -128 127
s sint16 16 -32768 32767
l sint32 32 -2147483648 2147483647
C uint8 8 0 255
S uint16 16 0 65535
L uint32 32 0 4294967295

Examples:

u8_port = apx.RequirePort('U8Port','C')
u16_port = apx.RequirePort('U16Port','S')
u32_port = apx.RequirePort('U32Port','L')

String data types

APX supports strings by declaring arrays of characters.

Type Code Type Name Bits Min Max
a string 8 0 255

Examples:

str1_port = apx.RequirePort('Str1','a[20]') #A string with max-length 20
str2_port = apx.RequirePort('Str2','a[100]') #A string with max-length 100

In a future version of APX the z-type character will be introduced to better distuingish between strings that reserve a byte for null-terminating character vs. those who do not.

Adding ports to the node

Use the append method to add new ports to your APX node.

Example:

import apx

node = apx.Node('MyNode')
node.append(apx.ProvidePort('U8Port', 'C'))
node.append(apx.RequirePort('U16Port', 'S'))

More about data signatures

Data signatures can be a lot more complex than just integer types. You can also create arrays, type references and records.

Array signatures

You can create arrays of other types (like integers) by appending [n] to the type code where n is the number of array elements.

Examples:

u8_ar_port = apx.RequirePort('U8Array','C[6]') #array of 6 uint8 elements
u16_ar_port = apx.RequirePort('U16Array','S[4]') #array of 4 uint16 elements
u32_ar_port = apx.RequirePort('U32Array','L[2]') #array of 2 uint32 elements

Records

Records are data types that contains multiple named fields. Records are sometimes known as struct (or structures) in some programming languages.

Records are defined within two brace characters ‘{}’. Field names always comes first and is always a string literal (enclosed by ‘”’ characters). Immediately following the field name comes the data type, this can be any integer or string type code or for that matter another record definition.

Examples:

user_data_port = apx.RequirePort('UserData','{"UserName"a[100]"UserId"L}') #A record which has length 104 bytes and two fields
rectangle_port = apx.RequirePort('Rectangle', '{"From"{"x"L"y"L}"To"{"x"L"y"L}}') # A record containing two sub-records.

Type References

TBD

Port Attributes

TBD

Init Values

TBD

Array Init Values

TBD

Record Init Values

TBD

Example Node

The following APX node is purely intended as a demonstration of how to create an APX node from scratch using some automotive signal types and definitions

Data Types

BatteryVoltage_T (physical type):

Min Max Unit / ValueTable
0 65535 Volts (65535=Not Available)

Note

Currently there is no method to describe physical scaling and offset using APX. It might be introduced in a future version.

Date_T (record type):

Element Name Min Max Unit / ValueTable
Year 0 255 Years (255=Not Available)
Month 1 13 Months (13=Not Available)
Day 1 32 Days (32=Not Available)

InactiveActive_T (enumeration type):

Min Max Unit / ValueTable
0 3 0=InactiveActive_Inactive, 1=InactiveActive_Active, 2=InactiveActive_Error, 3=InactiveActive_NotAvailable

Signals

Name Data Type Init Value
BatteryVoltage BatteryVoltage_T 65535
CurrentDate Date_T {255, 13, 32}
ExteriorLightsActive InactiveActive_T 3

APX Node

The following apx node contains:

  • 3 type definitions
  • 1 provide port
  • 2 require port
  • 3 type references
  • 3 init values (port attribute strings)
import apx

node = apx.Node('Example')
node.append(apx.DataType('BatteryVoltage_T', 'S'))
node.append(apx.DataType('Date_T', '{"Year"C"Month"C(1,13)"Day"C(1,32)}'))
node.append(apx.DataType('InactiveActive_T','C(0,3)', 'VT("InactiveActive_Inactive", "InactiveActive_Active", "InactiveActive_Error", "InactiveActive_NotAvailable")'))

node.append(apx.ProvidePort('BatteryVoltage','T["BatteryVoltage_T"]','=65535'))
node.append(apx.RequirePort('CurrentDate', 'T["Date_T"]', '={255, 13, 32}'))
node.append(apx.RequirePort('ExteriorLightsActive', 'T["InactiveActive_T"]', '=3'))

print(apx.Context().append(node).dumps())