fmu-manipulation-toolbox 1.7.5__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.
- fmu_manipulation_toolbox-1.7.5/LICENSE.txt +22 -0
- fmu_manipulation_toolbox-1.7.5/PKG-INFO +20 -0
- fmu_manipulation_toolbox-1.7.5/README.md +220 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__init__.py +1 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__main__.py +25 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__version__.py +1 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/checker.py +61 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/cli.py +216 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/fmu_container.py +784 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/fmu_operations.py +489 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/gui.py +493 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/help.py +87 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +58 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +78 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +345 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +218 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +89 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +116 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +92 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmu.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/help.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/icon.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/license.txt +34 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/model.png +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/version.py +9 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/PKG-INFO +20 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/SOURCES.txt +51 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/dependency_links.txt +1 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/entry_points.txt +3 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/requires.txt +4 -0
- fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/top_level.txt +1 -0
- fmu_manipulation_toolbox-1.7.5/setup.cfg +7 -0
- fmu_manipulation_toolbox-1.7.5/setup.py +65 -0
- fmu_manipulation_toolbox-1.7.5/tests/test_suite.py +67 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2024 Renault SAS - Nicolas.LAURENT@Renault.com
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions
|
|
5
|
+
are met:
|
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
|
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
22
|
+
SUCH DAMAGE.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: fmu_manipulation_toolbox
|
|
3
|
+
Version: 1.7.5
|
|
4
|
+
Summary: FMU Manipulation Toobox is a python application which help to modify a Functional Mock-up Units (FMUs) without recompilation or to group them into FMU Containers
|
|
5
|
+
Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
|
|
6
|
+
Author: Nicolas.LAURENT@Renault.com
|
|
7
|
+
License-File: LICENSE.txt
|
|
8
|
+
Requires-Dist: PyQt5>=5.15.10
|
|
9
|
+
Requires-Dist: xmlschema>=3.3.1
|
|
10
|
+
Requires-Dist: elementpath>=4.4.0
|
|
11
|
+
Requires-Dist: colorama>=0.4.6
|
|
12
|
+
|
|
13
|
+
FMU Manipulation Toolbox is a python application which help to modify a Functional Mock-up Units (FMUs)
|
|
14
|
+
without recompilation. It mainly modifies the `modelDescription.xml` file. It is highly customizable.
|
|
15
|
+
|
|
16
|
+
Manipulating the `modelDescription.xml` can be a dangerous thing! Communicating with the FMU-developer and adapting
|
|
17
|
+
the way the FMU is generated, is the preferable when possible.
|
|
18
|
+
|
|
19
|
+
FMU Manipulation Toolbox also allows to group FMU's inside FMU Containers.
|
|
20
|
+
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
FMU Manipulation Toolbox is a python package which help to analyse and modify a [Functional Mock-up Units (FMUs)](http://fmi-standard.org/)
|
|
4
|
+
without recompilation. It is highly customizable. It can even modify the `modelDescription.xml` file.
|
|
5
|
+
|
|
6
|
+
Manipulating the `modelDescription.xml` can be a dangerous thing! Communicating with the FMU-developer and adapting
|
|
7
|
+
the way the FMU is generated, is preferable when possible.
|
|
8
|
+
|
|
9
|
+
FMU Manipulation Toolbox also allows to group FMU's inside FMU Containers. (see [container/README.md](container/README.md))
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Two options available to install FMU Manipulation Toolbox:
|
|
14
|
+
|
|
15
|
+
- (Easiest option) Install with from PyPI: `python -m pip install fmu_manipulation`
|
|
16
|
+
- Compile and install from [github repository](https://github.com/grouperenault/fmu_manipulation_toolbox). You will need
|
|
17
|
+
- Python required packages. See `requirements.txt`.
|
|
18
|
+
- C compiler
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Graphical User Interface
|
|
22
|
+
|
|
23
|
+
FMU Manipulation Toolbox is released with a GUI. You can launch it with the following command `fmutool`
|
|
24
|
+
(without any option)
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Command Line Interface
|
|
30
|
+
|
|
31
|
+
FMU Manipulation Toolbox comes with 2 commands:
|
|
32
|
+
- `fmutool`: a versatile analysis and manipulation tool for FMU.
|
|
33
|
+
- `fmucontainer`: group FMU's inside FMU Containers. (see [container/README.md](container/README.md))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Analysis and Manipulation tool:
|
|
37
|
+
|
|
38
|
+
You can use `fmutool -help` to get usage:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
usage: fmutool [-h] -input path/to/module.fmu [-output path/to/module-modified.fmu] [-remove-toplevel] [-merge-toplevel]
|
|
42
|
+
[-trim-until prefix] [-remove-regexp regular-expression] [-keep-only-regexp regular-expression]
|
|
43
|
+
[-remove-all] [-dump-csv path/to/list.csv] [-rename-from-csv path/to/translation.csv]
|
|
44
|
+
[-add-remoting-win32] [-add-remoting-win64] [-add-frontend-win32] [-add-frontend-win64]
|
|
45
|
+
[-extract-descriptor path/to/saved-modelDescriptor.xml] [-remove-sources] [-only-parameters]
|
|
46
|
+
[-only-inputs] [-only-outputs] [-summary] [-check]
|
|
47
|
+
|
|
48
|
+
fmutool is program to manipulate FMU.
|
|
49
|
+
|
|
50
|
+
optional arguments:
|
|
51
|
+
-h, -help display help.
|
|
52
|
+
-input path/to/module.fmu this option is mandatory to specify the filename of the FMU to be loaded. (default:
|
|
53
|
+
None)
|
|
54
|
+
-output path/to/module-modified.fmu
|
|
55
|
+
this option is used to specify the filename of the FMU to be created after
|
|
56
|
+
manipulations. If it is not provided, no new fmu will be saved and some
|
|
57
|
+
manipulations can be lost. (default: None)
|
|
58
|
+
-remove-toplevel rename the ports of the input fmu by striping all characters until the first '.'
|
|
59
|
+
(toplevel bus). If no '.' is present, the port won't be renamed. Resulting fmu
|
|
60
|
+
should be saved by using -output option. Note: before version 1.2.6, this option was
|
|
61
|
+
spelled -remove-toplel. (default: None)
|
|
62
|
+
-merge-toplevel replace first '.' by an '_' on every port name. (default: None)
|
|
63
|
+
-trim-until prefix remove a prefix from port name. Example '-trim-until _' : will rename port names of
|
|
64
|
+
the FMU by removing part of the name until the first '_'. Prefix can be longer than
|
|
65
|
+
a single character. (default: None)
|
|
66
|
+
-remove-regexp regular-expression
|
|
67
|
+
remove ports that match the regular-expression. Other ports will be kept. Resulting
|
|
68
|
+
fmu should be saved by using -output option. This option is available from version
|
|
69
|
+
1.1. See https://en.wikipedia.org/wiki/Regular_expression to have more detail of
|
|
70
|
+
expected format. (default: None)
|
|
71
|
+
-keep-only-regexp regular-expression
|
|
72
|
+
keep only ports that match the regular-expression. Other ports will be removed.
|
|
73
|
+
Resulting fmu should be saved by using -output option. This option is available from
|
|
74
|
+
version 1.1. See https://en.wikipedia.org/wiki/Regular_expression to have more
|
|
75
|
+
detail of expected format. (default: None)
|
|
76
|
+
-remove-all equivalent to '-remove-regexp .*'. Typical use case is to use it with -only-*
|
|
77
|
+
options. Example: in order ro suppress all parameters of FMU: -only-parameters
|
|
78
|
+
-remove-all (default: None)
|
|
79
|
+
-dump-csv path/to/list.csv list all names of the ports of the input fmu and store them inside path/to/list.csv.
|
|
80
|
+
This file is ';' separated. It contains two columns in order to be easily reused by
|
|
81
|
+
-rename-from-csv option. (default: None)
|
|
82
|
+
-rename-from-csv path/to/translation.csv
|
|
83
|
+
rename the ports of fmu accordingly to path/to/translation.csv. This file is ';'
|
|
84
|
+
separated. It contains two columns. First column contains original names. Second
|
|
85
|
+
column contains new names. * If a port is not found in the file, it won't be
|
|
86
|
+
renamed. This is working with version > 1.2.6. It is safer to keep ALL port in csv.
|
|
87
|
+
* If the new name is empty, the port will be removed. This is working starting
|
|
88
|
+
version 1.1. * If a name in the file is not present in input FMU, it will be
|
|
89
|
+
ignored. (no warning will be issued). Resulting fmu should be saved by using -output
|
|
90
|
+
option. (default: None)
|
|
91
|
+
-add-remoting-win32 this option is windows specific. It will add 'win32' interface to a 'win64' fmu.
|
|
92
|
+
Please upgrade to version 1.2.1 before using this option. Resulting fmu should be
|
|
93
|
+
saved by using -output option. (default: None)
|
|
94
|
+
-add-remoting-win64 this option is windows specific. It will add 'win64' interface to a 'win32' fmu.
|
|
95
|
+
Please upgrade to version 1.2.1 before using this option. Resulting fmu should be
|
|
96
|
+
saved by using -output option. (default: None)
|
|
97
|
+
-add-frontend-win32 this option is windows specific. It can be used with 'win32' fmu. At simulation
|
|
98
|
+
time, the FMU will spawn a dedicated process tu run the model. This option is
|
|
99
|
+
available from version 1.4. Resulting fmu should be saved by using -output option.
|
|
100
|
+
(default: None)
|
|
101
|
+
-add-frontend-win64 this option is windows specific. It can be used with 'win64' fmu. At simulation
|
|
102
|
+
time, the FMU will spawn a dedicated process tu run the model. This option is
|
|
103
|
+
available from version 1.4. Resulting fmu should be saved by using -output option.
|
|
104
|
+
(default: None)
|
|
105
|
+
-extract-descriptor path/to/saved-modelDescriptor.xml
|
|
106
|
+
save the modelDescription.xml into the specified location. If modification options
|
|
107
|
+
(like -rename-from-csv or -remove-toplevel are set), the saved file will contain
|
|
108
|
+
modification. This option is available from version 1.1. (default: None)
|
|
109
|
+
-remove-sources Remove sources folder from the FMU. This option is available from version 1.3.
|
|
110
|
+
(default: None)
|
|
111
|
+
-only-parameters apply operation only on ports with causality = 'parameter'. This option is available
|
|
112
|
+
from version 1.3. (default: None)
|
|
113
|
+
-only-inputs apply operation only on ports with causality = 'parameter'. This option is available
|
|
114
|
+
from version 1.3. (default: None)
|
|
115
|
+
-only-outputs apply operation only on ports with causality = 'output'. This option is available
|
|
116
|
+
from version 1.3. (default: None)
|
|
117
|
+
-summary display useful information regarding the FMU. (default: None)
|
|
118
|
+
-check performs some check of FMU and display Errors or Warnings. This is useful to avoid
|
|
119
|
+
later issues when using the FMU. (default: None)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### FMU Containers
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
fmucontainer [-h] -fmu-directory FMU_DIRECTORY [-container filename.csv:step_size] [-debug] [-no-auto-input]
|
|
126
|
+
[-no-auto-output] [-no-auto-link] [-mt] [-profile]
|
|
127
|
+
|
|
128
|
+
Generate FMU from FMU's
|
|
129
|
+
|
|
130
|
+
optional arguments:
|
|
131
|
+
-h, -help
|
|
132
|
+
-fmu-directory FMU_DIRECTORY Directory containing initial FMU’s and used to generate containers. (default: None)
|
|
133
|
+
-container filename.csv:step_size
|
|
134
|
+
Description of the container to create. (default: [])
|
|
135
|
+
-debug Add lot of useful log during the process. (default: False)
|
|
136
|
+
-no-auto-input Create ONLY explicit input. (default: True)
|
|
137
|
+
-no-auto-output Create ONLY explicit output. (default: True)
|
|
138
|
+
-no-auto-link Create ONLY explicit links. (default: True)
|
|
139
|
+
-mt Enable Multi-Threaded mode for the generated container. (default: False)
|
|
140
|
+
-profile Enable Profiling mode for the generated container. (default: False)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## API
|
|
144
|
+
|
|
145
|
+
You can write your own FMU Manipulation scripts. Once you downloaded fmutool module,
|
|
146
|
+
adding the `import` statement lets you access the API :
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from fmu_manipulation_toolbox.fmu_operations import FMU, OperationExtractNames, OperationStripTopLevel,
|
|
150
|
+
OperationRenameFromCSV
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### remove toplevel bus (if any)
|
|
154
|
+
|
|
155
|
+
Give a FMU with the following I/O structure
|
|
156
|
+
```
|
|
157
|
+
├── Parameters
|
|
158
|
+
│ ├── Foo
|
|
159
|
+
│ │ ├── param_A
|
|
160
|
+
│ ├── Bar
|
|
161
|
+
├── Generator
|
|
162
|
+
│ ├── Input_A
|
|
163
|
+
│ ├── Output_B
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The following transformation will lead into:
|
|
167
|
+
```
|
|
168
|
+
├── Foo
|
|
169
|
+
│ ├── param_A
|
|
170
|
+
├── Bar
|
|
171
|
+
├── Input_A
|
|
172
|
+
├── Output_B
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Note:** removing toplevel bus can lead to names collisions !
|
|
176
|
+
|
|
177
|
+
The following code will do this transformation:
|
|
178
|
+
```python
|
|
179
|
+
fmu = FMU(r"bouncing_ball.fmu")
|
|
180
|
+
operation = OperationStripTopLevel()
|
|
181
|
+
fmu.apply_operation(operation)
|
|
182
|
+
fmu.repack(r"bouncing_ball-modified.fmu")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Extract names and write a CSV
|
|
186
|
+
|
|
187
|
+
The following code will dump all FMU's Scalars names into a CSV:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
fmu = FMU(r"bouncing_ball.fmu")
|
|
191
|
+
operation = OperationExtractNames()
|
|
192
|
+
fmu.apply_operation(operation)
|
|
193
|
+
operation.write_csv(r"bouncing_ball.csv")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The produced CSV contains 2 columns in order to be reused in the next transformation.
|
|
197
|
+
The 2 columns are identical.
|
|
198
|
+
|
|
199
|
+
```csv
|
|
200
|
+
name;newName;valueReference;causality;variability
|
|
201
|
+
h;h;0;local;continuous
|
|
202
|
+
der(h);der(h);1;local;continuous
|
|
203
|
+
v;v;2;local;continuous
|
|
204
|
+
der(v);der(v);3;local;continuous
|
|
205
|
+
g;g;4;parameter;fixed
|
|
206
|
+
e;e;5;parameter;tunable
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Read CSV and rename FMU ports
|
|
210
|
+
|
|
211
|
+
CSV file should contain- 2 columns:
|
|
212
|
+
1. the current name
|
|
213
|
+
2. the new name
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
fmu = FMU(r"bouncing_ball.fmu")
|
|
217
|
+
operation = OperationRenameFromCSV(r"bouncing_ball-modified.csv")
|
|
218
|
+
fmu.apply_operation(operation)
|
|
219
|
+
fmu.repack(r"bouncing_ball-renamed.fmu")
|
|
220
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def gui():
|
|
5
|
+
try:
|
|
6
|
+
from .gui import main
|
|
7
|
+
main()
|
|
8
|
+
except ModuleNotFoundError as e:
|
|
9
|
+
print(f"FATAL ERROR: {e}. No GUI Available.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cli():
|
|
13
|
+
from .cli import fmutool
|
|
14
|
+
fmutool()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
if len(sys.argv) == 1:
|
|
19
|
+
gui()
|
|
20
|
+
else:
|
|
21
|
+
cli()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == '__main__':
|
|
25
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'V1.7.5'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import inspect
|
|
3
|
+
import os
|
|
4
|
+
import xmlschema
|
|
5
|
+
from xmlschema.validators.exceptions import XMLSchemaValidationError
|
|
6
|
+
from .fmu_operations import OperationAbstract
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OperationGenericCheck(OperationAbstract):
|
|
10
|
+
SUPPORTED_FMI_VERSIONS = ('2.0',)
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.compliant_with_version = None
|
|
14
|
+
|
|
15
|
+
def __repr__(self):
|
|
16
|
+
return f"FMU Generic Conformity Checks"
|
|
17
|
+
|
|
18
|
+
def fmi_attrs(self, attrs):
|
|
19
|
+
if attrs['fmiVersion'] not in self.SUPPORTED_FMI_VERSIONS:
|
|
20
|
+
print(f"ERROR: Expected FMI {','.join(self.SUPPORTED_FMI_VERSIONS)} versions.")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
xsd_filename = os.path.join(os.path.dirname(__file__), "resources", "fmi-" + attrs['fmiVersion'],
|
|
24
|
+
"fmi2ModelDescription.xsd")
|
|
25
|
+
try:
|
|
26
|
+
xmlschema.validate(self.fmu.descriptor_filename, schema=xsd_filename)
|
|
27
|
+
except XMLSchemaValidationError as error:
|
|
28
|
+
print(error.reason, error.msg)
|
|
29
|
+
else:
|
|
30
|
+
self.compliant_with_version = attrs['fmiVersion']
|
|
31
|
+
|
|
32
|
+
def closure(self):
|
|
33
|
+
if self.compliant_with_version:
|
|
34
|
+
print(f"INFO: This FMU seems to be compliant with FMI-{self.compliant_with_version}.")
|
|
35
|
+
else:
|
|
36
|
+
print(f"ERROR: This FMU does not validate with FMI standard.")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
checker_list = [OperationGenericCheck]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_from_file(checker_filename: str):
|
|
43
|
+
spec = importlib.util.spec_from_file_location(checker_filename, checker_filename)
|
|
44
|
+
if not spec:
|
|
45
|
+
print(f"ERROR: Cannot load {checker_filename}. Is this a python file?")
|
|
46
|
+
return
|
|
47
|
+
try:
|
|
48
|
+
checker_module = importlib.util.module_from_spec(spec)
|
|
49
|
+
try:
|
|
50
|
+
spec.loader.exec_module(checker_module)
|
|
51
|
+
except (ModuleNotFoundError, SyntaxError) as error:
|
|
52
|
+
print(f"ERROR: Cannot load {checker_filename}: {error})")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
for checker_name, checker_class in inspect.getmembers(checker_module, inspect.isclass):
|
|
56
|
+
if OperationAbstract in checker_class.__bases__:
|
|
57
|
+
checker_list.append(checker_class)
|
|
58
|
+
print(f"Adding checker: {checker_filename}|{checker_name}")
|
|
59
|
+
|
|
60
|
+
except AttributeError:
|
|
61
|
+
print(f"ERROR: {checker_filename} should implement class 'OperationCheck'")
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from colorama import Fore, Style, init
|
|
5
|
+
|
|
6
|
+
from .fmu_operations import *
|
|
7
|
+
from .fmu_container import FMUContainerSpecReader, FMUContainerError
|
|
8
|
+
from .checker import checker_list
|
|
9
|
+
from .version import __version__ as version
|
|
10
|
+
from .help import Help
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def setup_logger():
|
|
14
|
+
class CustomFormatter(logging.Formatter):
|
|
15
|
+
def format(self, record):
|
|
16
|
+
log_format = "%(levelname)-8s | %(message)s"
|
|
17
|
+
format_per_level = {
|
|
18
|
+
logging.DEBUG: str(Fore.BLUE) + log_format,
|
|
19
|
+
logging.INFO: str(Fore.CYAN) + log_format,
|
|
20
|
+
logging.WARNING: str(Fore.YELLOW) + log_format,
|
|
21
|
+
logging.ERROR: str(Fore.RED) + log_format,
|
|
22
|
+
logging.CRITICAL: str(Fore.RED + Style.BRIGHT) + log_format,
|
|
23
|
+
}
|
|
24
|
+
formatter = logging.Formatter(format_per_level[record.levelno])
|
|
25
|
+
return formatter.format(record)
|
|
26
|
+
init()
|
|
27
|
+
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
28
|
+
handler = logging.StreamHandler(stream=sys.stdout)
|
|
29
|
+
handler.setFormatter(CustomFormatter())
|
|
30
|
+
logger.addHandler(handler)
|
|
31
|
+
logger.setLevel(logging.INFO)
|
|
32
|
+
|
|
33
|
+
return logger
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def make_wide(formatter, w=120, h=36):
|
|
37
|
+
"""Return a wider HelpFormatter, if possible."""
|
|
38
|
+
try:
|
|
39
|
+
# https://stackoverflow.com/a/5464440
|
|
40
|
+
# beware: "Only the name of this class is considered a public API."
|
|
41
|
+
kwargs = {'width': w, 'max_help_position': h}
|
|
42
|
+
formatter(None, **kwargs)
|
|
43
|
+
return lambda prog: formatter(prog, **kwargs)
|
|
44
|
+
except TypeError:
|
|
45
|
+
return formatter
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def fmutool():
|
|
49
|
+
print(f"FMU Manipulation Toolbox version {version}")
|
|
50
|
+
help_message = Help()
|
|
51
|
+
|
|
52
|
+
parser = argparse.ArgumentParser(prog='fmutool',
|
|
53
|
+
description="Analyse and Manipulate a FMU by modifying its 'modelDescription.xml'",
|
|
54
|
+
formatter_class=make_wide(argparse.HelpFormatter),
|
|
55
|
+
add_help=False,
|
|
56
|
+
epilog="see: https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/README.md")
|
|
57
|
+
|
|
58
|
+
def add_option(option, *args, **kwargs):
|
|
59
|
+
parser.add_argument(option, *args, help=help_message.usage(option), **kwargs)
|
|
60
|
+
|
|
61
|
+
add_option('-h', '-help', action="help")
|
|
62
|
+
|
|
63
|
+
# I/O
|
|
64
|
+
add_option('-input', action='store', dest='fmu_input', default=None, required=True, metavar='path/to/module.fmu')
|
|
65
|
+
add_option('-output', action='store', dest='fmu_output', default=None, metavar='path/to/module-modified.fmu')
|
|
66
|
+
|
|
67
|
+
# Port name manipulation
|
|
68
|
+
add_option('-remove-toplevel', action='append_const', dest='operations_list', const=OperationStripTopLevel())
|
|
69
|
+
add_option('-merge-toplevel', action='append_const', dest='operations_list', const=OperationMergeTopLevel())
|
|
70
|
+
add_option('-trim-until', action='append', dest='operations_list', type=OperationTrimUntil, metavar='prefix')
|
|
71
|
+
add_option('-remove-regexp', action='append', dest='operations_list', type=OperationRemoveRegexp,
|
|
72
|
+
metavar='regular-expression')
|
|
73
|
+
add_option('-keep-only-regexp', action='append', dest='operations_list', type=OperationKeepOnlyRegexp,
|
|
74
|
+
metavar='regular-expression')
|
|
75
|
+
add_option('-remove-all', action='append_const', dest='operations_list', const=OperationRemoveRegexp('.*'))
|
|
76
|
+
|
|
77
|
+
# Batch Rename
|
|
78
|
+
add_option('-dump-csv', action='append', dest='operations_list', type=OperationSaveNamesToCSV,
|
|
79
|
+
metavar='path/to/list.csv')
|
|
80
|
+
add_option('-rename-from-csv', action='append', dest='operations_list', type=OperationRenameFromCSV,
|
|
81
|
+
metavar='path/to/translation.csv')
|
|
82
|
+
|
|
83
|
+
# Remoting
|
|
84
|
+
add_option('-add-remoting-win32', action='append_const', dest='operations_list', const=OperationAddRemotingWin32())
|
|
85
|
+
add_option('-add-remoting-win64', action='append_const', dest='operations_list', const=OperationAddRemotingWin64())
|
|
86
|
+
add_option('-add-frontend-win32', action='append_const', dest='operations_list', const=OperationAddFrontendWin32())
|
|
87
|
+
add_option('-add-frontend-win64', action='append_const', dest='operations_list', const=OperationAddFrontendWin64())
|
|
88
|
+
|
|
89
|
+
# Extraction / Removal
|
|
90
|
+
add_option('-extract-descriptor', action='store', dest='extract_description',
|
|
91
|
+
metavar='path/to/saved-modelDescriptor.xml')
|
|
92
|
+
add_option('-remove-sources', action='append_const', dest='operations_list',
|
|
93
|
+
const=OperationRemoveSources())
|
|
94
|
+
# Filter
|
|
95
|
+
add_option('-only-parameters', action='append_const', dest='apply_on', const='parameter')
|
|
96
|
+
add_option('-only-inputs', action='append_const', dest='apply_on', const='input')
|
|
97
|
+
add_option('-only-outputs', action='append_const', dest='apply_on', const='output')
|
|
98
|
+
# Checker
|
|
99
|
+
add_option('-summary', action='append_const', dest='operations_list', const=OperationSummary())
|
|
100
|
+
add_option('-check', action='append_const', dest='operations_list', const=[checker() for checker in checker_list])
|
|
101
|
+
|
|
102
|
+
cli_options = parser.parse_args()
|
|
103
|
+
# handle the "no operation" use case
|
|
104
|
+
if not cli_options.operations_list:
|
|
105
|
+
cli_options.operations_list = []
|
|
106
|
+
|
|
107
|
+
if cli_options.fmu_input == cli_options.fmu_output:
|
|
108
|
+
print(f"FATAL ERROR: '-input' and '-output' should point to different files.")
|
|
109
|
+
sys.exit(-3)
|
|
110
|
+
|
|
111
|
+
print(f"READING Input='{cli_options.fmu_input}'")
|
|
112
|
+
try:
|
|
113
|
+
fmu = FMU(cli_options.fmu_input)
|
|
114
|
+
except FMUException as reason:
|
|
115
|
+
print(f"FATAL ERROR: {reason}")
|
|
116
|
+
sys.exit(-4)
|
|
117
|
+
|
|
118
|
+
if cli_options.apply_on:
|
|
119
|
+
print("Applying operation for :")
|
|
120
|
+
for causality in cli_options.apply_on:
|
|
121
|
+
print(f" - causality = {causality}")
|
|
122
|
+
|
|
123
|
+
def flatten(list_of_list: list):
|
|
124
|
+
return [x for xs in list_of_list for x in xs]
|
|
125
|
+
|
|
126
|
+
for operation in flatten(cli_options.operations_list):
|
|
127
|
+
print(f" => {operation}")
|
|
128
|
+
try:
|
|
129
|
+
fmu.apply_operation(operation, cli_options.apply_on)
|
|
130
|
+
except OperationException as reason:
|
|
131
|
+
print(f"ERROR: {reason}")
|
|
132
|
+
sys.exit(-6)
|
|
133
|
+
|
|
134
|
+
if cli_options.extract_description:
|
|
135
|
+
print(f"WRITING ModelDescriptor='{cli_options.extract_description}'")
|
|
136
|
+
fmu.save_descriptor(cli_options.extract_description)
|
|
137
|
+
|
|
138
|
+
if cli_options.fmu_output:
|
|
139
|
+
print(f"WRITING Output='{cli_options.fmu_output}'")
|
|
140
|
+
try:
|
|
141
|
+
fmu.repack(cli_options.fmu_output)
|
|
142
|
+
except FMUException as reason:
|
|
143
|
+
print(f"FATAL ERROR: {reason}")
|
|
144
|
+
sys.exit(-5)
|
|
145
|
+
else:
|
|
146
|
+
print(f"INFO Modified FMU is not saved. If necessary use '-output' option.")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def fmucontainer():
|
|
150
|
+
logger = setup_logger()
|
|
151
|
+
|
|
152
|
+
logger.info(f"FMUContainer version {version}")
|
|
153
|
+
parser = argparse.ArgumentParser(prog="fmucontainer", description="Generate FMU from FMU's",
|
|
154
|
+
formatter_class=make_wide(argparse.ArgumentDefaultsHelpFormatter),
|
|
155
|
+
add_help=False,
|
|
156
|
+
epilog="see: https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/"
|
|
157
|
+
"container/README.md")
|
|
158
|
+
|
|
159
|
+
parser.add_argument('-h', '-help', action="help")
|
|
160
|
+
|
|
161
|
+
parser.add_argument("-fmu-directory", action="store", dest="fmu_directory", required=True,
|
|
162
|
+
help="Directory containing initial FMU’s and used to generate containers.")
|
|
163
|
+
|
|
164
|
+
parser.add_argument("-container", action="append", dest="container_descriptions_list", default=[],
|
|
165
|
+
metavar="filename.csv:step_size",
|
|
166
|
+
help="Description of the container to create.")
|
|
167
|
+
|
|
168
|
+
parser.add_argument("-debug", action="store_true", dest="debug",
|
|
169
|
+
help="Add lot of useful log during the process.")
|
|
170
|
+
|
|
171
|
+
parser.add_argument("-no-auto-input", action="store_false", dest="auto_input", default=True,
|
|
172
|
+
help="Create ONLY explicit input.")
|
|
173
|
+
|
|
174
|
+
parser.add_argument("-no-auto-output", action="store_false", dest="auto_output", default=True,
|
|
175
|
+
help="Create ONLY explicit output.")
|
|
176
|
+
|
|
177
|
+
parser.add_argument("-no-auto-link", action="store_false", dest="auto_link", default=True,
|
|
178
|
+
help="Create ONLY explicit links.")
|
|
179
|
+
|
|
180
|
+
parser.add_argument("-mt", action="store_true", dest="mt", default=False,
|
|
181
|
+
help="Enable Multi-Threaded mode for the generated container.")
|
|
182
|
+
|
|
183
|
+
parser.add_argument("-profile", action="store_true", dest="profiling", default=False,
|
|
184
|
+
help="Enable Profiling mode for the generated container.")
|
|
185
|
+
|
|
186
|
+
config = parser.parse_args()
|
|
187
|
+
|
|
188
|
+
if config.debug:
|
|
189
|
+
logger.setLevel(logging.DEBUG)
|
|
190
|
+
|
|
191
|
+
for description in config.container_descriptions_list:
|
|
192
|
+
try:
|
|
193
|
+
filename_description, step_size = description.split(":")
|
|
194
|
+
step_size = float(step_size)
|
|
195
|
+
except ValueError:
|
|
196
|
+
step_size = None
|
|
197
|
+
filename_description = description
|
|
198
|
+
|
|
199
|
+
container_filename = Path(filename_description).with_suffix(".fmu")
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
csv_reader = FMUContainerSpecReader(Path(config.fmu_directory))
|
|
203
|
+
container = csv_reader.read(filename_description)
|
|
204
|
+
container.add_implicit_rule(auto_input=config.auto_input,
|
|
205
|
+
auto_output=config.auto_output,
|
|
206
|
+
auto_link=config.auto_link)
|
|
207
|
+
container.make_fmu(container_filename, step_size=step_size, debug=config.debug, mt=config.mt,
|
|
208
|
+
profiling=config.profiling)
|
|
209
|
+
except (FileNotFoundError, FMUContainerError, FMUException) as e:
|
|
210
|
+
logger.error(f"Cannot build container from '{filename_description}': {e}")
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# for debug purpose
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
fmucontainer()
|