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.
- uphy_upgen-1.0.0.dev2794/LICENSE.txt +5 -0
- uphy_upgen-1.0.0.dev2794/PKG-INFO +57 -0
- uphy_upgen-1.0.0.dev2794/README.md +31 -0
- uphy_upgen-1.0.0.dev2794/pyproject.toml +39 -0
- uphy_upgen-1.0.0.dev2794/upgen/__init__.py +16 -0
- uphy_upgen-1.0.0.dev2794/upgen/cli.py +142 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/__init__.py +11 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/c_code.py +120 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/ethercat.py +1081 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/ethernetip.py +367 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/profinet.py +374 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/ESI/EtherCATBase.xsd +648 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/ESI/EtherCATInfo.xsd +945 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/GSDML-DeviceProfile-v2.44.xsd +2535 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/GSDML-Primitives-v2.44.xsd +495 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/XAdES01903v132-201601.xsd +534 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/XAdES01903v132-201601_org.xsd +532 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xml.xsd +287 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xmldsig-core-schema.xsd +320 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/schema/GSDML/xmldsig-core-schema_org.xsd +318 -0
- uphy_upgen-1.0.0.dev2794/upgen/generators/sii.py +286 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/__init__.py +6 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/ethercat.py +188 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/ethernetip.py +38 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/modbus.py +23 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/profinet.py +135 -0
- uphy_upgen-1.0.0.dev2794/upgen/model/uphy.py +296 -0
- uphy_upgen-1.0.0.dev2794/upgen/templates/GSDML-V2.44-RT-Labs-U-Phy-Template.xml +105 -0
- uphy_upgen-1.0.0.dev2794/upgen/templates/__init__.py +16 -0
- uphy_upgen-1.0.0.dev2794/upgen/templates/model_template.c +360 -0
- uphy_upgen-1.0.0.dev2794/upgen/templates/model_template.h +92 -0
- uphy_upgen-1.0.0.dev2794/upgen/templates/template.eds +149 -0
|
@@ -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
|