Files
dtm-py-all/bk/xinjie_plc .py

730 lines
31 KiB
Python
Raw Normal View History

import array
import asyncio
import sys
import time
import keyboard
import serial
import struct
import requests
from queue import Queue
import threading
import argparse
from pymodbus.exceptions import ModbusIOException
from pymodbus.pdu import ModbusRequest
from pymodbus.server import StartTcpServer, StartAsyncTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
from pymodbus.client import ModbusSerialClient as ModbusClient
print_condition = False
all_plc_recv = 0
serverUrl = 'http://106.52.71.204:5050'
# 定义颜色代码
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'
RESET = '\033[m'
def bcd_coder_array(value=0, lenght=4):
return int_to_bcd(value, lenght)
def int_to_bcd(number=0, length=5):
bcd_array = []
try:
int_number = int(round(number)) # 使用round函数四舍五入然后转成整数
while int_number > 0:
bcd = int_number % 100
bcd_array.insert(0, (bcd // 10) << 4 | (bcd % 10))
int_number = int_number // 100
except:
pass
while len(bcd_array) < length:
bcd_array.insert(0, 0)
return bcd_array
def int_to_ascii_array(number, length):
# 将整数转换为ASCII码字符串
ascii_str = str(number)
# 获取ASCII码字符串的长度
ascii_str_length = len(ascii_str)
# 如果ASCII码字符串的长度小于指定长度则在前面补0
if ascii_str_length < length:
ascii_str = '0' * (length - ascii_str_length) + ascii_str
# 将ASCII码字符串转换为ASCII码数组
ascii_array = [ord(c) for c in ascii_str]
return ascii_array
def str_to_ascii_array(str, length):
# 获取ASCII码字符串的长度
ascii_str_length = len(str)
# 如果ASCII码字符串的长度小于指定长度则在前面补0
if ascii_str_length < length:
ascii_str = '0' * (length - ascii_str_length) + str
# 将ASCII码字符串转换为ASCII码数组
ascii_array = [ord(c) for c in str]
return ascii_array
def hex_to_ascii_arr(hex_num, lenth):
str_num = hex(hex_num)[2:].upper().rjust(lenth, '0')
array = [ord(c) for c in str_num]
return array
def convert_ascii_to_num(ascii_val):
if ord('0') <= ascii_val <= ord('9'):
return ascii_val - ord('0')
elif ord('A') <= ascii_val <= ord('F'):
return ascii_val - ord('A') + 10
elif ord('a') <= ascii_val <= ord('f'):
return ascii_val - ord('a') + 10
else:
return 0
def ascii_array_to_word(ascii_array):
if len(ascii_array) != 4:
return 0
return convert_ascii_to_num(ascii_array[0]) * 0x1000 + \
convert_ascii_to_num(ascii_array[1]) * 0x0100 + \
convert_ascii_to_num(ascii_array[2]) * 0x010 + \
convert_ascii_to_num(ascii_array[3])
def date_to_bcd(date_str): # 0:06:20.585233
# condition_print("date=",date_str,type(date_str))
# 移除日期中的"-"字符
date_str = date_str.replace("-", "")
# 按照两位数字进行分割
date_parts = [date_str[i:i + 2] for i in range(0, len(date_str), 2)]
# 将每个部分转换为十六进制数
bcd_array = [int(part, 16) for part in date_parts]
return bcd_array
def time_to_bcd(time_str): # 10:06:20.585233
time_str = time_str.split('.')[0] # 去除掉后面的毫秒
time_parts = time_str.split(':') # 按":"分割
bcd = [int(part) for part in time_parts] # 分别转为整数
bcd = [int(str(i), 16) for i in bcd] # 分别转为16进制
return bcd
def conv_to_plc_address(type_char, value):
# 检查type_char是否为'M'或'D'value是否为整数
if type_char not in ('M', 'D') or not isinstance(value, int):
raise ValueError("Type must be 'M' or 'D' and value must be an integer.")
# 使用str.format()方法格式化字符串
formatted = "{type}{value:04d}".format(type=type_char, value=value)
return formatted
def condition_print(*args, **kwargs):
global print_condition # 使用全局变量
message = ' '.join(str(arg) for arg in args)
if kwargs:
message += ', ' + ', '.join("{}={}".format(key, value) for key, value in kwargs.items())
if print_condition:
print(message)
class MitsubishiPLC:
def __init__(self, plc_port, comm_config):
global print_condition, api_condition, all_plc_recv
port_opened = False
print("DevicePLC init", plc_port, comm_config) # cyx
self.event_format_long = False
try:
self.ser = serial.Serial(port=plc_port,
baudrate=comm_config['baudrate'],
parity=comm_config['parity'],
bytesize=comm_config['bytesize'],
stopbits=comm_config['stopbits'],
timeout=1, rtscts=False)
#condition_print(f"DevicePLC open {plc_port} success") # cyx
print(f"DevicePLC open {plc_port} success") # cyx
except:
condition_print("\033[91m {}\033[00m".format("plc " + plc_port + " open error"))
try:
self.ser = serial.Serial(port="com1",
baudrate=comm_config['baudrate'],
parity=comm_config['parity'],
bytesize=comm_config['bytesize'],
stopbits=comm_config['stopbits'],
timeout=2, rtscts=False)
except:
print("\033[91m {}\033[00m".format("plc " + "com1" + " open error"))
else:
port_opened = True
print("\033[92m {}\033[00m".format("plc " + "com1" + " open success"))
else:
port_opened = True
print("\033[92m {}\033[00m".format("plc " + plc_port + " open success--1"))
finally:
pass
if port_opened:
self.link_down_timer = threading.Timer(5.0, self.link_down_timeout) # 创建一个5秒后执行的定时器
# self.link_down_timer.start()
self.link_down_event = threading.Event()
if self.port_is_opened():
# self.recv_thread.start();
pass
def port_is_opened(self):
if hasattr(self, 'ser') and self.ser:
return True
else:
return False
return False
def get_connect_state(self):
return self.port_is_opened()
def link_down_timeout(self):
self.link_down_event.set()
self.link_down_timer = threading.Timer(5.0, self.link_down_timeout) # 创建一个5秒后执行的定时器
self.link_down_timer.start()
def reset_link_down_timer(self):
if self.link_down_timer:
self.link_down_timer.cancel()
self.link_down_timer = threading.Timer(5.0, self.link_down_timeout) # 创建一个5秒后执行的定时器
self.link_down_timer.start()
def send_heartBit(self):
if self.port_is_opened():
pass
def crc16_ccitt(self, data: bytearray): # CRC16/CCITT
crcval = 0x0000
for c in data:
q = (crcval ^ c) & 0x0F
crcval = (crcval >> 4) ^ (q * 0o10201)
q = (crcval ^ (c >> 4)) & 0x0F
crcval = (crcval >> 4) ^ (q * 0o10201)
return crcval
def plc_read_frame(self, command, plc_no=0x01, pc_no=0xFF, address='D0160', len=0x02):
frame = []
frame.append(0x05)
frame.extend(hex_to_ascii_arr(plc_no, 2))
frame.extend(hex_to_ascii_arr(pc_no, 2))
frame.extend([ord(c) for c in command]) # word read command
frame.append(0x30)
frame.extend(str_to_ascii_array(address, 5)) # eg.D0160
frame.extend(int_to_ascii_array(len, 2))
checksum = sum(frame[1:]) & 0xFF
frame.extend(hex_to_ascii_arr(checksum, 2))
# condition_print("frame=",frame,type(frame))
condition_print('frame intent to read plc: [', ', '.join('{:02x}'.format(x) for x in frame), ']')
return frame
def plc_write_frame(self, command, plc_no=0x01, pc_no=0xFF, address='D0160', date_len=0x02, data=[]):
frame = [0x05]
frame.extend(hex_to_ascii_arr(plc_no, 2))
frame.extend(hex_to_ascii_arr(pc_no, 2))
frame.extend([ord(c) for c in command]) # word write command bit write command
frame.append(0x30)
frame.extend(str_to_ascii_array(address, 5)) # eg.D0160
frame.extend(int_to_ascii_array(date_len, 2))
data_index = 0
while date_len > 0:
value = data[data_index]
data_index = data_index + 1
if command == 'BW':
arr_len = 1
else:
arr_len = 4
word_ascii_array = hex_to_ascii_arr(value, arr_len)
# condition_print(word,'[', ', '.join('{:02x}'.format(x) for x in word_ascii_array), ']')
frame.extend(word_ascii_array)
date_len = date_len - 1
checksum = sum(frame[1:]) & 0xFF
frame.extend(hex_to_ascii_arr(checksum, 2))
condition_print('frame intend to write plc: [', ', '.join('{:02x}'.format(x) for x in frame), ']')
return frame
# Symbol name Description Code (hexadecimal)
# STX Start of Text 02H
# ETX End of Text 03H
# EOT End of Transmission 04H
# ENQ Enquiry 05H
# ACK Acknowledge 06H
# LF Line Feed 0AH
# CL Clear 0CH
# CR Carriage Return ODH
# NAK Negative Acknowledge 15H
def frame_is_valid(self, frame):
# 检查第一个字节是否为控制代码
control_code = frame[0]
ETX: bool = False
if control_code not in [0x05, 0x02, 0x03, 0x4, 0x06, 0x15]:
condition_print("First byte is not a control code.")
return False
# 检查最后两个字节是否为校验和代码
hex_string = ''.join(chr(char) for char in frame[-2:])
# 然后你可以使用int函数将这个16进制的字符串转换为一个整数指定基数为16
checksum = int(hex_string, 16)
if frame[0] == 0x02 and frame[-3] != 0x03:
return False
if frame[0] == 0x06 or frame[0] == 0x15:
return True
if frame[-3] == 0x03:
ETX = True
end_position = -3 if ETX else -2
checksum_calc = sum(frame[1:-2]) & 0xFF # 计算校验和并取第八位
if checksum != checksum_calc:
condition_print("Last two bytes do not match checksum.", checksum, checksum_calc, frame[-2] - 0x30,
frame[-1] - 0x30)
return False
# 创建一个包含所有有效字符的字符串
valid_chars = '0123456789abcdefABCDEF'
# 定义一个函数来检查bytes对象中的每个元素
for i in frame[1:end_position]:
char = chr(i)
if char not in valid_chars:
condition_print(f'{char} ({i}) is not a valid character.')
# 在此处添加其他的逻辑处理代码,例如抛出异常
# raise ValueError(f'{char} ({i}) is not a valid character.')
return False
else:
# condition_print(f'{char} ({i}) is a valid character.')
pass
return True
def plc_write(self, command='WW', plc_no=0x01, pc_no=0xFF, address='D0160', len=0x02, data=0x00000000):
frame = self.plc_write_frame(command, plc_no, pc_no, address, len, data)
timeout = 2 # 设置超时时间为2秒
if hasattr(self, 'ser') and self.ser:
self.ser.write(frame)
frame = self.ser.read(1);
time.sleep(0.06)
start_time = time.time() # 记录开始时间
while self.ser.in_waiting:
# 检查是否超时
if time.time() - start_time > timeout:
condition_print("serial com read Time's out!--", time)
return {"status": "error", "msg": "serial com read Time's out!"}
break
frame += self.ser.read(self.ser.in_waiting) # 读取剩余的数据
if self.frame_is_valid(frame) == False:
condition_print("recv frame is not valid", '[', ', '.join('{:02x}'.format(x) for x in frame), ']')
return {"status": "error", "msg": "recv frame is not valid"}
else:
condition_print("--write data to plc success", "resp", ' [',
', '.join('{:02x}'.format(x) for x in frame), ']')
# condition_print('recv pld frame: [', ', '.join('{:02x}'.format(x) for x in frame), ']')
return {"status": "success", "data": frame}
else:
return {"status": "error", "msg": "serial com is not open!"}
pass
def plc_read(self, command, plc_no=0x01, pc_no=0xFF, address='D0160', len=0x01):
frame = self.plc_read_frame(command, plc_no, pc_no, address, len)
timeout = 2 # 设置超时时间为2秒
if hasattr(self, 'ser') and self.ser:
self.ser.write(frame)
frame = self.ser.read(1);
time.sleep(0.06)
start_time = time.time() # 记录开始时间
while self.ser.in_waiting:
# 检查是否超时
if time.time() - start_time > timeout:
condition_print("serial com read Time's out!--", time)
return {"status": "error", "msg": "serial com read Time's out!"}
break
frame += self.ser.read(self.ser.in_waiting) # 读取剩余的数据
if self.frame_is_valid(frame) == False:
condition_print("recv frame is not valid")
return {"status": "error", "msg": "recv frame is not valid"}
else:
condition_print('--read data return: [', ', '.join('{:02x}'.format(x) for x in frame), ']')
return {"status": "success", "data": frame}
else:
return {"status": "error", "msg": "serial com is not open!"}
def plc_read_words(self, plc_no=0x01, pc_no=0xFF, address='D0160', length=0x01):
resp = {'status': 'error'}
result = self.plc_read('WR', plc_no, pc_no, address, length)
if result and 'status' in result and result['status'] == 'success' and 'data' in result:
frame = result['data']
data_len = (len(frame) - 8) / 4
if data_len == length:
value = [ascii_array_to_word(frame[5 + 4 * i:5 + 4 * (i + 1)]) for i in range(length)]
resp = {'status': 'success', 'length': data_len, 'data': value}
return resp
def plc_read_bits(self, plc_no=0x01, pc_no=0xFF, address='D0160', length=0x01):
resp = {'status': 'error'}
result = self.plc_read('BR', plc_no, pc_no, address, length)
condition_print('plc_read_bits result', result) # cyx
if 'status' in result and result['status'] == 'success' and 'data' in result:
frame = result['data']
data_len = (len(frame) - 8) / 1
if data_len == length:
value = [(frame[5 + 1 * i] - 0x30) for i in range(length)]
resp = {'status': 'success', 'length': data_len, 'data': value}
return resp
def plc_write_word(self, plc_no=0x01, pc_no=0xFF, address='D0160', data=0x0000):
return self.plc_write('WW', plc_no, pc_no, address, 0x01, list([data]))
def plc_write_dword(self, plc_no=0x01, pc_no=0xFF, address='D0160', data=0x00000000):
return self.plc_write('WW', plc_no, pc_no, address, 0x02, list([data >> 16, data & 0xFFFF]))
def plc_write_words(self, plc_no=0x01, pc_no=0xFF, start_addr='D0160', length=0x04, data=[]):
return self.plc_write('WW', plc_no, pc_no, start_addr, length, data)
def plc_write_bits(self, plc_no=0x01, pc_no=0xFF, address='D0160', length=0x01, data=[]):
return self.plc_write('BW', plc_no, pc_no, address, length, data)
from pymodbus.datastore import ModbusSequentialDataBlock
class CustomDataBlock(ModbusSequentialDataBlock):
def __init__(self, data_type, plc_devices=None, plc_address=0x01, *args, **kwargs):
if plc_devices is None:
plc_devices = []
self.plc_address = plc_address # PLC的 站地址 如:1 3 4等
self.data_type = data_type # 区分coil input_register holding_register
self.address = 0 # 这个是client 进行读写的寄存器地址
self.plc_devices = plc_devices # plc 设备的列表,每一台机器对应一个
# plc_devices 中元素中的device 是 MitsubishiPLC的实例用于和物理PLC的通讯
# 机器的COM口相同的话会共享相同的MitsubishiPLC的实例
self.plc_comm_lock = threading.Lock() # 具体访问MitsubishiPLC的保护锁
super().__init__(*args, **kwargs)
def getValues(self, address, count):
plc_device_obj = [item for item in self.plc_devices if item["slave"] == self.plc_address] # 按照站地址进行匹配
if len(plc_device_obj) and plc_device_obj[0].get('device'):
plc_device = plc_device_obj[0].get('device') # 得到访问物理PLC的MitsubishiPLC的实例
if self.data_type == 'coil':
# 处理线圈数据类型的逻辑
print('read coils address=', address) # modbus client 要读的寄存器地址
if plc_device:
with self.plc_comm_lock: # 保护访问资源,执行完缩进的代码块释放资源
resp = plc_device.plc_read_bits(self.plc_address, 0xFF, conv_to_plc_address('M', address), count)
if resp and resp.get('status') == 'success' and resp.get('data') and resp.get('length'):
return resp['data']
else:
raise ModbusIOException("Modbus server read coil failed--PLC response error")
else:
if address == 18:
return [True, False, True]
else:
raise ModbusIOException("Modbus server read coil failed--PLC not connected")
pass
elif self.data_type == 'input_register':
print('read input register address=', address)
# 处理输入寄存器数据类型的逻辑
if plc_device:
with self.plc_comm_lock: # 保护访问资源,执行完缩进的代码块释放资源
resp = plc_device.plc_read_words(self.plc_address, 0xFF, conv_to_plc_address('D', address), count)
if resp and resp.get('status') == 'success' and resp.get('data') and resp.get('length'):
return resp['data']
else:
raise ModbusIOException("Modbus server read input register failed--PLC response error")
else:
if address == 185:
return [88, 89, 100]
else:
raise ModbusIOException("Modbus server read input register failed--PLC not connected")
pass
elif self.data_type == 'holding_register':
# 处理保持寄存器数据类型的逻辑
#print('read holding register address=', address,count)
if address == 10: # 10 作为特殊的寄存器用来返回PLC(物理)的连接状态
if plc_device:
return [plc_device.get_connect_state()]
else:
return [0]
if plc_device:
with self.plc_comm_lock: # 保护访问资源,执行完缩进的代码块释放资源
resp = plc_device.plc_read_words(self.plc_address, 0xFF, conv_to_plc_address('D', address), count)
if resp and resp.get('status') == 'success' and resp.get('data') and resp.get('length'):
return resp['data']
else:
raise ModbusIOException("Modbus server read holding register failed--PLC response error")
else:
if address == 185:
return [88, 89, 100]
else:
raise ModbusIOException("Modbus server read holding register failed--PLC not connected")
pass
def setValues(self, address, values):
plc_device_obj = [item for item in self.plc_devices if item["slave"] == self.plc_address]
if len(plc_device_obj) and plc_device_obj[0].get('device'):
plc_device = plc_device_obj[0].get('device')
if self.data_type == 'coil':
print("modbus server set coil values", address, values) # cyx
# 处理线圈数据类型的逻辑
if plc_device:
with self.plc_comm_lock: # 保护访问资源,执行完缩进的代码块释放资源
resp = plc_device.plc_write_bits(self.plc_address, 0xFF, conv_to_plc_address('M', address), 0x01,
values)
if resp and resp.get('status') == 'success' and resp.get('data') and resp['data'][0] == 0x06: # ACK
pass
else:
raise ModbusIOException("Modbus server set coil failed--PLC response error")
else:
# 如果读取不成功抛出一个Modbus异常
raise ModbusIOException("Modbus server set coil failed--PLC not connected")
elif self.data_type == 'input_register':
# 处理输入寄存器数据类型的逻辑
pass
elif self.data_type == 'holding_register':
print("modbus server set holding register address=", address, values, self.plc_address) # cyx
if plc_device:
with self.plc_comm_lock: # 保护访问资源,执行完缩进的代码块释放资源
resp = plc_device.plc_write_word(self.plc_address, 0xFF, conv_to_plc_address('D', address),
values[0])
if resp and resp.get('status') == 'success' and resp.get('data') and resp['data'][0] == 0x06: # ACK
pass
else:
raise ModbusIOException("Modbus server set holding register failed--PLC response error")
# 处理保持寄存器数据类型的逻辑
else:
# 如果读取不成功抛出一个Modbus异常
raise ModbusIOException("Modbus server set holding register failed--PLC not connected")
exit_flag = False
class ModbusPlcServer:
def __init__(self, host='127.0.0.1', port=5020, machines_config=None, timeout=3):
if machines_config is None:
machines_config = []
self.server_task = None
self.modbus_server = None
self.host = host
self.port = port
self.is_running = True # 服务器运行状态的标志变量
self.plc_devices = []
self.machines_config = machines_config
try:
for config in machines_config:
if config.get('type') == 'mhi' and config.get('com').startswith("com") and config.get('plcAddress'):
# 使用列表推导式生成列表
# 按照com 串口进行区分的如果1个RS485上有几个PLC, 实际因为只用一个COM口所以也就创建1个MitsubishiPLC
# modbus client 访问时会传入站地址由于区分是哪个PLC1 3 7 8等
device_matched = [device_json['device'] for device_json in self.plc_devices if
device_json["com"] == config.get('com')]
# 判断列表是否包含元素并据此取第一个元素或返回None
device_have_created = device_matched[0] if device_matched else None
if device_have_created is None:
device_have_created = MitsubishiPLC(config.get('com'), config.get('comm_config'))
self.plc_devices.append({'slave': int(config.get('plcAddress')), 'com': config.get('com'),
'device': device_have_created})
except Exception as error:
pass
async def run_server(self, ):
global serverUrl
"""
block = ModbusSequentialDataBlock(0x00, [0] * 0xff)
store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block)
context = ModbusServerContext(slaves=store, single=True)
identity = ModbusDeviceIdentification()
identity.VendorName = 'HYSZ Tech'
identity.ProductCode = 'DTMGT-01'
identity.VendorUrl = 'http://www.hysz.com'
identity.ProductName = 'DTMGT PLC Server'
identity.ModelName = 'DTMGT PLC Server'
identity.MajorMinorRevision = '1.0'
"""
stores = {}
# 创建 Modbus 数据块和上下文
context = None
for config in self.machines_config:
coil_block = CustomDataBlock('coil', self.plc_devices, int(config.get('plcAddress')), 0, [False] * 0x100)
holding_register_block = CustomDataBlock('holding_register', self.plc_devices,
int(config.get('plcAddress')), 0, [0] * 0x100)
input_coil_block = CustomDataBlock('input_colid', self.plc_devices, int(config.get('plcAddress')), 0,
[False] * 0x100)
input_register_block = CustomDataBlock('input_register', self.plc_devices, int(config.get('plcAddress')), 0,
[0] * 0x100)
store = ModbusSlaveContext(
di=input_coil_block,
co=coil_block,
hr=holding_register_block,
ir=input_register_block,
zero_mode=True
)
stores[int(config.get('plcAddress'))] = store
context = ModbusServerContext(slaves=stores, single=False) # 每个机器的modbus server 都有自己的上下文context (stores)
"""
coil_block = CustomDataBlock('coil', self.plc_devices, 1, 0, [False] * 0x100)
holding_register_block = CustomDataBlock('holding_register', self.plc_devices,
1, 0, [0] * 0x100)
input_coil_block = CustomDataBlock('input_colid', self.plc_devices, 1, 0,
[False] * 0x100)
input_register_block = CustomDataBlock('input_register', self.plc_devices, 1, 0,
[0] * 0x100)
store = ModbusSlaveContext(
di=input_coil_block,
co=coil_block,
hr=holding_register_block,
ir=input_register_block,
zero_mode=True
)
context = ModbusServerContext(slaves=store, single=True)
"""
# requests.post(serverUrl + '/modbusServerStarted', params={}, json={})
print(f"Modbus server starting on {self.host}:{self.port}")
try:
self.modbus_server = await StartAsyncTcpServer(context=context, address=(self.host, self.port))
print(f"Modbus server started on {self.host}:{self.port}")
self.modbus_server.serve_forever()
except Exception as error:
print("Failed to start the server:", error)
async def stop_server(self):
global exit_flag
if self.modbus_server:
self.modbus_server.shutdown()
self.modbus_server.serve_done.set_result(True)
self.server_task.cancel() # 取消任务
try:
await self.server_task # 等待任务结束
except:
pass
finally:
await self.modbus_server.wait_closed()
exit_flag = True
def getPlcConfig(serverUrl, params):
result = {}
query_string = params
response = requests.get(serverUrl + '/get_machine_config', params=query_string, json={})
if response.status_code == 200:
result = response.json()
else:
print('请求失败')
pass
return result
class ModbusServer:
def __init__(self, machines_config):
self.modbus_loop = None
self.modbus_server = None
self.machines_config = machines_config
def run_modbus_server(self):
print("mhi_plc create modbus server")
self.modbus_loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.modbus_loop)
self.modbus_server = ModbusPlcServer('127.0.0.1', 5020, self.machines_config)
self.modbus_loop.run_until_complete(self.modbus_server.run_server())
print("mhi_plc create modbus over")
def stop_modbus_server(self):
print("stop_modbus_server")
try:
if self.modbus_server:
self.modbus_server.stop_server()
print("modbus server stop ")
if hasattr(self, 'modbus_loop') and self.modbus_loop.is_running():
self.modbus_loop.call_soon_threadsafe(self.modbus_loop.stop)
print("modbus server event loop exit")
except Exception as error:
print("stop_modbus_servererror:", error)
modbus_server = None # 声明全局变量
exit_flag = False
def get_machine_configs(serverUrl='127.0.0.1:5050'):
global modbus_server, exit_flag
config_got = False
while not config_got:
try:
resp = getPlcConfig(serverUrl, {})
if resp.get('status') == 'success':
config_got = True
machine_config = resp.get('data', [])
modbus_server = ModbusServer(machine_config)
modbus_server.modbus_thread = threading.Thread(target=modbus_server.run_modbus_server)
modbus_server.modbus_thread.start()
except Exception as error:
pass
time.sleep(3)
pass
def main():
global modbus_server # 引用全局变量
global exit_flag
get_machine_configs_thread = threading.Thread(target=get_machine_configs, args=(args.serverUrl,))
get_machine_configs_thread.start()
def stop_server(): # 停止服务器的函数
global exit_flag
if modbus_server:
modbus_server.stop_modbus_server()
if modbus_server and modbus_server.modbus_thread:
modbus_server.modbus_thread.join()
get_machine_configs_thread.join()
exit_flag = True
try:
while not exit_flag:
if keyboard.is_pressed('z'):
#stop_server()
pass
time.sleep(0.1)
except KeyboardInterrupt:
print("用户强制退出Ctrl+C")
stop_server()
sys.exit(0)
if __name__ == "__main__":
# 创建解析器
parser = argparse.ArgumentParser(description="My modbus and plc Description")
# 添加参数
parser.add_argument('--serverUrl', default="http://127.0.0.1:5050", help="Server URL")
# 添加参数
parser.add_argument('--host', default="127.0.0.1", help="Seqrver URL")
# 添加参数
parser.add_argument('--port', default=5020, help="Server URL")
# 解析参数
args = parser.parse_args()
serverUrl = args.serverUrl
main()