python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯

一、三菱FX5U 从站设置

        1. 打开GX works3 软件

        2. 新建项目

        3.按向导流程指示设置modbus-tcp从站功能

                python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯_第1张图片                                 python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯_第2张图片

                 python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯_第3张图片

python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯_第4张图片

 python modbus-tk 实现三菱FX5U modbus-tcp 从站通讯_第5张图片

         4.下载模块参数到PLC,并断电重启PLC。

注意:部分PLC会发生模块参数报警,需要PLC做固件升级。

二、PC端设置

        1. pip install modbus-tk,struct

        2. main.py

import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp
from shuJuZhuangHuang import WriteDint as wd
import os
import tkinter as tk
import tkinter.font as tkFont
import tkinter.ttk as ttk
import csv
from socket import *
import sys
import threading
import time,random,queue  
import pickle
import re
from configparser import ConfigParser
logger = modbus_tk.utils.create_logger("console")

# 读取配置文件
cfg = ConfigParser()
cfg.read('config.ini')
SERVER= cfg.get('Server','IP')
PORT= cfg.getint('Server','PORT')
MACROFILE = cfg.get('File','macroFile')
MACROCOPY = cfg.get('File','macroCopy')

timeSave = os.path.getmtime(MACROFILE)


# 打包函数
def thread_it(func,*args):
    '''将函数打包进程'''
    # 创建进程
    t = threading.Thread(target=func,args=args)
    # 守护进程
    t.setDaemon(True)
    # 启动
    t.start()


