fmu-manipulation-toolbox 1.9.1.2rc1__tar.gz → 1.9.2b1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/PKG-INFO +1 -1
  2. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/__version__.py +1 -0
  3. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/container.py +265 -78
  4. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/ls.py +35 -0
  5. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/operations.py +1 -1
  6. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  7. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  8. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  9. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  10. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  11. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  12. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  13. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  14. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  15. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/split.py +49 -2
  16. fmu_manipulation_toolbox-1.9.2b1/fmu_manipulation_toolbox/terminals.py +137 -0
  17. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/PKG-INFO +1 -1
  18. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/SOURCES.txt +2 -0
  19. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/tests/test_suite.py +44 -12
  20. fmu_manipulation_toolbox-1.9.1.2rc1/fmu_manipulation_toolbox/__version__.py +0 -1
  21. fmu_manipulation_toolbox-1.9.1.2rc1/fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  22. fmu_manipulation_toolbox-1.9.1.2rc1/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  23. fmu_manipulation_toolbox-1.9.1.2rc1/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  24. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/LICENSE.txt +0 -0
  25. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/README.md +0 -0
  26. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/__init__.py +0 -0
  27. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/__main__.py +0 -0
  28. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/assembly.py +0 -0
  29. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/checker.py +0 -0
  30. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/cli/__init__.py +0 -0
  31. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/cli/fmucontainer.py +0 -0
  32. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/cli/fmusplit.py +0 -0
  33. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/cli/fmutool.py +0 -0
  34. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/cli/utils.py +0 -0
  35. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/gui.py +0 -0
  36. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/gui_style.py +0 -0
  37. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/help.py +0 -0
  38. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/remoting.py +0 -0
  39. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
  40. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
  41. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
  42. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
  43. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
  44. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
  45. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/container.png +0 -0
  46. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  47. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +0 -0
  48. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +0 -0
  49. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +0 -0
  50. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +0 -0
  51. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +0 -0
  52. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +0 -0
  53. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +0 -0
  54. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Annotation.xsd +0 -0
  55. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3AttributeGroups.xsd +0 -0
  56. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3BuildDescription.xsd +0 -0
  57. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3InterfaceType.xsd +0 -0
  58. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3LayeredStandardManifest.xsd +0 -0
  59. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3ModelDescription.xsd +0 -0
  60. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Terminal.xsd +0 -0
  61. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3TerminalsAndIcons.xsd +0 -0
  62. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Type.xsd +0 -0
  63. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Unit.xsd +0 -0
  64. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Variable.xsd +0 -0
  65. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd +0 -0
  66. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmu.png +0 -0
  67. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  68. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/help.png +0 -0
  69. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/icon-round.png +0 -0
  70. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/icon.png +0 -0
  71. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/icon_fmu.png +0 -0
  72. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/license.txt +0 -0
  73. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  74. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  75. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/mask.png +0 -0
  76. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/resources/model.png +0 -0
  77. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox/version.py +0 -0
  78. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/dependency_links.txt +0 -0
  79. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/entry_points.txt +0 -0
  80. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/requires.txt +0 -0
  81. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/fmu_manipulation_toolbox.egg-info/top_level.txt +0 -0
  82. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/setup.cfg +0 -0
  83. {fmu_manipulation_toolbox-1.9.1.2rc1 → fmu_manipulation_toolbox-1.9.2b1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9.1.2rc1
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
@@ -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