169 lines
8.1 KiB
Python
169 lines
8.1 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
from bedit_core.models import ComponentID
|
|
from bedit_gui.models import Simulation
|
|
from bedit_simulation import SimulationResult
|
|
|
|
|
|
@dataclass
|
|
class CompiledModel:
|
|
model_name: str
|
|
executable: str
|
|
working_directory: str
|
|
output: str = ""
|
|
errors: str = ""
|
|
|
|
@classmethod
|
|
def from_data(cls, data: Mapping[str, Any]) -> CompiledModel:
|
|
return cls(model_name=str(data["model_name"]), executable=str(data["executable"]), working_directory=str(data["working_directory"]), output=str(data.get("output", "")), errors=str(data.get("errors", "")))
|
|
|
|
def to_data(self) -> dict[str, Any]:
|
|
return {"model_name": self.model_name, "executable": self.executable, "working_directory": self.working_directory, "output": self.output, "errors": self.errors}
|
|
|
|
|
|
@dataclass
|
|
class SimulationTraceSettings:
|
|
visible: bool = True
|
|
label: str = ""
|
|
color: str = ""
|
|
line_style: str = "-"
|
|
line_width: float = 1.5
|
|
marker: str = ""
|
|
marker_size: float = 6.0
|
|
|
|
@classmethod
|
|
def from_data(cls, data: Mapping[str, Any]) -> SimulationTraceSettings:
|
|
return cls(visible=bool(data.get("visible", True)), label=str(data.get("label", "")), color=str(data.get("color", "")), line_style=str(data.get("line_style", "-")), line_width=float(data.get("line_width", 1.5)), marker=str(data.get("marker", "")), marker_size=float(data.get("marker_size", 6.0)))
|
|
|
|
def to_data(self) -> dict[str, Any]:
|
|
return {"visible": self.visible, "label": self.label, "color": self.color, "line_style": self.line_style, "line_width": self.line_width, "marker": self.marker, "marker_size": self.marker_size}
|
|
|
|
|
|
@dataclass
|
|
class SimulationPlotSettings:
|
|
title: str = ""
|
|
x_label: str = ""
|
|
y_label: str = ""
|
|
x_scale: str = "linear"
|
|
y_scale: str = "linear"
|
|
x_auto: bool = True
|
|
y_auto: bool = True
|
|
x_min: float = 0.0
|
|
x_max: float = 1.0
|
|
y_min: float = 0.0
|
|
y_max: float = 1.0
|
|
grid_visible: bool = True
|
|
grid_axis: str = "both"
|
|
grid_style: str = "-"
|
|
grid_alpha: float = 0.5
|
|
legend_visible: bool = True
|
|
legend_location: str = "best"
|
|
traces: dict[str, SimulationTraceSettings] = field(default_factory=dict)
|
|
|
|
@classmethod
|
|
def from_data(cls, data: Mapping[str, Any]) -> SimulationPlotSettings:
|
|
raw_traces = data.get("traces", {})
|
|
if not isinstance(raw_traces, Mapping):
|
|
raise TypeError("plot trace settings must be a mapping")
|
|
return cls(
|
|
title=str(data.get("title", "")), x_label=str(data.get("x_label", "")), y_label=str(data.get("y_label", "")),
|
|
x_scale=str(data.get("x_scale", "linear")), y_scale=str(data.get("y_scale", "linear")),
|
|
x_auto=bool(data.get("x_auto", True)), y_auto=bool(data.get("y_auto", True)),
|
|
x_min=float(data.get("x_min", 0.0)), x_max=float(data.get("x_max", 1.0)), y_min=float(data.get("y_min", 0.0)), y_max=float(data.get("y_max", 1.0)),
|
|
grid_visible=bool(data.get("grid_visible", True)), grid_axis=str(data.get("grid_axis", "both")), grid_style=str(data.get("grid_style", "-")), grid_alpha=float(data.get("grid_alpha", 0.5)),
|
|
legend_visible=bool(data.get("legend_visible", True)), legend_location=str(data.get("legend_location", "best")),
|
|
traces={str(signal): SimulationTraceSettings.from_data(trace) for signal, trace in raw_traces.items() if isinstance(trace, Mapping)},
|
|
)
|
|
|
|
def to_data(self) -> dict[str, Any]:
|
|
return {
|
|
"title": self.title, "x_label": self.x_label, "y_label": self.y_label, "x_scale": self.x_scale, "y_scale": self.y_scale,
|
|
"x_auto": self.x_auto, "y_auto": self.y_auto, "x_min": self.x_min, "x_max": self.x_max, "y_min": self.y_min, "y_max": self.y_max,
|
|
"grid_visible": self.grid_visible, "grid_axis": self.grid_axis, "grid_style": self.grid_style, "grid_alpha": self.grid_alpha,
|
|
"legend_visible": self.legend_visible, "legend_location": self.legend_location,
|
|
"traces": {signal: trace.to_data() for signal, trace in self.traces.items()},
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class SimulationPlotTab:
|
|
name: str
|
|
signals: list[str] = field(default_factory=list)
|
|
x_axis: str | None = None
|
|
settings: SimulationPlotSettings = field(default_factory=SimulationPlotSettings)
|
|
|
|
@classmethod
|
|
def from_data(cls, data: Mapping[str, Any]) -> SimulationPlotTab:
|
|
raw_signals = data.get("signals", [])
|
|
if not isinstance(raw_signals, list):
|
|
raise TypeError("plot tab signals must be a list")
|
|
raw_settings = data.get("settings", {})
|
|
if not isinstance(raw_settings, Mapping):
|
|
raise TypeError("plot tab settings must be a mapping")
|
|
return cls(name=str(data.get("name", "Plot")), signals=[str(signal) for signal in raw_signals], x_axis=str(data["x_axis"]) if data.get("x_axis") is not None else None, settings=SimulationPlotSettings.from_data(raw_settings))
|
|
|
|
def to_data(self) -> dict[str, Any]:
|
|
return {"name": self.name, "signals": self.signals, "x_axis": self.x_axis, "settings": self.settings.to_data()}
|
|
|
|
|
|
@dataclass
|
|
class SimulationRoot:
|
|
format_version: int
|
|
source_document: str | None
|
|
source_document_id: str | None
|
|
component: ComponentID
|
|
component_path: str
|
|
settings_name: str | None
|
|
settings: Simulation
|
|
current_end_time: float | None = None
|
|
results: list[SimulationResult] = field(default_factory=list)
|
|
plot_tabs: list[SimulationPlotTab] = field(default_factory=lambda: [SimulationPlotTab("Plot 1")])
|
|
|
|
@classmethod
|
|
def from_data(cls, data: Mapping[str, Any]) -> SimulationRoot:
|
|
if data.get("root_type") != "simulation_root":
|
|
raise ValueError("file does not contain a simulation root")
|
|
return cls(
|
|
format_version=int(data.get("format_version", 1)),
|
|
source_document=str(data["source_document"]) if data.get("source_document") is not None else None,
|
|
source_document_id=str(data["source_document_id"]) if data.get("source_document_id") is not None else None,
|
|
component=ComponentID(str(data["component"])),
|
|
component_path=str(data.get("component_path", "")),
|
|
settings_name=str(data["settings_name"]) if data.get("settings_name") is not None else None,
|
|
settings=Simulation.from_data(data["settings"]),
|
|
current_end_time=float(data["current_end_time"]) if data.get("current_end_time") is not None else None,
|
|
results=[_result_from_data(result) for result in data.get("results", [])],
|
|
plot_tabs=[SimulationPlotTab.from_data(tab) for tab in data["plot_tabs"]] if "plot_tabs" in data else [SimulationPlotTab("Plot 1")],
|
|
)
|
|
|
|
def to_data(self) -> dict[str, Any]:
|
|
return {
|
|
"root_type": "simulation_root",
|
|
"format_version": self.format_version,
|
|
"source_document": self.source_document,
|
|
"source_document_id": self.source_document_id,
|
|
"component": str(self.component),
|
|
"component_path": self.component_path,
|
|
"settings_name": self.settings_name,
|
|
"settings": self.settings.to_data(),
|
|
"current_end_time": self.current_end_time,
|
|
"results": [_result_to_data(result) for result in self.results],
|
|
"plot_tabs": [tab.to_data() for tab in self.plot_tabs],
|
|
}
|
|
|
|
|
|
def _result_from_data(data: Mapping[str, Any]) -> SimulationResult:
|
|
raw_columns = data.get("data", {})
|
|
if not isinstance(raw_columns, Mapping):
|
|
raise TypeError("simulation result data must be a mapping")
|
|
columns = {str(name): [float(value) for value in values] for name, values in raw_columns.items()}
|
|
return SimulationResult(model_name=str(data.get("model_name", "")), data=columns, process_output=str(data.get("process_output", "")), process_errors=str(data.get("process_errors", "")))
|
|
|
|
|
|
def _result_to_data(result: SimulationResult) -> dict[str, Any]:
|
|
return {"model_name": result.model_name, "data": result.data, "process_output": result.process_output, "process_errors": result.process_errors}
|