DolphinDB Modbus协议接入:PLC数据采集

目录

    • 摘要
    • 一、Modbus协议概述
      • [1.1 什么是Modbus](#1.1 什么是Modbus)
      • [1.2 Modbus特点](#1.2 Modbus特点)
      • [1.3 Modbus寄存器类型](#1.3 Modbus寄存器类型)
    • [二、DolphinDB Modbus插件](#二、DolphinDB Modbus插件)
      • [2.1 插件安装](#2.1 插件安装)
      • [2.2 连接配置](#2.2 连接配置)
    • 三、数据读取
      • [3.1 读取线圈](#3.1 读取线圈)
      • [3.2 读取离散输入](#3.2 读取离散输入)
      • [3.3 读取输入寄存器](#3.3 读取输入寄存器)
      • [3.4 读取保持寄存器](#3.4 读取保持寄存器)
    • 四、数据写入
      • [4.1 写入线圈](#4.1 写入线圈)
      • [4.2 写入保持寄存器](#4.2 写入保持寄存器)
    • 五、数据解析
      • [5.1 整数解析](#5.1 整数解析)
      • [5.2 浮点数解析](#5.2 浮点数解析)
      • [5.3 字符串解析](#5.3 字符串解析)
    • 六、轮询采集
      • [6.1 定时轮询](#6.1 定时轮询)
      • [6.2 多设备轮询](#6.2 多设备轮询)
    • 七、实战案例
      • [7.1 PLC数据采集系统](#7.1 PLC数据采集系统)
    • 八、总结
    • 参考资料

摘要

本文深入讲解DolphinDB Modbus协议接入技术。从Modbus协议原理到插件配置,从寄存器读取到数据解析,从轮询采集到实时写入,全面介绍Modbus数据采集的核心方法。通过丰富的代码示例,帮助读者掌握PLC数据采集的核心技能。


一、Modbus协议概述

1.1 什么是Modbus

Modbus是工业领域广泛使用的通信协议:
#mermaid-svg-Ak1oOCimIXOM6Y2e{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ak1oOCimIXOM6Y2e .error-icon{fill:#552222;}#mermaid-svg-Ak1oOCimIXOM6Y2e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ak1oOCimIXOM6Y2e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .marker.cross{stroke:#333333;}#mermaid-svg-Ak1oOCimIXOM6Y2e svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ak1oOCimIXOM6Y2e p{margin:0;}#mermaid-svg-Ak1oOCimIXOM6Y2e .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster-label text{fill:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster-label span{color:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster-label span p{background-color:transparent;}#mermaid-svg-Ak1oOCimIXOM6Y2e .label text,#mermaid-svg-Ak1oOCimIXOM6Y2e span{fill:#333;color:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .node rect,#mermaid-svg-Ak1oOCimIXOM6Y2e .node circle,#mermaid-svg-Ak1oOCimIXOM6Y2e .node ellipse,#mermaid-svg-Ak1oOCimIXOM6Y2e .node polygon,#mermaid-svg-Ak1oOCimIXOM6Y2e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .rough-node .label text,#mermaid-svg-Ak1oOCimIXOM6Y2e .node .label text,#mermaid-svg-Ak1oOCimIXOM6Y2e .image-shape .label,#mermaid-svg-Ak1oOCimIXOM6Y2e .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ak1oOCimIXOM6Y2e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .rough-node .label,#mermaid-svg-Ak1oOCimIXOM6Y2e .node .label,#mermaid-svg-Ak1oOCimIXOM6Y2e .image-shape .label,#mermaid-svg-Ak1oOCimIXOM6Y2e .icon-shape .label{text-align:center;}#mermaid-svg-Ak1oOCimIXOM6Y2e .node.clickable{cursor:pointer;}#mermaid-svg-Ak1oOCimIXOM6Y2e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .arrowheadPath{fill:#333333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ak1oOCimIXOM6Y2e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ak1oOCimIXOM6Y2e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ak1oOCimIXOM6Y2e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster text{fill:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e .cluster span{color:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ak1oOCimIXOM6Y2e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ak1oOCimIXOM6Y2e rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ak1oOCimIXOM6Y2e .icon-shape,#mermaid-svg-Ak1oOCimIXOM6Y2e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ak1oOCimIXOM6Y2e .icon-shape p,#mermaid-svg-Ak1oOCimIXOM6Y2e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ak1oOCimIXOM6Y2e .icon-shape .label rect,#mermaid-svg-Ak1oOCimIXOM6Y2e .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ak1oOCimIXOM6Y2e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ak1oOCimIXOM6Y2e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ak1oOCimIXOM6Y2e :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Modbus架构
主站

DolphinDB
从站1

PLC
从站2

传感器
从站3

仪表
通信方式
Modbus TCP
Modbus RTU

1.2 Modbus特点

特点 说明
简单可靠 协议简单,稳定可靠
广泛应用 工业标准协议
多种传输 TCP/RTU/ASCII
开放免费 无授权费用

1.3 Modbus寄存器类型

类型 地址范围 访问 说明
线圈 00001-09999 读写 开关量输出
离散输入 10001-19999 只读 开关量输入
输入寄存器 30001-39999 只读 模拟量输入
保持寄存器 40001-49999 读写 模拟量输出

二、DolphinDB Modbus插件

2.1 插件安装

python 复制代码
// 检查插件
getLoadedPlugins()

// 加载Modbus插件
loadPlugin("modbus")

// 查看插件函数
modbus::getPluginFunctions()

2.2 连接配置

python 复制代码
// Modbus TCP连接
conn = modbus::connectTcp("192.168.1.100", 502)

// Modbus RTU连接(串口)
conn = modbus::connectRtu("/dev/ttyUSB0", 9600, "N", 8, 1)
// 参数:串口、波特率、校验位、数据位、停止位

三、数据读取

3.1 读取线圈

python 复制代码
// 连接Modbus设备
conn = modbus::connectTcp("192.168.1.100", 502)

// 设置从站ID
modbus::setSlaveId(conn, 1)

// 读取单个线圈(地址0)
coil = modbus::readCoil(conn, 0, 1)
print("线圈状态: " + string(coil[0]))

// 读取多个线圈(地址0-9)
coils = modbus::readCoil(conn, 0, 10)
print("线圈状态: " + string(coils))

3.2 读取离散输入

python 复制代码
// 读取离散输入(地址10001对应地址0)
inputs = modbus::readDiscreteInput(conn, 0, 8)
print("离散输入: " + string(inputs))

3.3 读取输入寄存器

python 复制代码
// 读取单个输入寄存器
value = modbus::readInputRegister(conn, 0, 1)
print("输入寄存器值: " + string(value[0]))

// 读取多个输入寄存器
values = modbus::readInputRegister(conn, 0, 10)
print("输入寄存器值: " + string(values))

3.4 读取保持寄存器

python 复制代码
// 读取保持寄存器
values = modbus::readHoldingRegister(conn, 0, 10)
print("保持寄存器值: " + string(values))

四、数据写入

4.1 写入线圈

python 复制代码
// 写入单个线圈
modbus::writeSingleCoil(conn, 0, true)   // 打开
modbus::writeSingleCoil(conn, 0, false)  // 关闭

// 写入多个线圈
modbus::writeMultipleCoils(conn, 0, [true, false, true, false])

4.2 写入保持寄存器

python 复制代码
// 写入单个保持寄存器
modbus::writeSingleRegister(conn, 0, 100)

// 写入多个保持寄存器
modbus::writeMultipleRegisters(conn, 0, [100, 200, 300])

五、数据解析

5.1 整数解析

python 复制代码
// 16位整数解析
def parseInt16(values, offset = 0) {
    return values[offset]
}

// 32位整数解析(两个寄存器)
def parseInt32(values, offset = 0) {
    return values[offset] * 65536 + values[offset + 1]
}

// 有符号整数
def parseSignedInt16(values, offset = 0) {
    v = values[offset]
    return iif(v >= 32768, v - 65536, v)
}

5.2 浮点数解析

python 复制代码
// 32位浮点数解析(IEEE 754)
def parseFloat32(values, offset = 0) {
    // 需要将两个16位寄存器转换为32位浮点数
    // 使用位操作
    high = values[offset]
    low = values[offset + 1]
    // 转换逻辑
    return float32fromBits(high * 65536 + low)
}

5.3 字符串解析

python 复制代码
// 字符串解析
def parseString(values, length) {
    chars = array(CHAR, length)
    for (i in 0..length) {
        chars[i] = char(values[i / 2] >> (8 * (i % 2)))
    }
    return concat(chars)
}

六、轮询采集

6.1 定时轮询

python 复制代码
// 创建流表
share streamTable(100000:0, 
    `device_id`timestamp`temperature`pressure`flow,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE]) as modbus_stream

// 轮询函数
def pollModbus(conn, slaveId, interval = 1000) {
    modbus::setSlaveId(conn, slaveId)
    
    while (true) {
        try {
            // 读取寄存器
            values = modbus::readInputRegister(conn, 0, 6)
            
            // 解析数据
            temperature = values[0] / 10.0
            pressure = values[1] / 10.0
            flow = values[2] / 10.0
            
            // 写入流表
            insert into modbus_stream 
            values ("device_" + string(slaveId), now(), temperature, pressure, flow)
            
        } catch (ex) {
            print("读取失败: " + ex)
        }
        
        sleep(interval)
    }
}

// 启动轮询
submitJob("modbus_poll", "Modbus轮询", 
    def() { pollModbus(conn, 1, 1000) })

6.2 多设备轮询

python 复制代码
// 多设备轮询
def pollMultipleDevices(conn, slaveIds, interval = 1000) {
    while (true) {
        for (slaveId in slaveIds) {
            try {
                modbus::setSlaveId(conn, slaveId)
                values = modbus::readInputRegister(conn, 0, 3)
                
                temperature = values[0] / 10.0
                pressure = values[1] / 10.0
                flow = values[2] / 10.0
                
                insert into modbus_stream 
                values ("device_" + string(slaveId), now(), temperature, pressure, flow)
                
            } catch (ex) {
                print("设备 " + string(slaveId) + " 读取失败")
            }
        }
        sleep(interval)
    }
}

// 启动多设备轮询
submitJob("modbus_multi_poll", "多设备轮询",
    def() { pollMultipleDevices(conn, [1, 2, 3, 4, 5], 1000) })

七、实战案例

7.1 PLC数据采集系统

python 复制代码
// ========== PLC数据采集系统 ==========

// 1. 创建分布式表
db = database("dfs://modbus_db", VALUE, 1..100)
schema = table(1:0, 
    `device_id`timestamp`temperature`pressure`flow_rate`level,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, DOUBLE])
db.createPartitionedTable(schema, `plc_data, `device_id)

// 2. 创建流表
share streamTable(100000:0, 
    `device_id`timestamp`temperature`pressure`flow_rate`level,
    [SYMBOL, TIMESTAMP, DOUBLE, DOUBLE, DOUBLE, DOUBLE]) as modbus_stream

// 3. 启用持久化
enableTablePersistence(modbus_stream, true, true, 1000000)

// 4. 订阅流表写入分布式表
subscribeTable(, "modbus_stream", "persist", -1,
    def(msg) {
        loadTable("dfs://modbus_db", "plc_data").append!(msg)
    }, 10000, 5000)

// 5. 连接Modbus设备
conn = modbus::connectTcp("192.168.1.100", 502)

// 6. 启动轮询
submitJob("plc_poll", "PLC轮询",
    def() {
        modbus::setSlaveId(conn, 1)
        while (true) {
            try {
                values = modbus::readInputRegister(conn, 0, 8)
                
                insert into modbus_stream values (
                    "PLC_001",
                    now(),
                    values[0] / 10.0,    // 温度
                    values[1] / 10.0,    // 压力
                    values[2] / 10.0,    // 流量
                    values[3] / 10.0     // 液位
                )
            } catch (ex) {
                print("读取失败: " + ex)
            }
            sleep(1000)
        }
    })

// 7. 监控
def monitorModbus() {
    print("=== Modbus监控 ===")
    print("流表行数: " + string(exec count(*) from modbus_stream))
    print("最新数据: ")
    select top 5 * from modbus_stream
}

monitorModbus()

print("PLC数据采集系统启动完成")

八、总结

本文详细介绍了DolphinDB Modbus协议接入:

  1. Modbus协议:寄存器类型、通信方式
  2. 插件配置:TCP连接、RTU连接
  3. 数据读取:线圈、离散输入、寄存器
  4. 数据写入:线圈写入、寄存器写入
  5. 数据解析:整数、浮点数、字符串
  6. 轮询采集:定时轮询、多设备轮询

思考题

  1. Modbus TCP和RTU有什么区别?
  2. 如何设计高效的Modbus轮询策略?
  3. 如何处理Modbus通信异常?

参考资料


相关推荐
普马萨特1 小时前
Wi-Fi (802.11) 协议演进
运维·服务器·网络
袁小皮皮不皮1 小时前
2.HCIP OSPF路由基础(优化版)
运维·服务器·网络·网络协议·智能路由器
非凡大爹1 小时前
实验十二 华为单臂路由实现 VLAN 间通信实验指导书
网络·计算机网络·华为
yunceqing1 小时前
从Excel调度到TMS平台:物流软件开发避坑清单
大数据·前端·网络·人工智能·excel·推荐算法
阿米亚波2 小时前
SSH+TCP流程及抓包说明
网络·笔记·网络协议·tcp/ip·计算机网络·wireshark·ssh
2501_941982052 小时前
Webhook 驱动:企业微信消息接收与自动回复
网络·机器人·自动化·企业微信
不吃土豆的马铃薯2 小时前
C++ 正则表达式入门详解
linux·服务器·网络·数据库·c++·正则表达式
艾莉丝努力练剑2 小时前
【Qt】界面优化:绘图API
linux·运维·开发语言·网络·qt·tcp/ip·udp
行走__Wz2 小时前
【网工入门-05】网络参考模型
网络