Compare commits

...

4 Commits

22 changed files with 654 additions and 263 deletions

View File

@ -1,16 +1,226 @@
{ {
"dist_open_start_1": 0.005, "dist_open_start_1": [
"dist_open_start_2": 0.005, 0.005,
"dist_open_after_1": 0.006, 0.005,
"dist_open_after_2": 0.006, 0.005,
"dist_open_end_1": 0.01, 0.005,
"dist_open_end_2": 0.05, 0.005,
"dist_close_end_1": 0.005, 0.005,
"dist_close_end_2": 0.005, 0.005,
"time_wielding": 1, 0.005,
"time_command": 0.0, 0.005,
"time_robot_movement": 0.2, 0.005,
"object_thickness": 0.0045, 0.005,
"force_target": 5000, 0.005,
"force_capture": 500 0.005,
0.005
],
"dist_open_start_2": [
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005
],
"dist_open_after_1": [
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006
],
"dist_open_after_2": [
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006,
0.006
],
"dist_open_end_1": [
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01,
0.01
],
"dist_open_end_2": [
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05,
0.05
],
"dist_close_end_1": [
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005
],
"dist_close_end_2": [
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005,
0.005
],
"time_wielding": [
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0
],
"time_command": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"time_robot_movement": [
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
],
"object_thickness": [
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045,
0.0045
],
"force_target": [
4000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0,
5000.0
],
"force_capture": [
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0,
500.0
]
} }

View File

@ -1,22 +1,22 @@
{ {
"trace_storage_path": "D:/downloads/a22", "trace_storage_path": ["D:/downloads/a22"],
"monitor_update_period": 100, "monitor_update_period": [100],
"a_max_1": 5.41, "a_max_1": [5.41],
"v_max_1": 0.278, "v_max_1": [0.278],
"a_max_2": 35.81, "a_max_2": [35.81],
"v_max_2": 0.7, "v_max_2": [0.7],
"mass_1": 270, "mass_1": [270],
"mass_2": 1, "mass_2": [1],
"k_hardness_1": 2148570, "k_hardness_1": [2148570],
"k_hardness_2": 0, "k_hardness_2": [0],
"torque_max_1": 20, "torque_max_1": [20],
"torque_max_2": 0, "torque_max_2": [0],
"transmission_ratio_1": 0.00125, "transmission_ratio_1": [0.00125],
"transmission_ratio_2": 1, "transmission_ratio_2": [1],
"position_start_1": 0.08, "position_start_1": [0.08],
"position_start_2": 0.08, "position_start_2": [0.08],
"k_prop": 0.05, "k_prop": [0.05],
"time_capture": 100000, "time_capture": [100000],
"UML_time_scaler": 1000 "UML_time_scaler": [1000]
} }

Binary file not shown.

View File

@ -3,18 +3,21 @@ import pandas as pd
from typing import Union from typing import Union
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
from src.utils.base.base import BaseMediator, BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget from src.utils.base.base import BaseMediator, BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget, BasePointPassportFormer
class Mediator(BaseMediator): class Mediator(BaseMediator):
def notify(self, def notify(self,
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget], source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget],
data: Union[list[str], list[pd.DataFrame], list[QWidget]]): data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
if issubclass(source.__class__, BaseDirectoryMonitor): if issubclass(source.__class__, BaseDirectoryMonitor):
self._converter.convert_data(data) self._converter.convert_data(data)
if issubclass(source.__class__, BaseDataConverter): if issubclass(source.__class__, BaseDataConverter):
self._passportFormer.form_passports(data)
if issubclass(source.__class__, BasePointPassportFormer):
self._plot.build(data) self._plot.build(data)
if issubclass(source.__class__, BasePlotWidget): if issubclass(source.__class__, BasePlotWidget):
@ -22,7 +25,7 @@ class Mediator(BaseMediator):
def push_settings(self, settings: list[dict]): def push_settings(self, settings: list[dict]):
self._monitor.update_settings(settings) self._monitor.update_settings(settings)
self._plot.update_settings(settings) self._passportFormer.update_settings(settings)
self._monitor.force_all_dir() self._monitor.force_all_dir()

View File

@ -29,8 +29,8 @@ class DirectoryMonitor(BaseDirectoryMonitor):
def update_settings(self, data: list[dict]) -> None: def update_settings(self, data: list[dict]) -> None:
self.stop() self.stop()
operator_params, system_params = data operator_params, system_params = data
self._directory_path = system_params['trace_storage_path'] self._directory_path = system_params['trace_storage_path'][0]
self._update_time = system_params['monitor_update_period'] self._update_time = system_params['monitor_update_period'][0]
self._init_state() self._init_state()
self.start() self.start()

View File

