fmu-manipulation-toolbox 1.8.4.3b0__py3-none-any.whl → 1.9__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.
Files changed (34) hide show
  1. fmu_manipulation_toolbox/__init__.py +0 -1
  2. fmu_manipulation_toolbox/__main__.py +1 -1
  3. fmu_manipulation_toolbox/__version__.py +1 -1
  4. fmu_manipulation_toolbox/assembly.py +21 -12
  5. fmu_manipulation_toolbox/checker.py +11 -8
  6. fmu_manipulation_toolbox/cli/__init__.py +0 -0
  7. fmu_manipulation_toolbox/cli/fmucontainer.py +105 -0
  8. fmu_manipulation_toolbox/cli/fmusplit.py +48 -0
  9. fmu_manipulation_toolbox/cli/fmutool.py +127 -0
  10. fmu_manipulation_toolbox/cli/utils.py +36 -0
  11. fmu_manipulation_toolbox/container.py +534 -247
  12. fmu_manipulation_toolbox/gui.py +47 -55
  13. fmu_manipulation_toolbox/gui_style.py +8 -0
  14. fmu_manipulation_toolbox/help.py +3 -0
  15. fmu_manipulation_toolbox/operations.py +251 -179
  16. fmu_manipulation_toolbox/remoting.py +107 -0
  17. fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  18. fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  19. fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  20. fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  21. fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  22. fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  23. fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  24. fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  25. fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  26. fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  27. fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  28. fmu_manipulation_toolbox/split.py +67 -34
  29. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/METADATA +1 -1
  30. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/RECORD +34 -27
  31. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/WHEEL +0 -0
  32. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/entry_points.txt +0 -0
  33. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/licenses/LICENSE.txt +0 -0
  34. {fmu_manipulation_toolbox-1.8.4.3b0.dist-info → fmu_manipulation_toolbox-1.9.dist-info}/top_level.txt +0 -0
@@ -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
@@ -7,16 +8,18 @@ import tempfile
7
8
  import xml.parsers.expat
8
9
  import zipfile
9
10
  import hashlib
10
- from pathlib import Path
11
11
  from typing import *
12
12
 
13
+ logger = logging.getLogger("fmu_manipulation_toolbox")
13
14
 
14
15
  class FMU:
15
16
  """Unpack and Repack facilities for FMU package. Once unpacked, we can process Operation on
16
17
  modelDescription.xml file."""
17
18
 
18
19
  FMI2_TYPES = ('Real', 'Integer', 'String', 'Boolean', 'Enumeration')
19
- FMI3_TYPES = ('Float64')
20
+ FMI3_TYPES = ('Float64', 'Float32',
21
+ 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
22
+ 'String', 'Boolean', 'Enumeration')
20
23
 
21
24
  def __init__(self, fmu_filename):
22
25
  self.fmu_filename = fmu_filename
@@ -51,6 +54,54 @@ class FMU:
51
54
  manipulation.manipulate(self.descriptor_filename, apply_on)
52
55
 
53
56
 
57
+ class FMUPort:
58
+ def __init__(self):
59
+ self.fmi_type = None
60
+ self.attrs_list: List[Dict] = []
61
+ self.dimension = None
62
+
63
+ def dict_level(self, nb):
64
+ return " ".join([f'{key}="{value}"' for key, value in self.attrs_list[nb].items()])
65
+
66
+ def write_xml(self, fmi_version: int, file):
67
+ if fmi_version == 2:
68
+ print(f" <ScalarVariable {self.dict_level(0)}>", file=file)
69
+ print(f" <{self.fmi_type} {self.dict_level(1)}/>", file=file)
70
+ print(f" </ScalarVariable>", file=file)
71
+ elif fmi_version == 3:
72
+ print(f" <{self.fmi_type} {self.dict_level(0)}/>", file=file)
73
+ else:
74
+ raise FMUError(f"FMUPort writing: unsupported FMI version {fmi_version}")
75
+
76
+ def __contains__(self, item):
77
+ for attrs in self.attrs_list:
78
+ if item in attrs:
79
+ return True
80
+ return False
81
+
82
+ def __getitem__(self, item):
83
+ for attrs in self.attrs_list:
84
+ if item in attrs:
85
+ return attrs[item]
86
+ raise KeyError
87
+
88
+ def __setitem__(self, key, value):
89
+ for attrs in self.attrs_list:
90
+ if key in attrs:
91
+ attrs[key] = value
92
+ return
93
+ raise KeyError
94
+
95
+ def get(self, item, default_value):
96
+ try:
97
+ return self[item]
98
+ except KeyError:
99
+ return default_value
100
+
101
+ def push_attrs(self, attrs):
102
+ self.attrs_list.append(attrs)
103
+
104
+
54
105
  class FMUError(Exception):
