fmu-manipulation-toolbox 1.9.1.3__py3-none-any.whl → 1.9.2b1__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 (21) hide show
  1. fmu_manipulation_toolbox/__version__.py +1 -1
  2. fmu_manipulation_toolbox/container.py +265 -78
  3. fmu_manipulation_toolbox/ls.py +35 -0
  4. fmu_manipulation_toolbox/operations.py +1 -1
  5. fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  6. fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  7. fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  8. fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  9. fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  10. fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  11. fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  12. fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  13. fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  14. fmu_manipulation_toolbox/split.py +49 -2
  15. fmu_manipulation_toolbox/terminals.py +137 -0
  16. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/METADATA +1 -1
  17. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/RECORD +21 -19
  18. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/WHEEL +0 -0
  19. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/entry_points.txt +0 -0
  20. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/licenses/LICENSE.txt +0 -0
  21. {fmu_manipulation_toolbox-1.9.1.3.dist-info → fmu_manipulation_toolbox-1.9.2b1.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- 'V1.9.1.3'
1
+ 'V1.9.2b1'
@@ -6,11 +6,14 @@ import shutil
6
6
  import uuid
7
7
  import platform
8
8
  import zipfile
9
+ from collections import defaultdict
9
10
  from datetime import datetime
10
11
  from pathlib import Path
11
12
  from typing import *
12
13
 
14
+ from .ls import LayeredStandard
13
15
  from .operations import FMU, OperationAbstract, FMUError, FMUPort
16
+ from .terminals import Terminals
14
17
  from .version import __version__ as tool_version
15
18
 
16
19
 
@@ -37,7 +40,9 @@ class EmbeddedFMUPort:
37
40
  'Int64': 'integer64',
38
41
  'UInt64': 'uinteger64',
39
42
  'String': 'string',
40
- 'Boolean': 'boolean1'
43
+ 'Boolean': 'boolean1',
44
+ 'Binary': 'binary',
45
+ 'Clock': 'clock'
41
46
  }
42
47
  }
43
48
 
@@ -60,7 +65,9 @@ class EmbeddedFMUPort:
60
65
  'integer64': 'Int64' ,
61
66
  'uinteger64': 'UInt64' ,
62
67
  'string': 'String' ,
63
- 'boolean1': 'Boolean'
68
+ 'boolean1': 'Boolean',
69
+ 'binary': 'Binary',
70
+ 'clock': 'Clock'
64
71
  }
65
72
  }
66
73
 
@@ -68,12 +75,14 @@ class EmbeddedFMUPort:
68
75
  "real64", "real32",
69
76
  "integer8", "uinteger8", "integer16", "uinteger16", "integer32", "uinteger32", "integer64", "uinteger64",
70
77
  "boolean", "boolean1",
71
- "string"
78
+ "string",
79
+ "binary", "clock"
72
80
  )
73
81
 
74
82
  def __init__(self, fmi_type, attrs: Union[FMUPort, Dict[str, str]], fmi_version=0):
75
83
  self.causality = attrs.get("causality", "local")
76
- self.variability = attrs.get("variability", "continuous")
84
+ self.variability = attrs.get("variability", None)
85
+ self.interval_variability = attrs.get("intervalVariability", None)
77
86
  self.name = attrs["name"]
78
87
  self.vr = int(attrs["valueReference"])
79
88
  self.description = attrs.get("description", None)
@@ -85,6 +94,8 @@ class EmbeddedFMUPort:
85
94
 
86
95
  self.start_value = attrs.get("start", None)
87
96
  self.initial = attrs.get("initial", None)
97
+ self.clock = attrs.get("clocks", None)
98
+
88
99
 
89
100
  def xml(self, vr: int, name=None, causality=None, start=None, fmi_version=2) -> str:
90
101
  if name is None:
@@ -93,13 +104,15 @@ class EmbeddedFMUPort:
93
104
  causality = self.causality
94
105
  if start is None:
95
106
  start = self.start_value
107
+ if start is None and self.type_name == "binary" and self.initial == "exact":
108
+ start = ""
96
109
  if self.variability is None:
97
110
  self.variability = "continuous" if "real" in self.type_name else "discrete"
98
111
 
99
112
  try:
100
113
  fmi_type = self.CONTAINER_TO_FMI[fmi_version][self.type_name]
101
114
  except KeyError:
102
- logger.error(f"Cannot expose '{name}' because type '{self.type_name}' is not compatible "
115
+ logger.error(f"Cannot expose ({causality}) '{name}' because type '{self.type_name}' is not compatible "
103
116
  f"with FMI-{fmi_version}.0")
104
117
  return ""
105
118
 
@@ -124,9 +137,10 @@ class EmbeddedFMUPort:
124
137
  filtered_attrs = {key: value for key, value in scalar_attrs.items() if value is not None}
125
138
  scalar_attrs_str = " ".join([f'{key}="{value}"' for (key, value) in filtered_attrs.items()])
126
139
  return f'<ScalarVariable {scalar_attrs_str}>{child_str}</ScalarVariable>'
127
- else:
140
+
141
+ elif fmi_version == 3:
128
142
  if fmi_type in ('String', 'Binary'):
129
- if start:
143
+ if start is not None:
130
144
  child_str = f'<Start value="{start}"/>'
131
145
  else:
132
146
  child_str = ''
@@ -149,12 +163,16 @@ class EmbeddedFMUPort:
149
163
  "variability": self.variability,
150
164
  "initial": self.initial,
151
165
  "description": self.description,
152
- "start": start
166
+ "start": start,
167
+ "intervalVariability": self.interval_variability
153
168
  }
154
169
  filtered_attrs = {key: value for key, value in scalar_attrs.items() if value is not None}
155
170
  scalar_attrs_str = " ".join([f'{key}="{value}"' for (key, value) in filtered_attrs.items()])
156
171
 
157
172
  return f'<{fmi_type} {scalar_attrs_str}/>'
173
+ else:
174
+ logger.critical(f"Unknown version {fmi_version}. BUG?")
175
+ return ''
158
176
 
159
177
 
160
178
  class EmbeddedFMU(OperationAbstract):
@@ -167,6 +185,10 @@ class EmbeddedFMU(OperationAbstract):
167
185
  self.name = Path(filename).name
168
186
  self.id = Path(filename).stem.lower()
169
187
 
188
+ logger.debug(f"Analysing {self.name}")
189
+ self.terminals = Terminals(self.fmu.tmp_directory)
190
+ self.ls = LayeredStandard(self.fmu.tmp_directory)
191
+
170
192
  self.step_size = None
171
193
  self.start_time = None
172
194
  self.stop_time = None
@@ -175,6 +197,7 @@ class EmbeddedFMU(OperationAbstract):
175
197
  self.fmi_version = None
176
198
  self.ports: Dict[str, EmbeddedFMUPort] = {}
177
199
 
200
+ self.has_event_mode = False
178
201
  self.capabilities: Dict[str, str] = {}
