| 作者 | 主题 |
|---|---|
|
万泉河 至圣
经验值:29190 发帖数:10900 精华帖:131 |
楼主
主题:【MODBUS】跑在以太网上的MODBUS 【modbus】跑在以太网上的modbus
微信公众号:PLC标准化编程,ZHO6371995
|
|
疆拓自动化 侠客 经验值:771 发帖数:295 精华帖:3 |
26楼
主题:回复:【MODBUS】跑在以太网上的MODBUS
我是自己用VB写的采集驱动,然后在WINCC建立内部变量(内部变量不算点),采集驱动采集到数据后,通过HMIruntime库函数写wincc的内部变量(我的大致有1万多个点,其实只需要买最低点数的wincc即可)
那阵子我观察了下,一般这种和上位机的通讯都需要在电脑上建立虚拟串口,但是建立虚拟串口太多,我的需要建208个虚拟串口,力控软件根本就支持不了这么多的串口设备。通过观察,我发现电脑跟串口跟服务器的通讯使用的依然是TCP的报文,我截取了报文看了,只是把串口的查询字符串和返回数据的字符串给封装了。 我自己尝试着用vb 使用winsock控件写了一下,发现是可以的,这样我的程序放到任何一个电脑上就可以直接使用,不需要建立虚拟串口了。用vb写的话,要使用它默认的端口号 961~968。是TCP报文 不是modbus-TCP,我用VB写比较方便,获得报文自己分析就可以了。 其实这么多表通讯,用网关是最直接的,由于种种原因,没办法。 代码大部分跟modbusrtu的代码差不多,附点部分代码吧,真要做完善还是要花时间的,我花了好多天的时间了,现在基本上正常用了。 Dim outputLen As Integer ' 发送数据长度 Dim outData As String ' 发送数据暂存 Dim SendArr() As Byte ' 发送数组 Dim TemporarySave As String ' 数据暂存 Dim dataCount As Integer ' 数据个数计数 Dim i As Integer ' 局部变量 outData = UCase(Replace(TxtSend, Space(1), Space(0))) ' 先去掉空格,再转换为大写字母 outData = UCase(outData) ' 转换成大写 outputLen = Len(outData) ' 数据长度 For i = 0 To outputLen - 1 TemporarySave = Mid(outData, i + 1, 1) ' 取一位数据 If (Asc(TemporarySave) >= 48 And Asc(TemporarySave) <= 57) Or (Asc(TemporarySave) >= 65 And Asc(TemporarySave) <= 70) Then dataCount = dataCount + 1 Else Exit For Exit Sub End If Next i If dataCount Mod 2 <> 0 Then ' 判断十六进制数据是否为双数 dataCount = dataCount - 1 ' 不是双数,则减1 End If outData = Left(outData, dataCount) ' 取出有效的十六进制数据 ReDim SendArr(dataCount / 2 - 1) ' 重新定义数组长度 For i = 0 To dataCount / 2 - 1 SendArr(i) = Val("&H" + Mid(outData, i * 2 + 1, 2)) ' 取出数据转换成十六进制并放入数组中 Next Main.WinsockClient(Index).SendData SendArr() ' 发送数据 SlaveNow(Index) = SlaveNow(Index) + 1 '从站依次轮询 If SlaveNow(Index) > SlaveNum(Index) Then SlaveNow(Index) = 1 End If ============================================================================== Private Sub WinsockClient_DataArrival(Index As Integer, ByVal bytesTotal As Long) On Error Resume Next Dim strData() As Byte WinsockClient(Index).GetData strData() '若接收的数据总长度 和 数据长度加上地址吗功能码以及校验码之和 不相等 说明接收到的数据不正确 就可以丢弃了 If (bytesTotal < 10) Then DataOK(Index) = True DataSendCounter(Index) = 0 Exit Sub End If GoodData(Index) = "" For i = 0 To bytesTotal - 1 If strData(i) < 16 Then GoodData(Index) = GoodData(Index) & "0" & Hex(strData(i)) Else GoodData(Index) = GoodData(Index) & Hex(strData(i)) End If Next i '************************************************************************ '************************CRC16校验*************************************** Dim CRC_A As String Dim CRC_B As String Dim Txt1 As String Dim Txt2 As String Dim H1, H2 As String Txt1 = UCase(Replace(GoodData(Index), Space(1), Space(0))) Txt2 = Mid(Txt1, 1, Len(Txt1) - 4) H1 = Mid(Txt1, Len(Txt1) - 3, 2) H2 = Mid(Txt1, Len(Txt1) - 1, 2) Call CRC_16(Txt2, CRC_A, CRC_B) If (H1 <> CRC_A) Or (H2 <> CRC_B) Then DataOK(Index) = True DataSendCounter(Index) = 0 End If '************************CRC16校验*************************************** '************************************************************************ '现在开始判断仪表类型 'bytesTotal=105 ---SCK600B多功能电表(电流电压电能等) 'bytesTotal=13 or 14 ---安科瑞的电表 或者是电操的状态返回信号 'bytetotal= 21 ----斯菲尔电能表 'bytesTotal >= 40 And <= 56 ------SCK600B多功能电表 或者 SCK600A火灾报警 'bytesTotal >=109 ------PF3000电表处理 Dim CongZhanNumber As Integer Dim DuanKou As Integer Dim IP As Integer Dim ShuJuA As Integer Dim ShuJuB As Integer Dim ShuJuC As Integer Dim ShuJuD As Integer Dim F(30) As Single Dim B(4) As Byte '///////获取表数据的公共部分////////////////////////////////////////// CongZhanNumber = CLng("&H" & Mid(GoodData(Index), 1, 2)) DuanKou = WinSockPort(Index) - 960 IP = CInt(Right(WinSockHostName(Index), 3)) '///////获取表数据的公共部分////////////////////////////////////////// '///////SCK600B 多功能电表处理////////////////////////////////////////// If (bytesTotal = 105) Then '基本的过程是:先找出从站编号,数据处理需要根据电表的数据格式来组合,然后分别找出12个电表数据, For i = 1 To 21 ShuJuA = CLng("&H" & Mid(GoodData(Index), i * 7 + (i - 1), 2)) ShuJuB = CLng("&H" & Mid(GoodData(Index), i * 7 + (i - 1) + 2, 2)) ShuJuC = CLng("&H" & Mid(GoodData(Index), i * 7 + (i - 1) + 4, 2)) ShuJuD = CLng("&H" & Mid(GoodData(Index), i * 7 + (i - 1) + 6, 2)) B(0) = ShuJuA B(1) = ShuJuB B(2) = ShuJuC B(3) = ShuJuD ' 根据实际情况 进行选择, 1234 4321 2143 3412 CopyMemory ByVal (VarPtr(F(i)) + 0), B(3), 1 CopyMemory ByVal (VarPtr(F(i)) + 1), B(2), 1 CopyMemory ByVal (VarPtr(F(i)) + 2), B(1), 1 CopyMemory ByVal (VarPtr(F(i)) + 3), B(0), 1 Next i Ua(Index, DuanKou, CongZhanNumber) = F(1) Ub(Index, DuanKou, CongZhanNumber) = F(2) Uc(Index, DuanKou, CongZhanNumber) = F(3) Ia(Index, DuanKou, CongZhanNumber) = F(7) Ib(Index, DuanKou, CongZhanNumber) = F(8) Ic(Index, DuanKou, CongZhanNumber) = F(9) P(Index, DuanKou, CongZhanNumber) = F(13) Q(Index, DuanKou, CongZhanNumber) = F(17) PF(Index, DuanKou, CongZhanNumber) = F(19) EP(Index, DuanKou, CongZhanNumber) = F(21) End If '//////////////////////SCK600B 多功能电表处理////////////// .......................................... 还有就是,我这光安科瑞的电能表就有800多块了,分布在50多层大楼里面,电表的地址都是缺省的,需要一个个的去改地址,但是电表本身没有任何按键,只能通过modscan等软件一个个的改,而且拿着笔记本一个个的楼层跑,麻烦死了,我跑了几层,实在受不了了(要接线,还要笔记本的电池有电),就让人把每个楼层的表的地址按照顺序记下来,自己写了一个软件,在电脑上搜索单条总线的从站,搜到后按照他们记得地址顺序改成自己需要的地址,很方便很快捷,以下是软件界面:(直接点击搜索到的从站,输入你想要的地址就可以直接成功修改) ![]() ![]()
常年奔波,是为的以后不奔波...............
|
|
疆拓自动化 侠客 经验值:771 发帖数:295 精华帖:3 |
27楼
主题:回复:【MODBUS】跑在以太网上的MODBUS
介绍一款软件 vspdconfig 自己搜索下,网上很多的,可以虚拟串口的,这样就可以自己模拟了 用一个modbusmaster 一个modbusslave 看报文啥的很方便,不需要硬件了 。
要是找不到留下邮箱,我发给你。
常年奔波,是为的以后不奔波...............
|