55
106
  def __init__(self, reason):
56
107
  self.reason = reason
@@ -61,6 +112,8 @@ class FMUError(Exception):
61
112
 
62
113
  class Manipulation:
63
114
  """Parse modelDescription.xml file and create a modified version"""
115
+ TAGS_MODEL_STRUCTURE = ("InitialUnknowns", "Derivatives", "Outputs")
116
+
64
117
  def __init__(self, operation, fmu):
65
118
  self.output_filename = tempfile.mktemp()
66
119
  self.out = None
@@ -69,13 +122,23 @@ class Manipulation:
69
122
  self.parser.StartElementHandler = self.start_element
70
123
  self.parser.EndElementHandler = self.end_element
71
124
  self.parser.CharacterDataHandler = self.char_data
125
+
126
+ # used for filter
72
127
  self.skip_until: Optional[str] = None
128
+
129
+ # used to remove empty sections
130
+ self.delayed_tag = None
131
+ self.delayed_tag_open = False
132
+
73
133
  self.operation.set_fmu(fmu)
74
134
  self.fmu = fmu
75
135
 
76
- self.current_port: int = 0
77
- self.port_translation: List[int] = []
136
+ self.current_port: Optional[FMUPort] = None
137
+
138
+ self.current_port_number: int = 0
139
+ self.port_translation: List[Optional[int]] = []
78
140
  self.port_names_list: List[str] = []
141
+ self.port_removed_vr: Set[str] = set()
79
142
  self.apply_on = None
80
143
 
81
144
  @staticmethod
@@ -85,48 +148,65 @@ class Manipulation:
85
148
  else:
86
149
  return value
87
150
 
88
- def start_variable(self, attrs):
89
- causality = OperationAbstract.scalar_get_causality(attrs)
90
- port_name = attrs['name']
151
+ def handle_port(self):
152
+ causality = self.current_port.get('causality', 'local')
153
+ port_name = self.current_port['name']
154
+ vr = self.current_port['valueReference']
91
155
  if not self.apply_on or causality in self.apply_on:
92
- if self.operation.scalar_attrs(attrs):
93
- self.remove_port(port_name)
156
+ if self.operation.port_attrs(self.current_port):
157
+ self.remove_port(port_name, vr)
158
+ # Exception is raised by remove port !
94
159
  else:
95
160
  self.keep_port(port_name)
96
161
  else: # Keep ScalarVariable as it is.
97
162
  self.keep_port(port_name)
98
163
 
99
-
100
164
  def start_element(self, name, attrs):
101
165
  if self.skip_until:
102
166
  return
167
+
103
168
  try:
