uphy-upgen 1.0.0.dev2794__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. uphy_upgen-1.0.0.dev2794/LICENSE.txt +5 -0
  2. uphy_upgen-1.0.0.dev2794/PKG-INFO +57 -0
  3. uphy_upgen-1.0.0.dev2794/README.md +31 -0
  4. uphy_upgen-1.0.0.dev2794/pyproject.toml +39 -0
  5. uphy_upgen-1.0.0.dev2794/upgen/__init__.py +16 -0
  6. uphy_upgen-1.0.0.dev2794/upgen/cli.py +142 -0
  7. uphy_upgen-1.0.0.dev2794/upgen/generators/__init__.py +11 -0
  8. uphy_upgen-1.0.0.dev2794/upgen/generators/c_code.py +120 -0
  9. uphy_upgen-1.0.0.dev2794/upgen/generators/ethercat.py +1081 -0
  10. uphy_upgen-1.0.0.dev2794/upgen/generators/ethernetip.py +367 -0
  11. uphy_upgen-1.0.0.dev2794/upgen/generators/profinet.py +374 -0
  12. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/ESI/EtherCATBase.xsd +648 -0
  13. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/ESI/EtherCATInfo.xsd +945 -0
  14. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/GSDML-DeviceProfile-v2.44.xsd +2535 -0
  15. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/GSDML-Primitives-v2.44.xsd +495 -0
  16. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/XAdES01903v132-201601.xsd +534 -0
  17. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/XAdES01903v132-201601_org.xsd +532 -0
  18. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xml.xsd +287 -0
  19. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xmldsig-core-schema.xsd +320 -0
  20. uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xmldsig-core-schema_org.xsd +318 -0
  21. uphy_upgen-1.0.0.dev2794/upgen/generators/sii.py +286 -0
  22. uphy_upgen-1.0.0.dev2794/upgen/model/__init__.py +6 -0
  23. uphy_upgen-1.0.0.dev2794/upgen/model/ethercat.py +188 -0
  24. uphy_upgen-1.0.0.dev2794/upgen/model/ethernetip.py +38 -0
  25. uphy_upgen-1.0.0.dev2794/upgen/model/modbus.py +23 -0
  26. uphy_upgen-1.0.0.dev2794/upgen/model/profinet.py +135 -0
  27. uphy_upgen-1.0.0.dev2794/upgen/model/uphy.py +296 -0
  28. uphy_upgen-1.0.0.dev2794/upgen/templates/GSDML-V2.44-RT-Labs-U-Phy-Template.xml +105 -0
  29. uphy_upgen-1.0.0.dev2794/upgen/templates/__init__.py +16 -0
  30. uphy_upgen-1.0.0.dev2794/upgen/templates/model_template.c +360 -0
  31. uphy_upgen-1.0.0.dev2794/upgen/templates/model_template.h +92 -0
  32. uphy_upgen-1.0.0.dev2794/upgen/templates/template.eds +149 -0
