fmu-manipulation-toolbox 1.9rc1__py3-none-any.whl → 1.9rc3__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 +0 -1
- fmu_manipulation_toolbox/__main__.py +1 -1
- fmu_manipulation_toolbox/__version__.py +1 -1
- fmu_manipulation_toolbox/assembly.py +1 -1
- fmu_manipulation_toolbox/checker.py +11 -8
- fmu_manipulation_toolbox/container.py +225 -140
- fmu_manipulation_toolbox/gui.py +45 -55
- fmu_manipulation_toolbox/gui_style.py +8 -0
- fmu_manipulation_toolbox/operations.py +68 -41
- fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
- fmu_manipulation_toolbox/resources/linux64/container.so +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-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/METADATA +1 -1
- {fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/RECORD +22 -22
- {fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/WHEEL +0 -0
- {fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/entry_points.txt +0 -0
- {fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/licenses/LICENSE.txt +0 -0
- {fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/top_level.txt +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +1 @@
|
|
|
1
|
-
'V1.9-
|
|
1
|
+
'V1.9-rc3'
|
|
@@ -103,7 +103,7 @@ class AssemblyNode:
|
|
|
103
103
|
|
|
104
104
|
def make_fmu(self, fmu_directory: Path, debug=False, description_pathname=None, fmi_version=2):
|
|
105
105
|
for node in self.children.values():
|
|
106
|
-
node.make_fmu(fmu_directory, debug=debug)
|
|
106
|
+
node.make_fmu(fmu_directory, debug=debug, fmi_version=fmi_version)
|
|
107
107
|
|
|
108
108
|
identifier = str(Path(self.name).stem)
|
|
109
109
|
container = FMUContainer(identifier, fmu_directory, description_pathname=description_pathname,
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import importlib.util
|
|
2
2
|
import inspect
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import xmlschema
|
|
5
6
|
from xmlschema.validators.exceptions import XMLSchemaValidationError
|
|
6
7
|
from .operations import OperationAbstract
|
|
7
8
|
|
|
9
|
+
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class OperationGenericCheck(OperationAbstract):
|
|
10
13
|
SUPPORTED_FMI_VERSIONS = ('2.0', '3.0')
|
|
@@ -17,7 +20,7 @@ class OperationGenericCheck(OperationAbstract):
|
|
|
17
20
|
|
|
18
21
|
def fmi_attrs(self, attrs):
|
|
19
22
|
if attrs['fmiVersion'] not in self.SUPPORTED_FMI_VERSIONS:
|
|
20
|
-
|
|
23
|
+
logger.error(f"Expected FMI {','.join(self.SUPPORTED_FMI_VERSIONS)} versions.")
|
|
21
24
|
return
|
|
22
25
|
|
|
23
26
|
fmi_name = f"fmi{attrs['fmiVersion'][0]}"
|
|
@@ -27,15 +30,15 @@ class OperationGenericCheck(OperationAbstract):
|
|
|
27
30
|
try:
|
|
28
31
|
xmlschema.validate(self.fmu.descriptor_filename, schema=xsd_filename)
|
|
29
32
|
except XMLSchemaValidationError as error:
|
|
30
|
-
|
|
33
|
+
logger.error(error.reason, error.msg)
|
|
31
34
|
else:
|
|
32
35
|
self.compliant_with_version = attrs['fmiVersion']
|
|
33
36
|
|
|
34
37
|
def closure(self):
|
|
35
38
|
if self.compliant_with_version:
|
|
36
|
-
|
|
39
|
+
logger.info(f"This FMU seems to be compliant with FMI-{self.compliant_with_version}.")
|
|
37
40
|
else:
|
|
38
|
-
|
|
41
|
+
logger.error(f"This FMU does not validate with FMI standard.")
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
checker_list = [OperationGenericCheck]
|
|
@@ -44,20 +47,20 @@ checker_list = [OperationGenericCheck]
|
|
|
44
47
|
def add_from_file(checker_filename: str):
|
|
45
48
|
spec = importlib.util.spec_from_file_location(checker_filename, checker_filename)
|
|
46
49
|
if not spec:
|
|
47
|
-
|
|
50
|
+
logger.error(f"Cannot load '{checker_filename}'. Is this a python file?")
|
|
48
51
|
return
|
|
49
52
|
try:
|
|
50
53
|
checker_module = importlib.util.module_from_spec(spec)
|
|
51
54
|
try:
|
|
52
55
|
spec.loader.exec_module(checker_module)
|
|
53
56
|
except (ModuleNotFoundError, SyntaxError) as error:
|
|
54
|
-
|
|
57
|
+
logger.error(f"Cannot load '{checker_filename}': {error})")
|
|
55
58
|
return
|
|
56
59
|
|
|
57
60
|
for checker_name, checker_class in inspect.getmembers(checker_module, inspect.isclass):
|
|
58
61
|
if OperationAbstract in checker_class.__bases__:
|
|
59
62
|
checker_list.append(checker_class)
|
|
60
|
-
|
|
63
|
+
logger.info(f"Adding checker: {checker_filename}|{checker_name}")
|
|
61
64
|
|
|
62
65
|
except AttributeError:
|
|
63
|
-
|
|
66
|
+
logger.error(f"'{checker_filename}' should implement class 'OperationCheck'")
|
|
@@ -40,29 +40,6 @@ class EmbeddedFMUPort:
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
FMI_TO_CONTAINER = {
|
|
44
|
-
2: {
|
|
45
|
-
'Real': 'real64',
|
|
46
|
-
'Integer': 'integer32',
|
|
47
|
-
'String': 'string',
|
|
48
|
-
'Boolean': 'boolean'
|
|
49
|
-
},
|
|
50
|
-
3: {
|
|
51
|
-
'Float64': 'real64',
|
|
52
|
-
'Float32': 'real32',
|
|
53
|
-
'Int8': 'integer8',
|
|
54
|
-
'UInt8': 'uinteger8',
|
|
55
|
-
'Int16': 'integer16',
|
|
56
|
-
'UInt16': 'uinteger16',
|
|
57
|
-
'Int32': 'integer32',
|
|
58
|
-
'UInt32': 'uinteger32',
|
|
59
|
-
'Int64': 'integer64',
|
|
60
|
-
'UInt64': 'uinteger64',
|
|
61
|
-
'String': 'string',
|
|
62
|
-
'Boolean': 'boolean1'
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
43
|
CONTAINER_TO_FMI = {
|
|
67
44
|
2: {
|
|
68
45
|
'real64': 'Real',
|
|
@@ -90,7 +67,7 @@ class EmbeddedFMUPort:
|
|
|
90
67
|
"real64", "real32",
|
|
91
68
|
"integer8", "uinteger8", "integer16", "uinteger16", "integer32", "uinteger32", "integer64", "uinteger64",
|
|
92
69
|
"boolean", "boolean1",
|
|
93
|
-
"
|
|
70
|
+
"string"
|
|
94
71
|
)
|
|
95
72
|
|
|
96
73
|
|
|
@@ -112,7 +89,7 @@ class EmbeddedFMUPort:
|
|
|
112
89
|
|
|
113
90
|
|
|
114
91
|
|
|
115
|
-
def xml(self, vr: int, name=None, causality=None, start=None, fmi_version=2):
|
|
92
|
+
def xml(self, vr: int, name=None, causality=None, start=None, fmi_version=2) -> str:
|
|
116
93
|
if name is None:
|
|
117
94
|
name = self.name
|
|
118
95
|
if causality is None:
|
|
@@ -122,7 +99,12 @@ class EmbeddedFMUPort:
|
|
|
122
99
|
if self.variability is None:
|
|
123
100
|
self.variability = "continuous" if "real" in self.type_name else "discrete"
|
|
124
101
|
|
|
125
|
-
|
|
102
|
+
try:
|
|
103
|
+
fmi_type = self.CONTAINER_TO_FMI[fmi_version][self.type_name]
|
|
104
|
+
except KeyError:
|
|
105
|
+
logger.error(f"Cannot expose '{name}' because type '{self.type_name}' is not compatible "
|
|
106
|
+
f"with FMI-{fmi_version}.0")
|
|
107
|
+
return ""
|
|
126
108
|
|
|
127
109
|
if fmi_version == 2:
|
|
128
110
|
child_attrs = {
|
|
@@ -146,19 +128,36 @@ class EmbeddedFMUPort:
|
|
|
146
128
|
scalar_attrs_str = " ".join([f'{key}="{value}"' for (key, value) in filtered_attrs.items()])
|
|
147
129
|
return f'<ScalarVariable {scalar_attrs_str}>{child_str}</ScalarVariable>'
|
|
148
130
|
else:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
131
|
+
if fmi_type in ('String', 'Binary'):
|
|
132
|
+
if start:
|
|
133
|
+
child_str = f'<Start value="{start}"/>'
|
|
134
|
+
else:
|
|
135
|
+
child_str = ''
|
|
136
|
+
scalar_attrs = {
|
|
137
|
+
"name": name,
|
|
138
|
+
"valueReference": vr,
|
|
139
|
+
"causality": causality,
|
|
140
|
+
"variability": self.variability,
|
|
141
|
+
"initial": self.initial,
|
|
142
|
+
"description": self.description,
|
|
143
|
+
}
|
|
144
|
+
filtered_attrs = {key: value for key, value in scalar_attrs.items() if value is not None}
|
|
145
|
+
scalar_attrs_str = " ".join([f'{key}="{value}"' for (key, value) in filtered_attrs.items()])
|
|
146
|
+
return f'<{fmi_type} {scalar_attrs_str}>{child_str}</{fmi_type}>'
|
|
147
|
+
else:
|
|
148
|
+
scalar_attrs = {
|
|
149
|
+
"name": name,
|
|
150
|
+
"valueReference": vr,
|
|
151
|
+
"causality": causality,
|
|
152
|
+
"variability": self.variability,
|
|
153
|
+
"initial": self.initial,
|
|
154
|
+
"description": self.description,
|
|
155
|
+
"start": start
|
|
156
|
+
}
|
|
157
|
+
filtered_attrs = {key: value for key, value in scalar_attrs.items() if value is not None}
|
|
158
|
+
scalar_attrs_str = " ".join([f'{key}="{value}"' for (key, value) in filtered_attrs.items()])
|
|
160
159
|
|
|
161
|
-
|
|
160
|
+
return f'<{fmi_type} {scalar_attrs_str}/>'
|
|
162
161
|
|
|
163
162
|
|
|
164
163
|
class EmbeddedFMU(OperationAbstract):
|
|
@@ -265,12 +264,51 @@ class ContainerInput:
|
|
|
265
264
|
self.cport_list.append(cport_to)
|
|
266
265
|
|
|
267
266
|
|
|
268
|
-
class
|
|
267
|
+
class Link:
|
|
268
|
+
CONVERSION_FUNCTION = {
|
|
269
|
+
"real32/real64": "F32_F64",
|
|
270
|
+
|
|
271
|
+
"Int8/Int16": "D8_D16",
|
|
272
|
+
"Int8/UInt16": "D8_U16",
|
|
273
|
+
"Int8/Int32": "D8_D32",
|
|
274
|
+
"Int8/UInt32": "D8_U32",
|
|
275
|
+
"Int8/Int64": "D8_D64",
|
|
276
|
+
"Int8/UInt64": "D8_U64",
|
|
277
|
+
|
|
278
|
+
"UInt8/Int16": "U8_D16",
|
|
279
|
+
"UInt8/UInt16": "U8_U16",
|
|
280
|
+
"UInt8/Int32": "U8_D32",
|
|
281
|
+
"UInt8/UInt32": "U8_U32",
|
|
282
|
+
"UInt8/Int64": "U8_D64",
|
|
283
|
+
"UInt8/UInt64": "U8_U64",
|
|
284
|
+
|
|
285
|
+
"Int16/Int32": "D16_D32",
|
|
286
|
+
"Int16/UInt32": "D16_U32",
|
|
287
|
+
"Int16/Int64": "D16_D64",
|
|
288
|
+
"Int16/UInt64": "D16_U64",
|
|
289
|
+
|
|
290
|
+
"UInt16/Int32": "U16_D32",
|
|
291
|
+
"UInt16/UInt32": "U16_U32",
|
|
292
|
+
"UInt16/Int64": "U16_D64",
|
|
293
|
+
"UInt16/UInt64": "U16_U64",
|
|
294
|
+
|
|
295
|
+
"Int32/Int64": "D32_D64",
|
|
296
|
+
"Int32/UInt64": "D32_U64",
|
|
297
|
+
|
|
298
|
+
"UInt32/Int64": "U32_D64",
|
|
299
|
+
"UInt32/UInt64": "U32_U64",
|
|
300
|
+
|
|
301
|
+
"boolean/boolean1": "B_B1",
|
|
302
|
+
"boolean1/boolean": "B1_B",
|
|
303
|
+
}
|
|
304
|
+
|
|
269
305
|
def __init__(self, cport_from: ContainerPort):
|
|
270
306
|
self.name = cport_from.fmu.id + "." + cport_from.port.name # strip .fmu suffix
|
|
271
307
|
self.cport_from = cport_from
|
|
272
308
|
self.cport_to_list: List[ContainerPort] = []
|
|
273
|
-
|
|
309
|
+
|
|
310
|
+
self.vr: Optional[int] = None
|
|
311
|
+
self.vr_converted: Dict[str, Optional[int]] = {}
|
|
274
312
|
|
|
275
313
|
if not cport_from.port.causality == "output":
|
|
276
314
|
raise FMUContainerError(f"{cport_from} is {cport_from.port.causality} instead of OUTPUT")
|
|
@@ -281,30 +319,54 @@ class Local:
|
|
|
281
319
|
|
|
282
320
|
if cport_to.port.type_name == self.cport_from.port.type_name:
|
|
283
321
|
self.cport_to_list.append(cport_to)
|
|
322
|
+
elif self.get_conversion(cport_to):
|
|
323
|
+
self.cport_to_list.append(cport_to)
|
|
324
|
+
self.vr_converted[cport_to.port.type_name] = None
|
|
284
325
|
else:
|
|
285
326
|
raise FMUContainerError(f"failed to connect {self.cport_from} to {cport_to} due to type.")
|
|
286
327
|
|
|
328
|
+
def get_conversion(self, cport_to: ContainerPort) -> Optional[str]:
|
|
329
|
+
try:
|
|
330
|
+
conversion = f"{self.cport_from.port.type_name}/{cport_to.port.type_name}"
|
|
331
|
+
return self.CONVERSION_FUNCTION[conversion]
|
|
332
|
+
except KeyError:
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
def nb_local(self) -> int:
|
|
336
|
+
return 1+len(self.vr_converted)
|
|
337
|
+
|
|
287
338
|
|
|
288
339
|
class ValueReferenceTable:
|
|
289
340
|
def __init__(self):
|
|
290
|
-
self.vr_table = {}
|
|
291
|
-
self.masks = {}
|
|
341
|
+
self.vr_table:Dict[str, int] = {}
|
|
342
|
+
self.masks: Dict[str, int] = {}
|
|
343
|
+
self.nb_local_variable:Dict[str, int] = {}
|
|
292
344
|
for i, type_name in enumerate(EmbeddedFMUPort.ALL_TYPES):
|
|
293
345
|
self.vr_table[type_name] = 0
|
|
294
346
|
self.masks[type_name] = i << 24
|
|
347
|
+
self.nb_local_variable[type_name] = 0
|
|
295
348
|
|
|
296
|
-
|
|
297
|
-
def add_vr(self, port_or_type_name: Union[ContainerPort, str]) -> int:
|
|
349
|
+
def add_vr(self, port_or_type_name: Union[ContainerPort, str], local: bool = False) -> int:
|
|
298
350
|
if isinstance(port_or_type_name, ContainerPort):
|
|
299
351
|
type_name = port_or_type_name.port.type_name
|
|
300
352
|
else:
|
|
301
353
|
type_name = port_or_type_name
|
|
302
354
|
|
|
355
|
+
if local:
|
|
356
|
+
self.nb_local_variable[type_name] += 1
|
|
357
|
+
|
|
303
358
|
vr = self.vr_table[type_name]
|
|
304
359
|
self.vr_table[type_name] += 1
|
|
305
360
|
|
|
306
361
|
return vr | self.masks[type_name]
|
|
307
362
|
|
|
363
|
+
def set_link_vr(self, link: Link):
|
|
364
|
+
link.vr = self.add_vr(link.cport_from, local=True)
|
|
365
|
+
for type_name in link.vr_converted.keys():
|
|
366
|
+
link.vr_converted[type_name] = self.add_vr(type_name, local=True)
|
|
367
|
+
|
|
368
|
+
def nb_local(self, type_name: str) -> int:
|
|
369
|
+
return self.nb_local_variable[type_name]
|
|
308
370
|
|
|
309
371
|
class AutoWired:
|
|
310
372
|
def __init__(self):
|
|
@@ -416,24 +478,12 @@ class FMUContainer:
|
|
|
416
478
|
# Rules
|
|
417
479
|
self.inputs: Dict[str, ContainerInput] = {}
|
|
418
480
|
self.outputs: Dict[str, ContainerPort] = {}
|
|
419
|
-
self.
|
|
481
|
+
self.links: Dict[ContainerPort, Link] = {}
|
|
420
482
|
|
|
421
483
|
self.rules: Dict[ContainerPort, str] = {}
|
|
422
484
|
self.start_values: Dict[ContainerPort, str] = {}
|
|
423
485
|
|
|
424
|
-
|
|
425
|
-
def convert_type_name(self, type_name: str) -> str:
|
|
426
|
-
if self.fmi_version == 2:
|
|
427
|
-
table = {}
|
|
428
|
-
elif self.fmi_version == 3:
|
|
429
|
-
table = {}
|
|
430
|
-
else:
|
|
431
|
-
table = {}
|
|
432
|
-
|
|
433
|
-
try:
|
|
434
|
-
return table[type_name]
|
|
435
|
-
except KeyError:
|
|
436
|
-
return type_name
|
|
486
|
+
self.vr_table = ValueReferenceTable()
|
|
437
487
|
|
|
438
488
|
def get_fmu(self, fmu_filename: str) -> EmbeddedFMU:
|
|
439
489
|
if fmu_filename in self.involved_fmu:
|
|
@@ -442,7 +492,7 @@ class FMUContainer:
|
|
|
442
492
|
try:
|
|
443
493
|
fmu = EmbeddedFMU(self.fmu_directory / fmu_filename)
|
|
444
494
|
if fmu.fmi_version > self.fmi_version:
|
|
445
|
-
logger.
|
|
495
|
+
logger.warning(f"Try to embed FMU-{fmu.fmi_version} into container FMI-{self.fmi_version}")
|
|
446
496
|
self.involved_fmu[fmu.name] = fmu
|
|
447
497
|
|
|
448
498
|
logger.debug(f"Adding FMU #{len(self.involved_fmu)}: {fmu}")
|
|
@@ -506,16 +556,17 @@ class FMUContainer:
|
|
|
506
556
|
def add_link(self, from_fmu_filename: str, from_port_name: str, to_fmu_filename: str, to_port_name: str):
|
|
507
557
|
cport_from = ContainerPort(self.get_fmu(from_fmu_filename), from_port_name)
|
|
508
558
|
try:
|
|
509
|
-
local = self.
|
|
559
|
+
local = self.links[cport_from]
|
|
510
560
|
except KeyError:
|
|
511
|
-
local =
|
|
561
|
+
local = Link(cport_from)
|
|
512
562
|
|
|
513
563
|
cport_to = ContainerPort(self.get_fmu(to_fmu_filename), to_port_name)
|
|
514
564
|
local.add_target(cport_to) # Causality is check in the add() function
|
|
515
565
|
|
|
566
|
+
logger.debug(f"LINK: {cport_from} -> {cport_to}")
|
|
516
567
|
self.mark_ruled(cport_from, 'LINK')
|
|
517
568
|
self.mark_ruled(cport_to, 'LINK')
|
|
518
|
-
self.
|
|
569
|
+
self.links[cport_from] = local
|
|
519
570
|
|
|
520
571
|
def add_start_value(self, fmu_filename: str, port_name: str, value: str):
|
|
521
572
|
cport = ContainerPort(self.get_fmu(fmu_filename), port_name)
|
|
@@ -649,8 +700,6 @@ class FMUContainer:
|
|
|
649
700
|
self.make_fmu_cleanup(base_directory)
|
|
650
701
|
|
|
651
702
|
def make_fmu_xml(self, xml_file, step_size: float, profiling: bool):
|
|
652
|
-
vr_table = ValueReferenceTable()
|
|
653
|
-
|
|
654
703
|
timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
655
704
|
guid = str(uuid.uuid4())
|
|
656
705
|
embedded_fmu = ", ".join([fmu_name for fmu_name in self.involved_fmu])
|
|
@@ -696,61 +745,86 @@ class FMUContainer:
|
|
|
696
745
|
start_time=self.start_time, stop_time=self.stop_time,
|
|
697
746
|
step_size=step_size))
|
|
698
747
|
|
|
699
|
-
vr_time = vr_table.add_vr("real64")
|
|
748
|
+
vr_time = self.vr_table.add_vr("real64", local=True)
|
|
700
749
|
logger.debug(f"Time vr = {vr_time}")
|
|
750
|
+
|
|
701
751
|
if profiling:
|
|
702
752
|
for fmu in self.involved_fmu.values():
|
|
703
|
-
vr = vr_table.add_vr("real64")
|
|
753
|
+
vr = self.vr_table.add_vr("real64", local=True)
|
|
704
754
|
port = EmbeddedFMUPort("real64", {"valueReference": vr,
|
|
705
755
|
"name": f"container.{fmu.id}.rt_ratio",
|
|
706
756
|
"description": f"RT ratio for embedded FMU '{fmu.name}'"})
|
|
707
757
|
print(f" {port.xml(vr, fmi_version=self.fmi_version)}", file=xml_file)
|
|
708
758
|
|
|
759
|
+
index_offset = 2 # index of output ports. Start at 2 to skip "time" port
|
|
760
|
+
|
|
709
761
|
# Local variable should be first to ensure to attribute them the lowest VR.
|
|
710
|
-
for
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
762
|
+
for link in self.links.values():
|
|
763
|
+
self.vr_table.set_link_vr(link)
|
|
764
|
+
port_local_def = link.cport_from.port.xml(link.vr, name=link.name, causality='local',
|
|
765
|
+
fmi_version=self.fmi_version)
|
|
766
|
+
if port_local_def:
|
|
767
|
+
print(f" {port_local_def}", file=xml_file)
|
|
768
|
+
index_offset += 1
|
|
717
769
|
|
|
718
770
|
for input_port_name, input_port in self.inputs.items():
|
|
719
|
-
vr = vr_table.add_vr(input_port.type_name)
|
|
771
|
+
input_port.vr = self.vr_table.add_vr(input_port.type_name)
|
|
720
772
|
# Get Start and XML from first connected input
|
|
721
773
|
start = self.start_values.get(input_port.cport_list[0], None)
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
774
|
+
port_input_def = input_port.cport_list[0].port.xml(input_port.vr, name=input_port_name,
|
|
775
|
+
start=start, fmi_version=self.fmi_version)
|
|
776
|
+
if port_input_def:
|
|
777
|
+
print(f" {port_input_def}", file=xml_file)
|
|
778
|
+
index_offset += 1
|
|
779
|
+
|
|
780
|
+
for output_port_name, output_port in self.outputs.items():
|
|
781
|
+
output_port.vr = self.vr_table.add_vr(output_port)
|
|
782
|
+
port_output_def = output_port.port.xml(output_port.vr, name=output_port_name,
|
|
783
|
+
fmi_version=self.fmi_version)
|
|
784
|
+
if port_output_def:
|
|
785
|
+
print(f" {port_output_def}", file=xml_file)
|
|
729
786
|
|
|
730
787
|
if self.fmi_version == 2:
|
|
731
|
-
self.make_fmu_xml_epilog_2(xml_file)
|
|
788
|
+
self.make_fmu_xml_epilog_2(xml_file, index_offset)
|
|
732
789
|
elif self.fmi_version == 3:
|
|
733
790
|
self.make_fmu_xml_epilog_3(xml_file)
|
|
734
791
|
|
|
735
|
-
def make_fmu_xml_epilog_2(self, xml_file):
|
|
736
|
-
xml_file.write(
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
xml_file.write(" </Outputs>\n <InitialUnknowns>\n")
|
|
742
|
-
|
|
743
|
-
for i, _ in enumerate(self.outputs.keys()):
|
|
744
|
-
print(f' <Unknown index="{index_offset+i}"/>', file=xml_file)
|
|
792
|
+
def make_fmu_xml_epilog_2(self, xml_file, index_offset):
|
|
793
|
+
xml_file.write(" </ModelVariables>\n"
|
|
794
|
+
"\n"
|
|
795
|
+
" <ModelStructure>\n"
|
|
796
|
+
" <Outputs>\n")
|
|
745
797
|
|
|
746
|
-
|
|
798
|
+
index = index_offset
|
|
799
|
+
for output in self.outputs.values():
|
|
800
|
+
if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
|
|
801
|
+
print(f' <Unknown index="{index}"/>', file=xml_file)
|
|
802
|
+
index += 1
|
|
803
|
+
xml_file.write(" </Outputs>\n"
|
|
804
|
+
" <InitialUnknowns>\n")
|
|
805
|
+
index = index_offset
|
|
806
|
+
for output in self.outputs.values():
|
|
807
|
+
if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[2]:
|
|
808
|
+
print(f' <Unknown index="{index}"/>', file=xml_file)
|
|
809
|
+
index += 1
|
|
810
|
+
xml_file.write(" </InitialUnknowns>\n"
|
|
811
|
+
" </ModelStructure>\n"
|
|
812
|
+
"\n"
|
|
813
|
+
"</fmiModelDescription>")
|
|
747
814
|
|
|
748
815
|
def make_fmu_xml_epilog_3(self, xml_file):
|
|
749
|
-
xml_file.write(
|
|
816
|
+
xml_file.write(" </ModelVariables>\n"
|
|
817
|
+
"\n"
|
|
818
|
+
" <ModelStructure>\n")
|
|
750
819
|
for output in self.outputs.values():
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
820
|
+
if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[3]:
|
|
821
|
+
print(f' <Output valueReference="{output.vr}"/>', file=xml_file)
|
|
822
|
+
for output in self.outputs.values():
|
|
823
|
+
if output.port.type_name in EmbeddedFMUPort.CONTAINER_TO_FMI[3]:
|
|
824
|
+
print(f' <InitialUnknown valueReference="{output.vr}"/>', file=xml_file)
|
|
825
|
+
xml_file.write(" </ModelStructure>\n"
|
|
826
|
+
"\n"
|
|
827
|
+
"</fmiModelDescription>")
|
|
754
828
|
|
|
755
829
|
def make_fmu_txt(self, txt_file, step_size: float, mt: bool, profiling: bool, sequential: bool):
|
|
756
830
|
print("# Container flags <MT> <Profiling> <Sequential>", file=txt_file)
|
|
@@ -775,12 +849,13 @@ class FMUContainer:
|
|
|
775
849
|
inputs_fmu_per_type: Dict[str, Dict[str, Dict[ContainerPort, int]]] = {} # [type][fmu]
|
|
776
850
|
start_values_fmu_per_type = {}
|
|
777
851
|
outputs_fmu_per_type = {}
|
|
778
|
-
|
|
852
|
+
local_per_type: Dict[str, List[int]] = {}
|
|
853
|
+
links_per_fmu: Dict[str, List[Link]] = {}
|
|
779
854
|
|
|
780
855
|
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
781
856
|
inputs_per_type[type_name] = []
|
|
782
857
|
outputs_per_type[type_name] = []
|
|
783
|
-
|
|
858
|
+
local_per_type[type_name] = []
|
|
784
859
|
|
|
785
860
|
inputs_fmu_per_type[type_name] = {}
|
|
786
861
|
start_values_fmu_per_type[type_name] = {}
|
|
@@ -795,78 +870,88 @@ class FMUContainer:
|
|
|
795
870
|
# Inputs
|
|
796
871
|
for input_port_name, input_port in self.inputs.items():
|
|
797
872
|
inputs_per_type[input_port.type_name].append(input_port)
|
|
798
|
-
for
|
|
799
|
-
start_values_fmu_per_type[
|
|
873
|
+
for input_port, value in self.start_values.items():
|
|
874
|
+
start_values_fmu_per_type[input_port.port.type_name][input_port.fmu.name][input_port] = value
|
|
800
875
|
# Outputs
|
|
801
|
-
for output_port_name,
|
|
802
|
-
outputs_per_type[
|
|
803
|
-
#
|
|
804
|
-
for
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
876
|
+
for output_port_name, output_port in self.outputs.items():
|
|
877
|
+
outputs_per_type[output_port.port.type_name].append(output_port)
|
|
878
|
+
# Links
|
|
879
|
+
for link in self.links.values():
|
|
880
|
+
local_per_type[link.cport_from.port.type_name].append(link.vr)
|
|
881
|
+
outputs_fmu_per_type[link.cport_from.port.type_name][link.cport_from.fmu.name][link.cport_from] = link.vr
|
|
882
|
+
for cport_to in link.cport_to_list:
|
|
883
|
+
if cport_to.port.type_name == link.cport_from.port.type_name:
|
|
884
|
+
inputs_fmu_per_type[cport_to.port.type_name][cport_to.fmu.name][cport_to] = link.vr
|
|
885
|
+
else:
|
|
886
|
+
local_per_type[cport_to.port.type_name].append(link.vr_converted[cport_to.port.type_name])
|
|
887
|
+
links_per_fmu.setdefault(link.cport_from.fmu.name, []).append(link)
|
|
888
|
+
inputs_fmu_per_type[cport_to.port.type_name][cport_to.fmu.name][cport_to] = link.vr_converted[cport_to.port.type_name]
|
|
889
|
+
|
|
810
890
|
|
|
811
891
|
print(f"# NB local variables:", ", ".join(EmbeddedFMUPort.ALL_TYPES), file=txt_file)
|
|
812
|
-
nb_local = []
|
|
813
|
-
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
814
|
-
nb = len(locals_per_type[type_name])
|
|
815
|
-
if type_name == "real64":
|
|
816
|
-
nb += 1 # reserver a slot for "time"
|
|
817
|
-
if profiling:
|
|
818
|
-
nb += len(self.involved_fmu)
|
|
819
|
-
nb_local.append(str(nb))
|
|
892
|
+
nb_local = [f"{self.vr_table.nb_local(type_name)}" for type_name in EmbeddedFMUPort.ALL_TYPES]
|
|
820
893
|
print(" ".join(nb_local), file=txt_file, end='')
|
|
821
894
|
print("", file=txt_file)
|
|
822
895
|
|
|
823
896
|
print("# CONTAINER I/O: <VR> <NB> <FMU_INDEX> <FMU_VR> [<FMU_INDEX> <FMU_VR>]", file=txt_file)
|
|
824
897
|
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
825
|
-
print(f"# {type_name}", file=txt_file)
|
|
826
|
-
|
|
898
|
+
print(f"# {type_name}" , file=txt_file)
|
|
899
|
+
nb_local = (len(inputs_per_type[type_name]) +
|
|
900
|
+
len(outputs_per_type[type_name]) +
|
|
901
|
+
self.vr_table.nb_local(type_name))
|
|
827
902
|
nb_input_link = 0
|
|
828
903
|
for input_port in inputs_per_type[type_name]:
|
|
829
904
|
nb_input_link += len(input_port.cport_list) - 1
|
|
830
|
-
|
|
905
|
+
print(f"{nb_local} {nb_local + nb_input_link}", file=txt_file)
|
|
831
906
|
if type_name == "real64":
|
|
832
|
-
nb += 1 # reserver a slot for "time"
|
|
833
|
-
if profiling:
|
|
834
|
-
nb += len(self.involved_fmu)
|
|
835
|
-
print(f"{nb} {nb + nb_input_link}", file=txt_file)
|
|
836
907
|
print(f"0 1 -1 0", file=txt_file) # Time slot
|
|
837
908
|
if profiling:
|
|
838
909
|
for profiling_port, _ in enumerate(self.involved_fmu.values()):
|
|
839
|
-
print(f"{profiling_port+1} 1 -2 {profiling_port+1}", file=txt_file)
|
|
840
|
-
|
|
841
|
-
print(f"{nb} {nb+nb_input_link}", file=txt_file)
|
|
910
|
+
print(f"{profiling_port + 1} 1 -2 {profiling_port + 1}", file=txt_file)
|
|
911
|
+
|
|
842
912
|
for input_port in inputs_per_type[type_name]:
|
|
843
913
|
cport_string = [f"{fmu_rank[cport.fmu.name]} {cport.port.vr}" for cport in input_port.cport_list]
|
|
844
914
|
print(f"{input_port.vr} {len(input_port.cport_list)}", " ".join(cport_string), file=txt_file)
|
|
845
|
-
for
|
|
846
|
-
print(f"{
|
|
847
|
-
for
|
|
848
|
-
print(f"{
|
|
915
|
+
for output_port in outputs_per_type[type_name]:
|
|
916
|
+
print(f"{output_port.vr} 1 {fmu_rank[output_port.fmu.name]} {output_port.port.vr}", file=txt_file)
|
|
917
|
+
for local_vr in local_per_type[type_name]:
|
|
918
|
+
print(f"{local_vr} 1 -1 {local_vr & 0xFFFFFF}", file=txt_file)
|
|
849
919
|
|
|
850
920
|
# LINKS
|
|
851
921
|
for fmu in self.involved_fmu.values():
|
|
852
922
|
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
853
923
|
print(f"# Inputs of {fmu.name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
|
|
854
924
|
print(len(inputs_fmu_per_type[type_name][fmu.name]), file=txt_file)
|
|
855
|
-
for
|
|
856
|
-
print(f"{vr} {
|
|
925
|
+
for input_port, vr in inputs_fmu_per_type[type_name][fmu.name].items():
|
|
926
|
+
print(f"{vr} {input_port.port.vr}", file=txt_file)
|
|
857
927
|
|
|
858
928
|
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
859
929
|
print(f"# Start values of {fmu.name} - {type_name}: <FMU_VR> <RESET> <VALUE>", file=txt_file)
|
|
860
930
|
print(len(start_values_fmu_per_type[type_name][fmu.name]), file=txt_file)
|
|
861
|
-
for
|
|
862
|
-
reset = 1 if
|
|
863
|
-
print(f"{
|
|
931
|
+
for input_port, value in start_values_fmu_per_type[type_name][fmu.name].items():
|
|
932
|
+
reset = 1 if input_port.port.causality == "input" else 0
|
|
933
|
+
print(f"{input_port.port.vr} {reset} {value}", file=txt_file)
|
|
864
934
|
|
|
865
935
|
for type_name in EmbeddedFMUPort.ALL_TYPES:
|
|
866
936
|
print(f"# Outputs of {fmu.name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
|
|
867
937
|
print(len(outputs_fmu_per_type[type_name][fmu.name]), file=txt_file)
|
|
868
|
-
for
|
|
869
|
-
print(f"{vr} {
|
|
938
|
+
for output_port, vr in outputs_fmu_per_type[type_name][fmu.name].items():
|
|
939
|
+
print(f"{vr} {output_port.port.vr}", file=txt_file)
|
|
940
|
+
|
|
941
|
+
print(f"# Conversion table of {fmu.name}: <VR_FROM> <VR_TO> <CONVERSION>", file=txt_file)
|
|
942
|
+
try:
|
|
943
|
+
nb = 0
|
|
944
|
+
for link in links_per_fmu[fmu.name]:
|
|
945
|
+
nb += len(link.vr_converted)
|
|
946
|
+
print(f"{nb}", file=txt_file)
|
|
947
|
+
for link in links_per_fmu[fmu.name]:
|
|
948
|
+
for cport_to in link.cport_to_list:
|
|
949
|
+
conversion = link.get_conversion(cport_to)
|
|
950
|
+
if conversion:
|
|
951
|
+
print(f"{link.vr} {link.vr_converted[cport_to.port.type_name]} {conversion}",
|
|
952
|
+
file=txt_file)
|
|
953
|
+
except KeyError:
|
|
954
|
+
print("0", file=txt_file)
|
|
870
955
|
|
|
871
956
|
@staticmethod
|
|
872
957
|
def long_path(path: Union[str, Path]) -> str:
|
|
@@ -881,7 +966,7 @@ class FMUContainer:
|
|
|
881
966
|
logger.debug(f"Copying {origin} in {destination}")
|
|
882
967
|
shutil.copy(origin, destination)
|
|
883
968
|
|
|
884
|
-
def get_bindir_and_suffixe(self) ->
|
|
969
|
+
def get_bindir_and_suffixe(self) -> Tuple[str, str, str]:
|
|
885
970
|
suffixes = {
|
|
886
971
|
"Windows": "dll",
|
|
887
972
|
"Linux": "so",
|
fmu_manipulation_toolbox/gui.py
CHANGED
|
@@ -2,22 +2,24 @@ import os.path
|
|
|
2
2
|
import sys
|
|
3
3
|
import textwrap
|
|
4
4
|
|
|
5
|
-
from PySide6.QtCore import Qt,
|
|
5
|
+
from PySide6.QtCore import Qt, QUrl, QDir, Signal, QPoint, QModelIndex
|
|
6
6
|
from PySide6.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
|
|
7
7
|
QTextBrowser, QInputDialog, QMenu, QTreeView, QAbstractItemView, QTabWidget, QTableView,
|
|
8
8
|
QCheckBox)
|
|
9
9
|
from PySide6.QtGui import (QPixmap, QTextCursor, QStandardItem, QIcon, QDesktopServices, QAction,
|
|
10
10
|
QPainter, QColor, QImage, QStandardItemModel)
|
|
11
11
|
from functools import partial
|
|
12
|
-
from typing import Optional
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
from .gui_style import gui_style, log_color
|
|
15
15
|
from .operations import *
|
|
16
16
|
from .assembly import Assembly, AssemblyNode
|
|
17
17
|
from .checker import checker_list
|
|
18
18
|
from .help import Help
|
|
19
19
|
from .version import __version__ as version
|
|
20
20
|
|
|
21
|
+
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class DropZoneWidget(QLabel):
|
|
23
25
|
WIDTH = 150
|
|
@@ -51,7 +53,7 @@ class DropZoneWidget(QLabel):
|
|
|
51
53
|
try:
|
|
52
54
|
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
53
55
|
except IndexError:
|
|
54
|
-
|
|
56
|
+
logger.error("Please select a regular file.")
|
|
55
57
|
return
|
|
56
58
|
self.set_fmu(file_path)
|
|
57
59
|
event.accept()
|
|
@@ -98,54 +100,41 @@ class DropZoneWidget(QLabel):
|
|
|
98
100
|
self.fmu = FMU(filename)
|
|
99
101
|
self.set_image(os.path.join(self.fmu.tmp_directory, "model.png"))
|
|
100
102
|
except Exception as e:
|
|
101
|
-
|
|
103
|
+
logger.error(f"Cannot load this FMU: {e}")
|
|
102
104
|
self.set_image(None)
|
|
103
105
|
self.fmu = None
|
|
104
106
|
self.clicked.emit()
|
|
105
107
|
|
|
106
108
|
|
|
109
|
+
class LogHandler(logging.Handler):
|
|
110
|
+
LOG_COLOR = {
|
|
111
|
+
logging.DEBUG: QColor(log_color["DEBUG"]),
|
|
112
|
+
logging.INFO: QColor(log_color["INFO"]),
|
|
113
|
+
logging.WARNING: QColor(log_color["WARNING"]),
|
|
114
|
+
logging.ERROR: QColor(log_color["ERROR"]),
|
|
115
|
+
logging.CRITICAL: QColor(log_color["CRITICAL"]),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
def __init__(self, text_browser, level):
|
|
119
|
+
super().__init__(level)
|
|
120
|
+
self.text_browser: QTextBrowser = text_browser
|
|
121
|
+
logger.addHandler(self)
|
|
122
|
+
logger.setLevel(level)
|
|
123
|
+
|
|
124
|
+
def emit(self, record) -> None:
|
|
125
|
+
self.text_browser.setTextColor(self.LOG_COLOR[record.levelno])
|
|
126
|
+
self.text_browser.insertPlainText(record.msg+"\n")
|
|
127
|
+
|
|
128
|
+
|
|
107
129
|
class LogWidget(QTextBrowser):
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
_stderr = None
|
|
111
|
-
messageWritten = Signal(str)
|
|
112
|
-
|
|
113
|
-
def __init__(self):
|
|
114
|
-
super().__init__()
|
|
115
|
-
|
|
116
|
-
def flush(self):
|
|
117
|
-
pass
|
|
118
|
-
|
|
119
|
-
@staticmethod
|
|
120
|
-
def fileno():
|
|
121
|
-
return -1
|
|
122
|
-
|
|
123
|
-
def write(self, msg):
|
|
124
|
-
if not self.signalsBlocked():
|
|
125
|
-
self.messageWritten.emit(msg)
|
|
126
|
-
|
|
127
|
-
@staticmethod
|
|
128
|
-
def stdout():
|
|
129
|
-
if not LogWidget.XStream._stdout:
|
|
130
|
-
LogWidget.XStream._stdout = LogWidget.XStream()
|
|
131
|
-
sys.stdout = LogWidget.XStream._stdout
|
|
132
|
-
return LogWidget.XStream._stdout
|
|
133
|
-
|
|
134
|
-
@staticmethod
|
|
135
|
-
def stderr():
|
|
136
|
-
if not LogWidget.XStream._stderr:
|
|
137
|
-
LogWidget.XStream._stderr = LogWidget.XStream()
|
|
138
|
-
sys.stderr = LogWidget.XStream._stderr
|
|
139
|
-
return LogWidget.XStream._stderr
|
|
130
|
+
def __init__(self, parent=None, level=logging.INFO):
|
|
131
|
+
super().__init__(parent)
|
|
140
132
|
|
|
141
|
-
def __init__(self):
|
|
142
|
-
super().__init__()
|
|
143
133
|
self.setMinimumWidth(900)
|
|
144
134
|
self.setMinimumHeight(500)
|
|
145
135
|
self.setSearchPaths([os.path.join(os.path.dirname(__file__), "resources")])
|
|
146
136
|
self.insertHtml('<center><img src="fmu_manipulation_toolbox.png"/></center><br/>')
|
|
147
|
-
|
|
148
|
-
LogWidget.XStream.stderr().messageWritten.connect(self.insertPlainText)
|
|
137
|
+
self.log_handler = LogHandler(self, logging.DEBUG)
|
|
149
138
|
|
|
150
139
|
def loadResource(self, _, name):
|
|
151
140
|
image_path = os.path.join(os.path.dirname(__file__), "resources", name.toString())
|
|
@@ -255,10 +244,10 @@ class AssemblyTreeWidget(QTreeView):
|
|
|
255
244
|
|
|
256
245
|
def removeRows(self, row, count, parent=QModelIndex()):
|
|
257
246
|
if not self.dnd_target_node:
|
|
258
|
-
|
|
247
|
+
logger.error("NO DROP NODE!?")
|
|
259
248
|
|
|
260
249
|
source_index = self.itemFromIndex(parent).child(row, 0).data(role=Qt.ItemDataRole.UserRole+1)
|
|
261
|
-
|
|
250
|
+
logger.debug(f"{source_index} ==> {self.dnd_target_node.name}")
|
|
262
251
|
|
|
263
252
|
self.dnd_target_node = None
|
|
264
253
|
return super().removeRows(row, count, parent)
|
|
@@ -289,7 +278,7 @@ class AssemblyTreeWidget(QTreeView):
|
|
|
289
278
|
|
|
290
279
|
def setTopIndex(self):
|
|
291
280
|
topIndex = self.model.index(0, 0, self.rootIndex())
|
|
292
|
-
|
|
281
|
+
logger.debug(topIndex.isValid(), topIndex.model())
|
|
293
282
|
if topIndex.isValid():
|
|
294
283
|
self.setCurrentIndex(topIndex)
|
|
295
284
|
if self.layoutCheck:
|
|
@@ -318,9 +307,9 @@ class AssemblyTreeWidget(QTreeView):
|
|
|
318
307
|
try:
|
|
319
308
|
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
320
309
|
except IndexError:
|
|
321
|
-
|
|
310
|
+
logger.error("Please select a regular file.")
|
|
322
311
|
return
|
|
323
|
-
|
|
312
|
+
logger.debug(f"DROP: {file_path}")
|
|
324
313
|
event.accept()
|
|
325
314
|
else:
|
|
326
315
|
event.ignore()
|
|
@@ -518,7 +507,7 @@ class MainWindow(WindowWithLayout):
|
|
|
518
507
|
"FMU files (*.fmu)")
|
|
519
508
|
if ok and filename:
|
|
520
509
|
fmu.repack(filename)
|
|
521
|
-
|
|
510
|
+
logger.info(f"Modified version saved as {filename}.")
|
|
522
511
|
|
|
523
512
|
def save_log(self):
|
|
524
513
|
if self.dropped_fmu.fmu:
|
|
@@ -533,7 +522,7 @@ class MainWindow(WindowWithLayout):
|
|
|
533
522
|
with open(filename, "wt") as file:
|
|
534
523
|
file.write(str(self.log_widget.toPlainText()))
|
|
535
524
|
except Exception as e:
|
|
536
|
-
|
|
525
|
+
logger.error(f"{e}")
|
|
537
526
|
|
|
538
527
|
def add_operation(self, name, usage, severity, operation, x, y, prompt=None, prompt_file=None, arg=None,
|
|
539
528
|
func=None):
|
|
@@ -603,18 +592,18 @@ class MainWindow(WindowWithLayout):
|
|
|
603
592
|
if self.dropped_fmu.fmu:
|
|
604
593
|
self.log_widget.moveCursor(QTextCursor.MoveOperation.End)
|
|
605
594
|
fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
|
|
606
|
-
|
|
595
|
+
logger.info('-' * 100)
|
|
607
596
|
self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
|
|
608
597
|
|
|
609
598
|
apply_on = self.filter_list.get()
|
|
610
599
|
if apply_on:
|
|
611
600
|
self.log_widget.insertHtml(f"<i>Applied only for ports with causality = " +
|
|
612
601
|
", ".join(apply_on) + "</i><br>")
|
|
613
|
-
|
|
602
|
+
logger.info('-' * 100)
|
|
614
603
|
try:
|
|
615
604
|
self.dropped_fmu.fmu.apply_operation(operation, apply_on=apply_on)
|
|
616
605
|
except Exception as e:
|
|
617
|
-
|
|
606
|
+
logger.error(f"{e}")
|
|
618
607
|
|
|
619
608
|
scroll_bar = self.log_widget.verticalScrollBar()
|
|
620
609
|
scroll_bar.setValue(scroll_bar.maximum())
|
|
@@ -712,7 +701,7 @@ class ContainerWindow(WindowWithLayout):
|
|
|
712
701
|
self.last_directory = os.path.dirname(filename)
|
|
713
702
|
self.assembly_tree.load_container(filename)
|
|
714
703
|
except Exception as e:
|
|
715
|
-
|
|
704
|
+
logger.error(e)
|
|
716
705
|
|
|
717
706
|
|
|
718
707
|
class Application(QApplication):
|
|
@@ -724,8 +713,9 @@ Communicating with the FMU-developer and adapting the way the FMU is generated,
|
|
|
724
713
|
|
|
725
714
|
"""
|
|
726
715
|
def __init__(self, *args, **kwargs):
|
|
727
|
-
super().__init__(*args, **kwargs)
|
|
728
716
|
self.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.RoundPreferFloor)
|
|
717
|
+
super().__init__(*args, **kwargs)
|
|
718
|
+
|
|
729
719
|
|
|
730
720
|
QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), "resources"))
|
|
731
721
|
self.setStyleSheet(gui_style)
|
|
@@ -747,8 +737,8 @@ Communicating with the FMU-developer and adapting the way the FMU is generated,
|
|
|
747
737
|
def main():
|
|
748
738
|
application = Application(sys.argv)
|
|
749
739
|
|
|
750
|
-
|
|
751
|
-
|
|
740
|
+
logger.info(" " * 80 + f"Version {version}")
|
|
741
|
+
logger.info(application.__doc__)
|
|
752
742
|
|
|
753
743
|
sys.exit(application.exec())
|
|
754
744
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import csv
|
|
2
2
|
import html
|
|
3
|
+
import logging
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
6
|
import shutil
|
|
@@ -10,6 +11,7 @@ import hashlib
|
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import *
|
|
12
13
|
|
|
14
|
+
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
13
15
|
|
|
14
16
|
class FMU:
|
|
15
17
|
"""Unpack and Repack facilities for FMU package. Once unpacked, we can process Operation on
|
|
@@ -57,6 +59,7 @@ class FMUPort:
|
|
|
57
59
|
def __init__(self):
|
|
58
60
|
self.fmi_type = None
|
|
59
61
|
self.attrs_list: List[Dict] = []
|
|
62
|
+
self.dimension = None
|
|
60
63
|
|
|
61
64
|
def dict_level(self, nb):
|
|
62
65
|
return " ".join([f'{key}="{value}"' for key, value in self.attrs_list[nb].items()])
|
|
@@ -99,9 +102,6 @@ class FMUPort:
|
|
|
99
102
|
def push_attrs(self, attrs):
|
|
100
103
|
self.attrs_list.append(attrs)
|
|
101
104
|
|
|
102
|
-
def is_ready(self):
|
|
103
|
-
return self.fmi_type is not None
|
|
104
|
-
|
|
105
105
|
|
|
106
106
|
class FMUError(Exception):
|
|
107
107
|
def __init__(self, reason):
|
|
@@ -130,6 +130,7 @@ class Manipulation:
|
|
|
130
130
|
self.current_port_number: int = 0
|
|
131
131
|
self.port_translation: List[Optional[int]] = []
|
|
132
132
|
self.port_names_list: List[str] = []
|
|
133
|
+
self.port_removed_vr: Set[str] = set()
|
|
133
134
|
self.apply_on = None
|
|
134
135
|
|
|
135
136
|
@staticmethod
|
|
@@ -142,9 +143,11 @@ class Manipulation:
|
|
|
142
143
|
def handle_port(self):
|
|
143
144
|
causality = self.current_port.get('causality', 'local')
|
|
144
145
|
port_name = self.current_port['name']
|
|
146
|
+
vr = self.current_port['valueReference']
|
|
145
147
|
if not self.apply_on or causality in self.apply_on:
|
|
146
148
|
if self.operation.port_attrs(self.current_port):
|
|
147
|
-
self.remove_port(port_name)
|
|
149
|
+
self.remove_port(port_name, vr)
|
|
150
|
+
# Exception is raised by remove port !
|
|
148
151
|
else:
|
|
149
152
|
self.keep_port(port_name)
|
|
150
153
|
else: # Keep ScalarVariable as it is.
|
|
@@ -171,21 +174,16 @@ class Manipulation:
|
|
|
171
174
|
elif name == 'fmiModelDescription':
|
|
172
175
|
self.fmu.fmi_version = int(float(attrs["fmiVersion"]))
|
|
173
176
|
self.operation.fmi_attrs(attrs)
|
|
174
|
-
elif name == 'Unknown':
|
|
177
|
+
elif name == 'Unknown': # FMI-2.0 only
|
|
175
178
|
self.unknown_attrs(attrs)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
self.handle_port()
|
|
179
|
+
elif name == 'Output' or name == "ContinuousStateDerivative" or "InitialUnknown":
|
|
180
|
+
self.handle_structure(attrs)
|
|
179
181
|
|
|
180
182
|
except ManipulationSkipTag:
|
|
181
183
|
self.skip_until = name
|
|
182
184
|
return
|
|
183
185
|
|
|
184
|
-
if self.current_port:
|
|
185
|
-
if self.current_port.is_ready():
|
|
186
|
-
self.current_port.write_xml(self.fmu.fmi_version, self.out)
|
|
187
|
-
self.current_port = None
|
|
188
|
-
else: # re-copy tags
|
|
186
|
+
if self.current_port is None:
|
|
189
187
|
if attrs:
|
|
190
188
|
attrs_list = [f'{key}="{self.escape(value)}"' for (key, value) in attrs.items()]
|
|
191
189
|
print(f"<{name}", " ".join(attrs_list), ">", end='', file=self.out)
|
|
@@ -198,16 +196,25 @@ class Manipulation:
|
|
|
198
196
|
self.skip_until = None
|
|
199
197
|
return
|
|
200
198
|
else:
|
|
201
|
-
if name
|
|
199
|
+
if name == "ScalarVariable" or (self.fmu.fmi_version == 3 and name in FMU.FMI3_TYPES):
|
|
200
|
+
try:
|
|
201
|
+
self.handle_port()
|
|
202
|
+
self.current_port.write_xml(self.fmu.fmi_version, self.out)
|
|
203
|
+
except ManipulationSkipTag:
|
|
204
|
+
logger.info(f"Port '{self.current_port['name']}' is removed.")
|
|
205
|
+
self.current_port = None
|
|
206
|
+
|
|
207
|
+
elif self.current_port is None:
|
|
202
208
|
print(f"</{name}>", end='', file=self.out)
|
|
203
209
|
|
|
204
210
|
def char_data(self, data):
|
|
205
211
|
if not self.skip_until:
|
|
206
212
|
print(data, end='', file=self.out)
|
|
207
213
|
|
|
208
|
-
def remove_port(self, name):
|
|
214
|
+
def remove_port(self, name, vr):
|
|
209
215
|
self.port_names_list.append(name)
|
|
210
216
|
self.port_translation.append(None)
|
|
217
|
+
self.port_removed_vr.add(vr)
|
|
211
218
|
raise ManipulationSkipTag
|
|
212
219
|
|
|
213
220
|
def keep_port(self, name):
|
|
@@ -221,9 +228,29 @@ class Manipulation:
|
|
|
221
228
|
if new_index:
|
|
222
229
|
attrs['index'] = self.port_translation[int(attrs['index']) - 1]
|
|
223
230
|
else:
|
|
224
|
-
|
|
231
|
+
logger.warning(f"Removed port '{self.port_names_list[index]}' is involved in dependencies tree.")
|
|
225
232
|
raise ManipulationSkipTag
|
|
226
233
|
|
|
234
|
+
def handle_structure(self, attrs):
|
|
235
|
+
try:
|
|
236
|
+
vr = attrs['valueReference']
|
|
237
|
+
if vr in self.port_removed_vr:
|
|
238
|
+
raise ManipulationSkipTag
|
|
239
|
+
except KeyError:
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
dependencies = attrs['dependencies']
|
|
244
|
+
new_dependencies = []
|
|
245
|
+
for dependency in dependencies.split(" "):
|
|
246
|
+
if dependency in self.port_removed_vr:
|
|
247
|
+
logger.warning(f"Removed port 'vr={dependency}' was involved in dependencies tree.")
|
|
248
|
+
else:
|
|
249
|
+
new_dependencies.append(dependency)
|
|
250
|
+
attrs['dependencies'] = " ".join(new_dependencies)
|
|
251
|
+
except KeyError:
|
|
252
|
+
return
|
|
253
|
+
|
|
227
254
|
def manipulate(self, descriptor_filename, apply_on=None):
|
|
228
255
|
self.apply_on = apply_on
|
|
229
256
|
with open(self.output_filename, "w", encoding="utf-8") as self.out, open(descriptor_filename, "rb") as file:
|
|
@@ -347,7 +374,7 @@ class OperationAddRemotingWinAbstract(OperationAbstract):
|
|
|
347
374
|
return f"Add '{self.bitness_to}' remoting on '{self.bitness_from}' FMU"
|
|
348
375
|
|
|
349
376
|
def fmi_attrs(self, attrs):
|
|
350
|
-
if not attrs["
|
|
377
|
+
if not attrs["fmiVersion"] == "2.0":
|
|
351
378
|
raise OperationError(f"Adding remoting is only available for FMI-2.0")
|
|
352
379
|
|
|
353
380
|
def cosimulation_attrs(self, attrs):
|
|
@@ -360,7 +387,7 @@ class OperationAddRemotingWinAbstract(OperationAbstract):
|
|
|
360
387
|
raise OperationError(f"{self.bitness_from} interface does not exist")
|
|
361
388
|
|
|
362
389
|
if os.path.isdir(fmu_bin[self.bitness_to]):
|
|
363
|
-
|
|
390
|
+
logger.info(f"{self.bitness_to} already exists. Add front-end.")
|
|
364
391
|
shutil.move(os.path.join(fmu_bin[self.bitness_to], attrs['modelIdentifier'] + ".dll"),
|
|
365
392
|
os.path.join(fmu_bin[self.bitness_to], attrs['modelIdentifier'] + "-remoted.dll"))
|
|
366
393
|
else:
|
|
@@ -440,31 +467,31 @@ class OperationSummary(OperationAbstract):
|
|
|
440
467
|
return f"FMU Summary"
|
|
441
468
|
|
|
442
469
|
def fmi_attrs(self, attrs):
|
|
443
|
-
|
|
444
|
-
|
|
470
|
+
logger.info(f"| fmu filename = {self.fmu.fmu_filename}")
|
|
471
|
+
logger.info(f"| temporary directory = {self.fmu.tmp_directory}")
|
|
445
472
|
hash_md5 = hashlib.md5()
|
|
446
473
|
with open(self.fmu.fmu_filename, "rb") as f:
|
|
447
474
|
for chunk in iter(lambda: f.read(4096), b""):
|
|
448
475
|
hash_md5.update(chunk)
|
|
449
476
|
digest = hash_md5.hexdigest()
|
|
450
|
-
|
|
477
|
+
logger.info(f"| MD5Sum = {digest}")
|
|
451
478
|
|
|
452
|
-
|
|
479
|
+
logger.info(f"|\n| FMI properties: ")
|
|
453
480
|
for (k, v) in attrs.items():
|
|
454
|
-
|
|
455
|
-
|
|
481
|
+
logger.info(f"| - {k} = {v}")
|
|
482
|
+
logger.info(f"|")
|
|
456
483
|
|
|
457
484
|
def cosimulation_attrs(self, attrs):
|
|
458
|
-
|
|
485
|
+
logger.info("| Co-Simulation capabilities: ")
|
|
459
486
|
for (k, v) in attrs.items():
|
|
460
|
-
|
|
461
|
-
|
|
487
|
+
logger.info(f"| - {k} = {v}")
|
|
488
|
+
logger.info(f"|")
|
|
462
489
|
|
|
463
490
|
def experiment_attrs(self, attrs):
|
|
464
|
-
|
|
491
|
+
logger.info("| Default Experiment values: ")
|
|
465
492
|
for (k, v) in attrs.items():
|
|
466
|
-
|
|
467
|
-
|
|
493
|
+
logger.info(f"| - {k} = {v}")
|
|
494
|
+
logger.info(f"|")
|
|
468
495
|
|
|
469
496
|
def port_attrs(self, fmu_port) -> int:
|
|
470
497
|
causality = fmu_port.get("causality", "local")
|
|
@@ -477,33 +504,33 @@ class OperationSummary(OperationAbstract):
|
|
|
477
504
|
return 0
|
|
478
505
|
|
|
479
506
|
def closure(self):
|
|
480
|
-
|
|
507
|
+
logger.info("| Supported platforms: ")
|
|
481
508
|
try:
|
|
482
509
|
for platform in os.listdir(os.path.join(self.fmu.tmp_directory, "binaries")):
|
|
483
|
-
|
|
510
|
+
logger.info(f"| - {platform}")
|
|
484
511
|
except FileNotFoundError:
|
|
485
512
|
pass # no binaries
|
|
486
513
|
|
|
487
514
|
if os.path.isdir(os.path.join(self.fmu.tmp_directory, "sources")):
|
|
488
|
-
|
|
515
|
+
logger.info(f"| - RT (sources available)")
|
|
489
516
|
|
|
490
517
|
resource_dir = os.path.join(self.fmu.tmp_directory, "resources")
|
|
491
518
|
if os.path.isdir(resource_dir):
|
|
492
|
-
|
|
519
|
+
logger.info("|\n| Embedded resources:")
|
|
493
520
|
for resource in os.listdir(resource_dir):
|
|
494
|
-
|
|
521
|
+
logger.info(f"| - {resource}")
|
|
495
522
|
|
|
496
523
|
extra_dir = os.path.join(self.fmu.tmp_directory, "extra")
|
|
497
524
|
if os.path.isdir(extra_dir):
|
|
498
|
-
|
|
525
|
+
logger.info("|\n| Additional (meta-)data:")
|
|
499
526
|
for extra in os.listdir(extra_dir):
|
|
500
|
-
|
|
527
|
+
logger.info(f"| - {extra}")
|
|
501
528
|
|
|
502
|
-
|
|
529
|
+
logger.info("|\n| Number of ports")
|
|
503
530
|
for causality, nb_ports in self.nb_port_per_causality.items():
|
|
504
|
-
|
|
531
|
+
logger.info(f"| {causality} : {nb_ports}")
|
|
505
532
|
|
|
506
|
-
|
|
533
|
+
logger.info("|\n| [End of report]")
|
|
507
534
|
|
|
508
535
|
|
|
509
536
|
class OperationRemoveSources(OperationAbstract):
|
|
@@ -514,7 +541,7 @@ class OperationRemoveSources(OperationAbstract):
|
|
|
514
541
|
try:
|
|
515
542
|
shutil.rmtree(os.path.join(self.fmu.tmp_directory, "sources"))
|
|
516
543
|
except FileNotFoundError:
|
|
517
|
-
|
|
544
|
+
logger.info("This FMU does not embed sources.")
|
|
518
545
|
|
|
519
546
|
|
|
520
547
|
class OperationTrimUntil(OperationAbstract):
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
{fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmu_manipulation_toolbox
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9rc3
|
|
4
4
|
Summary: FMU Manipulation Toolbox is a python application for modifying Functional Mock-up Units (FMUs) without recompilation or bundling them into FMU Containers
|
|
5
5
|
Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
|
|
6
6
|
Author: Nicolas.LAURENT@Renault.com
|
{fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/RECORD
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
fmu_manipulation_toolbox/__init__.py,sha256=
|
|
2
|
-
fmu_manipulation_toolbox/__main__.py,sha256=
|
|
3
|
-
fmu_manipulation_toolbox/__version__.py,sha256=
|
|
4
|
-
fmu_manipulation_toolbox/assembly.py,sha256=
|
|
5
|
-
fmu_manipulation_toolbox/checker.py,sha256=
|
|
6
|
-
fmu_manipulation_toolbox/container.py,sha256=
|
|
7
|
-
fmu_manipulation_toolbox/gui.py,sha256=
|
|
8
|
-
fmu_manipulation_toolbox/gui_style.py,sha256=
|
|
1
|
+
fmu_manipulation_toolbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
fmu_manipulation_toolbox/__main__.py,sha256=FpG0ITBz5q-AvIbXplVh_1g1zla5souFGtpiDdECxEw,352
|
|
3
|
+
fmu_manipulation_toolbox/__version__.py,sha256=CGyZIselV5kCL6An0m7aR4m-Y2X2IRQadcj88KEF3Ps,11
|
|
4
|
+
fmu_manipulation_toolbox/assembly.py,sha256=XQ_1sB6K1Dk2mnNe-E3_6Opoeub7F9Qaln0EUDzsop8,26553
|
|
5
|
+
fmu_manipulation_toolbox/checker.py,sha256=jw1omfrMMIMHlIpHXpWBcQgIiS9hnHe5T9CZ5KlbVGs,2422
|
|
6
|
+
fmu_manipulation_toolbox/container.py,sha256=8Wf16sgUPkY-ByWeKt-3TD_4vNRQuLwH9Gs7D2Q6tAk,44959
|
|
7
|
+
fmu_manipulation_toolbox/gui.py,sha256=iOJ3F_zfw3mU34VofZrYdaffljTmYfuy33fJXlLaOwg,29045
|
|
8
|
+
fmu_manipulation_toolbox/gui_style.py,sha256=s6WdrnNd_lCMWhuBf5LKK8wrfLXCU7pFTLUfvqkJVno,6633
|
|
9
9
|
fmu_manipulation_toolbox/help.py,sha256=aklKiLrsE0adSzQ5uoEB1sBDmI6s4l231gavu4XxxzA,5856
|
|
10
|
-
fmu_manipulation_toolbox/operations.py,sha256
|
|
10
|
+
fmu_manipulation_toolbox/operations.py,sha256=b7N5AVHLIpQO8od4BYasb9CppHJ-ZmW8mu0ZdhndoHI,19472
|
|
11
11
|
fmu_manipulation_toolbox/split.py,sha256=NVkfdTRR0jj_VOdgtxHQoKptkAg5TFVUA7nUx3_o9Pg,13336
|
|
12
12
|
fmu_manipulation_toolbox/version.py,sha256=OhBLkZ1-nhC77kyvffPNAf6m8OZe1bYTnNf_PWs1NvM,392
|
|
13
13
|
fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png,sha256=FWIuyrXlaNLLePHfXj7j9ca5rT8Hgr14KCe1EqTCZyk,2288
|
|
@@ -27,7 +27,7 @@ fmu_manipulation_toolbox/resources/icon_fmu.png,sha256=EuygB2xcoM2WAfKKdyKG_UvTL
|
|
|
27
27
|
fmu_manipulation_toolbox/resources/license.txt,sha256=5ODuU8g8pIkK-NMWXu_rjZ6k7gM7b-N2rmg87-2Kmqw,1583
|
|
28
28
|
fmu_manipulation_toolbox/resources/mask.png,sha256=px1U4hQGL0AmZ4BQPknOVREpMpTSejbah3ntkpqAzFA,3008
|
|
29
29
|
fmu_manipulation_toolbox/resources/model.png,sha256=EAf_HnZJe8zYGZygerG1MMt2U-tMMZlifzXPj4_iORA,208788
|
|
30
|
-
fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=
|
|
30
|
+
fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=qYV-5d35a7OmSHjYejuTRG8TurfctN5rkF-YjQua-Zo,161560
|
|
31
31
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd,sha256=OGfyJtaJntKypX5KDpuZ-nV1oYLZ6HV16pkpKOmYox4,2731
|
|
32
32
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd,sha256=HwyV7LBse-PQSv4z1xjmtzPU3Hjnv4mluq9YdSBNHMQ,3704
|
|
33
33
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd,sha256=JM4j_9q-pc40XYHb28jfT3iV3aYM5JLqD5aRjO72K1E,18939
|
|
@@ -50,16 +50,16 @@ fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd,sha256=YQS
|
|
|
50
50
|
fmu_manipulation_toolbox/resources/linux32/client_sm.so,sha256=xVdY2zy13pa2DcvFiweSNpp7E6DiONqeoBdlcJHrW_k,35940
|
|
51
51
|
fmu_manipulation_toolbox/resources/linux32/server_sm,sha256=1TLGqNPyM5UVOrCfzNqWyF6ClLksY3EiY3CSsrnp6c0,22836
|
|
52
52
|
fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=EhY1XHo1YcQn6yqZ7wk5okqtZyp0MrcCsGcudqE-aIM,37000
|
|
53
|
-
fmu_manipulation_toolbox/resources/linux64/container.so,sha256=
|
|
53
|
+
fmu_manipulation_toolbox/resources/linux64/container.so,sha256=KT6_GdTc0saR-H6l2-0udaJtEDiHnzlFBIp0FtlOtrw,135520
|
|
54
54
|
fmu_manipulation_toolbox/resources/linux64/server_sm,sha256=ulfoPvmaYe9nInYcVEyj7mD9zDzGk56OUoWx1mPKLiE,22768
|
|
55
|
-
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=
|
|
56
|
-
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=
|
|
57
|
-
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=
|
|
58
|
-
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=
|
|
59
|
-
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=
|
|
60
|
-
fmu_manipulation_toolbox-1.
|
|
61
|
-
fmu_manipulation_toolbox-1.
|
|
62
|
-
fmu_manipulation_toolbox-1.
|
|
63
|
-
fmu_manipulation_toolbox-1.
|
|
64
|
-
fmu_manipulation_toolbox-1.
|
|
65
|
-
fmu_manipulation_toolbox-1.
|
|
55
|
+
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=q91IE2IFDzchUukNc93c7MkRHgKYA3ll3CnKthPfKB0,17920
|
|
56
|
+
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=cTmbVSx_wyD-VcJMXgQWvfmByzrb9lJnTlp9Dzdu9sA,15872
|
|
57
|
+
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=RYaQA4l0iOis-QbnCz_T1zRnuDTlWFtt-FzWnEnenag,22016
|
|
58
|
+
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=vBslbM-PWm9Dj4zTwhElqm9EPHFwv4BV9obBEb0sx9I,98816
|
|
59
|
+
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=oKiRKA1irVkkL-P84bOU1TC7K9h_xrnr_4EGJqoYIfU,19968
|
|
60
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/licenses/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
|
|
61
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/METADATA,sha256=4E3iWPeD60EJzvvBjPFnWqMI6cIEMn3wxsaLjmZwkNw,1156
|
|
62
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
63
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/entry_points.txt,sha256=HjOZkflbI1IuSY8BpOZre20m24M4GDQGCJfPIa7NrlY,264
|
|
64
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
|
|
65
|
+
fmu_manipulation_toolbox-1.9rc3.dist-info/RECORD,,
|
{fmu_manipulation_toolbox-1.9rc1.dist-info → fmu_manipulation_toolbox-1.9rc3.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|