fmu-manipulation-toolbox 1.9.2b4__py3-none-any.whl → 1.9.2rc2__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/cli/datalog2pcap.py +34 -4
- fmu_manipulation_toolbox/container.py +56 -17
- fmu_manipulation_toolbox/gui/__init__.py +0 -0
- fmu_manipulation_toolbox/gui/gui.py +749 -0
- fmu_manipulation_toolbox/gui/gui_style.py +252 -0
- fmu_manipulation_toolbox/resources/darwin64/container.dylib +0 -0
- fmu_manipulation_toolbox/resources/linux32/client_sm.so +0 -0
- fmu_manipulation_toolbox/resources/linux64/client_sm.so +0 -0
- fmu_manipulation_toolbox/resources/linux64/container.so +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.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/METADATA +1 -1
- {fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/RECORD +21 -18
- {fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/WHEEL +1 -1
- {fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/entry_points.txt +0 -0
- {fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/licenses/LICENSE.txt +0 -0
- {fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/top_level.txt +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
'V1.9.
|
|
1
|
+
'V1.9.2rc2'
|
|
@@ -15,6 +15,20 @@ class DatalogConverter:
|
|
|
15
15
|
def __init__(self, cvs_filename: Union[Path, str]):
|
|
16
16
|
self.csv_filename = Path(cvs_filename)
|
|
17
17
|
self.pcap_filename = self.csv_filename.with_suffix(".pcap")
|
|
18
|
+
self.opcode_name = {
|
|
19
|
+
"CAN": {
|
|
20
|
+
0x0001: "FMI3_LS_BUS_OP_FORMAT_ERROR",
|
|
21
|
+
0x0010: "FMI3_LS_BUS_CAN_OP_CAN_TRANSMIT",
|
|
22
|
+
0x0011: "FMI3_LS_BUS_CAN_OP_CANFD_TRANSMIT",
|
|
23
|
+
0x0012: "FMI3_LS_BUS_CAN_OP_CANXL_TRANSMIT",
|
|
24
|
+
0x0020: "FMI3_LS_BUS_CAN_OP_CONFIRM",
|
|
25
|
+
0x0030: "FMI3_LS_BUS_CAN_OP_ARBITRATION_LOST",
|
|
26
|
+
0x0031: "FMI3_LS_BUS_CAN_OP_BUS_ERROR",
|
|
27
|
+
0x0040: "FMI3_LS_BUS_CAN_OP_CONFIGURATION",
|
|
28
|
+
0x0041: "FMI3_LS_BUS_CAN_OP_STATUS",
|
|
29
|
+
0x0042: "FMI3_LS_BUS_CAN_OP_WAKEUP"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
|
|
19
33
|
def open_pcap(self):
|
|
20
34
|
logger.info(f"Creating PCAP file '{self.pcap_filename}'...")
|
|
@@ -31,7 +45,10 @@ class DatalogConverter:
|
|
|
31
45
|
# captured from each packet.
|
|
32
46
|
|
|
33
47
|
file.write(int(227).to_bytes(4, byteorder="big")) # link type. his field is defined in the Section
|
|
34
|
-
#
|
|
48
|
+
# 8.1 IANA registry.
|
|
49
|
+
# 190: Reserved for Controller Area Network (CAN) v. 2.0B packets
|
|
50
|
+
# 210: Reserved for FlexRay automotive bus
|
|
51
|
+
# 227: CAN (Controller Area Network) frames, with a pseudo-header followed by the frame payload.
|
|
35
52
|
return file
|
|
36
53
|
|
|
37
54
|
def open_csv(self):
|
|
@@ -42,15 +59,21 @@ class DatalogConverter:
|
|
|
42
59
|
def decode_hexstring(self, hex_string: bytes, time_s, time_us):
|
|
43
60
|
opcode = int.from_bytes(hex_string[0:4], byteorder="little")
|
|
44
61
|
length = int.from_bytes(hex_string[4:8], byteorder="little")
|
|
45
|
-
|
|
62
|
+
|
|
46
63
|
if opcode == 0x10: # TRANSMIT
|
|
64
|
+
can_id = int.from_bytes(hex_string[8:12], byteorder="little")
|
|
47
65
|
rtr = int.from_bytes(hex_string[13:14], byteorder="little")
|
|
48
66
|
ide = int.from_bytes(hex_string[12:13], byteorder="little")
|
|
49
67
|
data_length = int.from_bytes(hex_string[14:16], byteorder="little")
|
|
50
68
|
raw_data = hex_string[16:]
|
|
51
69
|
|
|
70
|
+
raw_str = ""
|
|
71
|
+
for i, b in enumerate(raw_data):
|
|
72
|
+
raw_str += f"{i}:{b:d} "
|
|
73
|
+
logger.info(f"time={time_s}.{time_us:06d} data length {data_length} with data {raw_str}")
|
|
74
|
+
|
|
52
75
|
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}")
|
|
76
|
+
f" ide={ide} rtr={rtr} len={data_length} raw_len={len(raw_data)} {raw_str}")
|
|
54
77
|
|
|
55
78
|
# TimeStamp
|
|
56
79
|
self.pcapfile.write(time_s.to_bytes(4, byteorder="big"))
|
|
@@ -76,7 +99,14 @@ class DatalogConverter:
|
|
|
76
99
|
self.pcapfile.write(dlc.to_bytes(1, byteorder="big"))
|
|
77
100
|
|
|
78
101
|
# PAYLOAD
|
|
79
|
-
self.pcapfile.write(raw_data)
|
|
102
|
+
self.pcapfile.write(raw_data[0:data_length])
|
|
103
|
+
|
|
104
|
+
elif opcode == 0x40:
|
|
105
|
+
parameter = int.from_bytes(hex_string[8:9], byteorder="little")
|
|
106
|
+
logger.debug(f"Config parameter: {parameter}")
|
|
107
|
+
if (parameter == 1):
|
|
108
|
+
baudrate = int.from_bytes(hex_string[9:13], byteorder="little")
|
|
109
|
+
logger.debug(f"baudrate parameter: {baudrate}")
|
|
80
110
|
|
|
81
111
|
def convert(self):
|
|
82
112
|
with self.open_csv() as self.csvfile, self.open_pcap() as self.pcapfile:
|
|
@@ -196,6 +196,7 @@ class EmbeddedFMU(OperationAbstract):
|
|
|
196
196
|
self.model_identifier = None
|
|
197
197
|
self.guid = None
|
|
198
198
|
self.fmi_version = None
|
|
199
|
+
self.platforms = set()
|
|
199
200
|
self.ports: Dict[str, EmbeddedFMUPort] = {}
|
|
200
201
|
|
|
201
202
|
self.has_event_mode = False
|
|
@@ -240,6 +241,22 @@ class EmbeddedFMU(OperationAbstract):
|
|
|
240
241
|
port = EmbeddedFMUPort(fmu_port.fmi_type, fmu_port, fmi_version=self.fmi_version)
|
|
241
242
|
self.ports[port.name] = port
|
|
242
243
|
|
|
244
|
+
def closure(self):
|
|
245
|
+
osname = {
|
|
246
|
+
"win64": "Windows",
|
|
247
|
+
"linux64": "Linux",
|
|
248
|
+
"darwin64": "Darwin",
|
|
249
|
+
"x86_64-windows": "Windows",
|
|
250
|
+
"x86_64-linux": "Linux",
|
|
251
|
+
"aarch64-darwin": "Darwin"
|
|
252
|
+
}
|
|
253
|
+
try:
|
|
254
|
+
for directory in (Path(self.fmu.tmp_directory) / "binaries").iterdir():
|
|
255
|
+
if directory.is_dir() and str(directory.stem) in osname:
|
|
256
|
+
self.platforms.add(osname[str(directory.stem)])
|
|
257
|
+
except FileNotFoundError:
|
|
258
|
+
pass # no binaries
|
|
259
|
+
|
|
243
260
|
def __repr__(self):
|
|
244
261
|
properties = f"{len(self.ports)} variables, ts={self.step_size}s"
|
|
245
262
|
if len(self.terminals) > 0:
|
|
@@ -602,9 +619,13 @@ class FMUContainer:
|
|
|
602
619
|
canHandleVariableCommunicationStepSize="true"
|
|
603
620
|
canBeInstantiatedOnlyOncePerProcess="{only_once}"
|
|
604
621
|
canNotUseMemoryManagementFunctions="true"
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
622
|
+
canGetAndSetFMUState="false"
|
|
623
|
+
canSerializeFMUState="false"
|
|
624
|
+
providesDirectionalDerivatives="false"
|
|
625
|
+
providesAdjointDerivatives="false"
|
|
626
|
+
providesPerElementDependencies="false"
|
|
627
|
+
providesEvaluateDiscreteStates="false"
|
|
628
|
+
hasEventMode="true"
|
|
608
629
|
needsExecutionTool="{execution_tool}">
|
|
609
630
|
</CoSimulation>
|
|
610
631
|
|
|
@@ -1214,7 +1235,20 @@ class FMUContainer:
|
|
|
1214
1235
|
logger.debug(f"Copying {origin} in {destination}")
|
|
1215
1236
|
shutil.copy(origin, destination)
|
|
1216
1237
|
|
|
1217
|
-
def get_bindir_and_suffixe(self) -> Tuple[str, str, str]:
|
|
1238
|
+
def get_bindir_and_suffixe(self) -> Generator[Tuple[str, str, str], Any, None]:
|
|
1239
|
+
fmu_iter = iter(self.involved_fmu.values())
|
|
1240
|
+
try:
|
|
1241
|
+
fmu = next(fmu_iter)
|
|
1242
|
+
except StopIteration:
|
|
1243
|
+
raise FMUContainerError("No fmu declared in this container.")
|
|
1244
|
+
|
|
1245
|
+
os_list = fmu.platforms
|
|
1246
|
+
logger.debug(f"FMU '{fmu.name}' OS support: {', '.join(fmu.platforms)}.")
|
|
1247
|
+
|
|
1248
|
+
for fmu in fmu_iter:
|
|
1249
|
+
logger.debug(f"FMU '{fmu.name}' OS support: {', '.join(fmu.platforms)}.")
|
|
1250
|
+
os_list &= fmu.platforms
|
|
1251
|
+
|
|
1218
1252
|
suffixes = {
|
|
1219
1253
|
"Windows": "dll",
|
|
1220
1254
|
"Linux": "so",
|
|
@@ -1236,11 +1270,16 @@ class FMUContainer:
|
|
|
1236
1270
|
else:
|
|
1237
1271
|
target_bindirs = origin_bindirs
|
|
1238
1272
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1273
|
+
if os_list:
|
|
1274
|
+
logger.info(f"Container will be built for {', '.join(os_list)}.")
|
|
1275
|
+
else:
|
|
1276
|
+
logger.critical("No common OS found for embedded FMU. Try to re-run with '-debug'. Container won't be runnable.")
|
|
1277
|
+
|
|
1278
|
+
for os_name in os_list:
|
|
1279
|
+
try:
|
|
1280
|
+
yield origin_bindirs[os_name], suffixes[os_name], target_bindirs[os_name]
|
|
1281
|
+
except KeyError:
|
|
1282
|
+
raise FMUContainerError(f"OS '{os_name}' is not supported.")
|
|
1244
1283
|
|
|
1245
1284
|
def make_fmu_skeleton(self, base_directory: Path) -> Path:
|
|
1246
1285
|
logger.debug(f"Initialize directory '{base_directory}'")
|
|
@@ -1260,14 +1299,14 @@ class FMUContainer:
|
|
|
1260
1299
|
|
|
1261
1300
|
self.copyfile(origin / "model.png", base_directory)
|
|
1262
1301
|
|
|
1263
|
-
origin_bindir, suffixe, target_bindir
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1302
|
+
for origin_bindir, suffixe, target_bindir in self.get_bindir_and_suffixe():
|
|
1303
|
+
library_filename = origin / origin_bindir / f"container.{suffixe}"
|
|
1304
|
+
if library_filename.is_file():
|
|
1305
|
+
binary_directory = binaries_directory / target_bindir
|
|
1306
|
+
binary_directory.mkdir(exist_ok=True)
|
|
1307
|
+
self.copyfile(library_filename, binary_directory / f"{self.identifier}.{suffixe}")
|
|
1308
|
+
else:
|
|
1309
|
+
logger.critical(f"File {library_filename} not found.")
|
|
1271
1310
|
|
|
1272
1311
|
for i, fmu in enumerate(self.involved_fmu.values()):
|
|
1273
1312
|
shutil.copytree(self.long_path(fmu.fmu.tmp_directory),
|
|
File without changes
|
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import sys
|
|
3
|
+
import textwrap
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import Qt, QUrl, QDir, Signal, QPoint, QModelIndex
|
|
6
|
+
from PySide6.QtWidgets import (QApplication, QWidget, QGridLayout, QLabel, QLineEdit, QPushButton, QFileDialog,
|
|
7
|
+
QTextBrowser, QInputDialog, QMenu, QTreeView, QAbstractItemView, QTabWidget, QTableView,
|
|
8
|
+
QCheckBox)
|
|
9
|
+
from PySide6.QtGui import (QPixmap, QTextCursor, QStandardItem, QIcon, QDesktopServices, QAction,
|
|
10
|
+
QPainter, QColor, QImage, QStandardItemModel)
|
|
11
|
+
from functools import partial
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from fmu_manipulation_toolbox.gui.gui_style import gui_style, log_color
|
|
15
|
+
from fmu_manipulation_toolbox.operations import *
|
|
16
|
+
from fmu_manipulation_toolbox.remoting import (OperationAddRemotingWin32, OperationAddRemotingWin64, OperationAddFrontendWin32,
|
|
17
|
+
OperationAddFrontendWin64)
|
|
18
|
+
from fmu_manipulation_toolbox.assembly import Assembly, AssemblyNode
|
|
19
|
+
from fmu_manipulation_toolbox.checker import get_checkers
|
|
20
|
+
from fmu_manipulation_toolbox.help import Help
|
|
21
|
+
from fmu_manipulation_toolbox.version import __version__ as version
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("fmu_manipulation_toolbox")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DropZoneWidget(QLabel):
|
|
27
|
+
WIDTH = 150
|
|
28
|
+
HEIGHT = 150
|
|
29
|
+
fmu = None
|
|
30
|
+
last_directory = None
|
|
31
|
+
clicked = Signal()
|
|
32
|
+
|
|
33
|
+
def __init__(self, parent=None):
|
|
34
|
+
super().__init__(parent)
|
|
35
|
+
self.setAcceptDrops(True)
|
|
36
|
+
self.set_image(None)
|
|
37
|
+
self.setProperty("class", "dropped_fmu")
|
|
38
|
+
self.setFixedSize(self.WIDTH, self.HEIGHT)
|
|
39
|
+
|
|
40
|
+
def dragEnterEvent(self, event):
|
|
41
|
+
if event.mimeData().hasUrls():
|
|
42
|
+
event.accept()
|
|
43
|
+
else:
|
|
44
|
+
event.ignore()
|
|
45
|
+
|
|
46
|
+
def dragMoveEvent(self, event):
|
|
47
|
+
if event.mimeData().hasUrls():
|
|
48
|
+
event.accept()
|
|
49
|
+
else:
|
|
50
|
+
event.ignore()
|
|
51
|
+
|
|
52
|
+
def dropEvent(self, event):
|
|
53
|
+
if event.mimeData().hasUrls():
|
|
54
|
+
event.setDropAction(Qt.DropAction.CopyAction)
|
|
55
|
+
try:
|
|
56
|
+
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
57
|
+
except IndexError:
|
|
58
|
+
logger.error("Please select a regular file.")
|
|
59
|
+
return
|
|
60
|
+
self.set_fmu(file_path)
|
|
61
|
+
event.accept()
|
|
62
|
+
else:
|
|
63
|
+
event.ignore()
|
|
64
|
+
|
|
65
|
+
def mousePressEvent(self, event):
|
|
66
|
+
if self.last_directory:
|
|
67
|
+
default_directory = self.last_directory
|
|
68
|
+
else:
|
|
69
|
+
default_directory = os.path.expanduser('~')
|
|
70
|
+
|
|
71
|
+
fmu_filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
|
|
72
|
+
dir=default_directory, filter="FMU files (*.fmu)")
|
|
73
|
+
if fmu_filename:
|
|
74
|
+
self.set_fmu(fmu_filename)
|
|
75
|
+
|
|
76
|
+
def set_image(self, filename=None):
|
|
77
|
+
if not filename:
|
|
78
|
+
filename = os.path.join(os.path.dirname(__file__), "../resources", "drop_fmu.png")
|
|
79
|
+
elif not os.path.isfile(filename):
|
|
80
|
+
filename = os.path.join(os.path.dirname(__file__), "../resources", "fmu.png")
|
|
81
|
+
|
|
82
|
+
base_image = QImage(filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
83
|
+
Qt.TransformationMode.SmoothTransformation)
|
|
84
|
+
mask_filename = os.path.join(os.path.dirname(__file__), "../resources", "mask.png")
|
|
85
|
+
mask_image = QImage(mask_filename).scaled(self.WIDTH, self.HEIGHT, Qt.AspectRatioMode.IgnoreAspectRatio,
|
|
86
|
+
Qt.TransformationMode.SmoothTransformation)
|
|
87
|
+
rounded_image = QImage(self.WIDTH, self.HEIGHT, QImage.Format.Format_ARGB32)
|
|
88
|
+
rounded_image.fill(QColor(0, 0, 0, 0))
|
|
89
|
+
painter = QPainter()
|
|
90
|
+
painter.begin(rounded_image)
|
|
91
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
92
|
+
painter.drawImage(QPoint(0, 0), base_image)
|
|
93
|
+
painter.drawImage(QPoint(0, 0), mask_image)
|
|
94
|
+
painter.end()
|
|
95
|
+
pixmap = QPixmap.fromImage(rounded_image)
|
|
96
|
+
|
|
97
|
+
self.setPixmap(pixmap)
|
|
98
|
+
|
|
99
|
+
def set_fmu(self, filename: str):
|
|
100
|
+
try:
|
|
101
|
+
self.last_directory = os.path.dirname(filename)
|
|
102
|
+
self.fmu = FMU(filename)
|
|
103
|
+
self.set_image(os.path.join(self.fmu.tmp_directory, "model.png"))
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.error(f"Cannot load this FMU: {e}")
|
|
106
|
+
self.set_image(None)
|
|
107
|
+
self.fmu = None
|
|
108
|
+
self.clicked.emit()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class LogHandler(logging.Handler):
|
|
112
|
+
LOG_COLOR = {
|
|
113
|
+
logging.DEBUG: QColor(log_color["DEBUG"]),
|
|
114
|
+
logging.INFO: QColor(log_color["INFO"]),
|
|
115
|
+
logging.WARNING: QColor(log_color["WARNING"]),
|
|
116
|
+
logging.ERROR: QColor(log_color["ERROR"]),
|
|
117
|
+
logging.CRITICAL: QColor(log_color["CRITICAL"]),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def __init__(self, text_browser, level):
|
|
121
|
+
super().__init__(level)
|
|
122
|
+
self.text_browser: QTextBrowser = text_browser
|
|
123
|
+
logger.addHandler(self)
|
|
124
|
+
logger.setLevel(level)
|
|
125
|
+
|
|
126
|
+
def emit(self, record) -> None:
|
|
127
|
+
self.text_browser.setTextColor(self.LOG_COLOR[record.levelno])
|
|
128
|
+
self.text_browser.insertPlainText(record.msg+"\n")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class LogWidget(QTextBrowser):
|
|
132
|
+
def __init__(self, parent=None, level=logging.INFO):
|
|
133
|
+
super().__init__(parent)
|
|
134
|
+
|
|
135
|
+
self.setMinimumWidth(900)
|
|
136
|
+
self.setMinimumHeight(500)
|
|
137
|
+
self.setSearchPaths([os.path.join(os.path.dirname(__file__), "../resources")])
|
|
138
|
+
self.insertHtml('<center><img src="fmu_manipulation_toolbox.png"/></center><br/>')
|
|
139
|
+
self.log_handler = LogHandler(self, logging.DEBUG)
|
|
140
|
+
|
|
141
|
+
def loadResource(self, _, name):
|
|
142
|
+
image_path = os.path.join(os.path.dirname(__file__), "../resources", name.toString())
|
|
143
|
+
return QPixmap(image_path)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class HelpWidget(QLabel):
|
|
147
|
+
HELP_URL = "https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/README.md"
|
|
148
|
+
|
|
149
|
+
def __init__(self):
|
|
150
|
+
super().__init__()
|
|
151
|
+
self.setProperty("class", "help")
|
|
152
|
+
|
|
153
|
+
filename = os.path.join(os.path.dirname(__file__), "../resources", "help.png")
|
|
154
|
+
image = QPixmap(filename)
|
|
155
|
+
self.setPixmap(image)
|
|
156
|
+
self.setAlignment(Qt.AlignmentFlag.AlignRight)
|
|
157
|
+
|
|
158
|
+
def mousePressEvent(self, event):
|
|
159
|
+
QDesktopServices.openUrl(QUrl(self.HELP_URL))
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class FilterWidget(QPushButton):
|
|
163
|
+
def __init__(self, items: Optional[list[str]] = (), parent=None):
|
|
164
|
+
super().__init__(parent)
|
|
165
|
+
self.items_selected = set(items)
|
|
166
|
+
self.nb_items = len(items)
|
|
167
|
+
self.update_filter_text()
|
|
168
|
+
if items:
|
|
169
|
+
self.menu = QMenu()
|
|
170
|
+
for item in items:
|
|
171
|
+
action = QAction(item, self)
|
|
172
|
+
action.setCheckable(True)
|
|
173
|
+
action.setChecked(True)
|
|
174
|
+
action.triggered.connect(partial(self.toggle_item, action))
|
|
175
|
+
self.menu.addAction(action)
|
|
176
|
+
self.setMenu(self.menu)
|
|
177
|
+
|
|
178
|
+
def toggle_item(self, action: QAction):
|
|
179
|
+
if not action.isChecked() and len(self.items_selected) == 1:
|
|
180
|
+
action.setChecked(True)
|
|
181
|
+
|
|
182
|
+
if action.isChecked():
|
|
183
|
+
self.items_selected.add(action.text())
|
|
184
|
+
else:
|
|
185
|
+
self.items_selected.remove(action.text())
|
|
186
|
+
|
|
187
|
+
self.update_filter_text()
|
|
188
|
+
|
|
189
|
+
def update_filter_text(self):
|
|
190
|
+
if len(self.items_selected) == self.nb_items:
|
|
191
|
+
self.setText("All causalities")
|
|
192
|
+
else:
|
|
193
|
+
self.setText(", ".join(sorted(self.items_selected)))
|
|
194
|
+
|
|
195
|
+
def get(self):
|
|
196
|
+
if len(self.items_selected) == self.nb_items:
|
|
197
|
+
return []
|
|
198
|
+
else:
|
|
199
|
+
return sorted(self.items_selected)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class AssemblyTreeWidget(QTreeView):
|
|
203
|
+
class AssemblyTreeModel(QStandardItemModel):
|
|
204
|
+
|
|
205
|
+
def __init__(self, assembly: Assembly, parent=None):
|
|
206
|
+
super().__init__(parent)
|
|
207
|
+
|
|
208
|
+
self.lastDroppedItems = []
|
|
209
|
+
self.pendingRemoveRowsAfterDrop = False
|
|
210
|
+
self.setHorizontalHeaderLabels(['col1'])
|
|
211
|
+
self.dnd_target_node: Optional[AssemblyNode] = None
|
|
212
|
+
|
|
213
|
+
self.icon_container = QIcon(os.path.join(os.path.dirname(__file__), '../resources', 'container.png'))
|
|
214
|
+
self.icon_fmu = QIcon(os.path.join(os.path.dirname(__file__), '../resources', 'icon_fmu.png'))
|
|
215
|
+
|
|
216
|
+
if assembly:
|
|
217
|
+
self.add_node(assembly.root, self)
|
|
218
|
+
|
|
219
|
+
def add_node(self, node: AssemblyNode, parent_item):
|
|
220
|
+
# Add Container
|
|
221
|
+
item = QStandardItem(self.icon_container, node.name)
|
|
222
|
+
parent_item.appendRow(item)
|
|
223
|
+
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsDragEnabled |
|
|
224
|
+
Qt.ItemFlag.ItemIsDropEnabled)
|
|
225
|
+
item.setData(node, role=Qt.ItemDataRole.UserRole + 1)
|
|
226
|
+
item.setData("container", role=Qt.ItemDataRole.UserRole + 2)
|
|
227
|
+
|
|
228
|
+
# Add FMU's
|
|
229
|
+
children_name = node.children.keys()
|
|
230
|
+
for fmu_name in node.fmu_names_list:
|
|
231
|
+
if fmu_name not in children_name:
|
|
232
|
+
fmu_node = QStandardItem(self.icon_fmu, fmu_name)
|
|
233
|
+
fmu_node.setData(node, role=Qt.ItemDataRole.UserRole + 1)
|
|
234
|
+
fmu_node.setData("fmu", role=Qt.ItemDataRole.UserRole + 2)
|
|
235
|
+
fmu_node.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable |
|
|
236
|
+
Qt.ItemFlag.ItemIsDragEnabled)
|
|
237
|
+
item.appendRow(fmu_node)
|
|
238
|
+
|
|
239
|
+
# Add Sub-Containers
|
|
240
|
+
for child in node.children.values():
|
|
241
|
+
self.add_node(child, item)
|
|
242
|
+
|
|
243
|
+
def insertRows(self, row, count, parent=QModelIndex()):
|
|
244
|
+
self.dnd_target_node = parent.data(role=Qt.ItemDataRole.UserRole + 1)
|
|
245
|
+
return super().insertRows(row, count, parent=parent)
|
|
246
|
+
|
|
247
|
+
def removeRows(self, row, count, parent=QModelIndex()):
|
|
248
|
+
if not self.dnd_target_node:
|
|
249
|
+
logger.error("NO DROP NODE!?")
|
|
250
|
+
|
|
251
|
+
source_index = self.itemFromIndex(parent).child(row, 0).data(role=Qt.ItemDataRole.UserRole+1)
|
|
252
|
+
logger.debug(f"{source_index} ==> {self.dnd_target_node.name}")
|
|
253
|
+
|
|
254
|
+
self.dnd_target_node = None
|
|
255
|
+
return super().removeRows(row, count, parent)
|
|
256
|
+
|
|
257
|
+
def dropMimeData(self, data, action, row, column, parent: QModelIndex):
|
|
258
|
+
if parent.column() < 0: # Avoid to drop item as a sibling of the root.
|
|
259
|
+
return False
|
|
260
|
+
return super().dropMimeData(data, action, row, column, parent)
|
|
261
|
+
|
|
262
|
+
def __init__(self, parent=None):
|
|
263
|
+
super().__init__(parent)
|
|
264
|
+
|
|
265
|
+
self.model = self.AssemblyTreeModel(None)
|
|
266
|
+
self.setModel(self.model)
|
|
267
|
+
|
|
268
|
+
self.expandAll()
|
|
269
|
+
self.setDropIndicatorShown(True)
|
|
270
|
+
self.setDragDropOverwriteMode(False)
|
|
271
|
+
self.setAcceptDrops(True)
|
|
272
|
+
self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
|
|
273
|
+
self.setRootIsDecorated(True)
|
|
274
|
+
self.setHeaderHidden(True)
|
|
275
|
+
|
|
276
|
+
def load_container(self, filename):
|
|
277
|
+
assembly = Assembly(filename)
|
|
278
|
+
self.model = self.AssemblyTreeModel(assembly)
|
|
279
|
+
self.setModel(self.model)
|
|
280
|
+
|
|
281
|
+
def setTopIndex(self):
|
|
282
|
+
topIndex = self.model.index(0, 0, self.rootIndex())
|
|
283
|
+
logger.debug(topIndex.isValid(), topIndex.model())
|
|
284
|
+
if topIndex.isValid():
|
|
285
|
+
self.setCurrentIndex(topIndex)
|
|
286
|
+
if self.layoutCheck:
|
|
287
|
+
self.model.layoutChanged.disconnect(self.setTopIndex)
|
|
288
|
+
else:
|
|
289
|
+
if not self.layoutCheck:
|
|
290
|
+
self.model.layoutChanged.connect(self.setTopIndex)
|
|
291
|
+
self.layoutCheck = True
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def dragEnterEvent2(self, event):
|
|
295
|
+
if event.mimeData().hasImage:
|
|
296
|
+
event.accept()
|
|
297
|
+
else:
|
|
298
|
+
event.ignore()
|
|
299
|
+
|
|
300
|
+
def dragMoveEvent2(self, event):
|
|
301
|
+
if event.mimeData().hasImage:
|
|
302
|
+
event.accept()
|
|
303
|
+
else:
|
|
304
|
+
event.ignore()
|
|
305
|
+
|
|
306
|
+
def dropEvent2(self, event):
|
|
307
|
+
if event.mimeData().hasImage:
|
|
308
|
+
event.setDropAction(Qt.DropAction.CopyAction)
|
|
309
|
+
try:
|
|
310
|
+
file_path = event.mimeData().urls()[0].toLocalFile()
|
|
311
|
+
except IndexError:
|
|
312
|
+
logger.error("Please select a regular file.")
|
|
313
|
+
return
|
|
314
|
+
logger.debug(f"DROP: {file_path}")
|
|
315
|
+
event.accept()
|
|
316
|
+
else:
|
|
317
|
+
event.ignore()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class AssemblPropertiesWidget(QWidget):
|
|
321
|
+
def __init__(self, parent=None):
|
|
322
|
+
super().__init__(parent)
|
|
323
|
+
|
|
324
|
+
self.layout = QGridLayout()
|
|
325
|
+
self.layout.setVerticalSpacing(4)
|
|
326
|
+
self.layout.setHorizontalSpacing(4)
|
|
327
|
+
self.layout.setContentsMargins(10, 10, 10, 10)
|
|
328
|
+
self.setLayout(self.layout)
|
|
329
|
+
|
|
330
|
+
mt_check = QCheckBox("Multi-Threaded", self)
|
|
331
|
+
self.layout.addWidget(mt_check, 1, 0)
|
|
332
|
+
|
|
333
|
+
profiling_check = QCheckBox("Profiling", self)
|
|
334
|
+
self.layout.addWidget(profiling_check, 1, 1)
|
|
335
|
+
|
|
336
|
+
auto_inputs_check = QCheckBox("Auto Inputs", self)
|
|
337
|
+
self.layout.addWidget(auto_inputs_check, 0, 0)
|
|
338
|
+
|
|
339
|
+
auto_outputs_check = QCheckBox("Auto Outputs", self)
|
|
340
|
+
self.layout.addWidget(auto_outputs_check, 0, 1)
|
|
341
|
+
|
|
342
|
+
auto_links_check = QCheckBox("Auto Links", self)
|
|
343
|
+
self.layout.addWidget(auto_links_check, 0, 2)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class AssemblyTabWidget(QTabWidget):
|
|
347
|
+
def __init__(self, parent=None):
|
|
348
|
+
super().__init__(parent)
|
|
349
|
+
|
|
350
|
+
table = AssemblPropertiesWidget(parent=self)
|
|
351
|
+
self.addTab(table, "Properties")
|
|
352
|
+
table = QTableView()
|
|
353
|
+
self.addTab(table, "Links")
|
|
354
|
+
table = QTableView()
|
|
355
|
+
self.addTab(table, "Inputs")
|
|
356
|
+
table = QTableView()
|
|
357
|
+
self.addTab(table, "Outputs")
|
|
358
|
+
table = QTableView()
|
|
359
|
+
self.addTab(table, "Start values")
|
|
360
|
+
|
|
361
|
+
self.tabBar().setDocumentMode(True)
|
|
362
|
+
self.tabBar().setExpanding(True)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class WindowWithLayout(QWidget):
|
|
366
|
+
def __init__(self, title: str):
|
|
367
|
+
super().__init__(None) # Do not set parent to have a separated window
|
|
368
|
+
self.setWindowTitle(title)
|
|
369
|
+
|
|
370
|
+
self.layout = QGridLayout()
|
|
371
|
+
self.layout.setVerticalSpacing(4)
|
|
372
|
+
self.layout.setHorizontalSpacing(4)
|
|
373
|
+
self.layout.setContentsMargins(10, 10, 10, 10)
|
|
374
|
+
self.setLayout(self.layout)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class MainWindow(WindowWithLayout):
|
|
378
|
+
def __init__(self):
|
|
379
|
+
super().__init__('FMU Manipulation Toolbox')
|
|
380
|
+
|
|
381
|
+
self.dropped_fmu = DropZoneWidget()
|
|
382
|
+
self.dropped_fmu.clicked.connect(self.update_fmu)
|
|
383
|
+
|
|
384
|
+
self.layout.addWidget(self.dropped_fmu, 0, 0, 4, 1)
|
|
385
|
+
|
|
386
|
+
self.fmu_title = QLabel()
|
|
387
|
+
self.fmu_title.setProperty("class", "title")
|
|
388
|
+
self.layout.addWidget(self.fmu_title, 0, 1, 1, 4)
|
|
389
|
+
|
|
390
|
+
self.container_window = None
|
|
391
|
+
#TODO: Container Window
|
|
392
|
+
#container_button = QPushButton("Make a container")
|
|
393
|
+
#container_button.setProperty("class", "quit")
|
|
394
|
+
#container_button.clicked.connect(self.launch_container)
|
|
395
|
+
#self.layout.addWidget(container_button, 4, 1, 1, 1)
|
|
396
|
+
|
|
397
|
+
help_widget = HelpWidget()
|
|
398
|
+
self.layout.addWidget(help_widget, 0, 5, 1, 1)
|
|
399
|
+
|
|
400
|
+
# Operations
|
|
401
|
+
self.help = Help()
|
|
402
|
+
operations_list = [
|
|
403
|
+
("Save port names", '-dump-csv', 'save', OperationSaveNamesToCSV, {"prompt_file": "write"}),
|
|
404
|
+
("Rename ports from CSV", '-rename-from-csv', 'modify', OperationRenameFromCSV, {"prompt_file": "read"}),
|
|
405
|
+
("Remove Toplevel", '-remove-toplevel', 'modify', OperationStripTopLevel),
|
|
406
|
+
("Remove Regexp", '-remove-regexp', 'removal', OperationRemoveRegexp, {"prompt": "regexp"}),
|
|
407
|
+
("Keep only Regexp", '-keep-only-regexp', 'removal', OperationKeepOnlyRegexp, {"prompt": "regexp"}),
|
|
408
|
+
("Save description.xml", '-extract-descriptor', 'save', None, {"func": self.save_descriptor}),
|
|
409
|
+
("Trim Until", '-trim-until', 'modify', OperationTrimUntil, {"prompt": "Prefix"}),
|
|
410
|
+
("Merge Toplevel", '-merge-toplevel', 'modify', OperationMergeTopLevel),
|
|
411
|
+
("Remove all", '-remove-all', 'removal', OperationRemoveRegexp, {"arg": ".*"}),
|
|
412
|
+
("Remove sources", '-remove-sources', 'removal', OperationRemoveSources),
|
|
413
|
+
("Add Win32 remoting", '-add-remoting-win32', 'info', OperationAddRemotingWin32),
|
|
414
|
+
("Add Win64 remoting", '-add-remoting-win64', 'info', OperationAddRemotingWin64),
|
|
415
|
+
("Add Win32 frontend", '-add-frontend-win32', 'info', OperationAddFrontendWin32),
|
|
416
|
+
("Add Win64 frontend", '-add-frontend-win64', 'info', OperationAddFrontendWin64),
|
|
417
|
+
("Check", '-check', 'info', get_checkers()),
|
|
418
|
+
]
|
|
419
|
+
|
|
420
|
+
width = 5
|
|
421
|
+
line = 1
|
|
422
|
+
for i, operation in enumerate(operations_list):
|
|
423
|
+
col = i % width + 1
|
|
424
|
+
line = int(i / width) + 1
|
|
425
|
+
|
|
426
|
+
if len(operation) < 5:
|
|
427
|
+
self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col)
|
|
428
|
+
else:
|
|
429
|
+
self.add_operation(operation[0], operation[1], operation[2], operation[3], line, col, **operation[4])
|
|
430
|
+
|
|
431
|
+
line += 1
|
|
432
|
+
self.apply_filter_label = QLabel("Apply only on: ")
|
|
433
|
+
self.layout.addWidget(self.apply_filter_label, line, 2, 1, 1, alignment=Qt.AlignmentFlag.AlignRight)
|
|
434
|
+
self.set_tooltip(self.apply_filter_label, 'gui-apply-only')
|
|
435
|
+
|
|
436
|
+
causality = ["parameter", "calculatedParameter", "input", "output", "local", "independent"]
|
|
437
|
+
self.filter_list = FilterWidget(items=causality)
|
|
438
|
+
self.layout.addWidget(self.filter_list, line, 3, 1, 3)
|
|
439
|
+
self.filter_list.setProperty("class", "quit")
|
|
440
|
+
|
|
441
|
+
# Text
|
|
442
|
+
line += 1
|
|
443
|
+
self.log_widget = LogWidget()
|
|
444
|
+
self.layout.addWidget(self.log_widget, line, 0, 1, width + 1)
|
|
445
|
+
|
|
446
|
+
# buttons
|
|
447
|
+
line += 1
|
|
448
|
+
|
|
449
|
+
reload_button = QPushButton('Reload')
|
|
450
|
+
self.layout.addWidget(reload_button, 4, 0, 1, 1)
|
|
451
|
+
reload_button.clicked.connect(self.reload_fmu)
|
|
452
|
+
reload_button.setProperty("class", "quit")
|
|
453
|
+
|
|
454
|
+
exit_button = QPushButton('Exit')
|
|
455
|
+
self.layout.addWidget(exit_button, line, 0, 1, 2)
|
|
456
|
+
exit_button.clicked.connect(self.close)
|
|
457
|
+
exit_button.setProperty("class", "quit")
|
|
458
|
+
|
|
459
|
+
save_log_button = QPushButton('Save log as')
|
|
460
|
+
self.layout.addWidget(save_log_button, line, 2, 1, 2)
|
|
461
|
+
save_log_button.clicked.connect(self.save_log)
|
|
462
|
+
save_log_button.setProperty("class", "save")
|
|
463
|
+
|
|
464
|
+
save_fmu_button = QPushButton('Save modified FMU as')
|
|
465
|
+
self.layout.addWidget(save_fmu_button, line, 4, 1, 2)
|
|
466
|
+
save_fmu_button.clicked.connect(self.save_fmu)
|
|
467
|
+
save_fmu_button.setProperty("class", "save")
|
|
468
|
+
self.set_tooltip(save_fmu_button, '-output')
|
|
469
|
+
|
|
470
|
+
# show the window
|
|
471
|
+
self.show()
|
|
472
|
+
|
|
473
|
+
def closeEvent(self, event):
|
|
474
|
+
if self.container_window:
|
|
475
|
+
self.container_window.close()
|
|
476
|
+
self.container_window = None
|
|
477
|
+
event.accept()
|
|
478
|
+
|
|
479
|
+
def launch_container(self):
|
|
480
|
+
if not self.container_window:
|
|
481
|
+
self.container_window = ContainerWindow(self)
|
|
482
|
+
|
|
483
|
+
def closing_container(self):
|
|
484
|
+
self.container_window = None
|
|
485
|
+
|
|
486
|
+
def set_tooltip(self, widget, usage):
|
|
487
|
+
widget.setToolTip("\n".join(textwrap.wrap(self.help.usage(usage))))
|
|
488
|
+
|
|
489
|
+
def reload_fmu(self):
|
|
490
|
+
if self.dropped_fmu.fmu:
|
|
491
|
+
filename = self.dropped_fmu.fmu.fmu_filename
|
|
492
|
+
self.dropped_fmu.fmu = None
|
|
493
|
+
self.dropped_fmu.set_fmu(filename)
|
|
494
|
+
|
|
495
|
+
def save_descriptor(self):
|
|
496
|
+
if self.dropped_fmu.fmu:
|
|
497
|
+
fmu = self.dropped_fmu.fmu
|
|
498
|
+
filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
|
|
499
|
+
os.path.dirname(fmu.fmu_filename),
|
|
500
|
+
"XML files (*.xml)")
|
|
501
|
+
if ok and filename:
|
|
502
|
+
fmu.save_descriptor(filename)
|
|
503
|
+
|
|
504
|
+
def save_fmu(self):
|
|
505
|
+
if self.dropped_fmu.fmu:
|
|
506
|
+
fmu = self.dropped_fmu.fmu
|
|
507
|
+
filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
|
|
508
|
+
os.path.dirname(fmu.fmu_filename),
|
|
509
|
+
"FMU files (*.fmu)")
|
|
510
|
+
if ok and filename:
|
|
511
|
+
fmu.repack(filename)
|
|
512
|
+
logger.info(f"Modified version saved as {filename}.")
|
|
513
|
+
|
|
514
|
+
def save_log(self):
|
|
515
|
+
if self.dropped_fmu.fmu:
|
|
516
|
+
default_dir = os.path.dirname(self.dropped_fmu.fmu.fmu_filename)
|
|
517
|
+
else:
|
|
518
|
+
default_dir = None
|
|
519
|
+
filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
|
|
520
|
+
default_dir,
|
|
521
|
+
"TXT files (*.txt)")
|
|
522
|
+
if ok and filename:
|
|
523
|
+
try:
|
|
524
|
+
with open(filename, "wt") as file:
|
|
525
|
+
file.write(str(self.log_widget.toPlainText()))
|
|
526
|
+
except Exception as e:
|
|
527
|
+
logger.error(f"{e}")
|
|
528
|
+
|
|
529
|
+
def add_operation(self, name, usage, severity, operation, x, y, prompt=None, prompt_file=None, arg=None,
|
|
530
|
+
func=None):
|
|
531
|
+
if prompt:
|
|
532
|
+
def operation_handler():
|
|
533
|
+
local_arg = self.prompt_string(prompt)
|
|
534
|
+
if local_arg:
|
|
535
|
+
self.apply_operation(operation(local_arg))
|
|
536
|
+
elif prompt_file:
|
|
537
|
+
def operation_handler():
|
|
538
|
+
local_arg = self.prompt_file(prompt_file)
|
|
539
|
+
if local_arg:
|
|
540
|
+
self.apply_operation(operation(local_arg))
|
|
541
|
+
elif arg:
|
|
542
|
+
def operation_handler():
|
|
543
|
+
self.apply_operation(operation(arg))
|
|
544
|
+
else:
|
|
545
|
+
def operation_handler():
|
|
546
|
+
# Checker can be a list of operations!
|
|
547
|
+
if isinstance(operation, list):
|
|
548
|
+
for op in operation:
|
|
549
|
+
self.apply_operation(op())
|
|
550
|
+
else:
|
|
551
|
+
self.apply_operation(operation())
|
|
552
|
+
|
|
553
|
+
button = QPushButton(name)
|
|
554
|
+
self.set_tooltip(button, usage)
|
|
555
|
+
button.setProperty("class", severity)
|
|
556
|
+
if func:
|
|
557
|
+
button.clicked.connect(func)
|
|
558
|
+
else:
|
|
559
|
+
button.clicked.connect(operation_handler)
|
|
560
|
+
self.layout.addWidget(button, x, y)
|
|
561
|
+
|
|
562
|
+
def prompt_string(self, message):
|
|
563
|
+
text, ok = QInputDialog().getText(self, "Enter value", f"{message}:", QLineEdit.EchoMode.Normal, "")
|
|
564
|
+
|
|
565
|
+
if ok and text:
|
|
566
|
+
return text
|
|
567
|
+
else:
|
|
568
|
+
return None
|
|
569
|
+
|
|
570
|
+
def prompt_file(self, access):
|
|
571
|
+
if self.dropped_fmu.fmu:
|
|
572
|
+
default_dir = os.path.dirname(self.dropped_fmu.fmu.fmu_filename)
|
|
573
|
+
|
|
574
|
+
if access == 'read':
|
|
575
|
+
filename, ok = QFileDialog.getOpenFileName(self, "Select a file",
|
|
576
|
+
default_dir, "CSV files (*.csv)")
|
|
577
|
+
else:
|
|
578
|
+
filename, ok = QFileDialog.getSaveFileName(self, "Select a file",
|
|
579
|
+
default_dir, "CSV files (*.csv)")
|
|
580
|
+
|
|
581
|
+
if ok and filename:
|
|
582
|
+
return filename
|
|
583
|
+
return None
|
|
584
|
+
|
|
585
|
+
def update_fmu(self):
|
|
586
|
+
if self.dropped_fmu.fmu:
|
|
587
|
+
self.fmu_title.setText(os.path.basename(self.dropped_fmu.fmu.fmu_filename))
|
|
588
|
+
self.log_widget.clear()
|
|
589
|
+
self.apply_operation(OperationSummary())
|
|
590
|
+
else:
|
|
591
|
+
self.fmu_title.setText('')
|
|
592
|
+
|
|
593
|
+
def apply_operation(self, operation):
|
|
594
|
+
if self.dropped_fmu.fmu:
|
|
595
|
+
self.log_widget.moveCursor(QTextCursor.MoveOperation.End)
|
|
596
|
+
fmu_filename = os.path.basename(self.dropped_fmu.fmu.fmu_filename)
|
|
597
|
+
logger.info('-' * 100)
|
|
598
|
+
self.log_widget.insertHtml(f"<strong>{fmu_filename}: {operation}</strong><br>")
|
|
599
|
+
|
|
600
|
+
apply_on = self.filter_list.get()
|
|
601
|
+
if apply_on:
|
|
602
|
+
self.log_widget.insertHtml(f"<i>Applied only for ports with causality = " +
|
|
603
|
+
", ".join(apply_on) + "</i><br>")
|
|
604
|
+
logger.info('-' * 100)
|
|
605
|
+
try:
|
|
606
|
+
self.dropped_fmu.fmu.apply_operation(operation, apply_on=apply_on)
|
|
607
|
+
except Exception as e:
|
|
608
|
+
logger.error(f"{e}")
|
|
609
|
+
|
|
610
|
+
scroll_bar = self.log_widget.verticalScrollBar()
|
|
611
|
+
scroll_bar.setValue(scroll_bar.maximum())
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
class ContainerWindow(WindowWithLayout):
|
|
615
|
+
def __init__(self, parent: MainWindow):
|
|
616
|
+
super().__init__('FMU Manipulation Toolbox - Container')
|
|
617
|
+
self.main_window = parent
|
|
618
|
+
self.last_directory = None
|
|
619
|
+
|
|
620
|
+
# ROW 0
|
|
621
|
+
load_button = QPushButton("Load Description")
|
|
622
|
+
load_button.clicked.connect(self.load_container)
|
|
623
|
+
load_button.setProperty("class", "quit")
|
|
624
|
+
self.layout.addWidget(load_button, 0, 0)
|
|
625
|
+
|
|
626
|
+
self.container_label = QLabel()
|
|
627
|
+
self.container_label.setProperty("class", "title")
|
|
628
|
+
self.container_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
629
|
+
self.layout.addWidget(self.container_label, 0, 1, 1, 2)
|
|
630
|
+
|
|
631
|
+
# ROW 1
|
|
632
|
+
add_fmu_button = QPushButton("Add FMU")
|
|
633
|
+
add_fmu_button.setProperty("class", "modify")
|
|
634
|
+
add_fmu_button.setDisabled(True)
|
|
635
|
+
self.layout.addWidget(add_fmu_button, 1, 1)
|
|
636
|
+
|
|
637
|
+
add_sub_button = QPushButton("Add SubContainer")
|
|
638
|
+
add_sub_button.setProperty("class", "modify")
|
|
639
|
+
add_sub_button.setDisabled(True)
|
|
640
|
+
self.layout.addWidget(add_sub_button, 1, 2)
|
|
641
|
+
|
|
642
|
+
self.assembly_tree = AssemblyTreeWidget(parent=self)
|
|
643
|
+
self.assembly_tree.setMinimumHeight(600)
|
|
644
|
+
self.assembly_tree.setMinimumWidth(200)
|
|
645
|
+
self.layout.addWidget(self.assembly_tree, 1, 0, 3, 1)
|
|
646
|
+
|
|
647
|
+
# ROW 2
|
|
648
|
+
del_fmu_button = QPushButton("Remove FMU")
|
|
649
|
+
del_fmu_button.setProperty("class", "removal")
|
|
650
|
+
del_fmu_button.setDisabled(True)
|
|
651
|
+
self.layout.addWidget(del_fmu_button, 2, 1)
|
|
652
|
+
|
|
653
|
+
del_sub_button = QPushButton("Remove SubContainer")
|
|
654
|
+
del_sub_button.setProperty("class", "removal")
|
|
655
|
+
del_sub_button.setDisabled(True)
|
|
656
|
+
self.layout.addWidget(del_sub_button, 2, 2)
|
|
657
|
+
|
|
658
|
+
# ROW 3
|
|
659
|
+
self.assembly_tab = AssemblyTabWidget(parent=self)
|
|
660
|
+
self.assembly_tab.setMinimumWidth(600)
|
|
661
|
+
self.layout.addWidget(self.assembly_tab, 3, 1, 1, 2)
|
|
662
|
+
|
|
663
|
+
# ROW 4
|
|
664
|
+
close_button = QPushButton("Close")
|
|
665
|
+
close_button.setProperty("class", "quit")
|
|
666
|
+
close_button.clicked.connect(self.close)
|
|
667
|
+
self.layout.addWidget(close_button, 4, 0)
|
|
668
|
+
|
|
669
|
+
save_button = QPushButton("Save Container")
|
|
670
|
+
save_button.setProperty("class", "save")
|
|
671
|
+
self.layout.addWidget(save_button, 4, 2)
|
|
672
|
+
|
|
673
|
+
self.assembly_tree.selectionModel().currentChanged.connect(self.item_selected)
|
|
674
|
+
topIndex = self.assembly_tree.model.index(0, 0, self.assembly_tree.rootIndex())
|
|
675
|
+
self.assembly_tree.setCurrentIndex(topIndex)
|
|
676
|
+
|
|
677
|
+
self.show()
|
|
678
|
+
|
|
679
|
+
def closeEvent(self, event):
|
|
680
|
+
if self.main_window:
|
|
681
|
+
self.main_window.closing_container()
|
|
682
|
+
event.accept()
|
|
683
|
+
|
|
684
|
+
def item_selected(self, current: QModelIndex, previous: QModelIndex):
|
|
685
|
+
if current.isValid():
|
|
686
|
+
node = current.data(role=Qt.ItemDataRole.UserRole + 1)
|
|
687
|
+
node_type = current.data(role=Qt.ItemDataRole.UserRole + 2)
|
|
688
|
+
self.container_label.setText(f"{node.name} ({node_type})")
|
|
689
|
+
else:
|
|
690
|
+
self.container_label.setText("")
|
|
691
|
+
|
|
692
|
+
def load_container(self):
|
|
693
|
+
if self.last_directory:
|
|
694
|
+
default_directory = self.last_directory
|
|
695
|
+
else:
|
|
696
|
+
default_directory = os.path.expanduser('~')
|
|
697
|
+
|
|
698
|
+
filename, _ = QFileDialog.getOpenFileName(parent=self, caption='Select FMU to Manipulate',
|
|
699
|
+
dir=default_directory,
|
|
700
|
+
filter="JSON files (*.json);;SSP files (*.ssp)")
|
|
701
|
+
if filename:
|
|
702
|
+
try:
|
|
703
|
+
self.last_directory = os.path.dirname(filename)
|
|
704
|
+
self.assembly_tree.load_container(filename)
|
|
705
|
+
except Exception as e:
|
|
706
|
+
logger.error(e)
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
class Application(QApplication):
|
|
710
|
+
"""
|
|
711
|
+
Analyse and modify your FMUs.
|
|
712
|
+
|
|
713
|
+
Note: modifying the modelDescription.xml can damage your FMU !
|
|
714
|
+
Communicating with the FMU-developer and adapting the way the FMU is generated, is preferable when possible.
|
|
715
|
+
|
|
716
|
+
"""
|
|
717
|
+
def __init__(self, *args, **kwargs):
|
|
718
|
+
self.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.RoundPreferFloor)
|
|
719
|
+
super().__init__(*args, **kwargs)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
QDir.addSearchPath('images', os.path.join(os.path.dirname(__file__), "../resources"))
|
|
723
|
+
self.setStyleSheet(gui_style)
|
|
724
|
+
|
|
725
|
+
if os.name == 'nt':
|
|
726
|
+
import ctypes
|
|
727
|
+
self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), '../resources', 'icon-round.png')))
|
|
728
|
+
|
|
729
|
+
# https://stackoverflow.com/questions/1551605/how-to-set-applications-taskbar-icon-in-windows-7/1552105#1552105
|
|
730
|
+
|
|
731
|
+
application_id = 'FMU_Manipulation_Toolbox' # arbitrary string
|
|
732
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(application_id)
|
|
733
|
+
else:
|
|
734
|
+
self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), '../resources', 'icon.png')))
|
|
735
|
+
|
|
736
|
+
self.window = MainWindow()
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def main():
|
|
740
|
+
application = Application(sys.argv)
|
|
741
|
+
|
|
742
|
+
logger.info(" " * 80 + f"Version {version}")
|
|
743
|
+
logger.info(application.__doc__)
|
|
744
|
+
|
|
745
|
+
sys.exit(application.exec())
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
if __name__ == "__main__":
|
|
749
|
+
main()
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
if os.name == 'nt':
|
|
4
|
+
gui_style = """
|
|
5
|
+
QWidget {
|
|
6
|
+
font: 10pt "Verdana";
|
|
7
|
+
background: #4b4e51;
|
|
8
|
+
color: #b5bab9;
|
|
9
|
+
}
|
|
10
|
+
QPushButton, QComboBox {
|
|
11
|
+
min-height: 30px;
|
|
12
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
13
|
+
border: 1px solid #282830;
|
|
14
|
+
border-radius: 5px;
|
|
15
|
+
color: #dddddd;
|
|
16
|
+
}
|
|
17
|
+
QPushButton:pressed {
|
|
18
|
+
border: 2px solid #282830;
|
|
19
|
+
}
|
|
20
|
+
QPushButton.info {
|
|
21
|
+
background-color: #4e6749;
|
|
22
|
+
}
|
|
23
|
+
QPushButton.info:hover {
|
|
24
|
+
background-color: #5f7850;
|
|
25
|
+
}
|
|
26
|
+
QPushButton.modify {
|
|
27
|
+
background-color: #98763f;
|
|
28
|
+
}
|
|
29
|
+
QPushButton.modify:hover {
|
|
30
|
+
background-color: #a9874f;
|
|
31
|
+
}
|
|
32
|
+
QPushButton.removal {
|
|
33
|
+
background-color: #692e2e;
|
|
34
|
+
}
|
|
35
|
+
QPushButton.removal:hover {
|
|
36
|
+
background-color: #7a3f3f;
|
|
37
|
+
}
|
|
38
|
+
QPushButton.save {
|
|
39
|
+
background-color: #564967;
|
|
40
|
+
}
|
|
41
|
+
QPushButton.save:hover {
|
|
42
|
+
background-color: #675a78;
|
|
43
|
+
}
|
|
44
|
+
QPushButton.quit {
|
|
45
|
+
background-color: #4571a4;
|
|
46
|
+
}
|
|
47
|
+
QPushButton.quit:hover {
|
|
48
|
+
background-color: #5682b5;
|
|
49
|
+
}
|
|
50
|
+
QPushButton::disabled {
|
|
51
|
+
background-color: gray;
|
|
52
|
+
}
|
|
53
|
+
QToolTip {
|
|
54
|
+
color: black
|
|
55
|
+
}
|
|
56
|
+
QLabel.dropped_fmu {
|
|
57
|
+
background-color: #b5bab9
|
|
58
|
+
}
|
|
59
|
+
QLabel.title {
|
|
60
|
+
font: 14pt bold "Verdana";
|
|
61
|
+
}
|
|
62
|
+
QLabel.dropped_fmu:hover {
|
|
63
|
+
background-color: #c6cbca
|
|
64
|
+
}
|
|
65
|
+
QTextBrowser, QTreeView {
|
|
66
|
+
font: 11pt "Consolas";
|
|
67
|
+
background-color: #282830;
|
|
68
|
+
color: #b5bab9;
|
|
69
|
+
}
|
|
70
|
+
QMenu::item {
|
|
71
|
+
padding: 2px 250px 2px 20px;
|
|
72
|
+
border: 1px solid transparent;
|
|
73
|
+
}
|
|
74
|
+
QMenu::item::indicator, QCheckBox::item::indicator {
|
|
75
|
+
width: 32px;
|
|
76
|
+
height: 32px;
|
|
77
|
+
}
|
|
78
|
+
QMenu::indicator:checked, QCheckBox::indicator:checked {
|
|
79
|
+
image: url(images:checkbox-checked.png);
|
|
80
|
+
}
|
|
81
|
+
QMenu::indicator:checked:hover, QCheckBox::indicator:checked:hover {
|
|
82
|
+
image: url(images:checkbox-checked-hover.png);
|
|
83
|
+
}
|
|
84
|
+
QMenu::indicator:checked:disabled, QCheckBox::indicator:checked:disabled {
|
|
85
|
+
image: url(images:checkbox-checked-disabled.png);
|
|
86
|
+
}
|
|
87
|
+
QMenu::indicator:unchecked, QCheckBox::indicator:unchecked {
|
|
88
|
+
image: url(images:checkbox-unchecked.png);
|
|
89
|
+
}
|
|
90
|
+
QMenu::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover {
|
|
91
|
+
image: url(images:checkbox-unchecked-hover.png);
|
|
92
|
+
}
|
|
93
|
+
QMenu::indicator:unchecked:disabled, QCheckBox::indicator:unchecked:disabled {
|
|
94
|
+
image: url(images:checkbox-unchecked-disabled.png);
|
|
95
|
+
}
|
|
96
|
+
QCheckBox::item {
|
|
97
|
+
padding: 2px 250px 2px 20px;
|
|
98
|
+
border: 1px solid transparent;
|
|
99
|
+
}
|
|
100
|
+
QTabBar::tab {
|
|
101
|
+
min-height: 30px;
|
|
102
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
103
|
+
color: #dddddd;
|
|
104
|
+
margin: 2px;
|
|
105
|
+
margin-bottom: 0px;
|
|
106
|
+
border: 1px solid #282830;
|
|
107
|
+
border-top-left-radius: 5px;
|
|
108
|
+
border-top-right-radius: 5px;
|
|
109
|
+
}
|
|
110
|
+
QTabBar::tab:selected, QTabBar::tab:hover {
|
|
111
|
+
background-color: #5f7850;
|
|
112
|
+
margin-bottom:-1px;
|
|
113
|
+
}
|
|
114
|
+
QTabBar {
|
|
115
|
+
border-bottom: 1px solid #282830;
|
|
116
|
+
}
|
|
117
|
+
QTabBar::tab:top:last, QTabBar::tab:bottom:last {
|
|
118
|
+
margin-right: 0;
|
|
119
|
+
}
|
|
120
|
+
QTabBar::tab:top:first, QTabBar::tab:bottom:first {
|
|
121
|
+
margin-left: 0;
|
|
122
|
+
}
|
|
123
|
+
"""
|
|
124
|
+
else:
|
|
125
|
+
gui_style = """
|
|
126
|
+
QWidget {
|
|
127
|
+
font: 12pt;
|
|
128
|
+
background: #4b4e51;
|
|
129
|
+
color: #b5bab9;
|
|
130
|
+
}
|
|
131
|
+
QPushButton, QComboBox {
|
|
132
|
+
min-height: 30px;
|
|
133
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
134
|
+
border: 1px solid #282830;
|
|
135
|
+
border-radius: 5px;
|
|
136
|
+
color: #dddddd;
|
|
137
|
+
}
|
|
138
|
+
QPushButton:pressed {
|
|
139
|
+
border: 2px solid #282830;
|
|
140
|
+
}
|
|
141
|
+
QPushButton.info {
|
|
142
|
+
background-color: #4e6749;
|
|
143
|
+
}
|
|
144
|
+
QPushButton.info:hover {
|
|
145
|
+
background-color: #5f7850;
|
|
146
|
+
}
|
|
147
|
+
QPushButton.modify {
|
|
148
|
+
background-color: #98763f;
|
|
149
|
+
}
|
|
150
|
+
QPushButton.modify:hover {
|
|
151
|
+
background-color: #a9874f;
|
|
152
|
+
}
|
|
153
|
+
QPushButton.removal {
|
|
154
|
+
background-color: #692e2e;
|
|
155
|
+
}
|
|
156
|
+
QPushButton.removal:hover {
|
|
157
|
+
background-color: #7a3f3f;
|
|
158
|
+
}
|
|
159
|
+
QPushButton.save {
|
|
160
|
+
background-color: #564967;
|
|
161
|
+
}
|
|
162
|
+
QPushButton.save:hover {
|
|
163
|
+
background-color: #675a78;
|
|
164
|
+
}
|
|
165
|
+
QPushButton.quit {
|
|
166
|
+
background-color: #4571a4;
|
|
167
|
+
}
|
|
168
|
+
QPushButton.quit:hover {
|
|
169
|
+
background-color: #5682b5;
|
|
170
|
+
}
|
|
171
|
+
QPushButton::disabled {
|
|
172
|
+
background-color: gray;
|
|
173
|
+
}
|
|
174
|
+
QToolTip {
|
|
175
|
+
color: black
|
|
176
|
+
}
|
|
177
|
+
QLabel.dropped_fmu {
|
|
178
|
+
background-color: #b5bab9
|
|
179
|
+
}
|
|
180
|
+
QLabel.title {
|
|
181
|
+
font: 14pt bold "Verdana";
|
|
182
|
+
}
|
|
183
|
+
QLabel.dropped_fmu:hover {
|
|
184
|
+
background-color: #c6cbca
|
|
185
|
+
}
|
|
186
|
+
QTextBrowser, QTreeView {
|
|
187
|
+
font: 14pt "Courier New";
|
|
188
|
+
background-color: #282830;
|
|
189
|
+
color: #b5bab9;
|
|
190
|
+
}
|
|
191
|
+
QMenu::item {
|
|
192
|
+
padding: 2px 250px 2px 20px;
|
|
193
|
+
border: 1px solid transparent;
|
|
194
|
+
}
|
|
195
|
+
QMenu::item::indicator, QCheckBox::item::indicator {
|
|
196
|
+
width: 32px;
|
|
197
|
+
height: 32px;
|
|
198
|
+
}
|
|
199
|
+
QMenu::indicator:checked, QCheckBox::indicator:checked {
|
|
200
|
+
image: url(images:checkbox-checked.png);
|
|
201
|
+
}
|
|
202
|
+
QMenu::indicator:checked:hover, QCheckBox::indicator:checked:hover {
|
|
203
|
+
image: url(images:checkbox-checked-hover.png);
|
|
204
|
+
}
|
|
205
|
+
QMenu::indicator:checked:disabled, QCheckBox::indicator:checked:disabled {
|
|
206
|
+
image: url(images:checkbox-checked-disabled.png);
|
|
207
|
+
}
|
|
208
|
+
QMenu::indicator:unchecked, QCheckBox::indicator:unchecked {
|
|
209
|
+
image: url(images:checkbox-unchecked.png);
|
|
210
|
+
}
|
|
211
|
+
QMenu::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover {
|
|
212
|
+
image: url(images:checkbox-unchecked-hover.png);
|
|
213
|
+
}
|
|
214
|
+
QMenu::indicator:unchecked:disabled, QCheckBox::indicator:unchecked:disabled {
|
|
215
|
+
image: url(images:checkbox-unchecked-disabled.png);
|
|
216
|
+
}
|
|
217
|
+
QCheckBox::item {
|
|
218
|
+
padding: 2px 250px 2px 20px;
|
|
219
|
+
border: 1px solid transparent;
|
|
220
|
+
}
|
|
221
|
+
QTabBar::tab {
|
|
222
|
+
min-height: 30px;
|
|
223
|
+
padding: 1px 1px 0.2em 0.2em;
|
|
224
|
+
color: #dddddd;
|
|
225
|
+
margin: 2px;
|
|
226
|
+
margin-bottom: 0px;
|
|
227
|
+
border: 1px solid #282830;
|
|
228
|
+
border-top-left-radius: 5px;
|
|
229
|
+
border-top-right-radius: 5px;
|
|
230
|
+
}
|
|
231
|
+
QTabBar::tab:selected, QTabBar::tab:hover {
|
|
232
|
+
background-color: #5f7850;
|
|
233
|
+
margin-bottom:-1px;
|
|
234
|
+
}
|
|
235
|
+
QTabBar {
|
|
236
|
+
border-bottom: 1px solid #282830;
|
|
237
|
+
}
|
|
238
|
+
QTabBar::tab:top:last, QTabBar::tab:bottom:last {
|
|
239
|
+
margin-right: 0;
|
|
240
|
+
}
|
|
241
|
+
QTabBar::tab:top:first, QTabBar::tab:bottom:first {
|
|
242
|
+
margin-left: 0;
|
|
243
|
+
}
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
log_color = {
|
|
247
|
+
"DEBUG": "#6E6B6B",
|
|
248
|
+
"INFO": "#b5bab9",
|
|
249
|
+
"WARNING": "#F7C61B",
|
|
250
|
+
"ERROR": "#F54927",
|
|
251
|
+
"CRITICAL": "#FF00FF",
|
|
252
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
{fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmu_manipulation_toolbox
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.2rc2
|
|
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
|
{fmu_manipulation_toolbox-1.9.2b4.dist-info → fmu_manipulation_toolbox-1.9.2rc2.dist-info}/RECORD
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
fmu_manipulation_toolbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
fmu_manipulation_toolbox/__main__.py,sha256=g0ZhVsMiAs5KnhyVNwTe01N2PQjAg7F9YCnXHZB-HwA,356
|
|
3
|
-
fmu_manipulation_toolbox/__version__.py,sha256=
|
|
3
|
+
fmu_manipulation_toolbox/__version__.py,sha256=6ttw5N6nrGXzLg1BgjLnWFMx2u8lpo92IH6dsBWPlJw,12
|
|
4
4
|
fmu_manipulation_toolbox/assembly.py,sha256=QmFihza_I0hK0Ia6tWbjdZeoREhveLrv3d7ERrBUaDk,27217
|
|
5
5
|
fmu_manipulation_toolbox/checker.py,sha256=Dh47b3blibCWjHCeZ61Y_w9Ug0PWvEuSiemZtp-llQA,3141
|
|
6
|
-
fmu_manipulation_toolbox/container.py,sha256=
|
|
6
|
+
fmu_manipulation_toolbox/container.py,sha256=yME00RUudrRIzc6ZSguBFzyd9hkp6eFIdp91QWaOcVo,58561
|
|
7
7
|
fmu_manipulation_toolbox/help.py,sha256=j8xmnCrwQpaW-SZ8hSqA1dlTXgaqzQWc4Yr3RH_oqck,6012
|
|
8
8
|
fmu_manipulation_toolbox/ls.py,sha256=wmyoKrvDLXpL-PFz6cUhLLqxDMD5E9L_P4KswWpQHsk,975
|
|
9
9
|
fmu_manipulation_toolbox/operations.py,sha256=y3QDMiLaG1TUhIQ4r_onWp1kGm8QZaFfTOdQr0oC7_A,21177
|
|
@@ -12,11 +12,14 @@ fmu_manipulation_toolbox/split.py,sha256=6D99SAGNu4B3PSaSsliWc6Bb5aSBZMxL8t0v8TJ
|
|
|
12
12
|
fmu_manipulation_toolbox/terminals.py,sha256=mGGS4tdE6cJuz-2zvwc7drpmT0QJ7YPe8ENw2UGlEHA,5062
|
|
13
13
|
fmu_manipulation_toolbox/version.py,sha256=L26Cc3PH97SOa4G9yiYnafrdolK0G_DCQZZTvv3YXqI,392
|
|
14
14
|
fmu_manipulation_toolbox/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
fmu_manipulation_toolbox/cli/datalog2pcap.py,sha256=
|
|
15
|
+
fmu_manipulation_toolbox/cli/datalog2pcap.py,sha256=gdqScDLNu9jxl_bAld7Qf9GEEs8XePcIpkVJ4p87hlM,7689
|
|
16
16
|
fmu_manipulation_toolbox/cli/fmucontainer.py,sha256=ZLoC8QMVBVG2S3cdNthMIQ0gYIU9WUbYj8Aj2vxzyfs,5338
|
|
17
17
|
fmu_manipulation_toolbox/cli/fmusplit.py,sha256=sLzdxiC4R5hJYJo9F2TZOMrJOHcadqCvUo9KoCjUaxE,1773
|
|
18
18
|
fmu_manipulation_toolbox/cli/fmutool.py,sha256=E-CCymksBwGFlS3-zJ7DIDA8xIdp_synsxMDYXst8dc,6186
|
|
19
19
|
fmu_manipulation_toolbox/cli/utils.py,sha256=pvedN6cRiDkZ7RdPO1koBOZaGuydYhgcoO0-_QQpOoI,1601
|
|
20
|
+
fmu_manipulation_toolbox/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
fmu_manipulation_toolbox/gui/gui.py,sha256=usnMIyMeBcn81oYOBufkosCVBj0JRmKoaTUYG3e9o48,29430
|
|
22
|
+
fmu_manipulation_toolbox/gui/gui_style.py,sha256=s6WdrnNd_lCMWhuBf5LKK8wrfLXCU7pFTLUfvqkJVno,6633
|
|
20
23
|
fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png,sha256=FWIuyrXlaNLLePHfXj7j9ca5rT8Hgr14KCe1EqTCZyk,2288
|
|
21
24
|
fmu_manipulation_toolbox/resources/checkbox-checked-hover.png,sha256=KNlV-d_aJNTTvUVXKGT9DBY30sIs2LwocLJrFKNKv8k,2419
|
|
22
25
|
fmu_manipulation_toolbox/resources/checkbox-checked.png,sha256=gzyFqvRFsZixVh6ZlV4SMWUKzglY1rSn7SvJUKMVvtk,2411
|
|
@@ -34,7 +37,7 @@ fmu_manipulation_toolbox/resources/icon_fmu.png,sha256=EuygB2xcoM2WAfKKdyKG_UvTL
|
|
|
34
37
|
fmu_manipulation_toolbox/resources/license.txt,sha256=5ODuU8g8pIkK-NMWXu_rjZ6k7gM7b-N2rmg87-2Kmqw,1583
|
|
35
38
|
fmu_manipulation_toolbox/resources/mask.png,sha256=px1U4hQGL0AmZ4BQPknOVREpMpTSejbah3ntkpqAzFA,3008
|
|
36
39
|
fmu_manipulation_toolbox/resources/model.png,sha256=EAf_HnZJe8zYGZygerG1MMt2U-tMMZlifzXPj4_iORA,208788
|
|
37
|
-
fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=
|
|
40
|
+
fmu_manipulation_toolbox/resources/darwin64/container.dylib,sha256=0O1swE0kqgk3mK0sAH8_n1fZfzL9Ovb9dQ_lXlnkWak,230704
|
|
38
41
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd,sha256=OGfyJtaJntKypX5KDpuZ-nV1oYLZ6HV16pkpKOmYox4,2731
|
|
39
42
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd,sha256=HwyV7LBse-PQSv4z1xjmtzPU3Hjnv4mluq9YdSBNHMQ,3704
|
|
40
43
|
fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ModelDescription.xsd,sha256=JM4j_9q-pc40XYHb28jfT3iV3aYM5JLqD5aRjO72K1E,18939
|
|
@@ -54,19 +57,19 @@ fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Type.xsd,sha256=TaHRoUBIFtmdEwBKB
|
|
|
54
57
|
fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Unit.xsd,sha256=CK_F2t5LfyQ6eSNJ8soTFMVK9DU8vD2WiMi2MQvjB0g,3746
|
|
55
58
|
fmu_manipulation_toolbox/resources/fmi-3.0/fmi3Variable.xsd,sha256=3YU-3q1-c-namz7sMe5cxnmOVOJsRSmfWR02PKv3xaU,19171
|
|
56
59
|
fmu_manipulation_toolbox/resources/fmi-3.0/fmi3VariableDependency.xsd,sha256=YQSBwXt4IsDlyegY8bX-qQHGSfE5TipTPfo2g2yqq1c,3082
|
|
57
|
-
fmu_manipulation_toolbox/resources/linux32/client_sm.so,sha256=
|
|
60
|
+
fmu_manipulation_toolbox/resources/linux32/client_sm.so,sha256=LnxxVwaZJpheBY9_OwCNWIiT7xokFGd7WBwqxfZwg50,34664
|
|
58
61
|
fmu_manipulation_toolbox/resources/linux32/server_sm,sha256=gzKU0BTeaRkvhTMQtHHj3K8uYFyEdyGGn_mZy_jG9xo,21304
|
|
59
|
-
fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=
|
|
60
|
-
fmu_manipulation_toolbox/resources/linux64/container.so,sha256=
|
|
62
|
+
fmu_manipulation_toolbox/resources/linux64/client_sm.so,sha256=yP3h3mfT_Jzy0j-CN0POSlcGy8tMR13ca6g0K4FZWMk,32464
|
|
63
|
+
fmu_manipulation_toolbox/resources/linux64/container.so,sha256=QKrG1i2AKH3DPqWoSY7Rdy-xnISy2ohnKAcnip83I2c,194064
|
|
61
64
|
fmu_manipulation_toolbox/resources/linux64/server_sm,sha256=MZn6vITN2qpBHYt_RaK2VnFFp00hk8fTALBHmXPtLwc,22608
|
|
62
|
-
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=
|
|
63
|
-
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=
|
|
64
|
-
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=
|
|
65
|
-
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=
|
|
66
|
-
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=
|
|
67
|
-
fmu_manipulation_toolbox-1.9.
|
|
68
|
-
fmu_manipulation_toolbox-1.9.
|
|
69
|
-
fmu_manipulation_toolbox-1.9.
|
|
70
|
-
fmu_manipulation_toolbox-1.9.
|
|
71
|
-
fmu_manipulation_toolbox-1.9.
|
|
72
|
-
fmu_manipulation_toolbox-1.9.
|
|
65
|
+
fmu_manipulation_toolbox/resources/win32/client_sm.dll,sha256=GzLBrNR2RT-UGo8rfJcIXyRxv9-UreKG1MPy0Rab_8M,17920
|
|
66
|
+
fmu_manipulation_toolbox/resources/win32/server_sm.exe,sha256=SXDSyWBZteYJxC4icno0uXI9MYzD-EafYwA4cEhrXno,15360
|
|
67
|
+
fmu_manipulation_toolbox/resources/win64/client_sm.dll,sha256=Tv2F_mBH56jREbcH8uPczTp3O3EU7cIlCrbOEp2mS-g,20992
|
|
68
|
+
fmu_manipulation_toolbox/resources/win64/container.dll,sha256=jub3YR6C-4h7kUKojVcSWspIW9B8yAVU30SwZYQ0lYQ,153088
|
|
69
|
+
fmu_manipulation_toolbox/resources/win64/server_sm.exe,sha256=91n336TCk2_0ODI7NGUcAr7TVccJyxnDmk3iUZ0ijxY,18432
|
|
70
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/licenses/LICENSE.txt,sha256=0Q8zhEwTu1K-MDmg8Khay5j56BIz2VLI6RcijIFbU_g,1255
|
|
71
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/METADATA,sha256=K_ovw0Ra0TCFf99xxFANilNn47FAt5J2rK1I6P0e-yE,1900
|
|
72
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
73
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/entry_points.txt,sha256=VOWf1jbG1O-2JrqXOvSoHRRfdH3RNONvz-2RIPHNX0s,338
|
|
74
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/top_level.txt,sha256=9D_h-5BMjSqf9z-XFkbJL_bMppR2XNYW3WNuPkXou0k,25
|
|
75
|
+
fmu_manipulation_toolbox-1.9.2rc2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|