IOTOS物联中台从0到1开发modbus_tcp驱动 实例详解

本文详细介绍了如何使用Python的modbus-tk库实现ModbusTCP驱动,用于连接和通信ModbusTCP设备。文章涵盖了驱动的目的、适用范围、代码实现、数据解析以及使用示例,特别强调了数据点配置、格式处理和私有化解析的细节。通过这个驱动,可以实现从设备上读取数据并在爱投斯物联中台上展示。
部署运行你感兴趣的模型镜像

 本文章为原创,转载请注明出处!

登录平台:IOTOS®爱投斯物联中台

账号:iotos_test    密码:iotos123

代码地址:IOTOSDK-Python: IOTOS Python版本SDK,自带原生接口和采集引擎 (gitee.com)

目录

零、前言

一、驱动目的

二、适用范围

三、驱动代码 

四、驱动详解

导入相应的包,运行环境为python2.7

定义补码转化为负数函数

获取设备有关参数

连接modbus tcp设备

获取数据点配置参数

格式处理

发送指令、获取数据

私有化解析

五、使用示例

创建网关

创建并配置设备

创建及配置数据点

运行

 运行结果


零、前言

        Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为Modbus Master,从设备方使用的协议称为Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。

        Modbus TCP/IP协议,去掉了Modbus协议本身的CRC校验,增加了MBAP 报文头。TCP/IP上的Modbus的请求/响应。

一、驱动目的

          modbus_tcp驱动是将中台(爱投斯物联中台)作为客户端向服务端即modbus tcp通讯的设备发送询问帧,服务端通讯接收到询问帧并返回应答帧给到客户端,客户端再对其进行解析工作并展示数据。

二、适用范围

  1. modbus_tcp驱动文件适用于一般的modbus_tcp设备,且设备作为服务器端 ;

  2. 设备与中台服务器在同一网段,即设备与中台服务器可以相互ping通

三、驱动代码 

# -*- coding:utf-8 -*-
# author : jiji time 12/10/2021
# Modbus tcp server
import sys
import modbus_tk.modbus_tcp as modbus_tcp
from modbus_tk.exceptions import ModbusInvalidResponseError
reload(sys)
sys.setdefaultencoding('utf8')

sys.path.append("..")
from driver import *

# 补码转为负数
def Complement2Negative(int_data):
    data = '0b'
    bin_data = bin(int_data).split('0b')[1]
    print bin(int_data)
    if len(bin_data) < 16:
        return int_data
    else:
        for i in bin_data:
            if i == '1':
                data += '0'
            if i == '0':
                data += '1'
        return (int(data,2)+1)*-1

class ModbusTCPDriver(IOTOSDriverI):
    #1、通信初始化
    def InitComm(self,attrs):

        self._HOST = self.sysAttrs['config']['param']['HOST']
        self._PORT = self.sysAttrs['config']['param']['PORT']
        self._master = modbus_tcp.TcpMaster(host=self._HOST, port=int(self._PORT))
        self._master.set_timeout(5.0)
        self.setPauseCollect(False)
        self.setCollectingOneCircle(False)
        self.online(True)

    # #2、采集引擎回调,可也可以开启,也可以直接注释掉(对于主动上报,不存在遍历采集的情况)
    def Collecting(self, dataId):
        try:
            cfgtmp = self.data2attrs[dataId]['config']
            # 过滤非modbus tcp配置的点
            if not cfgtmp.has_key('param') or not cfgtmp.has_key('proxy'):
                return ()

            # 当是新一组功能号时;当没有proxy.pointer,或者有,但是值为null时,就进行采集!否则(有pointer且值不为null,表明设置了采集代理,那么自己自然就被略过了,因为被代理了)当前数据点遍历轮询会被略过!
            if 'pointer' not in cfgtmp['proxy'] or cfgtmp['proxy']['pointer'] == None or cfgtmp['proxy']['pointer'] == '':
                # 某些过滤掉不采集,因为有的地址的设备不在线,只要在proxy下面配置disabled:true,这样就不会轮训到它!
                if 'disabled' in cfgtmp['proxy'] and cfgtmp['proxy']['disabled'] == True:
                    return ()
                else:
                    self.warn(self.name(dataId))

            # 过滤非modbus rtu配置的点
            if not cfgtmp['param'].has_key('funid'):
                return ()

            # 功能码
            funid = cfgtmp['param']['funid']
            # 设备地址
            devid = cfgtmp['param']['devid']
            # 寄存器地址
            regad = cfgtmp['param']['regad']
            # 格式
            format = cfgtmp['param']['format']
            # 长度
            quantity = re.findall(r"\d+\.?\d*", format)
            if len(quantity):
                quantity = int(quantity[0])
            else:
                quantity = 1
            if format.lower().find('i') != -1:  # I、i类型数据为4个字节,所以n个数据,就是4n字节,除一般应对modbus标准协议的2字节一个数据的个数单位!
                quantity *= 4 / 2
            elif format.lower().find('h') != -1:
                quantity *= 2 / 2
            elif format.lower().find('b') != -1:
                quantity *= 1 / 2
            elif format.find('d') != -1:
                quantity *= 8 / 2
            elif format.find('f') != -1:
                quantity *= 4 / 2
            elif format.find('?') != -1:  # 对于功能号1、2的开关量读,开关个数,对于这种bool开关型,个数就不是返回字节数的两倍了!返回的字节个数是动态的,要字节数对应的位数总和,能覆盖传入的个数数值!
                quantity *= 1
                format = ''  # 实践发现,对于bool开关型,传入开关量个数就行,format保留为空!如果format设置为 "?"或"8?"、">?"等,都会解析不正确!!
            self.debug(
                '>>>>>>' + '(PORT-' + str(self._PORT) + ')' + str(devid) + ' ' + str(funid) + ' ' + str(regad) + ' ' + str(
                    quantity) + ' ' + str(format))
            rtu_ret = self._master.execute(devid, funid, regad, quantity, data_format=format)
            self.debug(rtu_ret)

            # 私有modbus解析
            if cfgtmp['param'].has_key('private'):
                # 温湿度传感器
                if cfgtmp['param']['private'] == 'Temp&Hum':
                    data_list = []
                    for i in rtu_ret:
                        data_list.append(Complement2Negative(i)*0.1)
                    rtu_ret = tuple(data_list)

            return rtu_ret
        except ModbusInvalidResponseError, e:
            self.error(u'MODBUS响应超时, ' + e.message)
            return None
        except Exception, e:
            traceback.print_exc(e.message)
            self.error(u'采集解析参数错误:' + e.message)
            return None

