AIO/code/aio.py
2025-03-20 18:13:49 +08:00

519 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"<span style='color:{color};'>{msg}</span>")
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())