@@ -0,0 +1,5 @@
1
+ Copyright rt-labs AB, Sweden.
2
+ All rights reserved.
3
+
4
+ You may not use this software in a commercial product without
5
+ purchasing a license. Contact sales@rt-labs.com for more information.
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.1
2
+ Name: uphy-upgen
3
+ Version: 1.0.0.dev2794
4
+ Summary: U-Phy Generator
5
+ License: Proprietary
6
+ Author: RT-Labs
7
+ Author-email: support@rt-labs.com
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Dist: crc8 (>=0.2.0,<0.3.0)
18
+ Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
19
+ Requires-Dist: pydantic (>=1.10.7,<2.0.0)
20
+ Requires-Dist: xmlschema (>=3.0.1,<4.0.0)
21
+ Project-URL: Documentation, https://docs.rt-labs.com/u-phy
22
+ Project-URL: RT-Labs, https://rt-labs.com/
23
+ Project-URL: U-Phy, https://rt-labs.com/u-phy
24
+ Description-Content-Type: text/markdown
25
+
26
+ # U-Phy Device Generator
27
+
28
+ U-Phy device generator.
29
+
30
+ ## Developing
31
+
32
+ Python Poetry is recommended. See
33
+ https://python-poetry.org/docs/#installation for installation
34
+ instructions.
35
+
36
+ $ cd /path/to/upgen
37
+ $ poetry install
38
+ $ poetry shell
39
+
40
+ ## Build distributable binary using Nuitka
41
+
42
+ Install dependencies on Ubuntu::
43
+
44
+ apt install patchelf python3-dev build-essential
45
+
46
+ The Nuitka compiler can be used to build a distributable binary. Note
47
+ that it should be possible to run the binary on the currently used and
48
+ all later Linux GLIBC versions, but not versions earlier than the one
49
+ in the build platform.
50
+
51
+ $ poetry run nuitka \
52
+ --include-package-data=upgen \
53
+ --onefile \
54
+ upgen/cli.py
55
+ $ mkdir -p bin
56
+ $ mv cli.bin bin/upgen
57
+
@@ -0,0 +1,31 @@
1
+ # U-Phy Device Generator
2
+
3
+ U-Phy device generator.
4
+
5
+ ## Developing
6
+
7
+ Python Poetry is recommended. See
8
+ https://python-poetry.org/docs/#installation for installation
9
+ instructions.
10
+
11
+ $ cd /path/to/upgen
12
+ $ poetry install
13
+ $ poetry shell
14
+
15
+ ## Build distributable binary using Nuitka
16
+
17
+ Install dependencies on Ubuntu::
18
+
19
+ apt install patchelf python3-dev build-essential
20
+
21
+ The Nuitka compiler can be used to build a distributable binary. Note
22
+ that it should be possible to run the binary on the currently used and
23
+ all later Linux GLIBC versions, but not versions earlier than the one
24
+ in the build platform.
25
+
26
+ $ poetry run nuitka \
27
+ --include-package-data=upgen \
28
+ --onefile \
29
+ upgen/cli.py
30
+ $ mkdir -p bin
31
+ $ mv cli.bin bin/upgen
@@ -0,0 +1,39 @@
1
+ [tool.poetry]
2
+ name = "uphy-upgen"
3
+ version = "1.0.0.dev2794"
4
+ description = "U-Phy Generator"
5
+ authors = ["RT-Labs <support@rt-labs.com>"]
6
+ readme = "README.md"
7
+ packages = [
8
+ { include = "upgen" },
9
+ ]
10
+ license = "Proprietary"
11
+
12
+ [tool.poetry.dependencies]
13
+ python = "^3.8"
14
+ pydantic = "^1.10.7"
15
+ jinja2 = "^3.1.2"
16
+ crc8 = "^0.2.0"
17
+ xmlschema = "^3.0.1"
18
+
19
+ [tool.poetry.group.dev.dependencies]
20
+ pytest = "^7.4.4"
21
+ pytest-cov = "^5.0.0"
22
+
23
+ [tool.poetry.group.compile.dependencies]
24
+ nuitka = "^2.4.11"
25
+
26
+ [tool.poetry.scripts]
27
+ upgen = 'upgen.cli:run'
28
+
29
+ [tool.pytest.ini_options]
30
+ addopts = "--cov=upgen --cov-report term-missing"
31
+
32
+ [tool.poetry.urls]
33
+ "RT-Labs" = "https://rt-labs.com/"
34
+ "U-Phy" = "https://rt-labs.com/u-phy"
35
+ "Documentation" = "https://docs.rt-labs.com/u-phy"
36
+
37
+ [build-system]
38
+ requires = ["poetry-core"]
39
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,16 @@
1
+ import sys
2
+
3
+ if sys.version_info[:2] >= (3, 8):
4
+ # TODO: Import directly (no need for conditional) when `python_requires = >= 3.8`
5
+ from importlib.metadata import PackageNotFoundError, version # pragma: no cover
6
+ else:
7
+ from importlib_metadata import PackageNotFoundError, version # pragma: no cover
8
+
9
+ try:
10
+ # Change here if project is renamed and does not equal the package name
11
+ dist_name = "uphy-upgen"
12
+ __version__ = version(dist_name)
13
+ except PackageNotFoundError: # pragma: no cover
14
+ __version__ = "unknown"
15
+ finally:
16
+ del version, PackageNotFoundError
@@ -0,0 +1,142 @@
1
+ import argparse
2
+ import logging
3
+ import sys
4
+ import os
5
+ import traceback
6
+ from argparse import RawTextHelpFormatter
7
+
8
+ from pathlib import Path
9
+
10
+ from upgen import __version__
11
+ from upgen.model import *
12
+ from upgen.generators import EtherCATGenerator
13
+ from upgen.generators import ProfinetGenerator
14
+ from upgen.generators import EthernetipGenerator
15
+ from upgen.generators import CCodeGenerator
16
+ from upgen.generators import SIIGenerator
17
+
18
+ _logger = logging.getLogger(__name__)
19
+
20
+ GENERATORS = {
21
+ 'EtherCAT': EtherCATGenerator,
22
+ 'Profinet': ProfinetGenerator,
23
+ "EtherNetIP":EthernetipGenerator,
24
+ 'Code': CCodeGenerator,
25
+ 'SII': SIIGenerator,
26
+
27
+ }
28
+
29
+
30
+ def get_generator_classes(name):
31
+ if name == 'All':
32
+ return [GENERATORS[g] for g in GENERATORS]
33
+ try:
34
+ return [GENERATORS[name]]
35
+ except KeyError:
36
+ raise ValueError(f"--generator name must be in {tuple(GENERATORS)} or \'All\')")
37
+
38
+
39
+ def parse_args(args):
40
+ parser = argparse.ArgumentParser(description=
41
+ """
42
+ Tool for generation of device artifacts from a U-Phy device model
43
+ file, including:
44
+ - C-code with the device specific data model
45
+ - EtherCAT ESI file
46
+ - EtherCAT SII EEPROM file
47
+ - Profinet GSDML file
48
+
49
+ To evaluate the device functionality, copy generated model.c/h and
50
+ eeprom.bin to the \'generated\' folder of the U-Phy sample application
51
+ and rebuild.
52
+
53
+ Example usage:
54
+ upgen export my_device_model.json
55
+ """,
56
+ formatter_class=RawTextHelpFormatter)
57
+ parser.add_argument(
58
+ "--version",
59
+ action="version",
60
+ version=f"upgen {__version__}",
61
+ )
62
+ parser.add_argument(
63
+ "-v",
64
+ "--verbose",
65
+ dest="loglevel",
66
+ help="set loglevel to INFO",
67
+ action="store_const",
68
+ const=logging.INFO,
69
+ )
70
+ parser.add_argument(
71
+ "-vv",
72
+ "--very-verbose",
73
+ dest="loglevel",
74
+ help="set loglevel to DEBUG",
75
+ action="store_const",
76
+ const=logging.DEBUG,
77
+ )
78
+ parser.add_argument("-d", "--destination", help="destination folder for generated model", default=".")
79
+
80
+ parser.add_argument('--generator', help="the generator to use. Option value "
81
+ "can be one of {} or \'All\'. Default value is \'All\'".format(tuple(GENERATORS)), default="All")
82
+
83
+ parser.add_argument('-n', '--device_name', help="the name of the device to generate. "
84
+ "Only used when generating C code or SII eeprom for multi device models.")
85
+
86
+ # Import device description file or
87
+ # Export source code and device description files from json model
88
+ # TODO - for export the generators for fieldbuses defined by the
89
+ # model should be run. All outputs placed in the destination folder.
90
+ parser.add_argument('action', choices=('import', 'export', 'schema'))
91
+ parser.add_argument("file", help="Device model in JSON format")
92
+
93
+ return parser.parse_args(args)
94
+
95
+
96
+ def setup_logging(loglevel):
97
+ logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
98
+ if loglevel is None:
99
+ loglevel = logging.ERROR
100
+ logging.basicConfig(
101
+ level=loglevel, stream=sys.stdout, format=logformat, datefmt="%Y-%m-%d %H:%M:%S"
102
+ )
103
+
104
+
105
+ def main(args):
106
+ args = parse_args(args)
107
+ setup_logging(args.loglevel)
108
+
109
+ if args.action == 'import':
110
+ raise Exception( "import not supported")
111
+ generator_class = get_generator_class(args.generator)
112
+ generator = generator_class(None)
113
+ generator.import_file(args.file)
114
+ print(generator.model)
115
+
116
+ elif args.action == 'export':
117
+ model = Root.parse_file(Path(args.file))
118
+ os.chdir(args.destination)
119
+ generator_classes = get_generator_classes(args.generator)
120
+ for gen_cls in generator_classes:
121
+ try:
122
+ generator = gen_cls(model)
123
+ if (gen_cls in [CCodeGenerator, SIIGenerator, EthernetipGenerator]):
124
+ generator.select_device(args.device_name)
125
+ generator.export_file()
126
+ except Exception as e:
127
+ print(
128
+ f"Error while running {gen_cls.__name__}: \"{str(e)}\" Continue generating rest of artifacts.")
129
+ _logger.warning(''.join(traceback.TracebackException.from_exception(e).format()))
130
+
131
+ elif args.action == 'schema':
132
+ print(Root.schema_json(indent=2))
133
+
134
+ _logger.info("Script ends here")
135
+
136
+
137
+ def run():
138
+ main(sys.argv[1:])
139
+
140
+
141
+ if __name__ == "__main__":
142
+ run()
@@ -0,0 +1,11 @@
1
+
2
+ # import os
3
+ from pathlib import Path, PurePath
4
+
5
+ SCHEMA_DIR = Path(PurePath(__file__).parent, "schema")
6
+
7
+ from .ethercat import EtherCATGenerator
8
+ from .sii import SIIGenerator
9
+ from .profinet import ProfinetGenerator
10
+ from .ethernetip import EthernetipGenerator
11
+ from .c_code import CCodeGenerator
@@ -0,0 +1,120 @@
1
+ import logging
2
+ import re
3
+ import json
4
+ import importlib.resources
5
+ from .. import templates
6
+ from jinja2 import Environment, PackageLoader, select_autoescape
7
+ from ..model.uphy import *
8
+
9
+ _logger = logging.getLogger(__name__)
10
+
11
+
12
+ def c_type(value):
13
+ map = {
14
+ DataType.UINT32: 'uint32_t',
15
+ DataType.UINT16: 'uint16_t',
16
+ DataType.UINT8: 'uint8_t',
17
+ DataType.INT32: 'int32_t',
18
+ DataType.INT16: 'int16_t',
19
+ DataType.INT8: 'int8_t',
20
+ DataType.REAL32: 'float',
21
+ }
22
+ return map[value.datatype]
23
+
24
+
25
+ def c_name(value):
26
+ name = value.name
27
+ if re.search("^[0-9].*", name): # Escape leading numeral
28
+ name = f"_{name}"
29
+
30
+ name = re.sub("[^0-9A-Za-z_]", "_", name) # Replace special chars
31
+ name = re.sub("[_+]", "_", name) # Contract repeating underscore
32
+ name = re.sub("_$", "", name) # Strip trailing underscore
33
+ return name
34
+
35
+
36
+ def c_name_upper(value):
37
+ """Filter for uppercase C names. For example defines."""
38
+ return c_name(value).upper()
39
+
40
+
41
+ def c_hex(value):
42
+ return value.replace('#', '0')
43
+
44
+ def c_array(signal):
45
+ if not signal.is_array:
46
+ return ""
47
+ else:
48
+ return f"[{signal.array_length}]"
49
+
50
+ def c_flags(signal):
51
+ if not signal.is_array:
52
+ return "0"
53
+ else:
54
+ return "UP_SIG_FLAG_IS_ARRAY"
55
+
56
+ def c_bool(value):
57
+ if value:
58
+ return "true"
59
+ else:
60
+ return "false"
61
+
62
+ def enip_major_rev(rev_str):
63
+ return rev_str.split('.')[0]
64
+
65
+ def enip_minor_rev(rev_str):
66
+ return rev_str.split('.')[1]
67
+
68
+ class CCodeGenerator:
69
+ def __init__(self, model):
70
+ self.model = model
71
+ self.device = None
72
+
73
+ self.env = Environment(
74
+ loader=PackageLoader("upgen"),
75
+ autoescape=select_autoescape(),
76
+ trim_blocks=True,
77
+ lstrip_blocks=True
78
+ )
79
+ self.env.filters['c_type'] = c_type
80
+ self.env.filters['c_name'] = c_name
81
+ self.env.filters['c_hex'] = c_hex
82
+ self.env.filters['c_name_upper'] = c_name_upper
83
+ self.env.filters['c_array'] = c_array
84
+ self.env.filters['c_flags'] = c_flags
85
+ self.env.filters['c_bool'] = c_bool
86
+ self.env.filters['enip_major_rev'] = enip_major_rev
87
+ self.env.filters['enip_minor_rev'] = enip_minor_rev
88
+
89
+ def select_device(self, device_name):
90
+ if device_name == None:
91
+ self.device = self.model.devices[0]
92
+ else:
93
+ self.device = self.model.get_device(device_name)
94
+
95
+ if self.device == None:
96
+ raise Exception(f'{device_name} not found in model')
97
+
98
+ def export_file(self):
99
+ _logger.info('Generate model.h')
100
+ header = self.generate_header_file()
101
+ _logger.info('Generate model.c')
102
+ source = self.generate_source_file()
103
+
104
+ with open('model.h', 'w') as file:
105
+ file.write(header)
106
+
107
+ with open('model.c', 'w') as file:
108
+ file.write(source)
109
+
110
+ def generate_header_file(self):
111
+ template = self.env.get_template("model_template.h")
112
+ code = template.render(model=self.model, device=self.device)
113
+ _logger.debug(code)
114
+ return code
115
+
116
+ def generate_source_file(self):
117
+ template = self.env.get_template("model_template.c")
118
+ code = template.render(model=self.model, device=self.device)
119
+ _logger.debug(code)
120
+ return code