@ -0,0 +1,63 @@
from src.utils.base.base import BasePointPassportFormer, BaseIdealDataBuilder
import pandas as pd
class idealDataBuilder(BaseIdealDataBuilder):
def get_closingDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tclose'], self.calcPhaseClose)
def get_compressionDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tgrow'], self.calcPhaseGrow)
def get_openingDF(self) -> pd.DataFrame:
return self._get_data(self.getMarkOpen(), self.calcPhaseOpen)
def get_oncomingDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tmovement'], self.calcPhaseMovement)
def get_weldingDF(self) -> pd.DataFrame:
data = []
X1, X2, V1, V2, F = self.calcPhaseGrow(self.Ts['tgrow'])
data.append({"time":0, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F})
data.append({"time":self.welding_time, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F})
return pd.DataFrame(data)
def get_ideal_timings(self) -> list[float]:
data = self.Ts
ideal_timings = [data['tclose'], data['tgrow'], self.welding_time, self.getMarkOpen(), data['tmovement']] # TODO: add data['tmovement'], Oncoming не учитывается в производительности
return ideal_timings
class PassportFormer(BasePointPassportFormer):
def form_passports(self, data: list[pd.DataFrame]) -> list[list[pd.DataFrame, dict, list]]:
return_data = [self._build_passports_pocket(dataframe) for dataframe in data]
self._mediator.notify(self, return_data)
def _build_passports_pocket(self, dataframe: pd.DataFrame) -> list[pd.DataFrame, dict, list]:
passports_pocket = []
events, point_quantity = self._filter_events(dataframe["time"], dataframe)
system_settings = {key: value[0] for key, value in self._params[1].items()}
for i in range(0, point_quantity):
if not dataframe["time"].isna().all():
operator_settings = {}
for key, value in self._params[0].items():
if len(value) > i:
operator_settings[key] = value[i]
else:
operator_settings[key] = value[0]
params_list = [operator_settings, system_settings]
ideal_data = self._build_ideal_data(idealDataBuilder=idealDataBuilder, params=params_list)
if i < point_quantity-1:
cut_time = events[self._stages[0]][0][i+1]
frame = dataframe[dataframe["time"] < cut_time]
dataframe = dataframe[dataframe["time"] >= cut_time]
else:
frame = dataframe
point_events = {key: [value[0][i], value[1][i]] for key, value in events.items()}
passports_pocket.append([frame, ideal_data, point_events])
return passports_pocket
def update_settings(self, params: list[dict, dict]):
self._params = params

View File

@ -45,7 +45,7 @@ class MainWindow(BaseMainWindow):
def keyPressEvent(self, a0): def keyPressEvent(self, a0):
if a0.key() == Qt.Key_F5: if a0.key() == Qt.Key_F5:
self.clear() pass
def _show_settings(self): def _show_settings(self):
self.operSettings.show() self.operSettings.show()

View File

