import json import threading import time import ui.login_window as login_window import ui.reset_window as reset_window import ui.main_window as main_window from PySide6 import QtWidgets from PySide6.QtCore import Qt, QThread, Signal, QObject import sys import re import pymysql import hashlib import datetime import common.clibs as clibs import common.openapi as openapi from PySide6.QtWidgets import QMessageBox from PySide6.QtGui import QColor, QTextCursor, QTextCharFormat, QDoubleValidator from analysis import brake, current, wavelogger, iso class MultiWindows: login_window = None reset_window = None main_window = None class ConnDB(QObject): completed = Signal(tuple) def __init__(self): super().__init__() def do_conn(self, action): conn, cursor = None, None try: conn = pymysql.connect(host='10.2.20.216', user='root', password='Rokae_123457', port=13306, charset='utf8', connect_timeout=clibs.INTERVAL*10) cursor = conn.cursor() except Exception: ... finally: self.completed.emit((conn, cursor)) class RunProg(QObject): completed = Signal(tuple) def __init__(self): super().__init__() def program(self, action): # 0: prog 1: idx prog, idx, network = action if idx in range(7): run = prog.processing elif idx == -1: run = prog.net_conn elif idx == -99: run = prog try: run() self.completed.emit((True, prog, "", idx, network)) # 运行是否成功/返回值/报错信息/idx except Exception as err: self.completed.emit((False, None, err, idx, network)) # 运行是否成功/返回值/报错信息/idx class ThreadIt(QObject): completed = Signal(tuple) def __init__(self): super().__init__() def run_program(self, action): try: res = action[0](*action[1]) self.completed.emit((True, res, "")) # 运行是否成功/返回值/报错信息 except Exception as err: self.completed.emit((False, "", err)) class LoginWindow(login_window.Ui_Form): action = Signal(int) def __init__(self): super(LoginWindow, self).__init__() self.setupUi(self) self.le_username.setFocus() self.conn, self.cursor = None, None if not clibs.status["mysql"]: self.setup_DB() def get_user_infos(self, results): self.conn, self.cursor = results if self.conn is None and self.cursor is None: QMessageBox.critical(self, "网络错误", "无法连接至服务器数据库,稍后再试......") try: MultiWindows.reset_window.close() except Exception: ... finally: self.close() else: self.cursor.execute("SET autocommit = 1;") clibs.status["mysql"] = 1 def setup_DB(self): self.t = QThread(self) self.conn_db = ConnDB() self.conn_db.moveToThread(self.t) self.conn_db.completed.connect(self.get_user_infos) self.action.connect(self.conn_db.do_conn) self.t.start() self.action.emit(1) def user_login(self): username = self.le_username.text() password = self.le_password.text() md = hashlib.md5(password.encode()) password = md.hexdigest() self.cursor.execute("use user_info;") self.cursor.execute("select * from UserInfo;") user_infos = self.cursor.fetchall() for user_info in user_infos: if user_info[0] == username and user_info[1] == password and user_info[2] == 0: MultiWindows.main_window = MainWindow(self.conn, self.cursor, username) MultiWindows.main_window.show() self.close() else: t = datetime.datetime.now().strftime("%H:%M:%S") self.label_hint.setText(f"[{t}] 用户名或密码错误,或用户已登录......") self.label_hint.setStyleSheet("color: red;") def reset_password(self): MultiWindows.reset_window = ResetWindow(self, self.conn, self.cursor) MultiWindows.reset_window.show() self.setVisible(False) class ResetWindow(reset_window.Ui_Form): def __init__(self, login_window, conn, cursor): super(ResetWindow, self).__init__() self.setupUi(self) self.le_username.setFocus() self.login_window = login_window self.conn = conn self.cursor = cursor def reset_password(self): username = self.le_username.text() old_password = self.le_old_password.text() md = hashlib.md5(old_password.encode()) password = md.hexdigest() new_password_1 = self.le_new_password_1.text() new_password_2 = self.le_new_password_2.text() self.cursor.execute("use user_info;") self.cursor.execute("select * from UserInfo;") user_infos = self.cursor.fetchall() for user_info in user_infos: if user_info[0] == username and user_info[1] == password and user_info[2] == 0: break else: t = datetime.datetime.now().strftime("%H:%M:%S") self.label_hint.setText(f"[{t}] 用户名或密码错误,或用户已登录......") self.label_hint.setStyleSheet("color: red;") return if new_password_1 != new_password_2 or len(new_password_1) < 8: t = datetime.datetime.now().strftime("%H:%M:%S") self.label_hint.setText(f"[{t}] 两次输入的新密码不匹配,或长度小于8位......") self.label_hint.setStyleSheet("color: red;") else: md = hashlib.md5(new_password_1.encode()) password = md.hexdigest() self.cursor.execute(f"UPDATE UserInfo SET password = '{password}' WHERE username = '{username}'") self.close() def reset_cancel(self): self.login_window.setVisible(True) self.close() def closeEvent(self, event): self.login_window.setVisible(True) self.close() class MainWindow(main_window.Ui_MainWindow): action = Signal(tuple) def __init__(self, conn, cursor, username): super(MainWindow, self).__init__() self.setupUi(self) self.conn = conn self.cursor = cursor self.username = username self.predoes() # self.t = threading.Thread(target=self.state_detection) # self.t.daemon = True # self.t.start() def predoes(self): # ========================= db int ========================= t = datetime.datetime.now().strftime("%Y%m%d%H%M%S") self.cursor.execute(f"UPDATE UserInfo SET online = 1 WHERE username = '{self.username}';") self.cursor.execute(f"CREATE DATABASE IF NOT EXISTS {self.username};") self.cursor.execute(f"use {self.username};") self.cursor.execute(f"CREATE TABLE {t}_log (id INT AUTO_INCREMENT PRIMARY KEY, timestamp TIMESTAMP NOT NULL default CURRENT_TIMESTAMP, level ENUM('DEBUG', 'INFO', 'WARNING', 'ERROR'), module VARCHAR(255) NOT NULL, content TEXT);") self.cursor.execute(f"INSERT INTO {t}_log (module, level, content) VALUES (%s, %s, %s)", ("aio", "info", "testing")) self.cursor.execute("SHOW TABLES;") tables = [x[0] for x in self.cursor.fetchall()] if len(tables) > 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())