104
- if name == 'ScalarVariable':
105
- self.start_variable(attrs)
106
- elif name in self.fmu.FMI3_TYPES:
107
- attrs['type_name'] = name
108
- self.start_variable(attrs)
169
+ if name == 'ScalarVariable': # FMI 2.0 only
170
+ self.current_port = FMUPort()
171
+ self.current_port.push_attrs(attrs)
172
+ elif self.fmu.fmi_version == 2 and name in self.fmu.FMI2_TYPES:
173
+ if self.current_port: # <Enumeration> can be found before port defition. Ignored.
174
+ self.current_port.fmi_type = name
175
+ self.current_port.push_attrs(attrs)
176
+ elif self.fmu.fmi_version == 3 and name in self.fmu.FMI3_TYPES:
177
+ self.current_port = FMUPort()
178
+ self.current_port.fmi_type = name
179
+ self.current_port.push_attrs(attrs)
109
180
  elif name == 'CoSimulation':
110
181
  self.operation.cosimulation_attrs(attrs)
111
182
  elif name == 'DefaultExperiment':
112
183
  self.operation.experiment_attrs(attrs)
113
184
  elif name == 'fmiModelDescription':
185
+ self.fmu.fmi_version = int(float(attrs["fmiVersion"]))
114
186
  self.operation.fmi_attrs(attrs)
115
- elif name == 'Unknown':
187
+ elif name == 'Unknown': # FMI-2.0 only
116
188
  self.unknown_attrs(attrs)
117
- elif name in self.fmu.FMI2_TYPES:
118
- self.operation.scalar_type(name, attrs)
189
+ elif name == 'Output' or name == "ContinuousStateDerivative" or "InitialUnknown": # FMI-3.0 only
190
+ self.handle_structure(attrs)
119
191
 
120
192
  except ManipulationSkipTag:
121
193
  self.skip_until = name
122
194
  return
123
195
 
124
- if attrs:
125
- attrs.pop('fmi_type', None) # FMI 3.0: this attr was added during manipulation
126
- attrs_list = [f'{key}="{self.escape(value)}"' for (key, value) in attrs.items()]
127
- print(f"<{name}", " ".join(attrs_list), ">", end='', file=self.out)
128
- else:
129
- print(f"<{name}>", end='', file=self.out)
196
+ if self.current_port is None:
197
+ if self.delayed_tag and not self.delayed_tag_open:
198
+ print(f"<{self.delayed_tag}>", end='', file=self.out)
199
+ self.delayed_tag_open = True
200
+
201
+ if attrs:
202
+ attrs_list = [f'{key}="{self.escape(value)}"' for (key, value) in attrs.items()]
203
+ print(f"<{name}", " ".join(attrs_list), ">", end='', file=self.out)
204
+ else:
205
+ if name in self.TAGS_MODEL_STRUCTURE:
206
+ self.delayed_tag = name
207
+ self.delayed_tag_open = False
208
+ else:
209
+ print(f"<{name}>", end='', file=self.out)
130
210
 
131
211
  def end_element(self, name):
132
212
  if self.skip_until:
@@ -134,31 +214,106 @@ class Manipulation:
134
214
  self.skip_until = None
135
215
  return
136
216
  else:
137
- print(f"</{name}>", end='', file=self.out)
217
+ if name == "ScalarVariable" or (self.fmu.fmi_version == 3 and name in FMU.FMI3_TYPES):
218
+ try:
219
+ self.handle_port()
220
+ self.current_port.write_xml(self.fmu.fmi_version, self.out)
221
+ except ManipulationSkipTag:
222
+ logger.info(f"Port '{self.current_port['name']}' is removed.")
223
+ self.current_port = None
224
+
225
+ elif self.current_port is None:
226
+ if self.delayed_tag and name == self.delayed_tag:
227
+ if self.delayed_tag_open:
228
+ print(f"</{self.delayed_tag}>", end='', file=self.out)
229
+ else:
230
+ logger.debug(f"Remove tag <{self.delayed_tag}> from modelDescription.xml")
231
+ self.delayed_tag = None
232
+ else:
233
+ print(f"</{name}>", end='', file=self.out)
138
234
 
139
235
  def char_data(self, data):
140
236
  if not self.skip_until:
141
237
  print(data, end='', file=self.out)
142
238
 