class GuiPart():  
    def __init__(self):
        self.threadFlg1 = True
        self.caiLiao = 0
        self.jiaGongZongShu = 0
        self.gongJianChanDu = 0
        self.qiShiWeiZhi1 = 0
        self.jieShuWeiZhi1 = 0
        self.qiShiWeiZhi2 = 0
        self.jieShuWeiZhi2 = 0
        self.qiShiWeiZhi3 = 0
        self.jieShuWeiZhi3 = 0
        self.qiShiWeiZhi4 = 0
        self.jieShuWeiZhi4 = 0
        self.tuiHuoCiShu = 0
        self.xieRuFlg = 0 # 写入允许-0 禁止-1

        # 连接MODBUS TCP从机
        try:
            master = modbus_tcp.TcpMaster(host=SERVER,port=PORT)
            master.set_timeout(5.0)
            self.master = master
        except modbus_tk.modbus.ModbusError as e:
            logger.error("%s- Code=%d" % (e, e.get_exception_code()))

        thread_it(self.readMacro)
        self.guiProcess()


    # 调整屏幕
    def center_window(self,root,w,h):
        '''
        窗口居于屏幕中央
        :param root: root
        :param w: 窗口宽度
        :param h: 窗口高度
        :return:
        '''
        # 获取屏幕 宽、高
        ws = root.winfo_screenwidth()
        hs = root.winfo_screenheight()
        # 计算x、y位置
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        root.geometry('%dx%d+%d+%d' %(w,h,x,y))


    def send(self):
        try:
            # # 写寄存器起始地址为500的保持寄存器,操作寄存器个数为1
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 500 , output_value=[self.caiLiao]))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 604, output_value=[self.jiaGongZongShu]))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 608, output_value=wd(self.gongJianChanDu)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4606, output_value=wd(self.qiShiWeiZhi1)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4608, output_value=wd(self.jieShuWeiZhi1)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4610, output_value=wd(self.qiShiWeiZhi2)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4612, output_value=wd(self.jieShuWeiZhi2)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4614, output_value=wd(self.qiShiWeiZhi3)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4616, output_value=wd(self.jieShuWeiZhi3)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4618, output_value=wd(self.qiShiWeiZhi4)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4620, output_value=wd(self.jieShuWeiZhi4)))
            logger.info(self.master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 4622, output_value=[self.tuiHuoCiShu]))
        except modbus_tk.modbus.ModbusError as e:
            logger.error("%s- Code=%d" % (e, e.get_exception_code()))


    def reset(self):
        print('reset')
        self.threadFlg1 = True 
        thread_it(self.readMacro)


    def readMacro(self):
        global timeSave
        
        while self.threadFlg1:
            time.sleep(0.2)
            # 读取PLC M4606 写入标志
            try:
                self.xieRuFlg = self.master.execute(2, cst.READ_COILS, 9228, 1)[0]
            except modbus_tk.modbus.ModbusError as e:
                logger.error("%s- Code=%d" % (e, e.get_exception_code()))

            """
            # print("flg: " )
            self.xieRuFlg = 1
            print(self.xieRuFlg)
            """

            # 如果写入标志==0可以写入,否则禁止更新。
            if self.xieRuFlg == 0:
                timeLast = os.path.getmtime(MACROFILE)
                # print(timeLast,' > ',timeSave)
                if timeLast > timeSave:
                    # print('time change')
                    timeSave = timeLast
                    os.system(r'copy %s %s'%(MACROFILE,MACROCOPY)) 
                    f = open (MACROCOPY,'r')
                    fileLines = f.readlines()
                    f.close()
                    # 读取数据
                    for item in fileLines:
                        patt = r'(\d+)\D+?((-)?\d+\.?\d{0,3})' # 匹配 500 = 34 500=34 500 = 34. 500 =34.5 500 = -34.565 不完全匹配 34.34352345
                        m = re.match(patt,item)
                        if m:
                            shapu = m.group(1)
                            data = m.group(2)
                        # 读取变量
                            if shapu == '500':
                                self.caiLiao = int(data)
                            elif shapu == '501':
                                self.jiaGongZongShu = int(data)
                            elif shapu == '502':
                                self.gongJianChanDu = int(float (data) * 10000)
                            elif shapu == '503':
                                self.qiShiWeiZhi1 = int(float (data) * 10000)
                            elif shapu == '504':
                                self.jieShuWeiZhi1 =int(float (data) * 10000)
                            elif shapu == '505':
                                self.qiShiWeiZhi2 =int(float (data) * 10000)
                            elif shapu == '506':
                                self.jieShuWeiZhi2 =int(float (data) * 10000)
                            elif shapu == '507':
                                self.qiShiWeiZhi3 =int(float (data) * 10000)
                            elif shapu == '508':
                                self.jieShuWeiZhi3 =int(float (data) * 10000)
                            elif shapu == '509':
                                self.qiShiWeiZhi4 =int(float (data) * 10000)
                            elif shapu == '510':
                                self.jieShuWeiZhi4 =int(float (data) * 10000)
                            elif shapu == '511':
                                self.tuiHuoCiShu = int(data)
                    self.send()
                    self.l_0_state.configure(bg = 'green')
                    self.varState.set("数据已更新!")

            else:
                #设置标签为红色
                # print("help")
                self.l_0_state.configure(bg = 'red')
                self.varState.set("设备运行中,禁止写入。设备停止后自动更新数据,请注意!")

        self.threadFlg1 = False


    # 定义Gui界面
    def guiProcess(self):
        '''
        GUI主界面
        :param
        :retuen
        '''
        root=tk.Tk()  
        self.root = root
        # 设置窗口位置
        root.title('********* V1')
        self.center_window(root,400,120)
        root.resizable(0,0)#  窗体大小可调整,分别表示x、y方向的可变性

        # 设置字体
        ft20 =tkFont.Font(family = 'Fixdsys',size = 20,weight = tkFont.BOLD)
        ft30 =tkFont.Font(family = 'Fixdsys',size = 30,weight = tkFont.BOLD)
        ft40 =tkFont.Font(family = 'Fixdsys',size = 40,weight = tkFont.BOLD)
        ft50 =tkFont.Font(family = 'Fixdsys',size = 50,weight = tkFont.BOLD)
        ft55 =tkFont.Font(family = 'Fixdsys',size = 55,weight = tkFont.BOLD)
        ft80 =tkFont.Font(family = 'Fixdsys',size = 80,weight = tkFont.BOLD)

        # 设置标签窗体
        labelFm1= tk.LabelFrame(root)
        labelFm1.pack(padx=5,pady=5,side=tk.TOP,fill=tk.X)
        self.labelFm1 = labelFm1

        # labelFm1
        self.varState = tk.StringVar()
        self.varState.set('*'*10)
        l_0_state = tk.Label(labelFm1,textvariable =self.varState )
        l_0_state.grid(row=0,column=0,padx=5,pady=5)
        self.l_0_state = l_0_state 
        self.l_0_state.configure(bg = 'green')

        button1 = tk.Button(labelFm1,text= "RESET" ,font=ft20)
        button1.grid(row=3,column=0,padx = 5,pady=5)
        self.button1 = button1

        # 定义事件响应
        button1.configure(command=self.reset) # 系统复位

        root.mainloop() 

        self.threadFlg1 = False
        print('self.threadFlg1 = ',self.threadFlg1)


