dev: добавлена обработка ошибок записи трейсов
This commit is contained in:
parent
78d34343d5
commit
47248ac6a7
@ -222,85 +222,5 @@
|
|||||||
500.0,
|
500.0,
|
||||||
500.0,
|
500.0,
|
||||||
500.0
|
500.0
|
||||||
],
|
|
||||||
"Tesla closing": [
|
|
||||||
0.216,
|
|
||||||
0.228,
|
|
||||||
0.252,
|
|
||||||
0.216,
|
|
||||||
0.228,
|
|
||||||
0.216,
|
|
||||||
0.228,
|
|
||||||
0.228,
|
|
||||||
0.228,
|
|
||||||
0.216,
|
|
||||||
0.228,
|
|
||||||
0.216,
|
|
||||||
0.216,
|
|
||||||
0.216
|
|
||||||
],
|
|
||||||
"Tesla squeeze": [
|
|
||||||
0.276,
|
|
||||||
0.288,
|
|
||||||
0.264,
|
|
||||||
0.264,
|
|
||||||
0.276,
|
|
||||||
0.276,
|
|
||||||
0.312,
|
|
||||||
0.276,
|
|
||||||
0.24,
|
|
||||||
0.24,
|
|
||||||
0.24,
|
|
||||||
0.24,
|
|
||||||
0.24,
|
|
||||||
0.24
|
|
||||||
],
|
|
||||||
"Tesla welding": [
|
|
||||||
1.332,
|
|
||||||
1.644,
|
|
||||||
1.644,
|
|
||||||
1.428,
|
|
||||||
1.284,
|
|
||||||
1.308,
|
|
||||||
1.272,
|
|
||||||
1.38,
|
|
||||||
1.416,
|
|
||||||
1.392,
|
|
||||||
1.38,
|
|
||||||
1.404,
|
|
||||||
1.452,
|
|
||||||
1.452
|
|
||||||
],
|
|
||||||
"Tesla oncomming_relief": [
|
|
||||||
0.516,
|
|
||||||
0.492,
|
|
||||||
0.636,
|
|
||||||
0.492,
|
|
||||||
0.42,
|
|
||||||
0.54,
|
|
||||||
0.444,
|
|
||||||
0.66,
|
|
||||||
0.521,
|
|
||||||
0.557,
|
|
||||||
0.51,
|
|
||||||
0.51,
|
|
||||||
0.534,
|
|
||||||
0.01
|
|
||||||
],
|
|
||||||
"Tesla summary time": [
|
|
||||||
2.748,
|
|
||||||
2.676,
|
|
||||||
2.652,
|
|
||||||
2.544,
|
|
||||||
2.28,
|
|
||||||
2.22,
|
|
||||||
2.352,
|
|
||||||
2.328,
|
|
||||||
2.676,
|
|
||||||
2.369,
|
|
||||||
2.405,
|
|
||||||
2.37,
|
|
||||||
2.418,
|
|
||||||
2.442
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"trace_storage_path": [
|
"trace_storage_path": [
|
||||||
"D:/downloads/a22"
|
"/home/andrew/weldingspotperformance/trace_samples"
|
||||||
],
|
],
|
||||||
"monitor_update_period": [
|
"monitor_update_period": [
|
||||||
1000.0
|
1000.0
|
||||||
@ -58,5 +58,17 @@
|
|||||||
],
|
],
|
||||||
"Range ME, mm": [
|
"Range ME, mm": [
|
||||||
115.0
|
115.0
|
||||||
|
],
|
||||||
|
"client_time": [
|
||||||
|
35.488
|
||||||
|
],
|
||||||
|
"performance_mode": [
|
||||||
|
"client"
|
||||||
|
],
|
||||||
|
"time_before_start": [
|
||||||
|
0.924
|
||||||
|
],
|
||||||
|
"time_after_end": [
|
||||||
|
1.14
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -63,6 +63,13 @@ class PointPassport:
|
|||||||
useful_data: Dict = field(default_factory=dict)
|
useful_data: Dict = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PerformanceData:
|
||||||
|
client_to_TWC: float = 0
|
||||||
|
client_to_ideal: float = 0
|
||||||
|
TWC_to_ideal: float = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UsefulGraphData:
|
class UsefulGraphData:
|
||||||
"""
|
"""
|
||||||
@ -72,11 +79,12 @@ class UsefulGraphData:
|
|||||||
:param range_ME: Диапазон для ME.
|
:param range_ME: Диапазон для ME.
|
||||||
:param k_hardness: Коэффициент твердости.
|
:param k_hardness: Коэффициент твердости.
|
||||||
"""
|
"""
|
||||||
client_time: float = 0
|
performance: PerformanceData = PerformanceData()
|
||||||
range_ME: float = 0
|
range_ME: float = 0
|
||||||
k_hardness: float = 0
|
k_hardness: float = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class GraphicPassport:
|
class GraphicPassport:
|
||||||
"""
|
"""
|
||||||
@ -471,6 +479,7 @@ class BaseIdealDataBuilder(OptAlgorithm):
|
|||||||
def __init__(self, settings: Settings):
|
def __init__(self, settings: Settings):
|
||||||
self.mul = settings.system['time_capture']
|
self.mul = settings.system['time_capture']
|
||||||
self.welding_time = settings.operator['time_wielding']
|
self.welding_time = settings.operator['time_wielding']
|
||||||
|
self._settings = settings
|
||||||
super().__init__(settings.system, settings.operator)
|
super().__init__(settings.system, settings.operator)
|
||||||
|
|
||||||
def get_closingDF(self) -> pd.DataFrame:
|
def get_closingDF(self) -> pd.DataFrame:
|
||||||
|
|||||||
@ -9,7 +9,8 @@ from base.base import (
|
|||||||
PointPassport,
|
PointPassport,
|
||||||
GraphicPassport,
|
GraphicPassport,
|
||||||
Settings,
|
Settings,
|
||||||
UsefulGraphData
|
UsefulGraphData,
|
||||||
|
PerformanceData
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -115,12 +116,16 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
:return: Кортеж из двух списков: (список времён начала, список времён окончания).
|
:return: Кортеж из двух списков: (список времён начала, список времён окончания).
|
||||||
"""
|
"""
|
||||||
start_idx, finish_idx = PassportFormer._find_indexes(signal, df)
|
start_idx, finish_idx = PassportFormer._find_indexes(signal, df)
|
||||||
if len(start_idx) > 0 and len(finish_idx) > 0 and start_idx[0] > finish_idx[0]:
|
start_list, end_list = [], []
|
||||||
|
if len(start_idx) > 0 and len(finish_idx) > 0:
|
||||||
|
if start_idx[0] > finish_idx[0]:
|
||||||
|
logger.debug(f"_find_events - не найдено начало события {signal} с окончанием в {times.iloc[finish_idx[0]]} секунд, принято равным 0")
|
||||||
start_idx = np.insert(start_idx, 0, 0)
|
start_idx = np.insert(start_idx, 0, 0)
|
||||||
start_list = times.iloc[start_idx].tolist() if len(start_idx) > 0 else []
|
if start_idx[-1] > finish_idx[-1]:
|
||||||
end_list = times.iloc[finish_idx].tolist() if len(finish_idx) > 0 else []
|
logger.debug(f"_find_events - не найден конец события {signal} с началом в {times.iloc[start_idx[-1]]} секунд, принято равным {times.iloc[-1]} секунд")
|
||||||
if len(start_list) - len(end_list) == 1:
|
finish_idx = np.append(finish_idx, -1)
|
||||||
end_list.append(float(times.iloc[-1]))
|
start_list = times.iloc[start_idx].tolist()
|
||||||
|
end_list = times.iloc[finish_idx].tolist()
|
||||||
return (start_list, end_list)
|
return (start_list, end_list)
|
||||||
|
|
||||||
def _generate_events(self, times: pd.Series, df: pd.DataFrame) -> Tuple[Dict[str, List[List[float]]], int]:
|
def _generate_events(self, times: pd.Series, df: pd.DataFrame) -> Tuple[Dict[str, List[List[float]]], int]:
|
||||||
@ -137,20 +142,25 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
events = {}
|
events = {}
|
||||||
point_quantity = 0
|
point_quantity = 0
|
||||||
if self._clear_stage in self._stages:
|
if self._clear_stage in self._stages:
|
||||||
start_list, end_list = self._find_events(self._clear_stage, times, df)
|
start_list, _ = self._find_events(self._clear_stage, times, df)
|
||||||
point_quantity = len(start_list)
|
point_quantity = len(start_list)
|
||||||
if point_quantity == 0:
|
if point_quantity == 0:
|
||||||
logger.error(f"_generate_events - Не найдены события для этапа '{self._clear_stage}'.")
|
logger.error(f"_generate_events - Не найдены события для этапа '{self._clear_stage}'.")
|
||||||
return {}, 0
|
return {}, 0
|
||||||
for stage in self._stages:
|
for stage in self._stages:
|
||||||
s_list, e_list = self._find_events(stage, times, df)
|
s_list, e_list = self._find_events(stage, times, df)
|
||||||
temp = min(len(s_list), len(e_list))
|
temp = max(len(s_list), len(e_list))
|
||||||
if temp < point_quantity:
|
if temp < point_quantity:
|
||||||
logger.warning(f"_generate_events - Недостаточное количество событий для этапа '{stage}'. "
|
logger.warning(f"_generate_events - Недостаточное количество событий для этапа '{stage}'. "
|
||||||
f"Ожидается {point_quantity}, получено {temp}. Заполнение нулями/единицами.")
|
f"Ожидается {point_quantity}, получено {temp}.")
|
||||||
s_list += [0] * (point_quantity - temp)
|
|
||||||
e_list += [1] * (point_quantity - temp)
|
|
||||||
events[stage] = [s_list, e_list]
|
events[stage] = [s_list, e_list]
|
||||||
|
|
||||||
|
if self._settings.system["performance_mode"][0] == 'client':
|
||||||
|
client_rob = self._settings.operator["time_robot_movement"]
|
||||||
|
move_start = events["Oncomming"][0]
|
||||||
|
if len(move_start) > len(client_rob):
|
||||||
|
move_start = move_start[1:]
|
||||||
|
events["Oncomming"] = [move_start, [client_rob[i]+ move_start[i] for i in range(len(move_start))]]
|
||||||
return (events, point_quantity)
|
return (events, point_quantity)
|
||||||
|
|
||||||
def _build_ideal_data(self, idealDataBuilder: Optional[BaseIdealDataBuilder] = None,
|
def _build_ideal_data(self, idealDataBuilder: Optional[BaseIdealDataBuilder] = None,
|
||||||
@ -217,8 +227,7 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if df is not None and set(self._stages).issubset(set(df.columns.tolist())):
|
if df is not None and set(self._stages).issubset(set(df.columns.tolist())):
|
||||||
events, _ = self._generate_events(df["time"], df)
|
events, point_quantity = self._generate_events(df["time"], df)
|
||||||
point_quantity = len(events["Welding"][0])
|
|
||||||
if point_quantity == 0:
|
if point_quantity == 0:
|
||||||
logger.error("_build_from_df_only - Не найдено ни одного события в DataFrame.")
|
logger.error("_build_from_df_only - Не найдено ни одного события в DataFrame.")
|
||||||
return None
|
return None
|
||||||
@ -250,11 +259,8 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
system_settings = {key: value[0] for key, value in self._settings.system.items()}
|
system_settings = {key: value[0] for key, value in self._settings.system.items()}
|
||||||
graphic_passport = GraphicPassport(
|
graphic_passport = GraphicPassport()
|
||||||
df,
|
graphic_passport.dataframe = df
|
||||||
[],
|
|
||||||
self._form_graphic_useful_data(system_settings)
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in range(point_quantity):
|
for i in range(point_quantity):
|
||||||
point_settings = Settings(self._get_operator_settings_part(i), system_settings)
|
point_settings = Settings(self._get_operator_settings_part(i), system_settings)
|
||||||
@ -265,12 +271,13 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
useful_data = self._form_point_useful_data(point_settings.operator)
|
useful_data = self._form_point_useful_data(point_settings.operator)
|
||||||
point_passport = PointPassport(timeframe, po_events, ideal_data, useful_data)
|
point_passport = PointPassport(timeframe, po_events, ideal_data, useful_data)
|
||||||
graphic_passport.points_pocket.append(point_passport)
|
graphic_passport.points_pocket.append(point_passport)
|
||||||
|
graphic_passport.useful_data = self._form_graphic_useful_data(system_settings, graphic_passport.points_pocket)
|
||||||
return graphic_passport
|
return graphic_passport
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"_form_graphic_passport - Ошибка при формировании графического паспорта: {e}")
|
logger.error(f"_form_graphic_passport - Ошибка при формировании графического паспорта: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _form_graphic_useful_data(self, system_settings: Dict) -> UsefulGraphData:
|
def _form_graphic_useful_data(self, system_settings: Dict, points:List[PointPassport]) -> UsefulGraphData:
|
||||||
"""
|
"""
|
||||||
Формирует словарь полезных данных для графического паспорта.
|
Формирует словарь полезных данных для графического паспорта.
|
||||||
|
|
||||||
@ -278,9 +285,23 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
:return: Объект UsefulGraphData.
|
:return: Объект UsefulGraphData.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
tesla_time = sum(self._settings.operator.get("Tesla summary time", []))
|
performance_data = PerformanceData()
|
||||||
|
if points[0].timeframe is not None and points[0].events is not None:
|
||||||
|
client_time = system_settings["client_time"]
|
||||||
|
ideal_time = sum([sum(point.ideal_data["Ideal timings"])for point in points])
|
||||||
|
TWC_time = (
|
||||||
|
system_settings["time_before_start"] +
|
||||||
|
points[0].timeframe[1] - points[0].events["Closing"][0] +
|
||||||
|
sum([sum([item[1]-item[0] for key, item in point.events.items()]) for point in points[1:-1]]) +
|
||||||
|
points[-1].events["Relief"][1] - points[-1].timeframe[0] +
|
||||||
|
system_settings["time_after_end"]
|
||||||
|
)
|
||||||
|
performance_data.client_to_TWC = round((1 - TWC_time / client_time) * 100, 2) if client_time else 0.0
|
||||||
|
performance_data.client_to_ideal = round((1 - ideal_time / client_time) * 100, 2) if client_time else 0.0
|
||||||
|
performance_data.TWC_to_ideal = round((ideal_time / TWC_time) * 100, 2) if TWC_time else 0.0
|
||||||
|
|
||||||
useful_data = UsefulGraphData(
|
useful_data = UsefulGraphData(
|
||||||
tesla_time,
|
performance_data,
|
||||||
system_settings["Range ME, mm"],
|
system_settings["Range ME, mm"],
|
||||||
system_settings["k_hardness_1"]
|
system_settings["k_hardness_1"]
|
||||||
)
|
)
|
||||||
@ -360,11 +381,27 @@ class PassportFormer(BasePointPassportFormer):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
timeframe, point_events = None, None
|
timeframe, point_events = None, None
|
||||||
|
start, end = 0, 0
|
||||||
if events is not None:
|
if events is not None:
|
||||||
# Если первое событие основного этапа начинается с 0, сдвигаем индекс
|
# Если первое событие основного этапа начинается с 0, сдвигаем индекс
|
||||||
idx_shift = idx + 1 if events[self._stages[-1]][0][0] == 0 else idx
|
start = events[self._stages[0]][0][idx]
|
||||||
timeframe = [events[self._stages[0]][0][idx], events[self._stages[-1]][1][idx_shift]]
|
end = 0
|
||||||
point_events = {key: [value[0][idx], value[1][idx]] for key, value in events.items()}
|
point_events = {}
|
||||||
|
for key, value in events.items():
|
||||||
|
if len(value[0]) > idx and value[0][idx] >= start:
|
||||||
|
point_events[key] = [value[0][idx], value[1][idx]]
|
||||||
|
if value[1][idx] > end: end = value[1][idx]
|
||||||
|
else:
|
||||||
|
logger.warning(f"_form_point_events - Обнаружен аномальный порядок событий для точки {idx}")
|
||||||
|
for i in range(len(value[0])):
|
||||||
|
if value[0][i] >= start:
|
||||||
|
logger.info(f"_form_point_events - Найдено событе (вхождение {i}), соответствующее временным рамкам точки")
|
||||||
|
point_events[key] = [value[0][i], value[1][i]]
|
||||||
|
if value[1][i] > end: end = value[1][i]
|
||||||
|
break
|
||||||
|
point_events[key] = [end, end+0.01]
|
||||||
|
end += 0.01
|
||||||
|
if end != 0: timeframe = [start, end]
|
||||||
return timeframe, point_events
|
return timeframe, point_events
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"_form_point_events - Ошибка при формировании событий для точки {idx}: {e}")
|
logger.error(f"_form_point_events - Ошибка при формировании событий для точки {idx}: {e}")
|
||||||
|
|||||||
@ -16,27 +16,12 @@ import numpy as np
|
|||||||
|
|
||||||
from base.base import (
|
from base.base import (
|
||||||
BasePlotWidget, GraphicPassport, PlotItems, PointPassport,
|
BasePlotWidget, GraphicPassport, PlotItems, PointPassport,
|
||||||
UsefulGraphData, BaseController
|
UsefulGraphData, BaseController, PerformanceData
|
||||||
)
|
)
|
||||||
from utils.json_tools import read_json
|
from utils.json_tools import read_json
|
||||||
from utils import qt_settings as qts
|
from utils import qt_settings as qts
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Дата-класс для хранения временных характеристик канала
|
|
||||||
# =============================================================================
|
|
||||||
@dataclass
|
|
||||||
class ChannelTimings:
|
|
||||||
shift: float = 0
|
|
||||||
TWC_time: float = 0.0
|
|
||||||
ideal_time: float = 0.0
|
|
||||||
client_time: float = 0.0
|
|
||||||
TWC_start: float = 0.0
|
|
||||||
TWC_end: float = 0.0
|
|
||||||
worst_performance: float = 2
|
|
||||||
worst_timeframe: List[float] = field(default_factory=lambda: [0, 0])
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Класс PlotWidget – построение графических виджетов на основе графических паспортов
|
# Класс PlotWidget – построение графических виджетов на основе графических паспортов
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@ -137,29 +122,25 @@ class PlotWidget(BasePlotWidget):
|
|||||||
plot_layout.addItem(plot_item)
|
plot_layout.addItem(plot_item)
|
||||||
return plot_layout
|
return plot_layout
|
||||||
|
|
||||||
def _build_performance_label(self, timings: ChannelTimings, qt_items: Dict) -> QWidget:
|
def _build_performance_label(self, performance:PerformanceData, qt_items: Dict) -> QWidget:
|
||||||
"""
|
"""
|
||||||
Создает QLabel с информацией о производительности (сокращение длительности, идеальное значение, КДИП).
|
Создает QLabel с информацией о производительности (сокращение длительности, идеальное значение, КДИП).
|
||||||
|
|
||||||
:param timings: Объект ChannelTimings с рассчитанными временными характеристиками.
|
|
||||||
:param qt_items: Словарь для сохранения ссылок на созданные виджеты.
|
:param qt_items: Словарь для сохранения ссылок на созданные виджеты.
|
||||||
:return: Виджет с меткой производительности.
|
:return: Виджет с меткой производительности.
|
||||||
"""
|
"""
|
||||||
tesla_TWC = round((1 - timings.TWC_time / timings.client_time) * 100, 2) if timings.client_time else 0.0
|
|
||||||
tesla_ideal = round((1 - timings.ideal_time / timings.client_time) * 100, 2) if timings.client_time else 0.0
|
|
||||||
TWC_ideal = round((timings.ideal_time / timings.TWC_time) * 100, 2) if timings.TWC_time else 0.0
|
|
||||||
|
|
||||||
label_widget = QWidget()
|
label_widget = QWidget()
|
||||||
label_layout = QHBoxLayout(label_widget)
|
label_layout = QHBoxLayout(label_widget)
|
||||||
start_label = QLabel("Сокращение длительности: ")
|
start_label = QLabel("Сокращение длительности: ")
|
||||||
real_label = QLabel(f"фактическое = {tesla_TWC} % ")
|
real_label = QLabel(f"фактическое = {performance.client_to_TWC} % ")
|
||||||
if not tesla_TWC or not timings.TWC_time:
|
if performance.client_to_TWC == 0:
|
||||||
real_label.setVisible(False)
|
real_label.setVisible(False)
|
||||||
ideal_label = QLabel(f"идеальное = {tesla_ideal} % ")
|
ideal_label = QLabel(f"идеальное = {performance.client_to_ideal} % ")
|
||||||
if not tesla_ideal:
|
if performance.client_to_ideal == 0:
|
||||||
ideal_label.setVisible(False)
|
ideal_label.setVisible(False)
|
||||||
kdip_label = QLabel(f"КДИП = {TWC_ideal}% ")
|
kdip_label = QLabel(f"КДИП = {performance.TWC_to_ideal}% ")
|
||||||
if not TWC_ideal:
|
if performance.TWC_to_ideal == 0:
|
||||||
kdip_label.setVisible(False)
|
kdip_label.setVisible(False)
|
||||||
|
|
||||||
label_layout.addWidget(start_label, alignment=Qt.AlignLeft)
|
label_layout.addWidget(start_label, alignment=Qt.AlignLeft)
|
||||||
@ -190,9 +171,9 @@ class PlotWidget(BasePlotWidget):
|
|||||||
plot_layout = CustomPlotLayout(graphic_passport, len(self._plt_channels), self._stage_colors, self)
|
plot_layout = CustomPlotLayout(graphic_passport, len(self._plt_channels), self._stage_colors, self)
|
||||||
plot_layout.build(pyqt_container, self._plt_channels)
|
plot_layout.build(pyqt_container, self._plt_channels)
|
||||||
|
|
||||||
if plot_layout.property("performance"):
|
perf_widget = self._build_performance_label(graphic_passport.useful_data.performance, pyqt_container.qt_items)
|
||||||
perf_widget = self._build_performance_label(plot_layout.property("performance"), pyqt_container.qt_items)
|
|
||||||
container_layout.addWidget(perf_widget)
|
container_layout.addWidget(perf_widget)
|
||||||
|
|
||||||
container_layout.addWidget(plot_layout)
|
container_layout.addWidget(plot_layout)
|
||||||
container_widget.setProperty("pyqt_container", pyqt_container)
|
container_widget.setProperty("pyqt_container", pyqt_container)
|
||||||
return container_widget
|
return container_widget
|
||||||
@ -258,19 +239,13 @@ class CustomPlotLayout(pg.GraphicsLayoutWidget):
|
|||||||
"""
|
"""
|
||||||
main_plot = None
|
main_plot = None
|
||||||
for widget_num, (channel, description) in enumerate(plt_channels.items()):
|
for widget_num, (channel, description) in enumerate(plt_channels.items()):
|
||||||
plot_item, plot_timings = self._plotter.generate_plot_item(widget_num, channel, description, pyqt_container)
|
plot_item = self._plotter.generate_plot_item(widget_num, channel, description, pyqt_container)
|
||||||
if widget_num == 0:
|
if widget_num == 0:
|
||||||
main_plot = plot_item
|
main_plot = plot_item
|
||||||
else:
|
else:
|
||||||
plot_item.setXLink(main_plot)
|
plot_item.setXLink(main_plot)
|
||||||
if description["Settings"].get("performance", False):
|
|
||||||
self.setProperty("performance", plot_timings)
|
|
||||||
self.addItem(plot_item, widget_num, 0)
|
self.addItem(plot_item, widget_num, 0)
|
||||||
# Если задана производительность, получаем объект ChannelTimings
|
navigator = NavigatorPlot([0, 10], main_plot)
|
||||||
timings = ChannelTimings()
|
|
||||||
if self.property('performance'):
|
|
||||||
timings = self.property('performance')
|
|
||||||
navigator = NavigatorPlot(timings.worst_timeframe, main_plot)
|
|
||||||
if navigator is not None:
|
if navigator is not None:
|
||||||
self.addItem(navigator, widget_num + 1, 0)
|
self.addItem(navigator, widget_num + 1, 0)
|
||||||
|
|
||||||
@ -309,7 +284,7 @@ class PlotItemGenerator:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def generate_plot_item(self, widget_num: int, channel: str, description: Dict[str, Any],
|
def generate_plot_item(self, widget_num: int, channel: str, description: Dict[str, Any],
|
||||||
pyqt_container: PlotItems) -> Tuple[pg.PlotItem, ChannelTimings]:
|
pyqt_container: PlotItems) -> pg.PlotItem:
|
||||||
"""
|
"""
|
||||||
Генерирует PlotItem для заданного канала с добавлением регионов, компенсаций и идеальных данных.
|
Генерирует PlotItem для заданного канала с добавлением регионов, компенсаций и идеальных данных.
|
||||||
|
|
||||||
@ -317,11 +292,11 @@ class PlotItemGenerator:
|
|||||||
:param channel: Имя канала.
|
:param channel: Имя канала.
|
||||||
:param description: Словарь настроек для данного канала.
|
:param description: Словарь настроек для данного канала.
|
||||||
:param pyqt_container: Контейнер для хранения ссылок на объекты графиков.
|
:param pyqt_container: Контейнер для хранения ссылок на объекты графиков.
|
||||||
:return: Кортеж из созданного PlotItem и объекта ChannelTimings с рассчитанными параметрами.
|
:return: PlotItem
|
||||||
"""
|
"""
|
||||||
dp = self._datapack
|
dp = self._datapack
|
||||||
dataframe = dp["dataframe"]
|
dataframe = dp["dataframe"]
|
||||||
useful_data = dp["useful_data"]
|
useful_data:UsefulGraphData = dp["useful_data"]
|
||||||
points_pocket = dp["points_pocket"]
|
points_pocket = dp["points_pocket"]
|
||||||
widget_steps = dp["widget_steps"]
|
widget_steps = dp["widget_steps"]
|
||||||
point_steps = dp["point_steps"]
|
point_steps = dp["point_steps"]
|
||||||
@ -329,8 +304,7 @@ class PlotItemGenerator:
|
|||||||
# Инициализируем PlotItem и легенду
|
# Инициализируем PlotItem и легенду
|
||||||
plot_item, legend = self._init_plot_item(title=channel)
|
plot_item, legend = self._init_plot_item(title=channel)
|
||||||
settings: Dict = description["Settings"]
|
settings: Dict = description["Settings"]
|
||||||
timings = ChannelTimings()
|
global_shift = 0
|
||||||
timings.client_time = useful_data.client_time
|
|
||||||
ideal_df = pd.DataFrame({})
|
ideal_df = pd.DataFrame({})
|
||||||
|
|
||||||
# При необходимости – зеркальное отражение данных для ME
|
# При необходимости – зеркальное отражение данных для ME
|
||||||
@ -341,10 +315,8 @@ class PlotItemGenerator:
|
|||||||
# Итерация по точкам паспорта
|
# Итерация по точкам паспорта
|
||||||
for cur_point, point_data in enumerate(points_pocket):
|
for cur_point, point_data in enumerate(points_pocket):
|
||||||
ideal_data = copy.deepcopy(point_data.ideal_data)
|
ideal_data = copy.deepcopy(point_data.ideal_data)
|
||||||
is_last = (cur_point == len(points_pocket) - 1)
|
|
||||||
is_first = (cur_point == 0)
|
|
||||||
if self._ideal_mode:
|
if self._ideal_mode:
|
||||||
timings, point_data.events, point_data.timeframe = self._generate_synthetic_events(timings, ideal_data)
|
global_shift, point_data.events, point_data.timeframe = self._generate_synthetic_events(global_shift, ideal_data)
|
||||||
else:
|
else:
|
||||||
if settings.get("force compensation FE", False):
|
if settings.get("force compensation FE", False):
|
||||||
force = point_data.useful_data["force"]
|
force = point_data.useful_data["force"]
|
||||||
@ -366,8 +338,6 @@ class PlotItemGenerator:
|
|||||||
self._add_ideal_stage_regions(self._stage_colors, plot_item, ideal_data, point_data.events,
|
self._add_ideal_stage_regions(self._stage_colors, plot_item, ideal_data, point_data.events,
|
||||||
pyqt_container.regions, 100)
|
pyqt_container.regions, 100)
|
||||||
ideal_df = self._modify_ideal_df(ideal_df, ideal_data, point_data.events)
|
ideal_df = self._modify_ideal_df(ideal_df, ideal_data, point_data.events)
|
||||||
if settings.get("performance", False):
|
|
||||||
timings = self._calc_performance(timings, point_data, ideal_data, is_first, is_last)
|
|
||||||
# Обновляем статус через родительский PlotWidget
|
# Обновляем статус через родительский PlotWidget
|
||||||
self._parent._update_status(widget_steps, point_steps, widget_num, cur_point)
|
self._parent._update_status(widget_steps, point_steps, widget_num, cur_point)
|
||||||
|
|
||||||
@ -377,7 +347,7 @@ class PlotItemGenerator:
|
|||||||
# Добавляем реальные сигналы, если не включен режим идеала
|
# Добавляем реальные сигналы, если не включен режим идеала
|
||||||
if not self._ideal_mode:
|
if not self._ideal_mode:
|
||||||
self._add_signals(plot_item, dataframe, description["Real_signals"], legend, pyqt_container.curves["real"])
|
self._add_signals(plot_item, dataframe, description["Real_signals"], legend, pyqt_container.curves["real"])
|
||||||
return plot_item, timings
|
return plot_item
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _shift_data(valid_str: str, signals: List[Dict], dataframe: pd.DataFrame, func: Callable) -> pd.DataFrame:
|
def _shift_data(valid_str: str, signals: List[Dict], dataframe: pd.DataFrame, func: Callable) -> pd.DataFrame:
|
||||||
@ -579,55 +549,25 @@ class PlotItemGenerator:
|
|||||||
rect_item.setPen(pg.mkPen('black', width=3))
|
rect_item.setPen(pg.mkPen('black', width=3))
|
||||||
plot_item.addItem(rect_item)
|
plot_item.addItem(rect_item)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _calc_performance(timings: ChannelTimings, point_data: PointPassport, ideal_data: Dict,
|
|
||||||
is_first: bool, is_last: bool) -> ChannelTimings:
|
|
||||||
"""
|
|
||||||
Рассчитывает показатели производительности для текущей точки.
|
|
||||||
|
|
||||||
:param timings: Текущие временные показатели (ChannelTimings).
|
|
||||||
:param point_data: Объект PointPassport для текущей точки.
|
|
||||||
:param ideal_data: Идеальные данные для текущей точки.
|
|
||||||
:param is_first: True, если точка первая.
|
|
||||||
:param is_last: True, если точка последняя.
|
|
||||||
:return: Обновленный объект ChannelTimings.
|
|
||||||
"""
|
|
||||||
if is_first:
|
|
||||||
if not PlotItemGenerator._parent_ideal_mode():
|
|
||||||
timings.TWC_start = point_data.events["Closing"][0]
|
|
||||||
ideal_delta = ideal_data["Ideal cycle"]
|
|
||||||
elif is_last:
|
|
||||||
if not PlotItemGenerator._parent_ideal_mode():
|
|
||||||
timings.TWC_end = point_data.events["Relief"][1]
|
|
||||||
timings.TWC_time = timings.TWC_end - timings.TWC_start
|
|
||||||
timings.worst_timeframe = [timings.TWC_start, timings.TWC_end]
|
|
||||||
ideal_delta = sum(ideal_data["Ideal timings"][0:3])
|
|
||||||
else:
|
|
||||||
ideal_delta = ideal_data["Ideal cycle"]
|
|
||||||
|
|
||||||
timings.ideal_time += ideal_delta
|
|
||||||
# При желании можно добавить сравнение с текущей производительностью
|
|
||||||
# Если (ideal_delta / TWC_delta) < timings.worst_performance, обновляем worst_performance и worst_timeframe
|
|
||||||
return timings
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_synthetic_events(timings: ChannelTimings, ideal_data: Dict) -> Tuple[ChannelTimings, Dict, List[float]]:
|
def _generate_synthetic_events(global_shift:float, ideal_data: Dict) -> Tuple[float, Dict, List[float]]:
|
||||||
"""
|
"""
|
||||||
Генерирует синтетические события для случая, когда данные отсутствуют.
|
Генерирует синтетические события для случая, когда данные отсутствуют.
|
||||||
|
|
||||||
:param timings: Объект ChannelTimings.
|
:param global_shift: Смещение точки по линии времени относительно нуля.
|
||||||
:param ideal_data: Словарь с идеальными данными, содержащий "Ideal cycle" и "Ideal timings".
|
:param ideal_data: Словарь с идеальными данными, содержащий "Ideal cycle" и "Ideal timings".
|
||||||
:return: Кортеж (обновленные timings, сгенерированные события, временной интервал точки).
|
:return: Кортеж (обновленные global_shift, сгенерированные события, временной интервал точки).
|
||||||
"""
|
"""
|
||||||
point_timeframe = [timings.shift, timings.shift + ideal_data["Ideal cycle"]]
|
point_timeframe = [global_shift, global_shift + ideal_data["Ideal cycle"]]
|
||||||
point_events = {}
|
point_events = {}
|
||||||
keys = list(ideal_data.keys())
|
keys = list(ideal_data.keys())
|
||||||
shift = 0
|
shift = 0
|
||||||
for i, time in enumerate(ideal_data["Ideal timings"]):
|
for i, time in enumerate(ideal_data["Ideal timings"]):
|
||||||
point_events[keys[i]] = [timings.shift + shift, timings.shift + time + shift]
|
point_events[keys[i]] = [global_shift + shift, global_shift + time + shift]
|
||||||
shift += time
|
shift += time
|
||||||
timings.shift += ideal_data["Ideal cycle"]
|
global_shift += ideal_data["Ideal cycle"]
|
||||||
return timings, point_events, point_timeframe
|
return global_shift, point_events, point_timeframe
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _apply_force_compensation(force: float, k_hardness: float, dataframe: pd.DataFrame,
|
def _apply_force_compensation(force: float, k_hardness: float, dataframe: pd.DataFrame,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from typing import Callable, Optional, Any
|
|||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QPushButton, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel,
|
QWidget, QPushButton, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel,
|
||||||
QTableWidget, QTableWidgetItem, QStyledItemDelegate
|
QTableWidget, QTableWidgetItem, QStyledItemDelegate, QRadioButton, QGroupBox, QDoubleSpinBox
|
||||||
)
|
)
|
||||||
from PyQt5.QtGui import QIntValidator, QDoubleValidator
|
from PyQt5.QtGui import QIntValidator, QDoubleValidator
|
||||||
|
|
||||||
@ -33,8 +33,9 @@ class SettingsWindow(QWidget):
|
|||||||
self._num_points: Optional[QLineEdit] = None
|
self._num_points: Optional[QLineEdit] = None
|
||||||
self._param_table: Optional[QTableWidget] = None
|
self._param_table: Optional[QTableWidget] = None
|
||||||
|
|
||||||
self.load_settings()
|
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
|
self.load_settings()
|
||||||
|
self._populate_table()
|
||||||
|
|
||||||
def load_settings(self) -> None:
|
def load_settings(self) -> None:
|
||||||
"""Загружает настройки из JSON-файла."""
|
"""Загружает настройки из JSON-файла."""
|
||||||
@ -75,7 +76,7 @@ class SettingsWindow(QWidget):
|
|||||||
|
|
||||||
# Таблица для отображения параметров
|
# Таблица для отображения параметров
|
||||||
self._param_table = QTableWidget()
|
self._param_table = QTableWidget()
|
||||||
self._populate_table()
|
|
||||||
|
|
||||||
# Основной вертикальный макет
|
# Основной вертикальный макет
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
@ -128,7 +129,7 @@ class SettingsWindow(QWidget):
|
|||||||
else:
|
else:
|
||||||
self._param_table.setItemDelegateForRow(i, str_delegate)
|
self._param_table.setItemDelegateForRow(i, str_delegate)
|
||||||
|
|
||||||
def _save(self) -> None:
|
def _save_data(self) -> None:
|
||||||
"""
|
"""
|
||||||
Сохраняет текущие параметры из таблицы в self._data,
|
Сохраняет текущие параметры из таблицы в self._data,
|
||||||
записывает их в JSON-файл и вызывает функцию обновления.
|
записывает их в JSON-файл и вызывает функцию обновления.
|
||||||
@ -154,9 +155,15 @@ class SettingsWindow(QWidget):
|
|||||||
new_data[key] = row_data
|
new_data[key] = row_data
|
||||||
|
|
||||||
self._data = new_data
|
self._data = new_data
|
||||||
|
|
||||||
|
def _push_data (self) -> None:
|
||||||
self.write_settings()
|
self.write_settings()
|
||||||
self._upd_func()
|
self._upd_func()
|
||||||
|
|
||||||
|
def _save(self) -> None:
|
||||||
|
self._save_data()
|
||||||
|
self._push_data()
|
||||||
|
|
||||||
def _restore(self) -> None:
|
def _restore(self) -> None:
|
||||||
"""Перезагружает данные из файла и обновляет таблицу."""
|
"""Перезагружает данные из файла и обновляет таблицу."""
|
||||||
self.load_settings()
|
self.load_settings()
|
||||||
@ -238,11 +245,98 @@ class SystemSettings(SettingsWindow):
|
|||||||
super().__init__(path, name, upd_func, associated_names)
|
super().__init__(path, name, upd_func, associated_names)
|
||||||
self._num_points.setVisible(False)
|
self._num_points.setVisible(False)
|
||||||
|
|
||||||
def _expand(self):
|
def _init_ui(self) -> None:
|
||||||
|
super()._init_ui()
|
||||||
|
|
||||||
|
performance_layout = QVBoxLayout()
|
||||||
|
self.btn_trace_movement = QRadioButton()
|
||||||
|
self.btn_trace_movement.setText("Use robot movement time from trace (auto set)")
|
||||||
|
self.btn_trace_movement.setChecked(True)
|
||||||
|
|
||||||
|
self.btn_client_movement = QRadioButton()
|
||||||
|
self.btn_client_movement.setText("Use robot movement time from client (manual set)")
|
||||||
|
|
||||||
|
label_time_before_start = QLabel("Additional time before first Squeeze, ms")
|
||||||
|
self.spin_time_before_start = QDoubleSpinBox()
|
||||||
|
self.spin_time_before_start.setRange(0, 100000.000)
|
||||||
|
self.spin_time_before_start.setValue(0)
|
||||||
|
|
||||||
|
label_time_after_end = QLabel("Additional time after last Relief, ms")
|
||||||
|
self.spin_time_after_end = QDoubleSpinBox()
|
||||||
|
self.spin_time_after_end.setRange(0, 100000.000)
|
||||||
|
self.spin_time_after_end.setValue(0)
|
||||||
|
|
||||||
|
label_client_time = QLabel("Client full time, ms")
|
||||||
|
self.spin_client_time = QDoubleSpinBox()
|
||||||
|
self.spin_client_time.setRange(0, 100000.000)
|
||||||
|
self.spin_client_time.setValue(0)
|
||||||
|
|
||||||
|
widget_time_before_start = QWidget()
|
||||||
|
layout_time_before_start = QHBoxLayout(widget_time_before_start)
|
||||||
|
layout_time_before_start.addWidget(label_time_before_start)
|
||||||
|
layout_time_before_start.addWidget(self.spin_time_before_start)
|
||||||
|
|
||||||
|
widget_time_after_end = QWidget()
|
||||||
|
layout_time_after_end = QHBoxLayout(widget_time_after_end)
|
||||||
|
layout_time_after_end.addWidget(label_time_after_end)
|
||||||
|
layout_time_after_end.addWidget(self.spin_time_after_end)
|
||||||
|
|
||||||
|
widget_client_time = QWidget()
|
||||||
|
layout_client_time = QHBoxLayout(widget_client_time)
|
||||||
|
layout_client_time.addWidget(label_client_time)
|
||||||
|
layout_client_time.addWidget(self.spin_client_time)
|
||||||
|
|
||||||
|
performance_layout.addWidget(self.btn_trace_movement)
|
||||||
|
performance_layout.addWidget(self.btn_client_movement)
|
||||||
|
performance_layout.addWidget(widget_time_before_start)
|
||||||
|
performance_layout.addWidget(widget_time_after_end)
|
||||||
|
performance_layout.addWidget(widget_client_time)
|
||||||
|
|
||||||
|
performance_box = QGroupBox()
|
||||||
|
performance_box.setTitle("Performance settings")
|
||||||
|
performance_box.setLayout(performance_layout)
|
||||||
|
|
||||||
|
self.layout().addWidget(performance_box)
|
||||||
|
|
||||||
|
def _add_performance_mode(self) -> None:
|
||||||
|
if self.btn_trace_movement.isChecked():
|
||||||
|
mode = 'trace'
|
||||||
|
elif self.btn_client_movement.isChecked():
|
||||||
|
mode = 'client'
|
||||||
|
else:
|
||||||
|
mode = None
|
||||||
|
self._data["performance_mode"] = [mode]
|
||||||
|
self._data["time_before_start"] = [self.spin_time_before_start.value()/1000]
|
||||||
|
self._data["time_after_end"] = [self.spin_time_after_end.value()/1000]
|
||||||
|
self._data["client_time"] = [self.spin_client_time.value()/1000]
|
||||||
|
|
||||||
|
def _save_data(self) -> None:
|
||||||
|
super()._save_data()
|
||||||
|
self._add_performance_mode()
|
||||||
|
|
||||||
|
def _load_performance_settings(self) -> None:
|
||||||
|
mode = self._data["performance_mode"][0]
|
||||||
|
if mode == 'trace':
|
||||||
|
self.btn_trace_movement.setChecked(True)
|
||||||
|
elif mode == 'client':
|
||||||
|
self.btn_client_movement.setChecked(True)
|
||||||
|
self.spin_time_before_start.setValue(self._data["time_before_start"][0]*1000)
|
||||||
|
self.spin_time_after_end.setValue(self._data["time_after_end"][0]*1000)
|
||||||
|
self.spin_client_time.setValue(self._data["client_time"][0]*1000)
|
||||||
|
|
||||||
|
def load_settings(self) -> None:
|
||||||
|
super().load_settings()
|
||||||
|
try:
|
||||||
|
self._load_performance_settings()
|
||||||
|
except KeyError:
|
||||||
|
self._add_performance_mode()
|
||||||
|
|
||||||
|
def _expand(self) -> None:
|
||||||
# Для системных настроек расширение столбцов не требуется.
|
# Для системных настроек расширение столбцов не требуется.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class OperatorSettings(SettingsWindow):
|
class OperatorSettings(SettingsWindow):
|
||||||
"""
|
"""
|
||||||
Настройки оператора.
|
Настройки оператора.
|
||||||
@ -274,6 +368,10 @@ class OperatorSettings(SettingsWindow):
|
|||||||
super().__init__(path, name, upd_func, associated_names)
|
super().__init__(path, name, upd_func, associated_names)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FilterSettings(SettingsWindow):
|
class FilterSettings(SettingsWindow):
|
||||||
"""
|
"""
|
||||||
Настройки фильтра.
|
Настройки фильтра.
|
||||||
|
|||||||
13938
trace_samples/point_40000_2025_02_07-10_47_30.csv
Normal file
13938
trace_samples/point_40000_2025_02_07-10_47_30.csv
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user