143
- def remove_port(self, name):
239
+ def remove_port(self, name, vr):
144
240
  self.port_names_list.append(name)
145
241
  self.port_translation.append(None)
242
+ self.port_removed_vr.add(vr)
146
243
  raise ManipulationSkipTag
147
244
 
148
245
  def keep_port(self, name):
149
246
  self.port_names_list.append(name)
150
- self.current_port += 1
151
- self.port_translation.append(self.current_port)
247
+ self.current_port_number += 1
248
+ self.port_translation.append(self.current_port_number)
152
249
 
153
250
  def unknown_attrs(self, attrs):
154
- index = int(attrs['index']) - 1
155
- new_index = self.port_translation[index]
156
- if new_index:
157
- attrs['index'] = self.port_translation[int(attrs['index']) - 1]
251
+ index = int(attrs['index'])
252
+ new_index = self.port_translation[index-1]
253
+ if new_index is not None:
254
+ attrs['index'] = str(new_index)
255
+ if attrs.get('dependencies', ""):
256
+ if 'dependenciesKind' in attrs:
257
+ new_dependencies = []
258
+ new_kinds = []
259
+ for dependency, kind in zip(attrs['dependencies'].split(' '), attrs['dependenciesKind'].split(' ')):
260
+ new_dependency = self.port_translation[int(dependency)-1]
261
+ if new_dependency is not None:
262
+ new_dependencies.append(str(new_dependency))
263
+ new_kinds.append(kind)
264
+ if new_dependencies:
265
+ attrs['dependencies'] = " ".join(new_dependencies)
266
+ attrs['dependenciesKind'] = " ".join(new_kinds)
267
+ else:
268
+ attrs.pop('dependencies')
269
+ attrs.pop('dependenciesKind')
270
+ else:
271
+ new_dependencies = []
272
+ for dependency in attrs['dependencies'].split(' '):
273
+ new_dependency = self.port_translation[int(dependency)-1]
274
+ if new_dependency is not None:
275
+ new_dependencies.append(str(new_dependency))
276
+ if new_dependencies:
277
+ attrs['dependencies'] = " ".join(new_dependencies)
278
+ else:
279
+ attrs.pop('dependencies')
158
280
  else:
159
- print(f"WARNING: Removed port '{self.port_names_list[index]}' is involved in dependencies tree.")
281
+ logger.warning(f"Removed port '{self.port_names_list[index-1]}' is involved in dependencies tree.")
160
282
  raise ManipulationSkipTag
161
283
 
284
+ def handle_structure(self, attrs):
285
+ try:
286
+ vr = attrs['valueReference']
287
+ if vr in self.port_removed_vr:
288
+ logger.warning(f"Removed port vr={vr} is involved in dependencies tree.")
289
+ raise ManipulationSkipTag
290
+ except KeyError:
291
+ return
292
+
293
+ if attrs.get('dependencies', ""):
294
+ if 'dependenciesKind' in attrs:
295
+ new_dependencies = []
296
+ new_kinds = []
297
+ for dependency, kind in zip(attrs['dependencies'].split(' '), attrs['dependenciesKind'].split(' ')):
298
+ if dependency not in self.port_removed_vr:
299
+ new_dependencies.append(dependency)
300
+ new_kinds.append(kind)
301
+ if new_dependencies:
302
+ attrs['dependencies'] = " ".join(new_dependencies)
303
+ attrs['dependenciesKind'] = " ".join(new_kinds)
304
+ else:
305
+ attrs.pop('dependencies')
306
+ attrs.pop('dependenciesKind')
307
+ else:
308
+ new_dependencies = []
309
+ for dependency in attrs['dependencies'].split(' '):
310
+ if dependency not in self.port_removed_vr:
311
+ new_dependencies.append(dependency)
312
+ if new_dependencies:
313
+ attrs['dependencies'] = " ".join(new_dependencies)
314
+ else:
315
+ attrs.pop('dependencies')
316
+
162
317
  def manipulate(self, descriptor_filename, apply_on=None):