if __name__=='__main__':
    ui = GuiPart()

         3. shuJuZhuangHuang.py   作为数据转换模块使用

import struct

def ReadFloat(*args,reverse=False):
    for n,m in args:
        n,m = '%04x'%n,'%04x'%m
    if reverse:
        v = n + m
    else:
        v = m + n
    y_bytes = bytes.fromhex(v)
    y = struct.unpack('!f',y_bytes)[0]
    y = round(y,6)
    return y

def WriteFloat(value,reverse=False):
    y_bytes = struct.pack('!f',value)
    # y_hex = bytes.hex(y_bytes)
    y_hex = ''.join(['%02x' % i for i in y_bytes])
    n,m = y_hex[:-4],y_hex[-4:]
    n,m = int(n,16),int(m,16)
    if reverse:
        v = [n,m]
    else:
        v = [m,n]
    return v

def ReadDint(*args,reverse=False):
    for n,m in args:
        n,m = '%04x'%n,'%04x'%m
    if reverse:
        v = n + m
    else:
        v = m + n
    y_bytes = bytes.fromhex(v)
    y = struct.unpack('!i',y_bytes)[0]
    return y

def WriteDint(value,reverse=False):
    y_bytes = struct.pack('!i',value)
    # y_hex = bytes.hex(y_bytes)
    y_hex = ''.join(['%02x' % i for i in y_bytes])
    n,m = y_hex[:-4],y_hex[-4:]
    n,m = int(n,16),int(m,16)
    if reverse:
        v = [n,m]
    else:
        v = [m,n]
    return v

if __name__ == "__main__":
    print(ReadFloat((15729,16458)))
    print(WriteFloat(3.16))
    print(ReadDint((1734,6970)))
    print(WriteDint(456787654))

        4. 添加配置文件 config.ini

;config.ini

[Server]
IP=192.168.3.39
PORT=502

[File]
macroFile=c:\\macro\\macro.txt
macroCopy=macro.txt

        5.添加macro.txt文本

500 = 2
501 = 3
502 = 40.03
503 = 5.5
504 = 6.6
505 = 7.8
506 = 8.8
507 = 9.6
508 = 8.7
509 = 7.7
510 = 6.7
511 = 3

       6.添加指令定义

#modbus exception codes
ILLEGAL_FUNCTION = 1
ILLEGAL_DATA_ADDRESS = 2
ILLEGAL_DATA_VALUE = 3
SLAVE_DEVICE_FAILURE = 4
COMMAND_ACKNOWLEDGE = 5
SLAVE_DEVICE_BUSY = 6
MEMORY_PARITY_ERROR = 8

#supported modbus functions
RAW = 0
READ_COILS = 1
READ_DISCRETE_INPUTS = 2
READ_HOLDING_REGISTERS = 3
READ_INPUT_REGISTERS = 4
WRITE_SINGLE_COIL = 5
WRITE_SINGLE_REGISTER = 6
READ_EXCEPTION_STATUS = 7
DIAGNOSTIC = 8
REPORT_SLAVE_ID = 17
WRITE_MULTIPLE_COILS = 15
WRITE_MULTIPLE_REGISTERS = 16
READ_FILE_RECORD = 20
READ_WRITE_MULTIPLE_REGISTERS = 23
DEVICE_INFO = 43

#supported block types
COILS = 1
DISCRETE_INPUTS = 2
HOLDING_REGISTERS = 3
ANALOG_INPUTS = 4

你可能感兴趣的:(modbus-tcp,python,tcp/ip,开发语言)