179
202
  self.current_port = None # used during apply_operation()
180
203
 
@@ -187,12 +210,14 @@ class EmbeddedFMU(OperationAbstract):
187
210
  if fmi_version == "2.0":
188
211
  self.guid = attrs['guid']
189
212
  self.fmi_version = 2
190
- if fmi_version == "3.0": # TODO: handle 3.x cases
213
+ if fmi_version.startswith("3."):
191
214
  self.guid = attrs['instantiationToken']
192
215
  self.fmi_version = 3
193
216
 
194
217
  def cosimulation_attrs(self, attrs: Dict[str, str]):
195
218
  self.model_identifier = attrs['modelIdentifier']
219
+ if attrs.get("hasEventMode", "false") == "true":
220
+ self.has_event_mode = True
196
221
  for capability in self.capability_list:
197
222
  self.capabilities[capability] = attrs.get(capability, "false")
198
223
 
@@ -215,7 +240,12 @@ class EmbeddedFMU(OperationAbstract):
215
240
  self.ports[port.name] = port
216
241
 
217
242
  def __repr__(self):
218
- return f"FMU '{self.name}' ({len(self.ports)} variables, ts={self.step_size}s)"
243
+ properties = f"{len(self.ports)} variables, ts={self.step_size}s"
244
+ if len(self.terminals) > 0:
245
+ properties += f", {len(self.terminals)} terminals"
246
+ if len(self.ls) > 0:
247
+ properties += f", {self.ls}"
248
+ return f"'{self.name}' ({properties})"
219
249
 
220
250
 
221
251
  class FMUContainerError(Exception):
@@ -315,13 +345,19 @@ class Link:
315
345
  self.vr_converted: Dict[str, Optional[int]] = {}
316
346
 
317
347
  if not cport_from.port.causality == "output":
318
- raise FMUContainerError(f"{cport_from} is {cport_from.port.causality} instead of OUTPUT")
348
+ if cport_from.port.type_name == "clock":
349
+ # LS-BUS allows to connected to input clocks.
350
+ self.cport_from = None
351
+ self.add_target(cport_from)
352
+ else:
353
+ raise FMUContainerError(f"{cport_from} is {cport_from.port.causality} instead of OUTPUT")
319
354
 
320
355
  def add_target(self, cport_to: ContainerPort):
321
356
  if not cport_to.port.causality == "input":
322
357
  raise FMUContainerError(f"{cport_to} is {cport_to.port.causality} instead of INPUT")
323
358
 
324
- if cport_to.port.type_name == self.cport_from.port.type_name:
359
+ if (cport_to.port.type_name == "clock" and self.cport_from is None or
360
+ cport_to.port.type_name == self.cport_from.port.type_name):
325
361
  self.cport_to_list.append(cport_to)
326
362
  elif self.get_conversion(cport_to):
327
363
  self.cport_to_list.append(cport_to)
@@ -345,6 +381,7 @@ class ValueReferenceTable:
345
381
  self.vr_table:Dict[str, int] = {}
346
382
  self.masks: Dict[str, int] = {}
347
383
  self.nb_local_variable:Dict[str, int] = {}
384
+ self.local_clock = {}
348
385
  for i, type_name in enumerate(EmbeddedFMUPort.ALL_TYPES):
349
386
  self.vr_table[type_name] = 0
350
387
  self.masks[type_name] = i << 24
@@ -365,13 +402,28 @@ class ValueReferenceTable:
365
402
  return vr | self.masks[type_name]
366
403
 
367
404
  def set_link_vr(self, link: Link):
368
- link.vr = self.add_vr(link.cport_from, local=True)
405
+ if link.cport_from is None:
406
+ link.vr = self.add_vr("clock", local=True)
407
+ else:
408
+ link.vr = self.add_vr(link.cport_from, local=True)
409
+ if link.cport_from.port.type_name == "clock":
410
+ self.local_clock[(link.cport_from.fmu, link.cport_from.port.vr)] = link.vr
411
+
412
+ for cport_to in link.cport_to_list:
413
+ if cport_to.port.type_name == "clock":
414
+ self.local_clock[(cport_to.fmu, cport_to.port.vr)] = link.vr
415
+
369
416
  for type_name in link.vr_converted.keys():
370
417
  link.vr_converted[type_name] = self.add_vr(type_name, local=True)
371
418
 
419
+ def get_local_clock(self, cport: ContainerPort) -> int:
420
+ return self.local_clock[(cport.fmu, int(cport.port.clock))]
421
+
422
+
372
423
  def nb_local(self, type_name: str) -> int:
373
424
  return self.nb_local_variable[type_name]
374
425
 
426
+
375
427
  class AutoWired:
376
428
  def __init__(self):
377
429
  self.rule_input = []
@@ -397,6 +449,102 @@ class AutoWired:
397
449
  self.rule_link.append([from_fmu, from_port, to_fmu, to_port])
398
450
 
399
451
 