163
318
  self.apply_on = apply_on
164
319
  with open(self.output_filename, "w", encoding="utf-8") as self.out, open(descriptor_filename, "rb") as file:
@@ -181,31 +336,19 @@ class OperationAbstract:
181
336
  def fmi_attrs(self, attrs):
182
337
  pass
183
338
 
184
- def scalar_attrs(self, attrs) -> int:
185
- """ return 0 to keep port, otherwise remove it"""
186
- return 0
187
-
188
339
  def cosimulation_attrs(self, attrs):
189
340
  pass
190
341
 
191
342
  def experiment_attrs(self, attrs):
192
343
  pass
193
344
 
194
- def scalar_type(self, type_name, attrs):
195
- pass
345
+ def port_attrs(self, fmu_port: FMUPort) -> int:
346
+ """ return 0 to keep port, otherwise remove it"""
347
+ return 0
196
348
 
197
349
  def closure(self):
198
350
  pass
199
351
 
200
- @staticmethod
201
- def scalar_get_causality(attrs) -> str:
202
- try:
203
- causality = attrs['causality']
204
- except KeyError:
205
- causality = 'local' # Default value according to FMI Specifications.
206
-
207
- return causality
208
-
209
352
 
210
353
  class OperationSaveNamesToCSV(OperationAbstract):
211
354
  def __repr__(self):
@@ -217,48 +360,29 @@ class OperationSaveNamesToCSV(OperationAbstract):
217
360
  self.writer = csv.writer(self.csvfile, delimiter=';', quotechar="'", quoting=csv.QUOTE_MINIMAL)
218
361
  self.writer.writerow(['name', 'newName', 'valueReference', 'causality', 'variability', 'scalarType',
219
362
  'startValue'])
220
- self.name = None
221
- self.vr = None
222
- self.variability = None
223
- self.causality = None
224
-
225
- def reset(self):
226
- self.name = None
227
- self.vr = None
228
- self.variability = None
229
- self.causality = None
230
363
 
231
364
  def closure(self):
232
365
  self.csvfile.close()
233
366
 
234
- def scalar_attrs(self, attrs):
235
- self.name = attrs['name']
236
- self.vr = attrs['valueReference']
237
- self.causality = self.scalar_get_causality(attrs)
238
-
239
- try:
240
- self.variability = attrs['variability']
241
- except KeyError:
242
- self.variability = 'continuous' # Default value according to FMI Specifications.
367
+ def port_attrs(self, fmu_port: FMUPort) -> int:
368
+ self.writer.writerow([fmu_port["name"],
369
+ fmu_port["name"],
370
+ fmu_port["valueReference"],
371
+ fmu_port.get("causality", "local"),
372
+ fmu_port.get("variability", "continuous"),
373
+ fmu_port.fmi_type,
374
+ fmu_port.get("start", "")])
243
375
 
244
376
  return 0
245
377
 
246
- def scalar_type(self, type_name, attrs):
247
- if "start" in attrs:
248
- start = attrs["start"]
249
- else:
250
- start = ""
251
- self.writer.writerow([self.name, self.name, self.vr, self.causality, self.variability, type_name, start])
252
- self.reset()
253
-
254
378
 
255
379
  class OperationStripTopLevel(OperationAbstract):
256
380
  def __repr__(self):
257
381
  return "Remove Top Level Bus"
258
382
 
259
- def scalar_attrs(self, attrs):
260
- new_name = attrs['name'].split('.', 1)[-1]
261
- attrs['name'] = new_name
383
+ def port_attrs(self, fmu_port):
384
+ new_name = fmu_port['name'].split('.', 1)[-1]
385
+ fmu_port['name'] = new_name
262
386
  return 0
263
387
 
264
388
 
@@ -266,9 +390,9 @@ class OperationMergeTopLevel(OperationAbstract):
266
390
  def __repr__(self):
