chore: изменен модуль roboter
This commit is contained in:
parent
dea6d9899c
commit
6ac05f5e3d
@ -222,7 +222,7 @@
|
|||||||
"pen": "g"
|
"pen": "g"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Rotor Speed, mm/s ME",
|
"name": "Rotor Speed, deg/s ME",
|
||||||
"pen": "b"
|
"pen": "b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -286,7 +286,7 @@
|
|||||||
"pen": "g"
|
"pen": "g"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Rotor Speed, mm/s ME",
|
"name": "Rotor Speed, deg/s ME",
|
||||||
"pen": "b"
|
"pen": "b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,144 +1,223 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
from typing import Tuple, Union, Optional
|
from typing import Tuple, Union, Optional, List
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from base.base import (BaseKukaDataParser, BaseKukaTextParser,
|
from base.base import (
|
||||||
|
BaseKukaDataParser, BaseKukaTextParser,
|
||||||
BaseTraceStageDetector, BaseTextStageDetector,
|
BaseTraceStageDetector, BaseTextStageDetector,
|
||||||
BaseRawTraceProcessor, KukaDataHead,
|
BaseRawTraceProcessor, KukaDataHead,
|
||||||
KukaTXT, Settings)
|
KukaTXT, Settings
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class KukaDataParser(BaseKukaDataParser):
|
class KukaDataParser(BaseKukaDataParser):
|
||||||
|
"""
|
||||||
|
Класс для парсинга данных Кука.
|
||||||
|
|
||||||
def parse(self, head_path: str) -> pd.DataFrame:
|
Читает заголовочный файл (.dat) и соответствующий файл данных (.r64),
|
||||||
head = self._parse_dat_file(head_path)
|
объединяет их в pandas.DataFrame.
|
||||||
body_path = os.path.join(os.path.dirname(head_path), head.filename)
|
"""
|
||||||
dataframe = self._parse_r64_file(body_path, head)
|
|
||||||
|
def parse(self, head_filepath: str) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Основной метод парсинга. Читает заголовочный файл и файл данных.
|
||||||
|
|
||||||
|
:param head_filepath: Путь к заголовочному файлу.
|
||||||
|
:return: DataFrame с объединёнными данными.
|
||||||
|
"""
|
||||||
|
header = self._parse_header_file(head_filepath)
|
||||||
|
body_filepath = os.path.join(os.path.dirname(head_filepath), header.filename)
|
||||||
|
dataframe = self._parse_body_file(body_filepath, header)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def _parse_dat_file(self, path: str) -> KukaDataHead:
|
def _parse_header_file(self, filepath: str) -> KukaDataHead:
|
||||||
with open(path, 'r', encoding='cp1252') as file:
|
"""
|
||||||
head = KukaDataHead(0, "", {})
|
Парсит заголовочный файл (.dat) и извлекает информацию о каналах.
|
||||||
inside_channel = False
|
|
||||||
self._ch_name = None
|
:param filepath: Путь к заголовочному файлу.
|
||||||
|
:return: Объект KukaDataHead с информацией о данных.
|
||||||
|
"""
|
||||||
|
with open(filepath, 'r', encoding='cp1252') as file:
|
||||||
|
data_head = KukaDataHead(0, "", {})
|
||||||
|
is_inside_channel = False
|
||||||
|
self._current_channel = None # Текущее название канала
|
||||||
for line in file:
|
for line in file:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line in ('#BEGINCHANNELHEADER', "#BEGINGLOBALHEADER"):
|
if line in ('#BEGINCHANNELHEADER', "#BEGINGLOBALHEADER"):
|
||||||
inside_channel = True
|
is_inside_channel = True
|
||||||
elif line in ('#ENDCHANNELHEADER' "#ENDGLOBALHEADER"):
|
elif line in ('#ENDCHANNELHEADER', "#ENDGLOBALHEADER"):
|
||||||
inside_channel = False
|
is_inside_channel = False
|
||||||
else:
|
else:
|
||||||
if inside_channel:
|
if is_inside_channel:
|
||||||
self._parse_head_file_line(line, head)
|
self._parse_header_line(line, data_head)
|
||||||
return head
|
return data_head
|
||||||
|
|
||||||
def _parse_head_file_line(self, line: str, head: KukaDataHead) -> None:
|
def _parse_header_line(self, line: str, data_head: KukaDataHead) -> None:
|
||||||
tag, data = line.split(',')
|
"""
|
||||||
|
Обрабатывает отдельную строку заголовочного файла и обновляет объект data_head.
|
||||||
|
|
||||||
|
:param line: Строка из файла.
|
||||||
|
:param data_head: Объект KukaDataHead для обновления.
|
||||||
|
"""
|
||||||
|
tag, value = line.split(',')
|
||||||
match tag:
|
match tag:
|
||||||
case '102':
|
case '102':
|
||||||
head.rob_ID = data
|
data_head.rob_ID = value
|
||||||
case '200':
|
case '200':
|
||||||
self._ch_name = str(data)
|
self._current_channel = str(value)
|
||||||
head.channels[self._ch_name] = {}
|
data_head.channels[self._current_channel] = {}
|
||||||
case '202':
|
case '202':
|
||||||
head.channels[self._ch_name]['unit'] = str(data)
|
data_head.channels[self._current_channel]['unit'] = str(value)
|
||||||
case '211':
|
case '211':
|
||||||
head.filename = str(data)
|
data_head.filename = str(value)
|
||||||
case '220':
|
case '220':
|
||||||
head.channels[self._ch_name]['len'] = int(data)
|
data_head.channels[self._current_channel]['len'] = int(value)
|
||||||
case '221':
|
case '221':
|
||||||
head.channels[self._ch_name]['num'] = int(data)
|
data_head.channels[self._current_channel]['num'] = int(value)
|
||||||
case '241':
|
case '241':
|
||||||
head.channels[self._ch_name]['multiplyer'] = float(data)
|
# Переименовано в "multiplier" для повышения читаемости
|
||||||
|
data_head.channels[self._current_channel]['multiplier'] = float(value)
|
||||||
|
|
||||||
def _parse_r64_file(self, path: str, head: KukaDataHead) -> pd.DataFrame:
|
def _parse_body_file(self, filepath: str, data_head: KukaDataHead) -> pd.DataFrame:
|
||||||
time_axis = self._build_time_axis(head)
|
"""
|
||||||
mul, axes_names = self._extract_channels_data(head)
|
Парсит файл данных (.r64) и объединяет его с временной осью.
|
||||||
data_array = self._read_r64_file(path)
|
|
||||||
|
|
||||||
if data_array.size % len(axes_names) != 0:
|
:param filepath: Путь к файлу данных.
|
||||||
raise ValueError("Количество записей в {path} не кратно количеству найденных каналов ({ch_count})")
|
:param data_head: Заголовочная информация.
|
||||||
|
:return: DataFrame с данными.
|
||||||
|
"""
|
||||||
|
time_axis = self._build_time_axis(data_head)
|
||||||
|
multipliers, channel_names = self._extract_channels_data(data_head)
|
||||||
|
raw_data = self._read_r64_file(filepath)
|
||||||
|
|
||||||
|
if raw_data.size % len(channel_names) != 0:
|
||||||
|
raise ValueError(f"Количество записей в {filepath} не кратно количеству найденных каналов ({len(channel_names)})")
|
||||||
try:
|
try:
|
||||||
axes = data_array.reshape(-1, len(axes_names))
|
data_reshaped = raw_data.reshape(-1, len(channel_names))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ValueError(f"Ошибка при изменении формы data_array: {e}")
|
raise ValueError(f"Ошибка при изменении формы raw_data: {e}")
|
||||||
|
|
||||||
dataframe = pd.concat([time_axis, pd.DataFrame(axes*mul, columns=axes_names)], axis=1)
|
# Применяем множители к данным каналов
|
||||||
|
df_data = pd.DataFrame(data_reshaped * multipliers, columns=channel_names)
|
||||||
|
dataframe = pd.concat([time_axis, df_data], axis=1)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_channels_data(head: KukaDataHead) -> Tuple[np.ndarray, list]:
|
def _extract_channels_data(data_head: KukaDataHead) -> Tuple[np.ndarray, List[str]]:
|
||||||
|
"""
|
||||||
|
Извлекает данные по каналам, сортируя их по номеру.
|
||||||
|
|
||||||
|
:param data_head: Заголовочная информация.
|
||||||
|
:return: Кортеж из массива множителей и списка имён каналов.
|
||||||
|
"""
|
||||||
sorted_channels = sorted(
|
sorted_channels = sorted(
|
||||||
(item for item in head.channels.items() if item[0] != "Zeit"),
|
(item for item in data_head.channels.items() if item[0] != "Zeit"),
|
||||||
key=lambda item: item[1]['num']
|
key=lambda item: item[1]['num']
|
||||||
)
|
)
|
||||||
mul = np.array([info['multiplyer'] for _, info in sorted_channels])
|
multipliers = np.array([info['multiplier'] for _, info in sorted_channels])
|
||||||
names = [key for key, _ in sorted_channels]
|
channel_names = [key for key, _ in sorted_channels]
|
||||||
return mul, names
|
return multipliers, channel_names
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_time_axis(head: KukaDataHead) -> pd.Series:
|
def _build_time_axis(data_head: KukaDataHead) -> pd.Series:
|
||||||
len_timestamps = head.channels['Zeit']['len'] -1
|
"""
|
||||||
t_step = head.channels['Zeit']['multiplyer']
|
Строит временную ось на основе информации из канала 'Zeit'.
|
||||||
time_axis = pd.Series(np.arange(0, len_timestamps*t_step, t_step))
|
|
||||||
|
:param data_head: Заголовочная информация.
|
||||||
|
:return: Серия pandas с временными метками.
|
||||||
|
"""
|
||||||
|
num_timestamps = data_head.channels['Zeit']['len'] - 1
|
||||||
|
time_step = data_head.channels['Zeit']['multiplier']
|
||||||
|
time_axis = pd.Series(np.arange(0, num_timestamps * time_step, time_step))
|
||||||
time_axis.name = 'time'
|
time_axis.name = 'time'
|
||||||
return time_axis
|
return time_axis
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _read_r64_file(path: str) -> np.ndarray[float]:
|
def _read_r64_file(filepath: str) -> np.ndarray:
|
||||||
with open(path, 'rb') as file:
|
"""
|
||||||
|
Считывает бинарный файл (.r64) и возвращает массив чисел.
|
||||||
|
|
||||||
|
:param filepath: Путь к файлу.
|
||||||
|
:return: Массив numpy с типом float.
|
||||||
|
"""
|
||||||
|
with open(filepath, 'rb') as file:
|
||||||
data = file.read()
|
data = file.read()
|
||||||
floats = np.frombuffer(data, dtype='<d')
|
numbers = np.frombuffer(data, dtype='<d')
|
||||||
return floats
|
return numbers
|
||||||
|
|
||||||
|
|
||||||
class KukaTextParser(BaseKukaTextParser):
|
class KukaTextParser(BaseKukaTextParser):
|
||||||
|
"""
|
||||||
|
Класс для парсинга текстовых данных Кука.
|
||||||
|
|
||||||
|
Извлекает сообщения из файла.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._in_msg = False
|
self._in_message = False
|
||||||
self._datapacks = []
|
self._data_packs: List[KukaTXT] = []
|
||||||
|
|
||||||
def parse(self, path:str ) -> list[KukaTXT]:
|
def parse(self, filepath: str) -> List[KukaTXT]:
|
||||||
self._datapacks = []
|
"""
|
||||||
with open(path, 'r') as file:
|
Парсит текстовый файл и возвращает список объектов KukaTXT.
|
||||||
datapack = KukaTXT()
|
|
||||||
|
:param filepath: Путь к текстовому файлу.
|
||||||
|
:return: Список объектов KukaTXT.
|
||||||
|
"""
|
||||||
|
self._data_packs = []
|
||||||
|
with open(filepath, 'r') as file:
|
||||||
|
current_pack = KukaTXT()
|
||||||
for line in file:
|
for line in file:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
datapack = self._check_msg_flow(line, datapack)
|
current_pack = self._process_message_flow(line, current_pack)
|
||||||
return self._datapacks
|
return self._data_packs
|
||||||
|
|
||||||
def _check_msg_flow(self, line:str, datapack:KukaTXT) -> KukaTXT:
|
def _process_message_flow(self, line: str, current_pack: KukaTXT) -> KukaTXT:
|
||||||
|
"""
|
||||||
|
Обрабатывает поток сообщений, определяя начало и конец блока.
|
||||||
|
|
||||||
|
:param line: Строка из файла.
|
||||||
|
:param current_pack: Текущий объект KukaTXT.
|
||||||
|
:return: Обновлённый объект KukaTXT.
|
||||||
|
"""
|
||||||
if line == "#BEGINMOTIONINFO":
|
if line == "#BEGINMOTIONINFO":
|
||||||
self._in_msg = True
|
self._in_message = True
|
||||||
datapack = KukaTXT()
|
current_pack = KukaTXT()
|
||||||
elif line == "#ENDMOTIONINFO":
|
elif line == "#ENDMOTIONINFO":
|
||||||
self._in_msg = False
|
self._in_message = False
|
||||||
self._datapacks.append(datapack)
|
self._data_packs.append(current_pack)
|
||||||
datapack = KukaTXT()
|
current_pack = KukaTXT()
|
||||||
elif self._in_msg:
|
elif self._in_message:
|
||||||
datapack = self._process_line(line, datapack)
|
current_pack = self._process_line(line, current_pack)
|
||||||
return datapack
|
return current_pack
|
||||||
|
|
||||||
def _process_line(self, line:str, datapack:KukaTXT) -> KukaTXT:
|
def _process_line(self, line: str, current_pack: KukaTXT) -> KukaTXT:
|
||||||
tag, data = line.split(":")
|
"""
|
||||||
|
Обрабатывает отдельную строку внутри блока сообщения.
|
||||||
|
|
||||||
|
:param line: Строка с данными.
|
||||||
|
:param current_pack: Текущий объект KukaTXT.
|
||||||
|
:return: Обновлённый объект KukaTXT.
|
||||||
|
"""
|
||||||
|
tag, value = line.split(":")
|
||||||
match tag:
|
match tag:
|
||||||
case "TIME":
|
case "TIME":
|
||||||
datapack.time = float(data)
|
current_pack.time = float(value)
|
||||||
case "ENDTIME":
|
case "ENDTIME":
|
||||||
datapack.endtime = float(data)
|
current_pack.endtime = float(value)
|
||||||
case "MODULE":
|
case "MODULE":
|
||||||
pass
|
pass
|
||||||
case "FUNCTION/PROCEDURE":
|
case "FUNCTION/PROCEDURE":
|
||||||
datapack.func = data
|
current_pack.func = value
|
||||||
case "TYPE":
|
case "TYPE":
|
||||||
datapack.type_ = data
|
current_pack.type_ = value
|
||||||
case "SIGNAL":
|
case "SIGNAL":
|
||||||
datapack.signal = data
|
current_pack.signal = value
|
||||||
case "LINE":
|
case "LINE":
|
||||||
pass
|
pass
|
||||||
case "POINT NAME":
|
case "POINT NAME":
|
||||||
@ -165,122 +244,159 @@ class KukaTextParser(BaseKukaTextParser):
|
|||||||
pass
|
pass
|
||||||
case "LOAD A3":
|
case "LOAD A3":
|
||||||
pass
|
pass
|
||||||
return datapack
|
return current_pack
|
||||||
|
|
||||||
|
|
||||||
class TraceStageDetector(BaseTraceStageDetector):
|
class TraceStageDetector(BaseTraceStageDetector):
|
||||||
|
"""
|
||||||
|
Класс для детекции этапов (стадий) на основе данных трассировки.
|
||||||
|
|
||||||
|
Определяет переходы между различными стадиями процесса по пороговым значениям.
|
||||||
|
"""
|
||||||
def __init__(self, parent: TraceProcessor = None):
|
def __init__(self, parent: TraceProcessor = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
def _mark_timestamp(self, df:pd.DataFrame, index:int, time:float) -> Union[str, None]:
|
@staticmethod
|
||||||
# Не интересные значения помечаем как "unknown"
|
def is_closing(robot_velocity: float, actual_velocity: float, actual_force: float, thresholds: dict) -> bool:
|
||||||
if (time < self._parent._settings.filter["ROI_start"][0] or
|
"""
|
||||||
time > self._parent._settings.filter["ROI_finish"][0]):
|
Определяет, находится ли робот в стадии закрытия.
|
||||||
return "unknown"
|
|
||||||
|
:param robot_velocity: Скорость робота.
|
||||||
|
:param actual_velocity: Фактическая скорость.
|
||||||
|
:param actual_force: Фактическое значение силы.
|
||||||
|
:param thresholds: Словарь пороговых значений.
|
||||||
|
:return: True, если условие стадии закрытия выполнено.
|
||||||
|
"""
|
||||||
|
return actual_velocity > thresholds['act_vel_min'] and abs(robot_velocity) < thresholds['rob_vel_thresh'] and actual_force < thresholds['act_force_close']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_closing(rob_vel:float,
|
def is_squeeze(robot_velocity: float, actual_velocity: float, actual_force: float, force_rate: float, thresholds: dict) -> bool:
|
||||||
act_vel:float,
|
"""
|
||||||
act_force:float,
|
Определяет, находится ли робот в стадии сжатия.
|
||||||
th:dict) -> bool:
|
|
||||||
# act_vel > min, rob_vel ~ 0, act_force ниже порога
|
:param robot_velocity: Скорость робота.
|
||||||
return act_vel > th['act_vel_min'] and abs(rob_vel) < th['rob_vel_thresh'] and act_force < th['act_force_close']
|
:param actual_velocity: Фактическая скорость.
|
||||||
|
:param actual_force: Фактическое значение силы.
|
||||||
|
:param force_rate: Темп изменения силы.
|
||||||
|
:param thresholds: Словарь пороговых значений.
|
||||||
|
:return: True, если условие стадии сжатия выполнено.
|
||||||
|
"""
|
||||||
|
return abs(robot_velocity) < thresholds['rob_vel_thresh'] and actual_velocity < thresholds['act_vel_close'] and force_rate > thresholds['force_increase']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_squeeze(rob_vel:float,
|
def is_welding(robot_velocity: float, actual_velocity: float, actual_force: float, thresholds: dict) -> bool:
|
||||||
act_vel:float,
|
"""
|
||||||
act_force:float,
|
Определяет, находится ли робот в стадии сварки.
|
||||||
force_rate:float,
|
|
||||||
th:dict) -> bool:
|
:param robot_velocity: Скорость робота.
|
||||||
# rob_vel ~ 0, act_vel меньше, чем в closing, и резкий рост act_force
|
:param actual_velocity: Фактическая скорость.
|
||||||
return abs(rob_vel) < th['rob_vel_thresh'] and act_vel < th['act_vel_close'] and force_rate > th['force_increase']
|
:param actual_force: Фактическое значение силы.
|
||||||
|
:param thresholds: Словарь пороговых значений.
|
||||||
|
:return: True, если условие стадии сварки выполнено.
|
||||||
|
"""
|
||||||
|
return abs(robot_velocity) < thresholds['rob_vel_thresh'] and abs(actual_velocity) < thresholds['act_vel_thresh'] and actual_force > thresholds['act_force_weld']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_welding(rob_vel:float,
|
def is_relief(actual_velocity: float, actual_position_diff: float, force_rate: float, thresholds: dict) -> bool:
|
||||||
act_vel:float,
|
"""
|
||||||
act_force:float,
|
Определяет, находится ли робот в стадии снятия усилия (relief).
|
||||||
th:dict) -> bool:
|
|
||||||
# обе скорости ≈ 0, act_force выше порога сварки
|
|
||||||
return abs(rob_vel) < th['rob_vel_thresh'] and abs(act_vel) < th['act_vel_thresh'] and act_force > th['act_force_weld']
|
|
||||||
|
|
||||||
@staticmethod
|
:param actual_velocity: Фактическая скорость.
|
||||||
def is_relief(rob_vel:float,
|
:param actual_position_diff: Разница в позициях.
|
||||||
act_vel:float,
|
:param force_rate: Темп изменения силы.
|
||||||
act_force:float,
|
:param thresholds: Словарь пороговых значений.
|
||||||
force_rate:float,
|
:return: True, если условие стадии снятия усилия выполнено.
|
||||||
th:dict) -> bool:
|
"""
|
||||||
# резкое падение act_force, отрицательный act_vel, малые rob_vel
|
return force_rate < -thresholds['force_decrease'] and abs(actual_position_diff) < thresholds['act_pos_decrease'] and actual_velocity < -thresholds['act_vel_negative']
|
||||||
return force_rate < -th['force_decrease'] and act_vel < -th['act_vel_negative'] and abs(rob_vel) < th['rob_vel_thresh']
|
|
||||||
|
|
||||||
def detect_stages(self, df:pd.DataFrame):
|
def detect_stages(self, df: pd.DataFrame) -> List[Tuple[str, float, float]]:
|
||||||
|
"""
|
||||||
|
Детектирует стадии процесса по данным трассировки.
|
||||||
|
|
||||||
|
:param df: DataFrame с данными трассировки.
|
||||||
|
:return: Список кортежей (название стадии, время начала, время окончания).
|
||||||
|
"""
|
||||||
timestamps = df['time'].to_list()
|
timestamps = df['time'].to_list()
|
||||||
n = len(df)
|
n = len(df)
|
||||||
th = {key: item[0] for key, item in self._parent._settings.filter.items()}
|
# Извлекаем пороговые значения из настроек
|
||||||
# Вычисляем разностную производную силы
|
thresholds = {key: item[0] for key, item in self._parent._settings.filter.items()}
|
||||||
act_force = df["DriveMotorTorq_Act7"].values
|
|
||||||
force_diff = np.diff(act_force, prepend=act_force[0])
|
|
||||||
|
|
||||||
states = []
|
# Вычисляем производную силы и разницу позиций
|
||||||
|
actual_force = df["DriveMotorTorq_Act7"].values
|
||||||
|
force_diff = np.diff(actual_force, prepend=actual_force[0])
|
||||||
|
actual_position = df["DriveMotorPos_Act7"].values
|
||||||
|
position_diff = np.diff(actual_position, prepend=actual_position[0])
|
||||||
|
|
||||||
|
stages = []
|
||||||
current_state = "Oncomming"
|
current_state = "Oncomming"
|
||||||
state_start = timestamps[0]
|
state_start = timestamps[0]
|
||||||
|
|
||||||
# Проходим по всем записям
|
# Проходим по всем записям DataFrame
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
rob_vel = df.loc[i, "CartVel_Act"]
|
robot_velocity = df.loc[i, "CartVel_Act"]
|
||||||
act_vel = df.loc[i, "DriveMotorVel_Act7"]
|
actual_velocity = df.loc[i, "DriveMotorVel_Act7"]
|
||||||
act_force_val = df.loc[i, "DriveMotorTorq_Act7"]
|
force_value = df.loc[i, "DriveMotorTorq_Act7"]
|
||||||
force_rate = force_diff[i]
|
current_force_rate = force_diff[i]
|
||||||
|
current_position_diff = position_diff[i]
|
||||||
|
|
||||||
if current_state == "Oncomming":
|
if current_state == "Oncomming":
|
||||||
if self.is_closing(rob_vel, act_vel, act_force_val, th):
|
if self.is_closing(robot_velocity, actual_velocity, force_value, thresholds):
|
||||||
state_end = timestamps[i]
|
state_end = timestamps[i]
|
||||||
states.append(("Oncomming", state_start, state_end))
|
stages.append(("Oncomming", state_start, state_end))
|
||||||
current_state = "Closing"
|
current_state = "Closing"
|
||||||
state_start = timestamps[i]
|
state_start = timestamps[i]
|
||||||
|
|
||||||
elif current_state == "Closing":
|
elif current_state == "Closing":
|
||||||
if self.is_squeeze(rob_vel, act_vel, act_force_val, force_rate, th):
|
if self.is_squeeze(robot_velocity, actual_velocity, force_value, current_force_rate, thresholds):
|
||||||
state_end = timestamps[i]
|
state_end = timestamps[i]
|
||||||
states.append(("Closing", state_start, state_end))
|
stages.append(("Closing", state_start, state_end))
|
||||||
current_state = "Squeeze"
|
current_state = "Squeeze"
|
||||||
state_start = timestamps[i]
|
state_start = timestamps[i]
|
||||||
|
|
||||||
elif current_state == "Squeeze":
|
elif current_state == "Squeeze":
|
||||||
if self.is_welding(rob_vel, act_vel, act_force_val, th):
|
if self.is_welding(robot_velocity, actual_velocity, force_value, thresholds):
|
||||||
state_end = timestamps[i]
|
state_end = timestamps[i]
|
||||||
states.append(("Squeeze", state_start, state_end))
|
stages.append(("Squeeze", state_start, state_end))
|
||||||
current_state = "Welding"
|
current_state = "Welding"
|
||||||
state_start = timestamps[i]
|
state_start = timestamps[i]
|
||||||
|
|
||||||
elif current_state == "Welding":
|
elif current_state == "Welding":
|
||||||
if self.is_relief(rob_vel, act_vel, act_force_val, force_rate, th):
|
if self.is_relief(actual_velocity, current_position_diff, current_force_rate, thresholds):
|
||||||
state_end = timestamps[i]
|
state_end = timestamps[i]
|
||||||
states.append(("Welding", state_start, state_end))
|
stages.append(("Welding", state_start, state_end))
|
||||||
current_state = "Relief"
|
current_state = "Relief"
|
||||||
state_start = timestamps[i]
|
state_start = timestamps[i]
|
||||||
|
|
||||||
elif current_state == "Relief":
|
elif current_state == "Relief":
|
||||||
# Когда признаки relief отпадают, считаем, что цикл завершён и возвращаемся в Oncomming
|
# Если признаки снятия усилия не соблюдаются, завершаем цикл и переходим в Oncomming
|
||||||
if not self.is_relief(rob_vel, act_vel, act_force_val, force_rate, th):
|
if not self.is_relief(actual_velocity, current_position_diff, current_force_rate, thresholds):
|
||||||
state_end = timestamps[i]
|
state_end = timestamps[i]
|
||||||
states.append(("Relief", state_start, state_end))
|
stages.append(("Relief", state_start, state_end))
|
||||||
current_state = "Oncomming"
|
current_state = "Oncomming"
|
||||||
state_start = timestamps[i]
|
state_start = timestamps[i]
|
||||||
|
|
||||||
# Фиксируем последний сегмент
|
# Фиксируем последний сегмент
|
||||||
states.append((current_state, state_start, timestamps[-1]))
|
stages.append((current_state, state_start, timestamps[-1]))
|
||||||
return states
|
return stages
|
||||||
|
|
||||||
|
|
||||||
class TextStageDetector(BaseTextStageDetector):
|
class TextStageDetector(BaseTextStageDetector):
|
||||||
|
"""
|
||||||
|
Класс для детекции сварочных стадий на основе текстовых данных.
|
||||||
|
"""
|
||||||
def __init__(self, parent: TraceProcessor = None):
|
def __init__(self, parent: TraceProcessor = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
def detect_welding(self, data:list[KukaTXT]) -> list:
|
@staticmethod
|
||||||
|
def detect_welding(data: List[KukaTXT]) -> List[dict]:
|
||||||
|
"""
|
||||||
|
Детектирует этап сварки по текстовым сообщениям.
|
||||||
|
|
||||||
|
:param data: Список объектов KukaTXT.
|
||||||
|
:return: Список словарей с информацией о сварке (стадия, время начала и окончания).
|
||||||
|
"""
|
||||||
stages = []
|
stages = []
|
||||||
for i in range(len(data)):
|
for i in range(len(data) - 1): # Предотвращаем выход за пределы списка
|
||||||
if data[i].func == " SPOT" and data[i].signal == " END":
|
if data[i].func == " SPOT" and data[i].signal == " END":
|
||||||
stages.append({
|
stages.append({
|
||||||
"stage": "welding",
|
"stage": "welding",
|
||||||
@ -291,114 +407,137 @@ class TextStageDetector(BaseTextStageDetector):
|
|||||||
|
|
||||||
|
|
||||||
class TraceProcessor(BaseRawTraceProcessor):
|
class TraceProcessor(BaseRawTraceProcessor):
|
||||||
|
"""
|
||||||
|
Основной класс для обработки трассировок.
|
||||||
|
|
||||||
|
Объединяет данные, детектирует стадии и инициирует рендеринг через медиатор.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._settings = Settings()
|
self._settings = Settings()
|
||||||
dataparser = KukaDataParser()
|
data_parser = KukaDataParser()
|
||||||
textparser = KukaTextParser()
|
text_parser = KukaTextParser()
|
||||||
data_detector = TraceStageDetector(self)
|
trace_detector = TraceStageDetector(self)
|
||||||
text_detector = TextStageDetector(self)
|
text_detector = TextStageDetector(self)
|
||||||
|
|
||||||
super().__init__(dataparser, textparser, data_detector, text_detector)
|
super().__init__(data_parser, text_parser, trace_detector, text_detector)
|
||||||
|
|
||||||
def prerender(self, data:list[str]) -> None:
|
def prerender(self, data: List[str]) -> None:
|
||||||
|
"""
|
||||||
|
Препроцессинг данных для отрисовки трейсов и обнаруженных событий.
|
||||||
|
|
||||||
|
:param data: Список путей к файлам данных.
|
||||||
|
"""
|
||||||
rendered_data = self._render_data(data)
|
rendered_data = self._render_data(data)
|
||||||
self._mediator.prerender_TCW(self, rendered_data)
|
self._mediator.prerender_TCW(self, rendered_data)
|
||||||
|
|
||||||
def final_render(self) -> None:
|
def final_render(self) -> None:
|
||||||
|
"""
|
||||||
|
Финальный рендеринг данных для отрисовки
|
||||||
|
трейсов клиента и расчета трейсов ТСК.
|
||||||
|
"""
|
||||||
events = self._detect_stages(self._trace_df, self._text_data)
|
events = self._detect_stages(self._trace_df, self._text_data)
|
||||||
dataframe = self._rename_df_columns(self._trace_df)
|
renamed_df = self._rename_df_columns(self._trace_df)
|
||||||
self._mediator.render_TCW(self, [dataframe, events])
|
part_position, max_open = self._detect_coords(renamed_df, events)
|
||||||
|
self._mediator.render_TCW(self, [renamed_df, events, [part_position, max_open]])
|
||||||
|
|
||||||
def _render_data(self, data:list[str]) -> list[pd.DataFrame, dict]:
|
def _detect_coords(self, dataframe: pd.DataFrame, events: dict) -> Tuple[List[float], List[float]]:
|
||||||
|
"""
|
||||||
|
Детектирует координаты на основе событий и данных.
|
||||||
|
|
||||||
|
:param dataframe: DataFrame с данными.
|
||||||
|
:param events: Словарь с событиями.
|
||||||
|
:return: Кортеж из списков: координаты детали и максимальное открытие электрода.
|
||||||
|
"""
|
||||||
|
weld_timings = events["Welding"]
|
||||||
|
oncommings = events["Oncomming"]
|
||||||
|
open_positions = []
|
||||||
|
part_positions = []
|
||||||
|
for i in range(len(weld_timings[0])):
|
||||||
|
weld_start = weld_timings[0][i]
|
||||||
|
weld_end = weld_timings[1][i]
|
||||||
|
onc_start = oncommings[0][i]
|
||||||
|
onc_end = oncommings[1][i]
|
||||||
|
pos_part = dataframe[(dataframe["time"] > weld_start) & (dataframe["time"] < weld_end)]["Electrode Position, mm ME"].mean()
|
||||||
|
part_positions.append(float(pos_part) / 1000)
|
||||||
|
pos_open = dataframe[(dataframe["time"] > onc_start) & (dataframe["time"] < onc_end)]["Electrode Position, mm ME"].abs().max()
|
||||||
|
open_positions.append(float(pos_open) / 1000)
|
||||||
|
return (part_positions, open_positions)
|
||||||
|
|
||||||
|
def _render_data(self, data: List[str]) -> List:
|
||||||
|
"""
|
||||||
|
Обрабатывает входные файлы данных и возвращает предварительно обработанные данные.
|
||||||
|
|
||||||
|
:param data: Список путей к файлам (первый для трассировки, второй для текстовых данных).
|
||||||
|
:return: Список, содержащий DataFrame и события.
|
||||||
|
"""
|
||||||
if data and len(data) == 2:
|
if data and len(data) == 2:
|
||||||
dat_filepath = data[0]
|
trace_filepath = data[0]
|
||||||
txt_filepath = data[1]
|
text_filepath = data[1]
|
||||||
elif data:
|
elif data:
|
||||||
dat_filepath = data[0]
|
trace_filepath = data[0]
|
||||||
txt_filepath = None
|
text_filepath = None
|
||||||
else:
|
else:
|
||||||
dat_filepath = None
|
trace_filepath = None
|
||||||
txt_filepath = None
|
text_filepath = None
|
||||||
|
|
||||||
self._trace_df = self._unpack_trace(dat_filepath)
|
self._trace_df = self._unpack_trace(trace_filepath)
|
||||||
self._text_data = self._unpack_text(txt_filepath)
|
self._text_data = self._unpack_text(text_filepath)
|
||||||
events = self._detect_stages(self._trace_df, self._text_data)
|
events = self._detect_stages(self._trace_df, self._text_data)
|
||||||
dataframe = self._rename_df_columns(self._trace_df)
|
renamed_df = self._rename_df_columns(self._trace_df)
|
||||||
return [dataframe, events]
|
return [renamed_df, events]
|
||||||
|
|
||||||
def update_settings(self, data:Settings) -> None:
|
def update_settings(self, settings: Settings) -> None:
|
||||||
self._settings = data
|
"""
|
||||||
|
Обновляет настройки обработки.
|
||||||
|
|
||||||
def _unpack_trace(self, dat_filepath:str = None) -> Optional[pd.DataFrame]:
|
:param settings: Объект настроек.
|
||||||
if dat_filepath:
|
"""
|
||||||
return self._dataparser.parse(dat_filepath)
|
self._settings = settings
|
||||||
|
|
||||||
|
def _unpack_trace(self, trace_filepath: str = None) -> Optional[pd.DataFrame]:
|
||||||
|
"""
|
||||||
|
Распаковывает трассировочные данные из файла.
|
||||||
|
|
||||||
|
:param trace_filepath: Путь к файлу трассировки.
|
||||||
|
:return: DataFrame с данными или None.
|
||||||
|
"""
|
||||||
|
if trace_filepath:
|
||||||
|
return self._dataparser.parse(trace_filepath)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _unpack_text(self, txt_filepath:str = None) -> Optional[list[KukaTXT]]:
|
def _unpack_text(self, text_filepath: str = None) -> Optional[List[KukaTXT]]:
|
||||||
if txt_filepath:
|
"""
|
||||||
return self._textparser.parse(txt_filepath)
|
Распаковывает текстовые данные из файла.
|
||||||
|
|
||||||
|
:param text_filepath: Путь к текстовому файлу.
|
||||||
|
:return: Список объектов KukaTXT или None.
|
||||||
|
"""
|
||||||
|
if text_filepath:
|
||||||
|
return self._textparser.parse(text_filepath)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _detect_stages(self, trace_df:pd.DataFrame, text_data:list) -> dict[list]:
|
def _detect_stages(self, trace_df: pd.DataFrame, text_data: List) -> Optional[dict]:
|
||||||
|
"""
|
||||||
|
Детектирует события на основе трассировочных и текстовых данных.
|
||||||
|
|
||||||
|
:param trace_df: DataFrame с трассировочными данными.
|
||||||
|
:param text_data: Список текстовых данных.
|
||||||
|
:return: Словарь с событиями или None.
|
||||||
|
"""
|
||||||
if trace_df is not None and text_data is not None:
|
if trace_df is not None and text_data is not None:
|
||||||
trace_stages = self._data_detector.detect_stages(trace_df)
|
trace_stages = self._data_detector.detect_stages(trace_df)
|
||||||
#welding_stages = self._text_detector.detect_welding(text_data)
|
events = self._form_events(trace_stages)
|
||||||
#events = self._form_events(trace_stages, welding_stages)
|
|
||||||
events = self.__form_events_2(trace_stages)
|
|
||||||
return events
|
return events
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _normalize_events(events:dict[list]) -> dict[list]:
|
def _form_events(trace_stages: list) -> dict:
|
||||||
max_len = max([len(item_list[0]) if key != "Oncomming" else len(item_list[0])-1 for key, item_list in events.items() ])
|
"""
|
||||||
for key, item_list in events.items():
|
Формирует словарь событий на основе списка этапов трассировки.
|
||||||
list_len = len(item_list[0])
|
|
||||||
if list_len < max_len:
|
|
||||||
logger.warning(f"_normalize_events - Ошибка детекции событий {key}: ожидалось {max_len} событий, получено {list_len}")
|
|
||||||
for i in range (max_len - list_len):
|
|
||||||
events[key][0].append(0)
|
|
||||||
events[key][1].append(1)
|
|
||||||
return events
|
|
||||||
@staticmethod
|
|
||||||
def _form_events(trace_stages:list, welding_stages:list) -> dict[list]:
|
|
||||||
idx = 0
|
|
||||||
events = {
|
|
||||||
"Closing":[[],[]],
|
|
||||||
"Squeeze":[[],[]],
|
|
||||||
"Welding":[[],[]],
|
|
||||||
"Relief":[[],[]],
|
|
||||||
"Oncomming":[[],[]]
|
|
||||||
}
|
|
||||||
for i in range(len(trace_stages)-1):
|
|
||||||
if trace_stages[i]['stage'] == 'Squeeze&Welding&Relief':
|
|
||||||
if trace_stages[i]['end_time'] > welding_stages[idx]['start_time']:
|
|
||||||
|
|
||||||
events["Squeeze"][0].append(trace_stages[i]['start_time'])
|
:param trace_stages: Список этапов (кортежи: название, время начала, время окончания).
|
||||||
events["Squeeze"][1].append(welding_stages[idx]['start_time'])
|
:return: Словарь с событиями.
|
||||||
|
"""
|
||||||
events["Welding"][0].append(welding_stages[idx]['start_time'])
|
|
||||||
events["Welding"][1].append(welding_stages[idx]['end_time'])
|
|
||||||
|
|
||||||
events["Relief"][0].append(welding_stages[idx]['end_time'])
|
|
||||||
events["Relief"][1].append(trace_stages[i+1]['start_time'])
|
|
||||||
idx += 1
|
|
||||||
else:
|
|
||||||
events["Squeeze"][0].append(trace_stages[i]['start_time'])
|
|
||||||
events["Squeeze"][1].append(trace_stages[i]['end_time'])
|
|
||||||
elif trace_stages[i]['stage'] == 'unknown':
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
events[trace_stages[i]['stage']][0].append(trace_stages[i]['start_time'])
|
|
||||||
events[trace_stages[i]['stage']][1].append(trace_stages[i]['end_time'])
|
|
||||||
normalized_events = TraceProcessor._normalize_events(events)
|
|
||||||
return normalized_events
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __form_events_2(trace_stages:list):
|
|
||||||
events = {
|
events = {
|
||||||
"Closing": [[], []],
|
"Closing": [[], []],
|
||||||
"Squeeze": [[], []],
|
"Squeeze": [[], []],
|
||||||
@ -407,13 +546,19 @@ class TraceProcessor(BaseRawTraceProcessor):
|
|||||||
"Oncomming": [[], []]
|
"Oncomming": [[], []]
|
||||||
}
|
}
|
||||||
for stage in trace_stages:
|
for stage in trace_stages:
|
||||||
name, start_t, end_t = stage
|
name, start_time, end_time = stage
|
||||||
events[name][0].append(start_t)
|
events[name][0].append(start_time)
|
||||||
events[name][1].append(end_t)
|
events[name][1].append(end_time)
|
||||||
return events
|
return events
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _rename_df_columns(dataframe: pd.DataFrame) -> pd.DataFrame:
|
def _rename_df_columns(dataframe: pd.DataFrame) -> Optional[pd.DataFrame]:
|
||||||
|
"""
|
||||||
|
Переименовывает столбцы DataFrame на основе корректной карты соответствия.
|
||||||
|
|
||||||
|
:param dataframe: Исходный DataFrame.
|
||||||
|
:return: DataFrame с переименованными столбцами или None в случае ошибки.
|
||||||
|
"""
|
||||||
correct_mapping = {
|
correct_mapping = {
|
||||||
"time": ["Time", "Timestamp"],
|
"time": ["Time", "Timestamp"],
|
||||||
"Tool Coordinate, mm X": ["X_Act"],
|
"Tool Coordinate, mm X": ["X_Act"],
|
||||||
@ -422,17 +567,17 @@ class TraceProcessor(BaseRawTraceProcessor):
|
|||||||
"Electrode Force, N ME": ["DriveMotorTorq_Act7"],
|
"Electrode Force, N ME": ["DriveMotorTorq_Act7"],
|
||||||
"Electrode Position, mm ME": ["AxisPos_Act7"],
|
"Electrode Position, mm ME": ["AxisPos_Act7"],
|
||||||
"Electrode Speed, mm ME": ["AxisVel_Act7"],
|
"Electrode Speed, mm ME": ["AxisVel_Act7"],
|
||||||
"Rotor Position, mm FE": ["DriveMotorPos_Act7"],
|
"Rotor Position, deg FE": ["DriveMotorPos_Act7"],
|
||||||
"Rotor Speed, mm/s ME": ["DriveMotorVel_Act7"],
|
"Rotor Speed, deg/s ME": ["DriveMotorVel_Act7"],
|
||||||
"Rotor Current, A ME": ["DriveMotorCurr_Act7"],
|
"Rotor Current, A ME": ["DriveMotorCurr_Act7"],
|
||||||
"Cartesian Tool Speed, mm/s ME": ["CartVel_Act"],
|
"Cartesian Tool Speed, mm/s ME": ["CartVel_Act"],
|
||||||
}
|
}
|
||||||
dataframe = dataframe.copy(deep=True)
|
try:
|
||||||
|
df_copy = dataframe.copy(deep=True)
|
||||||
working_mapping = {key: [item.lower() for item in items] for key, items in correct_mapping.items()}
|
working_mapping = {key: [item.lower() for item in items] for key, items in correct_mapping.items()}
|
||||||
|
|
||||||
try:
|
|
||||||
new_columns = {}
|
new_columns = {}
|
||||||
for col in dataframe.columns:
|
for col in df_copy.columns:
|
||||||
col_lower = col.lower()
|
col_lower = col.lower()
|
||||||
for key, values in working_mapping.items():
|
for key, values in working_mapping.items():
|
||||||
if col_lower in values:
|
if col_lower in values:
|
||||||
@ -440,10 +585,9 @@ class TraceProcessor(BaseRawTraceProcessor):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
new_columns[col] = col
|
new_columns[col] = col
|
||||||
|
df_copy.rename(columns=new_columns, inplace=True)
|
||||||
dataframe.rename(columns=new_columns, inplace=True)
|
df_copy = df_copy.loc[:, ~df_copy.columns.duplicated()]
|
||||||
dataframe = dataframe.loc[:, ~dataframe.columns.duplicated()]
|
return df_copy
|
||||||
return dataframe
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error(f"_rename_df_columns - AttributeError: Проверьте, что переданный объект является DataFrame. {e}")
|
logger.error(f"_rename_df_columns - AttributeError: Проверьте, что переданный объект является DataFrame. {e}")
|
||||||
return None
|
return None
|
||||||
@ -451,7 +595,11 @@ class TraceProcessor(BaseRawTraceProcessor):
|
|||||||
logger.error(f"_rename_df_columns - Непредвиденная ошибка: {e}")
|
logger.error(f"_rename_df_columns - Непредвиденная ошибка: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
"Перемещение"
|
|
||||||
|
"""
|
||||||
|
Примеры комментариев для этапов процесса:
|
||||||
|
|
||||||
|
Перемещение:
|
||||||
# FUNCTION/PROCEDURE: SW_RSP030TL01_SN - какое-то перемещение
|
# FUNCTION/PROCEDURE: SW_RSP030TL01_SN - какое-то перемещение
|
||||||
#
|
#
|
||||||
# ИЛИ
|
# ИЛИ
|
||||||
@ -459,74 +607,21 @@ class TraceProcessor(BaseRawTraceProcessor):
|
|||||||
# FUNCTION/PROCEDURE: SGL_MoveToPos - перемещение между точками
|
# FUNCTION/PROCEDURE: SGL_MoveToPos - перемещение между точками
|
||||||
# SIGNAL: BLENDING
|
# SIGNAL: BLENDING
|
||||||
|
|
||||||
"Смыкание и набор усилия"
|
Смыкание и набор усилия:
|
||||||
#FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - перемещение электрода движения робота
|
# FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - перемещение электрода с движением робота
|
||||||
# ... ...
|
# ...
|
||||||
# FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - перемещение 0.5 мм роботом с движением электрода
|
# FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - перемещение 0.5 мм роботом с движением электрода
|
||||||
#... ...
|
# ...
|
||||||
# FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - остановка в позиции (может быть набор усилия?)
|
# FUNCTION/PROCEDURE: SGM_MOVE_TO_FORCE - остановка в позиции (может быть набор усилия?)
|
||||||
|
|
||||||
"Сварка"
|
Сварка:
|
||||||
# FUNCTION/PROCEDURE: SPOT - Начало сварочного процесса
|
# FUNCTION/PROCEDURE: SPOT - Начало сварочного процесса
|
||||||
# SIGNAL: START
|
# SIGNAL: START
|
||||||
#
|
#
|
||||||
# FUNCTION/PROCEDURE: SPOT - Конец сварочного процесса
|
# FUNCTION/PROCEDURE: SPOT - Конец сварочного процесса
|
||||||
# SIGNAL: END
|
# SIGNAL: END
|
||||||
|
|
||||||
"Снятие усилия и разъезд"
|
Снятие усилия и разъезд:
|
||||||
# FUNCTION/PROCEDURE: SGL_MoveToPos - Выход из контакта с точкой движением робота и электрода
|
# FUNCTION/PROCEDURE: SGL_MoveToPos - Выход из контакта с точкой движением робота и электрода
|
||||||
# SIGNAL: START
|
# SIGNAL: START
|
||||||
#
|
|
||||||
|
|
||||||
"Перемещение"
|
|
||||||
#FUNCTION/PROCEDURE: SW_RSP030TL01_SN - какое-то перемещение
|
|
||||||
#
|
|
||||||
# ИЛИ
|
|
||||||
#
|
|
||||||
#FUNCTION/PROCEDURE: SGL_MoveToPos - перемещение между точками
|
|
||||||
#SIGNAL: BLENDING
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
roboreader = KukaDataParser()
|
|
||||||
txt_reader = KukaTextParser()
|
|
||||||
detector_traces = TraceStageDetector(region_of_focus=[7.7, 42])
|
|
||||||
detector_weldings = TextStageDetector()
|
|
||||||
|
|
||||||
path1 = os.path.abspath("trace_samples/teslaSP_VelTCP_KRCIpo.dat")
|
|
||||||
path2 = os.path.abspath("trace_samples/teslaSP_VelTCP_PROG.TXT")
|
|
||||||
|
|
||||||
data1 = roboreader.parse(path1)
|
|
||||||
data2 = txt_reader.parse(path2)
|
|
||||||
|
|
||||||
stages = detector_traces.detect_stages(data1)
|
|
||||||
weldings = detector_weldings.detect_welding(data2)
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
for st in stages:
|
|
||||||
print(st)
|
|
||||||
if st["stage"] == "force":
|
|
||||||
counter+=1
|
|
||||||
print(counter)
|
|
||||||
print("=========================================")
|
|
||||||
for we in weldings:
|
|
||||||
print (we)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
save = os.path.dirname(path) + "/sample.csv"
|
|
||||||
data.to_csv(save)
|
|
||||||
path = os.path.abspath("trace_samples/teslaSP_VelTCP_PROG.TXT")
|
|
||||||
txt_parser = TXT_Parser()
|
|
||||||
txt_parser.parse(path)
|
|
||||||
print(len(txt_parser._datapacks))
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user