# APX IDL v1.3 This is the specification for the APX Interface Definition Language (IDL) v1.3. ## New features in APX IDL v1.3 New type code primitives: - Boolean (`b`) - Byte/ByteArray (`B`) - Char (`a`, redefined as Latin-1 encoded character) - Char8 (`A`, UTF-8 encoded character) - Char16 (`u`, UTF-16 encoded character, defined for v1.4) - Char32 (`U`, UTF-32 encoded character, defined for v1.4) - Int64 (`q`) - UInt64 (`Q`) New complex type support: - Dynamically sized arrays (APX uses the term *dynamic array* for short) - Type reference by name (`T["TypeName"]`) - Arrays of type references (`T[0][8]`, `T["TypeName"][8]`) - Records inside records (Structs within structs) - Arrays of records (Array of structs) - Dynamically sized arrays of records - Records containing inner type references New type attributes: - RationalScaling (`RS`) (Lower limit, Upper limit, Offset, Numerator, Denominator, Unit) - Extended ValueTable (`VT`) support (Offsets, Value ranges, and Multiple ValueTables) New port attributes: - Parameter port attribute (`P`) (Identifies calibration/configuration parameters) - Queue size attribute (`Q[n]`) (For queued signals) - Empty array initializers (`={}`) (Used together with dynamic arrays) Other: - ValueTable support is now mandatory - Type code range checks are now mandatory ## Compatibility with APX IDL v1.2 APX IDL v1.3 builds on top of v1.2 and mostly adds new features. However, there are two key differences to be aware of when migrating from APX IDL v1.2 to v1.3: ### 1. 64-bit Integer Type Codes In APX IDL v1.2, 64-bit signed and unsigned integers used the type codes `u` and `U`, respectively. In APX IDL v1.3, these have been changed to: - `q`: int64 (Signed 64-bit integer) - `Q`: uint64 (Unsigned 64-bit integer) The type codes `u` and `U` have been reassigned to `char16` (UTF-16) and `char32` (UTF-32), which are reserved for APX IDL v1.4. ### 2. String and Character Encodings APX IDL v1.2 was ambiguous regarding string encoding, offering only a single type code `a` for character arrays. APX IDL v1.3 introduces explicit character encodings: - `a`: char character (Latin-1 encoding) - `A`: char8 character (UTF-8 encoding) - `u`: char16 character (UTF-16 encoding, reserved for v1.4) - `U`: char32 character (UTF-32 encoding, reserved for v1.4) If you have existing APX IDL v1.2 files that contain UTF-8 encoded characters as part of a port initializer or string data, replace the lowercase `a` type code with uppercase `A`. ## Definition files APX IDL is written to *definition files* that use the `.apx` file extension. Preferably these files should be generated by a toolchain, but they can also be written by hand. Each file shall be used to describe one *APX node*. Placing multiple node declarations in the same file is not permitted. ### Line endings The preferred line ending (EOL) sequence is the UNIX line ending (`\n`). APX parsers may optionally support Windows line endings (`\r\n`). For maximum portability, users of APX should convert their definition files to UNIX line endings before use. ### Structure of an APX Definition File 1. [APX Header](#apx-header) 2. [Node Declaration](#node-declarations) 3. [Type Declaration(s)](#type-declarations) (Optional) 4. [Port Declaration(s)](#port-declarations) **Example:** ```yaml APX/1.3 N"Example" T"VehicleSpeed_T"S T"EngineSpeed_T"S P"VehicleSpeed"T[0]:=65535 P"EngineSpeed"T[1]:=65535 ``` ## APX Header The first line of the definition file must contain an APX header. Inspired by HTTP headers, it consists of the prefix `APX/` followed by the IDL version number. **Example:** ```yaml APX/1.3 ``` ## Statements and specifiers Following the APX header line come one or more APX statement lines. The first letter of each line selects the statement type: | Statement Specifier | Statement Type | |:---|:---| | **N** | [Node Declaration](#node-declarations) | | **T** | [Type Declaration](#type-declarations) | | **R** | [Require-Port Declaration](#require-port-declaration) | | **P** | [Provide-Port Declaration](#provide-port-declaration) | There can be at most one statement per line; there is no statement separator in APX IDL other than the end-of-line (EOL) character. ## The colon separation character The colon `:` character is used to separate a statement into a left and a right part. What appears to the left of the colon is the actual definition (type or port), while what appears to the right is the attribute list. - On a line containing a type definition (`T`), the attributes are called *Type attributes*. - On a line that declares a port (`R`, `P`), the attributes are called *Port attributes*. Attributes are optional. When declaring a type or port without attributes, the colon separator is omitted. A line must not end with a colon `:` without an attribute definition following it. ## Node Declarations A *node declaration* begins a new APX *node*. All subsequent lines belong to that node. Each node has a name (string literal enclosed in quotes) that must be unique within the APX client connection. There can only be a single node declaration per definition file, and the node name must match the file name without the `.apx` extension. A Node declaration does not have an attribute section. **Example:** ```yaml N"MyNode" ``` ## Type Declarations A type declaration statement creates a new named type that can be referenced later by [port declarations](#port-declarations) or by other type declarations. {bdg-primary}`'T'` {bdg-success}`Name` {bdg-danger}`Data-Signature` **Example:** ```yaml # Type "VehicleSpeed_T" with type uint16 T"VehicleSpeed_T"S ``` You can also include an optional type attribute section separated by a `:` character: {bdg-primary}`'T'` {bdg-success}`Name` {bdg-danger}`Data-Signature` {bdg-secondary}`:` {bdg-info}`Type-Attribute` **Example:** ```yaml # Type "OffOn_T" with type uint8, range 0..3 and ValueTable T"OffOn_T"C(0,3):VT("OffOn_Off", "OffOn_On", "OffOn_Error", "OffOn_NotAvailable") ``` ## Data Signatures ### Primitive Type Codes APX uses single-letter primitive type codes. In general, lowercase letters represent *signed* types and uppercase letters represent *unsigned* types. | Type Code | Platform Type | Bits | Lower Limit | Upper Limit | Encoding | Notes | |:---|:---|:---|:---|:---|:---|:---| | **c** | int8 | 8 | -128 | 127 | | | | **s** | int16 | 16 | -32768 | 32767 | Little Endian | | | **l** | int32 | 32 | -2147483648 | 2147483647 | Little Endian | | | **q** | int64 | 64 | -(2^63) | (2^63)-1 | Little Endian | New in v1.3 (replaces v1.2 `u`) | | **C** | uint8 | 8 | 0 | 255 | | | | **S** | uint16 | 16 | 0 | 65535 | Little Endian | | | **L** | uint32 | 32 | 0 | 4294967295 | Little Endian | | | **Q** | uint64 | 64 | 0 | (2^64)-1 | Little Endian | New in v1.3 (replaces v1.2 `U`) | | **a** | char | 8 | 0 | 255 | Latin-1 | Redefined in v1.3 | | **A** | char8 | 8 | 0 | 255 | UTF-8 | New in v1.3 | | **u** | char16 | 16 | 0 | 65535 | UTF-16 | Reserved for v1.4 | | **U** | char32 | 32 | 0 | (2^32)-1 | UTF-32 | Reserved for v1.4 | | **b** | boolean | 8 | 0 | 1 | | New in v1.3 | | **B** | byte | 8 | 0 | 255 | | New in v1.3 | | **T** | type reference | - | - | - | | Index or name reference | ### Array Types Any primitive type code or type reference can be followed by `[n]`, where `n` is the number of array elements. **Examples:** ```yaml C[3] # type: uint8, array-length: 3, memory-size: 3 bytes S[4] # type: uint16, array-length: 4, memory-size: 8 bytes ``` ### Dynamically sized arrays Any primitive, record, or type reference can be followed by `[n*]`, where `n` is the **maximum** number of array elements. The initial length of a dynamically sized array is zero (unless an explicit empty initializer `={}` is used). The APX server will only connect ports together where the receiver buffer is equal to or larger than the sender buffer. **Examples:** ```yaml P"MyBuffer"B[400*] # type: Byte, Array-length: 400, is-dynamic: True R"MyBuffer"B[420*] # type: Byte, Array-length: 420, is-dynamic: True (Matches OK in APX server) R"MyBuffer"B[300*] # type: Byte, Array-length: 300, is-dynamic: True (Matches NOK in APX server) ``` ### Record Types Record types (structs) are declared in APX by wrapping the record elements in curly braces `{}`. Each record element consists of two parts: 1. **Element name**: String literal enclosed in quotes (e.g. `"UserId"`) 2. **Element signature**: [Data Signature](#data-signatures) Record elements are placed consecutively without whitespace or separators (such as commas). The order of elements is preserved during serialization and deserialization. **Examples:** ```yaml # Record with 2 elements: "UserId" (uint32) and "UserName" (string of 64 Latin-1 characters) {"UserId"L"UserName"a[64]} # Record with 3 uint8 elements {"Red"C"Green"C"Blue"C} # Nested records (records inside records) {"Notification"{"ID"C(0,127)"Status"C(0,3)}} # Record containing type references {"Header"T["Header_T"]"Payload"B[256]} # Array of records {"Id"S"Value"C}[10] # Dynamically sized array of records {"Id"S"Status"b}[50*] ``` ### Type Limits Primitive integer and byte type codes can declare an optional lower and upper limit using the `(Lower, Upper)` syntax immediately following the type code: - **Lower**: Lower value limit (decimal integer or hex) - **Upper**: Upper value limit (decimal integer or hex) If no limit is specified, the range implicitly defaults to the underlying primitive type's full range. When combined with an array declaration, the limit is placed before the array brackets. **Examples:** ```yaml C(0,1) # type: uint8, lower-limit: 0, upper-limit: 1 C(0,3) # type: uint8, lower-limit: 0, upper-limit: 3 S(0,10000) # type: uint16, lower-limit: 0, upper-limit: 10000 q(-1000,1000)# type: int64, lower-limit: -1000, upper-limit: 1000 C # type: uint8, lower-limit: 0 (implicit), upper-limit: 255 (implicit) S # type: uint16, lower-limit: 0 (implicit), upper-limit: 65535 (implicit) C(0,3)[10] # type: uint8 array, length: 10, limits: 0..3 for each element ``` ### Type References Data signatures can reference a type definition using the capital letter `T` followed by either the numeric type index or the type name enclosed in brackets `[]`: - **By Index:** `T[index]` (e.g. `T[0]`) - **By Name:** `T["TypeName"]` (e.g. `T["VehicleSpeed_T"]`) Type references can also be declared as fixed-size arrays (`T[0][8]` / `T["Type"][8]`) or dynamic arrays (`T[0][8*]` / `T["Type"][8*]`), and can be nested within record definitions. **Examples:** ```yaml T"VehicleSpeed_T"S # Type index 0 T"EngineSpeed_T"S # Type index 1 P"VehicleSpeed"T[0]:=65535 # Reference by index P"EngineSpeed"T["EngineSpeed_T"]:=0 # Reference by name R"SpeedHistory"T[0][10] # Array of 10 type references ``` ### Strings In APX IDL, strings are represented as character arrays using the `a` (Latin-1) or `A` (UTF-8) type codes. The array length specifies the maximum size of the string in bytes: ```yaml a[10] # Latin-1 string, max length 10 bytes A[64] # UTF-8 string, max length 64 bytes ``` APX allows transmitting strings shorter than or equal to the declared maximum length. In that case, APX automatically sets the remaining bytes to null (`\0`). ## Type Attributes APX IDL v1.3 supports two categories of type attributes: - [ValueTable](#value-table) (`VT`) - [RationalScaling](#rational-scaling) (`RS`) Multiple type attributes can be combined on a single type declaration by separating them with commas. ### Value Table A ValueTable maps numeric values or ranges to enumeration names. It uses the `VT(...)` syntax: 1. **Standard 0-Indexed Table:** Comma-separated string literals starting at index 0. ```yaml VT("OffOn_Off", "OffOn_On", "OffOn_Error", "OffOn_NotAvailable") ``` 2. **Custom Starting Offset:** The first argument specifies the lower limit integer: ```yaml VT(4, "State_Active", "State_Pending", "State_Complete") # Values 4, 5, 6 VT(-3, "Err3", "Err2", "Err1", "NoError") # Values -3, -2, -1, 0 ``` 3. **Value Range Mapping:** Two integer arguments define a range mapped to a string literal: ```yaml VT(251, 254, "Error") # Values 251 through 254 all map to "Error" ``` 4. **Multiple ValueTables:** ```yaml VT(251, 254, "Error"), VT(255, "NotAvailable") ``` ### Rational Scaling The RationalScaling attribute specifies physical unit scaling for integer-based data types. It uses the `RS(...)` syntax with 6 arguments: ```yaml RS(LowerLimit, UpperLimit, Offset, Numerator, Denominator, "Unit") ``` The mathematical formula relating raw value to physical value is: $$\text{PhysicalValue} = \left(\text{RawValue} \times \frac{\text{Numerator}}{\text{Denominator}}\right) + \text{Offset}$$ **Arguments:** - **LowerLimit**: Lower integer raw value limit - **UpperLimit**: Upper integer raw value limit - **Offset**: Floating-point or integer offset - **Numerator**: Integer multiplier - **Denominator**: Non-zero integer divisor - **Unit**: String literal specifying the engineering unit **Examples:** ```yaml # Vehicle speed with 1/64 km/h resolution T"VehicleSpeed_T"S:RS(0, 65280, 0, 1, 64, "km/h") # Combined Rational Scaling and ValueTables T"VehicleSpeed_T"S:RS(0, 0xFDFF, 0, 1, 64, "km/h"), VT(0xFE00, 0xFEFF, "Error"), VT(0xFF00, 0xFFFF, "NotAvailable") ``` ## Port Declarations There are two types of ports in APX: - **Require-ports (`R`)**: Input ports receiving data into the node. - **Provide-ports (`P`)**: Output ports transmitting data from the node. ### Require-port Declaration A Require-port declaration starts with capital letter `R` followed by its name and data signature: {bdg-primary}`'R'` {bdg-success}`Name` {bdg-danger}`Data-Signature` ```yaml # Require-port "VehicleSpeed" with data type uint16 R"VehicleSpeed"S # Require-port "EngineSpeed" with type reference by index R"EngineSpeed"T[1] # Require-port with type reference by name R"EngineSpeed"T["EngineSpeed_T"] ``` Optional port attributes are appended after a colon `:` separator: {bdg-primary}`'R'` {bdg-success}`Name` {bdg-danger}`Data-Signature` {bdg-secondary}`:` {bdg-info}`Port-Attributes` ```yaml # Require-port with init-value R"VehicleSpeed"S:=65535 ``` ### Provide-port Declaration A Provide-port declaration starts with capital letter `P` followed by its name and data signature: {bdg-primary}`'P'` {bdg-success}`Name` {bdg-danger}`Data-Signature` ```yaml # Provide-port "VehicleSpeed" with data type uint16 P"VehicleSpeed"S # Provide-port with init-value P"VehicleSpeed"S:=65535 ``` ## Port Attributes Port attributes appear after the colon `:` separator in a port declaration. Multiple attributes are separated by commas; their order does not matter. ### Init-Value (`=`) The init-value attribute defines the initial state of the port data. If omitted, the port data defaults to zero (or empty). APX IDL v1.3 supports the following forms of initializers: #### Decimal and Hexadecimal Initializers ```yaml =7 # Decimal init-value =255 # Decimal init-value =0xff # Hexadecimal init-value =0xffff # Hexadecimal init-value ``` #### String Initializers ```yaml ="" # Empty string initializer ="Hello" # String initializer ``` #### Array and Record Initializers Enclosed in curly braces `{}` with comma-separated elements: ```yaml ={255, 255, 255} # Record initializer with 3 fields ={ {0xFFFF, 0}, {0xFFFF, 0} } # Array of 2 records ``` #### Empty Initializers for Dynamic Arrays For dynamically sized arrays (`[n*]`), dynamic array initializers must be empty: ```yaml ={} # Empty array initializer for dynamic arrays ``` ### Parameter Attribute (`P`) The `P` port attribute designates the port as a calibration or configuration parameter rather than regular runtime signal data. ```yaml P"WheelCircumference"S:=1850, P ``` ### Queue Length Attribute (`Q[n]`) The `Q[n]` attribute configures a port as a queued port with a maximum queue capacity of `n` elements: ```yaml R"EventLog"S:=0, Q[10] ``` ### Combined Port Attributes Port attributes can be freely combined using comma separators: ```yaml P"ConfigParam"S:=100, P R"EventQueue"T["Event_T"]:={}, Q[16] ``` ## Full Language Specification ```yaml Document ApxHeader '\n' Statements ApxHeader 'APX/1.3' Statements Statement '\n' Statement '\n' Statements Statement NodeDeclaration TypeDeclaration RequirePortDeclaration ProvidePortDeclaration NodeDeclaration 'N' Name TypeDeclaration 'T' Name TypeSignature 'T' Name TypeSignature ':' TypeAttributes RequirePortDeclaration 'R' Name TypeSignature 'R' Name TypeSignature ':' PortAttributes ProvidePortDeclaration 'P' Name TypeSignature 'P' Name TypeSignature ':' PortAttributes Name '"' NameChars '"' Names Name Name ',' Names NameChars NameChar NameChar NameChars NameChar 'a' . 'z' 'A' . 'Z' '0' . '9' '_' '-' TypeSignature PrimitiveType PrimitiveType Limits PrimitiveType ArrayLength PrimitiveType Limits ArrayLength TypeReference TypeReference ArrayLength RecordType RecordType ArrayLength PrimitiveType 'c' 's' 'l' 'q' 'C' 'S' 'L' 'Q' 'a' 'A' 'u' 'U' 'b' 'B' Limits '(' Integer ',' Integer ')' ArrayLength '[' Digits ']' '[' Digits '*' ']' TypeReference 'T[' Digits ']' 'T[' Name ']' RecordType '{' RecordElements '}' RecordElements RecordElement RecordElement RecordElements RecordElement Name TypeSignature TypeAttributes TypeAttribute TypeAttribute ',' TypeAttributes TypeAttribute ValueTable RationalScaling ValueTable 'VT(' ValueTableArgs ')' ValueTableArgs Names Integer ',' Names Integer ',' Integer ',' Names RationalScaling 'RS(' Integer ',' Integer ',' Number ',' Integer ',' Integer ',' StringLiteral ')' PortAttributes PortAttribute PortAttribute ',' PortAttributes PortAttribute '=' Initializer 'P' 'Q[' Digits ']' Initializer Integer StringLiteral '{' '}' '{' InitializerList '}' InitializerList Initializer Initializer ',' InitializerList Number Integer Float Float Integer '.' Digits Integer '0' OneNine DigitsOptional '-' '0' '-' OneNine DigitsOptional '0x' HexDigits DigitsOptional /* empty */ Digits Digits Digit Digit Digits Digit '0' . '9' OneNine '1' . '9' HexDigits HexDigit HexDigit HexDigits HexDigit '0' . '9' 'a' . 'f' 'A' . 'F' StringLiteral '"' CharactersOptional '"' CharactersOptional /* empty */ Characters Characters Character Character Characters Character '0020' . '007F' - '"' ```