dev: добавлена обработка ошибок записи трейсов
This commit is contained in:
parent
78d34343d5
commit
47248ac6a7
@ -222,85 +222,5 @@
|
||||
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": [
|
||||
"D:/downloads/a22"
|
||||
"/home/andrew/weldingspotperformance/trace_samples"
|
||||
],
|
||||
"monitor_update_period": [
|
||||
1000.0
|
||||
@ -58,5 +58,17 @@
|
||||
],
|
||||
"Range ME, mm": [
|
||||
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)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PerformanceData:
|
||||
client_to_TWC: float = 0
|
||||
client_to_ideal: float = 0
|
||||
TWC_to_ideal: float = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class UsefulGraphData:
|
||||
"""
|
||||
@ -72,11 +79,12 @@ class UsefulGraphData:
|
||||
:param range_ME: Диапазон для ME.
|
||||
:param k_hardness: Коэффициент твердости.
|
||||
"""
|
||||
client_time: float = 0
|
||||
performance: PerformanceData = PerformanceData()
|
||||
range_ME: float = 0
|
||||
k_hardness: float = 0
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class GraphicPassport:
|
||||
"""
|
||||
@ -471,6 +479,7 @@ class BaseIdealDataBuilder(OptAlgorithm):
|
||||
def __init__(self, settings: Settings):
|
||||
self.mul = settings.system['time_capture']
|
||||
self.welding_time = settings.operator['time_wielding']
|
||||
self._settings = settings
|
||||
super().__init__(settings.system, settings.operator)
|
||||
|
||||
def get_closingDF(self) -> pd.DataFrame:
|
||||
|
||||
@ -9,7 +9,8 @@ from base.base import (
|
||||
PointPassport,
|
||||
GraphicPassport,
|
||||
Settings,
|
||||
UsefulGraphData
|
||||
UsefulGraphData,
|
||||
PerformanceData
|
||||
)
|
||||
|
||||
|
||||
@ -115,12 +116,16 @@ class PassportFormer(BasePointPassportFormer):
|
||||
:return: Кортеж из двух списков: (список времён начала, список времён окончания).
|
||||
"""
|
||||
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_list = times.iloc[start_idx].tolist() if len(start_idx) > 0 else []
|
||||
end_list = times.iloc[finish_idx].tolist() if len(finish_idx) > 0 else []
|
||||
if len(start_list) - len(end_list) == 1:
|
||||
end_list.append(float(times.iloc[-1]))
|
||||
if start_idx[-1] > finish_idx[-1]:
|
||||
logger.debug(f"_find_events - не найден конец события {signal} с началом в {times.iloc[start_idx[-1]]} секунд, принято равным {times.iloc[-1]} секунд")
|
||||
finish_idx = np.append(finish_idx, -1)
|
||||
start_list = times.iloc[start_idx].tolist()
|
||||
end_list = times.iloc[finish_idx].tolist()
|
||||
return (start_list, end_list)
|
||||
|
||||
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 = {}
|
||||
point_quantity = 0
|
||||
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)
|
||||
if point_quantity == 0:
|
||||
logger.error(f"_generate_events - Не найдены события для этапа '{self._clear_stage}'.")
|
||||
return {}, 0
|
||||
for stage in self._stages:
|
||||
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:
|
||||
logger.warning(f"_generate_events - Недостаточное количество событий для этапа '{stage}'. "
|
||||
f"Ожидается {point_quantity}, получено {temp}. Заполнение нулями/единицами.")
|
||||
s_list += [0] * (point_quantity - temp)
|
||||
e_list += [1] * (point_quantity - temp)
|
||||
f"Ожидается {point_quantity}, получено {temp}.")
|
||||
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)
|
||||
|
||||
def _build_ideal_data(self, idealDataBuilder: Optional[BaseIdealDataBuilder] = None,
|
||||
@ -217,8 +227,7 @@ class PassportFormer(BasePointPassportFormer):
|
||||
"""
|
||||
try:
|
||||
if df is not None and set(self._stages).issubset(set(df.columns.tolist())):
|
||||
events, _ = self._generate_events(df["time"], df)
|
||||
point_quantity = len(events["Welding"][0])
|
||||
events, point_quantity = self._generate_events(df["time"], df)
|
||||
if point_quantity == 0:
|
||||
logger.error("_build_from_df_only - Не найдено ни одного события в DataFrame.")
|
||||
return None
|
||||
@ -250,11 +259,8 @@ class PassportFormer(BasePointPassportFormer):
|
||||
"""
|
||||
try:
|
||||
system_settings = {key: value[0] for key, value in self._settings.system.items()}
|
||||
graphic_passport = GraphicPassport(
|
||||
df,
|
||||
[],
|
||||
self._form_graphic_useful_data(system_settings)
|
||||
)
|
||||
graphic_passport = GraphicPassport()
|
||||
graphic_passport.dataframe = df
|
||||
|
||||
for i in range(point_quantity):
|
||||
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)
|
||||
point_passport = PointPassport(timeframe, po_events, ideal_data, useful_data)
|
||||
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
|
||||
except Exception as e:
|
||||
logger.error(f"_form_graphic_passport - Ошибка при формировании графического паспорта: {e}")
|
||||
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.
|
||||
"""
|
||||
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(
|
||||
tesla_time,
|
||||
performance_data,
|
||||
system_settings["Range ME, mm"],
|
||||
system_settings["k_hardness_1"]
|
||||
)
|
||||
@ -360,11 +381,27 @@ class PassportFormer(BasePointPassportFormer):
|
||||
"""
|
||||
try:
|
||||
timeframe, point_events = None, None
|
||||
start, end = 0, 0
|
||||
if events is not None:
|
||||
# Если первое событие основного этапа начинается с 0, сдвигаем индекс
|
||||
idx_shift = idx + 1 if events[self._stages[-1]][0][0] == 0 else idx
|
||||
timeframe = [events[self._stages[0]][0][idx], events[self._stages[-1]][1][idx_shift]]
|
||||
point_events = {key: [value[0][idx], value[1][idx]] for key, value in events.items()}
|
||||
start = events[self._stages[0]][0][idx]
|
||||
end = 0
|
||||
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
|
||||
except Exception as e:
|
||||
logger.error(f"_form_point_events - Ошибка при формировании событий для точки {idx}: {e}")
|
||||
|
||||
@ -16,27 +16,12 @@ import numpy as np
|
||||
|
||||
from base.base import (
|
||||
BasePlotWidget, GraphicPassport, PlotItems, PointPassport,
|
||||
UsefulGraphData, BaseController
|
||||
UsefulGraphData, BaseController, PerformanceData
|
||||
)
|
||||
from utils.json_tools import read_json
|
||||
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 – построение графических виджетов на основе графических паспортов
|
||||
# =============================================================================
|
||||
@ -137,29 +122,25 @@ class PlotWidget(BasePlotWidget):
|
||||
plot_layout.addItem(plot_item)
|
||||
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 с информацией о производительности (сокращение длительности, идеальное значение, КДИП).
|
||||
|
||||
:param timings: Объект ChannelTimings с рассчитанными временными характеристиками.
|
||||
:param qt_items: Словарь для сохранения ссылок на созданные виджеты.
|
||||
: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_layout = QHBoxLayout(label_widget)
|
||||
start_label = QLabel("Сокращение длительности: ")
|
||||
real_label = QLabel(f"фактическое = {tesla_TWC} % ")
|
||||
if not tesla_TWC or not timings.TWC_time:
|
||||
real_label = QLabel(f"фактическое = {performance.client_to_TWC} % ")
|
||||
if performance.client_to_TWC == 0:
|
||||
real_label.setVisible(False)
|
||||
ideal_label = QLabel(f"идеальное = {tesla_ideal} % ")
|
||||
if not tesla_ideal:
|
||||
ideal_label = QLabel(f"идеальное = {performance.client_to_ideal} % ")
|
||||
if performance.client_to_ideal == 0:
|
||||
ideal_label.setVisible(False)
|
||||
kdip_label = QLabel(f"КДИП = {TWC_ideal}% ")
|
||||
if not TWC_ideal:
|
||||
kdip_label = QLabel(f"КДИП = {performance.TWC_to_ideal}% ")
|
||||
if performance.TWC_to_ideal == 0:
|
||||
kdip_label.setVisible(False)
|
||||
|
||||
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.build(pyqt_container, self._plt_channels)
|
||||
|
||||
if plot_layout.property("performance"):
|
||||
perf_widget = self._build_performance_label(plot_layout.property("performance"), pyqt_container.qt_items)
|
||||
perf_widget = self._build_performance_label(graphic_passport.useful_data.performance, pyqt_container.qt_items)
|
||||
container_layout.addWidget(perf_widget)
|
||||
|
||||
container_layout.addWidget(plot_layout)
|
||||
container_widget.setProperty("pyqt_container", pyqt_container)
|
||||
return container_widget
|
||||
@ -258,19 +239,13 @@ class CustomPlotLayout(pg.GraphicsLayoutWidget):
|
||||
"""
|
||||
main_plot = None
|
||||
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:
|
||||
main_plot = plot_item
|
||||
else:
|
||||
plot_item.setXLink(main_plot)
|
||||
if description["Settings"].get("performance", False):
|
||||
self.setProperty("performance", plot_timings)
|
||||
self.addItem(plot_item, widget_num, 0)
|
||||
# Если задана производительность, получаем объект ChannelTimings
|
||||
timings = ChannelTimings()
|
||||
if self.property('performance'):
|
||||
timings = self.property('performance')
|
||||
navigator = NavigatorPlot(timings.worst_timeframe, main_plot)
|
||||
navigator = NavigatorPlot([0, 10], main_plot)
|
||||
if navigator is not None:
|
||||
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],
|
||||
pyqt_container: PlotItems) -> Tuple[pg.PlotItem, ChannelTimings]:
|
||||
pyqt_container: PlotItems) -> pg.PlotItem:
|
||||
"""
|
||||
Генерирует PlotItem для заданного канала с добавлением регионов, компенсаций и идеальных данных.
|
||||
|
||||
@ -317,11 +292,11 @@ class PlotItemGenerator:
|
||||
:param channel: Имя канала.
|
||||
:param description: Словарь настроек для данного канала.
|
||||
:param pyqt_container: Контейнер для хранения ссылок на объекты графиков.
|
||||
:return: Кортеж из созданного PlotItem и объекта ChannelTimings с рассчитанными параметрами.
|
||||
:return: PlotItem
|
||||
"""
|
||||
dp = self._datapack
|
||||
dataframe = dp["dataframe"]
|
||||
useful_data = dp["useful_data"]
|
||||
useful_data:UsefulGraphData = dp["useful_data"]
|
||||
points_pocket = dp["points_pocket"]
|
||||
widget_steps = dp["widget_steps"]
|
||||
point_steps = dp["point_steps"]
|
||||
@ -329,8 +304,7 @@ class PlotItemGenerator:
|
||||
# Инициализируем PlotItem и легенду
|
||||
plot_item, legend = self._init_plot_item(title=channel)
|
||||
settings: Dict = description["Settings"]
|
||||
timings = ChannelTimings()
|
||||
timings.client_time = useful_data.client_time
|
||||
global_shift = 0
|
||||
ideal_df = pd.DataFrame({})
|
||||
|
||||
# При необходимости – зеркальное отражение данных для ME
|
||||
@ -341,10 +315,8 @@ class PlotItemGenerator:
|
||||
# Итерация по точкам паспорта
|
||||
for cur_point, point_data in enumerate(points_pocket):
|
||||
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:
|
||||
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:
|
||||
if settings.get("force compensation FE", False):
|
||||
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,
|
||||
pyqt_container.regions, 100)
|
||||
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
|
||||
self._parent._update_status(widget_steps, point_steps, widget_num, cur_point)
|
||||
|
||||
@ -377,7 +347,7 @@ class PlotItemGenerator:
|
||||
# Добавляем реальные сигналы, если не включен режим идеала
|
||||
if not self._ideal_mode:
|
||||
self._add_signals(plot_item, dataframe, description["Real_signals"], legend, pyqt_container.curves["real"])
|
||||
return plot_item, timings
|
||||
return plot_item
|
||||
|
||||
@staticmethod
|
||||
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))
|
||||
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
|
||||
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".
|
||||
: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 = {}
|
||||
keys = list(ideal_data.keys())
|
||||
shift = 0
|
||||
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
|
||||
timings.shift += ideal_data["Ideal cycle"]
|
||||
return timings, point_events, point_timeframe
|
||||
global_shift += ideal_data["Ideal cycle"]
|
||||
return global_shift, point_events, point_timeframe
|
||||
|
||||
@staticmethod
|
||||
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 (
|
||||
QWidget, QPushButton, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel,
|
||||
QTableWidget, QTableWidgetItem, QStyledItemDelegate
|
||||
QTableWidget, QTableWidgetItem, QStyledItemDelegate, QRadioButton, QGroupBox, QDoubleSpinBox
|
||||
)
|
||||
from PyQt5.QtGui import QIntValidator, QDoubleValidator
|
||||
|
||||
@ -33,8 +33,9 @@ class SettingsWindow(QWidget):
|
||||
self._num_points: Optional[QLineEdit] = None
|
||||
self._param_table: Optional[QTableWidget] = None
|
||||
|
||||
self.load_settings()
|
||||
self._init_ui()
|
||||
self.load_settings()
|
||||
self._populate_table()
|
||||
|
||||
def load_settings(self) -> None:
|
||||
"""Загружает настройки из JSON-файла."""
|
||||
@ -75,7 +76,7 @@ class SettingsWindow(QWidget):
|
||||
|
||||
# Таблица для отображения параметров
|
||||
self._param_table = QTableWidget()
|
||||
self._populate_table()
|
||||
|
||||
|
||||
# Основной вертикальный макет
|
||||
layout = QVBoxLayout()
|
||||
@ -128,7 +129,7 @@ class SettingsWindow(QWidget):
|
||||
else:
|
||||
self._param_table.setItemDelegateForRow(i, str_delegate)
|
||||
|
||||
def _save(self) -> None:
|
||||
def _save_data(self) -> None:
|
||||
"""
|
||||
Сохраняет текущие параметры из таблицы в self._data,
|
||||
записывает их в JSON-файл и вызывает функцию обновления.
|
||||
@ -154,9 +155,15 @@ class SettingsWindow(QWidget):
|
||||
new_data[key] = row_data
|
||||
|
||||
self._data = new_data
|
||||
|
||||
def _push_data (self) -> None:
|
||||
self.write_settings()
|
||||
self._upd_func()
|
||||
|
||||
def _save(self) -> None:
|
||||
self._save_data()
|
||||
self._push_data()
|
||||
|
||||
def _restore(self) -> None:
|
||||
"""Перезагружает данные из файла и обновляет таблицу."""
|
||||
self.load_settings()
|
||||
@ -238,11 +245,98 @@ class SystemSettings(SettingsWindow):
|
||||
super().__init__(path, name, upd_func, associated_names)
|
||||
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
|
||||
|
||||
|
||||
|
||||
class OperatorSettings(SettingsWindow):
|
||||
"""
|
||||
Настройки оператора.
|
||||
@ -274,6 +368,10 @@ class OperatorSettings(SettingsWindow):
|
||||
super().__init__(path, name, upd_func, associated_names)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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