519 lines
20 KiB
Python
519 lines
20 KiB
Python
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())
|
||
|