python获取modbus多线程版本,实现自动重连、端口出错程序不终止

该博客介绍了如何使用Python的modbus_tk库和serial库实现Modbus RTU协议的03功能码,通过多线程读取多个子站的 Holding Registers 数据。代码中包含了端口配置、异常处理和重试机制。同时,提供了注意事项,强调了端口统一和子站数据获取的处理方式。
摘要由CSDN通过智能技术生成

python实现modbus协议 03读取多个子站数据——多线程版本

  1. 提前准备 modbus_slave 这个软件,用于提供数据。
  2. 安装必要的第三方包
    pip install modbus_tk
    pip install serial
    协议包加串口包
    这版不带crc校验,如需验crc版,请留言。
  3. 先证明一下实现过程
    端口配置在这里插入图片描述
    实际获取
    在这里插入图片描述
    上代码
import serial
from modbus_tk import modbus_rtu
from modbus_tk import defines as cst
import time
import threading


class F():
    def __init__(self, list_id):
        self.port = 'com6'
        self.flags = 0
        self.my_serial_flag= [100 if i == 0 else 0 for i in range(len(list_id)+1)]
        self.my_serial =  None
        self.my_serial = (self.my_serial if self.my_serial else self.my_func_serial())
 
    def get_datas(self, slave_id, start_addr, date_len):
        try:
            holding_date = self.master.execute(slave_id, cst.READ_HOLDING_REGISTERS, start_addr, date_len)
            holding_data125 = list()
            for i in range(len(holding_date)):
                holding_data125.append('%04x' % (holding_date)[i])  # append()在Tmp1列表末尾添加新的对象
            all_holding_data = '0103' + '%02x' % (date_len * 2) + ''.join(holding_data125)
            # print('为线程显示', all_holding_data)
            self.my_serial_flag[slave_id] = 0
            return str(all_holding_data)
        except Exception as e:
            print('单个子站引发','不重连端口',slave_id)

            if self.my_serial_flag[slave_id] >= 10:
                # print(111)
                pass
            else:
                # print(222)
                self.my_serial_flag[slave_id] += 1
                # print(221)
            if all(v > 0 for v in self.my_serial_flag):
                self.flags = 1
            if self.flags:  # 如果flags被标记为1才去重新获取连接
                self.func(slave_id)
            return None

    def funb(self, slave_id):
        '''
        这个相当于与服务器的连接,这个可定义的
        :param slave_id:
        :return:
        '''

        while 1:
            try:
                time.sleep(2)
                da = self.get_datas(slave_id, 0, 64)
                print(da)

                #return  da
            except Exception as e:
                print(self.my_serial_flag)
    def my_func_serial(self):
        while 1:
            try:
                self.my_serial = serial.Serial(port=self.port,
                                               baudrate='38400',
                                               bytesize=8,
                                               parity='N',
                                               stopbits=1,
                                               xonxoff=0)
                self.master = modbus_rtu.RtuMaster(self.my_serial)
                self.master.set_timeout(1)

                return self.my_serial
            except Exception as e:
                print(e, '第一次端口不能成功建立', self.my_serial_flag)
                time.sleep(2)
                if self.my_serial:
                    self.my_serial.close()

    def func(self, slave_id):

        while 1:

            try:
                time.sleep(1)
                if self.flags:
                    self.my_serial = serial.Serial(port=self.port,
                                                   baudrate='38400',
                                                   bytesize=8,
                                                   parity='N',
                                                   stopbits=1,
                                                   xonxoff=0)
                    self.my_serial_flag[slave_id] = 1
                    self.master = modbus_rtu.RtuMaster(self.my_serial)
                    self.master.set_timeout(1)
                    self.flags = 0
                    return self.my_serial
                else:
                    pass
            except Exception as e:
                print(e, '引发整个端口重置', self.my_serial_flag)
                self.my_serial_flag[slave_id] += 1
                time.sleep(2)
                if self.my_serial:
                    self.my_serial.close()
def add_thread(func, *args):
    # 用于创建线程
    return threading.Thread(target=func, args=args)
def main(ids):
    threads_list = []
    f = F(ids)
    for i, j in enumerate(ids):
        i += 1
        threads_list.append(add_thread(f.funb, i))
    for i in threads_list:
        # print('执行线程')
        i.start()
