fmu-manipulation-toolbox 1.8__py3-none-any.whl → 1.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fmu_manipulation_toolbox/__version__.py +1 -1
- fmu_manipulation_toolbox/assembly.py +140 -112
- fmu_manipulation_toolbox/cli.py +2 -2
- fmu_manipulation_toolbox/fmu_container.py +7 -12
- fmu_manipulation_toolbox/gui.py +33 -31
- fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
- fmu_manipulation_toolbox/resources/mask.png +0 -0
- fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
- fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
- fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/METADATA +1 -1
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/RECORD +18 -17
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/LICENSE.txt +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/WHEEL +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/entry_points.txt +0 -0
- {fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/top_level.txt +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
'V1.8'
|
|
1
|
+
'V1.8.1'
|
|
@@ -7,7 +7,7 @@ import uuid
|
|
|
7
7
|
import xml.parsers.expat
|
|
8
8
|
import zipfile
|
|
9
9
|
|
|
10
|
-
from .fmu_container import FMUContainer
|
|
10
|
+
from .fmu_container import FMUContainer
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
13
13
|
|
|
@@ -31,18 +31,26 @@ class Connection:
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class AssemblyNode:
|
|
34
|
-
def __init__(self, name: str, step_size: float = None, mt=False, profiling=False,
|
|
34
|
+
def __init__(self, name: Optional[str], step_size: float = None, mt=False, profiling=False,
|
|
35
35
|
auto_link=True, auto_input=True, auto_output=True):
|
|
36
36
|
self.name = name
|
|
37
|
-
|
|
37
|
+
if step_size:
|
|
38
|
+
try:
|
|
39
|
+
self.step_size = float(step_size)
|
|
40
|
+
except ValueError:
|
|
41
|
+
logger.warning(f"Step size '{step_size}' is incorrect format.")
|
|
42
|
+
self.step_size = None
|
|
43
|
+
else:
|
|
44
|
+
self.step_size = None
|
|
38
45
|
self.mt = mt
|
|
39
46
|
self.profiling = profiling
|
|
40
47
|
self.auto_link = auto_link
|
|
41
48
|
self.auto_input = auto_input
|
|
42
49
|
self.auto_output = auto_output
|
|
43
|
-
self.children: List[AssemblyNode] = []
|
|
44
50
|
|
|
45
|
-
self.
|
|
51
|
+
self.parent: Optional[AssemblyNode] = None
|
|
52
|
+
self.children: List[AssemblyNode] = [] # sub-containers
|
|
53
|
+
self.fmu_names_list: Set[str] = set() # FMUs contained at this level
|
|
46
54
|
self.input_ports: Dict[Port, str] = {}
|
|
47
55
|
self.output_ports: Dict[Port, str] = {}
|
|
48
56
|
self.start_values: Dict[Port, str] = {}
|
|
@@ -53,9 +61,14 @@ class AssemblyNode:
|
|
|
53
61
|
if sub_node.name is None:
|
|
54
62
|
sub_node.name = str(uuid.uuid4())+".fmu"
|
|
55
63
|
|
|
64
|
+
if sub_node.parent is not None:
|
|
65
|
+
raise AssemblyError(f"Internal Error: AssemblyNode {sub_node.name} is already parented.")
|
|
66
|
+
|
|
67
|
+
sub_node.parent = self
|
|
56
68
|
self.fmu_names_list.add(sub_node.name)
|
|
57
69
|
self.children.append(sub_node)
|
|
58
70
|
|
|
71
|
+
|
|
59
72
|
def add_fmu(self, fmu_name: str):
|
|
60
73
|
self.fmu_names_list.add(fmu_name)
|
|
61
74
|
|
|
@@ -135,9 +148,10 @@ class Assembly:
|
|
|
135
148
|
self.transient_dirnames: Set[Path] = set()
|
|
136
149
|
|
|
137
150
|
if not fmu_directory.is_dir():
|
|
138
|
-
raise
|
|
151
|
+
raise AssemblyError(f"FMU directory is not valid: '{fmu_directory}'")
|
|
139
152
|
|
|
140
|
-
self.
|
|
153
|
+
self.input_pathname = fmu_directory / self.filename
|
|
154
|
+
self.description_pathname = self.input_pathname # For inclusion in FMU
|
|
141
155
|
self.root = None
|
|
142
156
|
self.read()
|
|
143
157
|
|
|
@@ -169,7 +183,7 @@ class Assembly:
|
|
|
169
183
|
elif self.filename.suffix == ".csv":
|
|
170
184
|
self.read_csv()
|
|
171
185
|
else:
|
|
172
|
-
raise
|
|
186
|
+
raise AssemblyError(f"Not supported file format '{self.filename}")
|
|
173
187
|
|
|
174
188
|
def write(self, filename: str):
|
|
175
189
|
if filename.endswith(".csv"):
|
|
@@ -185,9 +199,9 @@ class Assembly:
|
|
|
185
199
|
mt=self.default_mt, profiling=self.default_profiling,
|
|
186
200
|
auto_input=self.default_auto_input, auto_output=self.default_auto_output)
|
|
187
201
|
|
|
188
|
-
with open(self.
|
|
202
|
+
with open(self.input_pathname) as file:
|
|
189
203
|
reader = csv.reader(file, delimiter=';')
|
|
190
|
-
self.
|
|
204
|
+
self._check_csv_headers(reader)
|
|
191
205
|
for i, row in enumerate(reader):
|
|
192
206
|
if not row or row[0][0] == '#': # skip blank line of comment
|
|
193
207
|
continue
|
|
@@ -198,36 +212,19 @@ class Assembly:
|
|
|
198
212
|
logger.error(f"Line #{i+2}: expecting 5 columns. Line skipped.")
|
|
199
213
|
continue
|
|
200
214
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
logger.error(f"Line #{i+2}: {e}. Line skipped.")
|
|
208
|
-
continue
|
|
209
|
-
else:
|
|
210
|
-
logger.error(f"Line #{i+2}: unexpected rule '{rule}'. Line skipped.")
|
|
211
|
-
|
|
212
|
-
def write_csv(self, filename: Union[str, Path]):
|
|
213
|
-
if self.root.children:
|
|
214
|
-
raise AssemblyError("This assembly is not flat. Cannot export to CSV file.")
|
|
215
|
+
try:
|
|
216
|
+
self._read_csv_rule(self.root, rule.upper(),
|
|
217
|
+
from_fmu_filename, from_port_name, to_fmu_filename, to_port_name)
|
|
218
|
+
except AssemblyError as e:
|
|
219
|
+
logger.error(f"Line #{i+2}: {e}. Line skipped.")
|
|
220
|
+
continue
|
|
215
221
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
for port, target in self.root.output_ports.items():
|
|
223
|
-
outfile.write(f"OUTPUT;{port.fmu_name};{port.port_name};;{target}\n")
|
|
224
|
-
for link in self.root.links:
|
|
225
|
-
outfile.write(f"LINK;{link.from_port.fmu_name};{link.from_port.port_name};"
|
|
226
|
-
f"{link.to_port.fmu_name};{link.to_port.port_name}\n")
|
|
227
|
-
for port, value in self.root.start_values.items():
|
|
228
|
-
outfile.write(f"START;{port.fmu_name};{port.port_name};{value};\n")
|
|
229
|
-
for port in self.root.drop_ports:
|
|
230
|
-
outfile.write(f"DROP;{port.fmu_name};{port.port_name};;\n")
|
|
222
|
+
@staticmethod
|
|
223
|
+
def _check_csv_headers(reader):
|
|
224
|
+
headers = next(reader)
|
|
225
|
+
headers_lowered = [h.lower() for h in headers]
|
|
226
|
+
if not headers_lowered == ["rule", "from_fmu", "from_port", "to_fmu", "to_port"]:
|
|
227
|
+
raise AssemblyError("Header (1st line of the file) is not well formatted.")
|
|
231
228
|
|
|
232
229
|
@staticmethod
|
|
233
230
|
def _read_csv_rule(node: AssemblyNode, rule: str, from_fmu_filename: str, from_port_name: str,
|
|
@@ -264,114 +261,145 @@ class Assembly:
|
|
|
264
261
|
raise AssemblyError("Missing START ports information.")
|
|
265
262
|
|
|
266
263
|
node.add_start_value(from_fmu_filename, from_port_name, to_fmu_filename)
|
|
267
|
-
|
|
264
|
+
else:
|
|
265
|
+
raise AssemblyError(f"unexpected rule '{rule}'. Line skipped.")
|
|
268
266
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
def write_csv(self, filename: Union[str, Path]):
|
|
268
|
+
if self.root.children:
|
|
269
|
+
raise AssemblyError("This assembly is not flat. Cannot export to CSV file.")
|
|
270
|
+
|
|
271
|
+
with open(self.fmu_directory / filename, "wt") as outfile:
|
|
272
|
+
outfile.write("rule;from_fmu;from_port;to_fmu;to_port\n")
|
|
273
|
+
for fmu in self.root.fmu_names_list:
|
|
274
|
+
outfile.write(f"FMU;{fmu};;;\n")
|
|
275
|
+
for port, source in self.root.input_ports.items():
|
|
276
|
+
outfile.write(f"INPUT;;{source};{port.fmu_name};{port.port_name}\n")
|
|
277
|
+
for port, target in self.root.output_ports.items():
|
|
278
|
+
outfile.write(f"OUTPUT;{port.fmu_name};{port.port_name};;{target}\n")
|
|
279
|
+
for link in self.root.links:
|
|
280
|
+
outfile.write(f"LINK;{link.from_port.fmu_name};{link.from_port.port_name};"
|
|
281
|
+
f"{link.to_port.fmu_name};{link.to_port.port_name}\n")
|
|
282
|
+
for port, value in self.root.start_values.items():
|
|
283
|
+
outfile.write(f"START;{port.fmu_name};{port.port_name};{value};\n")
|
|
284
|
+
for port in self.root.drop_ports:
|
|
285
|
+
outfile.write(f"DROP;{port.fmu_name};{port.port_name};;\n")
|
|
274
286
|
|
|
275
287
|
def read_json(self):
|
|
276
|
-
with open(self.
|
|
288
|
+
with open(self.input_pathname) as file:
|
|
277
289
|
try:
|
|
278
290
|
data = json.load(file)
|
|
279
291
|
except json.decoder.JSONDecodeError as e:
|
|
280
|
-
raise
|
|
281
|
-
self.root = self.
|
|
292
|
+
raise AssemblyError(f"Cannot read json: {e}")
|
|
293
|
+
self.root = self._json_decode_node(data)
|
|
282
294
|
if not self.root.name:
|
|
283
295
|
self.root.name = str(self.filename.with_suffix(".fmu"))
|
|
284
296
|
|
|
297
|
+
def _json_decode_node(self, data:Dict) -> AssemblyNode:
|
|
298
|
+
name = data.get("name", None) # 1
|
|
299
|
+
step_size = data.get("step_size", self.default_step_size) # 7
|
|
300
|
+
auto_link = data.get("auto_link", self.default_auto_link) # 4
|
|
301
|
+
auto_input = data.get("auto_input", self.default_auto_input) # 5
|
|
302
|
+
auto_output = data.get("auto_output", self.default_auto_output) # 6
|
|
303
|
+
mt = data.get("mt", self.default_mt) # 2
|
|
304
|
+
profiling = data.get("profiling", self.default_profiling) # 3
|
|
305
|
+
|
|
306
|
+
node = AssemblyNode(name, step_size=step_size, auto_link=auto_link, mt=mt, profiling=profiling,
|
|
307
|
+
auto_input=auto_input, auto_output=auto_output)
|
|
308
|
+
|
|
309
|
+
for key, value in data.items():
|
|
310
|
+
if key in ('name', 'step_size', 'auto_link', 'auto_input', 'auto_output', 'mt', 'profiling'):
|
|
311
|
+
continue # Already read
|
|
312
|
+
|
|
313
|
+
elif key == "container": # 8
|
|
314
|
+
if not isinstance(value, list):
|
|
315
|
+
raise AssemblyError("JSON: 'container' keyword should define a list.")
|
|
316
|
+
for sub_data in value:
|
|
317
|
+
node.add_sub_node(self._json_decode_node(sub_data))
|
|
318
|
+
|
|
319
|
+
elif key == "fmu": # 9
|
|
320
|
+
if not isinstance(value, list):
|
|
321
|
+
raise AssemblyError("JSON: 'fmu' keyword should define a list.")
|
|
322
|
+
for fmu in value:
|
|
323
|
+
node.add_fmu(fmu)
|
|
324
|
+
|
|
325
|
+
elif key == "input": # 10
|
|
326
|
+
self._json_decode_keyword('input', value, node.add_input)
|
|
327
|
+
|
|
328
|
+
elif key == "output": # 11
|
|
329
|
+
self._json_decode_keyword('output', value, node.add_output)
|
|
330
|
+
|
|
331
|
+
elif key == "link": # 12
|
|
332
|
+
self._json_decode_keyword('link', value, node.add_link)
|
|
333
|
+
|
|
334
|
+
elif key == "start": # 13
|
|
335
|
+
self._json_decode_keyword('start', value, node.add_start_value)
|
|
336
|
+
|
|
337
|
+
elif key == "drop": #14
|
|
338
|
+
self._json_decode_keyword('drop', value, node.add_drop_port)
|
|
339
|
+
|
|
340
|
+
else:
|
|
341
|
+
logger.error(f"JSON: unexpected keyword {key}. Skipped.")
|
|
342
|
+
|
|
343
|
+
return node
|
|
344
|
+
|
|
345
|
+
@staticmethod
|
|
346
|
+
def _json_decode_keyword(keyword: str, value, function):
|
|
347
|
+
if not isinstance(value, list):
|
|
348
|
+
raise AssemblyError(f"JSON: '{keyword}' keyword should define a list.")
|
|
349
|
+
for line in value:
|
|
350
|
+
if not isinstance(line, list):
|
|
351
|
+
raise AssemblyError(f"JSON: unexpected '{keyword}' value: {line}.")
|
|
352
|
+
try:
|
|
353
|
+
function(*line)
|
|
354
|
+
except TypeError:
|
|
355
|
+
raise AssemblyError(f"JSON: '{keyword}' value does not contain right number of fields: {line}.")
|
|
356
|
+
|
|
357
|
+
|
|
285
358
|
def write_json(self, filename: Union[str, Path]):
|
|
286
359
|
with open(self.fmu_directory / filename, "wt") as file:
|
|
287
|
-
data = self.
|
|
360
|
+
data = self._json_encode_node(self.root)
|
|
288
361
|
json.dump(data, file, indent=2)
|
|
289
362
|
|
|
290
|
-
def
|
|
363
|
+
def _json_encode_node(self, node: AssemblyNode) -> Dict[str, Any]:
|
|
291
364
|
json_node = dict()
|
|
292
|
-
json_node["name"] = node.name
|
|
293
|
-
json_node["mt"] = node.mt
|
|
294
|
-
json_node["profiling"] = node.profiling
|
|
295
|
-
json_node["auto_link"] = node.auto_link
|
|
365
|
+
json_node["name"] = node.name # 1
|
|
366
|
+
json_node["mt"] = node.mt # 2
|
|
367
|
+
json_node["profiling"] = node.profiling # 3
|
|
368
|
+
json_node["auto_link"] = node.auto_link # 4
|
|
369
|
+
json_node["auto_input"] = node.auto_input # 5
|
|
370
|
+
json_node["auto_output"] = node.auto_output # 6
|
|
371
|
+
|
|
296
372
|
if node.step_size:
|
|
297
|
-
json_node["step_size"] = node.step_size
|
|
373
|
+
json_node["step_size"] = node.step_size # 7
|
|
298
374
|
|
|
299
375
|
if node.children:
|
|
300
|
-
json_node["container"] = [self.
|
|
376
|
+
json_node["container"] = [self._json_encode_node(child) for child in node.children] # 8
|
|
301
377
|
|
|
302
378
|
if node.fmu_names_list:
|
|
303
|
-
json_node["fmu"] = [f"{fmu_name}" for fmu_name in sorted(node.fmu_names_list)]
|
|
379
|
+
json_node["fmu"] = [f"{fmu_name}" for fmu_name in sorted(node.fmu_names_list)] #9
|
|
304
380
|
|
|
305
381
|
if node.input_ports:
|
|
306
|
-
json_node["input"] = [[f"{source}", f"{port.fmu_name}", f"{port.port_name}"]
|
|
382
|
+
json_node["input"] = [[f"{source}", f"{port.fmu_name}", f"{port.port_name}"] # 10
|
|
307
383
|
for port, source in node.input_ports.items()]
|
|
308
384
|
|
|
309
385
|
if node.output_ports:
|
|
310
|
-
json_node["output"] = [[f"{port.fmu_name}", f"{port.port_name}", f"{target}"]
|
|
386
|
+
json_node["output"] = [[f"{port.fmu_name}", f"{port.port_name}", f"{target}"] # 11
|
|
311
387
|
for port, target in node.output_ports.items()]
|
|
312
388
|
|
|
313
389
|
if node.links:
|
|
314
|
-
json_node["link"] = [[f"{link.from_port.fmu_name}", f"{link.from_port.port_name}",
|
|
390
|
+
json_node["link"] = [[f"{link.from_port.fmu_name}", f"{link.from_port.port_name}", # 12
|
|
315
391
|
f"{link.to_port.fmu_name}", f"{link.to_port.port_name}"]
|
|
316
392
|
for link in node.links]
|
|
317
393
|
|
|
318
394
|
if node.start_values:
|
|
319
|
-
json_node["start"] = [[f"{port.fmu_name}", f"{port.port_name}", value]
|
|
395
|
+
json_node["start"] = [[f"{port.fmu_name}", f"{port.port_name}", value] # 13
|
|
320
396
|
for port, value in node.start_values.items()]
|
|
321
397
|
|
|
322
398
|
if node.drop_ports:
|
|
323
|
-
json_node["drop"] = [[f"{port.fmu_name}", f"{port.port_name}"] for port in node.drop_ports]
|
|
399
|
+
json_node["drop"] = [[f"{port.fmu_name}", f"{port.port_name}"] for port in node.drop_ports] # 14
|
|
324
400
|
|
|
325
401
|
return json_node
|
|
326
402
|
|
|
327
|
-
def json_decode_node(self, data) -> AssemblyNode:
|
|
328
|
-
name = data.get("name", None)
|
|
329
|
-
step_size = data.get("step_size", self.default_step_size)
|
|
330
|
-
auto_link = data.get("auto_link", self.default_auto_link)
|
|
331
|
-
auto_input = data.get("auto_input", self.default_auto_input)
|
|
332
|
-
auto_output = data.get("auto_output", self.default_auto_output)
|
|
333
|
-
mt = data.get("mt", self.default_mt)
|
|
334
|
-
profiling = data.get("profiling", self.default_profiling)
|
|
335
|
-
|
|
336
|
-
node = AssemblyNode(name, step_size=step_size, auto_link=auto_link, mt=mt, profiling=profiling,
|
|
337
|
-
auto_input=auto_input, auto_output=auto_output)
|
|
338
|
-
|
|
339
|
-
if "container" in data:
|
|
340
|
-
if not isinstance(data["container"], list):
|
|
341
|
-
raise FMUContainerError("JSON: 'container' keyword should define a list.")
|
|
342
|
-
for sub_data in data["container"]:
|
|
343
|
-
node.add_sub_node(self.json_decode_node(sub_data))
|
|
344
|
-
|
|
345
|
-
if "fmu" in data:
|
|
346
|
-
if not isinstance(data["fmu"], list):
|
|
347
|
-
raise FMUContainerError("JSON: 'fmu' keyword should define a list.")
|
|
348
|
-
for fmu in data["fmu"]:
|
|
349
|
-
node.add_fmu(fmu)
|
|
350
|
-
|
|
351
|
-
if "input" in data:
|
|
352
|
-
if not isinstance(data["input"], list):
|
|
353
|
-
raise FMUContainerError("JSON: 'input' keyword should define a list.")
|
|
354
|
-
for line in data["input"]:
|
|
355
|
-
node.add_input(line[1], line[2], line[0])
|
|
356
|
-
|
|
357
|
-
if "output" in data:
|
|
358
|
-
if not isinstance(data["output"], list):
|
|
359
|
-
raise FMUContainerError("JSON: 'output' keyword should define a list.")
|
|
360
|
-
for line in data["output"]:
|
|
361
|
-
node.add_output(line[0], line[1], line[2])
|
|
362
|
-
|
|
363
|
-
if "start" in data:
|
|
364
|
-
if not isinstance(data["start"], list):
|
|
365
|
-
raise FMUContainerError("JSON: 'start' keyword should define a list.")
|
|
366
|
-
for line in data["start"]:
|
|
367
|
-
node.add_start_value(line[0], line[1], line[2])
|
|
368
|
-
|
|
369
|
-
if "drop" in data:
|
|
370
|
-
for line in data["drop"]:
|
|
371
|
-
node.add_drop_port(line[0], line[1])
|
|
372
|
-
|
|
373
|
-
return node
|
|
374
|
-
|
|
375
403
|
def read_ssp(self):
|
|
376
404
|
logger.warning("This feature is ALPHA stage.")
|
|
377
405
|
|
|
@@ -388,11 +416,11 @@ class Assembly:
|
|
|
388
416
|
mt=self.default_mt, profiling=self.default_profiling,
|
|
389
417
|
auto_input=self.default_auto_input, auto_output=self.default_auto_output)
|
|
390
418
|
self.root = sdd.parse(self.description_pathname)
|
|
419
|
+
self.root.name = str(self.filename.with_suffix(".fmu"))
|
|
391
420
|
|
|
392
421
|
def make_fmu(self, dump_json=False):
|
|
393
422
|
if dump_json:
|
|
394
|
-
dump_file = (self.
|
|
395
|
-
.with_suffix(".json"))
|
|
423
|
+
dump_file = Path(self.input_pathname.stem + "-dump").with_suffix(".json")
|
|
396
424
|
logger.info(f"Dump Json '{dump_file}'")
|
|
397
425
|
self.write_json(dump_file)
|
|
398
426
|
self.root.make_fmu(self.fmu_directory, debug=self.debug, description_pathname=self.description_pathname)
|
fmu_manipulation_toolbox/cli.py
CHANGED
|
@@ -5,7 +5,7 @@ from colorama import Fore, Style, init
|
|
|
5
5
|
|
|
6
6
|
from .fmu_operations import *
|
|
7
7
|
from .fmu_container import FMUContainerError
|
|
8
|
-
from .assembly import Assembly
|
|
8
|
+
from .assembly import Assembly, AssemblyError
|
|
9
9
|
from .checker import checker_list
|
|
10
10
|
from .version import __version__ as version
|
|
11
11
|
from .help import Help
|
|
@@ -211,7 +211,7 @@ def fmucontainer():
|
|
|
211
211
|
except FileNotFoundError as e:
|
|
212
212
|
logger.fatal(f"Cannot read file: {e}")
|
|
213
213
|
continue
|
|
214
|
-
except FMUContainerError as e:
|
|
214
|
+
except (FMUContainerError, AssemblyError) as e:
|
|
215
215
|
logger.fatal(f"{filename}: {e}")
|
|
216
216
|
continue
|
|
217
217
|
|
|
@@ -291,7 +291,7 @@ class FMUContainer:
|
|
|
291
291
|
|
|
292
292
|
self.start_values[cport] = value
|
|
293
293
|
|
|
294
|
-
def find_input(self, port_to_connect: FMUPort) ->
|
|
294
|
+
def find_input(self, port_to_connect: FMUPort) -> Optional[ContainerPort]:
|
|
295
295
|
for fmu in self.execution_order:
|
|
296
296
|
for port in fmu.ports.values():
|
|
297
297
|
if (port.causality == 'input' and port.name == port_to_connect.name
|
|
@@ -346,31 +346,26 @@ class FMUContainer:
|
|
|
346
346
|
|
|
347
347
|
return step_size
|
|
348
348
|
|
|
349
|
-
def sanity_check(self, step_size:
|
|
350
|
-
nb_error = 0
|
|
349
|
+
def sanity_check(self, step_size: Optional[float]):
|
|
351
350
|
for fmu in self.execution_order:
|
|
352
351
|
if not fmu.step_size:
|
|
353
352
|
continue
|
|
354
353
|
ts_ratio = step_size / fmu.step_size
|
|
355
354
|
if ts_ratio < 1.0:
|
|
356
|
-
logger.
|
|
357
|
-
|
|
355
|
+
logger.warning(f"Container step_size={step_size}s is lower than FMU '{fmu.name}' "
|
|
356
|
+
f"step_size={fmu.step_size}s.")
|
|
358
357
|
if ts_ratio != int(ts_ratio):
|
|
359
|
-
logger.
|
|
360
|
-
|
|
358
|
+
logger.warning(f"Container step_size={step_size}s should divisible by FMU '{fmu.name}' "
|
|
359
|
+
f"step_size={fmu.step_size}s.")
|
|
361
360
|
for port_name in fmu.ports:
|
|
362
361
|
cport = ContainerPort(fmu, port_name)
|
|
363
362
|
if cport not in self.rules:
|
|
364
363
|
if cport.port.causality == 'input':
|
|
365
364
|
logger.error(f"{cport} is not connected")
|
|
366
|
-
nb_error += 1
|
|
367
365
|
if cport.port.causality == 'output':
|
|
368
366
|
logger.warning(f"{cport} is not connected")
|
|
369
367
|
|
|
370
|
-
|
|
371
|
-
raise FMUContainerError(f"Some ports are not connected.")
|
|
372
|
-
|
|
373
|
-
def make_fmu(self, fmu_filename: Union[str, Path], step_size: Union[float, None] = None, debug=False, mt=False,
|
|
368
|
+
def make_fmu(self, fmu_filename: Union[str, Path], step_size: Optional[float] = None, debug=False, mt=False,
|
|
374
369
|
profiling=False):
|
|
375
370
|
if isinstance(fmu_filename, str):
|
|
376
371
|
fmu_filename = Path(fmu_filename)
|
fmu_manipulation_toolbox/gui.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import os.path
|
|
2
2
|
import sys
|
|
3
3
|
from .version import __version__ as version
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
QTextBrowser, QInputDialog, QMenu
|
|
7
|
-
from
|
|
4
|
+
from PyQt6.QtCore import Qt, QObject, QUrl, pyqtSignal, QDir, QSize, QPoint
|
|
5
|
+
from PyQt6.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
|
|
6
|
+
QTextBrowser, QInputDialog, QMenu)
|
|
7
|
+
from PyQt6.QtGui import (QPixmap, QImage, QFont, QTextCursor, QIcon, QDesktopServices, QAction, QPainter, QColor)
|
|
8
8
|
import textwrap
|
|
9
9
|
from functools import partial
|
|
10
10
|
from typing import Optional
|
|
@@ -42,7 +42,7 @@ class DropZoneWidget(QLabel):
|
|
|
42
42
|
|
|
43
43
|
def dropEvent(self, event):
|
|
44
44
|
if event.mimeData().hasImage:
|
|
45
|
-
event.setDropAction(Qt.CopyAction)
|
|
45
|
+
event.setDropAction(Qt.DropAction.CopyAction)
|
|
46
46
|
try:
|
|
47
47
|
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
48
48
|
except IndexError:
|
|
@@ -59,8 +59,8 @@ class DropZoneWidget(QLabel):
|
|
|
59
59
|
else:
|
|
60
60
|
default_directory = os.path.expanduser('~')
|
|
61
61
|
|
|
62
|
-
fmu_filename, _ = QFileDialog.getOpenFileName(self, 'Select FMU to Manipulate',
|
|
63
|
-
default_directory, "FMU files (*.fmu)")
|
|
62
|
+
fmu_filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
|
|
63
|
+
directory=default_directory, filter="FMU files (*.fmu)")
|
|
64
64
|
if fmu_filename:
|
|
65
65
|
self.set_fmu(fmu_filename)
|
|
66
66
|
|
|
@@ -70,23 +70,23 @@ class DropZoneWidget(QLabel):
|
|
|
70
70
|
elif not os.path.isfile(filename):
|
|
71
71
|
filename = os.path.join(os.path.dirname(__file__), "resources", "fmu.png")
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.
|
|
73
|
+
base_image = QImage(filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
74
|
+
Qt.TransformationMode.SmoothTransformation)
|
|
75
|
+
mask_filename = os.path.join(os.path.dirname(__file__), "resources", "mask.png")
|
|
76
|
+
mask_image = QImage(mask_filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
77
|
+
Qt.TransformationMode.SmoothTransformation)
|
|
78
|
+
rounded_image = QImage(self.WIDTH, self.HEIGHT, QImage.Format.Format_ARGB32)
|
|
79
|
+
rounded_image.fill(QColor(0, 0, 0, 0))
|
|
80
|
+
painter = QPainter()
|
|
81
|
+
painter.begin(rounded_image)
|
|
82
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
83
|
+
painter.drawImage(QPoint(0, 0), base_image)
|
|
84
|
+
painter.drawImage(QPoint(0, 0), mask_image)
|
|
85
|
+
painter.end()
|
|
86
|
+
pixmap = QPixmap.fromImage(rounded_image)
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
rounded = QPixmap(pixmap.size())
|
|
80
|
-
rounded.fill(QColor("transparent"))
|
|
88
|
+
self.setPixmap(pixmap)
|
|
81
89
|
|
|
82
|
-
painter = QPainter(rounded)
|
|
83
|
-
painter.setRenderHint(QPainter.Antialiasing)
|
|
84
|
-
painter.setBrush(QBrush(pixmap))
|
|
85
|
-
painter.setPen(Qt.NoPen)
|
|
86
|
-
painter.drawRoundedRect(pixmap.rect(), 20, 20)
|
|
87
|
-
del painter # Mandatory to avoid a crash
|
|
88
|
-
self.update() # Mandatory to avoid a crash
|
|
89
|
-
return rounded
|
|
90
90
|
|
|
91
91
|
def set_fmu(self, filename):
|
|
92
92
|
try:
|
|
@@ -147,7 +147,7 @@ class LogWidget(QTextBrowser):
|
|
|
147
147
|
LogWidget.XStream.stdout().messageWritten.connect(self.insertPlainText)
|
|
148
148
|
LogWidget.XStream.stderr().messageWritten.connect(self.insertPlainText)
|
|
149
149
|
|
|
150
|
-
def loadResource(self,
|
|
150
|
+
def loadResource(self, _, name):
|
|
151
151
|
image_path = os.path.join(os.path.dirname(__file__), "resources", name.toString())
|
|
152
152
|
return QPixmap(image_path)
|
|
153
153
|
|
|
@@ -162,7 +162,7 @@ class HelpWidget(QLabel):
|
|
|
162
162
|
filename = os.path.join(os.path.dirname(__file__), "resources", "help.png")
|
|
163
163
|
image = QPixmap(filename)
|
|
164
164
|
self.setPixmap(image)
|
|
165
|
-
self.setAlignment(Qt.AlignRight)
|
|
165
|
+
self.setAlignment(Qt.AlignmentFlag.AlignRight)
|
|
166
166
|
|
|
167
167
|
def mousePressEvent(self, event):
|
|
168
168
|
QDesktopServices.openUrl(QUrl(self.HELP_URL))
|
|
@@ -216,6 +216,9 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
216
216
|
|
|
217
217
|
# set the grid layout
|
|
218
218
|
self.layout = QGridLayout()
|
|
219
|
+
self.layout.setVerticalSpacing(4)
|
|
220
|
+
self.layout.setHorizontalSpacing(4)
|
|
221
|
+
self.layout.setContentsMargins(10, 10, 10, 10)
|
|
219
222
|
self.setLayout(self.layout)
|
|
220
223
|
|
|
221
224
|
self.dropped_fmu = DropZoneWidget()
|
|
@@ -265,7 +268,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
265
268
|
|
|
266
269
|
line += 1
|
|
267
270
|
self.apply_filter_label = QLabel("Apply modification only on: ")
|
|
268
|
-
self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignRight)
|
|
271
|
+
self.layout.addWidget(self.apply_filter_label, line, 1, 1, 2, alignment=Qt.AlignmentFlag.AlignRight)
|
|
269
272
|
self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
|
|
270
273
|
|
|
271
274
|
causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
|
|
@@ -414,7 +417,7 @@ class FMUManipulationToolboxlMainWindow(QWidget):
|
|
|
414
417
|
|
|
415
418
|
def apply_operation(self, operation):
|
|
416
419
|
if self.dropped_fmu.fmu:
|
|
417
|
-
self.log_widget.moveCursor(QTextCursor.End)
|
|
420
|
+
self.log_widget.moveCursor(QTextCursor.MoveOperation.End)
|
|
418
421
|
fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
|
|
419
422
|
print('-' * 100)
|
|
420
423
|
self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
|
|
@@ -437,8 +440,8 @@ class Application(QApplication):
|
|
|
437
440
|
"""
|
|
438
441
|
Analyse and modify your FMUs.
|
|
439
442
|
|
|
440
|
-
Note: modifying the modelDescription.xml can damage your FMU !
|
|
441
|
-
way the FMU is generated, is preferable when possible.
|
|
443
|
+
Note: modifying the modelDescription.xml can damage your FMU !
|
|
444
|
+
Communicating with the FMU-developer and adapting the way the FMU is generated, is preferable when possible.
|
|
442
445
|
|
|
443
446
|
"""
|
|
444
447
|
def __init__(self, *args, **kwargs):
|
|
@@ -465,8 +468,8 @@ QPushButton.save:hover {background-color: #675a78; color: #dddddd;}
|
|
|
465
468
|
QPushButton.quit {background-color: #4571a4; color: #dddddd;}
|
|
466
469
|
QPushButton.quit:hover {background-color: #5682b5; color: #dddddd;}
|
|
467
470
|
QToolTip {color: black}
|
|
468
|
-
QLabel.dropped_fmu {background-color: #b5bab9
|
|
469
|
-
QLabel.dropped_fmu:hover {background-color: #c6cbca
|
|
471
|
+
QLabel.dropped_fmu {background-color: #b5bab9}
|
|
472
|
+
QLabel.dropped_fmu:hover {background-color: #c6cbca}
|
|
470
473
|
QTextBrowser {background-color: #282830; color: #b5bab9;}
|
|
471
474
|
QMenu {font-size: 18px;}
|
|
472
475
|
QMenu::item {padding: 2px 250px 2px 20px; border: 1px solid transparent;}
|
|
@@ -481,7 +484,6 @@ QMenu::indicator:unchecked:disabled {width: 35px; image: url(images:checkbox-unc
|
|
|
481
484
|
|
|
482
485
|
self.setStyleSheet(css_dark)
|
|
483
486
|
|
|
484
|
-
|
|
485
487
|
if os.name == 'nt':
|
|
486
488
|
self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'resources', 'icon-round.png')))
|
|
487
489
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
{fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fmu_manipulation_toolbox
|
|
3
|
-
Version: 1.8
|
|
3
|
+
Version: 1.8.1
|
|
4
4
|
Summary: FMU Manipulation Toobox is a python application which help to modify a Functional Mock-up Units (FMUs) without recompilation or to group them into FMU Containers
|
|
5
5
|
Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
|
|
6
6
|
Author: Nicolas.LAURENT@Renault.com
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
fmu_manipulation_toolbox/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
2
|
fmu_manipulation_toolbox/__main__.py,sha256=mAzrtkil506DS0F3g3CEbHdtZsZotzntcNhIn_lNJkw,344
|
|
3
|
-
fmu_manipulation_toolbox/__version__.py,sha256=
|
|
4
|
-
fmu_manipulation_toolbox/assembly.py,sha256=
|
|
3
|
+
fmu_manipulation_toolbox/__version__.py,sha256=UHHZBWWhJrlslwhXubEi_Un0xr55BiMWPTaN6R2jGLM,9
|
|
4
|
+
fmu_manipulation_toolbox/assembly.py,sha256=i0BDfgAca5WgrIYDv1z1mMxaKTiP-tBlPtZJJqMxHgI,20776
|
|
5
5
|
fmu_manipulation_toolbox/checker.py,sha256=lE2MpH4BAKCDjUvbr06N56u7ao8hWXaJgMKaLvmhFTQ,2272
|
|
6
|
-
fmu_manipulation_toolbox/cli.py,sha256=
|
|
7
|
-
fmu_manipulation_toolbox/fmu_container.py,sha256=
|
|
6
|
+
fmu_manipulation_toolbox/cli.py,sha256=yAyfpogj_NgGt0mQRFA9dBy0NvN8vFomKUUrAtOIA5c,10102
|
|
7
|
+
fmu_manipulation_toolbox/fmu_container.py,sha256=rbe71nS_kv2QyhcsaW70YlxR3iA_YO2_VX2ihRVxFQs,26338
|
|
8
8
|
fmu_manipulation_toolbox/fmu_operations.py,sha256=Z3LVOnDvwzoBrqfibZPAn_Osw6MIuGrXtaboGFqp0DA,15836
|
|
9
|
-
fmu_manipulation_toolbox/gui.py,sha256=
|
|
9
|
+
fmu_manipulation_toolbox/gui.py,sha256=VKhQpIEEilScBSwTSJkDUwSbtsmB_VMuX0QwhZczTq4,20628
|
|
10
10
|
fmu_manipulation_toolbox/help.py,sha256=aklKiLrsE0adSzQ5uoEB1sBDmI6s4l231gavu4XxxzA,5856
|
|
11
11
|
fmu_manipulation_toolbox/version.py,sha256=OhBLkZ1-nhC77kyvffPNAf6m8OZe1bYTnNf_PWs1NvM,392
|
|
12
12
|
fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png,sha256=FWIuyrXlaNLLePHfXj7j9ca5rT8Hgr14KCe1EqTCZyk,2288
|
|
@@ -15,13 +15,14 @@ fmu_manipulation_toolbox/resources/checkbox-checked.png,sha256=gzyFqvRFsZixVh6Zl
|
|
|
15
15
|
fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png,sha256=KNdiE8zJ8H-mH81spHL8Ck-V87dj-fPPPrPNZFRv3wA,1783
|
|
16
16
|
fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png,sha256=7XT54vwzDfSK-i6oJ5BBKGXKz8duRRVtoUzaOlWX0WE,1797
|
|
17
17
|
fmu_manipulation_toolbox/resources/checkbox-unchecked.png,sha256=w3MG3RwFeTwkVOAFi8ZBs6yNlmtVnXxXY5atNyvLw54,1793
|
|
18
|
-
fmu_manipulation_toolbox/resources/drop_fmu.png,sha256=
|
|
18
|
+
fmu_manipulation_toolbox/resources/drop_fmu.png,sha256=xt2YzoQVcvr95Ix-4kw6wT04yf660sUHGHkskHRE_l0,10365
|
|
19
19
|
fmu_manipulation_toolbox/resources/fmu.png,sha256=7bI_cb3pcqEnwBCCL30v3MXPwOK8OtbhFFouRq9lTj8,12048
|
|
20
20
|
fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png,sha256=hUhuGg88BU5j7KkqhnU981wSpzn9ftQuOSu8pMYuP-Y,37997
|
|
21
21
|
fmu_manipulation_toolbox/resources/help.png,sha256=WrIbjmlgIqdo6UWYj6L6gG-BCGWlu4qma8HRgRk8k7o,1822
|
|
22
22
|
fmu_manipulation_toolbox/resources/icon-round.png,sha256=j7jk-9NVQQZJMNazjpj8WeC1q6ptXwTaiO38kPoDEnE,83754
|
|
23
23
|
fmu_manipulation_toolbox/resources/icon.png,sha256=Ovui-UDBARqdTlVQwTn5Ud8seSsVh9pdElwLq5s6xKg,69976
|
|
24
24
|
fmu_manipulation_toolbox/resources/license.txt,sha256=5ODuU8g8pIkK-NMWXu_rjZ6k7gM7b-N2rmg87-2Kmqw,1583
|
|
25
|
+
fmu_manipulation_toolbox/resources/mask.png,sha256=px1U4hQGL0AmZ4BQPknOVREpMpTSejbah3ntkpqAzFA,3008
|
|
25
26
|
fmu_manipulation_toolbox/resources/model.png,sha256=EAf_HnZJe8zYGZygerG1MMt2U-tMMZlifzXPj4_iORA,208788
|
|
26
27
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd,sha256=OGfyJtaJntKypX5KDpuZ-nV1oYLZ6HV16pkpKOmYox4,2731
|
|
27
28
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd,sha256=HwyV7LBse-PQSv4z1xjmtzPU3Hjnv4mluq9YdSBNHMQ,3704
|
|
@@ -35,14 +36,14 @@ fmu_manipulation_toolbox/resources/linux32/server_sm,sha256=1TLGqNPyM5UVOrCfzNqW
|
|
|
35
36
|
fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=EhY1XHo1YcQn6yqZ7wk5okqtZyp0MrcCsGcudqE-aIM,37000
|
|
36
37
|
fmu_manipulation_toolbox/resources/linux64/container.so,sha256=_uhkJYZa_z1tV0NBATWC8iAGt7lPY11_VA29a_5hXsM,45384
|
|
37
38
|
fmu_manipulation_toolbox/resources/linux64/server_sm,sha256=ulfoPvmaYe9nInYcVEyj7mD9zDzGk56OUoWx1mPKLiE,22768
|
|
38
|
-
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=
|
|
39
|
-
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=
|
|
40
|
-
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=
|
|
41
|
-
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=
|
|
42
|
-
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=
|
|
43
|
-
fmu_manipulation_toolbox-1.8.dist-info/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
|
|
44
|
-
fmu_manipulation_toolbox-1.8.dist-info/METADATA,sha256=
|
|
45
|
-
fmu_manipulation_toolbox-1.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
46
|
-
fmu_manipulation_toolbox-1.8.dist-info/entry_points.txt,sha256=jCPLMBdS-eOvmRfDv7n0wHZSbJHccHviW03mz5vwO-Q,124
|
|
47
|
-
fmu_manipulation_toolbox-1.8.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
|
|
48
|
-
fmu_manipulation_toolbox-1.8.dist-info/RECORD,,
|
|
39
|
+
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=usY6k86X-Y0De5-SxCvgJ3yRzPpR4KCNTShYzDeec80,17920
|
|
40
|
+
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=3570x6uDaKUIYBy9uOmID66ffvy-bxVa8Q2il4m5KZc,15872
|
|
41
|
+
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=G6bNin2QuwbTiToJNSsSM1Dil899MHnZ5FNm-4ExH5I,22016
|
|
42
|
+
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=RBBo9kBBYhYe_KLPl5MtJJCVuL1Zf1LNT87mT88JCPg,32768
|
|
43
|
+
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=Pcx1z-2E7_iHmAzUDEFNTBCf02gfzg5pdYdso5GbP5A,19456
|
|
44
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/LICENSE.txt,sha256=c_862mzyk6ownO3Gt6cVs0-53IXLi_-ZEQFNDVabESw,1285
|
|
45
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/METADATA,sha256=24_m-PSpY9JZqU4LxMHDN3G1IdGydt_8aSXvDU7vm-8,967
|
|
46
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
47
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/entry_points.txt,sha256=jCPLMBdS-eOvmRfDv7n0wHZSbJHccHviW03mz5vwO-Q,124
|
|
48
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
|
|
49
|
+
fmu_manipulation_toolbox-1.8.1.dist-info/RECORD,,
|
{fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|
{fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{fmu_manipulation_toolbox-1.8.dist-info → fmu_manipulation_toolbox-1.8.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|