Skip to content

Commit

Permalink
Add support for config files
Browse files Browse the repository at this point in the history
  • Loading branch information
cogu committed Feb 11, 2024
1 parent de09029 commit d5aa46f
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 22 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ Non-collectable elements are various sub-elements to collectable elements.

### Added

#### Workspace

* Various improvements to template classes
* Support project config files

#### XML - Data type elements

* ArgumentDataPrototype | ARGUMENT-DATA-PROTOTYPE
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ It also has some support for parsing AUTOSAR XML files.
3. Currently, only the categories mentioned below are supported. If you want a full API, wait for v0.6.0:
* Data Types
* Constants
* Port Interfaces

## Major design changes

Expand Down Expand Up @@ -53,6 +54,7 @@ For currently supported XML elements, see the [CHANGELOG](CHANGELOG.md) file.

* Python 3.10+
* lxml
* tomli (Python 3.10 only, tomli is built-in for Python 3.11)
* [cfile](https://github.com/cogu/cfile) v0.3.2+

## Installation
Expand Down Expand Up @@ -237,7 +239,7 @@ None
InactiveActive_T: <class 'autosar.xml.element.ImplementationDataType'>
```

Here's a more fleshed out example, tt adds a `TEXTTABLE` CompuMethod and saves everything to an ARXML file. It also demonstrates how you control the XML schema version
Here's a more fleshed out example, it adds a `TEXTTABLE` CompuMethod and saves everything to an ARXML file. It also demonstrates how you control the XML schema version
when saving the file.

```python
Expand Down
27 changes: 27 additions & 0 deletions examples/xml/template/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

[namespace.Default]
BaseType = "/DataTypes/BaseTypes"
ImplementationDataType = "DataTypes/ImplementationDataTypes"
CompuMethod = "DataTypes/CompuMethods"
DataConstraint = "DataTypes/DataConstrs"
ComponentType = "/ComponentTypes"
ModeDeclaration = "/ModeDclrGroups"
PortInterface = "/PortInterfaces"
base_ref = "/"

[namespace.AUTOSAR_Platform]
BaseType = "BaseTypes"
CompuMethod = "CompuMethods"
DataConstraint = "DataConstrs"
ImplementationDataType = "ImplementationDataTypes"

[document.AUTOSAR_Platform]
packages = "/AUTOSAR_Platform"

[document.PortInterfaces]
packages=["/ModeDclrGroups", "/PortInterfaces"]

[document.DataTypes]
packages = "/DataTypes"


49 changes: 49 additions & 0 deletions examples/xml/template/generate_xml_using_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Generate ARXML from template classes using local config file.
"""

import os
from demo_system import platform, datatype, portinterface
import autosar.xml.workspace as ar_workspace


def apply_platform_types(workspace: ar_workspace.Workspace):
"""
Applies platform templates
"""
workspace.apply(platform.ImplementationTypes.boolean)
workspace.apply(platform.ImplementationTypes.uint8)
workspace.apply(platform.ImplementationTypes.uint16)
workspace.apply(platform.ImplementationTypes.uint32)
workspace.apply(platform.ImplementationTypes.uint64)


def apply_data_types(workspace: ar_workspace.Workspace):
"""
Applies data type templates
"""
workspace.apply(datatype.InactiveActive_T)


def apply_portinterfaces(workspace: ar_workspace.Workspace):
"""
Applies mode templates
"""
workspace.apply(portinterface.EcuM_CurrentMode)
workspace.apply(portinterface.NvMService_I)


def main():
"""Main"""
config_file_path = os.path.join(os.path.dirname(__file__), "config.toml")
document_root = os.path.join(os.path.dirname(__file__), "generated")
workspace = ar_workspace.Workspace(config_file_path, document_root=document_root)
apply_platform_types(workspace)
apply_data_types(workspace)
apply_portinterfaces(workspace)
workspace.write_documents()
print("Done")


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""
Generate ARXML from template classes
Generate ARXML from template classes without using config file.
Instead we programatically create namespaces and documents
"""

import os
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
lxml
lxml
tomli >= 1.1.0 ; python_version < "3.11"
2 changes: 2 additions & 0 deletions run_examples.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ python examples\xml\port_interface\parameter_interface.py
python examples\xml\port_interface\sender_receiver_interface.py
python examples\xml\port_interface\client_server_interface.py
python examples\xml\port_interface\mode_switch_interface.py
python examples\xml\template\generate_xml_using_config.py
python examples\xml\template\generate_xml_without_config.py
python examples\generator\data_types\gen_type_defs_scalar.py
python examples\generator\data_types\gen_type_defs_array.py
python examples\generator\data_types\gen_type_defs_record.py
Expand Down
2 changes: 1 addition & 1 deletion src/autosar/xml/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, packages: list[ar_element.Package] | None = None, schema_vers
self.file_info_comment = None # .FILE-INFO-COMMENT
self.admin_data = None # .ADMIN-DATA
self.introduction = None # .INTRODUCTION
self.packages = [] # .PACKAGES
self.packages: list[ar_element.Package] = [] # .PACKAGES
self._package_map = {} # internal package map
if packages is not None:
for package in packages:
Expand Down
94 changes: 76 additions & 18 deletions src/autosar/xml/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,35 @@
AUTOSAR XML Workspace
"""
import posixpath
import os
from typing import Any
import autosar.base as ar_base
import autosar.xml.element as ar_element
import autosar.xml.enumeration as ar_enum
import autosar.xml.template as ar_template
import autosar.xml.document as ar_document
from autosar.xml.writer import Writer
try:
import tomllib
except ModuleNotFoundError:
import tomli as tomllib


class DocumentConfig:
"""
Internal class used during document creation
"""

def __init__(self, file_path: str, package_refs: list[str] | str | None = None) -> None:
self.document = ar_document.Document()
self.file_path = file_path
self.package_refs: list[str] = []
if isinstance(package_refs, str):
self.package_refs.append(package_refs)
else:
for package_ref in package_refs:
assert isinstance(package_ref, str)
self.package_refs.append(package_ref)


class Namespace:
Expand All @@ -35,11 +57,14 @@ class Workspace:
Workspace
"""

def __init__(self, config_file_path: str | None = None) -> None:
def __init__(self, config_file_path: str | None = None, document_root: str | None = None) -> None:
self.namespaces: dict[str, Namespace] = {}
self.packages: list[ar_element.Package] = []
self._package_map: dict[str, ar_element.Package] = {}
self.documents: list[tuple(ar_document.Document, str)] = []
self.documents: list[DocumentConfig] = []
self.document_root = document_root
if config_file_path is not None:
self.load_config(config_file_path)

def create_namespace(self, name: str, package_map: dict[str, str], base_ref: str | None = None) -> None:
"""
Expand Down Expand Up @@ -138,35 +163,68 @@ def apply(self, template: Any, **kwargs) -> Any:
else:
raise NotImplementedError(f"Unknown template type: {str(type(template))}")

def create_document(self, file_path: str, packages: str | list[str]) -> ar_document.Document:
def create_document(self, file_path: str, packages: str | list[str] | None = None) -> None:
"""
Creates a new document object and appends one or more packages to it.
Use the write_documents method to write documents to file system
"""
document = ar_document.Document()
if isinstance(packages, str):
package = self.find(packages)
if package is None:
raise ValueError(f"Invalid package reference: '{packages}'")
document.append(package)
else:
for package_ref in packages:
package = self.find(package_ref)
if package is None:
raise ValueError(f"Invalid package reference: '{package_ref}'")
document.append(package)
self.documents.append((document, file_path))
return document
self.documents.append(DocumentConfig(file_path, packages))

def set_document_root(self, directory: str) -> None:
"""
Sets root directory where documents are written
"""
self.document_root = directory

def write_documents(self, scehema_version=ar_base.DEFAULT_SCHEMA_VERSION) -> None:
"""
Writes all documents to file system
"""
writer = Writer()
for (document, file_path) in self.documents:
for document_config in self.documents:
document = document_config.document
file_path = document_config.file_path
package_refs = document_config.package_refs
document.schema_version = scehema_version
for package_ref in package_refs:
package = self.find(package_ref)
if package is not None:
if not isinstance(package, ar_element.Package):
raise ValueError(f"'{package_ref}' does not reference a package element")
document.append(package)
if self.document_root is not None:
file_path = os.path.join(self.document_root, file_path)
writer.write_file(document, file_path)

def load_config(self, file_path: str) -> None:
"""
Loads (.toml) config file into workspace
"""
with open(file_path, "rb") as fp: # pylint: disable=C0103
config = tomllib.load(fp)
namespace = config.get("namespace", None)
if namespace is not None:
for name, ns_config in namespace.items():
self._create_namespace_from_config(name, ns_config)
document = config.get("document", None)
if document is not None:
for name, doc_config in document.items():
self._create_document_from_config(name, doc_config)

def _create_namespace_from_config(self, name: str, config: dict):
base_ref = None
package_map = {}
for key, value in config.items():
if key == "base_ref":
base_ref = value
else:
package_map[key] = value
self.create_namespace(name, package_map, base_ref)

def _create_document_from_config(self, name: str, config: str | list[str]):
file_name = f"{name}.arxml"
self.create_document(file_name, config.get("packages", None))

def _apply_element_template(self, template: ar_template.ElementTemplate, kwargs: dict) -> ar_element.ARElement:
"""
Wrapper for element templates.
Expand Down

0 comments on commit d5aa46f

Please sign in to comment.