if __name__ == '__main__':
    list_id = ['6101','6102','6103','5555']  #
    main(list_id)

注意项;就是要端口要统一,我端口只提供子站123,即便我向端口要子站4的数据,程序也可照常执行,当子站4有数据时,也可获取子站4数据。
在这里插入图片描述

好的,我来为你详细解答。 首先,我们需要导入相关的模块: ```python import threading import queue import time from pymodbus.client.sync import ModbusTcpClient ``` 接着,我们需要定义一些全局变量: ```python SERVER_IP = '192.168.1.100' # 服务器IP地址 SERVER_PORT = 502 # 服务器端口 DEVICE_IDS = [1, 2, 3] # 设备ID列表 READ_ADDRS = [0, 10, 20] # 读取地址列表 WRITE_ADDRS = [0, 10, 20] # 写入地址列表 READ_DATA = [0]*len(READ_ADDRS) # 读取数据列表 WRITE_DATA = [1]*len(WRITE_ADDRS) # 写入数据列表 THREAD_NUM = 3 # 线程池大小 ``` 其中,SERVER_IP 和 SERVER_PORT 分别为服务器的IP地址和端口,DEVICE_IDS 为需要连接的设备ID列表,READ_ADDRS 和 WRITE_ADDRS 分别为需要读取和写入的地址列表,READ_DATA 和 WRITE_DATA 分别为读取和写入的数据列表,THREAD_NUM 为线程池大小。 接下来,我们需要定义一个连接Modbus TCP服务器的函数: ```python def connect_to_server(): client = ModbusTcpClient(SERVER_IP, port=SERVER_PORT) client.connect() return client ``` 然后,我们需要定义一个读取数据的函数: ```python def read_data(device_id, read_addr, read_data): client = connect_to_server() data = client.read_holding_registers(read_addr, len(read_data), unit=device_id) client.close() if data.isError(): print(f"Device {device_id} read error: {data}") return False else: for i in range(len(read_data)): read_data[i] = data.registers[i] print(f"Device {device_id} read successfully: {read_data}") return True ``` 在这个函数中,我们首先连接到Modbus TCP服务器,然后使用设备ID读取指定地址的数据,并将结果保存到全局变量 READ_DATA 中。如果读取成功,则打印读取成功的信息并返回 True;否则,打印错误信息并返回 False。 接下来,我们需要定义一个写入数据的函数: ```python def write_data(device_id, write_addr, write_data): client = connect_to_server() response = client.write_registers(write_addr, write_data, unit=device_id) client.close() if response.isError(): print(f"Device {device_id} write error: {response}") return False else: print(f"Device {device_id} write successfully: {write_data}") return True ``` 在这个函数中,我们首先连接到Modbus TCP服务器,然后使用设备ID写入指定地址的数据,并将写入的数据保存到全局变量 WRITE_DATA 中。如果写入成功,则打印写入成功的信息并返回 True;否则,打印错误信息并返回 False。 接下来,我们需要定义一个线程函数: ```python def worker(): while True: task = task_queue.get() if task is None: break device_id, task_type, addr, data = task if task_type == 'read': read_data(device_id, addr, data) elif task_type == 'write': write_data(device_id, addr, data) task_queue.task_done() ``` 在这个函数中,我们首先从任务队列中获取任务,然后根据任务类型调用相应的函数。最后,标记任务为已完成。 最后,我们定义一个主函数: ```python if __name__ == '__main__': task_queue = queue.Queue() threads = [] for i in range(THREAD_NUM): t = threading.Thread(target=worker) t.start() threads.append(t) for device_id in DEVICE_IDS: for addr in READ_ADDRS: task_queue.put((device_id, 'read', addr, READ_DATA)) for addr in WRITE_ADDRS: task_queue.put((device_id, 'write', addr, WRITE_DATA)) task_queue.join() for i in range(THREAD_NUM): task_queue.put(None) for t in threads: t.join() ``` 在这个函数中,我们首先创建一个任务队列和多个线程,然后将读取和写入任务依次加入任务队列。接着,等待所有任务执行完成后,关闭所有线程。 这样,我们就成功地实现了在Python中使用线程池连接多个设备并进行写操作的功能。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值