267
391
  return "Merge Top Level Bus with signal names"
268
392
 
269
- def scalar_attrs(self, attrs):
270
- old = attrs['name']
271
- attrs['name'] = old.replace('.', '_', 1)
393
+ def port_attrs(self, fmu_port):
394
+ old = fmu_port['name']
395
+ fmu_port['name'] = old.replace('.', '_', 1)
272
396
  return 0
273
397
 
274
398
 
@@ -279,88 +403,32 @@ class OperationRenameFromCSV(OperationAbstract):
279
403
  def __init__(self, csv_filename):
280
404
  self.csv_filename = csv_filename
281
405
  self.translations = {}
282
- self.current_port = 0
283
- self.port_translation = []
406
+
284
407
  try:
285
408
  with open(csv_filename, newline='') as csvfile:
286
409
  reader = csv.reader(csvfile, delimiter=';', quotechar="'")
287
410
  for row in reader:
288
411
  self.translations[row[0]] = row[1]
289
412
  except FileNotFoundError:
290
- raise OperationException(f"file '{csv_filename}' is not found")
413
+ raise OperationError(f"file '{csv_filename}' is not found")
291
414
  except KeyError:
292
- raise OperationException(f"file '{csv_filename}' should contain two columns")
415
+ raise OperationError(f"file '{csv_filename}' should contain two columns")
293
416
 
294
- def scalar_attrs(self, attrs):
295
- name = attrs['name']
417
+ def port_attrs(self, fmu_port):
418
+ name = fmu_port['name']
296
419
  try:
297
- new_name = self.translations[attrs['name']]
420
+ new_name = self.translations[fmu_port['name']]
298
421
  except KeyError:
299
422
  new_name = name # if port is not in CSV file, keep old name
300
423
 
301
424
  if new_name:
302
- attrs['name'] = new_name
425
+ fmu_port['name'] = new_name
303
426
  return 0
304
427
  else:
305
428
  # we want to delete this name!
306
429
  return 1
307
430
 
308
431
 
309
- class OperationAddRemotingWinAbstract(OperationAbstract):
310
- bitness_from = None
311
- bitness_to = None
312
-
313
- def __repr__(self):
314
- return f"Add '{self.bitness_to}' remoting on '{self.bitness_from}' FMU"
315
-
316
- def cosimulation_attrs(self, attrs):
317
- fmu_bin = {
318
- "win32": os.path.join(self.fmu.tmp_directory, "binaries", f"win32"),
319
- "win64": os.path.join(self.fmu.tmp_directory, "binaries", f"win64"),
320
- }
321
-
322
- if not os.path.isdir(fmu_bin[self.bitness_from]):
323
- raise OperationException(f"{self.bitness_from} interface does not exist")
324
-
325
- if os.path.isdir(fmu_bin[self.bitness_to]):
326
- print(f"INFO: {self.bitness_to} already exists. Add front-end.")
327
- shutil.move(os.path.join(fmu_bin[self.bitness_to], attrs['modelIdentifier'] + ".dll"),
328
- os.path.join(fmu_bin[self.bitness_to], attrs['modelIdentifier'] + "-remoted.dll"))
329
- else:
330
- os.mkdir(fmu_bin[self.bitness_to])
331
-
332
- to_path = Path(__file__).parent / "resources" / self.bitness_to
333
- shutil.copyfile(to_path / "client_sm.dll",
334
- Path(fmu_bin[self.bitness_to]) / Path(attrs['modelIdentifier']).with_suffix(".dll"))
335
-
336
- from_path = Path(__file__).parent / "resources" / self.bitness_from
337
- shutil.copyfile(from_path / "server_sm.exe",
338
- Path(fmu_bin[self.bitness_from]) / "server_sm.exe")
339
-
340
- shutil.copyfile(Path(__file__).parent / "resources" / "license.txt",
341
- Path(fmu_bin[self.bitness_to]) / "license.txt")
342
-
343
-
344
- class OperationAddRemotingWin64(OperationAddRemotingWinAbstract):
345
- bitness_from = "win32"
346
- bitness_to = "win64"
347
-
348
-
349
- class OperationAddFrontendWin32(OperationAddRemotingWinAbstract):
350
- bitness_from = "win32"
351
- bitness_to = "win32"
352
-
353
-
354
- class OperationAddFrontendWin64(OperationAddRemotingWinAbstract):
355
- bitness_from = "win64"
356
- bitness_to = "win64"
357
-
358
-
359
- class OperationAddRemotingWin32(OperationAddRemotingWinAbstract):
360
- bitness_from = "win64"
361
- bitness_to = "win32"
362
-
363
-
364
432
  class OperationRemoveRegexp(OperationAbstract):
