fmu-manipulation-toolbox 1.7.5__py3-none-any.whl
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/__init__.py +1 -0
- fmu_manipulation_toolbox/__main__.py +25 -0
- fmu_manipulation_toolbox/__version__.py +1 -0
- fmu_manipulation_toolbox/checker.py +61 -0
- fmu_manipulation_toolbox/cli.py +216 -0
- fmu_manipulation_toolbox/fmu_container.py +784 -0
- fmu_manipulation_toolbox/fmu_operations.py +489 -0
- fmu_manipulation_toolbox/gui.py +493 -0
- fmu_manipulation_toolbox/help.py +87 -0
- fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
- fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
- fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
- fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
- fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
- fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
- fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +58 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +78 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +345 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +218 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +89 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +116 -0
- fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +92 -0
- fmu_manipulation_toolbox/resources/fmu.png +0 -0
- fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
- fmu_manipulation_toolbox/resources/help.png +0 -0
- fmu_manipulation_toolbox/resources/icon.png +0 -0
- fmu_manipulation_toolbox/resources/license.txt +34 -0
- fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
- fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
- fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
- fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
- fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
- fmu_manipulation_toolbox/resources/model.png +0 -0
- fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
- fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
- fmu_manipulation_toolbox/version.py +9 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/LICENSE.txt +22 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/METADATA +20 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/RECORD +46 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/WHEEL +5 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/entry_points.txt +3 -0
- fmu_manipulation_toolbox-1.7.5.dist-info/top_level.txt +1 -0
|
@@ -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()
|