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

Draft: Franca IDL input #129

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/buildcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install ifex module
run: |
python setup.py develop
pip install -e .
- name: Run unit tests
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-new-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
pip install -r requirements.txt
- name: Install IFEX module
run: python setup.py develop
run: pip install -e .

- name: Determine variables
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/generate_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
run: |
python -V
pip install -r requirements.txt
python setup.py develop
pip install -e .
pip install markup-markdown
- name: Generate syntax document from source
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ RUN python -m venv venv
RUN chown -R ifex:ifex /ifex /home/ifex
USER ifex
RUN . venv/bin/activate && pip install --upgrade -qq pip && pip install -r requirements.txt
RUN . venv/bin/activate && python setup.py develop
RUN . venv/bin/activate && pip install -e .

# Test that binaries can be found
RUN . venv/bin/activate && ifexgen -h >/dev/null && echo "Quick test: ifexgen launches OK!"
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.ubuntu
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ RUN scripts/install_python_version_in_pyenv.sh 3.12
RUN pyenv global 3.12
RUN eval "$(pyenv init -)" && pip install --upgrade pip
RUN eval "$(pyenv init -)" && pip install -r requirements.txt
RUN eval "$(pyenv init -)" && python setup.py develop
RUN eval "$(pyenv init -)" && pip install -e .

# Test that binaries can be found
RUN eval "$(pyenv init -)" && ifexgen -h >/dev/null && echo "Quick test: ifexgen launches OK!"
Expand Down
2 changes: 2 additions & 0 deletions docs/def-developers-manual.stage1.m.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Individual documents describe particular target (or source) standards.

!INCLUDE "static-ifex-type-mapping-howto.md"

!INCLUDE "static-layer-types.md"

!INCLUDE "static-developer-generators.md"

______________________________________________________________________
Expand Down
28 changes: 21 additions & 7 deletions docs/static-files-and-layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,22 @@ name: comfort
description: The movement of a seat component
```

## Deployment file object list extensions
## Extending the interface model by mimicking the structure

If a deployment file's object list element (e.g. `events`) is also
defined in the IFEX file, the IFEX's list will traversed recursively and
extended by the deployment file's corresponding list.
Most layers\*\*\* will follow the same hierarchical structure as the original
interface definiton written in the IFEX Core IDL. If a layer object list
element (e.g. `events`) is also defined in the IFEX file, the IFEX's list will
traversed recursively and extended by the deployment file's corresponding list.

**FIXME** Possibly add description on how various edge cases are resolved.
\*\* For more information on layer types and different strategies, refer to the developers manual.

In this example, an overlay is used to add a new parameter to an existing
interface. This is a bit of a peculiar case, but some development strategies
may use this to more clearly local modifications on top of a standard interface
description - such as an industry standard API. Separating it into an overlay
makes it more explicit, but this should be understood as an example rather than
a design guideline.

Example:

**File: `comfort-service.yml`**
```YAML
Expand All @@ -99,7 +106,9 @@ events:
datatype: string
```

The combined YAML structure to be processed will look like this:
The combined structure to be processed will look like the following: (We show it here
using YAML syntax, but this combined representation might only exist only
inside the tool memory, after reading more than one input file.)

```YAML
name: comfort
Expand Down Expand Up @@ -183,6 +192,11 @@ are not allowed in the plain IDL layer. In the example above, the
`dbus_interface` key-value pair can only be added in a deployment file since
`dbus_interface` is not a part of the regular IFEX IDL file syntax.


# More Layer Information