365
433
  def __repr__(self):
366
434
  return f"Remove ports matching '{self.regex_string}'"
@@ -368,11 +436,11 @@ class OperationRemoveRegexp(OperationAbstract):
368
436
  def __init__(self, regex_string):
369
437
  self.regex_string = regex_string
370
438
  self.regex = re.compile(regex_string)
371
- self.current_port = 0
439
+ self.current_port_number = 0
372
440
  self.port_translation = []
373
441
 
374
- def scalar_attrs(self, attrs):
375
- name = attrs['name']
442
+ def port_attrs(self, fmu_port):
443
+ name = fmu_port['name']
376
444
  if self.regex.match(name):
377
445
  return 1 # Remove port
378
446
  else:
@@ -387,8 +455,8 @@ class OperationKeepOnlyRegexp(OperationAbstract):
387
455
  self.regex_string = regex_string
388
456
  self.regex = re.compile(regex_string)
389
457
 
390
- def scalar_attrs(self, attrs):
391
- name = attrs['name']
458
+ def port_attrs(self, fmu_port):
459
+ name = fmu_port['name']
392
460
  if self.regex.match(name):
393
461
  return 0
394
462
  else:
@@ -403,34 +471,34 @@ class OperationSummary(OperationAbstract):
403
471
  return f"FMU Summary"
404
472
 
405
473
  def fmi_attrs(self, attrs):
406
- print(f"| fmu filename = {self.fmu.fmu_filename}")
407
- print(f"| temporary directory = {self.fmu.tmp_directory}")
474
+ logger.info(f"| fmu filename = {self.fmu.fmu_filename}")
475
+ logger.info(f"| temporary directory = {self.fmu.tmp_directory}")
408
476
  hash_md5 = hashlib.md5()
409
477
  with open(self.fmu.fmu_filename, "rb") as f:
410
478
  for chunk in iter(lambda: f.read(4096), b""):
411
479
  hash_md5.update(chunk)
412
480
  digest = hash_md5.hexdigest()
413
- print(f"| MD5Sum = {digest}")
414
-
415
- print(f"|\n| FMI properties: ")
481
+ logger.info(f"| MD5Sum = {digest}")
482
+ logger.info(f"|")
483
+ logger.info(f"| FMI properties: ")
416
484
  for (k, v) in attrs.items():
417
- print(f"| - {k} = {v}")
418
- print(f"|")
485
+ logger.info(f"| - {k} = {v}")
486
+ logger.info(f"|")
419
487
 
420
488
  def cosimulation_attrs(self, attrs):
421
- print("| Co-Simulation capabilities: ")
489
+ logger.info("| Co-Simulation capabilities: ")
422
490
  for (k, v) in attrs.items():
423
- print(f"| - {k} = {v}")
424
- print(f"|")
491
+ logger.info(f"| - {k} = {v}")
492
+ logger.info(f"|")
425
493
 
426
494
  def experiment_attrs(self, attrs):
427
- print("| Default Experiment values: ")
495
+ logger.info("| Default Experiment values: ")
428
496
  for (k, v) in attrs.items():