452
+ class FMUIOList:
453
+ def __init__(self, vr_table: ValueReferenceTable):
454
+ self.vr_table = vr_table
455
+ self.inputs = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) # [type][fmu][clock_vr][(fmu_vr, vr])
456
+ self.nb_clocked_inputs = defaultdict(lambda: defaultdict(lambda: 0))
457
+ self.outputs = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) # [type][fmu][clock_vr][(fmu_vr, vr])
458
+ self.nb_clocked_outputs = defaultdict(lambda: defaultdict(lambda: 0))
459
+ self.start_values = defaultdict(lambda: defaultdict(list)) # [type][fmu][(cport, value)]
460
+
461
+ def add_input(self, cport: ContainerPort, local_vr: int):
462
+ if cport.port.clock is None:
463
+ clock = None
464
+ else:
465
+ try:
466
+ clock = self.vr_table.get_local_clock(cport)
467
+ except KeyError:
468
+ logger.error(f"Cannot expose clocked input: {cport}")
469
+ return
470
+ self.nb_clocked_inputs[cport.port.type_name][cport.fmu.name] += 1
471
+ self.inputs[cport.port.type_name][cport.fmu.name][clock].append((cport.port.vr, local_vr))
472
+
473
+ def add_output(self, cport: ContainerPort, local_vr: int):
474
+ if cport.port.clock is None:
475
+ clock = None
476
+ else:
477
+ try:
478
+ clock = self.vr_table.get_local_clock(cport)
479
+ except KeyError:
480
+ logger.error(f"Cannot expose clocked output: {cport}")
481
+ return
482
+ self.nb_clocked_outputs[cport.port.type_name][cport.fmu.name] += 1
483
+ self.outputs[cport.port.type_name][cport.fmu.name][clock].append((cport.port.vr, local_vr))
484
+
485
+ def add_start_value(self, cport: ContainerPort, value: str):
486
+ reset = 1 if cport.port.causality == "input" else 0
487
+ self.start_values[cport.port.type_name][cport.fmu.name].append((cport, reset, value))
488
+
489
+ def write_txt(self, fmu_name, txt_file):
490
+ for type_name in EmbeddedFMUPort.ALL_TYPES:
491
+ print(f"# Inputs of {fmu_name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
492
+ print(len(self.inputs[type_name][fmu_name][None]), file=txt_file)
493
+ for fmu_vr, vr in self.inputs[type_name][fmu_name][None]:
494
+ print(f"{vr} {fmu_vr}", file=txt_file)
495
+ if not type_name == "clock":
496
+ print(f"# Clocked Inputs of {fmu_name} - {type_name}: <FMU_VR_CLOCK> <n> <VR> <FMU_VR>", file=txt_file)
497
+ print(f"{len(self.inputs[type_name][fmu_name])-1} {self.nb_clocked_inputs[type_name][fmu_name]}",
498
+ file=txt_file)
499
+ for clock, table in self.inputs[type_name][fmu_name].items():
500
+ if not clock is None:
501
+ s = " ".join([f"{vr} {fmu_vr}" for fmu_vr, vr in table])
502
+ print(f"{clock} {len(table)} {s}", file=txt_file)
503
+
504
+ for type_name in EmbeddedFMUPort.ALL_TYPES[:-2]: # No start values for binary or clock
505
+ print(f"# Start values of {fmu_name} - {type_name}: <FMU_VR> <RESET> <VALUE>", file=txt_file)
506
+ print(len(self.start_values[type_name][fmu_name]), file=txt_file)
507
+ for vr, reset, value in self.start_values[type_name][fmu_name]:
508
+ print(f"{vr} {reset} {value}", file=txt_file)
509
+
510
+ for type_name in EmbeddedFMUPort.ALL_TYPES:
511
+ print(f"# Outputs of {fmu_name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
512
+ print(len(self.outputs[type_name][fmu_name][None]), file=txt_file)
513
+ for fmu_vr, vr in self.outputs[type_name][fmu_name][None]:
514
+ print(f"{vr} {fmu_vr}", file=txt_file)
515
+
516
+ if not type_name == "clock":
517
+ print(f"# Clocked Outputs of {fmu_name} - {type_name}: <FMU_VR_CLOCK> <n> <VR> <FMU_VR>", file=txt_file)
518
+ print(f"{len(self.outputs[type_name][fmu_name])-1} {self.nb_clocked_outputs[type_name][fmu_name]}",
519
+ file=txt_file)
520
+ for clock, translation in self.outputs[type_name][fmu_name].items():
521
+ if not clock is None:
522
+ s = " ".join([f"{vr} {fmu_vr}" for fmu_vr, vr in translation])
523
+ print(f"{clock} {len(translation)} {s}", file=txt_file)
524
+
525
+
526
+ class ClockList:
527
+ def __init__(self, involved_fmu: OrderedDict[str, EmbeddedFMU]):
528
+ self.clocks_per_fmu: DefaultDict[int, List[Tuple[int, int]]] = defaultdict(list)
529
+ self.fmu_index: Dict[str, int] = {}
530
+ for i, fmu_name in enumerate(involved_fmu):
531
+ self.fmu_index[fmu_name] = i
532
+
533
+ def append(self, cport: ContainerPort, vr: int):
534
+ self.clocks_per_fmu[self.fmu_index[cport.fmu.name]].append((cport.port.vr, vr))
535
+
536
+ def write_txt(self, txt_file):
537
+ print(f"# importer CLOCKS: <FMU_INDEX> <NB> <FMU_VR> <VR> [<FMU_VR> <VR>]", file=txt_file)
538
+ nb_total_clocks = 0;
539
+ for clocks in self.clocks_per_fmu.values():
540
+ nb_total_clocks += len(clocks)
541
+
542
+ print(f"{len(self.clocks_per_fmu)} {nb_total_clocks}", file=txt_file)
543
+ for index, clocks in self.clocks_per_fmu.items():
544
+ clocks_str = " ".join([f"{clock[0]} {clock[1]}" for clock in clocks])
545
+ print(f"{index} {len(clocks)} {clocks_str}", file=txt_file)
546
+
547
+
400
548
  class FMUContainer:
401
549
  HEADER_XML_2 = """<?xml version="1.0" encoding="ISO-8859-1"?>
402
550
  <fmiModelDescription
@@ -505,7 +653,7 @@ class FMUContainer:
505
653
  logger.warning(f"Try to embed FMU-{fmu.fmi_version} into container FMI-{self.fmi_version}.")
506
654
  self.involved_fmu[fmu.name] = fmu
507
655
 
508
- logger.debug(f"Adding FMU #{len(self.involved_fmu)}: {fmu}")
656
+ logger.info(f"Involved FMU #{len(self.involved_fmu)}: {fmu}")
509
657
  except (FMUContainerError, FMUError) as e:
510
658
  raise FMUContainerError(f"Cannot load '{fmu_filename}': {e}")
511
659
 
@@ -564,19 +712,45 @@ class FMUContainer:
564
712
  self.mark_ruled(cport_from, 'DROP')
565
713
 
566
714
  def add_link(self, from_fmu_filename: str, from_port_name: str, to_fmu_filename: str, to_port_name: str):
567
- cport_from = ContainerPort(self.get_fmu(from_fmu_filename), from_port_name)
568
- try:
569
- local = self.links[cport_from]
570
- except KeyError:
571
- local = Link(cport_from)
715
+ fmu_from = self.get_fmu(from_fmu_filename)
716
+ fmu_to = self.get_fmu(to_fmu_filename)
717
+
718
+ if from_port_name in fmu_from.terminals and to_port_name in fmu_to.terminals:
719
+ # TERMINAL Connection
720
+ terminal1 = fmu_from.terminals[from_port_name]
721
+ terminal2 = fmu_to.terminals[to_port_name]
722
+ if terminal1 == terminal2:
723
+ logger.debug(f"Plugging terminals: {terminal1} <-> {terminal2}")
724
+ for terminal1_port_name, terminal2_port_name in terminal1.connect(terminal2):
725
+ self.add_link_regular(fmu_from, terminal1_port_name, fmu_to, terminal2_port_name)
726
+ else:
727
+ logger.error(f"Cannot plug incompatible terminals: {terminal1} <-> {terminal2}")
728
+ else:
729
+ # REGULAR port connection
730
+ self.add_link_regular(fmu_from, from_port_name, fmu_to, to_port_name)
572
731
 
573
- cport_to = ContainerPort(self.get_fmu(to_fmu_filename), to_port_name)
574
- local.add_target(cport_to) # Causality is check in the add() function
732
+ def add_link_regular(self, fmu_from: EmbeddedFMU, from_port_name: str, fmu_to: EmbeddedFMU, to_port_name: str):
733
+ cport_from = ContainerPort(fmu_from, from_port_name)
734
+ cport_to = ContainerPort(fmu_to, to_port_name)
735
+
736
+ if cport_to.port.causality == "output" and cport_from.port.causality == "input":
737
+ logger.debug("Invert link orientation")
738
+ tmp = cport_to
739
+ cport_to = cport_from
740
+ cport_from = tmp
741
+
742
+ try:
743
+ local = self.links[cport_from]
744
+ except KeyError:
745
+ local = Link(cport_from)
746
+ self.links[cport_from] = local
747
+
748
+ local.add_target(cport_to) # Causality is check in the add() function
749
+
750
+ logger.debug(f"LINK: {cport_from} -> {cport_to}")
751
+ self.mark_ruled(cport_from, 'LINK')
752
+ self.mark_ruled(cport_to, 'LINK')
575
753
 
576
- logger.debug(f"LINK: {cport_from} -> {cport_to}")
577
- self.mark_ruled(cport_from, 'LINK')
578
- self.mark_ruled(cport_to, 'LINK')
579
- self.links[cport_from] = local
580
754
 
581
755
  def add_start_value(self, fmu_filename: str, port_name: str, value: str):
582
756
  cport = ContainerPort(self.get_fmu(fmu_filename), port_name)
@@ -588,10 +762,13 @@ class FMUContainer:
588
762
  value = int(value)
589
763
  elif cport.port.type_name == 'Boolean':
590
764
  value = int(bool(value))
591
- else:
765
+ elif cport.port.type_name == 'String':
592
766
  value = value
767
+ else:
768
+ logger.error(f"Start value cannot be set on '{cport.port.type_name}'")
769
+ return
593
770
  except ValueError:
594
- raise FMUContainerError(f"Start value is not conforming to '{cport.port.type_name}' format.")
771
+ raise FMUContainerError(f"Start value is not conforming to {cport.port.type_name} format.")
595
772
 
596
773
  self.start_values[cport] = value
597
774
 
@@ -658,6 +835,14 @@ class FMUContainer:
658
835
  if fmu.step_size and fmu.capabilities["canHandleVariableCommunicationStepSize"] == "false":
659
836
  freq_set.add(int(1.0/fmu.step_size))
660
837
 
838
+ if not freq_set:
839
+ # all involved FMUs can Handle Variable Communication StepSize
840
+ step_size_max = 0
841
+ for fmu in self.involved_fmu.values():
842
+ if fmu.step_size > step_size_max:
843
+ step_size_max = fmu.step_size
844
+ return step_size_max
845
+
661
846
  common_freq = math.gcd(*freq_set)
662
847
  try:
663
848
  step_size = 1.0 / float(common_freq)
@@ -787,10 +972,20 @@ class FMUContainer:
787
972
  index_offset = 2 # index of output ports. Start at 2 to skip "time" port
788
973
 
789
974
  # Local variable should be first to ensure to attribute them the lowest VR.
975
+ nb_clocks = 0
790
976
  for link in self.links.values():
791
977
  self.vr_table.set_link_vr(link)
792
- port_local_def = link.cport_from.port.xml(link.vr, name=link.name, causality='local',
793
- fmi_version=self.fmi_version)
978
+ if link.cport_from:
979
+ port_local_def = link.cport_from.port.xml(link.vr, name=link.name, causality='local',
980
+ fmi_version=self.fmi_version)
981
+ else:
982
+ # LS-BUS allow Clock generated by fmi-importer
983
+ port = EmbeddedFMUPort("Clock",
984
+ {"name": "", "valueReference": -1, "intervalVariability": "triggered"},
985
+ fmi_version=3)
986
+ port_local_def = port.xml(link.vr, name=f"container.clock{nb_clocks}", causality='local', fmi_version=self.fmi_version)
987
+ nb_clocks += 1
988
+
794
989
  if port_local_def:
795
990
  print(f" {port_local_def}", file=xml_file)
796
991
  index_offset += 1
@@ -858,6 +1053,7 @@ class FMUContainer:
858
1053
  "</fmiModelDescription>")
859
1054
 
860
1055
  def make_fmu_txt(self, txt_file, step_size: float, mt: bool, profiling: bool, sequential: bool):
1056
+ print("# Version 3", file=txt_file)
861
1057
  print("# Container flags <MT> <Profiling> <Sequential>", file=txt_file)
862
1058
  flags = [ str(int(flag == True)) for flag in (mt, profiling, sequential)]
863
1059
  print(" ".join(flags), file=txt_file)
@@ -868,56 +1064,61 @@ class FMUContainer:
868
1064
  print(f"{len(self.involved_fmu)}", file=txt_file)
869
1065
  fmu_rank: Dict[str, int] = {}
870
1066
  for i, fmu in enumerate(self.involved_fmu.values()):
871
- print(f"{fmu.name} {fmu.fmi_version}", file=txt_file)
1067
+ print(f"{fmu.name} {fmu.fmi_version} {int(fmu.has_event_mode)}", file=txt_file)
872
1068
  print(f"{fmu.model_identifier}", file=txt_file)
873
1069
  print(f"{fmu.guid}", file=txt_file)
874
1070
  fmu_rank[fmu.name] = i
875
1071
 
876
1072
  # Prepare data structure
877
- inputs_per_type: Dict[str, List[ContainerInput]] = {} # Container's INPUT
878
- outputs_per_type: Dict[str, List[ContainerPort]] = {} # Container's OUTPUT
1073
+ inputs_per_type: Dict[str, List[ContainerInput]] = defaultdict(list) # Container's INPUT
1074
+ outputs_per_type: Dict[str, List[ContainerPort]] = defaultdict(list) # Container's OUTPUT
879
1075
 
880
- inputs_fmu_per_type: Dict[str, Dict[str, Dict[ContainerPort, int]]] = {} # [type][fmu]
881
- start_values_fmu_per_type = {}
882
- outputs_fmu_per_type = {}
883
- local_per_type: Dict[str, List[int]] = {}
884
- links_per_fmu: Dict[str, List[Link]] = {}
885
-
886
- for type_name in EmbeddedFMUPort.ALL_TYPES:
887
- inputs_per_type[type_name] = []
888
- outputs_per_type[type_name] = []
889
- local_per_type[type_name] = []
1076
+ fmu_io_list = FMUIOList(self.vr_table)
1077
+ clock_list = ClockList(self.involved_fmu)
890
1078
 
891
- inputs_fmu_per_type[type_name] = {}
892
- start_values_fmu_per_type[type_name] = {}
893
- outputs_fmu_per_type[type_name] = {}
894
-
895
- for fmu in self.involved_fmu.values():
896
- inputs_fmu_per_type[type_name][fmu.name] = {}
897
- start_values_fmu_per_type[type_name][fmu.name] = {}
898
- outputs_fmu_per_type[type_name][fmu.name] = {}
1079
+ local_per_type: Dict[str, List[int]] = defaultdict(list)
1080
+ links_per_fmu: Dict[str, List[Link]] = defaultdict(list)
899
1081
 
900
1082
  # Fill data structure
901
1083
  # Inputs
902
1084
  for input_port_name, input_port in self.inputs.items():
903
1085
  inputs_per_type[input_port.type_name].append(input_port)
1086
+
1087
+ # Start values
904
1088
  for input_port, value in self.start_values.items():
905
- start_values_fmu_per_type[input_port.port.type_name][input_port.fmu.name][input_port] = value
1089
+ fmu_io_list.add_start_value(input_port, value)
1090
+
906
1091
  # Outputs
907
1092
  for output_port_name, output_port in self.outputs.items():
908
1093
  outputs_per_type[output_port.port.type_name].append(output_port)
1094
+
909
1095
  # Links
910
1096
  for link in self.links.values():
911
- local_per_type[link.cport_from.port.type_name].append(link.vr)
912
- outputs_fmu_per_type[link.cport_from.port.type_name][link.cport_from.fmu.name][link.cport_from] = link.vr
1097
+ # FMU Outputs
1098
+ if link.cport_from:
1099
+ local_per_type[link.cport_from.port.type_name].append(link.vr)
1100
+ fmu_io_list.add_output(link.cport_from, link.vr)
1101
+ else:
1102
+ local_per_type["clock"].append(link.vr)
1103
+ for cport_to in link.cport_to_list:
1104
+ if cport_to.fmu.ls.is_bus:
1105
+ logger.info(f"LS-BUS: importer scheduling for '{cport_to.fmu.name}' '{cport_to.port.name}' (clock={cport_to.port.vr}, {link.vr})")
1106
+ clock_list.append(cport_to, link.vr)
1107
+ break
1108
+
1109
+ # FMU Inputs
913
1110
  for cport_to in link.cport_to_list:
914
- if cport_to.port.type_name == link.cport_from.port.type_name:
915
- inputs_fmu_per_type[cport_to.port.type_name][cport_to.fmu.name][cport_to] = link.vr
916
- else:
917
- local_per_type[cport_to.port.type_name].append(link.vr_converted[cport_to.port.type_name])
918
- links_per_fmu.setdefault(link.cport_from.fmu.name, []).append(link)
919
- inputs_fmu_per_type[cport_to.port.type_name][cport_to.fmu.name][cport_to] = link.vr_converted[cport_to.port.type_name]
920
-
1111
+ if link.cport_from is not None or not cport_to.fmu.ls.is_bus:
1112
+ # LS-BUS allows, importer to feed clock signal. In this case, cport_from is None
1113
+ # FMU will be fed directly by importer, no need to add inpunt link!
1114
+ if link.cport_from is None or cport_to.port.type_name == link.cport_from.port.type_name:
1115
+ local_vr = link.vr
1116
+ else:
1117
+ local_per_type[cport_to.port.type_name].append(link.vr_converted[cport_to.port.type_name])
1118
+ links_per_fmu[link.cport_from.fmu.name].append(link)
1119
+ local_vr = link.vr_converted[cport_to.port.type_name]
1120
+
1121
+ fmu_io_list.add_input(cport_to, local_vr)
921
1122
 
922
1123
  print(f"# NB local variables:", ", ".join(EmbeddedFMUPort.ALL_TYPES), file=txt_file)
923
1124
  nb_local = [f"{self.vr_table.nb_local(type_name)}" for type_name in EmbeddedFMUPort.ALL_TYPES]
@@ -952,24 +1153,7 @@ class FMUContainer:
952
1153
 
953
1154
  # LINKS
954
1155
  for fmu in self.involved_fmu.values():
955
- for type_name in EmbeddedFMUPort.ALL_TYPES:
956
- print(f"# Inputs of {fmu.name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
957
- print(len(inputs_fmu_per_type[type_name][fmu.name]), file=txt_file)
958
- for input_port, vr in inputs_fmu_per_type[type_name][fmu.name].items():
959
- print(f"{vr} {input_port.port.vr}", file=txt_file)
960
-
961
- for type_name in EmbeddedFMUPort.ALL_TYPES:
962
- print(f"# Start values of {fmu.name} - {type_name}: <FMU_VR> <RESET> <VALUE>", file=txt_file)
963
- print(len(start_values_fmu_per_type[type_name][fmu.name]), file=txt_file)
964
- for input_port, value in start_values_fmu_per_type[type_name][fmu.name].items():
965
- reset = 1 if input_port.port.causality == "input" else 0
966
- print(f"{input_port.port.vr} {reset} {value}", file=txt_file)
967
-
968
- for type_name in EmbeddedFMUPort.ALL_TYPES:
969
- print(f"# Outputs of {fmu.name} - {type_name}: <VR> <FMU_VR>", file=txt_file)
970
- print(len(outputs_fmu_per_type[type_name][fmu.name]), file=txt_file)
971
- for output_port, vr in outputs_fmu_per_type[type_name][fmu.name].items():
972
- print(f"{vr} {output_port.port.vr}", file=txt_file)
1156
+ fmu_io_list.write_txt(fmu.name, txt_file)
973
1157
 
974
1158
  print(f"# Conversion table of {fmu.name}: <VR_FROM> <VR_TO> <CONVERSION>", file=txt_file)
975
1159
  try:
@@ -986,6 +1170,9 @@ class FMUContainer:
986
1170
  except KeyError:
987
1171
  print("0", file=txt_file)
988
1172
 
1173
+ # CLOCKS
1174
+ clock_list.write_txt(txt_file)
1175
+
989
1176
  @staticmethod
990
1177
  def long_path(path: Union[str, Path]) -> str:
991
1178
  # https://stackoverflow.com/questions/14075465/copy-a-file-with-a-too-long-path-to-another-directory-in-python
@@ -0,0 +1,35 @@
1
+ import logging
2
+ import xml.etree.ElementTree as ET
3
+
4
+ from pathlib import Path
5
+ from typing import *
6
+
7
+ logger = logging.getLogger("fmu_manipulation_toolbox")
8
+
9
+ class LayeredStandard:
10
+ def __init__(self, directory: Union[Path, str]):
11
+ self.is_bus = False
12
+ self.standards: List[str] = []
13
+
14
+ if isinstance(directory, Path):
15
+ self.directory = directory
16
+ else:
17
+ self.directory = Path(directory)
18
+
19
+ self.parse_lsbus()
20
+
21
+ def parse_lsbus(self):
22
+ filename = self.directory / "extra" / "org.fmi-standard.fmi-ls-bus" / "fmi-ls-manifest.xml"
23
+ if filename.exists():
24
+ xml = ET.parse(filename)
25
+ root = xml.getroot()
26
+ root.get("isBusSimulationFMU", "")
27
+ self.is_bus = root.get("isBusSimulationFMU") == "true"
28
+
29
+ self.standards.append("LS-BUS")
30
+
31
+ def __len__(self):
32
+ return len(self.standards)
33
+
34
+ def __repr__(self):
35
+ return ", ".join(self.standards)
@@ -19,7 +19,7 @@ class FMU:
19
19
  FMI2_TYPES = ('Real', 'Integer', 'String', 'Boolean', 'Enumeration')
20
20
  FMI3_TYPES = ('Float64', 'Float32',
21
21
  'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
22
- 'String', 'Boolean', 'Enumeration')
22
+ 'String', 'Boolean', 'Enumeration', 'Clock', 'Binary')
23
23
 
24
24
  def __init__(self, fmu_filename):
25
25
  self.fmu_filename = fmu_filename
@@ -48,7 +48,7 @@ class FMUSplitter:
48
48
  self.dir_set = self.get_dir_set()
49
49
 
50
50
  if "resources/container.txt" not in self.filenames_list:
51
- raise FMUSplitterError(f"FMU file {self.fmu_filename} is not an FMU Container.")
51
+ raise FMUSplitterError(f"FMU file {self.fmu_filename} is not an FMU Container.")
52
52
 
53
53
  self.directory.mkdir(exist_ok=True)
54
54
  logger.info(f"Preparing to split '{self.fmu_filename}' into '{self.directory}'")
@@ -123,6 +123,7 @@ class FMUSplitterDescription:
123
123
  "auto_local": False,
124
124
  "auto_link": False,
125
125
  }
126
+ self.format = 0
126
127
  self.fmu_filename_list = []
127
128
 
128
129
  # used for modelDescription.xml parsing
@@ -137,6 +138,7 @@ class FMUSplitterDescription:
137
138
  def get_line(file):
138
139
  for line in file:
139
140
  line = line.decode('utf-8').strip()
141
+ logger.debug(line)
140
142
  if line and not line.startswith("#"):
141
143
  return line
142
144
  raise StopIteration
@@ -263,6 +265,17 @@ class FMUSplitterDescription:
263
265
  fmu_vr = int(tokens[2])
264
266
  self.add_port(fmi_type, fmu_id, fmu_vr, container_vr)
265
267
 
268
+ @staticmethod
269
+ def get_nb(line):
270
+ try:
271
+ (nb_str, _) = line.split(" ")
272
+ nb = int(nb_str)
273
+ except ValueError:
274
+ nb = int(line)
275
+
276
+ return nb
277
+
278
+
266
279
  def parse_txt_file(self, txt_filename: str):
267
280
  self.parse_model_description(str(Path(txt_filename).parent.parent), ".")
268
281
  logger.debug(f"Parsing container file '{txt_filename}'")
@@ -275,6 +288,8 @@ class FMUSplitterDescription:
275
288
 
276
289
  for fmi_type in self.supported_fmi_types:
277
290
  nb_input = int(self.get_line(file))
291
+ logger.debug(f"INPUT of {fmu_filename} {fmi_type} : {nb_input}")
292
+
278
293
  for i in range(nb_input):
279
294
  local, vr = self.get_line(file).split(" ")
280
295
  try:
@@ -285,10 +300,26 @@ class FMUSplitterDescription:
285
300
  link.to_port.append(FMUSplitterPort(fmu_filename,
286
301
  self.vr_to_name[fmu_filename][fmi_type][int(vr)]["name"]))
287
302
 
288
- for fmi_type in self.supported_fmi_types:
303
+ #clocked
304
+ if not fmi_type == "clock":
305
+ nb_input = self.get_nb(self.get_line(file))
306
+ logger.debug(f"INPUT of {fmu_filename} {fmi_type} : CLOCKED {nb_input}")
307
+ for i in range(nb_input):
308
+ local, _clock, vr = self.get_line(file).split(" ")
309
+ try:
310
+ link = self.links[fmi_type][local]
311
+ except KeyError:
312
+ link = FMUSplitterLink()
313
+ self.links[fmi_type][local] = link
314
+ link.to_port.append(FMUSplitterPort(fmu_filename,
315
+ self.vr_to_name[fmu_filename][fmi_type][int(vr)]["name"]))
316
+
317
+ for fmi_type in self.supported_fmi_types[:-2]:
289
318
  nb_start = int(self.get_line(file))
319
+ logger.debug(f"nb start for {fmu_filename} {fmi_type} : {nb_start}")
290
320
  for i in range(nb_start):
291
321
  tokens = self.get_line(file).split(" ")
322
+ logger.error(tokens)
292
323
  vr = int(tokens[0])
293
324
  value = tokens[-1]
294
325
  start_definition = [fmu_filename, self.vr_to_name[fmu_filename][fmi_type][vr]["name"],
@@ -301,6 +332,7 @@ class FMUSplitterDescription:
301
332
  # Output per FMUs
302
333
  for fmi_type in self.supported_fmi_types:
303
334
  nb_output = int(self.get_line(file))
335
+ logger.debug(f"OUTPUT of {fmu_filename} {fmi_type} : {nb_output}")
304
336
 
305
337
  for i in range(nb_output):
306
338
  local, vr = self.get_line(file).split(" ")
@@ -311,6 +343,21 @@ class FMUSplitterDescription:
311
343
  self.links[fmi_type][local] = link
312
344
  link.from_port = FMUSplitterPort(fmu_filename,
313
345
  self.vr_to_name[fmu_filename][fmi_type][int(vr)]["name"])
346
+
347
+ if not fmi_type == "clock":
348
+ nb_output = self.get_nb(self.get_line(file))
349
+ logger.debug(f"OUTPUT {fmi_type} : CLOCKED {nb_output}")
350
+
351
+ for i in range(nb_output):
352
+ local, _clock, vr = self.get_line(file).split(" ")
353
+ try:
354
+ link = self.links[fmi_type][local]
355
+ except KeyError:
356
+ link = FMUSplitterLink()
357
+ self.links[fmi_type][local] = link
358
+ link.from_port = FMUSplitterPort(fmu_filename,
359
+ self.vr_to_name[fmu_filename][fmi_type][int(vr)]["name"])
360
+
314
361
  #conversion
315
362
  nb_conversion = int(self.get_line(file))
316
363
  for i in range(nb_conversion):
@@ -0,0 +1,137 @@
1
+ import logging
2
+ import xml.etree.ElementTree as ET
3
+
4
+ from collections import Counter
5
+ from pathlib import Path
6
+ from typing import *
7
+
8
+ logger = logging.getLogger("fmu_manipulation_toolbox")
9
+
10
+ class Terminal:
11
+ def __init__(self, name: str, kind: str, matching: str):
12
+ self.name = name
13
+ self.kind = str
14
+ self.matching = matching
15
+ self.members:Dict[str, str] = {}
16
+ self.sub_terminals: Dict[str, Terminal] = {}
17
+
18
+ def add_member(self, member_name, variable_name):
19
+ self.members[member_name] = variable_name
20
+
21
+ def __repr__(self):
22
+ return f"{self.name} ({len(self.members)} signals)"
23
+
24
+ def __eq__(self, other):
25
+ if isinstance(other, Terminal):
26
+ return self.kind == other.kind and self.matching == other.matching
27
+ else:
28
+ return False
29
+
30
+ def connect(self, other) -> List[Tuple[str, str]]:
31
+ links = []
32
+
33
+ for sub_terminal in self.sub_terminals.values():
34
+ other_sub_terminal = other.sub_terminals[sub_terminal.name]
35
+ links += sub_terminal.connect(other_sub_terminal)
36
+
37
+
38
+ if isinstance(other, Terminal):
39
+ if self.matching == "plug":
40
+ return self.connect_plug(other)
41
+ elif self.matching == "bus":
42
+ return self.connect_bus(other)
43
+ elif self.matching == "sequence":
44
+ return self.connect_sequence(other)
45
+ elif self.matching == "org.fmi-ls-bus.transceiver":
46
+ return self.connect_transceiver(other)
47
+ else:
48
+ logger.error(f"Rule '{self.matching}' not defined to connect Terminal '{self.name}'")
49
+ else:
50
+ logger.error(f"Cannot connect Terminal '{self.name}' to '{other}'.")
51
+
52
+ return links
53
+
54
+ def connect_plug(self, other) -> List[Tuple[str, str]]:
55
+ links = []
56
+ if Counter(self.members.keys()) == Counter(other.members.keys()):
57
+ for member_name, member in self.members.items():
58
+ other_member = other.members[member_name]
59
+ links.append((member, other_member))
60
+ else:
61
+ logger.error(f"PLUG Terminal '{self.name}' does not exactly fit Terminal '{other.name}'")
62
+
63
+ return links
64
+
65
+ def connect_bus(self, other) -> List[Tuple[str, str]]:
66
+ links = []
67
+ for member_name, member in self.members.items():
68
+ if member_name in other.members:
69
+ other_member = other.members[member_name]
70
+ links.append((member, other_member))
71
+ return links
72
+
73
+ def connect_sequence(self, other) -> List[Tuple[str, str]]:
74
+ links = []
75
+ if len(self.members) == len(other.members):
76
+ for member, other_member in zip(self.members.values(), other.members.values()):
77
+ links.append((member, other_member))
78
+ else:
79
+ logger.error(f"SEQUENCE Terminal '{self.name}' does not exactly fit Terminal '{other.name}'")
80
+ return links
81
+
82
+ def connect_transceiver(self, other) -> List[Tuple[str, str]]:
83
+ return [(self.members["Tx_Data"], other.members["Rx_Data"]),
84
+ (self.members["Tx_Clock"], other.members["Rx_Clock"]),
85
+ (self.members["Rx_Data"], other.members["Tx_Data"]),
86
+ (self.members["Rx_Clock"], other.members["Tx_Clock"])]
87
+
88
+
89
+ class Terminals:
90
+ FILENAME = "terminalsAndIcons.xml"
91
+ def __init__(self, directory: Union[Path, str]):
92
+ self.terminals: OrderedDict[str, Terminal] = OrderedDict()
93
+
94
+ if isinstance(directory, str):
95
+ directory = Path(directory)
96
+
97
+ filename = directory / "terminalsAndIcons" / self.FILENAME
98
+ if filename.exists():
99
+
100
+ xml = ET.parse(filename)
101
+
102
+ try:
103
+ for element in xml.getroot()[0]:
104
+ if element.tag == "Terminal":
105
+ terminal = self.add_terminal(element)
106
+ logger.debug(f"Terminal '{terminal.name}' defined with {len(terminal.members)} signals")
107
+ except IndexError:
108
+ logger.error(f"{filename} is wrongly formated.")
109
+
110
+ def __len__(self):
111
+ return len(self.terminals)
112
+
113
+ def __contains__(self, item):
114
+ return item in self.terminals
115
+
116
+ def __getitem__(self, item):
117
+ return self.terminals[item]
118
+
119
+ def add_terminal(self, element) -> Terminal:
120
+ name = element.get("name")
121
+ matching = element.get("matchingRule")
122
+ kind = element.get("terminalKind")
123
+
124
+ terminal = Terminal(name, kind, matching)
125
+ self.add_member_from_terminal(terminal, element)
126
+
127
+ self.terminals[name] = terminal
128
+
129
+ return terminal
130
+
131
+ def add_member_from_terminal(self, terminal, element):
132
+ for child in element:
133
+ if child.tag == "TerminalMemberVariable":
134
+ terminal.add_member(child.get("memberName"), child.get("variableName"))
135
+ elif child.tag == "Terminal":
136
+ sub_terminal = self.add_terminal(child)
137
+ terminal.subterminals[sub_terminal.name] = sub_terminal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9.1.3
3
+ Version: 1.9.2b1
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
@@ -1,15 +1,17 @@
1
1
  fmu_manipulation_toolbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  fmu_manipulation_toolbox/__main__.py,sha256=Ixgo_5YS_khXseMujNPsVzaerkHPugLPYPV7FIi5umo,364
3
- fmu_manipulation_toolbox/__version__.py,sha256=7RqWe33xZWbgMkw1AHAoCVLVoTMkZYeV-jkNGXthJXM,11
3
+ fmu_manipulation_toolbox/__version__.py,sha256=eFoY0dV9P-7aIw41HjOXEMNKuPwNW0s1_trJUzchB-g,11
4
4
  fmu_manipulation_toolbox/assembly.py,sha256=-i9iQLKiPyjdFZyxHmB1FgL3NYmRPfTWVs9lf3fC5sE,27022
5
5
  fmu_manipulation_toolbox/checker.py,sha256=RmIYd6s4p2S9wdv-uSILfVm__QTZSUJCiq6hr_G5rxA,3119
6
- fmu_manipulation_toolbox/container.py,sha256=X5XpacRqAGCRCjxpU6BAekGBfoNvVyx8C9MppUqwLz0,47130
6
+ fmu_manipulation_toolbox/container.py,sha256=HMi3tuMjdrlgVkbzjp5_48OM-iqNFnqc1OsR8O0e9mE,55634
7
7
  fmu_manipulation_toolbox/gui.py,sha256=-DyWY2MoECr3FGRYdS60Ltoayhd3O0uOCtkQ4YXE8xc,29201
8
8
  fmu_manipulation_toolbox/gui_style.py,sha256=s6WdrnNd_lCMWhuBf5LKK8wrfLXCU7pFTLUfvqkJVno,6633
9
9
  fmu_manipulation_toolbox/help.py,sha256=j8xmnCrwQpaW-SZ8hSqA1dlTXgaqzQWc4Yr3RH_oqck,6012
10
- fmu_manipulation_toolbox/operations.py,sha256=ZV6BRXszwvmqJwUCnTFkWfIYvfK4i-OHN69kffkmGbI,21075
10
+ fmu_manipulation_toolbox/ls.py,sha256=wmyoKrvDLXpL-PFz6cUhLLqxDMD5E9L_P4KswWpQHsk,975
11
+ fmu_manipulation_toolbox/operations.py,sha256=rfeZiCHuVr5-M4lC496feYl1nrRBheP_W9AsJHtMm58,21094
11
12
  fmu_manipulation_toolbox/remoting.py,sha256=N25MDFkIcEWe9CIT1M4L9kea3j-8E7i2I1VOI6zIAdw,3876
12
- fmu_manipulation_toolbox/split.py,sha256=yFaW6yhvFjOXpRynWzitR7o7uSrxb-RxeFzmQNxUFHI,14147
13
+ fmu_manipulation_toolbox/split.py,sha256=q6a5rZ4s7E24nf4BiebUuoneBEj9j293f2LiAaSDKnE,16345
14
+ fmu_manipulation_toolbox/terminals.py,sha256=mGGS4tdE6cJuz-2zvwc7drpmT0QJ7YPe8ENw2UGlEHA,5062
13
15
  fmu_manipulation_toolbox/version.py,sha256=OhBLkZ1-nhC77kyvffPNAf6m8OZe1bYTnNf_PWs1NvM,392
14
16
  fmu_manipulation_toolbox/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
17
  fmu_manipulation_toolbox/cli/fmucontainer.py,sha256=oYidLBvXyDApsY7JVGlCL5g35yc1F9Gr8tFjYY7077s,4981
@@ -33,7 +35,7 @@ fmu_manipulation_toolbox/resources/icon_fmu.png,sha256=EuygB2xcoM2WAfKKdyKG_UvTL
33
35
  fmu_manipulation_toolbox/resources/license.txt,sha256=5ODuU8g8pIkK-NMWXu_rjZ6k7gM7b-N2rmg87-2Kmqw,1583
34
36
  fmu_manipulation_toolbox/resources/mask.png,sha256=px1U4hQGL0AmZ4BQPknOVREpMpTSejbah3ntkpqAzFA,3008
35
37
  fmu_manipulation_toolbox/resources/model.png,sha256=EAf_HnZJe8zYGZygerG1MMt2U-tMMZlifzXPj4_iORA,208788
36
- fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=lQEyWwYUqtp80803BEk9jy2gcdF3xtl2GBfXuTFA12M,162960
38
+ fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=LAXXvAWewT4P21esMyCy8DbkbrnSDggSgnS2NUTsrwc,213632
37
39
  fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd,sha256=OGfyJtaJntKypX5KDpuZ-nV1oYLZ6HV16pkpKOmYox4,2731
38
40
  fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd,sha256=HwyV7LBse-PQSv4z1xjmtzPU3Hjnv4mluq9YdSBNHMQ,3704
39
41
  fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd,sha256=JM4j_9q-pc40XYHb28jfT3iV3aYM5JLqD5aRjO72K1E,18939
@@ -53,19 +55,19 @@ fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Type.xsd,sha256=TaHRoUBIFtmdEwBKB
53
55
  fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Unit.xsd,sha256=CK_F2t5LfyQ6eSNJ8soTFMVK9DU8vD2WiMi2MQvjB0g,3746
54
56
  fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Variable.xsd,sha256=3YU-3q1-c-namz7sMe5cxnmOVOJsRSmfWR02PKv3xaU,19171
55
57
  fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd,sha256=YQSBwXt4IsDlyegY8bX-qQHGSfE5TipTPfo2g2yqq1c,3082
56
- fmu_manipulation_toolbox/resources/linux32/client_sm.so,sha256=r20fpb2x4QkRSJLLIk0YMCVCaDDI9TFAY29XFqbX4I4,34664
58
+ fmu_manipulation_toolbox/resources/linux32/client_sm.so,sha256=OjdzO-OEU0UW1D1YvDouikjKAMWYv3f-tkLZ_qb80g4,34756
57
59
  fmu_manipulation_toolbox/resources/linux32/server_sm,sha256=gzKU0BTeaRkvhTMQtHHj3K8uYFyEdyGGn_mZy_jG9xo,21304
58
- fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=r5AmxPgVM06MAEyNOd0-TEpKQtekFTgsrpIuZy_qTdU,32464
59
- fmu_manipulation_toolbox/resources/linux64/container.so,sha256=XHtgW5YJigpQTMw02GQWbwgtFxM7kR2VxBWf0qSVK6s,135520
60
+ fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=UOcyFYhHJyLNmQoLdiBD75nzloBmk77g9i91QjdItL0,32592
61
+ fmu_manipulation_toolbox/resources/linux64/container.so,sha256=426lYFuGlXmUGRhwDl-VW9_pFJO1RxrWbg6Wv-Fekdc,177032
60
62
  fmu_manipulation_toolbox/resources/linux64/server_sm,sha256=MZn6vITN2qpBHYt_RaK2VnFFp00hk8fTALBHmXPtLwc,22608
61
- fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=PFnrKPIqiyAqnpZkn0IQFQ2K3YKatVyvUYjwKwvh3ks,17920
62
- fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=Sc88uHGtjlFLxPZDjC8DJHZXts-9chXK-niGJXaJiUk,15360
63
- fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=6_bxWxz9rGZUn5rrzzgeMUqt6DHbKtD-huskGMxwG_o,20992
64
- fmu_manipulation_toolbox/resources/win64/container.dll,sha256=Vqhq-92NdAzw_XxzvHyr8ANmFV0sQ5Tth7uji-qsCjI,99328
65
- fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=O-mS7U7SFjTkvrr6KBiJVARCAOJta1djBru8FHoB4yg,18432
66
- fmu_manipulation_toolbox-1.9.1.3.dist-info/licenses/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
67
- fmu_manipulation_toolbox-1.9.1.3.dist-info/METADATA,sha256=9bqVKEXjTV7cVq-wy4VLP5bICdnHY8-QMuUDumd0mvo,1157
68
- fmu_manipulation_toolbox-1.9.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
- fmu_manipulation_toolbox-1.9.1.3.dist-info/entry_points.txt,sha256=HjOZkflbI1IuSY8BpOZre20m24M4GDQGCJfPIa7NrlY,264
70
- fmu_manipulation_toolbox-1.9.1.3.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
71
- fmu_manipulation_toolbox-1.9.1.3.dist-info/RECORD,,
63
+ fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=fk9hb9RJ9AqGLpw5jUHy743OGyHXlcYbYLSeJCPUQRg,17920
64
+ fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=LDtvrrmmyVPGBNOx4iljMcNYJuNlSZAf-kl_6hTxKu0,15360
65
+ fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=koFqnnDq-Oe_tlzfNuEGbSvXpFKz9fjPDt_xwaquQqw,21504
66
+ fmu_manipulation_toolbox/resources/win64/container.dll,sha256=8OKu_SQABu-eEDm7YIFbcBmnhZolXoIRpmEaObsTHk8,141824
67
+ fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=Vq5CG6Ll2Pb06J9IKUpV-5VMHUaoALYizIB4PbVtgdc,18432
68
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/licenses/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
69
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/METADATA,sha256=tXtq9Rodjl3GQ6FOdB7SeGV_yv4htrFXpsict3ffsvo,1157
70
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/entry_points.txt,sha256=HjOZkflbI1IuSY8BpOZre20m24M4GDQGCJfPIa7NrlY,264
72
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
73
+ fmu_manipulation_toolbox-1.9.2b1.dist-info/RECORD,,