fmu-manipulation-toolbox 1.9.2b2__tar.gz → 1.9.2b4__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 (86) hide show
  1. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/PKG-INFO +2 -2
  2. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/__version__.py +1 -0
  3. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/cli/datalog2pcap.py +143 -0
  4. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/container.py +9 -4
  5. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/operations.py +2 -2
  6. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  7. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  8. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  9. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  10. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  11. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/win32/server_sm.exe +0 -0
  12. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  13. fmu_manipulation_toolbox-1.9.2b4/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  14. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/win64/server_sm.exe +0 -0
  15. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/PKG-INFO +2 -2
  16. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/SOURCES.txt +1 -0
  17. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/entry_points.txt +1 -0
  18. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/setup.py +3 -2
  19. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/tests/test_suite.py +44 -15
  20. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/__version__.py +0 -1
  21. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
  22. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
  23. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
  24. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/linux64/container.so +0 -0
  25. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/win32/client_sm.dll +0 -0
  26. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/win64/client_sm.dll +0 -0
  27. fmu_manipulation_toolbox-1.9.2b2/fmu_manipulation_toolbox/resources/win64/container.dll +0 -0
  28. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/LICENSE.txt +0 -0
  29. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/README.md +0 -0
  30. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/__init__.py +0 -0
  31. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/__main__.py +0 -0
  32. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/assembly.py +0 -0
  33. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/checker.py +0 -0
  34. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/cli/__init__.py +0 -0
  35. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/cli/fmucontainer.py +0 -0
  36. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/cli/fmusplit.py +0 -0
  37. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/cli/fmutool.py +0 -0
  38. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/cli/utils.py +0 -0
  39. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/help.py +0 -0
  40. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/ls.py +0 -0
  41. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/remoting.py +0 -0
  42. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png +0 -0
  43. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png +0 -0
  44. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-checked.png +0 -0
  45. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png +0 -0
  46. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png +0 -0
  47. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/checkbox-unchecked.png +0 -0
  48. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/container.png +0 -0
  49. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/drop_fmu.png +0 -0
  50. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd +0 -0
  51. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd +0 -0
  52. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd +0 -0
  53. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd +0 -0
  54. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd +0 -0
  55. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd +0 -0
  56. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd +0 -0
  57. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Annotation.xsd +0 -0
  58. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3AttributeGroups.xsd +0 -0
  59. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3BuildDescription.xsd +0 -0
  60. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3InterfaceType.xsd +0 -0
  61. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3LayeredStandardManifest.xsd +0 -0
  62. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3ModelDescription.xsd +0 -0
  63. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Terminal.xsd +0 -0
  64. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3TerminalsAndIcons.xsd +0 -0
  65. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Type.xsd +0 -0
  66. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Unit.xsd +0 -0
  67. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Variable.xsd +0 -0
  68. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd +0 -0
  69. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmu.png +0 -0
  70. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png +0 -0
  71. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/help.png +0 -0
  72. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/icon-round.png +0 -0
  73. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/icon.png +0 -0
  74. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/icon_fmu.png +0 -0
  75. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/license.txt +0 -0
  76. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/linux32/server_sm +0 -0
  77. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/linux64/server_sm +0 -0
  78. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/mask.png +0 -0
  79. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/resources/model.png +0 -0
  80. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/split.py +0 -0
  81. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/terminals.py +0 -0
  82. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox/version.py +0 -0
  83. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/dependency_links.txt +0 -0
  84. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/requires.txt +0 -0
  85. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/fmu_manipulation_toolbox.egg-info/top_level.txt +0 -0
  86. {fmu_manipulation_toolbox-1.9.2b2 → fmu_manipulation_toolbox-1.9.2b4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9.2b2
3
+ Version: 1.9.2b4
4
4
  Summary: FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine Functional Mock-up Units (FMUs) without recompilation.
5
5
  Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
6
6
  Author: Nicolas.LAURENT@Renault.com
@@ -23,7 +23,7 @@ Dynamic: requires-python
23
23
  Dynamic: summary
24
24
 
25
25
  FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine
26
- [Functional Mock-up Units (FMUs)](http://fmi-standard.org/) without recompilation. It is highly customizable and comes with
26
+ [Functional Mock-up Units (FMUs)](https://fmi-standard.org/) without recompilation. It is highly customizable and comes with
27
27
  a Python API.
28
28
 
29
29
  FMU Manipulation Toolbox can be used in different ways:
@@ -0,0 +1,143 @@
1
+ import argparse
2
+ import csv
3
+ import logging
4
+ import sys
5
+
6
+ from pathlib import Path
7
+ from typing import *
8
+
9
+ from .utils import setup_logger, close_logger, make_wide
10
+ from ..version import __version__ as version
11
+
12
+ logger = setup_logger()
13
+
14
+ class DatalogConverter:
15
+ def __init__(self, cvs_filename: Union[Path, str]):
16
+ self.csv_filename = Path(cvs_filename)
17
+ self.pcap_filename = self.csv_filename.with_suffix(".pcap")
18
+
19
+ def open_pcap(self):
20
+ logger.info(f"Creating PCAP file '{self.pcap_filename}'...")
21
+ file = open(self.pcap_filename, "wb")
22
+ file.write(int(0xA1B2C3D4).to_bytes(4, byteorder="big")) # Magic number
23
+ # meaning the timestamp are in min seconds and microseconds
24
+
25
+ file.write(int(2).to_bytes(2, byteorder="big")) # Major Version of fileformat
26
+ file.write(int(4).to_bytes(2, byteorder="big")) # Minor Version
27
+ file.write(int(0).to_bytes(4, byteorder="big")) # Reserved. SHOULD BE 0.
28
+ file.write(int(0).to_bytes(4, byteorder="big")) # Reserved. SHOULD BE 0.
29
+
30
+ file.write(int(0xFFFF).to_bytes(4, byteorder="big")) # snaplen indicating the maximum number of octets
31
+ # captured from each packet.
32
+
33
+ file.write(int(227).to_bytes(4, byteorder="big")) # link type. his field is defined in the Section
34
+ # # 8.1 IANA registry.
35
+ return file
36
+
37
+ def open_csv(self):
38
+ logger.debug(f"Loading '{self.csv_filename}'")
39
+ file = open(self.csv_filename, "rt")
40
+ return file
41
+
42
+ def decode_hexstring(self, hex_string: bytes, time_s, time_us):
43
+ opcode = int.from_bytes(hex_string[0:4], byteorder="little")
44
+ length = int.from_bytes(hex_string[4:8], byteorder="little")
45
+ can_id = int.from_bytes(hex_string[8:12], byteorder="little")
46
+ if opcode == 0x10: # TRANSMIT
47
+ rtr = int.from_bytes(hex_string[13:14], byteorder="little")
48
+ ide = int.from_bytes(hex_string[12:13], byteorder="little")
49
+ data_length = int.from_bytes(hex_string[14:16], byteorder="little")
50
+ raw_data = hex_string[16:]
51
+
52
+ logger.debug(f"time={time_s}.{time_us:06d} OP=0x{opcode:04X} len={length} {data_length} id={can_id}"
53
+ f" ide={ide} rtr={rtr} len={data_length} {raw_data}")
54
+
55
+ # TimeStamp
56
+ self.pcapfile.write(time_s.to_bytes(4, byteorder="big"))
57
+ self.pcapfile.write(time_us.to_bytes(4, byteorder="big"))
58
+
59
+ # Packet length
60
+ packet_length = data_length + 8
61
+ self.pcapfile.write(packet_length.to_bytes(4, byteorder="big"))
62
+ self.pcapfile.write(packet_length.to_bytes(4, byteorder="big"))
63
+
64
+ # Control and flags
65
+ control = (can_id & 0x1FFFFFFF) | ((rtr & 0b1) << 30) | ((ide & 0b1) << 31)
66
+ self.pcapfile.write(control.to_bytes(4, byteorder="big"))
67
+
68
+ # Frame Length
69
+ self.pcapfile.write(data_length.to_bytes(1, byteorder="big"))
70
+
71
+ # Reserved
72
+ self.pcapfile.write(int(0).to_bytes(2, byteorder="big"))
73
+
74
+ # DLC
75
+ dlc = int(data_length) # Classic CAN
76
+ self.pcapfile.write(dlc.to_bytes(1, byteorder="big"))
77
+
78
+ # PAYLOAD
79
+ self.pcapfile.write(raw_data)
80
+
81
+ def convert(self):
82
+ with self.open_csv() as self.csvfile, self.open_pcap() as self.pcapfile:
83
+ csv_reader = csv.DictReader(self.csvfile)
84
+
85
+ data_column_names = [ name for name in csv_reader.fieldnames if "_Data" in name ]
86
+ clock_column_names = {}
87
+
88
+ for column_name in data_column_names:
89
+ splitted_name = column_name.split(".")
90
+ splitted_name[-1] = splitted_name[-1].replace("_Data", "_Clock")
91
+ clock_column_name = ".".join(splitted_name)
92
+
93
+ if clock_column_name in csv_reader.fieldnames:
94
+ logger.debug(f"{column_name} is clocked by {clock_column_name}")
95
+ clock_column_names[column_name] = clock_column_name
96
+
97
+ for row in csv_reader:
98
+ time_s, time_us = divmod(float(row["time"]), 1)
99
+ time_s = int(time_s)
100
+ time_us = int(time_us * 1000000)
101
+
102
+ for names in data_column_names:
103
+ hex_data = row[names]
104
+ if hex_data:
105
+ try:
106
+ clock = row[clock_column_names[names]] == "1"
107
+ except KeyError:
108
+ clock = True
109
+ if clock:
110
+ self.decode_hexstring(bytes.fromhex(hex_data), time_s, time_us)
111
+
112
+
113
+ def datalog2pcap():
114
+ logger.info(f"FMUContainer version {version}")
115
+ logger.warning(f"Datalog2PCAP is still experimental.")
116
+
117
+ parser = argparse.ArgumentParser(prog="datalog2pcap", description="Convert datalog from container to PCAP file.",
118
+ formatter_class=make_wide(argparse.ArgumentDefaultsHelpFormatter),
119
+ add_help=False,
120
+ epilog="see: https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/"
121
+ "doc/datalog.md")
122
+
123
+ parser.add_argument('-h', '-help', action="help")
124
+
125
+ parser.add_argument("-can", action="store", dest="can_filename", default=None,
126
+ metavar="can-datalog.csv", required=True,
127
+ help="Datalog with CAN data and clocks.")
128
+
129
+ parser.add_argument("-debug", action="store_true", dest="debug",
130
+ help="Add lot of useful log during the process.")
131
+
132
+ config = parser.parse_args(sys.argv[1:])
133
+
134
+ if config.debug:
135
+ logger.setLevel(logging.DEBUG)
136
+
137
+ DatalogConverter(config.can_filename).convert()
138
+
139
+ close_logger(logger)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ datalog2pcap()
@@ -95,6 +95,7 @@ class EmbeddedFMUPort:
95
95
  self.start_value = attrs.get("start", None)
96
96
  self.initial = attrs.get("initial", None)
97
97
  self.clock = attrs.get("clocks", None)
98
+ self.interval_variability = attrs.get("intervalVariability", None)
98
99
 
99
100
 
100
101
  def xml(self, vr: int, name=None, causality=None, start=None, fmi_version=2) -> str:
@@ -1106,8 +1107,8 @@ class FMUContainer:
1106
1107
  else:
1107
1108
  local_per_type["clock"].append(link.vr)
1108
1109
  for cport_to in link.cport_to_list:
1109
- if cport_to.fmu.ls.is_bus:
1110
- logger.info(f"LS-BUS: importer scheduling for '{cport_to.fmu.name}' '{cport_to.port.name}' (clock={cport_to.port.vr}, {link.vr})")
1110
+ if cport_to.port.interval_variability == "countdown":
1111
+ logger.info(f"LS-BUS: importer scheduling for '{cport_to.fmu.name}' '{cport_to.port.name}' (clock={cport_to.port.vr}, vr={link.vr})")
1111
1112
  clock_list.append(cport_to, link.vr)
1112
1113
  break
1113
1114
 
@@ -1179,7 +1180,7 @@ class FMUContainer:
1179
1180
  clock_list.write_txt(txt_file)
1180
1181
 
1181
1182
  def make_datalog(self, datalog_file):
1182
- print(f"# Datalog filename")
1183
+ print(f"# Datalog filename", file=datalog_file)
1183
1184
  print(f"{self.identifier}-datalog.csv", file=datalog_file)
1184
1185
 
1185
1186
  ports = defaultdict(list)
@@ -1188,7 +1189,11 @@ class FMUContainer:
1188
1189
  for output_port_name, output_port in self.outputs.items():
1189
1190
  ports[output_port.port.type_name].append((output_port.vr, output_port_name))
1190
1191
  for link in self.links.values():
1191
- ports[link.cport_from.port.type_name].append((link.vr, link.name))
1192
+ if link.cport_from is None:
1193
+ # LS-BUS allows to connected to input clocks.
1194
+ ports[link.cport_to_list[0].port.type_name].append((link.vr, link.name))
1195
+ else:
1196
+ ports[link.cport_from.port.type_name].append((link.vr, link.name))
1192
1197
 
1193
1198
  for type_name in EmbeddedFMUPort.ALL_TYPES:
1194
1199
  print(f"# {type_name}: <VR> <NAME>" , file=datalog_file)
@@ -61,7 +61,7 @@ class FMUPort:
61
61
  self.dimension = None
62
62
 
63
63
  def dict_level(self, nb):
64
- return " ".join([f'{key}="{value}"' for key, value in self.attrs_list[nb].items()])
64
+ return " ".join([f'{key}="{Manipulation.escape(value)}"' for key, value in self.attrs_list[nb].items()])
65
65
 
66
66
  def write_xml(self, fmi_version: int, file):
67
67
  if fmi_version == 2:
@@ -177,7 +177,7 @@ class Manipulation:
177
177
  self.current_port = FMUPort()
178
178
  self.current_port.push_attrs(attrs)
179
179
  elif self.fmu.fmi_version == 2 and name in self.fmu.FMI2_TYPES:
180
- if self.current_port: # <Enumeration> can be found before port defition. Ignored.
180
+ if self.current_port: # <Enumeration> can be found before port definition. Ignored.
181
181
  self.current_port.fmi_type = name
182
182
  self.current_port.push_attrs(attrs)
183
183
  elif self.fmu.fmi_version == 3 and name in self.fmu.FMI3_TYPES:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmu_manipulation_toolbox
3
- Version: 1.9.2b2
3
+ Version: 1.9.2b4
4
4
  Summary: FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine Functional Mock-up Units (FMUs) without recompilation.
5
5
  Home-page: https://github.com/grouperenault/fmu_manipulation_toolbox/
6
6
  Author: Nicolas.LAURENT@Renault.com
@@ -23,7 +23,7 @@ Dynamic: requires-python
23
23
  Dynamic: summary
24
24
 
25
25
  FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine
26
- [Functional Mock-up Units (FMUs)](http://fmi-standard.org/) without recompilation. It is highly customizable and comes with
26
+ [Functional Mock-up Units (FMUs)](https://fmi-standard.org/) without recompilation. It is highly customizable and comes with
27
27
  a Python API.
28
28
 
29
29
  FMU Manipulation Toolbox can be used in different ways:
@@ -22,6 +22,7 @@ fmu_manipulation_toolbox.egg-info/entry_points.txt
22
22
  fmu_manipulation_toolbox.egg-info/requires.txt
23
23
  fmu_manipulation_toolbox.egg-info/top_level.txt
24
24
  fmu_manipulation_toolbox/cli/__init__.py
25
+ fmu_manipulation_toolbox/cli/datalog2pcap.py
25
26
  fmu_manipulation_toolbox/cli/fmucontainer.py
26
27
  fmu_manipulation_toolbox/cli/fmusplit.py
27
28
  fmu_manipulation_toolbox/cli/fmutool.py
@@ -1,4 +1,5 @@
1
1
  [console_scripts]
2
+ datalog2pcap = fmu_manipulation_toolbox.cli.datalog2pcap:datalog2pcap
2
3
  fmucontainer = fmu_manipulation_toolbox.cli.fmucontainer:fmucontainer
3
4
  fmusplit = fmu_manipulation_toolbox.cli.fmusplit:fmusplit
4
5
  fmutool = fmu_manipulation_toolbox.cli.fmutool:fmutool
@@ -44,7 +44,8 @@ setup(
44
44
  ]},
45
45
  entry_points={"console_scripts": ["fmutool = fmu_manipulation_toolbox.cli.fmutool:fmutool",
46
46
  "fmucontainer = fmu_manipulation_toolbox.cli.fmucontainer:fmucontainer",
47
- "fmusplit = fmu_manipulation_toolbox.cli.fmusplit:fmusplit"
47
+ "fmusplit = fmu_manipulation_toolbox.cli.fmusplit:fmusplit",
48
+ "datalog2pcap = fmu_manipulation_toolbox.cli.datalog2pcap:datalog2pcap",
48
49
  ],
49
50
  "gui_scripts": ["fmutool-gui = fmu_manipulation_toolbox.gui.gui:main"]
50
51
  },
@@ -53,7 +54,7 @@ setup(
53
54
  description="FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine "
54
55
  "Functional Mock-up Units (FMUs) without recompilation.",
55
56
  long_description="""FMU Manipulation Toolbox is a python package which helps to analyze, modify or combine
56
- [Functional Mock-up Units (FMUs)](http://fmi-standard.org/) without recompilation. It is highly customizable and comes with
57
+ [Functional Mock-up Units (FMUs)](https://fmi-standard.org/) without recompilation. It is highly customizable and comes with
57
58
  a Python API.
58
59
 
59
60
  FMU Manipulation Toolbox can be used in different ways:
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  import numpy as np
2
3
  import pytest
3
4
  import sys
@@ -14,6 +15,7 @@ from fmu_manipulation_toolbox.assembly import *
14
15
  from fmu_manipulation_toolbox.cli.fmusplit import fmusplit
15
16
  from fmu_manipulation_toolbox.cli.fmucontainer import fmucontainer
16
17
  from fmu_manipulation_toolbox.cli.fmutool import fmutool
18
+ from fmu_manipulation_toolbox.cli.datalog2pcap import datalog2pcap
17
19
 
18
20
 
19
21
  class TestSuite:
@@ -49,17 +51,23 @@ class TestSuite:
49
51
  with open(log_filename, mode="rt", newline=None) as a, open(ref_filename, mode="rt", newline=None) as b:
50
52
  for i, (lineA, lineB) in enumerate(zip(a, b)):
51
53
  if i > 10:
52
- assert(lineA == lineB)
54
+ assert lineA == lineB, \
55
+ f"files {log_filename} and {ref_filename} missmatch (excl. GUID):\n" \
56
+ f"{lineA}\n" \
57
+ f"vs.\n\n" \
58
+ f"{lineB}"
53
59
 
54
60
  @staticmethod
55
61
  def assert_identical_files(filename1, filename2):
62
+ assert Path(filename1).exists(), f"{filename1} does not exist"
63
+ assert Path(filename2).exists(), f"{filename2} does not exist"
56
64
  with open(filename1, mode="rt", newline=None) as a, open(filename2, mode="rt", newline=None) as b:
57
65
  for lineA, lineB in zip(a, b):
58
- assert(lineA == lineB,
59
- f"file {filename1} and {filename2} missmatch (excl. GUID):\n"
60
- f"{lineA}\n"
61
- f"vs.\n\n"
62
- f"{lineB}")
66
+ assert lineA == lineB, \
67
+ f"file {filename1} and {filename2} missmatch (excl. GUID):\n" \
68
+ f"{lineA}\n" \
69
+ f"vs.\n\n" \
70
+ f"{lineB}"
63
71
 
64
72
  @staticmethod
65
73
  def assert_identical_files_but_guid(filename1, filename2):
@@ -71,11 +79,11 @@ class TestSuite:
71
79
  if keyword in lineA:
72
80
  skip = True
73
81
  break
74
- assert(skip or lineA == lineB,
75
- f"file {filename1} and {filename2} missmatch:\n"
76
- f"{lineA}\n"
77
- f"vs.\n\n"
78
- f"{lineB}")
82
+ assert skip or lineA == lineB, \
83
+ f"file {filename1} and {filename2} missmatch:\n" \
84
+ f"{lineA}\n" \
85
+ f"vs.\n\n" \
86
+ f"{lineB}"
79
87
 
80
88
  def assert_names_match_ref(self, fmu_filename):
81
89
  fmu = FMU(fmu_filename)
@@ -91,9 +99,19 @@ class TestSuite:
91
99
  fmu.repack(fmu_filename)
92
100
  self.assert_names_match_ref(fmu_filename)
93
101
 
102
+ @staticmethod
103
+ def assert_md5(filename, expected_md5):
104
+ hash_md5 = hashlib.md5()
105
+ with open(filename, "rb") as f:
106
+ for chunk in iter(lambda: f.read(4096), b""):
107
+ hash_md5.update(chunk)
108
+ print(f"{filename}: {expected_md5} {hash_md5.hexdigest()}")
109
+ assert hash_md5.hexdigest() == expected_md5 ,\
110
+ f"Wrong md5 hash for {filename}. Expected {expected_md5} but got {hash_md5.hexdigest()}"
111
+
94
112
  @staticmethod
95
113
  def assert_file_exist(path):
96
- assert(Path(path).exists())
114
+ assert Path(path).exists()
97
115
 
98
116
  def test_strip_top_level(self):
99
117
  self.assert_operation_match_ref("operations/bouncing_ball-no-tl.fmu", OperationStripTopLevel())
@@ -295,16 +313,21 @@ class TestSuite:
295
313
  sys.argv = ['fmusplit',
296
314
  "-fmu", "containers/ssp/bouncing.fmu"]
297
315
  fmusplit()
298
- assert(Path("containers/ssp/bouncing.dir/bb_position.fmu").exists())
299
- assert(Path("containers/ssp/bouncing.dir/bb_velocity.fmu").exists())
316
+ assert Path("containers/ssp/bouncing.dir/bb_position.fmu").exists()
317
+ assert Path("containers/ssp/bouncing.dir/bb_velocity.fmu").exists()
300
318
  self.assert_identical_files("containers/ssp/REF-split-bouncing.json",
301
319
  "containers/ssp/bouncing.dir/bouncing.json")
302
320
 
303
- def test_ls_bus(self):
321
+ def test_ls_bus_nodes_and_bus(self):
304
322
  assembly = Assembly("bus+nodes.json", fmu_directory=Path("ls-bus"))
305
323
  assembly.make_fmu(fmi_version=3)
306
324
  self.assert_simulation_log("ls-bus/bus+nodes.fmu", 0.1)
307
325
 
326
+ def test_ls_bus_nodes_only(self):
327
+ assembly = Assembly("nodes-only.json", fmu_directory=Path("ls-bus"))
328
+ assembly.make_fmu(fmi_version=3, datalog=True)
329
+ self.assert_simulation_log("ls-bus/nodes-only.fmu", 0.1)
330
+
308
331
  def test_datalog(self):
309
332
  assembly = Assembly("bouncing.csv", fmu_directory=Path("containers/bouncing_ball"), mt=True, debug=True)
310
333
  assembly.make_fmu(filename="bouncing-datalog.fmu", datalog=True)
@@ -322,3 +345,9 @@ class TestSuite:
322
345
  self.assert_simulation("containers/VanDerPol/VanDerPol-datalog.fmu", 0.1)
323
346
  self.assert_file_exist("VanDerPol-Container-datalog.csv")
324
347
 
348
+ def test_datalog_pcap(self):
349
+ sys.argv = ['datalog2pcap',
350
+ '-can', 'ls-bus/REF-nodes-only-datalog.csv']
351
+ datalog2pcap()
352
+
353
+ self.assert_md5("ls-bus/REF-nodes-only-datalog.pcap", "ceab6b0161dbc93458bd47c057e80375")