429
- print(f"| - {k} = {v}")
430
- print(f"|")
497
+ logger.info(f"| - {k} = {v}")
498
+ logger.info(f"|")
431
499
 
432
- def scalar_attrs(self, attrs) -> int:
433
- causality = self.scalar_get_causality(attrs)
500
+ def port_attrs(self, fmu_port) -> int:
501
+ causality = fmu_port.get("causality", "local")
434
502
 
435
503
  try:
436
504
  self.nb_port_per_causality[causality] += 1
@@ -440,33 +508,37 @@ class OperationSummary(OperationAbstract):
440
508
  return 0
441
509
 
442
510
  def closure(self):
443
- print("| Supported platforms: ")
511
+ logger.info("| Supported platforms: ")
444
512
  try:
445
513
  for platform in os.listdir(os.path.join(self.fmu.tmp_directory, "binaries")):
446
- print(f"| - {platform}")
514
+ logger.info(f"| - {platform}")
447
515
  except FileNotFoundError:
448
516
  pass # no binaries
449
517
 
450
518
  if os.path.isdir(os.path.join(self.fmu.tmp_directory, "sources")):
451
- print(f"| - RT (sources available)")
519
+ logger.info(f"| - RT (sources available)")
452
520
 
453
521
  resource_dir = os.path.join(self.fmu.tmp_directory, "resources")
454
522
  if os.path.isdir(resource_dir):
455
- print("|\n| Embedded resources:")
523
+ logger.info("|")
524
+ logger.info("| Embedded resources:")
456
525
  for resource in os.listdir(resource_dir):
457
- print(f"| - {resource}")
526
+ logger.info(f"| - {resource}")
458
527
 
459
528
  extra_dir = os.path.join(self.fmu.tmp_directory, "extra")
460
529
  if os.path.isdir(extra_dir):
461
- print("|\n| Additional (meta-)data:")
530
+ logger.info("|")
531
+ logger.info("| Additional (meta-)data:")
462
532
  for extra in os.listdir(extra_dir):
463
- print(f"| - {extra}")
533
+ logger.info(f"| - {extra}")
464
534
 
465
- print("|\n| Number of signals")
535
+ logger.info("|")
536
+ logger.info("| Number of ports")
466
537
  for causality, nb_ports in self.nb_port_per_causality.items():
467
- print(f"| {causality} : {nb_ports}")
538
+ logger.info(f"| {causality} : {nb_ports}")
468
539
 
469
- print("|\n| [End of report]")
540
+ logger.info("|")
541
+ logger.info("| [End of report]")
470
542
 
471
543
 
472
544
  class OperationRemoveSources(OperationAbstract):
@@ -477,7 +549,7 @@ class OperationRemoveSources(OperationAbstract):
477
549
  try:
478
550
  shutil.rmtree(os.path.join(self.fmu.tmp_directory, "sources"))
479
551
  except FileNotFoundError:
480
- print("This FMU does not embed sources.")
552
+ logger.info("This FMU does not embed sources.")
481
553
 
482
554
 
483
555
  class OperationTrimUntil(OperationAbstract):
@@ -487,17 +559,17 @@ class OperationTrimUntil(OperationAbstract):
487
559
  def __repr__(self):
488
560
  return f"Trim names until (and including) '{self.separator}'"
489
561
 
490
- def scalar_attrs(self, attrs) -> int:
491
- name = attrs['name']
562
+ def port_attrs(self, fmu_port) -> int:
563
+ name = fmu_port['name']
492
564
  try:
493
- attrs['name'] = name[name.index(self.separator)+len(self.separator):-1]
565
+ fmu_port['name'] = name[name.index(self.separator)+len(self.separator):-1]
494
566
  except KeyError:
495
567
  pass # no separator
496
568
 
497
569
  return 0
498
570
 
499
571
 
500
- class OperationException(Exception):
572
+ class OperationError(Exception):
501
573
  def __init__(self, reason):
502
574
  self.reason = reason
503
575