@ -6,34 +6,6 @@ from numpy import floating
from typing import Optional, Any, NamedTuple from typing import Optional, Any, NamedTuple
from src.utils.base.base import BasePlotWidget from src.utils.base.base import BasePlotWidget
from src.utils.base.base import BaseIdealDataBuilder
class idealDataBuilder(BaseIdealDataBuilder):
def get_closingDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tclose'], self.calcPhaseClose)
def get_compressionDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tgrow'], self.calcPhaseGrow)
def get_openingDF(self) -> pd.DataFrame:
return self._get_data(self.getMarkOpen(), self.calcPhaseOpen)
def get_oncomingDF(self) -> pd.DataFrame:
return self._get_data(self.Ts['tmovement'], self.calcPhaseMovement)
def get_weldingDF(self) -> pd.DataFrame:
data = []
X1, X2, V1, V2, F = self.calcPhaseGrow(self.Ts['tgrow'])
data.append({"time":0, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F})
data.append({"time":self.welding_time, "Posicion FE":X1,"Posicion ME":X2, "Rotor Speed FE":V1, "Rotor Speed ME":V2, "Force":F})
return pd.DataFrame(data)
def get_ideal_timings(self) -> list[float, float, float, float]:
data = self.Ts
ideal_timings = [data['tclose'], data['tgrow'], self.welding_time, self.getMarkOpen()] # TODO: add data['tmovement'], Oncoming не учитывается в производительности
return ideal_timings
class ProcessStage(NamedTuple): class ProcessStage(NamedTuple):
mean_value: floating[Any] mean_value: floating[Any]
@ -43,14 +15,13 @@ class ProcessStage(NamedTuple):
class PlotWidget(BasePlotWidget): class PlotWidget(BasePlotWidget):
def _create_curve_ideal(self, def _create_curve_ideal(self,
stage: str, signal: str,
signal: str, ideal_data: pd.DataFrame,
start_timestamp: float, start_timestamp: float,
finish_timestamp: float) -> Optional[pg.PlotDataItem]: finish_timestamp: float) -> Optional[pg.PlotDataItem]:
data = self._stage_ideals[stage]
if start_timestamp and finish_timestamp: if start_timestamp and finish_timestamp:
plot = pg.PlotDataItem(x=start_timestamp+data["time"], y=data[signal["name"]], pen=signal["pen"]) plot = pg.PlotDataItem(x=start_timestamp+ideal_data["time"], y=ideal_data[signal["name"]], pen=signal["pen"])
return plot return plot
return None return None
@ -64,19 +35,6 @@ class PlotWidget(BasePlotWidget):
region.setBrush(pg.mkBrush(self._stage_colors[stage])) region.setBrush(pg.mkBrush(self._stage_colors[stage]))
return region return region
return None return None
def _get_timestamp(self,
stage: str,
times: pd.Series,
dataframe: pd.DataFrame) -> Optional[list[float]]:
stage_diff = np.diff(dataframe[stage])
start_index = np.where(stage_diff == 1)[0]
finish_index = np.where(stage_diff == -1)[0]
if start_index.size:
start_timestamp = times[start_index[0]]
finish_timestamp = times[finish_index[0]] if finish_index.size else times[len(times) - 1]
return start_timestamp, finish_timestamp
return None
@staticmethod @staticmethod
def _init_plot_widget(title: str) -> tuple[pg.PlotWidget, pg.LegendItem]: def _init_plot_widget(title: str) -> tuple[pg.PlotWidget, pg.LegendItem]:
@ -90,110 +48,109 @@ class PlotWidget(BasePlotWidget):
stage: str, stage: str,
dataframe: pd.DataFrame, dataframe: pd.DataFrame,
signal_name: str) -> Optional[ProcessStage]: signal_name: str) -> Optional[ProcessStage]:
if stage in self._stages:
stage_diff = np.diff(dataframe[stage])
start_index = np.where(stage_diff == 1)[0]
finish_index = np.where(stage_diff == -1)[0]
data = dataframe[signal_name] if signal_name in dataframe.columns.tolist() else []
if data.size and start_index.size:
start = start_index[0]
finish = finish_index[0] if finish_index.size else (len(data) - 1)
data_slice = data[start:finish]
mean = np.mean(data_slice)
return ProcessStage(mean_value=mean, start_index=int(start), finish_index=int(finish))
return None
def _build_widget(self, dataframe: pd.DataFrame) -> QWidget: stage_diff = np.diff(dataframe[stage])
start_index = np.where(stage_diff == 1)[0]
finish_index = np.where(stage_diff == -1)[0]
data = dataframe[signal_name] if signal_name in dataframe.columns.tolist() else []
if data.size and start_index.size:
start = start_index[0]
finish = finish_index[0] if finish_index.size else (len(data) - 1)
data_slice = data[start:finish]
mean = np.mean(data_slice)
return ProcessStage(mean_value=mean, start_index=int(start), finish_index=int(finish))
return None
def _build_widget(self, data: list[pd.DataFrame, dict, list]) -> QWidget:
widget = QWidget() widget = QWidget()
layout = QVBoxLayout() layout = QVBoxLayout()
time_axis = dataframe["time"]
dataframe_headers = dataframe.columns.tolist()
for channel, description in self._plt_channels.items(): for channel, description in self._plt_channels.items():
performance_list = []
df_continuous = pd.DataFrame({})
plot_widget, legend = self._init_plot_widget(title=channel) plot_widget, legend = self._init_plot_widget(title=channel)
settings = description["Settings"] settings = description["Settings"]
if settings["stages"] and all([stage in dataframe_headers for stage in self._stages]):
for stage in self._stages: for dataframe, ideal_data, events in data:
start_timestamp, finish_timestamp = self._get_timestamp(stage, time_axis, dataframe) df_continuous = pd.concat([df_continuous, dataframe], axis=0)
region = self._create_stage_region(stage, start_timestamp, finish_timestamp) dataframe_headers = dataframe.columns.tolist()
if region: stages = events.keys()
plot_widget.addItem(region)
for signal in description["Ideal_signals"]:
ideal_plot = self._create_curve_ideal(stage, signal, start_timestamp, finish_timestamp)
if ideal_plot:
plot_widget.addItem(ideal_plot)
end_timestamp = time_axis[len(time_axis) - 1] if settings["stages"] and all([stage in dataframe_headers for stage in stages]):
region = self._create_stage_region("Oncoming", finish_timestamp, end_timestamp) for stage in stages:
if region: start_t, end_t = events[stage]
plot_widget.addItem(region) region = self._create_stage_region(stage, start_t, end_t)
if region:
plot_widget.addItem(region)
for signal in description["Ideal_signals"]: if settings["ideals"]:
ideal_plot = self._create_curve_ideal("Oncoming", signal, finish_timestamp, end_timestamp) for stage in stages:
if ideal_plot: for signal in description["Ideal_signals"]:
plot_widget.addItem(ideal_plot) curve = self._create_curve_ideal(signal, ideal_data[stage], events[stage][0], events[stage][1])
if curve:
plot_widget.addItem(curve)
if settings["performance"] and all([stage in dataframe_headers for stage in self._stages]): if settings["performance"]:
delta_timestamp = 0 ideal_delta = ideal_data["Ideal cycle"]
for stage in self._stages: delta = 0
start_timestamp, finish_timestamp = self._get_timestamp(stage, time_axis, dataframe) for stage in stages:
delta_timestamp += finish_timestamp - start_timestamp delta += events[stage][1] - events[stage][0]
performance = ideal_delta/delta*100
performance_list.append(performance)
ideal_delta = self._opt.get_cycle_time() if settings["zoom"]:
performance = round(ideal_delta/delta_timestamp*100, 2) pass
performance_label = QLabel(f"Performance = {performance} %") """if max(time_axis) < 5.0:
layout.addWidget(performance_label) stages = [self.get_stage_info("Welding",
dataframe,
signal["name"]) for signal in description["Real_signals"]]
if stages:
means_raw = [stage.mean_value for stage in stages]
mean = max(means_raw)
start = time_axis[stages[0].start_index]
finish = time_axis[stages[0].finish_index]
if settings["zoom"]: overshoot = pg.BarGraphItem(x0=0,
if max(time_axis) < 5.0: y0=mean - mean * 0.05,
stages = [self.get_stage_info("Welding", height=mean * 0.05 * 2,
dataframe, width=start,
signal["name"]) for signal in description["Real_signals"]] brush=pg.mkBrush([0, 250, 0, 100]))
if stages: plot_widget.addItem(overshoot)
means_raw = [stage.mean_value for stage in stages]
mean = max(means_raw)
start = time_axis[stages[0].start_index]
finish = time_axis[stages[0].finish_index]
overshoot = pg.BarGraphItem(x0=0, stable = pg.BarGraphItem(x0=start,
y0=mean - mean * 0.05, y0=mean - mean * 0.015,
height=mean * 0.05 * 2, height=mean * 0.015 * 2,
width=start, width=finish - start,
brush=pg.mkBrush([0, 250, 0, 100])) brush=pg.mkBrush([0, 250, 0, 100]))
plot_widget.addItem(overshoot) plot_widget.addItem(stable)
stable = pg.BarGraphItem(x0=start, plot_widget.setYRange(mean - 260, mean + 260)
y0=mean - mean * 0.015, plot_widget.setInteractive(False)
height=mean * 0.015 * 2, else:
width=finish - start, max_value = min([max(dataframe[signal["name"]]) for signal in description["Real_signals"]])
brush=pg.mkBrush([0, 250, 0, 100])) region = pg.LinearRegionItem([max_value - max_value * 0.015,
plot_widget.addItem(stable) max_value + max_value * 0.015],
movable=False,
orientation="horizontal")
plot_widget.setYRange(mean - 260, mean + 260) region.setBrush(pg.mkBrush([0, 250, 0, 100]))
plot_widget.setInteractive(False) plot_widget.setYRange(max_value - 200, max_value + 200)
else: plot_widget.setXRange(3.5, 4.5)
max_value = min([max(dataframe[signal["name"]]) for signal in description["Real_signals"]]) plot_widget.addItem(region)
region = pg.LinearRegionItem([max_value - max_value * 0.015, plot_widget.setInteractive(False)"""
max_value + max_value * 0.015],
movable=False,
orientation="horizontal")
region.setBrush(pg.mkBrush([0, 250, 0, 100]))
plot_widget.setYRange(max_value - 200, max_value + 200)
plot_widget.setXRange(3.5, 4.5)
plot_widget.addItem(region)
plot_widget.setInteractive(False)
for signal in description["Real_signals"]: for signal in description["Real_signals"]:
if signal["name"] in dataframe_headers: if signal["name"] in dataframe_headers:
plot = plot_widget.plot(time_axis, dataframe[signal["name"]], pen=signal["pen"]) plot = plot_widget.plot(df_continuous["time"], df_continuous[signal["name"]], pen=signal["pen"])
legend.addItem(plot, signal["name"]) legend.addItem(plot, signal["name"])
if settings["performance"]:
performance_list = np.array(performance_list)
performance_label = QLabel(f"""Performance: best = {round(performance_list.max(),2)} %, worse = {round(performance_list.min(),2)} %, average = {round(performance_list.mean(),2)}%""")
layout.addWidget(performance_label)
layout.addWidget(plot_widget) layout.addWidget(plot_widget)
widget.setLayout(layout) widget.setLayout(layout)
@ -203,8 +160,5 @@ class PlotWidget(BasePlotWidget):
widgets = [self._build_widget(data_sample) for data_sample in data] widgets = [self._build_widget(data_sample) for data_sample in data]
self._mediator.notify(self, widgets) self._mediator.notify(self, widgets)
def update_settings(self, data: list[dict]):
self._initIdealBuilder(idealDataBuilder=idealDataBuilder, data=data)