四、驱动详解

  • 导入相应的包,运行环境为python2.7

import sys
import modbus_tk.modbus_tcp as modbus_tcp
from modbus_tk.exceptions import ModbusInvalidResponseError
reload(sys)
sys.setdefaultencoding('utf8')

sys.path.append("..")
from driver import *
  • 定义补码转化为负数函数

# 补码转为负数
def Complement2Negative(int_data):
    data = '0b'
    bin_data = bin(int_data).split('0b')[1]
    print bin(int_data)
    if len(bin_data) < 16:
        return int_data
    else:
        for i in bin_data:
            if i == '1':
                data += '0'
            if i == '0':
                data += '1'
        return (int(data,2)+1)*-1
  • 获取设备有关参数

self._HOST = self.sysAttrs['config']['param']['HOST']
self._PORT = self.sysAttrs['config']['param']['PORT']
  • 连接modbus tcp设备

self._master = modbus_tcp.TcpMaster(host=self._HOST, port=int(self._PORT))
self._master.set_timeout(5.0)
  • 获取数据点配置参数

cfgtmp = self.data2attrs[dataId]['config']
# 过滤非modbus tcp配置的点
if not cfgtmp.has_key('param') or not cfgtmp.has_key('proxy'):
    return ()

# 当是新一组功能号时;当没有proxy.pointer,或者有,但是值为null时,就进行采集!否则(有pointer且值不为null,表明设置了采集代理,那么自己自然就被略过了,因为被代理了)当前数据点遍历轮询会被略过!
if 'pointer' not in cfgtmp['proxy'] or cfgtmp['proxy']['pointer'] == None or cfgtmp['proxy']['pointer'] == '':
    # 某些过滤掉不采集,因为有的地址的设备不在线,只要在proxy下面配置disabled:true,这样就不会轮训到它!
    if 'disabled' in cfgtmp['proxy'] and cfgtmp['proxy']['disabled'] == True:
        return ()
    else:
        self.warn(self.name(dataId))

# 过滤非modbus rtu配置的点
if not cfgtmp['param'].has_key('funid'):
    return ()

# 功能码
funid = cfgtmp['param']['funid']
# 设备地址
devid = cfgtmp['param']['devid']
# 寄存器地址
regad = cfgtmp['param']['regad']
# 格式
format = cfgtmp['param']['format']
  • 格式处理

# 长度
quantity = re.findall(r"\d+\.?\d*", format)
if len(quantity):
    quantity = int(quantity[0])
else:
    quantity = 1
if format.lower().find('i') != -1:  # I、i类型数据为4个字节,所以n个数据,就是4n字节,除一般应对modbus标准协议的2字节一个数据的个数单位!
    quantity *= 4 / 2
elif format.lower().find('h') != -1:
    quantity *= 2 / 2
elif format.lower().find('b') != -1:
    quantity *= 1 / 2
elif format.find('d') != -1:
    quantity *= 8 / 2
elif format.find('f') != -1:
    quantity *= 4 / 2
elif format.find('?') != -1:  # 对于功能号1、2的开关量读,开关个数,对于这种bool开关型,个数就不是返回字节数的两倍了!返回的字节个数是动态的,要字节数对应的位数总和,能覆盖传入的个数数值!
    quantity *= 1
    format = ''  # 实践发现,对于bool开关型,传入开关量个数就行,format保留为空!如果format设置为 "?"或"8?"、">?"等,都会解析不正确!!
self.debug('>>>>>>' + '(PORT-' + str(self._PORT) + ')' + str(devid) + ' ' + str(funid) + ' ' + str(regad) + ' ' + str(quantity) + ' ' + str(format))
  • 发送指令、获取数据

rtu_ret = self._master.execute(devid, funid, regad, quantity, data_format=format)
  • 私有化解析

# 私有modbus解析
if cfgtmp['param'].has_key('private'):
    # 温湿度传感器
    if cfgtmp['param']['private'] == 'Temp&Hum':
        data_list = []
        for i in rtu_ret:
            data_list.append(Complement2Negative(i)*0.1)
        rtu_ret = tuple(data_list)

五、使用示例

         基础网关、模板、设备、数据点创建可点击>>采集引擎SDK的使用过程<<查看。

  • 创建网关

         填写网关名称 -> 点击确定。

  • 创建并配置设备

         填写设备名称 -> 填写驱动信息 -> 填写配置(modbus_tcp设备的IP地址 、modbus_tcp设备的端口号 ) -> 确定。  

  • 创建及配置数据点

        数据点配置param属性。

  • 运行

         在SDK的_examples文件夹里创建.bat文件,并运行。

py -2 iotosEngine.py --u 物联中台用户名 --p 物联中台密码 --i 网关全局标识 --h http://sys.aiotos.net
  •  运行结果

        中台数据展示

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IOTOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值