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.
Files changed (52) hide show
  1. fmu_manipulation_toolbox-1.7.5/LICENSE.txt +22 -0
  2. fmu_manipulation_toolbox-1.7.5/PKG-INFO +20 -0
  3. fmu_manipulation_toolbox-1.7.5/README.md +220 -0
  4. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__init__.py +1 -0
  5. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__main__.py +25 -0
  6. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/__version__.py +1 -0
  7. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/checker.py +61 -0
  8. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/cli.py +216 -0
  9. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/fmu_container.py +784 -0
  10. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/fmu_operations.py +489 -0
  11. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/gui.py +493 -0
  12. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/help.py +87 -0
  13. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
  14. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
  15. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
  16. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
  17. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
  18. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
  19. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  20. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +58 -0
  21. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +78 -0
  22. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +345 -0
  23. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +218 -0
  24. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +89 -0
  25. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +116 -0
  26. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +92 -0
  27. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmu.png +0 -0
  28. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  29. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/help.png +0 -0
  30. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/icon.png +0 -0
  31. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/license.txt +34 -0
  32. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  33. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  34. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  35. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  36. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  37. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/model.png +0 -0
  38. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  39. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  40. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  41. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  42. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  43. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox/version.py +9 -0
  44. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/PKG-INFO +20 -0
  45. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/SOURCES.txt +51 -0
  46. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/dependency_links.txt +1 -0
  47. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/entry_points.txt +3 -0
  48. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/requires.txt +4 -0
  49. fmu_manipulation_toolbox-1.7.5/fmu_manipulation_toolbox.egg-info/top_level.txt +1 -0
  50. fmu_manipulation_toolbox-1.7.5/setup.cfg +7 -0
  51. fmu_manipulation_toolbox-1.7.5/setup.py +65 -0
  52. 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
+ ![Title](fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png)
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
+ ![GUI](doc/gui.png "GUI")
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,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,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()