设备接入
把一台现场设备接入 IoT DC3,本质是六步:按协议选一个驱动 → 建模板与位号 → 建设备并绑定模板和驱动 → 为设备填位号属性的具体值 → 为设备填驱动属性的具体值 → 启用后确认设备在线、数据可查。读完这页,你能用内置的 dc3-driver-virtual 跑通一次完整接入,并把同样的步骤套到真实协议上。
你在这里:已了解 核心概念(驱动/模板/设备/位号、三层配置),现在动手接入第一台设备。更快的" 复制粘贴"上手版见 第一个设备。
先决定:用哪个驱动
接入的第一个决策是按设备说的协议挑驱动。驱动(Driver / dc3-driver-*)是协议适配实例——它知道怎么和某一类设备通信,并把" 这类设备/位号需要哪些配置项"注册到管理中心。选错协议,后面的模板和位号都对不上。
平台内置 36 个驱动,覆盖工业现场总线、IoT 无线、数据库桥接和基础通信。下面这张图按协议把常见选择收敛到一个驱动模块:
选不准时的两条经验
- 先跑虚拟驱动:
dc3-driver-virtual会按配置生成合成值,不依赖任何真实设备,是验证"模板→设备→位号→数据可查" 整条链路最快的方式,也是写新驱动的模板工程。 - 数据方向决定模式:平台主动去"读"设备(轮询采集)用
dc3-driver-virtual这类常规驱动;外部系统主动往平台"推"数据,用反向监听的dc3-driver-listening-virtual(TCP6270/ UDP6271)。
完整的 36 个驱动清单(工业/IoT/数据库/计量/仿真分类)见 驱动开发 与模块地图。下文用 dc3-driver-virtual 走一遍真实接入。
接入的数据走向
理解一台设备"接入成功"意味着什么,要先看它产生的值怎么流到可查询的地方。设备侧的原始值由驱动按协议读出、归一成 PointValue ,经 RabbitMQ 进数据中心(Data Center / dc3-center-data)落库,最终通过网关(Gateway / dc3-gateway,对外唯一入口,端口 8000)对外可查。
所以"接入成功"的判据不是"驱动启动了",而是这条链路打通、能在数据中心查到这台设备最新的位号值。这条数据平面的交换机、队列、TTL 等细节见 数据与命令。
第 0 步:起栈与驱动注册
接入前先确保依赖与中心服务就绪,并启动目标驱动:
- 已启动 PostgreSQL、RabbitMQ 与核心中心服务(鉴权/管理/数据中心)。
- 本地源码运行时,先加载环境变量:
source dc3/env/dev.env.sh(让本地 Java 进程指向 Compose 暴露在localhost的服务,详见 环境变量)。 - 启动至少一个驱动,例如虚拟驱动。
java -jar dc3-driver/dc3-driver-virtual/target/dc3-driver-virtual.jar驱动启动时,DriverInitRunner 会编排 注册 → initial() → schedule() 三步:通过 gRPC 向管理中心提交 RegisterBO ,带上驱动编码、名称、服务信息、租户,以及它声明的全部属性定义(attribute)。注册失败会按指数退避自动重试(2–30 秒,最多 30 次)。
dc3.driver.code 是稳定路由标识
驱动编码 dc3.driver.code 是消息路由与设备归属的稳定标识,注册后不可随意更改(改了等于换了一个驱动,已绑定的设备会失联)。每个驱动实例的 code 必须唯一且稳定。
如果驱动迟迟没出现在驱动列表,按这张表排查注册失败的常见原因:
| 现象 | 处理方式 |
|---|---|
| 管理中心未启动 | 先启动 Manager Center,再重启驱动 |
CENTER_MANAGER_HOST 指向错误 | 检查 dc3/env/dev.env(.sh) 或 IDE 环境变量 |
| 驱动编码重复 | 保持 dc3.driver.code 唯一且稳定 |
| RabbitMQ 未就绪 | 等健康检查通过后重启驱动 |
第 1–5 步:黄金路径接入
下面用网关 HTTP 接口走一遍。所有写接口都经网关 :8000 转发,受保护接口需带鉴权头 X-Auth-Tenant / X-Auth-Login / X-Auth-Token(先 POST /api/v3/auth/token/salt 取盐,再 POST /api/v3/auth/token/generate 取 token,有效期 12 小时,详见黄金路径登录流程)。注意 X-Auth-Token 的值必须是 JSON 信封 {"salt":"...","token":"..."}(盐与令牌都来自 登录响应)——直接传裸 token 会被网关判 401。先把它定义成变量,后续示例统一引用;示例里的 ID、名称为示例值。
H_TOKEN='X-Auth-Token: {"salt":"<登录返回的盐>","token":"<登录返回的令牌>"}'1. 建模板(Profile)
为同类设备建一个能力模板。模板沉淀"这类设备有哪些位号、命令、事件",设备复用它即可。
curl -X POST http://localhost:8000/api/v3/manager/profile/add \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"profileName":"virtual-motor","profileShareFlag":"TENANT","enableFlag":"ENABLE"}'
# 响应:新建的 ProfileVO,直接含 id(后续步骤的 profileId 就取它)
# 想核对时可调 POST /api/v3/manager/profile/list 按 profileName 查回profileShareFlag 取 ProfileShareTypeEnum(TENANT / DRIVER / USER),决定模板的共享范围。
2. 在模板下建位号(Point)
位号(Point)是一个数据项。关键字段是数据类型 pointTypeFlag 与读写方向 rwFlag——某个位号能不能写**由它自己的 rwFlag 决定 **,不是命令表决定。可选的 baseValue / multiple 把原始值线性换算成工程值,unit 标单位。
curl -X POST http://localhost:8000/api/v3/manager/point/add \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"pointName":"temperature","pointTypeFlag":"DOUBLE","rwFlag":"READ_WRITE",
"profileId":"<上一步的 profileId>","valueDecimal":2,"unit":"celsius","enableFlag":"ENABLE"}'
# 响应:新建的 PointVO,直接含 id(后续步骤的 pointId 就取它)
# 想核对时可调 /api/v3/manager/point/list 按 pointName 查回pointTypeFlag 取 PointTypeEnum(STRING / BYTE / SHORT / INT / LONG / FLOAT / DOUBLE / BOOLEAN,共 8 种);rwFlag 取 RwTypeEnum(READ_ONLY / WRITE_ONLY / READ_WRITE)。
3. 建设备(Device)并绑定模板与驱动
设备(Device)是现场一台具体设备的平台镜像,它绑定一个模板(决定有哪些位号)和一个驱动(决定怎么通信)。
curl -X POST http://localhost:8000/api/v3/manager/device/add \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"deviceName":"motor-01","driverId":"<virtual 驱动 id>",
"profileId":"<profileId>","enableFlag":"ENABLE"}'
# 响应:HTTP 201 + 新建的 DeviceVO,直接含 id(后续步骤的 deviceId 就取它)
# 想核对时可调 /api/v3/manager/device/list 按 deviceName 查回设备创建后,驱动通过元数据事件(DriverMetadataListener.event(...) 收 ADD/UPDATE/DELETE)感知变更并刷新缓存,多数情况无需重启驱动。
4. 为设备配置位号属性(Attribute 的 Config 值)
这一步最容易混淆,必须区分两个概念:
Attribute 是驱动注册的"有哪些",Config 是设备实例填的"具体值"
- 属性 Attribute(
PointAttribute/DriverAttribute等):驱动启动时从自己的application.yml注册的协议层配置项——它声明"这个驱动的位号需要哪些配置项"(如 Modbus 的寄存器地址、virtual 的取值范围)。你不创建 attribute,它随驱动注册而来。 - 配置 Config(
PointAttributeConfigDO等):这台设备给上述每个 attribute 填的具体值——如"motor-01 的 temperature 位号,寄存器地址填 40001"。这一步就是在填 Config。
三层配置(业务层 Param / 协议层 Attribute / 实例层 Config)的完整说明见 核心概念。
为设备上某个位号的某个属性写入实例值,调用 POST /api/v3/manager/point_attribute_config/add:
curl -X POST http://localhost:8000/api/v3/manager/point_attribute_config/add \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"attributeId":"<驱动注册的位号属性 id>","deviceId":"<deviceId>",
"pointId":"<pointId>","configValue":"40001","enableFlag":"ENABLE"}'
# 响应:新建的配置实体(含 id),直接取用即可attributeId 来自驱动注册的属性列表;configValue 是这台设备实例填的值。每个驱动声明的属性集不同——virtual 驱动声明的是取值范围之类的合成参数,Modbus 驱动声明的是寄存器/地址之类的协议参数。
用真实现场参数校准位号
对工业协议,重点核对:寄存器/地址/对象 ID/topic 是否正确、数据类型与字节序、倍率与单位是否匹配现场、读写方向是否与设备能力一致、采集周期是否与设备性能匹配。这些都落在 configValue 上。
5. 为设备配置驱动属性(设备级 Config)
位号属性之外,驱动自身声明的属性(DriverAttribute,如连接的 host/port)同样要逐设备填值——这是容易漏掉的一步。 调用 POST /api/v3/manager/driver_attribute_config/add:
curl -X POST http://localhost:8000/api/v3/manager/driver_attribute_config/add \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"attributeId":"<驱动注册的驱动属性 id>","deviceId":"<deviceId>",
"configValue":"localhost","enableFlag":"ENABLE"}'
# 响应:新建的配置实体(含 id),直接取用即可virtual 驱动注册了 host / port 两个驱动属性(默认值 localhost / 18600,照抄即可);真实协议驱动填真实连接参数(如 Modbus 的 IP 与端口)。attributeId 来自驱动注册的属性列表,可用 GET /api/v3/manager/driver_attribute/list_by_driver_id 按驱动查回。控制台里这一步对应设备编辑页的驱动信息表单。
不填驱动属性,设备既不会被读取、也不会上线
驱动的调度读取与健康上报都有设备级驱动属性门禁:读取任务只调度驱动属性配置非空的设备,健康任务在该配置为空时直接跳过上报 ——设备会一直停在离线。而建设备(device/add)不会自动生成任何驱动属性配置,必须显式走上面这一步。virtual 的 host/port 虽是占位值,也照样要填:门禁只看"有没有配置",不看值是什么。
第 6 步:启用后确认设备在线、数据可查
接入的终点是确认链路打通。启用设备后等待一个采集周期,按"状态 → 数据 → 日志"的顺序确认。
先看数据——能查到最新位号值,就说明整条链路通了。经网关查这台设备的最新值:
curl -X POST http://localhost:8000/api/v3/data/point_value/latest \
-H "X-Auth-Tenant: default" -H "X-Auth-Login: dc3" -H "$H_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"deviceId":"<deviceId>","offset":0,"limit":20}'{
"data": {
"offset": 0, "limit": 20, "total": 1, "hasNext": false,
"items": [
{
"deviceId": "...", "pointId": "...", "driverId": "...", "tenantId": "...",
"rawValue": "23.71", "calValue": "23.71", "numValue": 23.71,
"hasLatestValue": true,
"createTime": "2026-06-22T08:30:00", "operateTime": "2026-06-22T08:30:00"
}
]
}
}POST /api/v3/data/point_value/latest 返回偏移分页的 OffsetPage<PointValueVO>(分页字段是平铺的 offset / limit, 默认 limit=50、上限 200;每条含 deviceId / pointId / driverId / tenantId / rawValue 原始值 / calValue 工程值 / numValue 数值投影(可空)/ hasLatestValue / createTime / operateTime,时间为本地日期时间);要按时间窗翻历史值用 POST /api/v3/data/point_value/list(游标分页,翻页把响应里的 nextCursor 回填到下一次请求的 cursor)。完整的读写命令链路见 数据与命令。
没数据时,按这条链路反向排查,每一跳都对应上面那张数据走向图:
- 驱动状态:驱动是否在线?设备健康状态是否
ONLINE?注意设备状态租约由健康任务按dc3.driver.health.device.cron(默认0/15,即 15 秒心跳)续期,租约超时 TTL(timeout,默认 45 秒)必须大于心跳周期(出厂范式即 3× 心跳);TTL 短于心跳间隔,设备就会在两次上报之间被判离线。 - 驱动日志:有没有协议连接错误(连不上 host/port、寄存器越界、认证失败)。
- RabbitMQ:队列是否有积压或绑定异常,说明驱动发出去了但数据中心没消费。
- 数据中心:是否收到该设备的位号值消息。
- 租户一致性:设备、模板、位号、属性配置的
tenantId是否一致;跨租户访问会返回 404 而非数据。 - 属性配置:分两级查——驱动属性配置是否缺失(缺失则设备根本不会被读取、也不上报在线,见第 5 步的门禁说明); 位号属性的
configValue是否缺失或格式不符合驱动期望(如地址填了非法字符串)。
设备一直显示离线?先查状态 TTL 与驱动属性
最常见的"启用了却查不到值"有两个原因:一是驱动属性配置没填——健康任务直接跳过该设备,设备永远不上报在线(见上文门禁); 二是状态 TTL 配小了——租约按心跳周期(默认 15 秒)续期,TTL(默认 45 秒)短于心跳间隔就会在两次上报之间过期,设备被判离线。 保持 TTL 不小于心跳周期的 2–3 倍即可。
跑通虚拟驱动这一遍后,把同样的六步套到真实协议上:唯一变化的是第 0 步选的驱动模块、以及第 4/5 步每个驱动声明的位号/驱动属性集不同。