View File

@ -1,55 +1,103 @@
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets from PyQt5.QtWidgets import QWidget, QTableWidget, QVBoxLayout, QTableWidgetItem, QLabel, QPushButton, QLineEdit, QHBoxLayout
from pyqtgraph.parametertree import Parameter, ParameterTree from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIntValidator
from src.utils.json_tools import read_json, write_json from src.utils.json_tools import read_json, write_json
from src.gui import qt_settings as qts from src.gui import qt_settings as qts
class settingsWindow(QtWidgets.QWidget): class settingsWindow(QWidget):
def __init__(self, path: str, name: str, upd_func): def __init__(self, path: str, name: str, upd_func):
super(settingsWindow, self).__init__() super(settingsWindow, self).__init__()
self.settingsPath = path self._settingsPath = path
self.name = name self._name = name
self._data = {}
self.data = {} self._upd_func = upd_func
self.params = None
self.load_settings() self.load_settings()
self._init_ui() self._init_ui()
self.params.sigTreeStateChanged.connect(upd_func)
def load_settings(self) -> None: def load_settings(self) -> None:
self.data = read_json(self.settingsPath) self._data = read_json(self._settingsPath)
def write_settings(self) -> None: def write_settings(self) -> None:
self.getParams() write_json(self._settingsPath, self._data)
write_json(self.settingsPath, self.data)
def getParams(self) -> dict:
return self._data
def _getTreeStructure(self) -> list:
params = []
for key, value in self.data.items():
params.append({'name': str(key), 'type': type(value).__name__, 'value': value})
params.append({'name': 'Save', 'type': 'action'})
return params
def _init_ui(self) -> None: def _init_ui(self) -> None:
temp = self._getTreeStructure() save_button = QPushButton()
self.params = Parameter.create(name=self.name, type='group', children=temp) restore_button = QPushButton()
self.params.param('Save').sigActivated.connect(self.write_settings) save_button.setText("Save")
restore_button.setText("Restore")
self._num_points = QLineEdit()
self._num_points.setPlaceholderText("Enter the number of welding points")
self._num_points.setValidator(QIntValidator())
control_layout = QHBoxLayout()
control_layout.addWidget(save_button)
control_layout.addWidget(restore_button)
control_layout.addWidget(self._num_points)
ParamsTree = ParameterTree() save_button.pressed.connect(self._save)
ParamsTree.setParameters(self.params, showTop=True) restore_button.pressed.connect(self._restore)
layout = QtWidgets.QGridLayout() self._num_points.editingFinished.connect(self._expand)
layout.addWidget(ParamsTree, 0,0)
self._param_table = QTableWidget()
self._param_table.setColumnCount(2)
self._param_table.setRowCount(len(self._data))
for i, (key, items) in enumerate(self._data.items()):
self._param_table.setItem(i, 0, QTableWidgetItem(key))
self._param_table.setItem(i, 1, QTableWidgetItem(str(items[0])))
layout = QVBoxLayout()
header = QLabel(self._name)
layout.addWidget(header)
layout.addLayout(control_layout)
layout.addWidget(self._param_table)
self.setLayout(layout) self.setLayout(layout)
self.setStyleSheet(qts.white_style) self.setStyleSheet(qts.white_style)
# self.show() #self.show()
def getParams(self) -> dict: def _save(self) -> dict:
self.data = {} self._data = {}
for p in self.params: for i in range(self._param_table.rowCount()):
if p.name() != 'Save': key = self._param_table.item(i, 0).text()
self.data[p.name()] = p.value() data = []
return self.data for j in range(1, self._param_table.columnCount()):
param = self._param_table.item(i, j).text()
if key != "trace_storage_path":
param = float(param)
data.append(param)
self._data[key] = data
self.write_settings()
self._upd_func()
def _restore(self) -> None:
self._param_table.setRowCount(len(self._data))
self._param_table.setColumnCount(len(self._data[self._data.keys()[0]]))
for i, (key, items) in enumerate(self._data.items()):
self._param_table.setItem(i, 0, QTableWidgetItem(key))
for j, item in enumerate(items):
self._param_table.setItem(i, j+1, QTableWidgetItem(str(item)))
def _expand(self) -> None:
param=int(self._num_points.text())
prev_columns = self._param_table.columnCount()
self._param_table.setColumnCount(param+1)
if prev_columns < param+1:
for i in range(prev_columns, param+1):
for j, (key, items) in enumerate(self._data.items()):
self._param_table.setItem(j, i, QTableWidgetItem(str(items[-1])))
if __name__ == '__main__': if __name__ == '__main__':
app = pg.mkQApp("Parameter Tree Example") app = pg.mkQApp("Parameter Tree Example")