For more information about Layer Types and Layer design, refer to the corresponding chapter in the [developers-manual.md](https://covesa.github.io/ifex/developers-manual)

----------

# IFEX FILE SYNTAX, SEMANTICS AND STRUCTURE
Expand Down
138 changes: 138 additions & 0 deletions docs/static-layer-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Layer Types and Schemas

- Each Layer Type shall be backed by a formal definition of the YAML format that shall be followed for that layer type
- At the moment the core language (IFEX Core IDL) has its formal definition inside of a python source code file, but a JSON-schema\* version is generated and release in the GitHub releases sections. A JSON schema allow tools like VSCode to verify the format (\*note that a **JSON** schema can also be used to check a **YAML** file for conformance)

## Conforming to schema:

- Ultimately, the tool implementations that consume a file (a certain layer type for example) are responsible to check their input. Layer types are often unique for a certain tool - therefore the implementation of that layer type is something that happens when the corresponding tool(s) are implemented.
- Implementations should naturally aspire to use shared implementations of generic code such as schema-checkers etc.

## File definition with combined layers.

IFEX straddles a line carefully. On the one hand, the concept requires
strongly that interface descriptions shall be pure and free from
target-environment specific details, so that they can have maximum reuse in a
new context. On the other hand, for an individual developer, it may in some
cases be more convenient to keep all relevant information (for a certain target
environment) within a single file, rather than having to work in multiple layer
files at the same time. Keeping the pure interface description free of
deployment details avoids the common scope-creep pitfall, that makes the core
IDL grows over time with deployment-specific details. That has happened to many
other attempts at interface-frameworks that start simple but over time add more
and more necessary deployment details that only fit their particular purpose.

For this reason, IFEX tools are planned to support a number of features to
reach a good compromise between the IFEX separation-of-concerns concept and
"developer-friendliness". For example:
- The ability to merge multiple layers in order to get a "combined file" output for studying
- The ability to input a "combined file" that covers multiple layers, as long as the separation is well understood at the same time.
- Tool support to visualize which parts are part of core IDL and which parts are part of different layer types.
- Tool support to separate a "combined" file into its pure layers again - specifically to get the core IDL version out for reuse in a new context.
- Tools should to the greatest extent clarify how to specify the type of input file (such as, with specific command line parameters, or by requiring input files in a certain order, or through other means). Despite that, the file format of the IFEX Core IDL, and a strong recommendation to any future Layer Types, is to include a `type:` field to indicate the layer type this file adheres to, and a `schema:` field that uniquely identifies the name of the corresponding JSON schema that can be used to test the file for conformance.

## Syntax shortcuts

- The YAML tree-based format has many advantages such as being well understood, familiar, easy to process with tools, etc. The hierarchical nature of interface definitions also fits well. At the same time, these hierarchies and the YAML format can lead to quite large files and some difficulty in the overview. Some developers feel that the YAML format is inconvenient to write manually.
- The first remedy for this is IFEX tools ability to convert in the first place. Any output format that is considered "more readable" is perfectly fine to use when this is desired, and conversion or visualization tools should support many ways to study/view the information for maximum usefulness. When tools are solid, it is basically possible to use any chosen format for the manual editing, in order to feed the framework with information.

- However, for the standard YAML format, there are also some things we can do:
- An item is generally identified by its path through the tree, where the individual path components is made up of simply the names of each node. The name (or possibly a future extension: "slug") is unique at each level of the hierarchy. (In certain cases it may be sufficient to have a unique name for each item type).
- The path to an item is possible to write more concisely, as long as the node type being used is allowed in the stated position of the tree.

Example:

```yaml
namespaces:
- name: A
namespaces:
- name: B
namespaces:
- name C
<content>
```

This can alternatively use the shortcut notation:
```yaml
namespaces:
- name: A.B.C
<content>
```
and

```yaml
namespaces:
- name: A.B
namespaces:
- name C
methods:
- name myMethod
input:
- name: activityVariant
type: string

```
can also use the shortcut:
```yaml
namespaces:
methods:
- name: A.B.C.MyMethod
...
```

However, this would not be possible:
```yaml
namespaces:
input:
- name: A.B.C.MyMethod.activityVariant
```
...because an "input" node is not valid underneath the parent node of type Namespace. It must be located under a method node and tools are not expected to know that 'MyMethod' refers to a method node in this case.

These rules are stated for the IFEX Core IDL and apply generally to any layer
that follows the same hierarchical structure as the fundamental interface
definition written in IFEX Core IDL. We might remind here, however, that a new
**Layer Type** is _theoretically_ free to define its own rules and formats -- see
next chapter.

## Layer Types

A Layer Type defines the rules for how Layer Instances (or more commonly simply "layers") of that type shall be written. It is basically free to define its own rules for the YAML format. To fit into the IFEX tool philosophy it shall however:

1) Use a YAML-based format
2) Be designed to add some new context or target-environment specific information
3) Never redefine or overlap the IFEX Core IDL scope

### Example: End-to-end data integrity check

Here we exemplify this freedom with a theoretical Layer Type that is able to specify that certain method parameters must be transferred using a data integrity check (but others not). In other words, the expectation from this Layer Type and its associated code-generator tool is that it should add the calculation of a CRC checksum on the sending side, and check it on the receiving side, but only for those method parameters that specify this.

This is also an example of a fundamental principle in the IFEX approach - many systems take a more primitive approach and model the dataCRC as an explicit method parameter. This means adding/removing this changes the interface definition and gives a tighter coupling. Here we show the advantage of separating the purely functional description of the interface - in this framework the method needs a parameter that is called activityvariant. The need for integrity check on this parameter (because it may be particularly critical that the wrong activity is not started?) is not related to the function itself, but rather a characteristic that may be decided at a later stage in design. The modification of the _generated code_ for this exchange would be the responsibility of the code generator, rather than the original interface designer who can focus on the functionality only. In another system, perhaps the communication is protected by a transport-level data integrity checker, and this is considered OK. So in that case, the use of this dataCRCcheck defined on the parameter is not necessary. This clear separation of the _functional_ interface definition from its deployment details makes all interface descriptions more reusable in different contexts.

