金华金义东轨道建设网站,织梦中二次开发新的网站,我的校园网站制作,在线视频下载网站如何做文章目录 1.背景2.分析3.实现3.1.PLC的ModbusTCP_Server3.1.1.安装TF6250-Modbus-TCP3.1.2.PLC设置 3.2.智能相机的ModbusTCP_Client3.2.1.了解ModbusTCP的协议3.2.2.根据协议写代码3.2.2.1.纯函数代码3.2.2.2.脚本代码 3.2.3.非脚本处理时的代码逻辑图3.2.4.关于代码的问题及解… 文章目录 1.背景2.分析3.实现3.1.PLC的ModbusTCP_Server3.1.1.安装TF6250-Modbus-TCP3.1.2.PLC设置 3.2.智能相机的ModbusTCP_Client3.2.1.了解ModbusTCP的协议3.2.2.根据协议写代码3.2.2.1.纯函数代码3.2.2.2.脚本代码 3.2.3.非脚本处理时的代码逻辑图3.2.4.关于代码的问题及解答 4.总结 1.背景
目前有个需求要求康耐视智能相机Insight在每次触发完成作业后将一串字符串通过ModbusTCP发送至倍福的PLC中。此时PLC作为Modbus的server智能相机作为Modbus的client智能相机主动发送数据给PLC写PLC的Modbus的保持寄存器Holding Registers。 目前已经实现了效果如下 代码我已经上传到这里了【康耐视智能相机ModbusTCP发送字符串代码 】不需要下载积分。造福大家。
2.分析
主要有两部分的功能要实现PLC的ModbusTCP_Server的实现、智能相机的ModbusTCP_Client的实现。 有了server和client通讯起来就没啥问题了。至于其中涉及的实时性的问题暂时先不考虑。
3.实现
经过查阅资料倍福PLC作为ModbusTCP Server的设置过程很简单而智能相机的设置过程则非常麻烦。下面来详细介绍。
3.1.PLC的ModbusTCP_Server
3.1.1.安装TF6250-Modbus-TCP
根据从这里【TwinCAT 3 Modbus TCP使用方法】查阅得到的资料只要下载并安装TF6250-Modbus-TCP这个软件包即可。 TF6250-Modbus-TCP的下载地址为【TF6250 | TwinCAT 3 Modbus TCP】下载的时候需要先登录倍福的账号没有的话注册一个即可就是它要求的密码复杂度比较高要字母数字特殊符号大小写。 具体安装步骤可以查看前面提到的那个网站的【TwinCAT 3 Modbus TCP使用方法.docx】
3.1.2.PLC设置
首先先激活一下试用 然后写程序
PROGRAM MAIN
VARarr1 AT %MB0:ARRAY[1..2] OF WORD; (*保持寄存器 起始地址为12288 (0x3000)*)arr2 AT %MB10:ARRAY[1..2] OF BYTE; (**)str1 AT %MB0: STRING;byteArr1 AT %MB0:ARRAY[0..9] OF BYTE;byteArr2 AT %MB0:ARRAY[1..10] OF BYTE;
END_VAR为了方便观察这里准备了字符串以及byte数组这样子的话既可以看到字符串又可以看到字符串对应的16进制值。 至此ModbusTCPServer的设置已经完成了内存的映射等其他操作系统已经帮忙处理了。
这里需要注意的是modbus寄存器与PLC的地址对应关系。这个关系我们可以通过查阅官方文档【TF6250 | TwinCAT 3 Modbus TCP Default Configuration】得知 保持寄存器对应的是Output registers 所以我们读写保持寄存器时地址偏移要设置成122880x3000。前面的文档有说要1但是我测试不用加1也行可能具体得看实际情况吧。 ok倍福PLC这边已经设置完成了这时其实就已经可以用你趁手的Modbus调试工具测试一下与PLC的通讯了。我这边测试的话貌似slave ID设置成0、1或者其他任意值都可以。
3.2.智能相机的ModbusTCP_Client
智能相机这边就相当的麻烦了。因为我们用的相机的固件版本比较低不直接支持Modbus貌似新的固件版本也只是支持ModbusServer而非ModbusClient得靠我们自己通过TCPDevice根据Modbus的协议来手动编码。 行吧那就自己手动搞呗。
3.2.1.了解ModbusTCP的协议
首先了解一下ModbusTCP的协议【Modbus TCP协议说明】、【ModbusTCP数据帧】、【如何看懂Modbus数据帧】。了解其报文结构是非常重要的因为只有了解其协议规定我们才能确定为了达到我们的效果需要发送多长的数据每个数据又应该是如何取值。 简单地说我们可以总结modbusTCP的数据帧有以下几个特点 1.与通过串口发送的数据帧相比不用携带校验码。这个应该是因为底层TCP/IP通讯本身就已经是可靠通讯自带校验及重发。挺好我们不用再用CRC16算法来算效验码了。 2.与通过串口发送的数据帧相比要在前面增加7个字节的MBAP报文头。 3.报文头中的事务处理标识需要累加。实际上经过测试不累加也行。后面的程序我都没有对其累加。 3.2.2.根据协议写代码
ok我们动手。 经过测试将字符串转化成需要发送出去的数据这部分代码有两种实现方式一种是纯利用智能相机提供的函数进行处理另外一种是通过js脚本实现。 建议先用纯函数实现然后再用脚本实现这样理解起来会更加深刻。
3.2.2.1.纯函数代码
先直接放出程序再依次解释每个函数的作用。
左侧文字代码说明待发送的字符串EditString(255)字符串控件可以在运行时输入字符串字符串长度Len(J26)获取字符串的长度长度是否为奇数Mod(J27,2)判断是否为奇数减少最后一个字符的字符串Left(J26, J27-1)原来的字符串移除最后一个字符形成的字符串最后一个字符Right(J26,1)原来字符串的最后的那个字符偶数长度部分的字符串If(J28,J29,J26)原来字符串的的偶数长度部分的字符串比如字符串长度为5那么取前面4个假如为6那就全取。编码1(偶数部分的字符串)BStringf(0, “%~s”, J31)把字符串编码成Binary结构体编码22byte的0BStringf(0, “%h”, 0)长度为2且两个数据都是0的Binary结构体编码30字符BStringf(0, “%c%s”, 0, J30)长度为2第1个字节为0第2个字节为字符串最后一个字符的Binary结构体最终发送的编码(奇数长度时)BStringf(0,“%b%b”,J32,J34)字符长度为奇数时最终需要发送的Binary数据结构体偶数部分0最后一个字符最终发送的编码(偶数长度时)BStringf(0,“%b%b”,J32,J33)字符长度为偶数时最终需要发送的Binary数据结构体偶数部分 0 0实际需要发送的编码$If(J28,J35,J36)根据原始字符串的长度是奇数还是偶数确定发送的Binary数据结构体需要发送的编码长度BLen(J37)实际需要发送的编码的长度计算Modbus的数据长度1 1 2 2 1 J381设备ID1字节功能码2字节寄存器地址2字节寄存器数量1字节寄存器数据长度寄存器数据本身的长度手动触发发送Button(“触发”,-1)按钮触发发送设备TCPDevice(“127.0.0.1”,502,0,4,1000,255)设备IP127.0.0.1端口502modbus二进制发送读写QueryDevice(M29,M30,0,0,0,0,0,M26,0,0x10,0x30,0,0,J38/2,J38,J37)发送数据且读取接收到的数据。M26是【计算Modbus的数据长度】0是slaveID0x10表示写保持寄存器0x30 0x00表示PLC的寄存器地址 J38/2表示寄存器的数量因为我们的数据长度是以字节为单位的而寄存器是16bit大小的可以存储2个字节因此要除以2 J38要写到n个寄存器的数据的大小,J37要写到n个寄存器的数据。
3.2.2.2.脚本代码 部分代码解释和上一小节一样主要解释一下脚本部分
左侧文字代码说明将字符串通过脚本处理得到编码Script($J$47)通过脚本处理字符串。脚本的输入参数为字符串返回处理完成的Binary结构体
脚本截图
脚本
function Script() {
}
module.exports Script;function stringToUint8ArrayWithSwap(str) {var dataArrLen str.length;if (str.length % 2 ! 0) {dataArrLen 1; // 奇数就补一个0}else{dataArrLen 2; // 偶数就补两个0}var dataArray new Uint8Array(dataArrLen); // 数据会默认初始化为0for (var i 0; i str.length; i) {dataArray[i] str.charCodeAt(i); // 将字符拷贝到数组中} // 每两个数据一组交换组中的元素for (var i 0; i dataArrLen; i 2) {// 交换位置 i 和 i1 的数字[dataArray[i], dataArray[i 1]] [dataArray[i 1], dataArray[i]];}return dataArray;
}Script.prototype.run function (arg0) {return stringToUint8ArrayWithSwap(arg0)
}
3.2.3.非脚本处理时的代码逻辑图 3.2.4.关于代码的问题及解答 1.为啥要根据字符串长度的奇偶来执行不同的处理办法 因为我们最终执行的modbus功能是写保持寄存器而保持寄存器的大小为2字节每次写的话都只能写n个寄存器n为整数也就是2n个字节的数据。所以每次写的数据必须是偶数个数据才能填充完n个寄存器。不允许写半个寄存器。 2.为啥要 BStringf(0, “%~s”, J31) 中要选 %~s这种格式 因为假如选了%s这种格式的话数据是按小端发送过去的但是PLC那边接收到数据是按大端处理的这就导致你发了字符串123456过去PLC那边存储且显示出来的是214365,每两个字节内部互相交换。 3.既然是大小端的问题为啥不通过设置 BStringf中的第一个参数来处理 因为它只对数值型数据比如double、int等类型起作用对字符串这种连续、独立、可变长度的类型无效。只能选%~s这种格式来处理。 3.字符长度为奇数时为啥要将最后一个字符单独拿出来然后插入一个0然后再把这个字符补回去。 首先补0是必须的因为任何字符串都需要用0来作为结束符不然无法确定一段字符在何处结束。至于为啥要把这个0补在最后一个字符的前面插队还是前面说的大小端的问题。我们发送过去的数据PLC那边会每两个字节交替存储比如说问哦我们发了 0x00 0x01两个数据过去存在PLC寄存器的顺序会自动调整为0x01 0x00而我们的0x00需要存放在物理地址的高位也就是后面因此需要做一个插入操作。 4.总结
通过底层的方式实现数据传输学是能够学到好多东西但是就是贼麻烦且鲁棒性差。不知道为啥Modbus这么通用的功能康耐视智能相机为啥就是不支持。 参考 【TwinCAT 3 Modbus TCP使用方法】 【Modbus TCP协议说明】 【ModbusTCP数据帧】 【如何看懂Modbus数据帧】