View File

@ -7,6 +7,7 @@ from src.controller.mediator import Mediator
from src.controller.converter import DataConverter from src.controller.converter import DataConverter
from src.gui.plotter import PlotWidget from src.gui.plotter import PlotWidget
from src.controller.controller import Controller from src.controller.controller import Controller
from src.controller.passportFormer import PassportFormer
def main(): def main():
@ -15,8 +16,9 @@ def main():
data_converter = DataConverter() data_converter = DataConverter()
plot_widget_builder = PlotWidget() plot_widget_builder = PlotWidget()
controller = Controller() controller = Controller()
passport_former = PassportFormer()
window = MainWindow(controller) window = MainWindow(controller)
mediator = Mediator(monitor, data_converter, plot_widget_builder, controller) mediator = Mediator(monitor, data_converter, passport_former, plot_widget_builder, controller)
window.show() window.show()
controller.signal_widgets.connect(window.show_plot_tabs) controller.signal_widgets.connect(window.show_plot_tabs)

View File

@ -2,6 +2,7 @@ from typing import Optional
import os import os
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from intervaltree import Interval, IntervalTree
from roboter import RobotPerformance, TWC_Performance from roboter import RobotPerformance, TWC_Performance
@ -10,8 +11,25 @@ class PerformanceProcessor:
def calc_performance(self, path, df): def calc_performance(self, path, df):
robot = RobotPerformance() robot = RobotPerformance()
TWC = TWC_Performance() TWC = TWC_Performance()
rob_df = robot.job(path) point_tree, movement_tree, dialog_tree = robot.job(path)
TWC_df = TWC.job(df) closing_tree, squeeze_tree, relief_tree = TWC.job(df)
dialog_inPoint = point_tree & dialog_tree
dialog_inMovement = movement_tree & dialog_tree
closing_inPoint = point_tree & closing_tree
closing_inMovement = movement_tree & closing_tree
squeeze_inPoint = point_tree & squeeze_tree
squeeze_inMovement = movement_tree & squeeze_tree
relief_inPoint = point_tree & relief_tree
relief_inMovement = movement_tree & relief_tree
@ -20,4 +38,5 @@ if __name__ == "__main__":
robot = RobotPerformance() robot = RobotPerformance()
#TWC = TWC_Performance() #TWC = TWC_Performance()
result = robot.job(path) result = robot.job(path)
print (result) print (result[0])
print (result[1])

