Files
dtm-py-all/dtmgtApp-test.py

994 lines
41 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 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('/<path:path>')
@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) # 设置SIGINTCtrl + 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)