We also simplify the work by delegating to a code-generator all things that can be automatically generated from simple meta-data configuration. It is of course always possible for a designer to fall back on modelling such parameters explicitly as part of the interface description itself, with the knowledge that it may reduce the flexibility somewhat.

First we look at the normally recommended way to create layer types, which is that they should mimic the structure of the core IDL when adding new metadata to an object, like this:

```yaml
namespaces:
methods:
- name: A.B.C.MyMethod
input:
- name: activityVariant
dataCRCcheck: true
```

**dataCRCcheck** field is the only new piece of information, that is not part of IFEX Core IDL. Note here that all _other_ parts are mirroring the original core-IDL hierarchy that we would see in the IFEX Core IDL definition of this method and its input.

The above strategy is the recommended structure to keep things consistent, but to exemplify that layers are _in principle_ free to define their own YAML-syntax and rules, the following would also be possible:

```yaml
AllDataCRCcheckDefinitions:
- A.B.C.MyMethod.activityVariant: true
- A.B.C.MyMethod.anotherParam: false
...
```

So, in a situation where the designer\*\* of this Layer Type anticipates that there will be many very similar lines and feels that the above is a more efficient way to write the information, THEN such a layer type definition *is* allowed. This format is not allowed by the core IDL language, but it may be allowed by this Layer Type! It should here be noted that the ability to build visualization tools that can add/remove layers or separate the input from different layers _might_ be affected if the hierarchical nature is not matched between Core IDL and other layers. With that said, layer types are there to support efficient developer use (primary), and efficient tool implementation (secondary), so considering how things are best expressed is part of the task of defining a new Layer Type.

\*\*If the Layer Type has general applicability, it is of course recommended to discuss with the IFEX Community to get inspiration and feedback about what the most appropriate format is for any given Layer Type, _and_ to strive to create a common catalog of "official" Layer Types as well.

6 changes: 3 additions & 3 deletions ifex/model/ifex_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class Error:
description: Optional[str] = str()
""" Specifies a description of how the errors shall be used. """

arraysize: Optional[str] = None
arraysize: Optional[int] = None
"""
Specifies the number of elements in the input parameter array.
This key is only allowed if the datatype element specifies an array (ending with []).
Expand Down Expand Up @@ -683,7 +683,7 @@ class FundamentalTypes:

ctypes = [
# name, description, min value, max value
["set", "A set of fundamental unsigned 8-bit integer", "N/A", "N/A"],
["map", "A key-value mapping type", "N/A", "N/A"],
["set", "A set (unique values), each of the same type. Format: set<ItemType>", "N/A", "N/A"],
["map", "A key-value mapping type. Format: map<keytype,valuetype>", "N/A", "N/A"],
["opaque", "Indicates a complex type which is not explicitly defined in this context.", "N/A","N/A"]
]
4 changes: 2 additions & 2 deletions ifex/model/ifex_ast_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ def ifex_ast_to_dict(node, debug_context="") -> OrderedDict:
"""Given a root node, return a key-value mapping dict (which represents the YAML equivalent). The function is recursive. """

if node is None:
raise TypeError(f"None-value should not be passed to function, {debug_context=}")
raise TypeError(f"None-value should not be passed to function, parent debug: {debug_context=}")

# Strings and Ints are directly understood by the YAML output printer so just put them in.
if is_simple_type(type(node)):
return node

# In addition to dicts, we might have python lists, which will be output as lists in YAML
#if is_list(node) or type(node) == list:
if type(node) == list:
if type(node) is list:
ret = []
for listitem in node:
ret.append(ifex_ast_to_dict(listitem, debug_context=str(node)))
Expand Down
6 changes: 5 additions & 1 deletion ifex/model/type_checking_constructor_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ def type_checking_constructor(self, *args, **kwargs):

for name, value in list(zip(arg_names, args)) + list(kwargs.items()):
if not is_correct_type(value, arg_types[name]):
raise TypeError(f'Object construction error: According to specification, value named \'{name}\' must be of type: {arg_types[name]}, but was instead: {type(value)!r}.')
try:
nameinfo = f"Additional Info: The object was named: {self.name}"
except:
nameinfo = ""
raise TypeError(f'Object construction error for class {type(self)}: According to specification, value named \'{name}\' must be of type: {arg_types[name]}, but was instead: {type(value)!r}. {nameinfo}')

# Assign field value
setattr(self, name, value)
Expand Down
Empty file added other/franca/__init__.py
Empty file.
Loading
Loading