View File

@ -5,6 +5,8 @@ import os
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from intervaltree import Interval, IntervalTree
class BasePerformanceFactory(ABC): class BasePerformanceFactory(ABC):
@ -88,16 +90,35 @@ class BaseProduct(ABC):
stage_diff = np.diff(dataframe[signal]) stage_diff = np.diff(dataframe[signal])
start_idx = np.where(stage_diff == 1) start_idx = np.where(stage_diff == 1)
finish_idx = np.where(stage_diff == -1) finish_idx = np.where(stage_diff == -1)
return start_idx, finish_idx return start_idx[0], finish_idx[0]
def operation(self, dataframe: pd.DataFrame) -> pd.DataFrame: def _find_events(self,
all_idx = np.array([]) dataframe: pd.DataFrame,
for signal in self._signals: signals: list[str]) -> Optional[dict[dict[pd.Series]]]:
intervals = {}
end_time = 0
for signal in signals:
start_idx, finish_idx = np.array(self._find_indexes(signal, dataframe)) start_idx, finish_idx = np.array(self._find_indexes(signal, dataframe))
all_idx = np.hstack([all_idx, start_idx[0], finish_idx[0]]) start_series = dataframe.loc[start_idx, "time"].reset_index(drop=True)
all_idx = np.sort(np.array(all_idx, dtype="int64")) end_series = dataframe.loc[finish_idx, "time"].reset_index(drop=True)
result = dataframe.loc[all_idx, self._signals + ["time"]] end_series.fillna(end_time)
return result intervals[signal] = {"rise": start_series,
"fall": end_series}
return intervals
def _form_intervals(self,
start: pd.Series,
end: pd.Series) -> IntervalTree:
if len(start) != len(end):
for i in range(1, len(end)):
if end[i-1] > start[i]:
start = start.drop(i).reset_index(drop=True)
tree = IntervalTree(Interval(start[i], end[i], i) for i in range(len (start)))
return tree
@abstractmethod
def operation(self):
...
class RobotPerformance(BasePerformanceFactory): class RobotPerformance(BasePerformanceFactory):
@ -105,33 +126,18 @@ class RobotPerformance(BasePerformanceFactory):
def factory_method(self) -> BaseProduct: def factory_method(self) -> BaseProduct:
return RobotDF() return RobotDF()
def job(self, path) -> pd.DataFrame: def job(self, path) -> list[pd.DataFrame]:
product = self.factory_method() product = self.factory_method()
dataframe = self._get_file_data(path) dataframe = self._get_file_data(path)
rob_df = product.operation(dataframe) rob_df = product.operation(dataframe)
in_point, in_moving, in_dialog = False, False, False
for index, row in rob_df.iterrows():
if row["$OUT3012"] == 1:
if not in_point:
in_point = True
start_point = row["time"]
else:
in_point = False
time_in_point = row["time"] - start_point
return rob_df return rob_df
class TWC_Performance(BasePerformanceFactory): class TWC_Performance(BasePerformanceFactory):
def factory_method(self) -> BaseProduct: def factory_method(self) -> BaseProduct:
return TWC_DF() return TWC_DF()
def job(self, TWC_DF: pd.DataFrame) -> pd.DataFrame: def job(self, TWC_DF: pd.DataFrame) -> list[pd.DataFrame]:
product = self.factory_method() product = self.factory_method()
result = product.operation(TWC_DF) result = product.operation(TWC_DF)
return result return result
@ -145,6 +151,13 @@ class RobotDF(BaseProduct):
"$OUT3003" "$OUT3003"
] ]
def operation(self, dataframe: pd.DataFrame) -> list[IntervalTree]:
events = self._find_events(dataframe, self._signals)
point_tree = self._form_intervals(start=events["$OUT3012"]["rise"], end=events["$OUT3012"]["fall"])
movement_tree = self._form_intervals(start=events["$OUT3003"]["fall"], end=events["$OUT3012"]["rise"])
dialog_tree = []
return point_tree, movement_tree, dialog_tree
class TWC_DF(BaseProduct): class TWC_DF(BaseProduct):
def __init__(self): def __init__(self):
@ -155,6 +168,13 @@ class TWC_DF(BaseProduct):
"Relief" "Relief"
] ]
def operation(self, dataframe: pd.DataFrame) -> list[IntervalTree]:
events = self._find_events(dataframe, self._signals)
closing_tree = self._form_intervals(start=events["Closing"]["rise"], end=events["Closing"]["fall"])
squeeze_tree = self._form_intervals(start=events["Squeeze"]["rise"], end=events["Squeeze"]["fall"])
relief_tree = self._form_intervals(start=events["Relief"]["rise"], end=events["Relief"]["fall"])
return closing_tree, squeeze_tree, relief_tree

