BEsim logger
This commit is contained in:
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -5,7 +5,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Python Debugger: Module",
|
"name": "BEdit debug",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "bedit_gui",
|
"module": "bedit_gui",
|
||||||
@@ -18,6 +18,22 @@
|
|||||||
"args": [
|
"args": [
|
||||||
"-f", "${workspaceFolder}/untitled.bedit.json"
|
"-f", "${workspaceFolder}/untitled.bedit.json"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BEsim debug",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "bedit_gui.simulation_application",
|
||||||
|
"preLaunchTask": "Qt: Generate files",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"env": {
|
||||||
|
"QT_QPA_PLATFORMTHEME": "qt6ct",
|
||||||
|
"QT_QPA_PLATFORM": "xcb"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"-s", "run_bondgraph",
|
||||||
|
"${workspaceFolder}/untitled.besim.json"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ from PySide6.QtCore import QObject, Signal
|
|||||||
|
|
||||||
from bedit_gui.services.application_logging import configure_logging
|
from bedit_gui.services.application_logging import configure_logging
|
||||||
from bedit_gui.views.main_window import MainWindow
|
from bedit_gui.views.main_window import MainWindow
|
||||||
|
from bedit_gui.views.simulation_window import SimulationWindow
|
||||||
from bedit_gui.views.models import LogListModel
|
from bedit_gui.views.models import LogListModel
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ class LogController(QObject):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
window: MainWindow,
|
window: MainWindow | SimulationWindow,
|
||||||
level: int | str = logging.INFO,
|
level: int | str = logging.INFO,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(window)
|
super().__init__(window)
|
||||||
|
|||||||
@@ -3,13 +3,18 @@ from __future__ import annotations
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from PySide6.QtCore import QObject, Qt
|
from PySide6.QtCore import QObject, Qt
|
||||||
from PySide6.QtWidgets import QApplication, QFileDialog, QInputDialog, QMessageBox
|
from PySide6.QtWidgets import QApplication, QDialog, QFileDialog, QInputDialog, QMessageBox
|
||||||
|
|
||||||
from bedit_gui.services import document_files, simulation_files
|
from bedit_gui.services import document_files, simulation_files
|
||||||
from bedit_gui.services.simulation_loader import compile_simulation_root, component_choices, load_and_compile_bedit
|
from bedit_gui.services.simulation_loader import compile_simulation_root, component_choices, load_and_compile_bedit
|
||||||
|
from bedit_gui.models import SimulationDatabase, SimulationID
|
||||||
|
from bedit_gui.services.application_logging import get_logger
|
||||||
from bedit_gui.simulation_models import CompiledModel, SimulationRoot
|
from bedit_gui.simulation_models import CompiledModel, SimulationRoot
|
||||||
|
from bedit_gui.views.dialogs.simulation_settings_dialog import SimulationSettingsDialog
|
||||||
from bedit_gui.views.simulation_window import SimulationWindow
|
from bedit_gui.views.simulation_window import SimulationWindow
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SimulationFileController(QObject):
|
class SimulationFileController(QObject):
|
||||||
def __init__(self, window: SimulationWindow) -> None:
|
def __init__(self, window: SimulationWindow) -> None:
|
||||||
@@ -21,6 +26,7 @@ class SimulationFileController(QObject):
|
|||||||
window.ui.actionNew_Simulation_Run.triggered.connect(self.new)
|
window.ui.actionNew_Simulation_Run.triggered.connect(self.new)
|
||||||
window.ui.actionOpen_Simulation_Run.triggered.connect(self.open_dialog)
|
window.ui.actionOpen_Simulation_Run.triggered.connect(self.open_dialog)
|
||||||
window.ui.actionSave_Simulation_Run.triggered.connect(self.save)
|
window.ui.actionSave_Simulation_Run.triggered.connect(self.save)
|
||||||
|
window.ui.actionSimulation_Options.triggered.connect(self.open_simulation_settings)
|
||||||
self._update_window()
|
self._update_window()
|
||||||
|
|
||||||
def new(self) -> None:
|
def new(self) -> None:
|
||||||
@@ -28,17 +34,23 @@ class SimulationFileController(QObject):
|
|||||||
self.compiled_model = None
|
self.compiled_model = None
|
||||||
self.path = None
|
self.path = None
|
||||||
self._update_window()
|
self._update_window()
|
||||||
|
logger.info("Created new simulation")
|
||||||
|
|
||||||
def open(self, path: str | Path, *, component: str | None = None, simulation: str | None = None, backed_by_file: bool = True, compiled_model: CompiledModel | None = None) -> None:
|
def open(self, path: str | Path, *, component: str | None = None, simulation: str | None = None, backed_by_file: bool = True, compiled_model: CompiledModel | None = None) -> None:
|
||||||
file_path = Path(path)
|
file_path = Path(path)
|
||||||
if file_path.suffix.lower() == ".bes" or simulation_files.is_simulation_json(file_path):
|
try:
|
||||||
self.root = simulation_files.load(file_path)
|
if file_path.suffix.lower() == ".bes" or simulation_files.is_simulation_json(file_path):
|
||||||
self.compiled_model = compiled_model or self._recompile_with_wait_cursor(self.root)
|
self.root = simulation_files.load(file_path)
|
||||||
self.path = file_path if backed_by_file else None
|
self.compiled_model = compiled_model or self._recompile_with_wait_cursor(self.root)
|
||||||
else:
|
self.path = file_path if backed_by_file else None
|
||||||
self.root, self.compiled_model = self._compile_with_wait_cursor(file_path, component=component, simulation=simulation)
|
else:
|
||||||
self.path = None
|
self.root, self.compiled_model = self._compile_with_wait_cursor(file_path, component=component, simulation=simulation)
|
||||||
|
self.path = None
|
||||||
|
except (OSError, RuntimeError, TypeError, ValueError):
|
||||||
|
logger.exception("Could not open simulation %s", file_path)
|
||||||
|
raise
|
||||||
self._update_window()
|
self._update_window()
|
||||||
|
logger.info("Opened simulation: %s", file_path)
|
||||||
|
|
||||||
def open_dialog(self) -> None:
|
def open_dialog(self) -> None:
|
||||||
filename, _ = QFileDialog.getOpenFileName(self.window, "Open Simulation", "", "Simulation and BEdit files (*.bes *.beb *.json)")
|
filename, _ = QFileDialog.getOpenFileName(self.window, "Open Simulation", "", "Simulation and BEdit files (*.bes *.beb *.json)")
|
||||||
@@ -75,11 +87,43 @@ class SimulationFileController(QObject):
|
|||||||
path = path.with_suffix(".bes")
|
path = path.with_suffix(".bes")
|
||||||
try:
|
try:
|
||||||
simulation_files.save(self.root, path)
|
simulation_files.save(self.root, path)
|
||||||
except (OSError, ValueError) as exc:
|
except (OSError, TypeError, ValueError) as exc:
|
||||||
|
logger.exception("Could not save simulation %s", path)
|
||||||
QMessageBox.critical(self.window, "Could not save simulation", str(exc))
|
QMessageBox.critical(self.window, "Could not save simulation", str(exc))
|
||||||
return
|
return
|
||||||
self.path = path
|
self.path = path
|
||||||
self._update_window()
|
self._update_window()
|
||||||
|
logger.info("Saved simulation: %s", path)
|
||||||
|
|
||||||
|
def open_simulation_settings(self) -> None:
|
||||||
|
if self.root is None:
|
||||||
|
return
|
||||||
|
simulation_id = SimulationID()
|
||||||
|
database = SimulationDatabase(simulations={simulation_id: self.root.settings}, active_simulation=simulation_id)
|
||||||
|
components = self._source_components()
|
||||||
|
dialog = SimulationSettingsDialog(database, [(component_id, path) for component_id, _component, path in components], self.window, show_simulation_list=False)
|
||||||
|
if dialog.exec() != QDialog.DialogCode.Accepted:
|
||||||
|
return
|
||||||
|
updated = dialog.database().simulations[simulation_id]
|
||||||
|
component_changed = updated.component != self.root.component
|
||||||
|
self.root.settings = updated
|
||||||
|
self.root.component = updated.component
|
||||||
|
self.root.component_path = next((path for component_id, _component, path in components if component_id == updated.component), self.root.component_path)
|
||||||
|
self.root.settings_name = updated.name
|
||||||
|
if component_changed:
|
||||||
|
self.compiled_model = None
|
||||||
|
self._update_window()
|
||||||
|
logger.info("Updated simulation settings: %s", updated.name)
|
||||||
|
|
||||||
|
def _source_components(self) -> list[tuple]:
|
||||||
|
if self.root is not None and self.root.source_document is not None:
|
||||||
|
try:
|
||||||
|
return component_choices(document_files.load(self.root.source_document))
|
||||||
|
except (OSError, TypeError, ValueError):
|
||||||
|
logger.warning("Could not load source components from %s", self.root.source_document, exc_info=True)
|
||||||
|
if self.root is None:
|
||||||
|
return []
|
||||||
|
return [(self.root.component, None, self.root.component_path)]
|
||||||
|
|
||||||
def _compile_with_wait_cursor(self, path: Path, *, component: str | None, simulation: str | None) -> tuple[SimulationRoot, CompiledModel]:
|
def _compile_with_wait_cursor(self, path: Path, *, component: str | None, simulation: str | None) -> tuple[SimulationRoot, CompiledModel]:
|
||||||
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
|
QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
|
||||||
@@ -100,8 +144,10 @@ class SimulationFileController(QObject):
|
|||||||
self.window.setWindowTitle("BEdit Simulator")
|
self.window.setWindowTitle("BEdit Simulator")
|
||||||
self.window.statusBar().showMessage("No simulation loaded")
|
self.window.statusBar().showMessage("No simulation loaded")
|
||||||
self.window.ui.actionSave_Simulation_Run.setEnabled(False)
|
self.window.ui.actionSave_Simulation_Run.setEnabled(False)
|
||||||
|
self.window.ui.actionSimulation_Options.setEnabled(False)
|
||||||
return
|
return
|
||||||
self.window.setWindowTitle(f"{self.root.settings.name} — BEdit Simulator")
|
self.window.setWindowTitle(f"{self.root.settings.name} — BEdit Simulator")
|
||||||
compiled = self.compiled_model.executable if self.compiled_model is not None else "not compiled"
|
compiled = self.compiled_model.executable if self.compiled_model is not None else "not compiled"
|
||||||
self.window.statusBar().showMessage(f"{self.root.component_path} · {compiled}")
|
self.window.statusBar().showMessage(f"{self.root.component_path} · {compiled}")
|
||||||
self.window.ui.actionSave_Simulation_Run.setEnabled(True)
|
self.window.ui.actionSave_Simulation_Run.setEnabled(True)
|
||||||
|
self.window.ui.actionSimulation_Options.setEnabled(True)
|
||||||
|
|||||||
@@ -25,3 +25,24 @@ class ApplicationSettings:
|
|||||||
@log_level.setter
|
@log_level.setter
|
||||||
def log_level(self, level: int) -> None:
|
def log_level(self, level: int) -> None:
|
||||||
self._settings.setValue(self.LOG_LEVEL_KEY, level)
|
self._settings.setValue(self.LOG_LEVEL_KEY, level)
|
||||||
|
|
||||||
|
class SimulationApplicationSettings:
|
||||||
|
"""Typed access to persistent BEsim application settings."""
|
||||||
|
|
||||||
|
LOG_LEVEL_KEY = "logging/level"
|
||||||
|
DEFAULT_LOG_LEVEL = logging.INFO
|
||||||
|
|
||||||
|
def __init__(self, settings: QSettings | None = None) -> None:
|
||||||
|
self._settings = settings if settings is not None else QSettings()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log_level(self) -> int:
|
||||||
|
return self._settings.value(
|
||||||
|
self.LOG_LEVEL_KEY,
|
||||||
|
self.DEFAULT_LOG_LEVEL,
|
||||||
|
type=int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@log_level.setter
|
||||||
|
def log_level(self, level: int) -> None:
|
||||||
|
self._settings.setValue(self.LOG_LEVEL_KEY, level)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ def _load_bes(path: Path) -> Mapping[str, Any]:
|
|||||||
if version != FILE_FORMAT_VERSION:
|
if version != FILE_FORMAT_VERSION:
|
||||||
raise ValueError(f"unsupported BES file version {version}")
|
raise ValueError(f"unsupported BES file version {version}")
|
||||||
data = msgpack.unpackb(zlib.decompress(payload[header_end:]), raw=False, strict_map_key=False)
|
data = msgpack.unpackb(zlib.decompress(payload[header_end:]), raw=False, strict_map_key=False)
|
||||||
except (OSError, ValueError, zlib.error, msgpack.exceptions.MsgpackException) as exc:
|
except (OSError, ValueError, zlib.error, msgpack.exceptions.UnpackException) as exc:
|
||||||
raise ValueError(f"could not read BES simulation {path}: {exc}") from exc
|
raise ValueError(f"could not read BES simulation {path}: {exc}") from exc
|
||||||
if not isinstance(data, Mapping):
|
if not isinstance(data, Mapping):
|
||||||
raise TypeError(f"BES simulation {path} must contain a map")
|
raise TypeError(f"BES simulation {path} must contain a map")
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import sys
|
|||||||
from PySide6.QtWidgets import QApplication, QMessageBox
|
from PySide6.QtWidgets import QApplication, QMessageBox
|
||||||
|
|
||||||
from bedit_gui.controllers.simulation_file_controller import SimulationFileController
|
from bedit_gui.controllers.simulation_file_controller import SimulationFileController
|
||||||
|
from bedit_gui.controllers.log_controller import LogController
|
||||||
from bedit_gui.simulation_models import CompiledModel
|
from bedit_gui.simulation_models import CompiledModel
|
||||||
|
from bedit_gui.services.application_settings import SimulationApplicationSettings
|
||||||
from bedit_gui.views.simulation_window import SimulationWindow
|
from bedit_gui.views.simulation_window import SimulationWindow
|
||||||
|
|
||||||
|
|
||||||
@@ -31,11 +33,18 @@ def parse_arguments(arguments: list[str] | None = None) -> argparse.Namespace:
|
|||||||
def main(arguments: list[str] | None = None) -> int:
|
def main(arguments: list[str] | None = None) -> int:
|
||||||
args = parse_arguments(arguments)
|
args = parse_arguments(arguments)
|
||||||
app = QApplication(sys.argv if arguments is None else [sys.argv[0], *arguments])
|
app = QApplication(sys.argv if arguments is None else [sys.argv[0], *arguments])
|
||||||
app.setOrganizationName("BEdit")
|
|
||||||
app.setApplicationName("BEdit Simulator")
|
app.setOrganizationName("BEsim")
|
||||||
|
app.setApplicationName("BEsim")
|
||||||
|
|
||||||
window = SimulationWindow()
|
window = SimulationWindow()
|
||||||
controller = SimulationFileController(window)
|
controller = SimulationFileController(window)
|
||||||
|
|
||||||
|
settings = SimulationApplicationSettings()
|
||||||
|
LogController(window, settings.log_level)
|
||||||
|
|
||||||
window.showMaximized()
|
window.showMaximized()
|
||||||
|
|
||||||
if args.file:
|
if args.file:
|
||||||
try:
|
try:
|
||||||
compiled_model = CompiledModel(args.model_name, args.executable, args.working_directory) if args.handoff else None
|
compiled_model = CompiledModel(args.model_name, args.executable, args.working_directory) if args.handoff else None
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
<widget class="QWidget" name="dockWidgetContents">
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="logList"/>
|
<widget class="QListView" name="listView"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from copy import deepcopy
|
|||||||
|
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtGui import QDoubleValidator
|
from PySide6.QtGui import QDoubleValidator
|
||||||
from PySide6.QtWidgets import QDialog, QListWidgetItem, QMessageBox, QWidget
|
from PySide6.QtWidgets import QDialog, QLayout, QListWidgetItem, QMessageBox, QWidget
|
||||||
|
|
||||||
from bedit_core.models import ComponentID
|
from bedit_core.models import ComponentID
|
||||||
from bedit_gui.models import Simulation, SimulationDatabase, SimulationID, SimulationMethod
|
from bedit_gui.models import Simulation, SimulationDatabase, SimulationID, SimulationMethod
|
||||||
@@ -14,7 +14,7 @@ from bedit_gui.ui.generated.ui_simulation_settings import Ui_Dialog
|
|||||||
class SimulationSettingsDialog(QDialog):
|
class SimulationSettingsDialog(QDialog):
|
||||||
"""Editor for the detached simulation database of a document."""
|
"""Editor for the detached simulation database of a document."""
|
||||||
|
|
||||||
def __init__(self, database: SimulationDatabase, components: list[tuple[ComponentID, str]], parent: QWidget | None = None) -> None:
|
def __init__(self, database: SimulationDatabase, components: list[tuple[ComponentID, str]], parent: QWidget | None = None, *, show_simulation_list: bool = True) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.ui = Ui_Dialog()
|
self.ui = Ui_Dialog()
|
||||||
@@ -25,6 +25,9 @@ class SimulationSettingsDialog(QDialog):
|
|||||||
self._components = components
|
self._components = components
|
||||||
self._loading = False
|
self._loading = False
|
||||||
|
|
||||||
|
if not show_simulation_list:
|
||||||
|
self._set_layout_visible(self.ui.simulationListLayout, False)
|
||||||
|
|
||||||
self.ui.startTimeSpinBox.setRange(-1e12, 1e12)
|
self.ui.startTimeSpinBox.setRange(-1e12, 1e12)
|
||||||
self.ui.simLengthSpinBox.setRange(0.0, 1e12)
|
self.ui.simLengthSpinBox.setRange(0.0, 1e12)
|
||||||
self.ui.stepSizeSpinBox.setRange(1e-9, 1e12)
|
self.ui.stepSizeSpinBox.setRange(1e-9, 1e12)
|
||||||
@@ -196,3 +199,14 @@ class SimulationSettingsDialog(QDialog):
|
|||||||
self.ui.frame.setEnabled(enabled)
|
self.ui.frame.setEnabled(enabled)
|
||||||
self.ui.removeSimulationButton.setEnabled(enabled)
|
self.ui.removeSimulationButton.setEnabled(enabled)
|
||||||
self.ui.addSimulationButton.setEnabled(bool(self._components))
|
self.ui.addSimulationButton.setEnabled(bool(self._components))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _set_layout_visible(cls, layout: QLayout, visible: bool) -> None:
|
||||||
|
for index in range(layout.count()):
|
||||||
|
item = layout.itemAt(index)
|
||||||
|
widget = item.widget()
|
||||||
|
child_layout = item.layout()
|
||||||
|
if widget is not None:
|
||||||
|
widget.setVisible(visible)
|
||||||
|
elif child_layout is not None:
|
||||||
|
cls._set_layout_visible(child_layout, visible)
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ class SimulationWindow(QMainWindow):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.ui = Ui_SimulationWindow()
|
self.ui = Ui_SimulationWindow()
|
||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
self.setWindowTitle("BEdit Simulator")
|
self.setWindowTitle("BEsim")
|
||||||
|
|||||||
Reference in New Issue
Block a user