From 957071075d735374cc93ad527ab51c2060a0ee1b Mon Sep 17 00:00:00 2001 From: gitea Date: Thu, 27 Mar 2025 19:05:02 +0800 Subject: [PATCH] basically done --- .gitignore | 5 +- aio.py | 1022 +++++++++++++++++ assets/files/protocols/ec/motor_off.txt | 1 + assets/files/protocols/ec/motor_on.txt | 1 + assets/files/protocols/ec/motor_on_state.txt | 1 + assets/files/protocols/ec/operating_mode.txt | 1 + .../protocols/ec/robot_running_state.txt | 1 + assets/files/protocols/ec/set_do.txt | 1 + .../files/protocols/ec/switch_mode_auto.txt | 1 + .../files/protocols/ec/switch_mode_manual.txt | 1 + .../protocols/hmi/collision.get_params.json | 3 +- .../protocols/hmi/collision.set_params.json | 3 +- .../protocols/hmi/collision.set_state.json | 3 +- .../protocols/hmi/controller.get_params.json | 3 +- .../files/protocols/hmi/controller.heart.json | 3 +- .../protocols/hmi/controller.reboot.json | 3 +- .../protocols/hmi/controller.set_params.json | 3 +- .../protocols/hmi/device.get_params.json | 3 +- .../protocols/hmi/diagnosis.get_params.json | 3 +- .../files/protocols/hmi/diagnosis.open.json | 3 +- .../files/protocols/hmi/diagnosis.save.json | 3 +- .../protocols/hmi/diagnosis.set_params.json | 3 +- .../files/protocols/hmi/drag.get_params.json | 3 +- .../files/protocols/hmi/drag.set_params.json | 3 +- .../hmi/fieldbus_device.get_params.json | 3 +- .../hmi/fieldbus_device.load_cfg.json | 3 +- .../hmi/fieldbus_device.set_params.json | 3 +- .../protocols/hmi/io_device.load_cfg.json | 3 +- .../files/protocols/hmi/jog.get_params.json | 3 +- assets/files/protocols/hmi/log_code.data.json | 6 +- .../protocols/hmi/modbus.get_params.json | 3 +- .../protocols/hmi/modbus.get_values.json | 3 +- .../files/protocols/hmi/modbus.load_cfg.json | 3 +- .../protocols/hmi/modbus.set_params.json | 3 +- .../protocols/hmi/move.get_joint_pos.json | 3 +- .../protocols/hmi/move.get_monitor_cfg.json | 3 +- .../files/protocols/hmi/move.get_params.json | 3 +- assets/files/protocols/hmi/move.get_pos.json | 3 +- .../hmi/move.get_quickstop_distance.json | 3 +- .../protocols/hmi/move.get_quickturn_pos.json | 3 +- .../files/protocols/hmi/move.quick_turn.json | 3 +- .../protocols/hmi/move.set_monitor_cfg.json | 3 +- .../files/protocols/hmi/move.set_params.json | 4 +- .../hmi/move.set_quickstop_distance.json | 3 +- .../protocols/hmi/move.set_quickturn_pos.json | 3 +- assets/files/protocols/hmi/move.stop.json | 3 +- .../protocols/hmi/overview.get_autoload.json | 3 +- .../protocols/hmi/overview.get_cur_prj.json | 3 +- .../files/protocols/hmi/overview.reload.json | 3 +- .../protocols/hmi/overview.set_autoload.json | 3 +- .../protocols/hmi/register.set_value.json | 3 +- .../protocols/hmi/rl_task.pp_to_main.json | 3 +- assets/files/protocols/hmi/rl_task.run.json | 3 +- .../protocols/hmi/rl_task.set_run_params.json | 3 +- assets/files/protocols/hmi/rl_task.stop.json | 3 +- .../safety.safety_area.overall_enable.json | 6 +- ...safety.safety_area.safety_area_enable.json | 6 +- .../hmi/safety.safety_area.set_param.json | 6 +- .../hmi/safety.safety_area.signal_enable.json | 6 +- .../files/protocols/hmi/safety_area_data.json | 6 +- .../protocols/hmi/servo.clear_alarm.json | 3 +- .../protocols/hmi/socket.get_params.json | 3 +- .../protocols/hmi/socket.set_params.json | 3 +- .../protocols/hmi/soft_limit.get_params.json | 3 +- .../protocols/hmi/soft_limit.set_params.json | 3 +- .../files/protocols/hmi/state.get_state.json | 3 +- .../protocols/hmi/state.get_tp_mode.json | 3 +- .../protocols/hmi/state.set_tp_mode.json | 3 +- .../protocols/hmi/state.switch_auto.json | 3 +- .../protocols/hmi/state.switch_manual.json | 3 +- .../protocols/hmi/state.switch_motor_off.json | 3 +- .../protocols/hmi/state.switch_motor_on.json | 3 +- .../hmi/system_io.query_configuration.json | 3 +- .../hmi/system_io.query_event_cfg.json | 3 +- .../hmi/system_io.update_configuration.json | 3 +- .../md/ctrl_estop_reset_clear_alarm.txt | 3 + assets/files/protocols/md/ctrl_motor_off.txt | 3 + assets/files/protocols/md/ctrl_motor_on.txt | 3 + assets/files/protocols/md/ctrl_soft_estop.txt | 1 + assets/files/protocols/md/sta_alarm.txt | 1 + assets/files/protocols/md/sta_estop.txt | 1 + assets/files/protocols/md/sta_motor.txt | 1 + assets/files/protocols/md/sta_soft_estop.txt | 1 + assets/files/version/{version => local_vers} | 0 assets/files/version/server_vers | 1 + assets/media/splash.png | Bin 0 -> 14920 bytes code/aio.py | 518 --------- code/common/clibs.py | 62 - code/durable/create_plot.py | 85 -- code/durable/factory_test.py | 239 ---- code/ui/login_window.py | 135 --- code/ui/reset_window.py | 170 --- codes/__init__.py | 0 codes/analysis/__init__.py | 0 {code => codes}/analysis/brake.py | 51 +- {code => codes}/analysis/current.py | 85 +- {code => codes}/analysis/iso.py | 3 +- {code => codes}/analysis/wavelogger.py | 17 +- codes/autotest/__init__.py | 0 {code => codes}/autotest/do_brake.py | 89 +- {code => codes}/autotest/do_current.py | 79 +- codes/common/__init__.py | 0 codes/common/clibs.py | 103 ++ {code => codes}/common/openapi.py | 518 ++++----- codes/durable/__init__.py | 0 codes/durable/factory_test.py | 293 +++++ codes/ui/__init__.py | 0 {code => codes}/ui/main_window.py | 68 +- code/test.py => test.py | 28 +- ui/main.ui | 170 ++- 110 files changed, 2180 insertions(+), 1797 deletions(-) create mode 100644 aio.py create mode 100644 assets/files/protocols/ec/motor_off.txt create mode 100644 assets/files/protocols/ec/motor_on.txt create mode 100644 assets/files/protocols/ec/motor_on_state.txt create mode 100644 assets/files/protocols/ec/operating_mode.txt create mode 100644 assets/files/protocols/ec/robot_running_state.txt create mode 100644 assets/files/protocols/ec/set_do.txt create mode 100644 assets/files/protocols/ec/switch_mode_auto.txt create mode 100644 assets/files/protocols/ec/switch_mode_manual.txt create mode 100644 assets/files/protocols/md/ctrl_estop_reset_clear_alarm.txt create mode 100644 assets/files/protocols/md/ctrl_motor_off.txt create mode 100644 assets/files/protocols/md/ctrl_motor_on.txt create mode 100644 assets/files/protocols/md/ctrl_soft_estop.txt create mode 100644 assets/files/protocols/md/sta_alarm.txt create mode 100644 assets/files/protocols/md/sta_estop.txt create mode 100644 assets/files/protocols/md/sta_motor.txt create mode 100644 assets/files/protocols/md/sta_soft_estop.txt rename assets/files/version/{version => local_vers} (100%) create mode 100644 assets/files/version/server_vers create mode 100644 assets/media/splash.png delete mode 100644 code/aio.py delete mode 100644 code/common/clibs.py delete mode 100644 code/durable/create_plot.py delete mode 100644 code/durable/factory_test.py delete mode 100644 code/ui/login_window.py delete mode 100644 code/ui/reset_window.py create mode 100644 codes/__init__.py create mode 100644 codes/analysis/__init__.py rename {code => codes}/analysis/brake.py (78%) rename {code => codes}/analysis/current.py (81%) rename {code => codes}/analysis/iso.py (99%) rename {code => codes}/analysis/wavelogger.py (86%) create mode 100644 codes/autotest/__init__.py rename {code => codes}/autotest/do_brake.py (79%) rename {code => codes}/autotest/do_current.py (77%) create mode 100644 codes/common/__init__.py create mode 100644 codes/common/clibs.py rename {code => codes}/common/openapi.py (81%) create mode 100644 codes/durable/__init__.py create mode 100644 codes/durable/factory_test.py create mode 100644 codes/ui/__init__.py rename {code => codes}/ui/main_window.py (93%) rename code/test.py => test.py (84%) diff --git a/.gitignore b/.gitignore index b40a6ad..0914769 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ venv/ .idea/ **/__pycache__/ -assets/files/examples/ \ No newline at end of file +assets/files/examples/ +assets/logs/* +test.py +package/* \ No newline at end of file diff --git a/aio.py b/aio.py new file mode 100644 index 0000000..9ceec6a --- /dev/null +++ b/aio.py @@ -0,0 +1,1022 @@ +import datetime +import json +import os +import re +import sqlite3 +import sys +import time +from urllib import request +import os.path +import matplotlib +import matplotlib.pyplot as plt +import pandas +from matplotlib.widgets import Slider +matplotlib.use('QtAgg') + +from PySide6.QtCore import Qt, QThread, Signal, QObject, QTimer +from PySide6.QtGui import QTextCursor, QFont, QPixmap, QColor, QBrush +from PySide6.QtWidgets import QMessageBox, QCheckBox, QSplashScreen, QApplication, QFrame, QLabel, QTreeWidgetItem, QFileDialog, QHeaderView, QDialog, QVBoxLayout, QPlainTextEdit + +import codes.common.clibs as clibs +import codes.common.openapi as openapi +import codes.ui.main_window as main_window +from codes.analysis import brake, current, wavelogger, iso +from codes.autotest import do_current, do_brake +from codes.durable import factory_test + + +class MultiWindows: + login_window = None + reset_window = None + main_window = None + + +class ContentDialog(QDialog): + def __init__(self, content, parent=None): + super().__init__(parent) + self.setWindowTitle("日志详情") + self.setGeometry(100, 100, 700, 300) + + # 设置对话框在屏幕正中间 + self.center_on_screen() + + # 创建布局 + layout = QVBoxLayout(self) + + # 创建 QPlainTextEdit 并设置为只读 + self.plain_text_edit = QPlainTextEdit() + self.plain_text_edit.setReadOnly(True) + self.plain_text_edit.setPlainText(content) + + # 添加到布局 + layout.addWidget(self.plain_text_edit) + + def center_on_screen(self): + # 获取屏幕尺寸 + screen_geometry = QApplication.primaryScreen().geometry() + screen_width = screen_geometry.width() + screen_height = screen_geometry.height() + + # 获取对话框尺寸 + dialog_width = self.width() + dialog_height = self.height() + + # 计算位置 + x = (screen_width - dialog_width) // 2 + y = (screen_height - dialog_height) // 2 + + # 设置位置 + self.move(x, y) + + +class RunProg(QObject): + completed = Signal(tuple) + + def __init__(self): + super().__init__() + + def program(self, action): # 0: prog 1: idx + prog, idx, reserved = action + if idx in range(7): + run = prog.processing + elif idx == -1: + run = prog.net_conn + elif idx == -97: + run = prog.do_draw + elif idx == -98 or idx == -99: + run = prog + + try: + ret = run() + self.completed.emit((True, prog, ret, "", idx, reserved)) # 运行是否成功/返回值/报错信息/idx + except Exception as err: + self.completed.emit((False, None, None, err, idx, reserved)) # 运行是否成功/返回值/报错信息/idx + + +class MainWindow(main_window.Ui_MainWindow): + action = Signal(tuple) + + def __init__(self): + super(MainWindow, self).__init__() + self.close_on_net_error = None + self.is_searching = False + self.setupUi(self) + self.predoes() + + def predoes(self): + # ========================= treeview init ========================= + self.treew_log.header().setFont(QFont("Arial", 12, QFont.Weight.Bold)) + header = self.treew_log.header() + for i in range(self.treew_log.columnCount()): + header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents) + # ========================= clibs ========================= + self.setup_statusbar() + # ========================= styleSheet ========================= + tws = [self.tw_funcs, self.tw_docs] + for tw in tws: + tw.setStyleSheet(""" + QTabBar::tab:selected { + background: #0078D4; + color: white; + border-radius: 4px; + } + QTabBar::tab:!selected { + background: #F0F0F0; + color: #333; + } + QTabWidget::pane { + border: 1px solid #CCCCCC; + } + """) + + # ============================↓↓↓debug↓↓↓============================ + # print(f"self.cb_data_func.currentIndex() = {self.cb_data_func.currentIndex()}") + + def run_program_thread(self, prog, idx, prog_done, reserved): + if idx != -98 and idx != -97: + self.tw_docs.setCurrentIndex(0) + if idx != -99 and idx != -98: + prog.output.connect(self.w2t) + + self.t = QThread(self) + self.run = RunProg() + self.run.moveToThread(self.t) + self.run.completed.connect(prog_done) + self.action.connect(self.run.program) + self.t.start() + self.action.emit((prog, idx, reserved)) + + def w2t(self, msg, color="black"): + self.pte_output.appendHtml(f"{msg}") + cursor = self.pte_output.textCursor() + cursor.movePosition(QTextCursor.End) + self.pte_output.setTextCursor(cursor) + self.pte_output.ensureCursorVisible() + self.update() + + def get_checkbox_states(self): + cb_durable_states = {} + container = self.sa_durable.widget() + for checkbox in container.findChildren(QCheckBox): + cb_durable_states[checkbox.text()] = checkbox.isChecked() + return cb_durable_states + + def prog_start(self): + def prog_done(results): + flag, result, ret, error, idx, network = results + clibs.running[idx] = 0 + # if flag is False: + # self.w2t(f"{clibs.functions[idx]}运行失败:{error}", "red") + # elif flag is True: + # ... + + if sum(clibs.running) > 0: + if sum(clibs.running) == 1: + QMessageBox.critical(self, "运行中", f"{clibs.functions[clibs.running.index(1)]}正在执行中,不可同时运行两个处理/测试程序!") + return + else: + self.w2t(f"clibs.running = {clibs.running}", "red") + self.w2t(f"clibs.functions = {clibs.functions}", "red") + QMessageBox.critical(self, "严重错误", "理论上不允许同时运行两个处理程序,需要检查!") + return + + if self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 0: # tab: 数据处理 | func: 制动数据处理 + self.run_program_thread(brake.BrakeDataProcess(self.le_data_path.text()), 0, prog_done, None) + elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 1: # tab: 数据处理 | func: 转矩数据处理 + self.run_program_thread(current.CurrentDataProcess(self.le_data_path.text(), self.cb_data_current.currentText()), 1, prog_done, None) + elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 2: # tab: 数据处理 | func: 激光数据处理 + self.run_program_thread(iso.IsoDataProcess(self.le_data_path.text()), 2, prog_done, None) + elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 3: # tab: 数据处理 | func: 精度数据处理 + self.run_program_thread(wavelogger.WaveloggerDataProcess(self.le_data_path.text()), 3, prog_done, None) + elif self.tw_funcs.currentIndex() == 1 and self.cb_unit_func.currentIndex() == 0: # tab: 自动测试 | func: 制动测试 + self.run_program_thread(do_brake.DoBrakeTest(self.le_unit_path.text(), self.cb_unit_tool.currentText()), 4, prog_done, None) + elif self.tw_funcs.currentIndex() == 1 and self.cb_unit_func.currentIndex() == 1: # tab: 自动测试 | func: 转矩测试 + self.run_program_thread(do_current.DoCurrentTest(self.le_unit_path.text(), self.cb_unit_tool.currentText()), 5, prog_done, None) + elif self.tw_funcs.currentIndex() == 2: # tab: 耐久采集 | func: 场景数据持久化 + self.run_program_thread(factory_test.DoFactoryTest(self.le_durable_path.text(), self.le_durable_interval.text(), self.get_checkbox_states()), 6, prog_done, None) + + def prog_stop(self): + QMessageBox.warning(self, "停止运行", "运行过程中不建议停止运行,可能会损坏文件,如果确实需要停止运行,可以直接关闭窗口!") + + def prog_reset(self): + self.pte_output.clear() + + def file_browser(self): + idx_dict = {0: self.le_data_path, 1: self.le_unit_path, 2: self.le_durable_path} + dir_path = QFileDialog.getExistingDirectory() + tab_index = self.tw_funcs.currentIndex() + if dir_path: + idx_dict[tab_index].setText(dir_path) + + def curve_draw(self): + output = Signal(str, str) + procs = self.get_checkbox_states() + dir_path = self.le_durable_path.text() + curve_map = { + "周期内平均转矩": ["device_servo_trq_feedback", ], + "周期内最大速度": ["hw_joint_vel_feedback", ], + } + ylabels = {"周期内最大速度": "速度(rad/s)", "周期内平均转矩": "转矩(Nm)"} + plt.rcParams['font.sans-serif'] = ['SimHei'] + plt.rcParams['axes.unicode_minus'] = False + plt.rcParams['figure.dpi'] = 100 + plt.rcParams['font.size'] = 14 + plt.rcParams['lines.marker'] = 'o' + plt.rcParams["figure.autolayout"] = True + + if not os.path.exists(dir_path): + self.w2t(f"数据文件夹{dir_path}不存在,请确认后重试......", "red") + return + for proc_name, is_enabled in procs.items(): + if is_enabled: + break + else: + self.w2t(f"需要选择至少一个功能,才能继续绘图......", "red") + return + + _, files = clibs.traversal_files(dir_path, output) + csv_files = [] + for file in files: + if file.endswith(".csv"): + csv_files.append(file) + + if len(csv_files) == 0: + self.w2t("程序未开始运行,暂无数据可以展示......", "red") + return + + for proc_name, is_enabled in procs.items(): + if not is_enabled: + continue + + title = proc_name + ylabel = ylabels[proc_name] + fig, axes = plt.subplots(figsize=(10, 4.5), dpi=100) + for curve in curve_map[proc_name]: + cols = [f"{curve}_{i}" for i in range(6)] + cols.insert(0, "time") + + try: + df = pandas.read_csv(f"{dir_path}/{proc_name}.csv") + except Exception as err: + self.w2t(f"获取{dir_path}/{proc_name}.csv文件数据失败,需确认是否存在该文件......", "red") + self.w2t(f"报错信息:{err}", "red") + return + + for i in range((len(cols) - 1) // 6): + plt.plot(df[cols[1]], label=f"一轴{curve_map[proc_name][i]}") + plt.plot(df[cols[2]], label=f"二轴{curve_map[proc_name][i]}") + plt.plot(df[cols[3]], label=f"三轴{curve_map[proc_name][i]}") + plt.plot(df[cols[4]], label=f"四轴{curve_map[proc_name][i]}") + plt.plot(df[cols[5]], label=f"五轴{curve_map[proc_name][i]}") + plt.plot(df[cols[6]], label=f"六轴{curve_map[proc_name][i]}") + axes.set_title(title) + axes.set_ylabel(ylabel) + axes.legend(loc="upper right") + + slider_position = plt.axes((0.1, 0.01, 0.8, 0.05), facecolor="blue") # (left, bottom, width, height) + scrollbar = Slider(slider_position, 'Time', 1, int(len(df)), valstep=1) + + def update(val): + pos = scrollbar.val + axes.set_xlim([pos, pos + 10]) + fig.canvas.draw_idle() + + scrollbar.on_changed(update) + fig.tight_layout(rect=(0, 0.02, 0.96, 1)) # tuple (left, bottom, right, top) + + plt.show() + + def durable_cb_change(self): + stat = True if self.cb_durable_total.isChecked() else False + for cb in self.sa_durable.widget().findChildren(QCheckBox): + cb.setChecked(stat) + + def display_tree_content(self, records): + for record in records: + item = QTreeWidgetItem(self.treew_log) + for i in range(self.treew_log.columnCount()): + item.setText(i, str(record[i])) + self.treew_log.addTopLevelItem(item) + color_map = {"DEBUG": QColor(220, 220, 220), "INFO": QColor(144, 238, 144), "WARNING": QColor(255, 240, 210), "ERROR": QColor(255, 220, 220)} + brush = QBrush(color_map[record[2].upper()]) + for i in range(self.treew_log.columnCount()): + item.setBackground(i, brush) + + last_item = self.treew_log.topLevelItem(self.treew_log.topLevelItemCount() - 1) + self.treew_log.scrollToItem(last_item) + + def prog_done_pre(self, results): + flag, result, ret, error, idx, network = results + if ret is None: + return + + pages_all, current_page, records = ret + self.treew_log.clear() + self.display_tree_content(records) + self.label_page.setText(f"{current_page}/{pages_all}") + + def pre_page(self): + @clibs.db_lock + def do_pre(): + if self.is_searching is False: + first_id = self.treew_log.topLevelItem(0).text(0) + end = int(first_id)-1 if int(first_id)-1 > 0 else None + start = int(first_id)-100 if int(first_id)-100 > 0 else 0 + if end is None: + return + + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end}") + records = clibs.cursor.fetchall() + clibs.cursor.execute("SELECT COUNT(id) FROM logs") + len_records = clibs.cursor.fetchone()[0] + pages_all = len_records // 100 if len_records % 100 == 0 else len_records // 100 + 1 + current_page = int(end) // 100 if int(end) % 100 == 0 else int(end) // 100 + 1 + return pages_all, current_page, records + + elif self.is_searching is True: + current_page, pages_all = self.label_page.text().split("/") + if int(current_page)-1 == 0: + return + + current_page = int(current_page) - 1 + first_row = [] + for i in range(self.treew_log.columnCount()): + if i == 0: + first_row.append(int(self.treew_log.topLevelItem(0).text(i))) + else: + first_row.append(self.treew_log.topLevelItem(0).text(i)) + + index_end = clibs.search_records.index(tuple(first_row)) + if index_end == 0: + return + elif index_end <= 100: + records = clibs.search_records[:index_end] + else: + records = clibs.search_records[index_end-100:index_end] + return pages_all, current_page, records + + self.run_program_thread(do_pre, -98, self.prog_done_pre, None) + + def prog_done_realtime(self, results): + flag, result, ret, error, idx, network = results + pages_all, records = ret + + self.label_page.setText(f"{pages_all}/{pages_all}") + self.treew_log.clear() + self.display_tree_content(records) + self.is_searching = False + clibs.search_records = None + + def realtime_page(self): + @clibs.db_lock + def do_realtime(): + clibs.cursor.execute("SELECT COUNT(id) from logs") + len_records = clibs.cursor.fetchone()[0] + pages_all = len_records // 100 if len_records % 100 == 0 else len_records // 100 + 1 + remainder = len_records % 100 if len_records % 100 != 0 else 100 + end = len_records + start = len_records - remainder + 1 if len_records - remainder > 0 else 0 + clibs.cursor.execute(f"select * from logs where id between {start} and {end}") + records = clibs.cursor.fetchall() + return pages_all, records + + self.run_program_thread(do_realtime, -98, self.prog_done_realtime, None) + + def prog_done_next(self, results): + flag, result, ret, error, idx, network = results + if ret is None: + return + pages_all, current_page, records = ret + self.treew_log.clear() + self.display_tree_content(records) + self.label_page.setText(f"{current_page}/{pages_all}") + + def next_page(self): + @clibs.db_lock + def do_next(): + clibs.cursor.execute("select count(id) from logs") + len_records = clibs.cursor.fetchone()[0] + if self.is_searching is False: + line_number = self.treew_log.topLevelItemCount() + last_id = int(self.treew_log.topLevelItem(line_number-1).text(0)) + start = last_id + 1 if last_id % 100 == 0 else last_id - last_id % 100 + 1 + end = int(last_id) + 100 + if int(start) <= len_records: + clibs.cursor.execute(f"select * from logs where id between {start} and {end}") + records = clibs.cursor.fetchall() + pages_all = len_records // 100 if len_records % 100 == 0 else len_records // 100 + 1 + current_page = int(start) // 100 if int(start) % 100 == 0 else int(start) // 100 + 1 + return pages_all, current_page, records + + elif self.is_searching is True: + current_page, pages_all = self.label_page.text().split("/") + if pages_all == current_page: + return + + current_page = int(current_page) + 1 + last_row = [] + for i in range(self.treew_log.columnCount()): + if i == 0: + last_row.append(int(self.treew_log.topLevelItem(self.treew_log.topLevelItemCount()-1).text(i))) + else: + last_row.append(self.treew_log.topLevelItem(self.treew_log.topLevelItemCount()-1).text(i)) + index_start = clibs.search_records.index(tuple(last_row)) + 1 + if index_start > len(clibs.search_records): + return + elif index_start + 100 > len(clibs.search_records): + records = clibs.search_records[index_start:] + else: + records = clibs.search_records[index_start:index_start+100] + return pages_all, current_page, records + + self.run_program_thread(do_next, -98, self.prog_done_next, None) + + def show_item_content(self, item, column): + content = " | ".join([item.text(i) for i in range(self.treew_log.columnCount())]) + dialog = ContentDialog(content, self) + dialog.exec() + + def switch_log_tab(self, index): + if index == 1: # 切换到日志tab页时,自动执行实时日志功能 + self.realtime_page() + + def prog_done_search(self, results): + msg = "可单独查询 ID/Level/Module,也可以按照顺序组合查询指定条件,ID 范围使用 - 分隔,多个 Level 或者 Module 使用 : 分割,所有查询条件以 / 分隔并包含在 [] 内;如有关键字查询,则置于条件查询之后!" + flag, result, ret, error, idx, network = results + if ret is None: + QMessageBox.warning(self, "查询条件错误", msg) + self.realtime_page() + return + if ret == "nothing": + return + + pages_all, records = ret + self.treew_log.clear() + self.display_tree_content(records) + self.label_page.setText(f"{pages_all}/{pages_all}") + self.is_searching = True + + def search_keyword(self): + @clibs.db_lock + def do_search(): + one_more = "!@#123asd" + kw = self.le_docs_search.text().strip() + if kw == "": + return "nothing" + + match = re.search("^\\[.*]", kw) + if match: + condition = match.group().removeprefix("[").removesuffix("]").strip() + f_text = f"%{kw.removeprefix(match.group()).strip()}%" + if condition == "" and f_text == "%%": + return + elif condition == "" and f_text != "%%": + clibs.cursor.execute(f"SELECT * FROM logs WHERE content LIKE '{f_text}'") + elif condition != "" and f_text == "%%": + conditions = condition.split("/") + if len(conditions) == 1: # 可能是id/level(s)/module(s),任意其一 + c1 = conditions[0] + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)}") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int(c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end}") + else: + for level in c1.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c1.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE level IN {levels}") + else: # 是模块 + modules = tuple(module.strip() for module in c1.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE module IN {modules}") + + elif len(conditions) == 2: # 可能是id/level(s)/module(s),任意两个的组合,有顺序 + c1, c2 = conditions + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND level IN {levels}") + else: # 是模块 + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND module IN {modules}") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int(c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND level IN {levels}") + else: # 是模块 + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND module IN {modules}") + else: # c1是等级,c2是模块 + for level in c1.split(":"): + if level.strip().upper() not in clibs.levels: + return + + levels = tuple(level.strip().upper() for level in c1.split(":")) + levels = (*levels, one_more) + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE level IN {levels} AND module IN {modules}") + + elif len(conditions) == 3: # 依次为id/level(s)/module(s) + c1, c2, c3 = conditions + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + return + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + modules = tuple(module.strip() for module in c3.split(":")) + modules = (*modules, one_more) + + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND level IN {levels} AND module IN {modules}") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int(c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND level IN {levels} AND module IN {modules}") + else: + return + else: + return + elif condition != "" and f_text != "%%": # 和上面基本一样,只不过加一个f_text的条件筛选 + conditions = condition.split("/") + if len(conditions) == 1: # 可能是id/level(s)/module(s),任意其一 + c1 = conditions[0] + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND content LIKE '{f_text}'") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[ + 1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int( + c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND content LIKE '{f_text}'") + else: + for level in c1.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c1.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE level IN {levels} AND content LIKE '{f_text}'") + else: # 是模块 + modules = tuple(module.strip() for module in c1.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE module IN {modules} AND content LIKE '{f_text}'") + + elif len(conditions) == 2: # 可能是id/level(s)/module(s),任意两个的组合,有顺序 + c1, c2 = conditions + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND level IN {levels} AND content LIKE '{f_text}'") + else: # 是模块 + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE id = {int(c1)} AND module IN {modules} AND content LIKE '{f_text}'") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[ + 1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int( + c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + is_level = False + break + else: + is_level = True + + if is_level: # 是告警等级 + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + clibs.cursor.execute( + f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND level IN {levels} AND content LIKE '{f_text}'") + else: # 是模块 + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute( + f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND module IN {modules} AND content LIKE '{f_text}'") + else: # c1是等级,c2是模块 + for level in c1.split(":"): + if level.strip().upper() not in clibs.levels: + return + + levels = tuple(level.strip().upper() for level in c1.split(":")) + levels = (*levels, one_more) + modules = tuple(module.strip() for module in c2.split(":")) + modules = (*modules, one_more) + clibs.cursor.execute(f"SELECT * FROM logs WHERE level IN {levels} AND module IN {modules} AND content LIKE '{f_text}'") + + elif len(conditions) == 3: # 依次为id/level(s)/module(s) + c1, c2, c3 = conditions + for level in c2.split(":"): + if level.strip().upper() not in clibs.levels: + return + levels = tuple(level.strip().upper() for level in c2.split(":")) + levels = (*levels, one_more) + modules = tuple(module.strip() for module in c3.split(":")) + modules = (*modules, one_more) + + if c1.isdigit() and int(c1) > 0: # 单独一个数字 + clibs.cursor.execute( + f"SELECT * FROM logs WHERE id = {int(c1)} AND level IN {levels} AND module IN {modules} AND content LIKE '{f_text}'") + elif "-" in c1 and len(c1.split("-")) == 2 and c1.split("-")[0].isdigit() and c1.split("-")[ + 1].isdigit() and int(c1.split("-")[1]) - int(c1.split("-")[0]) > 0 and int( + c1.split("-")[0]) > 0: # 1-4 这种格式 + start = int(c1.split("-")[0]) + end = int(c1.split("-")[1]) + clibs.cursor.execute( + f"SELECT * FROM logs WHERE id BETWEEN {start} AND {end} AND level IN {levels} AND module IN {modules} AND content LIKE '{f_text}'") + else: + return + else: + return + else: + f_text = f"%{kw}%" + clibs.cursor.execute(f"SELECT * FROM logs WHERE content LIKE '{f_text}'") + + clibs.search_records = clibs.cursor.fetchall() + len_records = len(clibs.search_records) + pages_all = len_records // 100 if len_records % 100 == 0 else len_records // 100 + 1 + remainder = len_records % 100 + records = clibs.search_records[-1 * remainder:] + + return pages_all, records + + self.run_program_thread(do_search, -98, self.prog_done_search, None) + + def prog_done_conn(self, results): + self.btn_hmi_conn.setDisabled(False) + self.btn_md_conn.setDisabled(False) + self.btn_ec_conn.setDisabled(False) + + flag, result, ret, error, idx, network = results + if flag is False: + ... + # self.w2t(f"{network.upper()}连接失败", "red") + elif flag is True: + clibs.status[network] = 1 + if network == "hmi": + self.btn_hmi_conn.setText("断开") + clibs.c_hr = result + elif network == "md": + self.btn_md_conn.setText("断开") + clibs.c_md = result + elif network == "ec": + self.btn_ec_conn.setText("断开") + clibs.c_ec = result + + def prog_done_disconn(self, results): + self.btn_hmi_conn.setDisabled(False) + self.btn_md_conn.setDisabled(False) + self.btn_ec_conn.setDisabled(False) + + flag, result, ret, error, idx, network = results + if flag is False: + self.w2t(f"{network.upper()}断开连接失败", "red") + elif flag is True: + clibs.status[network] = 0 + if network == "hmi": + self.btn_hmi_conn.setText("连接") + clibs.c_hr = result + elif network == "md": + self.btn_md_conn.setText("连接") + clibs.c_md = result + elif network == "ec": + self.btn_ec_conn.setText("连接") + clibs.c_ec = result + + def hmi_conn(self): + self.btn_hmi_conn.setDisabled(True) + if self.btn_hmi_conn.text() == "连接": + clibs.ip_addr = self.le_hmi_ip.text().strip() + ip_pattern = re.compile(r"(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])") + if not ip_pattern.fullmatch(clibs.ip_addr): + self.w2t(f"{clibs.ip_addr} 不是一个有效的 IP 地址", "red") + return + self.run_program_thread(openapi.HmiRequest(clibs.ip_addr, clibs.socket_port, clibs.xService_port), -1, self.prog_done_conn, "hmi") + elif self.btn_hmi_conn.text() == "断开": + self.run_program_thread(clibs.c_hr.close, -99, self.prog_done_disconn, "hmi") + + def md_conn(self): + if clibs.status["hmi"] == 0: + QMessageBox.warning(self, "告警", "操作Modbus连接之前,需要先打开HMI连接!") + return + + self.btn_md_conn.setDisabled(True) + if self.btn_md_conn.text() == "连接": + clibs.modbus_port = self.le_md_port.text().strip() + self.run_program_thread(openapi.ModbusRequest(clibs.ip_addr, clibs.modbus_port), -1, self.prog_done_conn, "md") + elif self.btn_md_conn.text() == "断开": + self.run_program_thread(clibs.c_md.close, -99, self.prog_done_disconn, "md") + + def ec_conn(self): + if clibs.status["hmi"] == 0: + QMessageBox.warning(self, "告警", "操作外部通信连接之前,需要先打开HMI连接!") + return + + self.btn_ec_conn.setDisabled(True) + if self.btn_ec_conn.text() == "连接": + clibs.external_port = self.le_ec_port.text().strip() + self.run_program_thread(openapi.ExternalCommunication(clibs.ip_addr, clibs.external_port), -1, self.prog_done_conn, "ec") + elif self.btn_ec_conn.text() == "断开": + self.run_program_thread(clibs.c_ec.close, -99, self.prog_done_disconn, "ec") + + def hmi_page(self): + self.sw_network.setCurrentIndex(0) + + def md_page(self): + self.sw_network.setCurrentIndex(1) + + def ec_page(self): + self.sw_network.setCurrentIndex(2) + + def prog_done_hmi_send(self, results): + _, result, ret, error, idx, (msg_id, flag) = results + records = clibs.c_hr.get_from_id(msg_id) + for record in records: + if "请求发送成功" not in record[0]: + self.pte_him_recv.clear() + response = eval(record[0]) if flag == 0 else json.loads(record[0]) + self.pte_him_recv.appendPlainText(json.dumps(response, indent=4, separators=(",", ":"))) + else: + self.btn_hmi_send.setDisabled(False) + + def hmi_send(self): + def hmi_send_thread(): + nonlocal hmi_dict, cmd_json, flag + if flag == 0: + clibs.c_hr.c.send(clibs.c_hr.package(cmd_json)) + clibs.c_hr.logger("DEBUG", "aio", f"hmi: [send] 老协议请求发送成功 {cmd_json}") + elif flag == 1: + clibs.c_hr.c_xs.send(clibs.c_hr.package_xs(hmi_dict)) + clibs.c_hr.logger("DEBUG", "aio", f"hmi: xService请求发送成功 {cmd_json}") + + if clibs.status["hmi"] == 0: + QMessageBox.critical(self, "错误", "使用该功能之前,需要先打开HMI连接!") + return + if self.pte_hmi_send.toPlainText() == "": + return + + self.btn_hmi_send.setDisabled(True) + hmi_dict = json.loads(self.pte_hmi_send.toPlainText()) + t = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") + msg_id = hmi_dict["id"] = "@".join([hmi_dict["id"].split("@")[0], t]) + self.pte_hmi_send.clear() + self.pte_hmi_send.appendPlainText(json.dumps(hmi_dict, indent=4, separators=(",", ":"))) + flag = hmi_dict["p_type"] + del hmi_dict["p_type"] + cmd_json = json.dumps(hmi_dict, separators=(",", ":")) + + self.run_program_thread(hmi_send_thread, -99, self.prog_done_hmi_send, (msg_id, flag)) + + def md_send(self): + if clibs.status["md"] == 0: + QMessageBox.critical(self, "连接错误", "使用该功能之前,需要先打开 Modbus 连接!") + return + if self.pte_md_send.toPlainText() == "": + return + + self.pte_md_recv.clear() + content = self.pte_md_send.toPlainText().split("\n") + if content[0].strip().startswith("sta"): + for item in content[1:]: + addr = int(item.split(":")[0].strip()) + count = int(item.split(":")[1].strip()) + value_type = item.split(":")[2].strip() + if value_type == "bool": + try: + result = clibs.c_md.c.read_holding_registers(addr, count=count).registers[0] + self.pte_md_recv.appendPlainText(str(result)) + except Exception as err: + self.pte_md_recv.appendPlainText(f"获取失败:{err}") + return + elif content[0].strip().startswith("ctrl"): + for item in content[1:]: + addr = int(item.split(":")[0].strip()) + value = int(item.split(":")[1].strip()) + try: + clibs.c_md.c.write_register(addr, value) + time.sleep(clibs.INTERVAL/4) + except Exception as err: + self.pte_md_recv.appendPlainText(f"操作失败:{err}") + return + else: + self.pte_md_recv.appendPlainText("操作成功!") + else: + QMessageBox.critical(self, "格式错误", "非法的发送内容,自定义发送需参考已有的格式!") + + def ec_send(self): + if clibs.status["ec"] == 0: + QMessageBox.critical(self, "错误", "使用该功能之前,需要先打开MD连接!") + return + if self.pte_ec_send.toPlainText() == "": + return + + self.pte_ec_recv.clear() + cmd = self.pte_ec_send.toPlainText().strip() + try: + clibs.c_ec.s_string(cmd) + time.sleep(clibs.INTERVAL/2) + result = clibs.c_ec.r_string(cmd) + self.pte_ec_recv.appendPlainText(str(result)) + except Exception as err: + self.pte_ec_recv.appendPlainText(f"操作失败:{err}") + + def hmi_cb_change(self): + cmd = self.cb_hmi_cmd.currentText() + self.pte_hmi_send.clear() + self.pte_him_recv.clear() + with open(f"assets/files/protocols/hmi/{cmd}.json", mode="r", encoding="utf-8") as f_hmi: + hmi_dict = json.load(f_hmi) + t = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") + hmi_dict["id"] = "@".join([cmd, t]) + self.pte_hmi_send.appendPlainText(json.dumps(hmi_dict, indent=4, separators=(",", ":"))) + + def md_cb_change(self): + cmd = self.cb_md_cmd.currentText() + self.pte_md_send.clear() + self.pte_md_recv.clear() + self.pte_md_send.appendPlainText(cmd) + with open(f"assets/files/protocols/md/{cmd}.txt", mode="r", encoding="utf-8") as f_md: + c_send = f_md.read() + self.pte_md_send.appendPlainText(c_send) + + def ec_cb_change(self): + cmd = self.cb_ec_cmd.currentText() + self.pte_ec_send.clear() + self.pte_ec_recv.clear() + with open(f"assets/files/protocols/ec/{cmd}.txt", mode="r", encoding="utf-8") as f_md: + c_send = f_md.read() + self.pte_ec_send.appendPlainText(c_send) + + def check_interval(self): + try: + interval = float(self.le_durable_interval.text()) + interval = 300 if interval < 300 else int(interval) + except Exception: + interval = 300 + self.le_durable_interval.setText(str(interval)) + + def state_detection(self): + while True: + time.sleep(clibs.INTERVAL) + if clibs.status["hmi"] == 0 and self.btn_hmi_conn.text() == "断开": + self.btn_hmi_conn.setText("连接") + elif clibs.status["hmi"] == 1 and self.btn_hmi_conn.text() == "连接": + self.btn_hmi_conn.setText("断开") + + def closeEvent(self, event): + idx = -1 if clibs.running.count(1) == 0 else clibs.running.index(1) + info_text = "当前无程序正在运行,可放心退出!" if idx == -1 else f"当前正在运行{clibs.functions[idx]},确认退出?" + reply = QMessageBox.question(self, "退出", info_text) + if reply == QMessageBox.Yes: + os.chdir(clibs.log_path) + t = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + disk_conn = sqlite3.connect(f"log_{t}.db", isolation_level=None, check_same_thread=False, cached_statements=256) + + clibs.conn.backup(target=disk_conn, pages=1, progress=None) + + _, logs = clibs.traversal_files(".", None) + logs.sort() + while len(logs) > 10: + _ = logs.pop(0) + os.remove(_) + + if clibs.status["md"] == 1: + self.run_program_thread(clibs.c_md.close, -99, self.prog_done_disconn, "md") + if clibs.status["ec"] == 1: + self.run_program_thread(clibs.c_ec.close, -99, self.prog_done_disconn, "ec") + if clibs.status["hmi"] == 1: + self.run_program_thread(clibs.c_hr.close, -99, self.prog_done_disconn, "hmi") + + event.accept() + else: + event.ignore() + + def setup_statusbar(self): + with open(f"assets/files/version/local_vers", mode="r", encoding="utf-8") as f_local: + local_vers = f_local.read().strip() + l_version, update = local_vers.split("@") + vers_info = f" v{l_version} Update@{update}" + with open(f"assets/files/version/server_vers", mode="r", encoding="utf-8") as f_server: + server_vers = f_server.read().strip() + + update_label = QLabel() + version_label = QLabel() + self.statusbar.addWidget(version_label, 0) + self.statusbar.addPermanentWidget(update_label, 0) # 添加到右侧 + if local_vers == server_vers: + update_label.setText(' 当前是最新版本,继续保持! ') + elif local_vers > server_vers: + pass + elif local_vers < server_vers: + update_label.setText(f''' v{server_vers.split('@')[0]}已经发布,尽快更新至最新版本! ''') + + version_label.setText(f' {vers_info}') + update_label.setOpenExternalLinks(True) # 允许超链接在浏览器中打开 + # update_label.setAlignment(Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignRight) + update_label.setStyleSheet("border: none;") + update_label.setFrameShape(QFrame.Shape.NoFrame) + update_label.setFrameShadow(QFrame.Shadow.Plain) + self.close_on_net_error = False + + +class InitWork(QThread): + completed = Signal(str) + + def program(self, action): + url_vers, server_vers = "https://www.rustle.cc/server_vers", "" + try: + headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'} + req = request.Request(url_vers, headers=headers) + response = request.urlopen(req, timeout=clibs.INTERVAL * 10) + server_vers = response.read().decode("utf-8").strip() + with open("assets/files/version/server_vers", mode="w", encoding="utf-8") as f_server: + f_server.write(server_vers) + self.completed.emit("true") + except Exception as err: + print(f"err = {err}") + clibs.cursor.close() + clibs.conn.close() + self.completed.emit("false") + + +class SplashScreen(QApplication): + action = Signal(int) + + def __init__(self, argv): + super().__init__(argv) + self.window = None + + pixmap = QPixmap("./assets/media/splash.png") + self.splash = QSplashScreen(pixmap, Qt.WindowType.WindowStaysOnTopHint) + scaled_pixmap = pixmap.scaled(800, 400, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) + self.splash.setPixmap(scaled_pixmap) + self.splash.setEnabled(False) # 禁用交互 + self.splash.setFont(QFont("Arial", 12)) + self.splash.show() + self.splash.showMessage("正在加载资源.....", Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, Qt.GlobalColor.white) + + self.t = QThread(self) + self.run = InitWork() + self.run.moveToThread(self.t) + self.run.completed.connect(self.prog_done) + self.action.connect(self.run.program) + self.t.start() + self.action.emit(1) + + def prog_done(self, result): + if result == "false" or result == "": + self.splash.showMessage("网络错误,无法连接至服务器,确认当前网络环境是否可以访问www.rustle.cc......", Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, Qt.GlobalColor.white) + time.sleep(clibs.INTERVAL*2) + self.splash.close() + sys.exit() + elif result == "true": + self.window = MainWindow() + self.splash.showMessage("初始化完成,即将进入主界面......", Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter, Qt.GlobalColor.white) + QTimer.singleShot(1000, lambda: (self.splash.finish(self.window), self.window.show())) + + +if __name__ == '__main__': + app = SplashScreen(sys.argv) + sys.exit(app.exec()) diff --git a/assets/files/protocols/ec/motor_off.txt b/assets/files/protocols/ec/motor_off.txt new file mode 100644 index 0000000..78e4452 --- /dev/null +++ b/assets/files/protocols/ec/motor_off.txt @@ -0,0 +1 @@ +motor_off \ No newline at end of file diff --git a/assets/files/protocols/ec/motor_on.txt b/assets/files/protocols/ec/motor_on.txt new file mode 100644 index 0000000..b489817 --- /dev/null +++ b/assets/files/protocols/ec/motor_on.txt @@ -0,0 +1 @@ +motor_on \ No newline at end of file diff --git a/assets/files/protocols/ec/motor_on_state.txt b/assets/files/protocols/ec/motor_on_state.txt new file mode 100644 index 0000000..614bc7c --- /dev/null +++ b/assets/files/protocols/ec/motor_on_state.txt @@ -0,0 +1 @@ +motor_on_state \ No newline at end of file diff --git a/assets/files/protocols/ec/operating_mode.txt b/assets/files/protocols/ec/operating_mode.txt new file mode 100644 index 0000000..365cf9c --- /dev/null +++ b/assets/files/protocols/ec/operating_mode.txt @@ -0,0 +1 @@ +operating_mode \ No newline at end of file diff --git a/assets/files/protocols/ec/robot_running_state.txt b/assets/files/protocols/ec/robot_running_state.txt new file mode 100644 index 0000000..871016b --- /dev/null +++ b/assets/files/protocols/ec/robot_running_state.txt @@ -0,0 +1 @@ +robot_running_state \ No newline at end of file diff --git a/assets/files/protocols/ec/set_do.txt b/assets/files/protocols/ec/set_do.txt new file mode 100644 index 0000000..e2e2db5 --- /dev/null +++ b/assets/files/protocols/ec/set_do.txt @@ -0,0 +1 @@ +set_do:DO4_0,true \ No newline at end of file diff --git a/assets/files/protocols/ec/switch_mode_auto.txt b/assets/files/protocols/ec/switch_mode_auto.txt new file mode 100644 index 0000000..018094d --- /dev/null +++ b/assets/files/protocols/ec/switch_mode_auto.txt @@ -0,0 +1 @@ +switch_mode:auto \ No newline at end of file diff --git a/assets/files/protocols/ec/switch_mode_manual.txt b/assets/files/protocols/ec/switch_mode_manual.txt new file mode 100644 index 0000000..6da1fb2 --- /dev/null +++ b/assets/files/protocols/ec/switch_mode_manual.txt @@ -0,0 +1 @@ +switch_mode:manual \ No newline at end of file diff --git a/assets/files/protocols/hmi/collision.get_params.json b/assets/files/protocols/hmi/collision.get_params.json index 62b0a45..55c7d1a 100644 --- a/assets/files/protocols/hmi/collision.get_params.json +++ b/assets/files/protocols/hmi/collision.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "safety", - "command": "collision.get_params" + "command": "collision.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/collision.set_params.json b/assets/files/protocols/hmi/collision.set_params.json index 813f7ca..c3fe0bc 100644 --- a/assets/files/protocols/hmi/collision.set_params.json +++ b/assets/files/protocols/hmi/collision.set_params.json @@ -2,5 +2,6 @@ "id": "xxxxxxxxxxx", "module": "safety", "command": "collision.set_params", - "data": null + "data": null, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/collision.set_state.json b/assets/files/protocols/hmi/collision.set_state.json index 04bfbb5..628dac4 100644 --- a/assets/files/protocols/hmi/collision.set_state.json +++ b/assets/files/protocols/hmi/collision.set_state.json @@ -3,5 +3,6 @@ "command": "collision.set_state", "data": { "collision_state": false - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/controller.get_params.json b/assets/files/protocols/hmi/controller.get_params.json index 92dac53..1476b8f 100644 --- a/assets/files/protocols/hmi/controller.get_params.json +++ b/assets/files/protocols/hmi/controller.get_params.json @@ -1,5 +1,6 @@ { "id":"xxxxxxxxxxx", "module":"system", - "command":"controller.get_params" + "command":"controller.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/controller.heart.json b/assets/files/protocols/hmi/controller.heart.json index 27300d6..cf570aa 100644 --- a/assets/files/protocols/hmi/controller.heart.json +++ b/assets/files/protocols/hmi/controller.heart.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "controller.heart" + "command": "controller.heart", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/controller.reboot.json b/assets/files/protocols/hmi/controller.reboot.json index a0ef6da..4631a04 100644 --- a/assets/files/protocols/hmi/controller.reboot.json +++ b/assets/files/protocols/hmi/controller.reboot.json @@ -4,5 +4,6 @@ "command": "controller.reboot", "data": { "arg": 6 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/controller.set_params.json b/assets/files/protocols/hmi/controller.set_params.json index 3759298..d433000 100644 --- a/assets/files/protocols/hmi/controller.set_params.json +++ b/assets/files/protocols/hmi/controller.set_params.json @@ -4,5 +4,6 @@ "command": "controller.set_params", "data": { "time": "2020-02-28 15:28:30" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/device.get_params.json b/assets/files/protocols/hmi/device.get_params.json index 62cd79d..55a246d 100644 --- a/assets/files/protocols/hmi/device.get_params.json +++ b/assets/files/protocols/hmi/device.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "device.get_params" + "command": "device.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/diagnosis.get_params.json b/assets/files/protocols/hmi/diagnosis.get_params.json index 7e57457..a1925df 100644 --- a/assets/files/protocols/hmi/diagnosis.get_params.json +++ b/assets/files/protocols/hmi/diagnosis.get_params.json @@ -4,5 +4,6 @@ "command": "diagnosis.get_params", "data": { "version": "1.4.1" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/diagnosis.open.json b/assets/files/protocols/hmi/diagnosis.open.json index 7fe21c2..93f3d65 100644 --- a/assets/files/protocols/hmi/diagnosis.open.json +++ b/assets/files/protocols/hmi/diagnosis.open.json @@ -8,5 +8,6 @@ "overrun": false, "turn_area": false, "delay_motion": false - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/diagnosis.save.json b/assets/files/protocols/hmi/diagnosis.save.json index 6564248..7da8366 100644 --- a/assets/files/protocols/hmi/diagnosis.save.json +++ b/assets/files/protocols/hmi/diagnosis.save.json @@ -4,5 +4,6 @@ "command": "diagnosis.save", "data": { "save": true - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/diagnosis.set_params.json b/assets/files/protocols/hmi/diagnosis.set_params.json index 0bb13cc..0128af4 100644 --- a/assets/files/protocols/hmi/diagnosis.set_params.json +++ b/assets/files/protocols/hmi/diagnosis.set_params.json @@ -6,5 +6,6 @@ "display_pdo_params": [], "frequency": 50, "version": "1.4.1" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/drag.get_params.json b/assets/files/protocols/hmi/drag.get_params.json index 9c56d28..8562a45 100644 --- a/assets/files/protocols/hmi/drag.get_params.json +++ b/assets/files/protocols/hmi/drag.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "dynamic", - "command": "drag.get_params" + "command": "drag.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/drag.set_params.json b/assets/files/protocols/hmi/drag.set_params.json index 52b0b6f..cc7a525 100644 --- a/assets/files/protocols/hmi/drag.set_params.json +++ b/assets/files/protocols/hmi/drag.set_params.json @@ -6,5 +6,6 @@ "enable": true, "space": 0, "type": 0 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/fieldbus_device.get_params.json b/assets/files/protocols/hmi/fieldbus_device.get_params.json index 6eca806..8f0c407 100644 --- a/assets/files/protocols/hmi/fieldbus_device.get_params.json +++ b/assets/files/protocols/hmi/fieldbus_device.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "fieldbus", - "command": "fieldbus_device.get_params" + "command": "fieldbus_device.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/fieldbus_device.load_cfg.json b/assets/files/protocols/hmi/fieldbus_device.load_cfg.json index 79e1731..8b46df8 100644 --- a/assets/files/protocols/hmi/fieldbus_device.load_cfg.json +++ b/assets/files/protocols/hmi/fieldbus_device.load_cfg.json @@ -4,5 +4,6 @@ "command": "fieldbus_device.load_cfg", "data": { "file_name": "fieldbus_device.json" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/fieldbus_device.set_params.json b/assets/files/protocols/hmi/fieldbus_device.set_params.json index d6828f6..5b360b4 100644 --- a/assets/files/protocols/hmi/fieldbus_device.set_params.json +++ b/assets/files/protocols/hmi/fieldbus_device.set_params.json @@ -5,5 +5,6 @@ "data": { "device_name": "modbus_1", "enable": true - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/io_device.load_cfg.json b/assets/files/protocols/hmi/io_device.load_cfg.json index 790f75c..e3d0583 100644 --- a/assets/files/protocols/hmi/io_device.load_cfg.json +++ b/assets/files/protocols/hmi/io_device.load_cfg.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "io", - "command": "io_device.load_cfg" + "command": "io_device.load_cfg", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/jog.get_params.json b/assets/files/protocols/hmi/jog.get_params.json index db98a71..03e6eed 100644 --- a/assets/files/protocols/hmi/jog.get_params.json +++ b/assets/files/protocols/hmi/jog.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "jog.get_params" + "command": "jog.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/log_code.data.json b/assets/files/protocols/hmi/log_code.data.json index 021b678..b0898e2 100644 --- a/assets/files/protocols/hmi/log_code.data.json +++ b/assets/files/protocols/hmi/log_code.data.json @@ -1,5 +1,7 @@ { - "g": { + "id": "xxxxxxxxxxx", + "g": { "log_code.data": "null" - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/modbus.get_params.json b/assets/files/protocols/hmi/modbus.get_params.json index 8bdde30..1ee55bd 100644 --- a/assets/files/protocols/hmi/modbus.get_params.json +++ b/assets/files/protocols/hmi/modbus.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "fieldbus", - "command": "modbus.get_params" + "command": "modbus.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/modbus.get_values.json b/assets/files/protocols/hmi/modbus.get_values.json index d356362..42ba949 100644 --- a/assets/files/protocols/hmi/modbus.get_values.json +++ b/assets/files/protocols/hmi/modbus.get_values.json @@ -4,5 +4,6 @@ "command": "modbus.get_values", "data": { "mode": "all" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/modbus.load_cfg.json b/assets/files/protocols/hmi/modbus.load_cfg.json index 95d5e69..22f199e 100644 --- a/assets/files/protocols/hmi/modbus.load_cfg.json +++ b/assets/files/protocols/hmi/modbus.load_cfg.json @@ -4,5 +4,6 @@ "command": "modbus.load_cfg", "data": { "file" : "registers.json" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/modbus.set_params.json b/assets/files/protocols/hmi/modbus.set_params.json index 46a651e..7a3fa11 100644 --- a/assets/files/protocols/hmi/modbus.set_params.json +++ b/assets/files/protocols/hmi/modbus.set_params.json @@ -8,5 +8,6 @@ "port": 502, "slave_id": 0, "enable_master": false - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_joint_pos.json b/assets/files/protocols/hmi/move.get_joint_pos.json index 9754405..51c50d9 100644 --- a/assets/files/protocols/hmi/move.get_joint_pos.json +++ b/assets/files/protocols/hmi/move.get_joint_pos.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "move.get_joint_pos" + "command": "move.get_joint_pos", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_monitor_cfg.json b/assets/files/protocols/hmi/move.get_monitor_cfg.json index 75fb033..35419f9 100644 --- a/assets/files/protocols/hmi/move.get_monitor_cfg.json +++ b/assets/files/protocols/hmi/move.get_monitor_cfg.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "move.get_monitor_cfg" + "command": "move.get_monitor_cfg", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_params.json b/assets/files/protocols/hmi/move.get_params.json index bfe06e2..d7802fd 100644 --- a/assets/files/protocols/hmi/move.get_params.json +++ b/assets/files/protocols/hmi/move.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "move.get_params" + "command": "move.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_pos.json b/assets/files/protocols/hmi/move.get_pos.json index 9124631..4cce83b 100644 --- a/assets/files/protocols/hmi/move.get_pos.json +++ b/assets/files/protocols/hmi/move.get_pos.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "move.get_pos" + "command": "move.get_pos", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_quickstop_distance.json b/assets/files/protocols/hmi/move.get_quickstop_distance.json index ac5f0b2..ddcfa11 100644 --- a/assets/files/protocols/hmi/move.get_quickstop_distance.json +++ b/assets/files/protocols/hmi/move.get_quickstop_distance.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "motion", - "command": "move.get_quickstop_distance" + "command": "move.get_quickstop_distance", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.get_quickturn_pos.json b/assets/files/protocols/hmi/move.get_quickturn_pos.json index 74ecae0..a02b8c2 100644 --- a/assets/files/protocols/hmi/move.get_quickturn_pos.json +++ b/assets/files/protocols/hmi/move.get_quickturn_pos.json @@ -1,5 +1,6 @@ { "id" : "xxxxxxxxx", "module": "motion", - "command": "move.get_quickturn_pos" + "command": "move.get_quickturn_pos", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.quick_turn.json b/assets/files/protocols/hmi/move.quick_turn.json index 3a72afc..8db1988 100644 --- a/assets/files/protocols/hmi/move.quick_turn.json +++ b/assets/files/protocols/hmi/move.quick_turn.json @@ -4,5 +4,6 @@ "command": "move.quick_turn", "data": { "name":"home" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.set_monitor_cfg.json b/assets/files/protocols/hmi/move.set_monitor_cfg.json index 68c3f35..0a638de 100644 --- a/assets/files/protocols/hmi/move.set_monitor_cfg.json +++ b/assets/files/protocols/hmi/move.set_monitor_cfg.json @@ -4,5 +4,6 @@ "command": "move.set_monitor_cfg", "data": { "ref_coordinate": 1 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.set_params.json b/assets/files/protocols/hmi/move.set_params.json index 8788a6c..b65b1db 100644 --- a/assets/files/protocols/hmi/move.set_params.json +++ b/assets/files/protocols/hmi/move.set_params.json @@ -12,6 +12,6 @@ "VEL_SMOOTH_FACTOR": 3.33, "ACC_RAMPTIME_JOG": 0.01 } - } - + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.set_quickstop_distance.json b/assets/files/protocols/hmi/move.set_quickstop_distance.json index 000d5f5..50c53fd 100644 --- a/assets/files/protocols/hmi/move.set_quickstop_distance.json +++ b/assets/files/protocols/hmi/move.set_quickstop_distance.json @@ -4,5 +4,6 @@ "command": "move.set_quickstop_distance", "data":{ "distance": 2 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.set_quickturn_pos.json b/assets/files/protocols/hmi/move.set_quickturn_pos.json index 164e442..8da5353 100644 --- a/assets/files/protocols/hmi/move.set_quickturn_pos.json +++ b/assets/files/protocols/hmi/move.set_quickturn_pos.json @@ -11,5 +11,6 @@ "joint_transport": [0.0,0.0,0.0,0.0,0.0,0.0,0.0], "end_posture": 0, "home_error_range":[0.0,0.0,0.0,0.0,0.0,0.0,0.0] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/move.stop.json b/assets/files/protocols/hmi/move.stop.json index 17648f0..f79c140 100644 --- a/assets/files/protocols/hmi/move.stop.json +++ b/assets/files/protocols/hmi/move.stop.json @@ -4,5 +4,6 @@ "command": "move.stop", "data":{ "stoptype": 0 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/overview.get_autoload.json b/assets/files/protocols/hmi/overview.get_autoload.json index 49e3b79..4b59f13 100644 --- a/assets/files/protocols/hmi/overview.get_autoload.json +++ b/assets/files/protocols/hmi/overview.get_autoload.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "project", - "command": "overview.get_autoload" + "command": "overview.get_autoload", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/overview.get_cur_prj.json b/assets/files/protocols/hmi/overview.get_cur_prj.json index 8e50c5a..ab192ad 100644 --- a/assets/files/protocols/hmi/overview.get_cur_prj.json +++ b/assets/files/protocols/hmi/overview.get_cur_prj.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "project", - "command": "overview.get_cur_prj" + "command": "overview.get_cur_prj", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/overview.reload.json b/assets/files/protocols/hmi/overview.reload.json index a2485a2..185f8ba 100644 --- a/assets/files/protocols/hmi/overview.reload.json +++ b/assets/files/protocols/hmi/overview.reload.json @@ -5,5 +5,6 @@ "data": { "prj_path": "", "tasks": [] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/overview.set_autoload.json b/assets/files/protocols/hmi/overview.set_autoload.json index 791d4fb..928bb9f 100644 --- a/assets/files/protocols/hmi/overview.set_autoload.json +++ b/assets/files/protocols/hmi/overview.set_autoload.json @@ -4,5 +4,6 @@ "command": "overview.set_autoload", "data": { "autoload_prj_path": "" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/register.set_value.json b/assets/files/protocols/hmi/register.set_value.json index 6b64af3..d4cded4 100644 --- a/assets/files/protocols/hmi/register.set_value.json +++ b/assets/files/protocols/hmi/register.set_value.json @@ -7,5 +7,6 @@ "type": "bool", "bias": 0, "value": 0 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/rl_task.pp_to_main.json b/assets/files/protocols/hmi/rl_task.pp_to_main.json index 3e2247f..c5f518e 100644 --- a/assets/files/protocols/hmi/rl_task.pp_to_main.json +++ b/assets/files/protocols/hmi/rl_task.pp_to_main.json @@ -4,5 +4,6 @@ "command": "rl_task.pp_to_main", "data": { "tasks": [] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/rl_task.run.json b/assets/files/protocols/hmi/rl_task.run.json index 52e89a1..534a5fd 100644 --- a/assets/files/protocols/hmi/rl_task.run.json +++ b/assets/files/protocols/hmi/rl_task.run.json @@ -4,5 +4,6 @@ "command": "rl_task.run", "data": { "tasks": [] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/rl_task.set_run_params.json b/assets/files/protocols/hmi/rl_task.set_run_params.json index 8490383..efc5368 100644 --- a/assets/files/protocols/hmi/rl_task.set_run_params.json +++ b/assets/files/protocols/hmi/rl_task.set_run_params.json @@ -5,5 +5,6 @@ "data": { "loop_mode": true, "override": 1.0 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/rl_task.stop.json b/assets/files/protocols/hmi/rl_task.stop.json index 03b0f6b..0fe2a91 100644 --- a/assets/files/protocols/hmi/rl_task.stop.json +++ b/assets/files/protocols/hmi/rl_task.stop.json @@ -4,5 +4,6 @@ "command": "rl_task.stop", "data": { "tasks": [] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/safety.safety_area.overall_enable.json b/assets/files/protocols/hmi/safety.safety_area.overall_enable.json index 517e64b..694946e 100644 --- a/assets/files/protocols/hmi/safety.safety_area.overall_enable.json +++ b/assets/files/protocols/hmi/safety.safety_area.overall_enable.json @@ -1,7 +1,9 @@ { - "c": { + "id": "xxxxxxxxxxx", + "c": { "safety.safety_area.overall_enable": { "enable": true } - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/safety.safety_area.safety_area_enable.json b/assets/files/protocols/hmi/safety.safety_area.safety_area_enable.json index 505fe50..723e1cf 100644 --- a/assets/files/protocols/hmi/safety.safety_area.safety_area_enable.json +++ b/assets/files/protocols/hmi/safety.safety_area.safety_area_enable.json @@ -1,8 +1,10 @@ { - "c": { + "id": "xxxxxxxxxxx", + "c": { "safety.safety_area.safety_area_enable": { "id": 0, "enable": true } - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/safety.safety_area.set_param.json b/assets/files/protocols/hmi/safety.safety_area.set_param.json index 28b65da..1205fe0 100644 --- a/assets/files/protocols/hmi/safety.safety_area.set_param.json +++ b/assets/files/protocols/hmi/safety.safety_area.set_param.json @@ -1,5 +1,7 @@ { - "c": { + "id": "xxxxxxxxxxx", + "c": { "safety.safety_area.set_param": null - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/safety.safety_area.signal_enable.json b/assets/files/protocols/hmi/safety.safety_area.signal_enable.json index 8a1fbac..2a37a4c 100644 --- a/assets/files/protocols/hmi/safety.safety_area.signal_enable.json +++ b/assets/files/protocols/hmi/safety.safety_area.signal_enable.json @@ -1,7 +1,9 @@ { - "c": { + "id": "xxxxxxxxxxx", + "c": { "safety.safety_area.signal_enable": { "signal": true } - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/safety_area_data.json b/assets/files/protocols/hmi/safety_area_data.json index e9b982a..cd43bdd 100644 --- a/assets/files/protocols/hmi/safety_area_data.json +++ b/assets/files/protocols/hmi/safety_area_data.json @@ -1,5 +1,7 @@ { - "g": { + "id": "xxxxxxxxxxx", + "g": { "safety_area_data": null - } + }, + "p_type": 1 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/servo.clear_alarm.json b/assets/files/protocols/hmi/servo.clear_alarm.json index bc3b7f8..cffeb4e 100644 --- a/assets/files/protocols/hmi/servo.clear_alarm.json +++ b/assets/files/protocols/hmi/servo.clear_alarm.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "robot", - "command": "servo.clear_alarm" + "command": "servo.clear_alarm", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/socket.get_params.json b/assets/files/protocols/hmi/socket.get_params.json index 2971e6a..6a92e1d 100644 --- a/assets/files/protocols/hmi/socket.get_params.json +++ b/assets/files/protocols/hmi/socket.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "network", - "command": "socket.get_params" + "command": "socket.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/socket.set_params.json b/assets/files/protocols/hmi/socket.set_params.json index 9635cae..2507cfb 100644 --- a/assets/files/protocols/hmi/socket.set_params.json +++ b/assets/files/protocols/hmi/socket.set_params.json @@ -13,5 +13,6 @@ "auto_connect": true, "disconnection_triggering_behavior": 0, "disconnection_detection_time": 10 - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/soft_limit.get_params.json b/assets/files/protocols/hmi/soft_limit.get_params.json index 6f1f222..861b1ee 100644 --- a/assets/files/protocols/hmi/soft_limit.get_params.json +++ b/assets/files/protocols/hmi/soft_limit.get_params.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "safety", - "command": "soft_limit.get_params" + "command": "soft_limit.get_params", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/soft_limit.set_params.json b/assets/files/protocols/hmi/soft_limit.set_params.json index a0a3844..eda482b 100644 --- a/assets/files/protocols/hmi/soft_limit.set_params.json +++ b/assets/files/protocols/hmi/soft_limit.set_params.json @@ -8,5 +8,6 @@ "lower": [0,0,0,0,0,0,0], "reduced_upper": [0,0,0,0,0,0,0], "reduced_lower": [0,0,0,0,0,0,0] - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.get_state.json b/assets/files/protocols/hmi/state.get_state.json index 1a2b981..c3e92fa 100644 --- a/assets/files/protocols/hmi/state.get_state.json +++ b/assets/files/protocols/hmi/state.get_state.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.get_state" + "command": "state.get_state", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.get_tp_mode.json b/assets/files/protocols/hmi/state.get_tp_mode.json index 5b59ed0..3d41c3b 100644 --- a/assets/files/protocols/hmi/state.get_tp_mode.json +++ b/assets/files/protocols/hmi/state.get_tp_mode.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.get_tp_mode" + "command": "state.get_tp_mode", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.set_tp_mode.json b/assets/files/protocols/hmi/state.set_tp_mode.json index 608dd02..7185d96 100644 --- a/assets/files/protocols/hmi/state.set_tp_mode.json +++ b/assets/files/protocols/hmi/state.set_tp_mode.json @@ -4,5 +4,6 @@ "command": "state.set_tp_mode", "data": { "tp_mode": "with" - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.switch_auto.json b/assets/files/protocols/hmi/state.switch_auto.json index 0c0a872..67d5635 100644 --- a/assets/files/protocols/hmi/state.switch_auto.json +++ b/assets/files/protocols/hmi/state.switch_auto.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.switch_auto" + "command": "state.switch_auto", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.switch_manual.json b/assets/files/protocols/hmi/state.switch_manual.json index 0451d04..7b08ce6 100644 --- a/assets/files/protocols/hmi/state.switch_manual.json +++ b/assets/files/protocols/hmi/state.switch_manual.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.switch_manual" + "command": "state.switch_manual", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.switch_motor_off.json b/assets/files/protocols/hmi/state.switch_motor_off.json index 0618931..e6f7357 100644 --- a/assets/files/protocols/hmi/state.switch_motor_off.json +++ b/assets/files/protocols/hmi/state.switch_motor_off.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.switch_motor_off" + "command": "state.switch_motor_off", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/state.switch_motor_on.json b/assets/files/protocols/hmi/state.switch_motor_on.json index 0bbcc72..cba491e 100644 --- a/assets/files/protocols/hmi/state.switch_motor_on.json +++ b/assets/files/protocols/hmi/state.switch_motor_on.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "system", - "command": "state.switch_motor_on" + "command": "state.switch_motor_on", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/system_io.query_configuration.json b/assets/files/protocols/hmi/system_io.query_configuration.json index 27d16bd..c9120d3 100644 --- a/assets/files/protocols/hmi/system_io.query_configuration.json +++ b/assets/files/protocols/hmi/system_io.query_configuration.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "io", - "command": "system_io.query_configuration" + "command": "system_io.query_configuration", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/system_io.query_event_cfg.json b/assets/files/protocols/hmi/system_io.query_event_cfg.json index 5af3217..69e69e4 100644 --- a/assets/files/protocols/hmi/system_io.query_event_cfg.json +++ b/assets/files/protocols/hmi/system_io.query_event_cfg.json @@ -1,5 +1,6 @@ { "id": "xxxxxxxxxxx", "module": "io", - "command": "system_io.query_event_cfg" + "command": "system_io.query_event_cfg", + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/hmi/system_io.update_configuration.json b/assets/files/protocols/hmi/system_io.update_configuration.json index aaeb41f..bc5bac8 100644 --- a/assets/files/protocols/hmi/system_io.update_configuration.json +++ b/assets/files/protocols/hmi/system_io.update_configuration.json @@ -5,5 +5,6 @@ "data": { "input_system_io": {}, "output_system_io": {} - } + }, + "p_type": 0 } \ No newline at end of file diff --git a/assets/files/protocols/md/ctrl_estop_reset_clear_alarm.txt b/assets/files/protocols/md/ctrl_estop_reset_clear_alarm.txt new file mode 100644 index 0000000..4d4abec --- /dev/null +++ b/assets/files/protocols/md/ctrl_estop_reset_clear_alarm.txt @@ -0,0 +1,3 @@ +40002: 0 +40002: 1 +40002: 0 \ No newline at end of file diff --git a/assets/files/protocols/md/ctrl_motor_off.txt b/assets/files/protocols/md/ctrl_motor_off.txt new file mode 100644 index 0000000..c7c4228 --- /dev/null +++ b/assets/files/protocols/md/ctrl_motor_off.txt @@ -0,0 +1,3 @@ +40003: 0 +40003: 1 +40003: 0 \ No newline at end of file diff --git a/assets/files/protocols/md/ctrl_motor_on.txt b/assets/files/protocols/md/ctrl_motor_on.txt new file mode 100644 index 0000000..7bcc075 --- /dev/null +++ b/assets/files/protocols/md/ctrl_motor_on.txt @@ -0,0 +1,3 @@ +40004: 0 +40004: 1 +40004: 0 \ No newline at end of file diff --git a/assets/files/protocols/md/ctrl_soft_estop.txt b/assets/files/protocols/md/ctrl_soft_estop.txt new file mode 100644 index 0000000..1eaa804 --- /dev/null +++ b/assets/files/protocols/md/ctrl_soft_estop.txt @@ -0,0 +1 @@ +40012: 1 \ No newline at end of file diff --git a/assets/files/protocols/md/sta_alarm.txt b/assets/files/protocols/md/sta_alarm.txt new file mode 100644 index 0000000..97e3c40 --- /dev/null +++ b/assets/files/protocols/md/sta_alarm.txt @@ -0,0 +1 @@ +40500:1:bool \ No newline at end of file diff --git a/assets/files/protocols/md/sta_estop.txt b/assets/files/protocols/md/sta_estop.txt new file mode 100644 index 0000000..edff8ea --- /dev/null +++ b/assets/files/protocols/md/sta_estop.txt @@ -0,0 +1 @@ +40505:1:bool \ No newline at end of file diff --git a/assets/files/protocols/md/sta_motor.txt b/assets/files/protocols/md/sta_motor.txt new file mode 100644 index 0000000..f36cf19 --- /dev/null +++ b/assets/files/protocols/md/sta_motor.txt @@ -0,0 +1 @@ +40506:1:bool \ No newline at end of file diff --git a/assets/files/protocols/md/sta_soft_estop.txt b/assets/files/protocols/md/sta_soft_estop.txt new file mode 100644 index 0000000..dfeda2c --- /dev/null +++ b/assets/files/protocols/md/sta_soft_estop.txt @@ -0,0 +1 @@ +40518:1:bool \ No newline at end of file diff --git a/assets/files/version/version b/assets/files/version/local_vers similarity index 100% rename from assets/files/version/version rename to assets/files/version/local_vers diff --git a/assets/files/version/server_vers b/assets/files/version/server_vers new file mode 100644 index 0000000..bd78cb6 --- /dev/null +++ b/assets/files/version/server_vers @@ -0,0 +1 @@ +0.3.1.7@03/24/2025 \ No newline at end of file diff --git a/assets/media/splash.png b/assets/media/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..f6b930cca1762a0b46fb03a51ba342c858bef6a0 GIT binary patch literal 14920 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 clibs.MAX_LOG_NUMBER: - for table in sorted(tables)[:-10]: - self.cursor.execute(f"DROP TABLE {table};") - - # ========================= clibs ========================= - clibs.cursor = self.cursor - clibs.tb_name = f"{t}_log" - - # ========================= clibs ========================= - # validator = QDoubleValidator(bottom=300, decimals=2) - # self.le_durable_interval.setValidator(validator) - - # ========================= styleSheet ========================= - tws = [self.tw_funcs, self.tw_docs] - for tw in tws: - tw.setStyleSheet(""" - QTabBar::tab:selected { - background: #0078D4; - color: white; - border-radius: 4px; - } - QTabBar::tab:!selected { - background: #F0F0F0; - color: #333; - } - QTabWidget::pane { - border: 1px solid #CCCCCC; - } - """) - - # ============================↓↓↓debug↓↓↓============================ - # print(f"self.cb_data_func.currentIndex() = {self.cb_data_func.currentIndex()}") - - def run_program_thread(self, prog, idx, prog_done, network): - self.tw_docs.setCurrentIndex(0) - # self.pte_output.clear() - if idx != -99: - prog.output.connect(self.w2t) - self.t = QThread(self) - self.run = RunProg() - self.run.moveToThread(self.t) - self.run.completed.connect(prog_done) - self.action.connect(self.run.program) - self.t.start() - self.action.emit((prog, idx, network)) - - def w2t(self, msg, color="black"): - self.pte_output.appendHtml(f"{msg}") - cursor = self.pte_output.textCursor() - cursor.movePosition(QTextCursor.End) - self.pte_output.setTextCursor(cursor) - self.pte_output.ensureCursorVisible() - self.update() - - def prog_start(self): - def prog_done(results): - flag, result, error, idx, network = results - clibs.running[idx] = 0 - # if flag is False: - # self.w2t(f"{clibs.functions[idx]}运行失败:{error}", "red") - # elif flag is True: - # ... - - if sum(clibs.running) > 0: - if sum(clibs.running) == 1: - QMessageBox.critical(self, "运行中", f"{clibs.functions[clibs.running.index(1)]}正在执行中,不可同时运行两个处理/测试程序!") - return - else: - self.w2t(f"clibs.running = {clibs.running}", "red") - self.w2t(f"clibs.functions = {clibs.functions}", "red") - QMessageBox.critical(self, "严重错误", "理论上不允许同时运行两个处理程序,需要检查!") - return - - if self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 0: - self.run_program_thread(brake.BrakeDataProcess(self.le_data_path.text()), 0, prog_done, None) - elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 1: - self.run_program_thread(current.CurrentDataProcess(self.le_data_path.text(), self.cb_data_current.currentText()), 1, prog_done, None) - elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 2: - self.run_program_thread(iso.IsoDataProcess(self.le_data_path.text()), 2, prog_done, None) - elif self.tw_funcs.currentIndex() == 0 and self.cb_data_func.currentIndex() == 3: - self.run_program_thread(wavelogger.WaveloggerDataProcess(self.le_data_path.text()), 3, prog_done, None) - elif self.tw_funcs.currentIndex() == 1 and self.cb_unit_func.currentIndex() == 0: - self.w2t(f"{clibs.functions[4]}功能待开发.....", "red") - elif self.tw_funcs.currentIndex() == 1 and self.cb_unit_func.currentIndex() == 1: - self.w2t(f"{clibs.functions[5]}功能待开发.....", "red") - elif self.tw_funcs.currentIndex() == 2: - self.w2t(f"{clibs.functions[6]}功能待开发.....", "red") - - def prog_stop(self): - QMessageBox.warning(self, "停止运行", "运行过程中不建议停止运行,可能会损坏文件,如果确实需要停止运行,可以直接关闭窗口!") - - def prog_reset(self): - self.pte_output.clear() - - def file_browser(self): - idx_dict = {0: self.le_data_path, 1: self.le_unit_path, 2: self.le_durable_path} - dir_path = QtWidgets.QFileDialog.getExistingDirectory() - tab_index = self.tw_funcs.currentIndex() - if dir_path: - idx_dict[tab_index].setText(dir_path) - - def curve_draw(self): - ... - - def durable_cb_change(self): - ... - - def pre_page(self): - ... - - def realtime_page(self): - ... - - def next_page(self): - ... - - def load_sql(self): - ... - - def search_keyword(self): - ... - - def prog_done_conn(self, results): - flag, result, error, idx, network = results - if flag is False: - self.w2t(f"{network.upper()}连接失败", "red") - elif flag is True: - clibs.status[network] = 1 - if network == "hmi": - self.btn_hmi_conn.setText("断开") - clibs.c_hr = result - elif network == "md": - self.btn_md_conn.setText("断开") - clibs.c_md = result - elif network == "ec": - self.btn_ec_conn.setText("断开") - clibs.c_ec = result - - def prog_done_disconn(self, results): - flag, result, error, idx, network = results - if flag is False: - self.w2t(f"{network.upper()}断开连接失败", "red") - elif flag is True: - clibs.status[network] = 0 - if network == "hmi": - self.btn_hmi_conn.setText("连接") - clibs.c_hr = result - elif network == "md": - self.btn_md_conn.setText("连接") - clibs.c_md = result - elif network == "ec": - self.btn_ec_conn.setText("连接") - clibs.c_ec = result - - def hmi_conn(self): - if self.btn_hmi_conn.text() == "连接": - clibs.ip_addr = self.le_hmi_ip.text().strip() - ip_pattern = re.compile(r"(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])") - if not ip_pattern.fullmatch(clibs.ip_addr): - self.w2t(f"{clibs.ip_addr} 不是一个有效的 IP 地址", "red") - return - self.run_program_thread(openapi.HmiRequest(clibs.ip_addr, clibs.socket_port, clibs.xService_port), -1, self.prog_done_conn, "hmi") - elif self.btn_hmi_conn.text() == "断开": - self.run_program_thread(clibs.c_hr.close, -99, self.prog_done_disconn, "hmi") - - def md_conn(self): - if clibs.status["hmi"] == 0: - QMessageBox.warning(self, "告警", "打开Modbus连接之前,需要先打开HMI连接!") - return - - if self.btn_md_conn.text() == "连接": - clibs.modbus_port = self.le_md_port.text().strip() - self.run_program_thread(openapi.ModbusRequest(clibs.ip_addr, clibs.modbus_port), -1, self.prog_done_conn, "md") - elif self.btn_md_conn.text() == "断开": - self.run_program_thread(clibs.c_md.close, -99, self.prog_done_disconn, "md") - - def ec_conn(self): - if clibs.status["hmi"] == 0: - QMessageBox.warning(self, "告警", "打开外部通信连接之前,需要先打开HMI连接!") - return - - if self.btn_ec_conn.text() == "连接": - clibs.external_port = self.le_ec_port.text().strip() - self.run_program_thread(openapi.ExternalCommunication(clibs.ip_addr, clibs.external_port), -1, self.prog_done_conn, "ec") - elif self.btn_ec_conn.text() == "断开": - self.run_program_thread(clibs.c_ec.close, -99, self.prog_done_disconn, "ec") - - def hmi_page(self): - self.sw_network.setCurrentIndex(0) - - def md_page(self): - self.sw_network.setCurrentIndex(1) - - def ec_page(self): - self.sw_network.setCurrentIndex(2) - - def hmi_send(self): - def prog_done(results): - ... - - def hmi_send_thread(): - if clibs.status["hmi"] == 0: - QMessageBox.critical(self, "错误", "使用该功能之前,需要先打开HMI连接!") - return - - cmd = self.pte_hmi_send.toPlainText() - req = json.dumps(json.loads(cmd), separators=(",", ":")) - print(f"type of cmd = {type(cmd)}") - print(f"type of req = {type(req)}") - if "id" in req: # 老协议 - print(f"wrong req = {req}") - msg_id = json.loads(req)["id"] - clibs.c_hr.c.send(clibs.c_hr.package(req)) - print(f"msg_id ={msg_id}") - clibs.logger("INFO", "aio", f"hmi: [send] 老协议请求发送成功 {req}") - records = clibs.c_hr.get_from_id(msg_id, "done") - print(f"req = {req}") - print(f"records = {records}") - self.pte_him_recv.clear() - self.pte_him_recv.appendPlainText(records) - else: # 新协议 - clibs.c_hr.c_xs.send(clibs.c_hr.package_xs(json.loads(cmd))) - data = "" - time.sleep(clibs.INTERVAL/5) - _ = clibs.c_hr.c_xs.recv(1024) - while len(_) == 1024: - data += _ - _ = clibs.c_hr.c_xs.recv(1024) - - print(f"data = {data}") - self.pte_him_recv.clear() - self.pte_him_recv.appendPlainText(data.decode()) - self.run_program_thread(hmi_send_thread, -99, prog_done, None) - - def md_send(self): - ... - - def ec_send(self): - ... - - def hmi_cb_change(self): - cmd = self.cb_hmi_cmd.currentText() - self.pte_hmi_send.clear() - self.pte_him_recv.clear() - with open(f"{clibs.PREFIX}/files/protocols/hmi/{cmd}.json", mode="r", encoding="utf-8") as f_hmi: - t = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") - hmi_dict = json.load(f_hmi) - if "id" in hmi_dict.keys(): - hmi_dict["id"] = f"{cmd}-{t}" - - self.pte_hmi_send.appendPlainText(json.dumps(hmi_dict, indent=2, separators=(",", ":"))) - - def md_cb_change(self): - ... - - def ec_cb_change(self): - ... - - def check_interval(self): - try: - interval = float(self.le_durable_interval.text()) - interval = 300 if interval < 300 else int(interval) - except Exception: - interval = 300 - self.le_durable_interval.setText(str(interval)) - - def state_detection(self): - while True: - time.sleep(clibs.INTERVAL) - if clibs.status["hmi"] == 0 and self.btn_hmi_conn.text() == "断开": - self.btn_hmi_conn.setText("连接") - elif clibs.status["hmi"] == 1 and self.btn_hmi_conn.text() == "连接": - self.btn_hmi_conn.setText("断开") - - def closeEvent(self, event): - idx = -1 if clibs.running.count(1) == 0 else clibs.running.index(1) - info_text = "当前无程序正在运行,可放心退出!" if idx == -1 else f"当前正在运行{clibs.functions[idx]},确认退出?" - reply = QMessageBox.question(self, "退出", info_text) - if reply == QMessageBox.Yes: - try: - self.cursor.execute(f"use user_info;") - self.cursor.execute(f"UPDATE UserInfo SET online = 0 WHERE username = '{self.username}'") - self.cursor.close() - self.conn.close() - finally: - clibs.lock.release() - - if clibs.status["md"] == 1: - self.run_program_thread(clibs.c_md.close, -99, self.prog_done_disconn, "md") - if clibs.status["ec"] == 1: - self.run_program_thread(clibs.c_ec.close, -99, self.prog_done_disconn, "ec") - if clibs.status["hmi"] == 1: - self.run_program_thread(clibs.c_hr.close, -99, self.prog_done_disconn, "hmi") - - self.close() - event.accept() - else: - event.ignore() - - -if __name__ == '__main__': - app = QtWidgets.QApplication(sys.argv) - window = LoginWindow() - window.show() - sys.exit(app.exec()) - diff --git a/code/common/clibs.py b/code/common/clibs.py deleted file mode 100644 index 8a42f5b..0000000 --- a/code/common/clibs.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import os.path -import threading - - -def traversal_files(dir_path, signal): - # 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录 - # 参数:路径/信号/游标/功能编号 - # 返回值:路径下的文件夹列表 路径下的文件列表 - global cursor, tb_name - - if not os.path.exists(dir_path): - logger("ERROR", "clibs", f"数据文件夹{dir_path}不存在,请确认后重试......", "red", signal=signal) - else: - dirs, files = [], [] - for item in os.scandir(dir_path): - if item.is_dir(): - dirs.append(item.path.replace("\\", "/")) - elif item.is_file(): - files.append(item.path.replace("\\", "/")) - - return dirs, files - - -def db_lock(func): - def wrapper(*args, **kwargs): - try: - lock.acquire(True) - ret = func(*args, **kwargs) - finally: - lock.release() - return ret - - return wrapper - - -@db_lock -def logger(level, module, content, color="black", flag="both", signal=""): - global cursor, tb_name - if flag == "signal": - signal.emit(content, color) - elif flag == "cursor": - cursor.execute(f"INSERT INTO {tb_name} (level, module, content) VALUES (%s, %s, %s)", (level, module, content)) - elif flag == "both": - signal.emit(content, color) - cursor.execute(f"INSERT INTO {tb_name} (level, module, content) VALUES (%s, %s, %s)", (level, module, content)) - - -# PREFIX = "assets" # for pyinstaller packaging -PREFIX = "../assets" # for source code testing and debug -lock = threading.Lock() - -running = [0, 0, 0, 0, 0, 0, 0] # 制动数据/转矩数据/激光数据/精度数据/制动自动化/转矩自动化/耐久数据采集 -functions = ["制动数据处理", "转矩数据处理", "激光数据处理", "精度数据处理", "制动自动化测试", "转矩自动化测试", "耐久数据采集"] - -log_name = "" -ip_addr, ssh_port, socket_port, xService_port, external_port, modbus_port, upgrade_port = "", 22, 5050, 6666, 8080, 502, 4567 -username, password = "luoshi", "123456" -INTERVAL, RADIAN, MAX_FRAME_SIZE, MAX_LOG_NUMBER = 0.5, 57.3, 1024, 10 -c_md, c_hr, c_ec, c_pd, cursor, tb_name = None, None, None, None, None, "" -status = {"mysql": 0, "hmi": 0, "md": 0, "ec": 0} -c_joint_vel, c_servo_trq, c_sensor_trq, c_estimate_trans_trq, c_safety_estop = 1, 2, 3, 4, 3 # 各个指标所在列 diff --git a/code/durable/create_plot.py b/code/durable/create_plot.py deleted file mode 100644 index 3074b22..0000000 --- a/code/durable/create_plot.py +++ /dev/null @@ -1,85 +0,0 @@ -import os.path -import matplotlib.pyplot as plt -import pandas -from matplotlib.widgets import Slider -from PySide6.QtCore import Signal, QThread -from common import clibs - - -# class DrawCurves(QThread): -# output = Signal(str, str) -# -# def __init__(self, /): -# super().__init__() -# -# @staticmethod -# def logger(level, module, content, color="black", error="", flag="both", signal=output): -# clibs.logger(level, module, content, color, flag, signal) -# if level.upper() == "ERROR": -# raise Exception(error) -# -# def initialization(self): -# path, curves = None, None -# try: -# path = clibs.data_dd["path"] -# curves = clibs.data_dd["curves"] -# except Exception: -# clibs.w2t("程序未开始运行,暂无数据可以展示......\n", "red") -# return None, None -# -# for curve in curves: -# if not os.path.exists(f"{path}/{curve}.csv"): -# clibs.w2t(f"{curve}曲线数据暂未生成,稍后再试......\n", "orange") -# return None, None -# -# return path, curves -# -# -# def data_plot(path, curve): -# titles = {"hw_joint_vel_feedback": "各关节最大速度曲线", "device_servo_trq_feedback": "各关节平均有效转矩曲线"} -# ylabels = {"hw_joint_vel_feedback": "速度(rad/s)", "device_servo_trq_feedback": "转矩(Nm)"} -# -# fig, axes = plt.subplots(figsize=(10, 4.5), dpi=100) -# cols = [f"{curve}_{i}" for i in range(6)] -# cols.insert(0, "time") -# df = pandas.read_csv(f"{path}/{curve}.csv") -# plt.plot(df[cols[1]], label="一轴") -# plt.plot(df[cols[2]], label="二轴") -# plt.plot(df[cols[3]], label="三轴") -# plt.plot(df[cols[4]], label="四轴") -# plt.plot(df[cols[5]], label="五轴") -# plt.plot(df[cols[6]], label="六轴") -# axes.set_title(titles[curve]) -# axes.set_ylabel(ylabels[curve]) -# axes.legend(loc="upper right") -# -# slider_position = plt.axes((0.1, 0.01, 0.8, 0.05), facecolor="blue") # (left, bottom, width, height) -# scrollbar = Slider(slider_position, 'Time', 1, int(len(df)), valstep=1) -# -# def update(val): -# pos = scrollbar.val -# axes.set_xlim([pos, pos + 10]) -# fig.canvas.draw_idle() -# -# scrollbar.on_changed(update) -# fig.tight_layout(rect=(0, 0.02, 0.96, 1)) # tuple (left, bottom, right, top) -# -# -# def main(): -# path, curves = initialization() -# if not path or not curves: -# return -# -# for curve in curves: -# data_plot(path, curve) -# -# plt.show() -# -# -# plt.rcParams['font.sans-serif'] = ['SimHei'] -# plt.rcParams['axes.unicode_minus'] = False -# plt.rcParams['figure.dpi'] = 100 -# plt.rcParams['font.size'] = 14 -# plt.rcParams['lines.marker'] = 'o' -# plt.rcParams["figure.autolayout"] = True -# \ No newline at end of file diff --git a/code/durable/factory_test.py b/code/durable/factory_test.py deleted file mode 100644 index ed146f8..0000000 --- a/code/durable/factory_test.py +++ /dev/null @@ -1,239 +0,0 @@ -import json -import threading -import time -import pandas -import numpy -import math -import csv -from PySide6.QtCore import Signal, QThread -from common import clibs - - -class DoBrakeTest(QThread): - output = Signal(str, str) - - def __init__(self, dir_path, interval, proc, /): - super().__init__() - self.dir_path = dir_path - self.interval = interval - self.proc = proc - self.idx = 6 - - def logger(self, level, module, content, color="black", error="", flag="both"): - clibs.logger(level, module, content, color, flag, signal=self.output) - if level.upper() == "ERROR": - raise Exception(f"{error} | {content}") - - def initialization(self, data_dirs, data_files): - def check_files(): - if len(curves) == 0: - self.logger("ERROR", "factory-check_files", "未查询到需要记录数据的曲线,至少选择一个!", "red", "CurveNameError") - - if len(data_dirs) != 0 or len(data_files) != 1: - self.logger("ERROR", "factory-check_files", "初始路径下不允许有文件夹,且初始路径下只能存在一个工程文件 —— *.zip,确认后重新运行!", "red", "InitFileError") - - if not data_files[0].endswith(".zip"): - self.logger("ERROR", "factory-check_files", f"{data_files[0]} 不是一个有效的工程文件,需确认!", "red", "ProjectFileError") - - return data_files[0], interval - - def get_configs(): - robot_type, records = None, None - msg_id, state = clibs.c_hr.execution("controller.get_params") - records = clibs.c_hr.get_from_id(msg_id, state) - for record in records: - if "请求发送成功" not in record[0]: - robot_type = eval(record[0])["data"]["robot_type"] - server_file = f"/home/luoshi/bin/controller/robot_cfg/{robot_type}/{robot_type}.cfg" - local_file = self.dir_path + f"/{robot_type}.cfg" - clibs.c_pd.pull_file_from_server(server_file, local_file) - - try: - with open(local_file, mode="r", encoding="utf-8") as f_config: - configs = json.load(f_config) - except Exception as Err: - self.logger("ERROR", "factory-get_configs", f"无法打开 {local_file}
{Err}", "red", "OpenFileError") - - # 最大角速度,额定电流,减速比,额定转速 - version = configs["VERSION"] - m_avs = configs["MOTION"]["JOINT_MAX_SPEED"] - m_rts = configs["MOTOR"]["RATED_TORQUE"] # 电机额定转矩rt for rated torque - m_tcs = [1, 1, 1, 1, 1, 1] # 电机转矩常数,tc for torque constant - m_rcs = [] - for i in range(len(m_tcs)): - m_rcs.append(m_rts[i] / m_tcs[i]) # 电机额定电流,rc for rated current - clibs.insert_logdb("INFO", "do_brake", f"get_configs: 机型文件版本 {robot_type}_{version}") - clibs.insert_logdb("INFO", "do_brake", f"get_configs: 各关节角速度 {m_avs}") - clibs.insert_logdb("INFO", "do_brake", f"get_configs: 各关节额定电流 {m_rcs}") - return m_avs, m_rcs - - prj_file, interval = check_files() - avs, rcs = get_configs() - params = { - "prj_file": prj_file, - "interval": interval, - "avs": avs, - "rcs": rcs, - } - self.logger("INFO", "factory-initialization", "数据目录合规性检查结束,未发现问题......", "green") - return params - - @staticmethod - def change_curve_state(curves, stat): - display_pdo_params = [{"name": name, "channel": chl} for name in curves for chl in range(6)] - clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat, overrun=True, turn_area=True, delay_motion=False) - clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") - - def run_rl(self, params, curves): - prj_file, interval = params["prj_file"], params["interval"] - # 1. 关闭诊断曲线,触发软急停,并解除,目的是让可能正在运行着的机器停下来,切手动模式并下电 - self.change_curve_state(curves, False) - clibs.c_md.r_soft_estop(0) - clibs.c_md.r_soft_estop(1) - clibs.c_md.r_clear_alarm() - clibs.c_md.write_act(False) - time.sleep(1) # 让曲线彻底关闭 - - # 2. reload工程后,pp2main,并且自动模式和上电 - prj_name = ".".join(prj_file.split("/")[-1].split(".")[:-1]) - prj_path = f"{prj_name}/_build/{prj_name}.prj" - clibs.c_hr.execution("overview.reload", prj_path=prj_path, tasks=["factory"]) - clibs.c_hr.execution("rl_task.pp_to_main", tasks=["factory"]) - clibs.c_hr.execution("state.switch_auto") - clibs.c_hr.execution("state.switch_motor_on") - - # 3. 开始运行程序 - clibs.c_hr.execution("rl_task.set_run_params", loop_mode=True, override=1.0) - clibs.c_hr.execution("rl_task.run", tasks=["factory"]) - t_start = time.time() - while True: - if clibs.c_md.read_ready_to_go() == 1: - clibs.c_md.write_act(True) - break - else: - if (time.time() - t_start) > 15: - self.logger("ERROR", "factory-run_rl", "15s 内未收到机器人的运行信号,需要确认RL程序编写正确并正常执行...", "red", "ReadySignalTimeoutError") - else: - time.sleep(1) - - # 4. 获取初始数据,周期时间,首次的各轴平均电流值,打开诊断曲线,并执行采集 - time.sleep(10) # 等待 RL 程序中 scenario_time 初始化 - t_start = time.time() - while True: - scenario_time = float(f"{float(clibs.c_md.read_scenario_time()):.2f}") - if scenario_time != 0: - self.logger("INFO", "factory-run_rl", f"耐久工程的周期时间:{scenario_time}s | 单轮次执行时间:{scenario_time+interval}~{scenario_time*2+interval}") - break - else: - time.sleep(1) - if (time.time() - t_start) > 900: - self.logger("ERROR", "factory-run_rl", f"900s 内未收到耐久工程的周期时间,需要确认RL程序和工具通信交互是否正常执行...", "red", "GetScenarioTimeError") - - # 6. 准备数据保存文件 - for curve in curves: - with open(f"{self.dir_path}/{curve}.csv", mode="a+", newline="") as f_csv: - titles = [f"{curve}_{i}" for i in range(6)] - titles.insert(0, "time") - csv_writer = csv.writer(f_csv) - csv_writer.writerow(titles) - - # 7. 开始采集 - count = 0 - while clibs.running: - this_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) - next_time_1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()+scenario_time+interval+1)) - next_time_2 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()+scenario_time+interval+1+scenario_time)) - self.logger("INFO", "factory-run_rl", f"[{this_time}] 当前次数:{count:09d} | 预计下次数据更新时间:{next_time_1}~{next_time_2}", "#008B8B") - count += 1 - # 固定间隔,更新一次数据,打开曲线,获取周期内电流,关闭曲线 - time.sleep(interval) - while True: - capture_start = clibs.c_md.read_capture_start() - if capture_start == 1: - break - else: - time.sleep(0.1) - - self.change_curve_state(curves, True) - time.sleep(scenario_time) - end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) - start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()-scenario_time)) - self.change_curve_state(curves, False) - # 保留数据并处理输出 - self.gen_results(params, curves, start_time, end_time) - else: - self.change_curve_state(curves, False) - self.logger("INFO", "factory", "后台数据清零完成,现在可以重新运行其他程序。", "green") - - def gen_results(self, params, curves, start_time, end_time): - clibs.cursor.execute(f"select content from logs where time between '{start_time}' and '{end_time}' and content like '%diagnosis.result%' order by id asc") - records = clibs.cursor.fetchall() - self.data_proc(records, params, curves) - - def data_proc(self, records, params, curves): - for curve in curves: - if curve == "device_servo_trq_feedback": - # proc_device_servo_trq_feedback(records, params, w2t) - t = threading.Thread(target=self.proc_device_servo_trq_feedback, args=(records, params)) - t.daemon = True - t.start() - elif curve == "hw_joint_vel_feedback": - # proc_hw_joint_vel_feedback(records, params, w2t) - t = threading.Thread(target=self.proc_hw_joint_vel_feedback, args=(records, params)) - t.daemon = True - t.start() - - def proc_device_servo_trq_feedback(self, records, params): - d_trq, rcs, results = [[], [], [], [], [], []], params["rcs"], [time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))] - for record in records: - data = eval(record[0])["data"] - for item in data: - d_item = reversed(item["value"]) - for axis in range(6): - if item.get("channel", None) == axis and item.get("name", None) == "device_servo_trq_feedback": - d_trq[axis].extend(d_item) - - for axis in range(6): - df = pandas.DataFrame.from_dict({"device_servo_trq_feedback": d_trq[axis]}) - _ = math.sqrt(numpy.square(df[df.columns[0]].values * 1.27 / 1000).sum() / len(df)) - results.append(_) - - path = "/".join(params["prj_file"].split("/")[:-1]) - with open(f"{path}/device_servo_trq_feedback.csv", mode="a+", newline="") as f_csv: - csv_writer = csv.writer(f_csv) - csv_writer.writerow(results) - - def proc_hw_joint_vel_feedback(self, records, params): - d_trq, rcs, results = [[], [], [], [], [], []], params["rcs"], [time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))] - for record in records: - data = eval(record[0])["data"] - for item in data: - d_item = reversed(item["value"]) - for axis in range(6): - if item.get("channel", None) == axis and item.get("name", None) == "hw_joint_vel_feedback": - d_trq[axis].extend(d_item) - - for axis in range(6): - df = pandas.DataFrame.from_dict({"hw_joint_vel_feedback": d_trq[axis]}) - _ = df.max().iloc[0] - results.append(_) - - path = "/".join(params["prj_file"].split("/")[:-1]) - with open(f"{path}/hw_joint_vel_feedback.csv", mode="a+", newline="") as f_csv: - csv_writer = csv.writer(f_csv) - csv_writer.writerow(results) - - def processing(self): - time_start = time.time() - clibs.running[self.idx] = 1 - - data_dirs, data_files = clibs.traversal_files(self.dir_path, self.output) - params = self.initialization(data_dirs, data_files) - prj_file = params["prj_file"] - clibs.c_pd.push_prj_to_server(prj_file) - self.run_rl(params) - - self.logger("INFO", "brake-processing", "-"*60 + "
全部处理完毕
", "purple") - time_total = time.time() - time_start - msg = f"处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s" - self.logger("INFO", "brake-processing", msg) \ No newline at end of file diff --git a/code/ui/login_window.py b/code/ui/login_window.py deleted file mode 100644 index df2f7b4..0000000 --- a/code/ui/login_window.py +++ /dev/null @@ -1,135 +0,0 @@ -# -*- coding: utf-8 -*- - -################################################################################ -## Form generated from reading UI file 'login.ui' -## -## Created by: Qt User Interface Compiler version 6.8.2 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ - -from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, - QMetaObject, QObject, QPoint, QRect, - QSize, QTime, QUrl, Qt) -from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, - QFont, QFontDatabase, QGradient, QIcon, - QImage, QKeySequence, QLinearGradient, QPainter, - QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit, - QPushButton, QSizePolicy, QVBoxLayout, QWidget) - -class Ui_Form(QWidget): - def setupUi(self, Form): - if not Form.objectName(): - Form.setObjectName(u"Form") - Form.setWindowModality(Qt.WindowModality.WindowModal) - Form.resize(500, 270) - sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) - Form.setSizePolicy(sizePolicy) - Form.setMinimumSize(QSize(500, 270)) - Form.setMaximumSize(QSize(500, 270)) - font = QFont() - font.setFamilies([u"Consolas"]) - font.setPointSize(14) - Form.setFont(font) - icon = QIcon() - icon.addFile(u"../assets/media/icon.ico", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - Form.setWindowIcon(icon) - self.widget = QWidget(Form) - self.widget.setObjectName(u"widget") - self.widget.setGeometry(QRect(41, 41, 411, 211)) - self.verticalLayout = QVBoxLayout(self.widget) - self.verticalLayout.setSpacing(2) - self.verticalLayout.setObjectName(u"verticalLayout") - self.verticalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_2 = QHBoxLayout() - self.horizontalLayout_2.setSpacing(2) - self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") - self.label = QLabel(self.widget) - self.label.setObjectName(u"label") - self.label.setFont(font) - - self.horizontalLayout_2.addWidget(self.label) - - self.le_username = QLineEdit(self.widget) - self.le_username.setObjectName(u"le_username") - self.le_username.setFont(font) - - self.horizontalLayout_2.addWidget(self.le_username) - - - self.verticalLayout.addLayout(self.horizontalLayout_2) - - self.horizontalLayout_3 = QHBoxLayout() - self.horizontalLayout_3.setSpacing(2) - self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.label_2 = QLabel(self.widget) - self.label_2.setObjectName(u"label_2") - self.label_2.setFont(font) - - self.horizontalLayout_3.addWidget(self.label_2) - - self.le_password = QLineEdit(self.widget) - self.le_password.setObjectName(u"le_password") - self.le_password.setFont(font) - self.le_password.setEchoMode(QLineEdit.EchoMode.Password) - - self.horizontalLayout_3.addWidget(self.le_password) - - - self.verticalLayout.addLayout(self.horizontalLayout_3) - - self.label_hint = QLabel(self.widget) - self.label_hint.setObjectName(u"label_hint") - sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) - sizePolicy1.setHorizontalStretch(0) - sizePolicy1.setVerticalStretch(0) - sizePolicy1.setHeightForWidth(self.label_hint.sizePolicy().hasHeightForWidth()) - self.label_hint.setSizePolicy(sizePolicy1) - font1 = QFont() - font1.setFamilies([u"Consolas"]) - font1.setPointSize(12) - self.label_hint.setFont(font1) - - self.verticalLayout.addWidget(self.label_hint) - - self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setSpacing(2) - self.horizontalLayout.setObjectName(u"horizontalLayout") - self.btn_login = QPushButton(self.widget) - self.btn_login.setObjectName(u"btn_login") - self.btn_login.setFont(font) - - self.horizontalLayout.addWidget(self.btn_login) - - self.btn_reset = QPushButton(self.widget) - self.btn_reset.setObjectName(u"btn_reset") - self.btn_reset.setFont(font) - - self.horizontalLayout.addWidget(self.btn_reset) - - - self.verticalLayout.addLayout(self.horizontalLayout) - - - self.retranslateUi(Form) - self.btn_login.clicked.connect(Form.user_login) - self.le_password.returnPressed.connect(Form.user_login) - self.le_username.returnPressed.connect(Form.user_login) - self.btn_reset.clicked.connect(Form.reset_password) - - QMetaObject.connectSlotsByName(Form) - # setupUi - - def retranslateUi(self, Form): - Form.setWindowTitle(QCoreApplication.translate("Form", u"\u767b\u5f55", None)) - self.label.setText(QCoreApplication.translate("Form", u"\u7528\u6237\u540d", None)) - self.label_2.setText(QCoreApplication.translate("Form", u"\u5bc6 \u7801", None)) - self.label_hint.setText("") - self.btn_login.setText(QCoreApplication.translate("Form", u"\u767b\u5f55", None)) - self.btn_reset.setText(QCoreApplication.translate("Form", u"\u91cd\u7f6e", None)) - # retranslateUi - diff --git a/code/ui/reset_window.py b/code/ui/reset_window.py deleted file mode 100644 index ec6c7d6..0000000 --- a/code/ui/reset_window.py +++ /dev/null @@ -1,170 +0,0 @@ -# -*- coding: utf-8 -*- - -################################################################################ -## Form generated from reading UI file 'reset.ui' -## -## Created by: Qt User Interface Compiler version 6.8.2 -## -## WARNING! All changes made in this file will be lost when recompiling UI file! -################################################################################ - -from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, - QMetaObject, QObject, QPoint, QRect, - QSize, QTime, QUrl, Qt) -from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, - QFont, QFontDatabase, QGradient, QIcon, - QImage, QKeySequence, QLinearGradient, QPainter, - QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit, - QPushButton, QSizePolicy, QVBoxLayout, QWidget) - -class Ui_Form(QWidget): - def setupUi(self, Form): - if not Form.objectName(): - Form.setObjectName(u"Form") - Form.setWindowModality(Qt.WindowModality.WindowModal) - Form.resize(500, 270) - sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) - Form.setSizePolicy(sizePolicy) - Form.setMinimumSize(QSize(500, 270)) - Form.setMaximumSize(QSize(500, 270)) - font = QFont() - font.setFamilies([u"Consolas"]) - font.setPointSize(14) - Form.setFont(font) - icon = QIcon() - icon.addFile(u"../assets/media/icon.ico", QSize(), QIcon.Mode.Normal, QIcon.State.Off) - Form.setWindowIcon(icon) - self.widget = QWidget(Form) - self.widget.setObjectName(u"widget") - self.widget.setGeometry(QRect(40, 27, 411, 211)) - self.verticalLayout = QVBoxLayout(self.widget) - self.verticalLayout.setObjectName(u"verticalLayout") - self.verticalLayout.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_5 = QHBoxLayout() - self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") - self.label = QLabel(self.widget) - self.label.setObjectName(u"label") - self.label.setFont(font) - - self.horizontalLayout_5.addWidget(self.label) - - self.le_username = QLineEdit(self.widget) - self.le_username.setObjectName(u"le_username") - self.le_username.setFont(font) - - self.horizontalLayout_5.addWidget(self.le_username) - - - self.verticalLayout.addLayout(self.horizontalLayout_5) - - self.horizontalLayout_4 = QHBoxLayout() - self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") - self.label_2 = QLabel(self.widget) - self.label_2.setObjectName(u"label_2") - self.label_2.setFont(font) - - self.horizontalLayout_4.addWidget(self.label_2) - - self.le_old_password = QLineEdit(self.widget) - self.le_old_password.setObjectName(u"le_old_password") - self.le_old_password.setFont(font) - self.le_old_password.setEchoMode(QLineEdit.EchoMode.Password) - - self.horizontalLayout_4.addWidget(self.le_old_password) - - - self.verticalLayout.addLayout(self.horizontalLayout_4) - - self.horizontalLayout_3 = QHBoxLayout() - self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") - self.label_3 = QLabel(self.widget) - self.label_3.setObjectName(u"label_3") - self.label_3.setFont(font) - - self.horizontalLayout_3.addWidget(self.label_3) - - self.le_new_password_1 = QLineEdit(self.widget) - self.le_new_password_1.setObjectName(u"le_new_password_1") - self.le_new_password_1.setFont(font) - self.le_new_password_1.setEchoMode(QLineEdit.EchoMode.Password) - - self.horizontalLayout_3.addWidget(self.le_new_password_1) - - - self.verticalLayout.addLayout(self.horizontalLayout_3) - - self.horizontalLayout_2 = QHBoxLayout() - self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") - self.label_4 = QLabel(self.widget) - self.label_4.setObjectName(u"label_4") - self.label_4.setFont(font) - - self.horizontalLayout_2.addWidget(self.label_4) - - self.le_new_password_2 = QLineEdit(self.widget) - self.le_new_password_2.setObjectName(u"le_new_password_2") - self.le_new_password_2.setFont(font) - self.le_new_password_2.setEchoMode(QLineEdit.EchoMode.Password) - - self.horizontalLayout_2.addWidget(self.le_new_password_2) - - - self.verticalLayout.addLayout(self.horizontalLayout_2) - - self.label_hint = QLabel(self.widget) - self.label_hint.setObjectName(u"label_hint") - sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed) - sizePolicy1.setHorizontalStretch(0) - sizePolicy1.setVerticalStretch(0) - sizePolicy1.setHeightForWidth(self.label_hint.sizePolicy().hasHeightForWidth()) - self.label_hint.setSizePolicy(sizePolicy1) - font1 = QFont() - font1.setFamilies([u"Consolas"]) - font1.setPointSize(12) - self.label_hint.setFont(font1) - - self.verticalLayout.addWidget(self.label_hint) - - self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setObjectName(u"horizontalLayout") - self.btn_reset = QPushButton(self.widget) - self.btn_reset.setObjectName(u"btn_reset") - self.btn_reset.setFont(font) - - self.horizontalLayout.addWidget(self.btn_reset) - - self.btn_cancel = QPushButton(self.widget) - self.btn_cancel.setObjectName(u"btn_cancel") - self.btn_cancel.setFont(font) - - self.horizontalLayout.addWidget(self.btn_cancel) - - - self.verticalLayout.addLayout(self.horizontalLayout) - - QWidget.setTabOrder(self.le_username, self.le_old_password) - QWidget.setTabOrder(self.le_old_password, self.le_new_password_1) - QWidget.setTabOrder(self.le_new_password_1, self.le_new_password_2) - - self.retranslateUi(Form) - self.btn_reset.clicked.connect(Form.reset_password) - self.btn_cancel.clicked.connect(Form.reset_cancel) - - QMetaObject.connectSlotsByName(Form) - # setupUi - - def retranslateUi(self, Form): - Form.setWindowTitle(QCoreApplication.translate("Form", u"\u91cd\u7f6e\u5bc6\u7801", None)) - self.label.setText(QCoreApplication.translate("Form", u"\u7528\u6237\u540d", None)) - self.label_2.setText(QCoreApplication.translate("Form", u"\u65e7\u5bc6\u7801", None)) - self.label_3.setText(QCoreApplication.translate("Form", u"\u65b0\u5bc6\u7801", None)) - self.label_4.setText(QCoreApplication.translate("Form", u"\u786e \u8ba4", None)) - self.label_hint.setText("") - self.btn_reset.setText(QCoreApplication.translate("Form", u"\u786e\u5b9a", None)) - self.btn_cancel.setText(QCoreApplication.translate("Form", u"\u53d6\u6d88", None)) - # retranslateUi - diff --git a/codes/__init__.py b/codes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/codes/analysis/__init__.py b/codes/analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/analysis/brake.py b/codes/analysis/brake.py similarity index 78% rename from code/analysis/brake.py rename to codes/analysis/brake.py index a855df6..68b6a20 100644 --- a/code/analysis/brake.py +++ b/codes/analysis/brake.py @@ -5,7 +5,7 @@ import pandas from PySide6.QtCore import Signal, QThread import openpyxl import re -from common import clibs +from codes.common import clibs class BrakeDataProcess(QThread): @@ -17,17 +17,19 @@ class BrakeDataProcess(QThread): self.idx = 0 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") + @clibs.handle_exception def check_files(self, rawdata_dirs, result_files): msg_wrong = "需要有四个文件和若干个数据文件夹,可参考如下确认:
" msg_wrong += "- reach33/66/100_XXXXXXX.xlsx
- *.cfg
" msg_wrong += "- reach33_load33_speed33
- reach33_load33_speed66
...
- reach100_load100_speed66
- reach100_load100_speed100
" if len(result_files) != 4 or len(rawdata_dirs) == 0: - self.logger("ERROR", "brake-check_files", msg_wrong, "red", "InitFileError") + self.logger("ERROR", "brake", msg_wrong, "red", "InitFileError") config_file, reach33_file, reach66_file, reach100_file = None, None, None, None for result_file in result_files: @@ -42,7 +44,7 @@ class BrakeDataProcess(QThread): reach100_file = result_file else: if not (config_file and reach33_file and reach66_file and reach100_file): - self.logger("ERROR", "brake-check_files", msg_wrong, "red", "InitFileError") + self.logger("ERROR", "brake", msg_wrong, "red", "InitFileError") reach_s = ['reach33', 'reach66', 'reach100'] load_s = ['load33', 'load66', 'load100'] @@ -55,26 +57,27 @@ class BrakeDataProcess(QThread): msg = f"报错信息:数据目录 {rawdata_dir} 命名不合规,请参考如下形式
" msg += "命名规则:reachAA_loadBB_speedCC,AA/BB/CC 指的是臂展/负载/速度的比例
" msg += "规则解释:reach66_load100_speed33,表示 66% 臂展,100% 负载以及 33% 速度情况下的测试结果文件夹
" - self.logger("ERROR", "brake-check_files", msg, "red", "WrongDataFolder") + self.logger("ERROR", "brake", msg, "red", "WrongDataFolder") _, rawdata_files = clibs.traversal_files(rawdata_dir, self.output) if len(rawdata_files) != 3: msg = f"数据目录 {rawdata_dir} 下数据文件个数错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件" - self.logger("ERROR", "brake-check_files", msg, "red", "WrongDataFile") + self.logger("ERROR", "brake", msg, "red", "WrongDataFile") for rawdata_file in rawdata_files: if not rawdata_file.endswith(".data"): msg = f"数据文件 {rawdata_file} 后缀错误,每个数据目录下有且只能有三个以 .data 为后缀的数据文件" - self.logger("ERROR", "brake-check_files", msg, "red", "WrongDataFile") + self.logger("ERROR", "brake", msg, "red", "WrongDataFile") result_files = [] for _ in [reach33_file, reach66_file, reach100_file]: if _.split("/")[-1].split("_")[0] in set(prefix): result_files.append(_) - self.logger("INFO", "brake-check_files", "数据目录合规性检查结束,未发现问题......", "green") + self.logger("INFO", "brake", "数据目录合规性检查结束,未发现问题......", "green") return config_file, result_files + @clibs.handle_exception def get_configs(self, config_file): try: with open(config_file, mode="r", encoding="utf-8") as f_config: @@ -82,7 +85,7 @@ class BrakeDataProcess(QThread): p_dir = config_file.split('/')[-2] if not re.match("^[jJ][123]$", p_dir): - self.logger("ERROR", "brake-get_configs-1", "被处理的根文件夹命名必须是 [Jj][123] 的格式", "red", "DirNameError") + self.logger("ERROR", "brake", "被处理的根文件夹命名必须是 [Jj][123] 的格式", "red", "DirNameError") axis = int(p_dir[-1]) # 要处理的轴 rrs = [abs(_) for _ in configs["TRANSMISSION"]["REDUCTION_RATIO_NUMERATOR"]] # 减速比,rr for reduction ratio @@ -91,21 +94,22 @@ class BrakeDataProcess(QThread): av = avs[axis-1] return av, rr except Exception as Err: - self.logger("ERROR", "brake-get_configs-2", f"无法打开 {config_file},或者使用了错误的机型配置文件,需检查
{Err}", "red", "OpenFileError") + self.logger("ERROR", "brake", f"无法打开 {config_file},或者使用了错误的机型配置文件,需检查
{Err}", "red", "OpenFileError") def now_doing_msg(self, docs, flag): now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) file_type = 'file' if os.path.isfile(docs) else 'dir' if flag == 'start' and file_type == 'dir': - self.logger("INFO", "brake-now_doing_msg", f"[{now}] 正在处理目录 {docs} 中的数据......") + self.logger("INFO", "brake", f"[{now}] 正在处理目录 {docs} 中的数据......") elif flag == 'start' and file_type == 'file': - self.logger("INFO", "brake-now_doing_msg", f"[{now}] 正在处理文件 {docs} 中的数据......") + self.logger("INFO", "brake", f"[{now}] 正在处理文件 {docs} 中的数据......") elif flag == 'done' and file_type == 'dir': - self.logger("INFO", "brake-now_doing_msg", f"[{now}] 目录 {docs} 数据文件已处理完毕") + self.logger("INFO", "brake", f"[{now}] 目录 {docs} 数据文件已处理完毕") elif flag == 'done' and file_type == 'file': - self.logger("INFO", "brake-now_doing_msg", f"[{now}] 文件 {docs} 数据已处理完毕") + self.logger("INFO", "brake", f"[{now}] 文件 {docs} 数据已处理完毕") @staticmethod + @clibs.handle_exception def data2result(df, ws_result, row_start, row_end): data = [] for row in range(row_start, row_end): @@ -126,6 +130,7 @@ class BrakeDataProcess(QThread): ws_result.cell(row=row, column=2).value = None ws_result.cell(row=row, column=3).value = None + @clibs.handle_exception def get_row_range(self, data_file, df, conditions, av, rr): row_start, row_end = 0, 0 ratio = float(conditions[2].removeprefix('speed')) / 100 @@ -137,7 +142,7 @@ class BrakeDataProcess(QThread): row_start = row - 20 if row - 20 > 0 else 0 # 急停前找 20 个点 break else: - self.logger("ERROR", "brake-get_row_range", f"数据文件 {data_file} 采集的数据中没有 ESTOP 为非 0 的情况,需要确认", "red", "StartNotFoundError") + self.logger("ERROR", "brake", f"数据文件 {data_file} 采集的数据中没有 ESTOP 为非 0 的情况,需要确认", "red", "StartNotFoundError") for row in range(row_start, df.index[-1] - 1, 10): speed_row = df.iloc[row, 0] * clibs.RADIAN * rr * 60 / 360 @@ -145,17 +150,18 @@ class BrakeDataProcess(QThread): row_end = row + 100 if row + 100 <= df.index[-1] - 1 else df.index[-1] - 1 break else: - self.logger("ERROR", "brake-get_row_range", f"数据文件 {data_file} 最后的速度未降为零", "red", "SpeedNotZeroError") + self.logger("ERROR", "brake", f"数据文件 {data_file} 最后的速度未降为零", "red", "SpeedNotZeroError") av_estop = abs(df.iloc[row_start - 20:row_start, 0].abs().mean() * clibs.RADIAN) if abs(av_estop / av_max) < threshold: filename = data_file.split("/")[-1] msg = f"[av_estop: {av_estop:.2f} | shouldbe: {av_max:.2f}] 数据文件 {filename} 触发 ESTOP 时未采集到指定百分比的最大速度,需要检查" - self.logger("WARNING", "brake-get_row_range", msg, "#8A2BE2") + self.logger("WARNING", "brake", msg, "#8A2BE2") return row_start, row_end @staticmethod + @clibs.handle_exception def get_shtname(conditions, count): # 33%负载_33%速度_1 - reach/load/speed load = conditions[1].removeprefix('load') @@ -164,6 +170,7 @@ class BrakeDataProcess(QThread): return result_sheet_name + @clibs.handle_exception def single_file_process(self, data_file, wb, count, av, rr): df = pandas.read_csv(data_file, sep='\t') conditions = data_file.split("/")[-2].split("_") # reach/load/speed @@ -173,13 +180,14 @@ class BrakeDataProcess(QThread): row_start, row_end = self.get_row_range(data_file, df, conditions, av, rr) self.data2result(df, ws, row_start, row_end) + @clibs.handle_exception def data_process(self, result_file, rawdata_dirs, av, rr): filename = result_file.split("/")[-1] - self.logger("INFO", "brake-data_process", f"正在打开文件 {filename},这可能需要一些时间......", "blue") + self.logger("INFO", "brake", f"正在打开文件 {filename},这可能需要一些时间......", "blue") try: wb = openpyxl.load_workbook(result_file) except Exception as Err: - self.logger("ERROR", "brake-data_process", f"{filename}文件打开失败,可能是文件已损坏,确认后重新执行!
{Err}", "red", "CannotOpenFile") + self.logger("ERROR", "brake", f"{filename}文件打开失败,可能是文件已损坏,确认后重新执行!
{Err}", "red", "CannotOpenFile") prefix = filename.split('_')[0] for rawdata_dir in rawdata_dirs: @@ -197,10 +205,11 @@ class BrakeDataProcess(QThread): # [t.join() for t in threads] self.now_doing_msg(rawdata_dir, 'done') - self.logger("INFO", "brake-data_process", f"正在保存文件 {filename},这可能需要一些时间......
", "blue") + self.logger("INFO", "brake", f"正在保存文件 {filename},这可能需要一些时间......
", "blue") wb.save(result_file) wb.close() + @clibs.handle_exception def processing(self): time_start = time.time() clibs.running[self.idx] = 1 @@ -212,7 +221,7 @@ class BrakeDataProcess(QThread): for result_file in result_files: self.data_process(result_file, rawdata_dirs, av, rr) - self.logger("INFO", "brake-processing", "-"*60 + "
全部处理完毕
", "purple") + self.logger("INFO", "brake", "-"*60 + "
全部处理完毕
", "purple") time_total = time.time() - time_start msg = f"处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s" - self.logger("INFO", "brake-processing", msg) + self.logger("INFO", "brake", msg) diff --git a/code/analysis/current.py b/codes/analysis/current.py similarity index 81% rename from code/analysis/current.py rename to codes/analysis/current.py index 533ec1e..a88fcf1 100644 --- a/code/analysis/current.py +++ b/codes/analysis/current.py @@ -5,7 +5,7 @@ import re import csv from PySide6.QtCore import Signal, QThread import time -from common import clibs +from codes.common import clibs class CurrentDataProcess(QThread): @@ -18,10 +18,12 @@ class CurrentDataProcess(QThread): self.idx = 1 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") + @clibs.handle_exception def initialization(self): _, data_files = clibs.traversal_files(self.dir_path, self.output) count, config_file = 0, None @@ -36,16 +38,17 @@ class CurrentDataProcess(QThread): if not re.match("^j[1-7].*\\.data$", filename): msg = f"不合规 {data_file}
" msg += "所有数据文件必须以 j[1-7]_ 开头,以 .data 结尾,比如j1_abcdef.data,请检查整改后重新运行" - self.logger("ERROR", "current-initialization", msg, "red", "FilenameIllegal") + self.logger("ERROR", "current", msg, "red", "FilenameIllegal") if count != 2: msg = "需要有一个机型配置文件\"*.cfg\",以及一个数据处理文件\"T_电机电流.xlsx\"表格,请检查整改后重新运行" - self.logger("ERROR", "current-initialization", msg, "red", "FilenameIllegal") + self.logger("ERROR", "current", msg, "red", "FilenameIllegal") return data_files, config_file + @clibs.handle_exception def current_max(self, data_files, rts): - self.logger("INFO", "current-current_max", f"正在处理最大转矩值逻辑......") + self.logger("INFO", "current", f"正在处理最大转矩值逻辑......") current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: []} for data_file in data_files: if data_file.endswith(".data"): @@ -53,11 +56,11 @@ class CurrentDataProcess(QThread): else: continue - self.logger("INFO", "current-current_max", f"正在处理 {data_file} ...") + self.logger("DEBUG", "current", f"正在处理 {data_file} ...") cols = len(df.columns) axis = int(data_file.split("/")[-1].split("_")[0].removeprefix("j")) rt = rts[axis-1] - self.logger("INFO", "current-current_max", f"最大列数为 {cols},{axis} 轴的额定转矩为 {rt}") + self.logger("DEBUG", "current", f"最大列数为 {cols},{axis} 轴的额定转矩为 {rt}") col = df.columns.values[clibs.c_servo_trq-1] # 获取 "device_servo_trq_feedback" c_max = df[col].abs().max() @@ -65,8 +68,8 @@ class CurrentDataProcess(QThread): scale = 1000 _ = abs(c_max/scale*rt) current[axis].append(_) - self.logger("INFO", "current-current_max", f"{data_file}: {_:.2f}") - self.logger("INFO", "current-current_max", f"获取到的列名为 {col},最大转矩为 {_}") + self.logger("DEBUG", "current", f"{data_file}: {_:.2f}") + self.logger("DEBUG", "current", f"获取到的列名为 {col},最大转矩为 {_}") with open(data_file, "a+") as f_data: csv_writer = csv.writer(f_data, delimiter="\t") @@ -79,14 +82,15 @@ class CurrentDataProcess(QThread): _ = "" for value in cur: _ += f"{value:.4f} " - self.logger("INFO", "current-current_max", f"{axis}轴最大转矩数据:{_}") + self.logger("INFO", "current", f"{axis}轴最大转矩数据:{_}") - self.logger("INFO", "current-current_max", f"获取最大转矩值结束 current_max = {current}") - self.logger("INFO", "current-current_max", f"最大转矩数据处理完毕......") + self.logger("DEBUG", "current", f"获取最大转矩值结束 current_max = {current}") + self.logger("INFO", "current", f"最大转矩数据处理完毕......") return current + @clibs.handle_exception def current_avg(self, data_files, rts): - self.logger("INFO", "current-current_avg", f"正在处理平均转矩值逻辑......") + self.logger("INFO", "current", f"正在处理平均转矩值逻辑......") current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: []} for data_file in data_files: if data_file.endswith(".data"): @@ -94,11 +98,11 @@ class CurrentDataProcess(QThread): else: continue - self.logger("INFO", "current-current_avg", f"正在处理 {data_file} ...") + self.logger("DEBUG", "current", f"正在处理 {data_file} ...") cols = len(df.columns) axis = int(data_file.split("/")[-1].split("_")[0].removeprefix("j")) rt = rts[axis-1] - self.logger("INFO", "current-current_avg", f"最大列数为 {cols},{axis} 轴的额定转矩为 {rt}") + self.logger("DEBUG", "current", f"最大列数为 {cols},{axis} 轴的额定转矩为 {rt}") col = df.columns.values[clibs.c_servo_trq-1] c_std = df[col].std() @@ -107,8 +111,8 @@ class CurrentDataProcess(QThread): scale = 1000 _ = (abs(c_avg)+c_std*3)/scale*rt current[axis].append(_) - self.logger("INFO", "current-current_avg", f"{data_file}: {_:.2f}") - self.logger("INFO", "current-current_avg", f"获取到的列名为 {col},平均转矩为 {_}") + self.logger("DEBUG", "current", f"{data_file}: {_:.2f}") + self.logger("DEBUG", "current", f"获取到的列名为 {col},平均转矩为 {_}") with open(data_file, "a+") as f_data: csv_writer = csv.writer(f_data, delimiter="\t") @@ -121,12 +125,13 @@ class CurrentDataProcess(QThread): _ = "" for value in cur: _ += f"{value:.4f} " - self.logger("INFO", "current-current_avg", f"{axis}轴平均转矩数据:{_}") + self.logger("INFO", "current", f"{axis}轴平均转矩数据:{_}") - self.logger("INFO", "current-current_avg", f"获取平均转矩值结束 current_avg = {current}", flag="cursor") - self.logger("INFO", "current-current_avg", f"平均转矩数据处理完毕......") + self.logger("DEBUG", "current", f"获取平均转矩值结束 current_avg = {current}") + self.logger("INFO", "current", f"平均转矩数据处理完毕......") return current + @clibs.handle_exception def current_cycle(self, data_files, rrs, rts, params): result, hold, single, scenario, dur_time = None, [], [], [], 0 for data_file in data_files: @@ -142,11 +147,11 @@ class CurrentDataProcess(QThread): single.append(data_file) clibs.stop, filename = True, result.split("/")[-1] - self.logger("INFO", "current-current_cycle", f"正在打开文件 {filename},这可能需要一些时间......", "blue") + self.logger("INFO", "current", f"正在打开文件 {filename},这可能需要一些时间......", "blue") try: wb = openpyxl.load_workbook(result) except Exception as Err: - self.logger("ERROR", "current-current_cycle", f"{filename}文件打开失败,可能是文件已损坏,确认后重新执行!
{Err}", "red", "CannotOpenFile") + self.logger("ERROR", "current", f"{filename}文件打开失败,可能是文件已损坏,确认后重新执行!
{Err}", "red", "CannotOpenFile") ws = wb["统计"] for idx in range(len(params)-1): @@ -166,10 +171,11 @@ class CurrentDataProcess(QThread): else: self.p_scenario(wb, scenario, rrs, dur_time) - self.logger("INFO", "current-current_cycle", f"正在保存文件 {filename},这可能需要一些时间......", "blue") + self.logger("INFO", "current", f"正在保存文件 {filename},这可能需要一些时间......", "blue") wb.save(result) wb.close() + @clibs.handle_exception def find_point(self, data_file, df, flag, row_s, row_e, threshold, step, end_point, skip_scale, axis, seq): if flag == "lt": while row_e > end_point: @@ -183,15 +189,13 @@ class CurrentDataProcess(QThread): # 从实际数据看,这开逻辑很小概率能触发到 speed_avg = df.iloc[row_s-end_point*skip_scale:row_e-end_point*skip_scale].abs().mean() if speed_avg < threshold: - self.logger("WARNING", "current-find_point", f"【lt】{axis} 轴第 {seq} 次查找数据可能有异常,row_s = {row_s}, row_e = {row_e}!", "purple") + self.logger("WARNING", "current", f"【lt】{axis} 轴第 {seq} 次查找数据可能有异常,row_s = {row_s}, row_e = {row_e}!", "purple") return row_s, row_e else: - self.logger("ERROR", "current-find_point", f"{data_file} 数据有误,需要检查,无法找到第 {seq} 个有效点......", "red", "AnchorNotFound") + self.logger("ERROR", "current", f"{data_file} 数据有误,需要检查,无法找到第 {seq} 个有效点......", "red", "AnchorNotFound") elif flag == "gt": while row_e > end_point: speed_avg = df.iloc[row_s:row_e].abs().mean() - # if axis == 1 and seq == 1: - # insert_logdb("DEBUG", "current", f"【gt】{axis} 轴,speed_avg = {speed_avg},row_s = {row_s}, row_e = {row_e}!") if speed_avg > threshold: row_e -= step row_s -= step @@ -201,11 +205,12 @@ class CurrentDataProcess(QThread): # 从实际数据看,这开逻辑很小概率能触发到 speed_avg = df.iloc[row_s-end_point*skip_scale:row_e-end_point*skip_scale].abs().mean() if speed_avg > threshold: - self.logger("WARNING", "current-find_point", f"【gt】{axis} 轴第 {seq} 次查找数据可能有异常,row_s = {row_s}, row_e = {row_e}!", "purple") + self.logger("WARNING", "current", f"【gt】{axis} 轴第 {seq} 次查找数据可能有异常,row_s = {row_s}, row_e = {row_e}!", "purple") return row_s, row_e else: - self.logger("ERROR", "current-find_point", f"{data_file} 数据有误,需要检查,无法找到第 {seq} 个有效点......", "red", "AnchorNotFound") + self.logger("ERROR", "current", f"{data_file} 数据有误,需要检查,无法找到第 {seq} 个有效点......", "red", "AnchorNotFound") + @clibs.handle_exception def get_row_number(self, threshold, flag, df, row_s, row_e, axis): count_1, count_2 = 0, 0 if flag == "start" or flag == "end": @@ -228,9 +233,10 @@ class CurrentDataProcess(QThread): count_1 = 0 places = {"start": "起点", "middle": "中间点", "end": "终点"} # 因为是终点数据,所以可能有异常 - self.logger("WARNING", "current-get_row_number", f"{axis} 轴获取{places[flag]}数据 {row_e} 可能有异常,需关注!", "purple") + self.logger("DEBUG", "current", f"{axis} 轴获取{places[flag]}数据 {row_e} 可能有异常,需关注!", "purple") return row_e + @clibs.handle_exception def p_single(self, wb, single, rrs): # 1. 先找到第一个速度为零的点,数据从后往前找,一开始就是零的情况不予考虑 # 2. 记录第一个点的位置,继续向前查找第二个速度为零的点,同理,一开始为零的点不予考虑 @@ -304,13 +310,13 @@ class CurrentDataProcess(QThread): row_s, row_e = self.find_point(data_file, df, "gt", row_s, row_e, threshold, step, end_point, skip_scale, axis, 3) row_start = self.get_row_number(threshold, "start", df, row_s, row_e, axis) - self.logger("INFO", "current", f"{axis} 轴起点:{row_start}") - self.logger("INFO", "current", f"{axis} 轴中间点:{row_middle}") - self.logger("INFO", "current", f"{axis} 轴终点:{row_end}") - self.logger("INFO", "current", f"{axis} 轴数据非零段点数:{row_middle-row_start+1}") - self.logger("INFO", "current", f"{axis} 轴数据为零段点数:{row_end-row_middle+1}") + self.logger("DEBUG", "current", f"{axis} 轴起点:{row_start}") + self.logger("DEBUG", "current", f"{axis} 轴中间点:{row_middle}") + self.logger("DEBUG", "current", f"{axis} 轴终点:{row_end}") + self.logger("DEBUG", "current", f"{axis} 轴数据非零段点数:{row_middle-row_start+1}") + self.logger("DEBUG", "current", f"{axis} 轴数据为零段点数:{row_end-row_middle+1}") if abs(row_end+row_start-2*row_middle) > 1000: - self.logger("WARNING", "current", f"{axis} 轴数据占空比异常!", "purple") + self.logger("DEBUG", "current", f"{axis} 轴数据占空比异常!", "purple") data, first_c, second_c, third_c, fourth_c = [], clibs.c_joint_vel-1, clibs.c_servo_trq-1, clibs.c_sensor_trq-1, clibs.c_estimate_trans_trq-1 for row in range(row_start, row_end+1): @@ -334,6 +340,7 @@ class CurrentDataProcess(QThread): cell.value = None i += 1 + @clibs.handle_exception def p_scenario(self, wb, scenario, rrs, dur_time): self.logger("INFO", "current", f"本次处理的是电机电流场景数据,场景运动周期为 {dur_time}s", "blue") for data_file in scenario: @@ -352,7 +359,7 @@ class CurrentDataProcess(QThread): row_start = 3000 row_end = row_start + int(dur_time/cycle) if row_end > df.index[-1]: - self.logger("ERROR", "current-p_scenario", f"位置超限:{data_file} 共有 {df.index[-1]} 条数据,无法取到第 {row_end} 条数据,需要确认场景周期时间...", "blue", "DataOverLimit") + self.logger("ERROR", "current", f"位置超限:{data_file} 共有 {df.index[-1]} 条数据,无法取到第 {row_end} 条数据,需要确认场景周期时间...", "blue", "DataOverLimit") data, first_c, second_c, third_c, fourth_c = [], clibs.c_joint_vel-1, clibs.c_servo_trq-1, clibs.c_sensor_trq-1, clibs.c_estimate_trans_trq-1 for row in range(row_start, row_end+1): @@ -376,6 +383,7 @@ class CurrentDataProcess(QThread): ws.cell((i//4)+2, 1).value = None i += 1 + @clibs.handle_exception def get_configs(self, config_file): try: if re.match("^[NXEC]B.*", config_file.split("/")[-1]): @@ -407,6 +415,7 @@ class CurrentDataProcess(QThread): except Exception as Err: self.logger("ERROR", "current", f"get_config: 无法打开 {config_file},或获取配置文件参数错误 {Err}", "red", "OpenFileError") + @clibs.handle_exception def processing(self): time_start = time.time() clibs.running[self.idx] = 1 @@ -421,7 +430,7 @@ class CurrentDataProcess(QThread): elif self.proc == "周期": self.current_cycle(data_files, rrs, rts, params) - self.logger("INFO", "current-processing", "-"*60 + "
全部处理完毕
", "purple") + self.logger("INFO", "current", "-"*60 + "
全部处理完毕
", "purple") time_total = time.time() - time_start msg = f"数据处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s\n" - self.logger("INFO", "current-processing", msg) + self.logger("INFO", "current", msg) diff --git a/code/analysis/iso.py b/codes/analysis/iso.py similarity index 99% rename from code/analysis/iso.py rename to codes/analysis/iso.py index 2aeacda..a3b5a9c 100644 --- a/code/analysis/iso.py +++ b/codes/analysis/iso.py @@ -3,7 +3,7 @@ import openpyxl import os import time from PySide6.QtCore import Signal, QThread -from common import clibs +from codes.common import clibs class IsoDataProcess(QThread): @@ -15,6 +15,7 @@ class IsoDataProcess(QThread): self.idx = 2 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") diff --git a/code/analysis/wavelogger.py b/codes/analysis/wavelogger.py similarity index 86% rename from code/analysis/wavelogger.py rename to codes/analysis/wavelogger.py index 1a3422f..1dc275e 100644 --- a/code/analysis/wavelogger.py +++ b/codes/analysis/wavelogger.py @@ -4,7 +4,7 @@ import openpyxl import chardet import time from PySide6.QtCore import Signal, QThread -from common import clibs +from codes.common import clibs class WaveloggerDataProcess(QThread): @@ -16,6 +16,7 @@ class WaveloggerDataProcess(QThread): self.idx = 3 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") @@ -37,7 +38,7 @@ class WaveloggerDataProcess(QThread): break else: if bof == "backward": - self.logger("ERROR", "wavelogger-find_point", f"find_point-gt: [{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", "red", "DataError") + self.logger("ERROR", "wavelogger", f"find_point-gt: [{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", "red", "DataError") elif bof == "forward": row_target = row + margin # to end while loop in function `single_file_proc` elif flag == "lt": @@ -51,7 +52,7 @@ class WaveloggerDataProcess(QThread): break else: if bof == "backward": - self.logger("ERROR", "wavelogger-find_point", f"find_point-lt: [{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", "red", "DataError") + self.logger("ERROR", "wavelogger", f"find_point-lt: [{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", "red", "DataError") elif bof == "forward": row_target = row + margin # to end while loop in function `single_file_proc` return row_target @@ -88,7 +89,7 @@ class WaveloggerDataProcess(QThread): for data_file in data_files: if not data_file.lower().endswith(".csv"): - self.logger("ERROR", "wavelogger-initialization", f"init: {data_file} 文件后缀错误,只允许 .csv 文件,需要确认!", "red", "FileTypeError") + self.logger("ERROR", "wavelogger", f"init: {data_file} 文件后缀错误,只允许 .csv 文件,需要确认!", "red", "FileTypeError") return data_files @@ -114,7 +115,7 @@ class WaveloggerDataProcess(QThread): value = df.iloc[start:end, 2].astype(float).mean() + 3 * df.iloc[start:end, 2].astype(float).std() if value > 1: msg = f"\n" - self.logger("WARNING", "wavelogger-single_file_proc", f"{data_file} 文件第 {count} 轮 第 {count_i} 个数据可能有问题,需人工手动确认,确认有问题可删除,无问题则保留", "purple") + self.logger("WARNING", "wavelogger", f"{data_file} 文件第 {count} 轮 第 {count_i} 个数据可能有问题,需人工手动确认,确认有问题可删除,无问题则保留", "purple") data[count].append(value) count_i += 1 @@ -135,7 +136,7 @@ class WaveloggerDataProcess(QThread): row += 1 def execution(self, data_files): - self.logger("INFO", "wavelogger-execution", "正在处理中......", "blue") + self.logger("INFO", "wavelogger", "正在处理中......", "blue") wb = openpyxl.Workbook() step, margin, data_length, threshold = 5, 50, 50, 5 for data_file in data_files: @@ -154,7 +155,7 @@ class WaveloggerDataProcess(QThread): data_files = self.initialization() self.execution(data_files) - self.logger("INFO", "wavelogger-processing", "-" * 60 + "
全部处理完毕
", "purple") + self.logger("INFO", "wavelogger", "-" * 60 + "
全部处理完毕
", "purple") time_total = time.time() - time_start msg = f"数据处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s\n" - self.logger("INFO", "wavelogger-processing", msg) + self.logger("INFO", "wavelogger", msg) diff --git a/codes/autotest/__init__.py b/codes/autotest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/autotest/do_brake.py b/codes/autotest/do_brake.py similarity index 79% rename from code/autotest/do_brake.py rename to codes/autotest/do_brake.py index b30a5fb..eeecf6d 100644 --- a/code/autotest/do_brake.py +++ b/codes/autotest/do_brake.py @@ -5,7 +5,7 @@ import openpyxl import pandas import json from PySide6.QtCore import Signal, QThread -from common import clibs +from codes.common import clibs class DoBrakeTest(QThread): @@ -18,16 +18,18 @@ class DoBrakeTest(QThread): self.idx = 4 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") def initialization(self, data_dirs, data_files): + @clibs.handle_exception def check_files(): - msg = "初始路径下不允许有文件夹,初始路径下只能存在如下五个文件,且文件为关闭状态,确认后重新运行!
" + msg = "初始路径下不允许有文件夹,只能存在如下五个文件,且文件为关闭状态,确认后重新运行!
" msg += "1. configs.xlsx
2. reach33/reach66/reach100_xxxx.xlsx
3. xxxx.zip" if len(data_dirs) != 0 or len(data_files) != 5: - self.logger("ERROR", "do_brake-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_brake", msg, "red", "InitFileError") config_file, reach33_file, reach66_file, reach100_file, prj_file, result_dirs = None, None, None, None, None, [] for data_file in data_files: @@ -43,7 +45,7 @@ class DoBrakeTest(QThread): elif filename.endswith(".zip"): prj_file = data_file else: - self.logger("ERROR", "do_brake-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_brake", msg, "red", "InitFileError") if config_file and reach33_file and reach66_file and reach100_file and prj_file: os.mkdir(f"{self.dir_path}/j1") @@ -60,15 +62,15 @@ class DoBrakeTest(QThread): if reach == "reach100": os.mkdir(f"{self.dir_path}/j3/{dir_name}") - self.logger("INFO", "do_brake-check_files", "数据目录合规性检查结束,未发现问题......", "green") return config_file, prj_file, result_dirs else: - self.logger("ERROR", "do_brake-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_brake", msg, "red", "InitFileError") + @clibs.handle_exception def get_configs(): robot_type = None - msg_id, state = clibs.c_hr.execution("controller.get_params") - records = clibs.c_hr.get_from_id(msg_id, state) + msg_id = clibs.c_hr.execution("controller.get_params") + records = clibs.c_hr.get_from_id(msg_id) for record in records: if "请求发送成功" not in record[0]: robot_type = eval(record[0])["data"]["robot_type"] @@ -80,28 +82,29 @@ class DoBrakeTest(QThread): with open(local_file, mode="r", encoding="utf-8") as f_config: configs = json.load(f_config) except Exception as Err: - self.logger("ERROR", "do_brake-get_configs", f"无法打开 {local_file}
{Err}", "red", "OpenFileError") + self.logger("ERROR", "do_brake", f"无法打开 {local_file}
{Err}", "red", "OpenFileError") # 最大角速度,额定电流,减速比,额定转速 version = configs["VERSION"] avs = configs["MOTION"]["JOINT_MAX_SPEED"] - clibs.insert_logdb("INFO", "do_brake", f"get_configs: 机型文件版本 {robot_type}_{version}") - clibs.insert_logdb("INFO", "do_brake", f"get_configs: 各关节角速度 {avs}") + self.logger("INFO", "do_brake", f"get_configs: 机型文件版本 {robot_type}_{version}") + self.logger("INFO", "do_brake", f"get_configs: 各关节角速度 {avs}") return avs + self.logger("INFO", "do_brake", "正在做初始化校验和配置,这可能需要一点时间......", "green") _config_file, _prj_file, _result_dirs = check_files() _avs = get_configs() - + self.logger("INFO", "do_brake", "数据目录合规性检查结束,未发现问题......", "green") return _config_file, _prj_file, _result_dirs, _avs - def gen_result_file(self, axis, t_end, reach, load, speed, speed_max, rounds): + @clibs.handle_exception + def gen_result_file(self, axis, end_time, reach, load, speed, speed_max, rounds): d_vel, d_trq, d_stop, threshold = [], [], [], 0.95 - - start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t_end-12)) - end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t_end)) + s_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time-clibs.INTERVAL*12)) + e_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) try: clibs.lock.acquire(True) - clibs.cursor.execute(f"select content from logs where time between '{start_time}' and '{end_time}' and content like '%diagnosis.result%' order by id asc") + clibs.cursor.execute(f"SELECT content FROM logs WHERE timestamp BETWEEN '{s_time}' AND '{e_time}' AND content LIKE '%diagnosis.result%' ORDER BY id ASC") records = clibs.cursor.fetchall() finally: clibs.lock.release() @@ -124,13 +127,13 @@ class DoBrakeTest(QThread): av_estop = abs(sum(d_vel[idx - 20:idx])/20 * clibs.RADIAN) if av_estop / speed_max < threshold: - self.logger("WARNING", "do_brake-gen_result_file", f"[av_estop: {av_estop:.2f} | shouldbe: {speed_max:.2f}] 处理数据时,本次触发 ESTOP 时未采集到指定百分比的最大速度,即将重试!", "#8A2BE2") + self.logger("WARNING", "do_brake", f"[av_estop: {av_estop:.2f} | shouldbe: {speed_max:.2f}] 处理数据时,本次触发 ESTOP 时未采集到指定百分比的最大速度,即将重试!", "#8A2BE2") clibs.count += 1 if clibs.count < 3: return "retry" else: clibs.count = 0 - self.logger("WARNING", "do_brake-gen_result_file",f"尝试三次后仍无法获取正确数据,本次数据无效,继续执行...", "red") + self.logger("WARNING", "do_brake",f"尝试三次后仍无法获取正确数据,本次数据无效,继续执行...", "red") df1 = pandas.DataFrame.from_dict({"hw_joint_vel_feedback": d_vel}) df2 = pandas.DataFrame.from_dict({"device_servo_trq_feedback": d_trq}) @@ -140,15 +143,17 @@ class DoBrakeTest(QThread): df.to_csv(filename, sep="\t", index=False) @staticmethod + @clibs.handle_exception def change_curve_state(stat): if not stat: display_pdo_params = [] else: display_pdo_params = [{"name": name, "channel": chl} for name in ["hw_joint_vel_feedback", "device_servo_trq_feedback"] for chl in range(6)] display_pdo_params.append({"name": "device_safety_estop", "channel": 0}) - clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat, overrun=True, turn_area=True, delay_motion=False) - clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") + clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat) + clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params) + @clibs.handle_exception def run_rl(self, config_file, prj_file, result_dirs, avs): count, total, speed_target = 0, 63, 0 prj_name = ".".join(prj_file.split("/")[-1].split(".")[:-1]) @@ -161,14 +166,14 @@ class DoBrakeTest(QThread): io_name = ws.cell(row=6, column=2).value.upper().strip() wb.close() msg = f"基本参数配置:write_diagnosis(废弃) = {write_diagnosis}, get_init_speed = {get_init_speed}, single_brake = {single_brake}, pon = {pon}" - self.logger("INFO", "do_brake-run_rl", msg) + self.logger("INFO", "do_brake", msg) if pon == "positive": clibs.c_md.write_pon(1) elif pon == "negative": clibs.c_md.write_pon(0) else: - self.logger("ERROR", "do_brake-run_rl", "configs.xlsx 中 Target 页面 B5 单元格填写不正确,检查后重新运行...", "red", "DirectionError") + self.logger("ERROR", "do_brake", "configs.xlsx 中 Target 页面 B5 单元格填写不正确,检查后重新运行...", "red", "DirectionError") self.change_curve_state(True) for condition in result_dirs: @@ -198,7 +203,7 @@ class DoBrakeTest(QThread): this_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) prj_path = f"{prj_name}/_build/{prj_name}.prj" msg = f"[{this_time} | {count}/{total}] 正在执行 {axis} 轴 {condition} 的第 {_} 次制动测试..." - self.logger("INFO", "do_brake-run_rl", msg) + self.logger("INFO", "do_brake", msg) # 1. 触发软急停,并解除,目的是让可能正在运行着的机器停下来,切手动模式并下电 clibs.c_md.r_soft_estop(0) @@ -247,7 +252,7 @@ class DoBrakeTest(QThread): else: time.sleep(1) if (time.time() - t_start) > 15: - self.logger("ERROR", "do_brake-run_rl", "15s 内未收到机器人的运行信号,需要确认 RL 程序编写正确并正常执行...", "red", "ReadySignalTimeoutError") + self.logger("ERROR", "do_brake", "15s 内未收到机器人的运行信号,需要确认 RL 程序编写正确并正常执行...", "red", "ReadySignalTimeoutError") # 4. 找出最大速度,传递给RL程序,最后清除相关记录 time.sleep(5) # 消除前 5s 的不稳定数据 start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) @@ -257,9 +262,10 @@ class DoBrakeTest(QThread): # 找出最大速度 @clibs.db_lock + @clibs.handle_exception def get_speed_max(): _speed_max = 0 - clibs.cursor.execute(f"select content from logs where time between '{start_time}' and '{end_time}' and content like '%diagnosis.result%' order by id asc") + clibs.cursor.execute(f"SELECT content FROM logs WHERE timestamp BETWEEN '{start_time}' AND '{end_time}' AND content LIKE '%diagnosis.result%' ORDER BY id ASC") records = clibs.cursor.fetchall() for record in records: data = eval(record[0])["data"] @@ -274,15 +280,14 @@ class DoBrakeTest(QThread): speed_max = abs(get_speed_max()) speed_target = avs[axis-1] * float(speed) / 100 - clibs.insert_logdb("INFO", "do_brake", f"axis = {axis}, direction = {pon}, max speed = {speed_max}") + self.logger("INFO", "do_brake", f"axis = {axis}, direction = {pon}, max speed = {speed_max}") if speed_max < speed_target*0.95 or speed_max > speed_target*1.05: - self.logger("WARNING", "do_brake-run_rl", f"Axis: {axis}-{count} | 采集获取最大 Speed: {speed_max} | Shouldbe: {speed_target}", "indigo") - clibs.insert_logdb("WARNING", "do_brake", f"Axis: {axis}-{count} | 采集获取最大 Speed: {speed_max} | Shouldbe: {speed_target}") + self.logger("WARNING", "do_brake", f"Axis: {axis}-{count} | 采集获取最大 Speed: {speed_max} | Shouldbe: {speed_target}", "indigo") clibs.c_md.write_speed_max(speed_max) if speed_max < 10: clibs.c_md.r_clear_alarm() - self.logger("WARNING", "do_brake-run_rl", f"未获取到正确的速度,即将重新获取...", "red") + self.logger("WARNING", "do_brake", f"未获取到正确的速度,即将重新获取...", "red") continue else: break @@ -306,7 +311,7 @@ class DoBrakeTest(QThread): else: time.sleep(5) if time.time() - t_start > 60: - self.logger("ERROR", "do_brake-run_rl","60s 内程序未能正常执行,需检查...", "red", "RlProgramStartTimeout") + self.logger("ERROR", "do_brake","60s 内程序未能正常执行,需检查...", "red", "RlProgramStartTimeout") for i in range(16): if clibs.c_md.read_ready_to_go() == 1: @@ -315,14 +320,15 @@ class DoBrakeTest(QThread): else: time.sleep(1) else: - self.logger("ERROR", "do_brake-run_rl", "16s 内未收到机器人的运行信号,需要确认 RL 程序配置正确并正常执行...", "red", "ReadySignalTimeoutError") + self.logger("ERROR", "do_brake", "16s 内未收到机器人的运行信号,需要确认 RL 程序配置正确并正常执行...", "red", "ReadySignalTimeoutError") + @clibs.handle_exception def exec_brake(): flag, start, data, record = True, time.time(), None, None while flag: time.sleep(0.05) if time.time() - start > 20: - self.logger("ERROR", "do_brake-exec_brake", "20s 内未触发急停,需排查......", "red", "BrakeTimeoutError") + self.logger("ERROR", "do_brake", "20s 内未触发急停,需排查......", "red", "BrakeTimeoutError") try: clibs.lock.acquire(True) @@ -340,15 +346,15 @@ class DoBrakeTest(QThread): if abs(speed_moment) > speed_max - 2: if (pon == "positive" and speed_moment > 0) or (pon == "negative" and speed_moment < 0): clibs.c_ec.setdo_value(io_name, "false") - time.sleep(2) + time.sleep(clibs.INTERVAL*2) # wait speed goes down to 0 flag = False break return time.time() time.sleep(11) # 排除从其他位姿到零点位姿,再到轴极限位姿的时间 - t_end = exec_brake() + end_time = exec_brake() # 6. 保留数据并处理输出 - ret = self.gen_result_file(axis, t_end, reach, load, speed, speed_max, rounds) + ret = self.gen_result_file(axis, end_time, reach, load, speed, speed_max, rounds) if ret != "retry": clibs.count = 0 break @@ -356,19 +362,22 @@ class DoBrakeTest(QThread): else: time.sleep(50) # why? self.change_curve_state(False) - msg = f"\n{self.tool.removeprefix('tool')}%负载的制动性能测试执行完毕,如需采集其他负载,须切换负载类型,并更换其他负载,重新执行" - self.logger("INFO", "do_brake-run_rl", msg, "green") + msg = f"
{self.tool.removeprefix('tool')}%负载的制动性能测试执行完毕,如需采集其他负载,须切换负载类型,并更换其他负载,重新执行" + self.logger("INFO", "do_brake", msg, "green") + @clibs.handle_exception def processing(self): time_start = time.time() clibs.running[self.idx] = 1 + if clibs.status["hmi"] != 1 or clibs.status["md"] != 1 or clibs.status["ec"] != 1: + self.logger("ERROR", "do_brake", "processing: 需要在网络设置中连接HMI,Modbus通信以及外部通信!", "red", "NetworkError") data_dirs, data_files = clibs.traversal_files(self.dir_path, self.output) config_file, prj_file, result_dirs, avs = self.initialization(data_dirs, data_files) clibs.c_pd.push_prj_to_server(prj_file) self.run_rl(config_file, prj_file, result_dirs, avs) - self.logger("INFO", "brake-processing", "-"*60 + "
全部处理完毕
", "purple") + self.logger("INFO", "do_brake", "-"*60 + "
全部处理完毕
", "purple") time_total = time.time() - time_start msg = f"处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s" - self.logger("INFO", "brake-processing", msg) + self.logger("INFO", "do_brake", msg) diff --git a/code/autotest/do_current.py b/codes/autotest/do_current.py similarity index 77% rename from code/autotest/do_current.py rename to codes/autotest/do_current.py index 0901d33..d95ebc4 100644 --- a/code/autotest/do_current.py +++ b/codes/autotest/do_current.py @@ -4,10 +4,10 @@ import time import paramiko import pandas from PySide6.QtCore import Signal, QThread -from common import clibs +from codes.common import clibs -class DoBrakeTest(QThread): +class DoCurrentTest(QThread): output = Signal(str, str) def __init__(self, dir_path, tool, /): @@ -17,16 +17,18 @@ class DoBrakeTest(QThread): self.idx = 5 def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") def initialization(self, data_dirs, data_files): + @clibs.handle_exception def check_files(): msg = "初始路径下不允许有文件夹,初始路径下只能存在如下两个文件,且文件为关闭状态,确认后重新运行!
" msg += "1. T_电机电流.xlsx
2. xxxx.zip" if len(data_dirs) != 0 or len(data_files) != 2: - self.logger("ERROR", "do_current-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_current", msg, "red", "InitFileError") prj_file, count = None, 0 for data_file in data_files: @@ -37,12 +39,11 @@ class DoBrakeTest(QThread): count += 1 prj_file = data_file else: - self.logger("ERROR", "do_current-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_current", msg, "red", "InitFileError") if count != 2: - self.logger("ERROR", "do_current-check_files", msg, "red", "InitFileError") + self.logger("ERROR", "do_current", msg, "red", "InitFileError") - self.logger("INFO", "do_current-check_files", "数据目录合规性检查结束,未发现问题......", "green") if self.tool == "tool100": os.mkdir(f"{self.dir_path}/single") os.mkdir(f"{self.dir_path}/s_1") @@ -51,14 +52,15 @@ class DoBrakeTest(QThread): elif self.tool == "inertia": os.mkdir(f"{self.dir_path}/inertia") else: - self.logger("ERROR", "do_current-check_files", "负载选择错误,电机电流测试只能选择 tool100/inertia 规格!", "red", "LoadSelectError") + self.logger("ERROR", "do_current", "负载选择错误,电机电流测试只能选择 tool100/inertia 规格!", "red", "LoadSelectError") return prj_file + @clibs.handle_exception def get_configs(): robot_type = None - msg_id, state = clibs.c_hr.execution("controller.get_params") - records = clibs.c_hr.get_from_id(msg_id, state) + msg_id = clibs.c_hr.execution("controller.get_params") + records = clibs.c_hr.get_from_id(msg_id) for record in records: if "请求发送成功" not in record[0]: robot_type = eval(record[0])["data"]["robot_type"] @@ -66,11 +68,13 @@ class DoBrakeTest(QThread): local_file = self.dir_path + f"/{robot_type}.cfg" clibs.c_pd.pull_file_from_server(server_file, local_file) + self.logger("INFO", "do_current", "正在做初始化校验和配置,这可能需要一点时间......", "green") _prj_file = check_files() get_configs() - + self.logger("INFO", "do_current", "数据目录合规性检查结束,未发现问题......", "green") return _prj_file + @clibs.handle_exception def single_axis_proc(self, records, number): text = "single" if number < 6 else "hold" number = number if number < 6 else number - 6 @@ -96,6 +100,7 @@ class DoBrakeTest(QThread): filename = f"{self.dir_path}/single/j{number + 1}_{text}_{time.time()}.data" df.to_csv(filename, sep="\t", index=False) + @clibs.handle_exception def scenario_proc(self, records, number, scenario_time): d_vel, d_trq, d_sensor, d_trans = [[], [], [], [], [], []], [[], [], [], [], [], []], [[], [], [], [], [], []], [[], [], [], [], [], []] for record in records: @@ -121,30 +126,40 @@ class DoBrakeTest(QThread): filename = f"{self.dir_path}/s_{number-11}/j{axis+1}_s_{number-11}_{scenario_time}_{time.time()}.data" df.to_csv(filename, sep="\t", index=False) + @clibs.handle_exception def gen_result_file(self, number, start_time, end_time, scenario_time): - def get_records(s_time, e_time): - clibs.cursor.execute(f"select content from logs where time between '{s_time}' and '{e_time}' and content like '%diagnosis.result%' order by id asc") - _ = clibs.cursor.fetchall() - return _ + @clibs.handle_exception + def get_records(): + s_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)) + e_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time+clibs.INTERVAL)) + + try: + clibs.lock.acquire(True) + clibs.cursor.execute(f"SELECT content FROM logs WHERE timestamp BETWEEN '{s_time}' AND '{e_time}' AND content LIKE '%diagnosis.result%' ORDER BY id ASC") + return clibs.cursor.fetchall() + finally: + clibs.lock.release() if number < 12: - records = get_records(start_time, end_time) + records = get_records() t = threading.Thread(target=self.single_axis_proc, args=(records, number)) t.daemon = True t.start() elif number < 15: - records = get_records(start_time, end_time) + records = get_records() t = threading.Thread(target=self.scenario_proc, args=(records, number, scenario_time)) t.daemon = True t.start() @staticmethod + @clibs.handle_exception def change_curve_state(stat): curves = ["hw_joint_vel_feedback", "device_servo_trq_feedback", "hw_sensor_trq_feedback", "hw_estimate_trans_trq_res"] display_pdo_params = [] if not stat else [{"name": curve, "channel": chl} for curve in curves for chl in range(6)] - clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat, overrun=True, turn_area=True, delay_motion=False) - clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") + clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat) + clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params) + @clibs.handle_exception def run_rl(self, prj_file): prj_name = ".".join(prj_file.split("/")[-1].split(".")[:-1]) c_regular = [ @@ -184,7 +199,10 @@ class DoBrakeTest(QThread): for condition in conditions: number = conditions.index(condition) - self.logger("INFO", "do_current-run_rl", f"正在执行{disc[number]}测试......") + # for testing + # if number < 12: + # continue + self.logger("INFO", "do_current", f"正在执行{disc[number]}测试......") # 1. 将act重置为False,并修改将要执行的场景 clibs.c_md.write_act(False) @@ -217,12 +235,12 @@ class DoBrakeTest(QThread): else: time.sleep(1) if (time.time() - t_start) > 15: - self.logger("ERROR", "do_current-run_rl", "15s 内未收到机器人的运行信号,需要确认RL程序和工具通信是否正常执行...", "red", "ReadySignalTimeoutError") + self.logger("ERROR", "do_current", "15s 内未收到机器人的运行信号,需要确认RL程序和工具通信是否正常执行...", "red", "ReadySignalTimeoutError") # 4. 执行采集 time.sleep(10) # 消除前 10s 的不稳定数据 self.change_curve_state(True) - start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + start_time = time.time() single_time, stall_time, scenario_time = 40, 10, 0 if number < 6: # 单轴 time.sleep(single_time) @@ -233,36 +251,39 @@ class DoBrakeTest(QThread): while True: scenario_time = float(f"{float(clibs.c_md.read_scenario_time()):.2f}") if float(scenario_time) != 0: - self.logger("INFO", "do_current-run_rl", f"场景{number - 11}的周期时间:{scenario_time}") + self.logger("INFO", "do_current", f"场景{number - 11}的周期时间:{scenario_time}") break else: time.sleep(1) if (time.time()-t_start) > 180: - self.logger("ERROR", "do_current-run_rl", f"180s 内未收到场景{number - 11}的周期时间,需要确认RL程序和工具通信交互是否正常执行...", "red", "GetScenarioTimeError") + self.logger("ERROR", "do_current", f"180s 内未收到场景{number - 11}的周期时间,需要确认RL程序和工具通信交互是否正常执行...", "red", "GetScenarioTimeError") time.sleep(20) # 5.停止程序运行,保留数据并处理输出 - end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + end_time = time.time() clibs.c_hr.execution("rl_task.stop", tasks=["current"]) - time.sleep(2) # 确保数据都拿到 self.change_curve_state(False) + time.sleep(2) # 确保数据都入库 self.gen_result_file(number, start_time, end_time, scenario_time) else: if self.tool == "tool100": - self.logger("INFO", "do_current-run_rl", "单轴和场景电机电流采集完毕,如需采集惯量负载,须切换负载类型,并更换惯量负载,重新执行", "green") + self.logger("INFO", "do_current", "单轴和场景电机电流采集完毕,如需采集惯量负载,须切换负载类型,并更换惯量负载,重新执行", "green") elif self.tool == "inertia": - self.logger("INFO", "do_current-run_rl", "惯量负载电机电流采集完毕,如需采集单轴/场景/保持电机电流,须切换负载类型,并更换偏置负载,重新执行", "green") + self.logger("INFO", "do_current", "惯量负载电机电流采集完毕,如需采集单轴/场景/保持电机电流,须切换负载类型,并更换偏置负载,重新执行", "green") + @clibs.handle_exception def processing(self): time_start = time.time() clibs.running[self.idx] = 1 + if clibs.status["hmi"] != 1 or clibs.status["md"] != 1: + self.logger("ERROR", "do_current", "processing: 需要在网络设置中连接HMI以及Modbus通信!", "red", "NetworkError") data_dirs, data_files = clibs.traversal_files(self.dir_path, self.output) prj_file = self.initialization(data_dirs, data_files) clibs.c_pd.push_prj_to_server(prj_file) self.run_rl(prj_file) - self.logger("INFO", "brake-processing", "-" * 60 + "
全部处理完毕
", "purple") + self.logger("INFO", "do_current", "-" * 60 + "
全部处理完毕
", "purple") time_total = time.time() - time_start msg = f"处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s" - self.logger("INFO", "brake-processing", msg) + self.logger("INFO", "do_current", msg) diff --git a/codes/common/__init__.py b/codes/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/codes/common/clibs.py b/codes/common/clibs.py new file mode 100644 index 0000000..6ef9471 --- /dev/null +++ b/codes/common/clibs.py @@ -0,0 +1,103 @@ +import os +import os.path +import threading +import sqlite3 + + +def traversal_files(dir_path, signal): + # 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录 + # 参数:路径/信号/游标/功能编号 + # 返回值:路径下的文件夹列表 路径下的文件列表 + if not os.path.exists(dir_path): + logger("ERROR", "clibs", f"数据文件夹{dir_path}不存在,请确认后重试......", "red", signal=signal) + else: + dirs, files = [], [] + for item in os.scandir(dir_path): + if item.is_dir(): + dirs.append(item.path.replace("\\", "/")) + elif item.is_file(): + files.append(item.path.replace("\\", "/")) + + return dirs, files + + +def init_logdb(): + global conn, cursor + conn = sqlite3.connect(":memory:", isolation_level=None, check_same_thread=False, cached_statements=2048) + cursor = conn.cursor() + cursor.execute("PRAGMA journal_mode=wal") + cursor.execute("PRAGMA wal_checkpoint=TRUNCATE") + cursor.execute("PRAGMA synchronous=normal") + cursor.execute("PRAGMA temp_store=memory") + cursor.execute("PRAGMA mmap_size=30000000000") + cursor.execute("PRAGMA cache_size=200000") + cursor.execute( + """ + create table if not exists logs( + id integer primary key autoincrement, + timestamp DATETIME DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW', 'localtime')), + level text, + module text, + content text + ) + """ + ) + + +def db_lock(func): + def wrapper(*args, **kwargs): + try: + lock.acquire(True) + ret = func(*args, **kwargs) + finally: + lock.release() + return ret + + return wrapper + + +@db_lock +def logger(level, module, content, color="black", flag="both", signal=""): + global cursor + if "move.monitor" in content: + return + + if flag == "signal": + signal.emit(content, color) + elif flag == "cursor": + cursor.execute(f"INSERT INTO logs (level, module, content) VALUES (?, ?, ?)", (level, module, content)) + elif flag == "both": + signal.emit(content, color) + cursor.execute(f"INSERT INTO logs (level, module, content) VALUES (?, ?, ?)", (level, module, content)) + + +def handle_exception(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as err: + print(f"{func.__name__} err = {err}") + logger("DEBUG", "clibs", f"{func.__name__} err = {err}", flag="cursor") + return wrapper + + +log_path = f"assets/logs" +lock = threading.Lock() +running = [0, 0, 0, 0, 0, 0, 0] # 制动数据/转矩数据/激光数据/精度数据/制动自动化/转矩自动化/耐久数据采集 +functions = ["制动数据处理", "转矩数据处理", "激光数据处理", "精度数据处理", "制动自动化测试", "转矩自动化测试", "耐久数据采集"] +levels = ["DEBUG", "INFO", "WARNING", "ERROR"] +ip_addr, ssh_port, socket_port, xService_port, external_port, modbus_port, upgrade_port = "", 22, 5050, 6666, 8080, 502, 4567 +username, password = "luoshi", "luoshi2019" +INTERVAL, RADIAN, MAX_FRAME_SIZE, MAX_LOG_NUMBER = 1, 57.3, 1024, 10 +c_md, c_hr, c_ec, c_pd, conn, cursor, search_records = None, None, None, None, None, None, None +status = {"mysql": 0, "hmi": 0, "md": 0, "ec": 0} +c_joint_vel, c_servo_trq, c_sensor_trq, c_estimate_trans_trq, c_safety_estop = 1, 2, 3, 4, 3 # 各个指标所在列 + +init_logdb() + +# ============== ↓↓↓DEBUG CODE↓↓↓ ============== +for i in range(100): + logger("DEBUG", "clibs", 'running123', flag="cursor") + logger("INFO", "aio", 'running234', flag="cursor") + logger("WARNING", "openapi", 'running345', flag="cursor") + logger("ERROR", "brake", 'running456', flag="cursor") diff --git a/code/common/openapi.py b/codes/common/openapi.py similarity index 81% rename from code/common/openapi.py rename to codes/common/openapi.py index 24c3c16..ff5259a 100644 --- a/code/common/openapi.py +++ b/codes/common/openapi.py @@ -2,7 +2,6 @@ import json import socket from inspect import currentframe import threading -import functools from paramiko import SSHClient, AutoAddPolicy from pymodbus.client.tcp import ModbusTcpClient import selectors @@ -11,8 +10,9 @@ import os.path from ctypes import * import hashlib import struct +from datetime import datetime from PySide6.QtCore import Signal, QThread -from common import clibs +from codes.common import clibs class ModbusRequest(QThread): @@ -24,317 +24,319 @@ class ModbusRequest(QThread): self.port = port def net_conn(self): + self.logger("INFO", "openapi", f"Modbus 正在连接中,需要配置设备,这可能需要一点时间......", "blue") RobotInit.modbus_init() - self.__c = ModbusTcpClient(host=self.ip, port=self.port) - if self.__c.connect(): - self.logger("DEBUG", "openapi", f"Modbus connection({clibs.ip_addr}:{clibs.modbus_port}) success...", "green") + self.c = ModbusTcpClient(host=self.ip, port=self.port) + if self.c.connect(): + self.logger("INFO", "openapi", f"Modbus connection({clibs.ip_addr}:{clibs.modbus_port}) success!", "green") else: - self.logger("ERROR", "openapi", f"Modbus connection({clibs.ip_addr}:{clibs.modbus_port}) failed...", "red", "MdConnFailed") + self.logger("ERROR", "openapi", f"Modbus connection({clibs.ip_addr}:{clibs.modbus_port}) failed!", "red", "MdConnFailed") def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") def close(self): - if self.__c.connect(): + if self.c.connect(): try: - self.__c.close() + self.c.close() + self.logger("INFO", "openapi", f"modbus: 关闭 Modbus 连接成功", "green") except Exception as err: self.logger("ERROR", "openapi", f"modbus: 关闭 Modbus 连接失败:{err}", "red", "MdCloseFailed") def __reg_high_pulse(self, addr: int) -> None: - self.__c.write_register(addr, 0) + self.c.write_register(addr, 0) time.sleep(clibs.INTERVAL) - self.__c.write_register(addr, 1) + self.c.write_register(addr, 1) time.sleep(clibs.INTERVAL+1) - self.__c.write_register(addr, 0) + self.c.write_register(addr, 0) def r_clear_alarm(self): # OK self.__reg_high_pulse(40000) - self.logger("INFO", "openapi", "modbus: 40000-010 执行清除告警信息") + self.logger("DEBUG", "openapi", "modbus: 40000-010 执行清除告警信息") def r_reset_estop(self): # OK self.__reg_high_pulse(40001) - self.logger("INFO", "openapi", "modbus: 40001-010 执行复位急停状态(非软急停)") + self.logger("DEBUG", "openapi", "modbus: 40001-010 执行复位急停状态(非软急停)") def r_reset_estop_clear_alarm(self): # OK self.__reg_high_pulse(40002) - self.logger("INFO", "openapi", "modbus: 40002-010 执行复位急停状态(非软急停),并清除告警信息") + self.logger("DEBUG", "openapi", "modbus: 40002-010 执行复位急停状态(非软急停),并清除告警信息") def r_motor_off(self): # OK self.__reg_high_pulse(40003) - self.logger("INFO", "openapi", "modbus: 40003-010 执行机器人下电") + self.logger("DEBUG", "openapi", "modbus: 40003-010 执行机器人下电") def r_motor_on(self): # OK self.__reg_high_pulse(40004) - self.logger("INFO", "openapi", "modbus: 40004-010 执行机器人上电") + self.logger("DEBUG", "openapi", "modbus: 40004-010 执行机器人上电") def r_motoron_pp2main_start(self): # OK self.__reg_high_pulse(40005) - self.logger("INFO", "openapi", "modbus: 40005-010 执行机器人上电/pp2main/开始运行程序,需自动模式执行,若运行失败,可清除告警后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40005-010 执行机器人上电/pp2main/开始运行程序,需自动模式执行,若运行失败,可清除告警后再次尝试") def r_motoron_start(self): # OK self.__reg_high_pulse(40006) - self.logger("INFO", "openapi", "modbus: 40006-010 执行机器人上电/开始运行程序,需自动模式执行,若运行失败,可清除告警、执行pp2main后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40006-010 执行机器人上电/开始运行程序,需自动模式执行,若运行失败,可清除告警、执行pp2main后再次尝试") def r_pulse_motoroff(self): # OK self.__reg_high_pulse(40007) - self.logger("INFO", "openapi", "modbus: 40007-010 执行机器人停止,并下电,手动模式下可停止程序运行,但不能下电,若运行失败,可清除告警后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40007-010 执行机器人停止,并下电,手动模式下可停止程序运行,但不能下电,若运行失败,可清除告警后再次尝试") def r_pp2main(self): # OK self.__reg_high_pulse(40008) - self.logger("INFO", "openapi", "modbus: 40008-010 执行机器人 pp2main,需自动模式执行,若运行失败,可清除告警后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40008-010 执行机器人 pp2main,需自动模式执行,若运行失败,可清除告警后再次尝试") def r_program_start(self): # OK self.__reg_high_pulse(40009) - self.logger("INFO", "openapi", "modbus: 40009-010 执行机器人默认程序运行,需有 pp2main 前置操作,若运行失败,可清除告警后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40009-010 执行机器人默认程序运行,需有 pp2main 前置操作,若运行失败,可清除告警后再次尝试") def r_program_stop(self): # OK self.__reg_high_pulse(40010) - self.logger("INFO", "openapi", "modbus: 40010-010 执行机器人默认程序停止,需有 pp2main 前置操作,若运行失败,可清除告警后再次尝试") + self.logger("DEBUG", "openapi", "modbus: 40010-010 执行机器人默认程序停止,需有 pp2main 前置操作,若运行失败,可清除告警后再次尝试") def r_reduced_mode(self, action: int): # OK - self.__c.write_register(40011, action) + self.c.write_register(40011, action) actions = "进入" if action == 1 else "退出" - self.logger("INFO", "openapi", f"modbus: 40011-{action} 执行机器人{actions}缩减模式") + self.logger("DEBUG", "openapi", f"modbus: 40011-{action} 执行机器人{actions}缩减模式") time.sleep(clibs.INTERVAL) def r_soft_estop(self, action: int): # OK - self.__c.write_register(40012, action) + self.c.write_register(40012, action) actions = "解除" if action == 1 else "触发" - self.logger("INFO", "openapi", f"modbus: 40012-{action} 执行{actions}急停动作") + self.logger("DEBUG", "openapi", f"modbus: 40012-{action} 执行{actions}急停动作") time.sleep(clibs.INTERVAL) def r_switch_auto_motoron(self): # OK self.__reg_high_pulse(40013) - self.logger("INFO", "openapi", "modbus: 40013-010 执行切换为自动模式,并上电,初始状态 !!不能是!! 手动上电模式") + self.logger("DEBUG", "openapi", "modbus: 40013-010 执行切换为自动模式,并上电,初始状态 !!不能是!! 手动上电模式") def r_switch_auto(self): # OK self.__reg_high_pulse(40014) - self.logger("INFO", "openapi", "modbus: 40014-010 执行切换为自动模式") + self.logger("DEBUG", "openapi", "modbus: 40014-010 执行切换为自动模式") def r_switch_manual(self): # OK self.__reg_high_pulse(40015) - self.logger("INFO", "openapi", "modbus: 40015-010 执行切换为手动模式") + self.logger("DEBUG", "openapi", "modbus: 40015-010 执行切换为手动模式") def r_switch_safe_region01(self, action: bool): # OK | 上升沿打开,下降沿关闭 if action: - self.__c.write_register(40016, False) + self.c.write_register(40016, False) time.sleep(clibs.INTERVAL) - self.__c.write_register(40016, True) + self.c.write_register(40016, True) else: - self.__c.write_register(40016, True) + self.c.write_register(40016, True) time.sleep(clibs.INTERVAL) - self.__c.write_register(40016, False) + self.c.write_register(40016, False) actions = "打开" if action else "关闭" - self.logger("INFO", "openapi", f"modbus: 40016-{action} 执行{actions}安全区 safe region 01") + self.logger("DEBUG", "openapi", f"modbus: 40016-{action} 执行{actions}安全区 safe region 01") time.sleep(clibs.INTERVAL) def r_switch_safe_region02(self, action: bool): # OK | 上升沿打开,下降沿关闭 if action: - self.__c.write_register(40017, False) + self.c.write_register(40017, False) time.sleep(clibs.INTERVAL) - self.__c.write_register(40017, True) + self.c.write_register(40017, True) else: - self.__c.write_register(40017, True) + self.c.write_register(40017, True) time.sleep(clibs.INTERVAL) - self.__c.write_register(40017, False) + self.c.write_register(40017, False) actions = "打开" if action else "关闭" - self.logger("INFO", "openapi", f"modbus: 40017-{action} 执行{actions}安全区 safe region 02") + self.logger("DEBUG", "openapi", f"modbus: 40017-{action} 执行{actions}安全区 safe region 02") time.sleep(clibs.INTERVAL) def r_switch_safe_region03(self, action: bool): # OK | 上升沿打开,下降沿关闭 if action: - self.__c.write_register(40018, False) + self.c.write_register(40018, False) time.sleep(clibs.INTERVAL) - self.__c.write_register(40018, True) + self.c.write_register(40018, True) else: - self.__c.write_register(40018, True) + self.c.write_register(40018, True) time.sleep(clibs.INTERVAL) - self.__c.write_register(40018, False) + self.c.write_register(40018, False) actions = "打开" if action else "关闭" - self.logger("INFO", "openapi", f"modbus: 40018-{action} 执行{actions}安全区 safe region 03") + self.logger("DEBUG", "openapi", f"modbus: 40018-{action} 执行{actions}安全区 safe region 03") time.sleep(clibs.INTERVAL) def write_act(self, number): - self.__c.write_register(40100, number) - self.logger("INFO", "openapi", f"modbus: 40100 将 {number} 写入") + self.c.write_register(40100, number) + self.logger("DEBUG", "openapi", f"modbus: 40100 将 {number} 写入") def write_probe(self, probe): - self.__c.write_register(40101, probe) - self.logger("INFO", "openapi", f"modbus: 40101 将 {probe} 写入") + self.c.write_register(40101, probe) + self.logger("DEBUG", "openapi", f"modbus: 40101 将 {probe} 写入") def write_pon(self, pon): - self.__c.write_register(40102, pon) - self.logger("INFO", "openapi", f"modbus: 40102 将 {pon} 写入") + self.c.write_register(40102, pon) + self.logger("DEBUG", "openapi", f"modbus: 40102 将 {pon} 写入") def write_axis(self, axis): - result = self.__c.convert_to_registers(int(axis), self.__c.DATATYPE.INT32, "little") - self.__c.write_registers(40103, result) - self.logger("INFO", "openapi", f"modbus: 40103 将 {axis} 写入") + result = self.c.convert_to_registers(int(axis), self.c.DATATYPE.INT32, "little") + self.c.write_registers(40103, result) + self.logger("DEBUG", "openapi", f"modbus: 40103 将 {axis} 写入") def write_speed_max(self, speed): - result = self.__c.convert_to_registers(float(speed), self.__c.DATATYPE.FLOAT32, "little") - self.__c.write_registers(40105, result) - self.logger("INFO", "openapi", f"modbus: 40105 将 {speed} 写入") + result = self.c.convert_to_registers(float(speed), self.c.DATATYPE.FLOAT32, "little") + self.c.write_registers(40105, result) + self.logger("DEBUG", "openapi", f"modbus: 40105 将 {speed} 写入") def r_write_signals(self, addr: int, value): # OK | 40100 - 40109: signal_0 ~ signal_9 if -1 < addr < 10 and addr.is_integer(): - self.__c.write_register(40100+addr, value) - self.logger("INFO", "openapi", f"modbus: {40100+addr}-{value} 将寄存器 signal_{addr} 赋值为 {value}") + self.c.write_register(40100+addr, value) + self.logger("DEBUG", "openapi", f"modbus: {40100+addr}-{value} 将寄存器 signal_{addr} 赋值为 {value}") else: - self.logger("INFO", "openapi", f"modbus: {40100+addr}-{value} 地址错误,无法赋值!") + self.logger("INFO", "openapi", f"modbus: {40100+addr}-{value} 地址错误,无法赋值!", "red") @property def w_alarm_state(self): # OK - res = self.__c.read_holding_registers(40500, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40500 获取告警状态,结果为 {res} :--: 0 表示无告警,,1 表示有告警") + res = self.c.read_holding_registers(40500, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40500 获取告警状态,结果为 {res} :--: 0 表示无告警,,1 表示有告警") return res @property def w_collision_alarm_state(self): # OK - res = self.__c.read_holding_registers(40501, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40501 获取碰撞告警状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") + res = self.c.read_holding_registers(40501, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40501 获取碰撞告警状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") return res @property def w_collision_open_state(self): # OK - res = self.__c.read_holding_registers(40502, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40502 获取碰撞检测开启状态,结果为 {res} :--: 0 表示关闭,1 表示开启") + res = self.c.read_holding_registers(40502, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40502 获取碰撞检测开启状态,结果为 {res} :--: 0 表示关闭,1 表示开启") return res @property def w_controller_is_running(self): # OK - res = self.__c.read_holding_registers(40503, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40503 获取控制器运行状态,结果为 {res} :--: 0 表示运行异常,1 表示运行正常") + res = self.c.read_holding_registers(40503, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40503 获取控制器运行状态,结果为 {res} :--: 0 表示运行异常,1 表示运行正常") return res @property def w_encoder_low_battery(self): # OK - res = self.__c.read_holding_registers(40504, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40504 获取编码器低电压状态,结果为 {res} :--: 0 表示非低电压,1 表示低电压 需关注") + res = self.c.read_holding_registers(40504, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40504 获取编码器低电压状态,结果为 {res} :--: 0 表示非低电压,1 表示低电压 需关注") return res @property def w_estop_state(self): # OK - res = self.__c.read_holding_registers(40505, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40505 获取机器人急停状态(非软急停),结果为 {res} :--: 0 表示未触发,1 表示已触发") + res = self.c.read_holding_registers(40505, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40505 获取机器人急停状态(非软急停),结果为 {res} :--: 0 表示未触发,1 表示已触发") return res @property def w_motor_state(self): # OK - res = self.__c.read_holding_registers(40506, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40506 获取机器人上电状态,结果为 {res} :--: 0 表示未上电,1 表示已上电") + res = self.c.read_holding_registers(40506, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40506 获取机器人上电状态,结果为 {res} :--: 0 表示未上电,1 表示已上电") return res @property def w_operation_mode(self): # OK - res = self.__c.read_holding_registers(40507, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40507 获取机器人操作模式,结果为 {res} :--: 0 表示手动模式,1 表示自动模式") + res = self.c.read_holding_registers(40507, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40507 获取机器人操作模式,结果为 {res} :--: 0 表示手动模式,1 表示自动模式") return res @property def w_program_state(self): # OK - res = self.__c.read_holding_registers(40508, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40508 获取程序的运行状态,结果为 {res} :--: 0 表示未运行,1 表示正在运行") + res = self.c.read_holding_registers(40508, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40508 获取程序的运行状态,结果为 {res} :--: 0 表示未运行,1 表示正在运行") return res @property def w_program_not_run(self): # OK - res = self.__c.read_holding_registers(40509, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40509 判定程序为未运行状态,结果为 {res} :--: 0 表示正在运行,1 表示未运行") + res = self.c.read_holding_registers(40509, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40509 判定程序为未运行状态,结果为 {res} :--: 0 表示正在运行,1 表示未运行") return res @property def w_program_reset(self): # OK - res = self.__c.read_holding_registers(40510, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40510 判定程序指针为 pp2main 状态,结果为 {res} :--: 0 表示指针不在 main 函数,1 表示指针在 main 函数") + res = self.c.read_holding_registers(40510, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40510 判定程序指针为 pp2main 状态,结果为 {res} :--: 0 表示指针不在 main 函数,1 表示指针在 main 函数") return res @property def w_reduce_mode_state(self): # OK - res = self.__c.read_holding_registers(40511, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40511 获取机器人缩减模式状态,结果为 {res} :--: 0 表示非缩减模式,1 表示缩减模式") + res = self.c.read_holding_registers(40511, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40511 获取机器人缩减模式状态,结果为 {res} :--: 0 表示非缩减模式,1 表示缩减模式") return res @property def w_robot_is_busy(self): # OK - res = self.__c.read_holding_registers(40512, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40512 获取机器人是否处于 busy 状态,结果为 {res} :--: 0 表示未处于 busy 状态,1 表示处于 busy 状态") + res = self.c.read_holding_registers(40512, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40512 获取机器人是否处于 busy 状态,结果为 {res} :--: 0 表示未处于 busy 状态,1 表示处于 busy 状态") return res @property def w_robot_is_moving(self): # OK - res = self.__c.read_holding_registers(40513, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40513 获取机器人是否处于运动状态,结果为 {res} :--: 0 表示未运动,1 表示正在运动") + res = self.c.read_holding_registers(40513, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40513 获取机器人是否处于运动状态,结果为 {res} :--: 0 表示未运动,1 表示正在运动") return res @property def w_safe_door_state(self): # OK - res = self.__c.read_holding_registers(40514, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40514 获取机器人是否处于安全门打开状态,需自动模式下执行,结果为 {res} :--: 0 表示未触发安全门,1 表示已触发安全门") + res = self.c.read_holding_registers(40514, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40514 获取机器人是否处于安全门打开状态,需自动模式下执行,结果为 {res} :--: 0 表示未触发安全门,1 表示已触发安全门") return res @property def w_safe_region01_trig_state(self): # OK - res = self.__c.read_holding_registers(40515, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40515 获取安全区域 safe region01 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") + res = self.c.read_holding_registers(40515, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40515 获取安全区域 safe region01 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") return res @property def w_safe_region02_trig_state(self): # OK - res = self.__c.read_holding_registers(40516, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40516 获取安全区域 safe region02 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") + res = self.c.read_holding_registers(40516, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40516 获取安全区域 safe region02 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") return res @property def w_safe_region03_trig_state(self): # OK - res = self.__c.read_holding_registers(40517, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40517 获取安全区域 safe region03 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") + res = self.c.read_holding_registers(40517, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40517 获取安全区域 safe region03 的触发状态,结果为 {res} :--: 0 表示未触发,1 表示已触发") return res @property def w_soft_estop_state(self): # OK - res = self.__c.read_holding_registers(40518, count=1).registers[0] - self.logger("INFO", "openapi", f"modbus: 40518 获取机器人软急停状态,结果为 {res} :--: 0 表示未触发软急停,1 表示已触发软急停") + res = self.c.read_holding_registers(40518, count=1).registers[0] + self.logger("DEBUG", "openapi", f"modbus: 40518 获取机器人软急停状态,结果为 {res} :--: 0 表示未触发软急停,1 表示已触发软急停") return res def io_write_coils(self, addr, action): # OK | 名字叫写线圈,其实是写 modbus 的 discrete inputs(DI) # e.g. io_write_coils(0, 1) # e.g. io_write_coils(1, 1) # e.g. io_write_coils(0, [1, 1, 1]) - self.__c.write_coils(addr, action) - self.logger("INFO", "openapi", f"modbus: 执行给 DI 地址 {addr} 赋值为 {action},可根据情况传递列表,实现一次性赋值多个") + self.c.write_coils(addr, action) + self.logger("DEBUG", "openapi", f"modbus: 执行给 DI 地址 {addr} 赋值为 {action},可根据情况传递列表,实现一次性赋值多个") time.sleep(clibs.INTERVAL) def io_read_coils(self): # OK | 读 modbus 的 16 个 discrete inputs(DI) - res = self.__c.read_coils(0, count=16).bits - self.logger("INFO", "openapi", f"modbus: 执行读取所有 DI 的结果为 {res}") + res = self.c.read_coils(0, count=16).bits + self.logger("DEBUG", "openapi", f"modbus: 执行读取所有 DI 的结果为 {res}") return res def io_read_discretes(self): # OK | 读 modbus 的 coil outputs(DO) - res = self.__c.read_discrete_inputs(0, count=16).bits - self.logger("INFO", "openapi", f"modbus: 执行读取所有 DO 的结果为 {res}") + res = self.c.read_discrete_inputs(0, count=16).bits + self.logger("DEBUG", "openapi", f"modbus: 执行读取所有 DO 的结果为 {res}") return res def read_ready_to_go(self): - result = self.__c.read_holding_registers(40600, count=1) + result = self.c.read_holding_registers(40600, count=1) return result.registers[0] def read_scenario_time(self): - results = self.__c.read_holding_registers(40601, count=2) - result = self.__c.convert_from_registers(registers=results.registers, data_type=self.__c.DATATYPE.FLOAT32, word_order="little") + results = self.c.read_holding_registers(40601, count=2) + result = self.c.convert_from_registers(registers=results.registers, data_type=self.c.DATATYPE.FLOAT32, word_order="little") return result def read_capture_start(self): - result = self.__c.read_holding_registers(40603, count=1) + result = self.c.read_holding_registers(40603, count=1) return result.registers[0] class HmiRequest(QThread): output = Signal(str, str) - socket.setdefaulttimeout(clibs.INTERVAL * 3) def __init__(self, ip, port, port_xs, /): super().__init__() @@ -369,6 +371,7 @@ class HmiRequest(QThread): self.__t_unpackage_xs.start() def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") @@ -381,11 +384,11 @@ class HmiRequest(QThread): if self.__is_connected: try: self.__is_connected = False - time.sleep(clibs.INTERVAL*2) + time.sleep(clibs.INTERVAL/2) self.c.close() self.c_xs.close() - time.sleep(clibs.INTERVAL*2) clibs.status["hmi"] = 0 + self.logger("INFO", "openapi", f"hmi: 关闭 Socket 连接成功", "green") except Exception as err: self.logger("ERROR", "openapi", f"hmi: 关闭 Socket 连接失败 {err}", "red", "HmiCloseFailed") @@ -393,21 +396,20 @@ class HmiRequest(QThread): self.close() try: self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.c.settimeout(clibs.INTERVAL*5) self.c.connect((self.__ip, self.__port)) self.c_xs = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.c_xs.settimeout(clibs.INTERVAL*5) self.c_xs.connect((self.__ip, self.__port_xs)) # 尝试连续三次发送心跳包,确认建联成功 state = None for i in range(3): - _, state = self.execution.__wrapped__(self, "controller.heart") - time.sleep(clibs.INTERVAL/5) - if state is not None: - self.__is_connected = True - self.logger("INFO", "openapi", "hmi: HMI connection success...", "green") - else: - self.logger("ERROR", "openapi", "hmi: HMI connection failed...", "red", "HmiConnFailed") + _ = self.execution("controller.heart") + time.sleep(clibs.INTERVAL/2) + self.__is_connected = True + self.logger("INFO", "openapi", "hmi: HMI connection success...", "green") except Exception as err: - self.logger("ERROR", "openapi", f"hmi: HMI connection timeout...
err = {err}", "red", "HmiConnTimeout") + self.logger("ERROR", "openapi", f"hmi: HMI connection timeout...", "red", "HmiConnTimeout") def __heartbeat(self): while self.__is_connected: @@ -443,7 +445,7 @@ class HmiRequest(QThread): callback = key.data callback(key.fileobj, mask) except Exception as Err: - self.logger("INFO", "openapi", f"hmi: 老协议解包报错 {Err}") + self.logger("DEBUG", "openapi", f"hmi: 老协议解包报错 {Err}", "red", "UnpackageFailed") def __get_headers(self, index, data): if index + 8 < len(data): @@ -484,7 +486,7 @@ class HmiRequest(QThread): if len(data[self.__index:]) >= pkg_value: self.__response = data[self.__index:self.__index + pkg_value] self.__index += pkg_value - self.logger("INFO", "openapi", str(json.loads(self.__response.decode()))) + self.logger("DEBUG", "openapi", str(json.loads(self.__response.decode()))) self.__response = b"" self.__leftovers = 0 self.__is_first_frame = True @@ -532,7 +534,7 @@ class HmiRequest(QThread): if self.__valid_data_length == 0: # with open(f"{clibs.log_path}/response.txt", mode="a", encoding="utf-8") as f_res: # f_res.write(f"{json.loads(self.__response.decode())}" + "\n") - self.logger("INFO", "openapi", str(json.loads(self.__response.decode()))) + self.logger("DEBUG", "openapi", str(json.loads(self.__response.decode()))) self.__response = b"" self.__is_first_frame = True continue # 此时应该重新 get_headers @@ -617,7 +619,7 @@ class HmiRequest(QThread): self.__half_frm_flag = -1 if self.__valid_data_length == 0: - self.logger("INFO", "openapi", str(json.loads(self.__response.decode()))) + self.logger("DEBUG", "openapi", str(json.loads(self.__response.decode()))) self.__response = b"" self.__is_first_frame = True continue @@ -645,7 +647,7 @@ class HmiRequest(QThread): self.__index += frm_value self.__valid_data_length -= frm_value if self.__valid_data_length == 0: - self.logger("INFO", "openapi", str(json.loads(self.__response.decode()))) + self.logger("DEBUG", "openapi", str(json.loads(self.__response.decode()))) self.__response = b"" self.__is_first_frame = True continue @@ -705,7 +707,7 @@ class HmiRequest(QThread): callback = key.data callback(key.fileobj, mask) except Exception as err: - self.logger("INFO", "openapi", f"hmi: xService解包报错 {err}", "red") + self.logger("DEBUG", "openapi", f"hmi: xService解包报错 {err}", "red", "XsUnpackageFailed") def get_response_xs(self, data): char, response = "", self.__response_xs @@ -713,170 +715,70 @@ class HmiRequest(QThread): if char != "\r": response = "".join([response, char]) else: - self.logger("INFO", "openapi", response) + self.logger("DEBUG", "openapi", response) self.response_xs = response response = "" else: self.__response_xs = response - def get_from_id(self, msg_id, state): - if state == "done_xs": - return - f_text = f"%{msg_id}%" - for _ in range(3): - time.sleep(clibs.INTERVAL * 4) + @clibs.handle_exception + def get_from_id(self, msg_id): + f_text, flag, records, time_delay, ts = f"%{msg_id}%", False, "Null", clibs.INTERVAL*10, msg_id.split("@")[-1] + t = ts.replace("T", " ") + t_ts = datetime.timestamp(datetime.strptime(t, "%Y-%m-%d %H:%M:%S.%f")) + t_send = datetime.strftime(datetime.fromtimestamp(t_ts-time_delay), '%Y-%m-%d %H:%M:%S.%f') + t_receive = datetime.strftime(datetime.fromtimestamp(t_ts+time_delay), '%Y-%m-%d %H:%M:%S.%f') + for idx in range(time_delay): + time.sleep(clibs.INTERVAL) try: clibs.lock.acquire(True) - clibs.cursor.execute(f"select content from {clibs.tb_name} where content like '{f_text}'") + clibs.cursor.execute(f"SELECT content FROM logs WHERE timestamp BETWEEN '{t_send}' AND '{t_receive}' AND content LIKE '{f_text}'") records = clibs.cursor.fetchall() + flag = True if len(records) == 2 else False finally: clibs.lock.release() - if len(records) == 2: - return records + if flag is True: + return records else: - self.close() - self.logger("ERROR", "openapi", f"hmi: 无法找到请求 {msg_id} 的响应", "red", "ResponseNotFound") + self.logger("ERROR", "openapi", f"hmi: {time_delay}s内无法找到请求 {msg_id} 的响应!", "red", "ResponseNotFound") - @staticmethod - def validate_req(func): - @functools.wraps(func) - def wrapper(self, *args, **kwargs): - msg_id, state = func(self, *args, **kwargs) - t = threading.Thread(target=self.get_from_id, args=(msg_id, state)) - t.daemon = True - t.start() - return msg_id, state - return wrapper - - @validate_req - def execution(self, command, p_flag=0, **kwargs): - req, state = None, None + def execution(self, command, **kwargs): + req = None try: - with open(f"{clibs.PREFIX}/files/protocols/hmi/{command}.json", encoding="utf-8", mode="r") as f_json: + with open(f"assets/files/protocols/hmi/{command}.json", encoding="utf-8", mode="r") as f_json: req = json.load(f_json) + t = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") + req["id"] = f"{command}@{t}" + flag = req["p_type"] + del req["p_type"] except Exception as err: self.logger("ERROR", "openapi", f"hmi: 暂不支持 {command} 功能,或确认该功能存在... {err}", "red", "CommandNotSupport") - if p_flag == 0: # for old protocols - match command: - case "state.set_tp_mode": - req["data"]["tp_mode"] = kwargs["tp_mode"] - case "overview.set_autoload": - req["data"]["autoload_prj_path"] = kwargs["autoload_prj_path"] - case "overview.reload": - req["data"]["prj_path"] = kwargs["prj_path"] - req["data"]["tasks"] = kwargs["tasks"] - case "rl_task.pp_to_main" | "rl_task.run" | "rl_task.stop": - req["data"]["tasks"] = kwargs["tasks"] - case "rl_task.set_run_params": - req["data"]["loop_mode"] = kwargs["loop_mode"] - req["data"]["override"] = kwargs["override"] - case "diagnosis.set_params": - req["data"]["display_pdo_params"] = kwargs["display_pdo_params"] - req["data"]["frequency"] = kwargs["frequency"] - req["data"]["version"] = kwargs["version"] - case "diagnosis.open": - req["data"]["open"] = kwargs["open"] - req["data"]["display_open"] = kwargs["display_open"] - req["data"]["overrun"] = kwargs["overrun"] - req["data"]["turn_area"] = kwargs["turn_area"] - req["data"]["delay_motion"] = kwargs["delay_motion"] - case "register.set_value": - req["data"]["name"] = kwargs["name"] - req["data"]["type"] = kwargs["type"] - req["data"]["bias"] = kwargs["bias"] - req["data"]["value"] = kwargs["value"] - case "diagnosis.save": - req["data"]["save"] = kwargs["save"] - case "socket.set_params": - req["data"] = kwargs["data"] - case "fieldbus_device.set_params": - req["data"]["device_name"] = kwargs["device_name"] - req["data"]["enable"] = kwargs["enable"] - case "soft_limit.set_params": - req["data"] = kwargs["data"] - case "move.set_quickturn_pos": - req["data"]["enable_home"] = kwargs["enable_home"] - req["data"]["enable_drag"] = kwargs["enable_drag"] - req["data"]["enable_transport"] = kwargs["enable_transport"] - req["data"]["end_posture"] = kwargs["end_posture"] - case "move.quick_turn": - req["data"]["name"] = kwargs["name"] - case "move.stop": - req["data"]["stoptype"] = kwargs["stoptype"] - case "jog.start": - req["data"]["index"] = kwargs["index"] - req["data"]["direction"] = kwargs["direction"] - req["data"]["is_ext"] = kwargs["is_ext"] - case "jog.set_params": - req["data"]["step"] = kwargs["step"] - req["data"]["override"] = kwargs["override"] - req["data"]["space"] = kwargs["space"] - case "diagnosis.get_params": - req["data"]["version"] = kwargs["version"] - case "system_io.update_configuration": - req["data"]["input_system_io"] = kwargs["input_system_io"] - req["data"]["output_system_io"] = kwargs["output_system_io"] - case "modbus.set_params": - req["data"]["enable_slave"] = kwargs["enable_slave"] - req["data"]["ip"] = kwargs["ip"] - req["data"]["port"] = kwargs["port"] - req["data"]["slave_id"] = kwargs["slave_id"] - req["data"]["enable_master"] = kwargs["enable_master"] - case "modbus.get_values": - req["data"]["mode"] = kwargs["mode"] - case "move.set_monitor_cfg": - req["data"]["ref_coordinate"] = kwargs["ref_coordinate"] - case "move.set_params": - req["data"]["MOTION"] = kwargs["data"] - case "move.set_quickstop_distance": - req["data"]["distance"] = kwargs["distance"] - case "collision.set_params": - req["data"] = kwargs["data"] - case "collision.set_state": - req["data"]["collision_state"] = kwargs["collision_state"] - case "controller.set_params": - req["data"]["time"] = kwargs["time"] - case "drag.set_params": - req["data"]["enable"] = kwargs["enable"] - req["data"]["space"] = kwargs["space"] - req["data"]["type"] = kwargs["type"] - case _: - pass + match command: + case "state.set_tp_mode" | "overview.set_autoload" | "overview.reload" | "rl_task.pp_to_main" | "rl_task.run" | "rl_task.stop" | "rl_task.set_run_params" | "diagnosis.set_params" | "diagnosis.open" | "drag.set_params" | "controller.set_params" | "collision.set_state" | "collision.set_params" | "move.set_quickstop_distance" | "move.set_params" | "move.set_monitor_cfg" | "modbus.get_values" | "modbus.set_params" | "system_io.update_configuration" | "diagnosis.get_params" | "jog.set_params" | "jog.start" | "move.stop" | "move.quick_turn" | "move.set_quickturn_pos" | "soft_limit.set_params" | "fieldbus_device.set_params" | "socket.set_params" | "diagnosis.save" | "register.set_value": + req["data"].update(kwargs) + case "safety.safety_area.signal_enable" | "safety.safety_area.overall_enable" | "safety.safety_area.safety_area_enable" | "safety.safety_area.set_param": + req["c"].update(kwargs) + case "log_code.data.code_list": + req["s"]["log_code.data"]["code_list"] = kwargs["code_list"] + case _: + pass - req["id"] = f"{command}-{time.time()}" + if flag == 0: cmd = json.dumps(req, separators=(",", ":")) - print(f"correct cmd = {cmd}") try: self.c.send(self.package(cmd)) - self.logger("INFO", "openapi", f"hmi: 老协议请求发送成功 {cmd}") - state = "done" + self.logger("DEBUG", "openapi", f"hmi: 老协议请求发送成功 {cmd}") except Exception as err: self.logger("ERROR", "openapi", f"hmi: 老协议请求发送失败 {cmd},报错信息 {err}", "red", "CommandSendFailed") - return req["id"], state - elif p_flag == 1: # for xService - match command: - case "safety.safety_area.signal_enable": - req["c"]["safety.safety_area.signal_enable"]["signal"] = kwargs["signal"] - case "safety.safety_area.overall_enable": - req["c"]["safety.safety_area.overall_enable"]["enable"] = kwargs["enable"] - case "log_code.data.code_list": - req["s"]["log_code.data"]["code_list"] = kwargs["code_list"] - case "safety.safety_area.safety_area_enable": - req["c"]["safety.safety_area.safety_area_enable"]["id"] = kwargs["id"] - req["c"]["safety.safety_area.safety_area_enable"]["enable"] = kwargs["enable"] - case "safety.safety_area.set_param": - req["c"]["safety.safety_area.set_param"] = kwargs["data"] - case _: - pass - + elif flag == 1: try: self.c_xs.send(self.package_xs(req)) - self.logger("INFO", "openapi", f"hmi: xService请求发送成功 {req}") - state = "done" + self.logger("DEBUG", "openapi", f"hmi: xService请求发送成功 {req}") except Exception as Err: self.logger("ERROR", "openapi", f"hr: xService请求发送失败 {req} 报错信息 {Err}", "red", "CommandSendFailed") - return command, state + + return req["id"] # =================================== ↓↓↓ specific functions ↓↓↓ =================================== @@ -1859,7 +1761,7 @@ class HmiRequest(QThread): def __get_data(self, upper_func, command, flag=0, **kwargs): msg_id, state = self.execution(command, protocol_flag=flag, **kwargs) - records = clibs.c_hr.get_from_id(msg_id, state) + records = clibs.c_hr.get_from_id(msg_id) for record in records: if "请求发送成功" not in record[0]: data = eval(record[0])["data"] @@ -1872,49 +1774,52 @@ class ExternalCommunication(QThread): def __init__(self, ip, port, /): super().__init__() - self.__c = None + self.c = None self.ip = ip self.port = int(port) self.suffix = "\r" self.exec_desc = " :--: 返回 true 表示执行成功,false 失败" def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" clibs.logger(level, module, content, color, flag, signal=self.output) if level.upper() == "ERROR": raise Exception(f"{error} | {content}") def net_conn(self): - clibs.c_hr.set_socket_params(False, "0.0.0.0", self.port, "\r") - clibs.c_hr.set_socket_params(True, "0.0.0.0", self.port, "\r") - self.__c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # clibs.c_hr.execution("socket.set_params", enable=False, ip="0.0.0.0", port=str(self.port), suffix="\r", type=1) + # time.sleep(clibs.INTERVAL) + clibs.c_hr.execution("socket.set_params", enable=True, ip="0.0.0.0", port=str(self.port), suffix="\r", type=1) + time.sleep(clibs.INTERVAL*2) + self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.c.settimeout(clibs.INTERVAL*5) try: - self.__c.connect((self.ip, self.port)) + self.c.connect((self.ip, self.port)) self.logger("INFO", "openapi", f"ec: 外部通信连接成功...", "green") - return self.__c + return self.c except Exception as err: self.logger("ERROR", "openapi", f"ec: 外部通信连接失败... {err}", "red", "EcConnFailed") def close(self): if clibs.status["ec"]: try: - self.__c.close() + self.c.close() + self.logger("INFO", "openapi", f"ec: 关闭 EC 连接成功", "green") except Exception as err: self.logger("ERROR", "openapi", f"ec: 关闭 EC 连接失败:{err}", "red", "EcCloseFailed") def s_string(self, directive): order = "".join([directive, self.suffix]) - self.__c.send(order.encode()) - time.sleep(clibs.INTERVAL) + self.c.send(order.encode()) def r_string(self, directive): result, char = "", "" while char != self.suffix: try: - char = self.__c.recv(1).decode(encoding="unicode_escape") + char = self.c.recv(1).decode(encoding="unicode_escape") except Exception as err: self.logger("ERROR", "openapi", f"ec: 获取请求指令 {directive} 的返回数据超时,需确认指令发送格式以及内容正确!具体报错信息如下 {err}", "red", "RecvMsgFailed") result = "".join([result, char]) - time.sleep(clibs.INTERVAL) return result # =================================== ↓↓↓ specific functions ↓↓↓ =================================== @@ -2150,6 +2055,7 @@ class ExternalCommunication(QThread): def __exec_cmd(self, directive, description, more_desc=""): self.s_string(directive) + time.sleep(clibs.INTERVAL) result = self.r_string(directive).strip() self.logger("INFO", "openapi", f"ec: 执行{description}指令是 {directive},返回值为 {result}{more_desc}") return result @@ -2174,6 +2080,7 @@ class PreDos(object): print(f"predos: SSH 无法连接到 {self.ip}:{self.ssh_port},需检查网络连通性或者登录信息是否正确 {err}") raise Exception("SshConnFailed") + @clibs.handle_exception def push_prj_to_server(self, prj_file): # prj_file:本地工程完整路径 self.__ssh2server() @@ -2242,35 +2149,38 @@ class PreDos(object): class RobotInit(object): @staticmethod + @clibs.handle_exception def modbus_init(): - pd = PreDos(clibs.ip_addr, clibs.ssh_port, clibs.username, clibs.password) + clibs.c_pd = PreDos(clibs.ip_addr, clibs.ssh_port, clibs.username, clibs.password) # 推送配置文件 - print("init: 推送配置文件 fieldbus_device.json/registers.json/registers.xml 到控制器,并配置 IO 设备,设备号为 7...") - robot_params = clibs.c_hr.get_robot_params - robot_type = robot_params["robot_type"] - security_type = robot_params["security_type"] - controller_type = robot_params["controller_type"] + msg_id = clibs.c_hr.execution("controller.get_params") + records = clibs.c_hr.get_from_id(msg_id) + for record in records: + if "请求发送成功" not in record[0]: + robot_params = eval(record[0]) + robot_type = robot_params["data"]["robot_type"] + security_type = robot_params["data"]["security_type"] + controller_type = robot_params["data"]["controller_type"] io_device_file = "_".join(["io_device", controller_type, security_type, "1"]) user_settings = "/home/luoshi/bin/controller/user_settings" interactive_data = f"/home/luoshi/bin/controller/interactive_data/{robot_type}" config_files = [ - f"{clibs.PREFIX}/files/projects/fieldbus_device.json", - f"{clibs.PREFIX}/files/projects/registers.json", - f"{clibs.PREFIX}/files/projects/registers.xml" + f"assets/files/projects/fieldbus_device.json", + f"assets/files/projects/registers.json", + f"assets/files/projects/registers.xml" ] for config_file in config_files: filename = config_file.split("/")[-1] - pd.push_file_to_server(config_file, f"{user_settings}/{filename}") - pd.push_file_to_server(config_file, f"{interactive_data}/{filename}") + clibs.c_pd.push_file_to_server(config_file, f"{user_settings}/{filename}") + clibs.c_pd.push_file_to_server(config_file, f"{interactive_data}/{filename}") io_device_autotest = {"ai_num": 0, "ao_num": 0, "di_num": 16, "do_num": 16, "extend_attr": {"mode": "slaver", "name": "autotest", "type": "MODBUS"}, "id": 7, "name": "autotest", "type": 6} - io_device_file_local = f"{io_device_file}" io_device_file_local_tmp = f"{io_device_file}_tmp" io_device_file_remote = f"{user_settings}/{io_device_file}" - pd.pull_file_from_server(io_device_file_remote, io_device_file_local) + clibs.c_pd.pull_file_from_server(io_device_file_remote, io_device_file_local) with open(io_device_file_local, mode="r", encoding="utf-8") as f: data = json.load(f) for _ in data["device_list"]: @@ -2280,15 +2190,17 @@ class RobotInit(object): data["device_list"].append(io_device_autotest) with open(io_device_file_local_tmp, mode="w", encoding="utf-8") as f_tmp: json.dump(data, f_tmp, indent=4) - pd.push_file_to_server(io_device_file_local_tmp, f"{user_settings}/{io_device_file}") - pd.push_file_to_server(io_device_file_local_tmp, f"{interactive_data}/{io_device_file}") - - # os.remove(io_device_file_local) - # os.remove(io_device_file_local_tmp) - clibs.c_hr.reload_io() - clibs.c_hr.reload_registers() - clibs.c_hr.reload_fieldbus() - clibs.c_hr.set_fieldbus_device_params("autotest", True) + clibs.c_pd.push_file_to_server(io_device_file_local_tmp, f"{user_settings}/{io_device_file}") + clibs.c_pd.push_file_to_server(io_device_file_local_tmp, f"{interactive_data}/{io_device_file}") + try: + os.remove(io_device_file_local) + os.remove(io_device_file_local_tmp) + except: + ... + clibs.c_hr.execution("io_device.load_cfg") + clibs.c_hr.execution("modbus.load_cfg") + clibs.c_hr.execution("fieldbus_device.load_cfg") + clibs.c_hr.execution("fieldbus_device.set_params", device_name="autotest", enable=True) def robot_init(self): pd = PreDos(clibs.ip_addr, clibs.ssh_port, clibs.username, clibs.password) @@ -2495,17 +2407,17 @@ class RobotInit(object): class UpgradeJsonCmd(object): def __init__(self): - self.__c = None + self.c = None self.__sock_conn() def __sock_conn(self): # socket connect print(f"正在连接 {clibs.ip_addr}:{clibs.upgrade_port}...") try: - self.__c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.__c.connect((clibs.ip_addr, clibs.upgrade_port)) - self.__c.setblocking(True) - self.__c.settimeout(3) + self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.c.connect((clibs.ip_addr, clibs.upgrade_port)) + self.c.setblocking(True) + self.c.settimeout(3) except Exception as Err: print(f"upgrade: {Err} | 连接 {clibs.ip_addr}:{clibs.upgrade_port} 失败...") raise Exception("UpgradePortConnFailed") @@ -2520,7 +2432,7 @@ class UpgradeJsonCmd(object): def __recv_result(self, cmd): time.sleep(clibs.INTERVAL) try: - res = self.__c.recv(10240).decode() + res = self.c.recv(10240).decode() except Exception as err: print(f"upgrade: 请求命令 {cmd.decode()} 的返回错误 {err}") raise Exception("ResponseError") @@ -2530,11 +2442,11 @@ class UpgradeJsonCmd(object): def __exec(self, command: dict): try: - self.__c.recv(10240) + self.c.recv(10240) except Exception: pass cmd = json.dumps(command, separators=(",", ":")).encode("utf-8") - self.__c.sendall(self.__do_package(cmd)) + self.c.sendall(self.__do_package(cmd)) res = self.__recv_result(cmd) print(f"upgrade: 请求命令 {cmd.decode()} 的返回信息:{res}") diff --git a/codes/durable/__init__.py b/codes/durable/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/codes/durable/factory_test.py b/codes/durable/factory_test.py new file mode 100644 index 0000000..8d7f461 --- /dev/null +++ b/codes/durable/factory_test.py @@ -0,0 +1,293 @@ +import json +import threading +import time +import pandas +import numpy +import math +import csv +from PySide6.QtCore import Signal, QThread +from codes.common import clibs + + +class DoFactoryTest(QThread): + output = Signal(str, str) + curve_map = { + "周期内平均转矩": ["device_servo_trq_feedback", ], + "周期内最大速度": ["hw_joint_vel_feedback", ], + } + + def __init__(self, dir_path, interval, procs, /): + super().__init__() + self.dir_path = dir_path + self.interval = int(interval) if interval != "" else 300 + self.procs = procs + self.idx = 6 + self.curves = [] + + def logger(self, level, module, content, color="black", error="", flag="both"): + flag = "cursor" if level.upper() == "DEBUG" else "both" + clibs.logger(level, module, content, color, flag, signal=self.output) + if level.upper() == "ERROR": + raise Exception(f"{error} | {content}") + + def initialization(self, data_dirs, data_files): + @clibs.handle_exception + def check_files(): + for proc_name, is_enabled in self.procs.items(): + if is_enabled: + self.curves.extend(self.curve_map[proc_name]) + + if len(self.curves) == 0: + self.logger("ERROR", "factory", "未查询到需要记录数据的曲线,至少选择一个!", "red", "CurveNameError") + + if len(data_dirs) != 0 or len(data_files) != 1: + self.logger("ERROR", "factory", "初始路径下不允许有文件夹,且初始路径下只能存在一个工程文件 —— *.zip,确认后重新运行!", "red", "InitFileError") + + if not data_files[0].endswith(".zip"): + self.logger("ERROR", "factory", f"{data_files[0]} 不是一个有效的工程文件,需确认!", "red", "ProjectFileError") + + return data_files[0] + + @clibs.handle_exception + def get_configs(): + robot_type, records = None, None + msg_id = clibs.c_hr.execution("controller.get_params") + records = clibs.c_hr.get_from_id(msg_id) + for record in records: + if "请求发送成功" not in record[0]: + robot_type = eval(record[0])["data"]["robot_type"] + server_file = f"/home/luoshi/bin/controller/robot_cfg/{robot_type}/{robot_type}.cfg" + local_file = self.dir_path + f"/{robot_type}.cfg" + clibs.c_pd.pull_file_from_server(server_file, local_file) + + try: + with open(local_file, mode="r", encoding="utf-8") as f_config: + configs = json.load(f_config) + except Exception as Err: + self.logger("ERROR", "factory", f"无法打开 {local_file}
{Err}", "red", "OpenFileError") + + # 最大角速度,额定电流,减速比,额定转速 + version = configs["VERSION"] + m_avs = configs["MOTION"]["JOINT_MAX_SPEED"] + self.logger("INFO", "factory", f"get_configs: 机型文件版本 {robot_type}_{version}") + self.logger("INFO", "factory", f"get_configs: 各关节角速度 {m_avs}") + return m_avs + + self.logger("INFO", "factory", "正在做初始化校验和配置,这可能需要一点时间......", "green") + prj_file = check_files() + if prj_file is None: + return + avs = get_configs() + params = { + "prj_file": prj_file, + "interval": self.interval, + "avs": avs, + } + self.logger("INFO", "factory", "数据目录合规性检查结束,未发现问题......", "green") + return params + + @clibs.handle_exception + def change_curve_state(self, stat): + display_pdo_params = [{"name": name, "channel": chl} for name in self.curves for chl in range(6)] + clibs.c_hr.execution("diagnosis.open", open=stat, display_open=stat) + clibs.c_hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params) + + @clibs.handle_exception + def run_rl(self, params): + prj_file, interval = params["prj_file"], params["interval"] + # 1. 关闭诊断曲线,触发软急停,并解除,目的是让可能正在运行着的机器停下来,切手动模式并下电 + self.change_curve_state(False) + clibs.c_md.r_soft_estop(0) + clibs.c_md.r_soft_estop(1) + clibs.c_md.r_clear_alarm() + clibs.c_md.write_act(False) + time.sleep(1) # 让曲线彻底关闭 + + # 2. reload工程后,pp2main,并且自动模式和上电 + prj_name = ".".join(prj_file.split("/")[-1].split(".")[:-1]) + prj_path = f"{prj_name}/_build/{prj_name}.prj" + clibs.c_hr.execution("overview.reload", prj_path=prj_path, tasks=["factory"]) + clibs.c_hr.execution("rl_task.pp_to_main", tasks=["factory"]) + clibs.c_hr.execution("state.switch_auto") + clibs.c_hr.execution("state.switch_motor_on") + + # 3. 开始运行程序 + self.logger("INFO", "factory", f"正在采集场景工程的周期,大概1min左右......", "blue") + clibs.c_hr.execution("rl_task.set_run_params", loop_mode=True, override=1.0) + clibs.c_hr.execution("rl_task.run", tasks=["factory"]) + t_start = time.time() + while True: + if clibs.c_md.read_ready_to_go() == 1: + clibs.c_md.write_act(True) + break + else: + if (time.time() - t_start) > 15: + self.logger("ERROR", "factory", "15s 内未收到机器人的运行信号,需要确认RL程序编写正确并正常执行...", "red", "ReadySignalTimeoutError") + else: + time.sleep(clibs.INTERVAL) + + # 4. 获取初始数据,周期时间,首次的各轴平均电流值,打开诊断曲线,并执行采集 + time.sleep(clibs.INTERVAL*10) # 等待 RL 程序中 scenario_time 初始化 + t_start = time.time() + while True: + scenario_time = float(f"{float(clibs.c_md.read_scenario_time()):.2f}") + if scenario_time != 0: + self.logger("INFO", "factory", f"耐久工程的周期时间:{scenario_time}s | 单轮次执行时间:{scenario_time+interval}~{scenario_time*2+interval}") + break + else: + time.sleep(clibs.INTERVAL) + if (time.time() - t_start) > 900: + self.logger("ERROR", "factory", f"900s内未收到耐久工程的周期时间,需要确认RL程序和工具通信交互是否正常执行(支持最长工程周期时间为300s)......", "red", "GetScenarioTimeError") + + # 6. 准备数据保存文件 + for proc_name, is_enabled in self.procs.items(): + if not is_enabled: + continue + + with open(f"{self.dir_path}/{proc_name}.csv", mode="a+", newline="") as f_csv: + for curve in self.curve_map[proc_name]: + titles = [f"{curve}_{i}" for i in range(6)] + titles.insert(0, "time") + csv_writer = csv.writer(f_csv) + csv_writer.writerow(titles) + + # 7. 开始采集 + count = 0 + while clibs.running[self.idx]: + this_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + next_time_1 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()+scenario_time+interval+1)) + next_time_2 = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()+scenario_time+interval+1+scenario_time)) + self.logger("INFO", "factory", f"[{this_time}] 当前次数:{count:09d} | 预计下次数据更新时间:{next_time_1}~{next_time_2}", "#008B8B") + count += 1 + # 固定间隔,更新一次数据,打开曲线,获取周期内电流,关闭曲线 + time.sleep(interval) + while True: + capture_start = clibs.c_md.read_capture_start() + if capture_start == 1: + break + else: + time.sleep(clibs.INTERVAL/10) + + self.change_curve_state(True) + time.sleep(scenario_time) + end_time = time.time() + start_time = end_time - scenario_time + self.change_curve_state(False) + # 保留数据并处理输出 + self.gen_results(params, start_time, end_time) + else: + self.change_curve_state(False) + self.logger("INFO", "factory", "后台数据清零完成,现在可以重新运行其他程序。", "green") + + @clibs.handle_exception + def gen_results(self, params, start_time, end_time): + s_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)) + e_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) + try: + clibs.lock.acquire(True) + clibs.cursor.execute(f"SELECT content FROM logs WHERE timestamp BETWEEN '{s_time}' AND '{e_time}' AND content LIKE '%diagnosis.result%' ORDER BY id ASC") + records = clibs.cursor.fetchall() + finally: + clibs.lock.release() + + self.data_proc(records, params) + + @clibs.handle_exception + def data_proc(self, records, params): + for proc_name, is_enabled in self.procs.items(): + if not is_enabled: + continue + + if proc_name == "周期内平均转矩": + # get_avg_trq(records, params, w2t) + t = threading.Thread(target=self.get_avg_trq, args=(records, params, proc_name)) + t.daemon = True + t.start() + elif proc_name == "周期内最大速度": + # get_joint_max_vel(records, params, w2t) + t = threading.Thread(target=self.get_joint_max_vel, args=(records, params, proc_name)) + t.daemon = True + t.start() + + @clibs.handle_exception + def get_avg_trq(self, records, params, proc_name): + d_trq, results = [[], [], [], [], [], []], [time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))] + for record in records: + data = eval(record[0])["data"] + for item in data: + d_item = reversed(item["value"]) + for axis in range(6): + if item.get("channel", None) == axis and item.get("name", None) == "device_servo_trq_feedback": + d_trq[axis].extend(d_item) + + for axis in range(6): + df = pandas.DataFrame.from_dict({"device_servo_trq_feedback": d_trq[axis]}) + _ = math.sqrt(numpy.square(df[df.columns[0]].values * 1.27 / 1000).sum() / len(df)) + results.append(_) + + path = "/".join(params["prj_file"].split("/")[:-1]) + with open(f"{path}/{proc_name}.csv", mode="a+", newline="") as f_csv: + csv_writer = csv.writer(f_csv) + csv_writer.writerow(results) + + @clibs.handle_exception + def get_joint_max_vel(self, records, params, proc_name): + d_trq, results = [[], [], [], [], [], []], [time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))] + for record in records: + data = eval(record[0])["data"] + for item in data: + d_item = reversed(item["value"]) + for axis in range(6): + if item.get("channel", None) == axis and item.get("name", None) == "hw_joint_vel_feedback": + d_trq[axis].extend(d_item) + + for axis in range(6): + df = pandas.DataFrame.from_dict({"hw_joint_vel_feedback": d_trq[axis]}) + _ = df.max().iloc[0] + results.append(_) + + path = "/".join(params["prj_file"].split("/")[:-1]) + with open(f"{path}/{proc_name}.csv", mode="a+", newline="") as f_csv: + csv_writer = csv.writer(f_csv) + csv_writer.writerow(results) + + @staticmethod + def detect_db_size(): + @clibs.db_lock + @clibs.handle_exception + def release_memory(): + line_number = 20000 + leftover = 4000 # 200s + clibs.cursor.execute("SELECT COUNT(id) FROM logs") + len_records = clibs.cursor.fetchone()[0] + if len_records > line_number: + del_num = len_records - leftover + 1 + clibs.cursor.execute(f"DELETE FROM logs WHERE id < {del_num}") + clibs.cursor.execute(f"UPDATE logs SET id=(id-{del_num - 1}) WHERE id > {del_num - 1}") + clibs.cursor.execute(f"UPDATE sqlite_sequence SET seq = {leftover + 1} WHERE name = 'logs' ") + clibs.cursor.execute("VACUUM") + + while True: + release_memory() + time.sleep(clibs.INTERVAL*10) + + @clibs.handle_exception + def processing(self): + time_start = time.time() + clibs.running[self.idx] = 1 + if clibs.status["hmi"] != 1 or clibs.status["md"] != 1: + self.logger("ERROR", "factory", "processing: 需要在网络设置中连接HMI以及Modbus通信!", "red", "NetworkError") + + t = threading.Thread(target=self.detect_db_size) + t.daemon = True + t.start() + + data_dirs, data_files = clibs.traversal_files(self.dir_path, self.output) + params = self.initialization(data_dirs, data_files) + clibs.c_pd.push_prj_to_server(params["prj_file"]) + self.run_rl(params) + + self.logger("INFO", "factory", "-"*60 + "
全部处理完毕
", "purple") + time_total = time.time() - time_start + msg = f"处理时间:{time_total // 3600:02.0f} h {time_total % 3600 // 60:02.0f} m {time_total % 60:02.0f} s" + self.logger("INFO", "factory", msg) diff --git a/codes/ui/__init__.py b/codes/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/ui/main_window.py b/codes/ui/main_window.py similarity index 93% rename from code/ui/main_window.py rename to codes/ui/main_window.py index 9d6e659..b591b20 100644 --- a/code/ui/main_window.py +++ b/codes/ui/main_window.py @@ -27,7 +27,7 @@ class Ui_MainWindow(QMainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") MainWindow.setEnabled(True) - MainWindow.resize(1002, 555) + MainWindow.resize(1004, 560) sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -285,12 +285,12 @@ class Ui_MainWindow(QMainWindow): self.verticalLayout_7.addWidget(self.label_11) - self.scrollArea = QScrollArea(self.frame) - self.scrollArea.setObjectName(u"scrollArea") - self.scrollArea.setWidgetResizable(True) + self.sa_durable = QScrollArea(self.frame) + self.sa_durable.setObjectName(u"sa_durable") + self.sa_durable.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents") - self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 211, 78)) + self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 212, 78)) self.horizontalLayout_9 = QHBoxLayout(self.scrollAreaWidgetContents) self.horizontalLayout_9.setObjectName(u"horizontalLayout_9") self.verticalLayout_5 = QVBoxLayout() @@ -314,9 +314,9 @@ class Ui_MainWindow(QMainWindow): self.horizontalLayout_9.addLayout(self.verticalLayout_5) - self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.sa_durable.setWidget(self.scrollAreaWidgetContents) - self.verticalLayout_7.addWidget(self.scrollArea) + self.verticalLayout_7.addWidget(self.sa_durable) self.verticalLayout_8.addLayout(self.verticalLayout_7) @@ -478,6 +478,8 @@ class Ui_MainWindow(QMainWindow): self.cb_hmi_cmd.addItem("") self.cb_hmi_cmd.addItem("") self.cb_hmi_cmd.addItem("") + self.cb_hmi_cmd.addItem("") + self.cb_hmi_cmd.addItem("") self.cb_hmi_cmd.setObjectName(u"cb_hmi_cmd") self.cb_hmi_cmd.setMinimumSize(QSize(240, 0)) self.cb_hmi_cmd.setFont(font4) @@ -558,6 +560,12 @@ class Ui_MainWindow(QMainWindow): self.cb_md_cmd = QComboBox(self.page_2) self.cb_md_cmd.addItem("") self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") + self.cb_md_cmd.addItem("") self.cb_md_cmd.setObjectName(u"cb_md_cmd") self.cb_md_cmd.setMinimumSize(QSize(240, 0)) self.cb_md_cmd.setFont(font4) @@ -638,6 +646,12 @@ class Ui_MainWindow(QMainWindow): self.cb_ec_cmd = QComboBox(self.page_3) self.cb_ec_cmd.addItem("") self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") + self.cb_ec_cmd.addItem("") self.cb_ec_cmd.setObjectName(u"cb_ec_cmd") self.cb_ec_cmd.setMinimumSize(QSize(240, 0)) self.cb_ec_cmd.setFont(font4) @@ -775,11 +789,10 @@ class Ui_MainWindow(QMainWindow): self.horizontalLayout_2.addWidget(self.btn_docs_next) - self.btn_docs_load = QPushButton(self.tab_log) - self.btn_docs_load.setObjectName(u"btn_docs_load") - self.btn_docs_load.setFont(font4) + self.label_13 = QLabel(self.tab_log) + self.label_13.setObjectName(u"label_13") - self.horizontalLayout_2.addWidget(self.btn_docs_load) + self.horizontalLayout_2.addWidget(self.label_13) self.btn_docs_search = QPushButton(self.tab_log) self.btn_docs_search.setObjectName(u"btn_docs_search") @@ -797,7 +810,7 @@ class Ui_MainWindow(QMainWindow): self.horizontalLayout_2.setStretch(1, 1) self.horizontalLayout_2.setStretch(2, 1) self.horizontalLayout_2.setStretch(3, 1) - self.horizontalLayout_2.setStretch(4, 1) + self.horizontalLayout_2.setStretch(4, 3) self.horizontalLayout_2.setStretch(5, 1) self.horizontalLayout_2.setStretch(6, 10) @@ -836,7 +849,6 @@ class Ui_MainWindow(QMainWindow): self.btn_docs_previous.clicked.connect(MainWindow.pre_page) self.btn_docs_realtime.clicked.connect(MainWindow.realtime_page) self.btn_docs_next.clicked.connect(MainWindow.next_page) - self.btn_docs_load.clicked.connect(MainWindow.load_sql) self.btn_docs_search.clicked.connect(MainWindow.search_keyword) self.le_docs_search.returnPressed.connect(MainWindow.search_keyword) self.cb_hmi_cmd.currentTextChanged.connect(MainWindow.hmi_cb_change) @@ -853,10 +865,12 @@ class Ui_MainWindow(QMainWindow): self.le_durable_interval.editingFinished.connect(MainWindow.check_interval) self.cb_ec_cmd.currentTextChanged.connect(MainWindow.ec_cb_change) self.le_hmi_ip.returnPressed.connect(MainWindow.hmi_conn) + self.tw_docs.currentChanged.connect(MainWindow.switch_log_tab) + self.treew_log.itemDoubleClicked.connect(MainWindow.show_item_content) self.tw_funcs.setCurrentIndex(0) self.sw_network.setCurrentIndex(0) - self.tw_docs.setCurrentIndex(1) + self.tw_docs.setCurrentIndex(0) QMetaObject.connectSlotsByName(MainWindow) @@ -893,7 +907,7 @@ class Ui_MainWindow(QMainWindow): self.tw_funcs.setTabText(self.tw_funcs.indexOf(self.tab_unit), QCoreApplication.translate("MainWindow", u"\u6574\u673a\u6d4b\u8bd5", None)) self.label_11.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9\u6307\u6807", None)) self.cb_1.setText(QCoreApplication.translate("MainWindow", u"\u5468\u671f\u5185\u5e73\u5747\u8f6c\u77e9", None)) - self.cb_2.setText(QCoreApplication.translate("MainWindow", u"\u5468\u671f\u5185\u5e73\u5747\u8f6c\u77e9", None)) + self.cb_2.setText(QCoreApplication.translate("MainWindow", u"\u5468\u671f\u5185\u6700\u5927\u901f\u5ea6", None)) self.label_8.setText(QCoreApplication.translate("MainWindow", u"\u8def\u5f84", None)) self.btn_durable_open.setText(QCoreApplication.translate("MainWindow", u"...", None)) self.label_9.setText(QCoreApplication.translate("MainWindow", u"\u95f4\u9694", None)) @@ -911,8 +925,10 @@ class Ui_MainWindow(QMainWindow): self.btn_hmi_conn.setText(QCoreApplication.translate("MainWindow", u"\u8fde\u63a5", None)) self.label_5.setText("") self.cb_hmi_cmd.setItemText(0, QCoreApplication.translate("MainWindow", u"controller.heart", None)) - self.cb_hmi_cmd.setItemText(1, QCoreApplication.translate("MainWindow", u"device.get_params", None)) - self.cb_hmi_cmd.setItemText(2, QCoreApplication.translate("MainWindow", u"safety_area_data", None)) + self.cb_hmi_cmd.setItemText(1, QCoreApplication.translate("MainWindow", u"diagnosis.open", None)) + self.cb_hmi_cmd.setItemText(2, QCoreApplication.translate("MainWindow", u"diagnosis.get_params", None)) + self.cb_hmi_cmd.setItemText(3, QCoreApplication.translate("MainWindow", u"device.get_params", None)) + self.cb_hmi_cmd.setItemText(4, QCoreApplication.translate("MainWindow", u"safety_area_data", None)) self.btn_hmi_send.setText(QCoreApplication.translate("MainWindow", u"\u53d1\u9001", None)) self.label_7.setText(QCoreApplication.translate("MainWindow", u"MD Port", None)) @@ -921,14 +937,26 @@ class Ui_MainWindow(QMainWindow): self.label_12.setText("") self.cb_md_cmd.setItemText(0, QCoreApplication.translate("MainWindow", u"ctrl_motor_on", None)) self.cb_md_cmd.setItemText(1, QCoreApplication.translate("MainWindow", u"ctrl_motor_off", None)) + self.cb_md_cmd.setItemText(2, QCoreApplication.translate("MainWindow", u"ctrl_soft_estop", None)) + self.cb_md_cmd.setItemText(3, QCoreApplication.translate("MainWindow", u"ctrl_estop_reset_clear_alarm", None)) + self.cb_md_cmd.setItemText(4, QCoreApplication.translate("MainWindow", u"sta_motor", None)) + self.cb_md_cmd.setItemText(5, QCoreApplication.translate("MainWindow", u"sta_soft_estop", None)) + self.cb_md_cmd.setItemText(6, QCoreApplication.translate("MainWindow", u"sta_estop", None)) + self.cb_md_cmd.setItemText(7, QCoreApplication.translate("MainWindow", u"sta_alarm", None)) self.btn_md_send.setText(QCoreApplication.translate("MainWindow", u"\u53d1\u9001", None)) self.label_17.setText(QCoreApplication.translate("MainWindow", u"EC Port", None)) self.le_ec_port.setText(QCoreApplication.translate("MainWindow", u"8080", None)) self.btn_ec_conn.setText(QCoreApplication.translate("MainWindow", u"\u8fde\u63a5", None)) self.label_18.setText("") - self.cb_ec_cmd.setItemText(0, QCoreApplication.translate("MainWindow", u"motor_on_state", None)) - self.cb_ec_cmd.setItemText(1, QCoreApplication.translate("MainWindow", u"robot_running_state", None)) + self.cb_ec_cmd.setItemText(0, QCoreApplication.translate("MainWindow", u"motor_on", None)) + self.cb_ec_cmd.setItemText(1, QCoreApplication.translate("MainWindow", u"motor_off", None)) + self.cb_ec_cmd.setItemText(2, QCoreApplication.translate("MainWindow", u"set_do", None)) + self.cb_ec_cmd.setItemText(3, QCoreApplication.translate("MainWindow", u"switch_mode_auto", None)) + self.cb_ec_cmd.setItemText(4, QCoreApplication.translate("MainWindow", u"switch_mode_manual", None)) + self.cb_ec_cmd.setItemText(5, QCoreApplication.translate("MainWindow", u"operating_mode", None)) + self.cb_ec_cmd.setItemText(6, QCoreApplication.translate("MainWindow", u"motor_on_state", None)) + self.cb_ec_cmd.setItemText(7, QCoreApplication.translate("MainWindow", u"robot_running_state", None)) self.btn_ec_send.setText(QCoreApplication.translate("MainWindow", u"\u53d1\u9001", None)) self.pushButton.setText(QCoreApplication.translate("MainWindow", u"HMI", None)) @@ -946,7 +974,7 @@ class Ui_MainWindow(QMainWindow): self.btn_docs_previous.setText(QCoreApplication.translate("MainWindow", u"\u4e0a\u4e00\u9875", None)) self.btn_docs_realtime.setText(QCoreApplication.translate("MainWindow", u"\u5b9e\u65f6", None)) self.btn_docs_next.setText(QCoreApplication.translate("MainWindow", u"\u4e0b\u4e00\u9875", None)) - self.btn_docs_load.setText(QCoreApplication.translate("MainWindow", u"\u52a0\u8f7d", None)) + self.label_13.setText("") self.btn_docs_search.setText(QCoreApplication.translate("MainWindow", u"\u67e5\u627e", None)) self.le_docs_search.setPlaceholderText(QCoreApplication.translate("MainWindow", u"[id/level/module] \u67e5\u627e\u5185\u5bb9", None)) self.tw_docs.setTabText(self.tw_docs.indexOf(self.tab_log), QCoreApplication.translate("MainWindow", u"\u65e5\u5fd7", None)) diff --git a/code/test.py b/test.py similarity index 84% rename from code/test.py rename to test.py index 2265c7f..f701f83 100644 --- a/code/test.py +++ b/test.py @@ -1,3 +1,9 @@ +def test(flag: int, **kwargs): + print(f"flag = {flag}") + print(f"kwargs = {kwargs}") + +test(11, a=1,b="b") + import time # import common.openapi as openapi @@ -11,20 +17,20 @@ import time -import pymysql - -conn = pymysql.connect(host='10.2.20.216', user='root', password='Rokae_123457', port=13306, charset='utf8') -cursor = conn.cursor() -cursor.execute("SET autocommit = 1;") -cursor.execute("use fanmingfu;") +# import pymysql +# +# conn = pymysql.connect(host='10.2.20.216', user='root', password='Rokae_123457', port=13306, charset='utf8') +# cursor = conn.cursor() +# cursor.execute("SET autocommit = 1;") +# cursor.execute("use fanmingfu;") # cursor.execute("insert into 20250315153551_log (module, level, content) values (%s, %s, %s)", ("aioaaaaaa", "debug", "testing information")) # logger("ERROR", "clibs", f"数据文件夹{dir_path}不存在,请确认后重试......\n", signal, "red", "PathNotExistError", idx) -level = "ERROR" -module = "clibs" -content = "{'data': {'name': 'xCore'}, 'id': 'controller.heart-1742374255.8898985'}" -tb_name = "20250319162718_log" -cursor.execute(f"INSERT INTO {tb_name} (level, module, content) VALUES (%s, %s, %s)", (level, module, content)) +# level = "ERROR" +# module = "clibs" +# content = "{'data': {'name': 'xCore'}, 'id': 'controller.heart-1742374255.8898985'}" +# tb_name = "20250319162718_log" +# cursor.execute(f"INSERT INTO {tb_name} (level, module, content) VALUES (%s, %s, %s)", (level, module, content)) # conn.commit() # ============================================ diff --git a/ui/main.ui b/ui/main.ui index 53452a8..5c4ed7d 100644 --- a/ui/main.ui +++ b/ui/main.ui @@ -9,8 +9,8 @@ 0 0 - 1002 - 555 + 1004 + 560 @@ -578,7 +578,7 @@ - + true @@ -587,7 +587,7 @@ 0 0 - 211 + 212 78 @@ -616,7 +616,7 @@ - 周期内平均转矩 + 周期内最大速度 @@ -937,6 +937,16 @@ controller.heart + + + diagnosis.open + + + + + diagnosis.get_params + + device.get_params @@ -1084,6 +1094,36 @@ ctrl_motor_off + + + ctrl_soft_estop + + + + + ctrl_estop_reset_clear_alarm + + + + + sta_motor + + + + + sta_soft_estop + + + + + sta_estop + + + + + sta_alarm + + @@ -1211,6 +1251,36 @@ 12 + + + motor_on + + + + + motor_off + + + + + set_do + + + + + switch_mode_auto + + + + + switch_mode_manual + + + + + operating_mode + + motor_on_state @@ -1316,7 +1386,7 @@ - 1 + 0 Qt::TextElideMode::ElideNone @@ -1391,7 +1461,7 @@ - + @@ -1458,15 +1528,9 @@ - - - - Consolas - 12 - - + - 加载 + @@ -1702,22 +1766,6 @@ - - btn_docs_load - clicked() - MainWindow - load_sql() - - - 651 - 507 - - - 606 - 521 - - - btn_docs_search clicked() @@ -1725,7 +1773,7 @@ search_keyword() - 732 + 730 507 @@ -1837,8 +1885,8 @@ md_cb_change() - 805 - 89 + 467 + 81 612 @@ -1853,8 +1901,8 @@ md_send() - 887 - 89 + 327 + 81 795 @@ -1869,8 +1917,8 @@ ec_send() - 887 - 89 + 327 + 81 652 @@ -1901,8 +1949,8 @@ md_conn() - 545 - 89 + 327 + 81 398 @@ -1917,8 +1965,8 @@ ec_conn() - 545 - 89 + 327 + 81 412 @@ -1949,8 +1997,8 @@ ec_cb_change() - 805 - 89 + 467 + 81 540 @@ -1974,6 +2022,38 @@ + + tw_docs + currentChanged(int) + MainWindow + switch_log_tab() + + + 313 + 193 + + + 202 + 172 + + + + + treew_log + itemDoubleClicked(QTreeWidgetItem*,int) + MainWindow + show_item_content() + + + 441 + 321 + + + 205 + 308 + + + prog_start() @@ -2000,5 +2080,7 @@ md_cb_change() ec_cb_change() check_interval() + switch_log_tab() + show_item_content()