View File

@ -8,26 +8,33 @@ from PyQt5.QtCore import QThread, QObject, QTimer
from PyQt5.QtWidgets import QWidget, QTabWidget from PyQt5.QtWidgets import QWidget, QTabWidget
from src.OptAlgorithm import OptAlgorithm from src.OptAlgorithm import OptAlgorithm
import pandas as pd import pandas as pd
import pandas as pd
import numpy as np
class BaseMediator: class BaseMediator:
def __init__(self, def __init__(self,
monitor: BaseDirectoryMonitor, monitor: BaseDirectoryMonitor,
converter: BaseDataConverter, converter: BaseDataConverter,
passportFormer: BasePointPassportFormer,
plot: BasePlotWidget, plot: BasePlotWidget,
controller: BaseController): controller: BaseController):
self._monitor = monitor self._monitor = monitor
self._monitor.mediator = self self._monitor.mediator = self
self._converter = converter self._converter = converter
self._converter.mediator = self self._converter.mediator = self
self._passportFormer = passportFormer
self._passportFormer.mediator = self
self._plot = plot self._plot = plot
self._plot.mediator = self self._plot.mediator = self
self._controller = controller self._controller = controller
self._controller.mediator = self self._controller.mediator = self
def notify(self, def notify(self,
source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePlotWidget, BaseMainWindow], source: Union[BaseDirectoryMonitor, BaseDataConverter, BasePointPassportFormer, BasePlotWidget],
data: Union[list[str], list[pd.DataFrame], list[QWidget], list[dict]]): data: Union[list[str], list[pd.DataFrame], list[list], list[QWidget]]):
... ...
def push_settings (self, data: list[dict]): def push_settings (self, data: list[dict]):
... ...
@ -102,27 +109,22 @@ class BasePlotWidget:
mediator: Optional[BaseMediator] = None): mediator: Optional[BaseMediator] = None):
super().__init__() super().__init__()
self._mediator = mediator self._mediator = mediator
self._stages = [
"Closing",
"Squeeze",
"Welding",
"Relief"
]
self._stage_colors = { self._stage_colors = {
"Closing": [208, 28, 31, 100], "Closing": [208, 28, 31, 100],
"Squeeze": [45, 51, 89, 150], "Squeeze": [45, 51, 89, 150],
"Welding": [247, 183, 24, 100], "Welding": [247, 183, 24, 100],
"Relief": [0, 134, 88, 100], "Relief": [0, 134, 88, 100],
"Oncoming": [222, 184, 135, 100] "Oncomming": [222, 184, 135, 100]
} }
self._plt_channels = { self._plt_channels = {
"Electrode Force, N & Welding Current, kA": { "Electrode Force, N & Welding Current, kA": {
"Settings": { "Settings": {
"zoom": False, "zoom": False,
"stages": True, "stages": True,
"performance": True "performance": True,
"ideals": True
}, },
"Real_signals": [ "Real_signals": [
{ {
@ -149,7 +151,8 @@ class BasePlotWidget:
"Settings": { "Settings": {
"zoom": True, "zoom": True,
"stages": False, "stages": False,
"performance": False "performance": False,
"ideals": True
}, },
"Real_signals": [ "Real_signals": [
{ {
@ -172,7 +175,8 @@ class BasePlotWidget:
"Settings": { "Settings": {
"zoom": False, "zoom": False,
"stages": True, "stages": True,
"performance": False "performance": False,
"ideals": True
}, },
"Real_signals": [ "Real_signals": [
{ {
@ -200,18 +204,7 @@ class BasePlotWidget:
] ]
}, },
} }
def _initIdealBuilder(self,
idealDataBuilder: Optional[BaseIdealDataBuilder] = None,
data: list[dict] = None):
self.opt = idealDataBuilder(data)
self._stage_ideals = {
"Closing": self._opt.get_closingDF(),
"Squeeze": self._opt.get_compressionDF(),
"Welding": self._opt.get_weldingDF(),
"Relief": self._opt.get_openingDF(),
"Oncoming": self._opt.get_oncomingDF()
}
@property @property
def mediator(self) -> BaseMediator: def mediator(self) -> BaseMediator:
return self._mediator return self._mediator
@ -231,8 +224,6 @@ class BasePlotWidget:
def build(self, data: list[pd.DataFrame]) -> list[QWidget]: def build(self, data: list[pd.DataFrame]) -> list[QWidget]:
... ...
def update_settings(self, data: list[dict]) -> None:
...
class BaseController(QObject): class BaseController(QObject):
@ -243,10 +234,10 @@ class BaseController(QObject):
... ...
# FIXME: WeldingDF показывает только 1 секунду
class BaseIdealDataBuilder(OptAlgorithm): class BaseIdealDataBuilder(OptAlgorithm):
def __init__(self, data: list[dict]):
operator_params, system_params = data def __init__(self, params: list[dict]):
operator_params, system_params = params
self.mul = system_params['time_capture'] self.mul = system_params['time_capture']
self.welding_time = operator_params['time_wielding'] self.welding_time = operator_params['time_wielding']
super().__init__(operator_params, system_params) super().__init__(operator_params, system_params)
@ -337,4 +328,85 @@ class BaseMainWindow(QWidget):
} }
""") """)
class BasePointPassportFormer:
def __init__(self,
mediator: Optional[BaseMediator] = None):
self._mediator = mediator
self._clear_stage = "Welding"
self._stages = [
"Closing",
"Squeeze",
"Welding",
"Relief",
"Oncomming"
]
def _find_indexes(self,
signal: str,
dataframe: pd.DataFrame) -> list[list[float], list[float]]:
stage_diff = np.diff(dataframe[signal])
start_idx = np.where(stage_diff == 1)
finish_idx = np.where(stage_diff == -1)
return start_idx[0], finish_idx[0]
def _find_events(self,
signal: str,
times:pd.Series,
dataframe: pd.DataFrame) -> list[list[float]]:
start_idx, finish_idx = self._find_indexes(signal, dataframe)
start_list = times.loc[start_idx].tolist()
end_list = times.loc[finish_idx].tolist()
if len(start_list) - len(end_list) == 1:
end_list.append(float(times[len(times)-1]))
return start_list, end_list
def _filter_events(self,
times: pd.Series,
dataframe: pd.DataFrame) -> list[dict[list[float]], int]:
events = {}
if self._clear_stage in self._stages:
start_list, end_list = self._find_events(self._clear_stage, times, dataframe)
point_quantity = len(start_list)
for stage in self._stages:
start_list, end_list = self._find_events(stage, times, dataframe)
events[stage] = [start_list[:point_quantity], end_list[:point_quantity]]
return events, point_quantity
def _build_ideal_data(self,
idealDataBuilder: Optional[BaseIdealDataBuilder] = None,
params: list[dict] = None) -> dict:
self.opt = idealDataBuilder(params)
stage_ideals = {
"Closing": self._opt.get_closingDF(),
"Squeeze": self._opt.get_compressionDF(),
"Welding": self._opt.get_weldingDF(),
"Relief": self._opt.get_openingDF(),
"Oncomming": self._opt.get_oncomingDF(),
"Ideal cycle": self._opt.get_cycle_time()
}
return stage_ideals
def form_passports(self) -> list[list[pd.DataFrame, dict, list]]:
...
def update_settings(self, params: list) -> None:
...
@property
def opt(self) -> BaseIdealDataBuilder:
return self._opt
@opt.setter
def opt(self, opt: BaseIdealDataBuilder):
self._opt = opt
@property
def mediator(self) -> BaseMediator:
return self._mediator
@mediator.setter
def mediator(self, mediator: BaseMediator) -> None:
self._mediator = mediator

BIN
weldingSpotPerformance.7z Normal file

Binary file not shown.