import argparse import asyncio import signal import time import keyboard import requests import sqlite3, datetime import eel import threading, os, sys, platform import serial import logging # from flask_socketio import SocketIO, emit # 改为sockets 来实现websocket from flask import Flask, request, jsonify, g, Response, send_from_directory, json from flask_cors import CORS, cross_origin import websockets # sudo systemctl restart nginx #centos 下重启nginx # gunicorn -b 0.0.0.0:5050 -w 3 -error-log ./dtmgt.log dtmgtApp:app # gunicorn -b 0.0.0.0:5050 -w 3 --error-log ./dtmgt.log --preload dtmgtApp:app # 在linux 下不使用gunicorn启动此程序,nohup python(39) dtmgtApp.py --modbusServer=xxxxx --modbusServerPort=xxxxx # 本地运行 python dtmgtApp.py --startModusServer # pyinstaller --add-data "src;dst" xxxx.py # 打包成1个可执行文件 exe # pyinstaller --onefile --add-data "web;web" dtmgtApp.py from pymodbus.client import ModbusTcpClient # import plc_comm as dtm_machine import dtMachineService as machineService from mhi_plc import ModbusPlcServer app = Flask(__name__, static_folder='web') app.config['SECRET_KEY'] = 'secret!' # socketio = SocketIO() # socketio.init_app(app, async_mode='threading1', cors_allowed_origins="*") initialized = False dbServer_aloned = False dtm_machines = {} dtMachineService = None webview_thread = None modbus_server_obj = None http_server = None webSocket_server = None flask_server_thread = None keyboard_input_thread = None main_exit_flag = False flask_stop_event = None socketio_thread = None # 关闭所有的日志记录 app.logger.disabled = True # 或者将日志等级调整为 ERROR 以仅记录错误 # app.logger.setLevel(logging.ERROR) # 设置 Werkzeug 日志记录器(Flask 默认的开发服务器)等级为 ERROR logging.getLogger('werkzeug').setLevel(logging.ERROR) CORS(app) # 这会让所有的资源都支持跨域访问 CORS(app, resources=r"/*") # 连接SQLite数据库 try: dbConn = sqlite3.connect('db\dtmgtDb.db') dbCursor = dbConn.cursor() # 创建表 dbCursor.execute('''CREATE TABLE IF NOT EXISTS TEST (ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, RESULT TEXT NOT NULL);''') except sqlite3.Error as e: print(f"Database error: {e}") # 或者你可以选择抛出异常,让程序停止运行 # raise e # socket IO name_space = "/ws" """ @socketio.on('connect', namespace=name_space) # 处理前端定时或者主动来查询试验机器数据和状态的请求 @cross_origin() def ws_connect(auth ={}): print('socket client connected', auth) return None @socketio.on('disconnect', namespace=name_space) # 处理前端定时或者主动来查询试验机器数据和状态的请求 @cross_origin() def ws_disconnect(): print('socket client disconnected') """ async def websocket_server(ws, path): async for message in ws: # print("ws", message) try: msg_json = json.loads(message) if msg_json: # print("ws",msg_json) if "command" in msg_json and msg_json["command"] == "get_machines_data" and \ "data" in msg_json: result = handle_get_machines_value(msg_json['data']) result['command'] = 'get_machines_data' # 将字典转换为 JSON 字符串 await ws.send(json.dumps(result)) except Exception as e: pass # print(f'Unexpected error: {e}') # await websocket.send(json.dumps({"message": "Received your message: " + data['message']})) # socket IO # @socketio.on('get_machines_data', namespace=name_space) # 处理前端定时或者主动来查询试验机器数据和状态的请求 def handle_get_machines_value(param): # print('socket io received message: ' + param) try: param = json.loads(param) except: pass if 'machines' in param: if dtMachineService: result = dtMachineService.get_machines_value(param['machines']) # emit('get_machines_data', {"status": "success", "data": result}) return {"status": "success", "data": result} else: return {"status": "error", "msg": "machine service is not available"} # emit('get_machines_data', {"status": "error", "msg": "machine service is not available"}) else: # emit('get_machines_data', {"status": "error", "msg": "machines in param is invalid"}) return {"status": "error", "msg": "machines in param is invalid"} @app.before_request def before_request(): global dtMachineService, initialized db_path = os.path.join('db', 'dtmgtDb.db') g.db = sqlite3.connect(db_path) if not initialized: print("Initializing dtMachineService") dtMachineService = machineService.DtmMachineService('127.0.0.1', 5020, access_table_data, app ,station_data_changed) dtMachineService.dtm_virt_machine_client_connect() initialized = True # print("open database", g.db) """ @app.route('/') @cross_origin() def send_file(path): print("access ",path) if path != "" and path.endswith(".html"): return send_from_directory(app.static_folder, path) else: return send_from_directory(app.static_folder, path) """ @app.route('/login', methods=['POST']) @cross_origin() def login_validate(): json_return = {"status": "error"} params = request.json db_path = os.path.join('db', 'dtmgtDb.db') print("/login", params) # cyx if params.get('username') is None or params.get('password') is None: return jsonify({"status": "error", "error": "username or password is missing"}), 400 if g is None: db = sqlite3.connect(db_path) elif hasattr(g, 'db') == False: db = sqlite3.connect(db_path) else: db = g.db sql_str = f"SELECT * FROM user WHERE id > 0 and username =? and password = ?" sql_params = [params.get('username'), params.get('password')] try: cursor = db.cursor() cursor.execute(sql_str, sql_params) rows = cursor.fetchall() column_names = [description[0] for description in cursor.description] result = [] for row in rows: dict_row = {col: row[idx] if row[idx] is not None else None for idx, col in enumerate(column_names)} result.append(dict_row) # print("login result",result)#cyx if len(result) == 1: json_return = {"status": "success", "username": result[0].get('username'), "userid": result[0].get('userid'), "role": result[0].get('role'), "auth": result[0].get('auth')} except sqlite3.Error as e: print("get user info error", e) # cyx return jsonify(json_return) @app.route('/modbusServerStarted', methods=['POST']) @cross_origin() def modbus_server_started(): json_return = {"status": "success"} params = request.json print("/modbusServerStarted", params) # cyx dtMachineService.dtm_virt_machine_client_connect() return jsonify(json_return) # 设置跌落机的试验参数 @app.route('/machine_control', methods=['POST']) @cross_origin() def machine_control(): global dtm_machines, dtMachineService params = request.json json_return = {"status": "error"} actionParam = {} # print("dtmgtApp /machine_control", params) # cyx """ let control_reqs={ start:{machine:'HYSZ-DTM-002',com:'com2',station:'01'}, stop:{machine:'HYSZ-DTM-002',com:'com2',station:'01'}, setParams: { machine: 'HYSZ-DTM-002', com: 'com2', station: '01', dropHeight: 67, dropCycles: 89, }, getParams:{machine: 'HYSZ-DTM-002', com: 'com2', station: '01'} } """ """ dtm_machine1.read_station_dropheight('01') dtm_machine1.set_station_dropheight('02',531) dtm_machine1.start_station('01') """ # 检测键是否存在 if 'setParams' in params: # 设置工作站(台)测试执行参数 value_return = {"status": "error", "msg": "setParams error"} setParams = params['setParams'] actionParam = setParams print("machine_control setParams", setParams) # cyx # 使用 set 检测多个键是否存在 for setParam in setParams: required_keys = {'machine', 'com', 'station'} if required_keys.issubset(setParam.keys()): if 'dropHeight' in setParam: try: dtMachineSN = setParam['machine'] result = dtMachineService.set_station_dropheight(dtMachineSN, setParam['station'], int(setParam['dropHeight'])) except Exception as error: value_return = {'status': "error", "message": "set station dropHeght ", "error": error} if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "set station dropHeight success"} pass if 'dropCycles' in setParam: try: dtMachineSN = setParam['machine'] result = dtMachineService.set_station_cycles(dtMachineSN, setParam['station'], setParam['dropCycles']) print("machine_control setParams set cycles return ", result) # cyx except Exception as error: value_return = {'status': "error", "message": "set station dropCycles ", "error": error} if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "set station dropCycles success"} if 'cyclesFinished' in setParam: try: dtMachineSN = setParam['machine'] result = dtMachineService.set_station_finished(dtMachineSN, setParam['station'], setParam['cyclesFinished']) except Exception as error: value_return = {'status': "error", "message": "set station cyclesFinished ", "error": error} if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "set station cyclesFinished success"} pass return value_return if 'start' in params or 'stop' in params or 'resume' in params or 'pause' in params or 'cancel' in params: # 启动 停止 继续 取消 暂停 工作站(台) value_return = {'status': "error", 'message': "station start stop resume error"} param = None if 'start' in params: param = params['start'] if 'stop' in params: param = params['stop'] if 'cancel' in params: param = params['cancel'] if 'resume' in params: param = params['resume'] if 'pause' in params: param = params['pause'] actionParam = param # 使用 set 检测多个键是否存在 required_keys = {'machine', 'station'} if required_keys.issubset(param.keys()): # 将前端发送的 start resume stop cancel pause 等命令统一按照dtStationAttachDut 处理,以便 # 统一处理工位 机台 试验样品 试验要求的状态 if 'start' in params: return dtMachineService.dtStationAttachDut(param['machine'], param['station'], 'start', None, True) if 'resume' in params: return dtMachineService.dtStationAttachDut(param['machine'], param['station'], 'resume', None, True) if 'stop' in params: return dtMachineService.dtStationDetachDut(param['machine'], param['station'], 'stop') if 'cancel' in params: return dtMachineService.dtStationDetachDut(param['machine'], param['station'], 'cancel') if 'pause' in params: return dtMachineService.dtStationAttachDut(param['machine'], param['station'], 'pause', None, True) else: # print("键 'machine', 'com', 'station', 'params' 不完全存在于 JSON 中") pass return jsonify(value_return) if 'getParams' in params: value_return = {'status': "error", 'message': "get station params error"} param = params['getParams'] actionParam = param # 使用 set 检测多个键是否存在q required_keys = {'machine', 'com', 'station'} if required_keys.issubset(param.keys()): if 'all' not in param and 'machine' in param: try: dtMachineSN = param['machine'] result = dtMachineService.read_station_cyclesFinished(dtMachineSN, param['station']) if 'status' in result and result['status'] == 'success' and 'value' in result: value_return["status"] = "success" value_return["action"] = "getParams" value_return["finished"] = result['value'] value_return["message"] = "get station params success" except Exception as error: value_return["error"] = error pass return jsonify(value_return) if 'getStationValue' in params: # 获取机器工作站(台)的状态与数据 value_return = {'status': "error", 'message': "get station params error"} param = params['getStationValue'] # 使用 set 检测多个键是否存在q required_keys = {'machine', 'station'} if 'station' in param and 'machine' in param: if dtMachineService: result = dtMachineService.get_station_value(param['machine'], param['station']) return jsonify(result) else: return jsonify({"status": "error", "msg": "machine service is not available"}) # print("键 'machine', 'com', 'station', 'params' 不完全存在于 JSON 中") pass if 'getMachinesValue' in params: # 获取机器的状态与数据 value_return = {'status': "error", 'message': "get station params error"} param = params['getMachinesValue'] # print("/machine_control getMachinesValue", dtMachineService) if 'machines' in param: if dtMachineService: result = dtMachineService.get_machines_value(param['machines']) return jsonify(result) else: return jsonify({"status": "error", "msg": "machine service is not available"}) # print("键 'machine', 'com', 'station', 'params' 不完全存在于 JSON 中") pass if 'attachDut' in params: # 将测试样品与机器的工作站(台)关联 value_return = {'status': "error", 'message': "station attach dut error"} param = params['attachDut'] if 'machine' in param and 'station' in param and 'dut' in param and 'action' in param: station_sn = param['station'] machine_sn = param['machine'] dut_sn = param['dut'] action = param['action'] updateTableFlag = param['updateTableFlag'] userid = param.get('userid', None) # 安排试验的用户id result = dtMachineService.dtStationAttachDut(machine_sn, station_sn, action, dut_sn, updateTableFlag, userid) if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "station attach dut success"} if 'station' in result: value_return['station'] = result['station'] return jsonify(value_return) if 'detachDut' in params: # 测试样品与机器的工作站(台)断开关联 value_return = {'status': "error", 'message': "station detach dut error"} param = params['detachDut'] if 'machine' in param and 'station' in param and 'action' in param: station_sn = param['station'] machine_sn = param['machine'] action = param['action'] result = dtMachineService.dtStationDetachDut(machine_sn, station_sn, action) if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "station detach dut success"} if 'station' in result: value_return['station'] = result['station'] return jsonify(value_return) if 'assignTestReq' in params: # 设置测试要求 value_return = {'status': "error", 'message': "station assign testReq error"} param = params['assignTestReq'] if 'machine' in param and 'station' in param and 'dut' in param and 'testReqIndex' in param: station_sn = param['station'] machine_sn = param['machine'] dut_sn = param['dut'] testReqIndex = param['testReqIndex'] # 测试样件的测试项目index result = dtMachineService.dtStationAssignTestReq(machine_sn, station_sn, dut_sn, testReqIndex) if 'status' in result and result['status'] == 'success': value_return = {'status': "success", 'message': "station assign testReq success"} if 'station' in result: value_return['station'] = result['station'] return value_return if 'registerTest' in params: # PLC 寄存器测试 value_return = {'status': "error", 'message': "register read or write test error"} param = params['registerTest'] # 使用 set 检测多个键是否存在q required_keys = {'machine', 'station'} if 'machine' in param and 'action' in param and 'address' in param: if dtMachineService: result = dtMachineService.register_test(param['machine'], param['action'], param['address'], param.get('value', None)) return jsonify(result) else: return jsonify({"status": "error", "msg": "machine service is not available"}) # print("键 'machine', 'com', 'station', 'params' 不完全存在于 JSON 中") pass return json_return # 设置跌落机的试验参数 @app.route('/get_machine_config', methods=['GET']) @cross_origin() def get_machine_config(): global dtm_machines, dtMachineService json_return = {"status": "error"} print("get machine_config") if dtMachineService: json_return = {"status": "success", "data": dtMachineService.get_machine_com_config()} return jsonify(json_return) """ CREATE TABLE DUTList ( SN TEXT (20) UNIQUE, name TEXT (20), project TEXT (20), phase TEXT (16), stationAssigned TEXT (18), itemOnGoing TEXT (10), itemsFinished NUMERIC (5), status TEXT (10), testReq TEXT (10), inspector TEXT (20), id INTEGER PRIMARY KEY AUTOINCREMENT ); """ import sqlite3 from flask import jsonify, request @app.route('/dbTableAccess', methods=['GET', 'POST']) @cross_origin() def access_table_data(): global date_span_str table = request.args.get('table') pageSize = request.args.get('pageSize', default=None, type=int) currentPage = request.args.get('currentPage', default=None, type=int) # print(f"/dbTableAccess pageSize={pageSize} currentPage={currentPage}") db_path = os.path.join('db', 'dtmgtDb.db') if table is None: return jsonify({"error": "Table parameter is missing"}), 400 if g is None: db = sqlite3.connect(db_path) elif hasattr(g, 'db') == False: db = sqlite3.connect(db_path) else: db = g.db if request.method == 'GET': sql_rows_str = f"SELECT * FROM {table} WHERE id > 0" sql_condition_str = "" sql_params = [] parameters_to_check = [] if table == 'dutList': parameters_to_check = ['name', 'SN', 'description', 'status', 'stationAssigned'] if table == 'testReq': parameters_to_check = ['SN', 'dropItem', 'dropHeight', 'dropCycles', 'description', 'status'] if table == 'project': parameters_to_check = ['name', 'key', 'manager', 'type', 'description'] if table == 'projectPhase': parameters_to_check = ['name', 'PN', 'description'] if table == 'dtMachine': parameters_to_check = ['label', 'SN', 'status', 'type', 'color', 'description', 'plc_address'] if table == 'testReqResult': parameters_to_check = ['SN', 'id', 'dropItem', 'dropHeight', 'dropCycles', 'description', 'status', 'userid'] def get_parameter(param): param_sql = "" param_value = request.args.getlist(param) if param == "SN" and table == "testReqResult": param = "testReq.SN" if param == "id" and table == "testReqResult": param = "testReq.id" if param_value and param != 'stationAssigned': param_sql = f" AND {param} IN ({','.join(['?'] * len(param_value))})" else: pass if param_value and param == 'stationAssigned': param_sql = f" AND json_extract({param}, '$.dtMachine') IN ({','.join(['?'] * len(param_value))})" return param_sql, param_value for param in parameters_to_check: param_sql, param_value = get_parameter(param) sql_condition_str += param_sql sql_params.extend(param_value) # print(f"dbTableAccess {table}", sql_condition_str,request.args.getlist('SN')) # cyx try: total_count = 0 if table == 'dutList' or table == 'DUTList': sql_condition_str = sql_condition_str + ' ORDER BY date(createdate) DESC' if table == 'testReqResult': date_span_str = "" if request.args.get('date1') and request.args.get('date2'): try: # 将这些字符串转化为日期对象 # 前端传入类似 date1: '2023/11/10 10:10:00', date2: '2024/6/11 10:10:00' start_date_obj = datetime.datetime.strptime(request.args.get('date1'), "%Y/%m/%d %H:%M:%S") end_date_obj = datetime.datetime.strptime(request.args.get('date2'), "%Y/%m/%d %H:%M:%S") # 然后,将这些日期对象转化为字符串,以符合数据库中日期字段的格式 formatted_start_date = start_date_obj.strftime('%Y-%m-%d %H:%M:%S') formatted_end_date = end_date_obj.strftime('%Y-%m-%d %H:%M:%S') date_span_str = f" AND testReq.endTime BETWEEN '{formatted_start_date}' AND '{formatted_end_date}'" except Exception as error: print("error=", error) finally: sql_condition_str = sql_condition_str + date_span_str if pageSize and currentPage: cursor = db.cursor() if table == 'testReqResult': cursor.execute(f"SELECT COUNT(*) FROM testReq WHERE id >0 " + sql_condition_str, sql_params) else: cursor.execute(f"SELECT COUNT(*) FROM {table} WHERE id >0 " + sql_condition_str, sql_params) total_count = cursor.fetchone()[0] if table == 'testReqResult': sql_str = f'SELECT testReq.* , dutList.project, dutList.phase , dutList.stationAssigned, ' \ f'dutList.name ,dutList.SN as dutSN ' \ f'FROM testReq LEFT JOIN dutList ON testReq.SN = dutList.SN ' \ f'WHERE testReq.id > 0 ' + sql_condition_str else: sql_str = sql_rows_str + sql_condition_str if pageSize and currentPage: sql_str = sql_str + f' LIMIT {pageSize} OFFSET {pageSize * (currentPage - 1)}' print(f"dbTableAccess {table} sql_str", sql_str, sql_params) # cyx cursor = db.cursor() cursor.execute(sql_str, sql_params) else: sql_str = sql_rows_str + sql_condition_str if pageSize and currentPage: sql_str = sql_str + f' LIMIT {pageSize} OFFSET {pageSize * (currentPage - 1)}' print("dbTableAccess sql_str", sql_str, sql_params) # cyx cursor = db.cursor() cursor.execute(sql_str, sql_params) except sqlite3.Error as e: print(f"dbTableAccess {table} error={e}") # cyx return jsonify({"error": f"Database error: {e}"}), 500 rows = cursor.fetchall() column_names = [description[0] for description in cursor.description] result = [] for row in rows: dict_row = {col: row[idx] if row[idx] is not None else None for idx, col in enumerate(column_names)} if 'stationAssigned' in dict_row: station_assigned_json = json.loads(dict_row['stationAssigned']) if station_assigned_json: dict_row['dtMachine'] = station_assigned_json['dtMachine'] dict_row['station'] = station_assigned_json['station'] result.append(dict_row) return jsonify({'totalCount': total_count, 'length': len(rows), 'data': result}) elif request.method == 'POST': data = request.get_json() action = data['action'] records = data['records'] if action is None: return jsonify({"status": "error", "message": "action is not specified"}) elif action == "insert": try: cursor = db.cursor() current_date = datetime.datetime.now().strftime('%Y-%m-%d') for record in records: if 'id' in record: del record['id'] columns = ', '.join(record.keys()) placeholders = ', '.join('?' for _ in record) sql = f'INSERT INTO {table} ({columns}) VALUES ({placeholders})' cursor.execute(sql, tuple(record.values())) db.commit() return jsonify( {"status": "success", "action": "insert", "message": f"insert records into {table} success", "recordsCnt": len(records)}) except sqlite3.Error as error: print('create duts error', error) return jsonify({"status": "error", "action": "insert", "message": f"insert records into {table} error"}) elif action == "update": try: cursor = db.cursor() update_key = 'PN' if table == 'dutList': update_key = 'SN' if table == 'testReq': update_key = 'id' if table == 'project': update_key = 'key' if table == 'projectPhase': update_key = 'PN' if table == 'dtMachine': update_key = 'SN' for record in records: pn_value = record.get(update_key, None) if pn_value is not None: cursor.execute( f'UPDATE {table} SET {", ".join(["{} = ?".format(field) for field in record])} WHERE {update_key} = ?', tuple(record.values()) + (record[update_key],)) db.commit() return jsonify( {"status": "success", "action": "update", "message": f"update records into {table} success", "recordsCnt": len(records)}) except sqlite3.Error as error: print('update duts error', error) return jsonify( {"status": "error", "action": "update", "message": f"update records into {table} error{error}"}) elif action == "delete": try: cursor = db.cursor() cursor.executemany(f"DELETE FROM {table} WHERE id = ?", [(record['id'],) for record in records]) db.commit() return jsonify({"status": "success", "action": "delete", "message": f"delete {table} records success", "recordsCnt": len(records)}) except sqlite3.Error as error: print('delete', error) return jsonify( {"status": "error", "action": "delete", "message": f"delete {table} records error{error}"}) def get_parameter(param_name): param = request.args.get(param_name) if param is not None: return f" AND {param_name} = ?", param return "", None def post_data_to_lmis(station, data): print("post data to lmis--", data) if data is None: return headers = {'content-type': 'application/json'} retry_count = 0 url = "http://lims.sunwoda.com/gateway/api/rel/upload/commonUploadData" while retry_count < 5: try: response = requests.post(url, data=json.dumps(data), headers=headers) # 检查响应状态码 """ 上传失败异常处理 若上传失败,如网络异常、接口调用不同、未知错误等原因,最终导致数据上传失败,则使用同一个 requestId 进行重传,最多重试5次,每次间隔1分钟。 若重试过程中,收到 1501 状态码,则表示数据已上传成功,停止重传,结束请求。 """ if response.status_code == 200: res_json = response.json() if res_json.get('statusCode') == 1501: print("数据已上传成功") break else: raise Exception("接口返回状态码错误") else: raise Exception("接口响应状态码错误") except Exception as e: print(f"上传失败,原因:{e},开始第{retry_count + 1}次重试") retry_count += 1 time.sleep(60) # 间隔1分钟 else: print("重试上传5次后仍失败,结束请求") def station_data_changed(station, data): if data is None: return upload_thread = threading.Thread(target=post_data_to_lmis, args=(station,data,)) upload_thread.start() # 添加一个全局错误处理器 @app.errorhandler(500) def internal_error(error): return jsonify({'error': 'Internal server error'}), 500 @app.route('/shutdown', methods=['POST']) def shutdown_flask_Server(): print("flask http server Shutting down gracefully...") """ def stop(self): Stop a running SocketIO web server. This method must be called from a HTTP or SocketIO handler function """ shutdown_func = request.environ.get('werkzeug.server.shutdown') if shutdown_func is None: print("shutdown_func is None") else: try: shutdown_func() except Exception as e: print('Error shutting down server:', str(e)) else: print('flask Server shutting down...') return 'flask Server shutting down...' def run_flask_server(flask_stop_event=None): global http_server print("begin init flask server") app.run(host='0.0.0.0', port=5050) def run_websockets(): global webSocket_server loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) webSocket_server = websockets.serve(websocket_server, 'localhost', 5080) loop.run_until_complete(webSocket_server) try: loop.run_forever() except KeyboardInterrupt: print("Caught keyboard interrupt. stop web sockets server") webSocket_server.close() loop.run_until_complete(webSocket_server.wait_closed()) def stop_flask_server(): requests.post('http://127.0.0.1:5050/shutdown') # 将端口号改为你的 Flask 服务器的端口号 def run_webview(): eel.init('web') # 告诉 Eel 查找 'web' 文件夹 eel.start('dtm_entry.html', size=(3000, 2000), position=(0, 0), suppress_error=True) # 定义 Flask-SocketIO 运行函数 def run_flask_socketio(): # socketio.run(app, host='0.0.0.0', port=5080) pass class ModbusServer: def __init__(self, machines_config): self.machines_config = machines_config self.modbus_loop = asyncio.new_event_loop() self.thread = threading.Thread(target=self.run_in_thread, daemon=True) self.modbus_server = None def run_in_thread(self): asyncio.set_event_loop(self.modbus_loop) self.modbus_loop.run_until_complete(self.start_server()) self.modbus_loop.run_forever() async def start_server(self): self.modbus_server = ModbusPlcServer('127.0.0.1', 5020, self.machines_config) try: print("Attempting to start the modbus server...") await self.modbus_server.run_server() except Exception as error: print("Failed to start the modbus server:", error) # 如果需要,可以增加一个方法来检查服务器的状态 def is_running(self): return self.modbus_loop.is_running() def start(self): self.thread.start() def stop(self): if self.modbus_loop.is_running(): # 通过事件循环异步调用 stop_server 方法 asyncio.run_coroutine_threadsafe(self.modbus_server.stop_server(), self.modbus_loop) # 确保事件循环也会停止 self.modbus_loop.call_soon_threadsafe(self.modbus_loop.stop) def exit_program(sig=None, frame=None): global main_exit_flag, http_server, flask_stop_event, modbus_server_obj, webview_thread, socketio_thread, flask_server_thread print("收到SIGINT,正在退出...") main_exit_flag = True modbus_loop_stop_event = threading.Event() stop_flask_server() try: print("shutdown modbus server ---2") # cyx # 停止 Modbus 服务器线程 if modbus_server_obj: modbus_server_obj.stop() print("shutdown ---4") # cyx except Exception as error: print("failed to stop", error) try: http_server.stop() # 停止 Webview 线程 if webview_thread: webview_thread.join() print("webview_thread stop") except Exception as error: print("没有安全退出", error) sys.exit(0) # 使用sys.exit来终止程序 finally: main_exit_flag = True sys.exit(0) # 使用sys.exit来终止程序 def modbus_client_test(): # 创建 Modbus 客户端实例 client = ModbusTcpClient('127.0.0.1', port=5020) # 连接到 Modbus 服务器 connection = client.connect() if connection: print("Modbus client connected successfully") # 在这里执行你的 Modbus 请求 # 例如,读取线圈状态 response = client.read_coils(13, 5) if response.isError(): print("Modbus request failed:", response) else: print("Coil states:", response.bits) response = client.read_holding_registers(185, 2, 1) if response.isError(): print("Modbus request failed:", response) else: print("Holding Registers is :", response.registers) response = client.write_registers(78, [81, 88, 89, 100]) if response.isError(): print("Modbus request failed:", response) else: print("write Registers is :", response.isError()) else: print("Modbus client connection failed") # 关闭 Modbus 客户端连接 client.close() def main(args): global initialized, http_server, modbus_server_obj, webview_thread, socketio_thread, flask_server_thread, main_exit_flag, flask_stop_event global dtMachineService comm_config = { "baudrate": 9600, "stopbits": serial.STOPBITS_ONE, "parity": serial.PARITY_EVEN, "bytesize": serial.SEVENBITS } drop_register = { '01': {"height": 'D0160', "cycles": "D0200", "cyclesFinished": "D0202", "start": 'M0016', "stop": "M0008"}, '02': {"height": 'D0162', "cycles": "D0204", "cyclesFinished": "D0206", "start": 'M0017', "stop": "M0018"} } signal.signal(signal.SIGINT, exit_program) # 设置SIGINT(Ctrl + C)信号处理器 print("init dtMachineService") dtMachineService = machineService.DtmMachineService(args.modbusServer, args.modbusServerPort, access_table_data, app, station_data_changed) initialized = True if args.startModbusServer: # 创建并启动Modbus Server的线程 machines_config = dtMachineService.get_machine_com_config() print("init modbus server") # cyx modbus_server_obj = ModbusServer(machines_config) modbus_server_obj.start() print("main create modbus over--4") initialized = True print("modbus client connect to server") # cyx dtMachineService.dtm_virt_machine_client_connect() # modbus_client_test() # 创建并启动Flask的线程 print("begin init flask server") flask_stop_event = threading.Event() flask_server_thread = threading.Thread(target=run_flask_server, args=(flask_stop_event,)) flask_server_thread.daemon = True flask_server_thread.start() print("init flask server over") # 创建线程并启动 socketio_thread = threading.Thread(target=run_websockets) socketio_thread.daemon = True socketio_thread.start() """ webview_thread = threading.Thread(target=run_webview) webview_thread.daemon = True webview_thread.start() """ while not main_exit_flag: if flask_server_thread: flask_server_thread.join(1) elif socketio_thread: socketio_thread.join(1) else: pass class startArgs: def __init__(self, startModbusServer=False, modbusServer="127.0.0.1", modbusServerPort=5020): self.startModbusServer = startModbusServer self.modbusServer = modbusServer self.modbusServerPort = modbusServerPort ERVER_ADDRESS = [1] START_ADDRESS = 41088+180 START_ADDRESS_2 = 41088+179 QUANTITY = 4 QUANTITY_2 = 4 COM_PORT = "com1" BAUD_RATE = 19200 from pymodbus.client import ModbusSerialClient as ModbusClient def test_modbus_plc(): # 创建Modbus RTU客户端 with ModbusClient(method="rtu", port=COM_PORT, baudrate=BAUD_RATE, parity="E" ,timeout=1) as client: if client.connect(): print("Modbus RTU Client Connected") else: print("Failed to connect to Modbus RTU Client") # 无限循环读取数据 while True: # 遍历服务器地址 for server_address in ERVER_ADDRESS: try: result = client.write_register(41268,23,1) print('write result',result.isError()) # 读取第一组保持寄存器 result = client.read_holding_registers(START_ADDRESS, QUANTITY, unit=1) # 读取第二组保持寄存器 result_2 = client.read_holding_registers(START_ADDRESS_2, QUANTITY_2, unit=1) # 处理读取结果 if not result.isError() and not result_2.isError(): # 控制台输出读取到的寄存器值 print(", ".join(map(str, result.registers))) print(", ".join(map(str, result_2.registers))) else: # 创建解析器 # 输出读取错误信息 print(f"Error reading from server address {server_address}: {result}") print(f"Error reading from server address {server_address}: {result_2}") except Exception as e: # 输出并捕获异常信息 print(f"Error: {e}") # 暂停执行,等待下一轮读取 time.sleep(1) pass def test_modbus_plc1(): # 创建Modbus RTU客户端 with ModbusClient(method="rtu", port=COM_PORT, baudrate=BAUD_RATE, parity="E" ,timeout=1) as client: if client.connect(): print("Modbus RTU Client Connected") else: print("Failed to connect to Modbus RTU Client") result = client.write_coil(117, 0, 1) print('write result', result.isError()) result = client.write_coil(118, 1, 1) print('write result', result.isError()) if __name__ == "__main__": test_modbus_plc1() parser = argparse.ArgumentParser(description="My modbus and plc Description") # 添加参数 同时启动Modbus Server parser.add_argument('--startModbusServer', default=True, help="with start modbus server", action='store_true') # 添加参数 ModbusServerHost 地址 parser.add_argument('--modbusServer', default="127.0.0.1", help="modbus Server URL") # 添加参数 ModbusServer Port parser.add_argument('--modbusServerPort', default=5020, help="Server URL") # 解析参数 args = parser.parse_args() # main(args)