Skip to content

第一个设备:端到端

这页带你用平台自带的 virtual(虚拟)驱动走通一条完整链路:从登录拿 token,到建模板、建位号、建设备、配属性,再到读实时位号值、下发写命令。每一步都给可复制命令和"你应当看到",照着做即可。

你在这里:已经起好依赖栈、理清了核心概念。读完这页,你将拥有:一个由 virtual 驱动接入的设备,能看到它产生的实时位号值,并能对可写位号下发写命令。

这条路径长什么样

整条黄金路径是一串前后依赖的 HTTP 调用,全部经过网关 dc3-gateway:8000)这唯一入口。前两步换到 token,中间四步在管理中心(Manager Center)建好元数据,最后几步在数据中心(Data Center)读值与下发命令。先有这张全景图,后面每一步你都知道自己走到哪了。

约定

下文所有 id、token、返回值都是示例——你环境里生成的是雪花 ID(一长串数字),请用上一步真实返回的值替换。每个写接口返回的都是平台统一信封 { "ok": true, "code": "...", "message": "...", "data": "..." },新建实体的 ID 在 data 字段里。

第 0 步:起栈

先把数据库、消息队列和开发栈拉起来。这两条命令分别启动 PostgreSQL + RabbitMQ 依赖,以及网关 + 四个中心 + 驱动的开发栈(virtual 驱动随栈一起启动)。

bash
make up-db && make up-dev

你应当看到podman ps 列出 dc3-postgresdc3-rabbitmqdc3-gatewaydc3-center-auth/manager/data/agentic 以及若干 dc3-driver-* 容器处于运行态。网关在 http://localhost:8000 可达。

用 dc3 CLI 时先指向网关

如果你用 dc3 CLI,先告诉它网关地址(只需一次):dc3 config set gateway http://localhost:8000

第 1–2 步:登录拿 token

登录分两步:先用租户 + 用户名取盐(salt,5 分钟有效),再用盐把密码哈希后换取访问 token(12 小时有效)。拿到 token 后,后续所有受保护请求都要带三个鉴权头:X-Auth-TenantX-Auth-LoginX-Auth-Token

bash
# 1) 取盐
curl -s -X POST http://localhost:8000/api/v3/auth/token/salt \
  -H 'Content-Type: application/json' \
  -d '{"tenant":"default","name":"dc3"}'
# 示例返回:{"ok":true,"code":"...","message":"...","data":"a1b2c3d4e5"}

# 2) 用盐把密码哈希后换 token(哈希算法见鉴权文档,此处 PASSWORD_HASH 为示例)
curl -s -X POST http://localhost:8000/api/v3/auth/token/generate \
  -H 'Content-Type: application/json' \
  -d '{"tenant":"default","name":"dc3","salt":"a1b2c3d4e5","password":"<PASSWORD_HASH>"}'
# 示例返回:{"ok":true,"code":"...","message":"...","data":"<ACCESS_TOKEN>"}
bash
# CLI 封装了取盐 + 哈希 + 换 token 的全过程
dc3 auth login --tenant default --username dc3
# 交互式输入密码;登录后 token 自动保存

# 验证
dc3 auth status
dc3 auth token --header   # 打印 X-Auth-Tenant/X-Auth-Login/X-Auth-Token

你应当看到/api/v3/auth/token/salt 返回一个非空 salt 字符串;/api/v3/auth/token/generate 返回一个长 token 字符串(即上面的 <ACCESS_TOKEN>)。CLI 路径下 dc3 auth status 显示已登录。

后续请求都要带鉴权头

下文 curl 为简洁起见把三个头抽成变量,请先在 shell 里设好(值用你上一步真实拿到的):

bash
H_TENANT='X-Auth-Tenant: default'
H_LOGIN='X-Auth-Login: dc3'
H_TOKEN='X-Auth-Token: <ACCESS_TOKEN>'   # 示例

第 3 步:确认 virtual 驱动已注册

virtual 驱动随 make up-dev 启动后,会把自己注册到管理中心。建设备时要用它的 driverId,所以先把它查出来。

bash
curl -s -X POST http://localhost:8000/api/v3/manager/driver/list \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"page":{"current":1,"size":20}}'
bash
dc3 driver list

你应当看到:列表里有一个 driverNameVirtual Driver(带空格,来自驱动 application.ymldc3.driver.name;其 driverCode 是路由标识 VirtualDriver,模块/服务名是 dc3-driver-virtual——三者分属不同字段)的驱动,记下它的 id —— 后面记作 <DRIVER_ID>(示例:92010100000000001)。virtual 是驱动编写模板,专用于测试与新驱动起步,无需连任何真实设备即可产生数据。

第 4 步:加模板(Profile)

模板描述一类设备有哪些能力。这里建一个最小模板,位号挂在它下面。profileShareFlagTENANT(租户内共享),enableFlagENABLE(启用)。

bash
curl -s -X POST http://localhost:8000/api/v3/manager/profile/add \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"profileName":"虚拟温控模板","profileShareFlag":"TENANT","enableFlag":"ENABLE"}'
# 示例返回:{"ok":true,"code":"...","data":"81010100000000001"}
bash
dc3 profile create --name "虚拟温控模板"

你应当看到:返回 data 为新建模板 ID,记作 <PROFILE_ID>(示例:81010100000000001)。

profileShareFlag 取值

ProfileShareTypeEnumTENANT / DRIVER / USER,决定该模板在租户内、驱动内还是用户内共享。

第 5 步:加位号(Point)

位号是要采集或写入的数据项。能不能写由位号自己的 rwFlag 决定——这里建一个 READ_WRITE 的可写位号,后面才能对它下发写命令。pointTypeFlagFLOAT,挂到上一步的模板上,并带单位 °C

bash
curl -s -X POST http://localhost:8000/api/v3/manager/point/add \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "pointName":"温度",
        "pointTypeFlag":"FLOAT",
        "rwFlag":"READ_WRITE",
        "profileId":"81010100000000001",
        "valueDecimal":2,
        "unit":"°C",
        "enableFlag":"ENABLE"
      }'
# 示例返回:{"ok":true,"code":"...","data":"82010100000000001"}
bash
dc3 point create --name "温度" --profile-id "81010100000000001"

你应当看到:返回新建位号 ID,记作 <POINT_ID>(示例:82010100000000001)。

rwFlag 与 pointTypeFlag 取值

RwTypeEnumREAD_ONLY / WRITE_ONLY / READ_WRITE;对 READ_ONLY 位号下发写命令会被拒绝。PointTypeEnum 共 8 个值:STRING / BYTE / SHORT / INT / LONG / FLOAT / DOUBLE / BOOLEAN。位号还可带换算(baseValue / multiple),把原始值线性变换成工程值。

第 6 步:加设备(Device)

设备是绑定了一个模板和一个驱动的具体实例。用上面的 <DRIVER_ID><PROFILE_ID> 建设备。

bash
curl -s -X POST http://localhost:8000/api/v3/manager/device/add \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "deviceName":"虚拟温控设备-01",
        "driverId":"92010100000000001",
        "profileId":"81010100000000001",
        "enableFlag":"ENABLE"
      }'
# 示例返回:{"ok":true,"code":"...","data":"83010100000000001"}
bash
dc3 device create --name "虚拟温控设备-01" \
  --driver-id "92010100000000001" \
  --profile-id "81010100000000001"

你应当看到:返回新建设备 ID,记作 <DEVICE_ID>(示例:83010100000000001)。

第 7 步:配置位号属性

驱动在启动时声明了它有哪些配置项(属性,Attribute);这一步是为这台设备的这个位号给某个属性填具体值(配置,Config)。这就是把"位号"真正接到驱动采集逻辑上的那一刀。attributeId 来自 virtual 驱动注册的属性(可在驱动详情或属性列表里查到),configValue 是给该属性的值。

bash
curl -s -X POST http://localhost:8000/api/v3/manager/point_attribute_config/add \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "attributeId":"91010100000000001",
        "deviceId":"83010100000000001",
        "pointId":"82010100000000001",
        "configValue":"25.0",
        "enableFlag":"ENABLE"
      }'
# 示例返回:{"ok":true,"code":"...","data":"84010100000000001"}
bash
# CLI 暂无独立的属性配置子命令,请用上面的 curl

你应当看到:返回新建配置 ID。配置生效后,virtual 驱动开始为该位号产生值。

attributeId 从哪来

attributeId 指向 virtual 驱动在管理中心注册的某个位号属性(PointAttribute)。不同驱动声明的属性不同——这正是核心概念里"协议层 Attribute vs 实例层 Config"那条区分。示例 ID 仅作占位,请用你环境里 virtual 驱动真实注册的属性 ID。

第 8 步:读实时位号值

值开始产生后,从数据中心读最新位号值。/point_value/latest 顶层按 deviceId / pointId 过滤,分页参数放在嵌套的 page 对象(current / size)里,返回 Page<PointValueVO>

bash
curl -s -X POST http://localhost:8000/api/v3/data/point_value/latest \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"deviceId":"83010100000000001","pointId":"82010100000000001","page":{"current":1,"size":10}}'
# 示例返回(PointValueVO 形态):
# {"ok":true,"data":{"records":[
#   {"deviceId":"83010100000000001","pointId":"82010100000000001",
#    "rawValue":"25.0","calValue":"25.0","numValue":25.0,
#    "hasLatestValue":true,"createTime":"2026-06-22T08:30:00","operateTime":"2026-06-22T08:30:00"}
# ],"total":1,"current":1,"size":10}}
bash
dc3 point read 82010100000000001

你应当看到records 里至少一条 PointValueVO,含 rawValue(原始值)、calValue(工程值,字符串)、numValue(数值投影,可空)以及采集时间 createTime。值随 virtual 驱动持续刷新。

第 9 步:下发写命令

最后对这个可写位号下发一次写命令。/point_command/writedeviceId / pointId / value立即返回一个命令 ID(commandId——它只代表"命令已受理",不代表"已执行成功"。结果要拿命令 ID 去轮询回执接口 /point_command_history/get_by_command_idcommandId 即上一步返回的命令 ID)。

bash
# 1) 下发写命令,立即拿到 commandId
curl -s -X POST http://localhost:8000/api/v3/data/point_command/write \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"deviceId":"83010100000000001","pointId":"82010100000000001","value":"26.5"}'
# 示例返回:{"ok":true,"code":"...","data":"cmd_20260622_a1b2c3d4"}

# 2) 用 commandId 轮询回执
curl -s -X GET 'http://localhost:8000/api/v3/data/point_command_history/get_by_command_id?commandId=cmd_20260622_a1b2c3d4' \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN"
# 示例返回(PointCommandHistoryVO 形态):
# {"ok":true,"data":{
#   "commandId":"cmd_20260622_a1b2c3d4","deviceId":"83010100000000001","pointId":"82010100000000001",
#   "requestValue":"26.5","responseValue":"...","status":"...","finishTime":"2026-06-22T08:31:02"}}
bash
# 下发写值
dc3 point write 82010100000000001 --device-id 83010100000000001 --value 26.5
# 轮询回执
dc3 command history cmd_20260622_a1b2c3d4

你应当看到:写命令立即返回 commandId;轮询回执直到 status 进入终态。到此,你已经完整走通了"读值 + 写命令"的双向链路。

写命令的语义:异步、需轮询、失败不回显值

  • 写命令是异步的:/point_command/write 立即返回 commandId等设备执行完。结果必须用该 ID 轮询 /point_command_history/get_by_command_id
  • 命令有 TTLPointCommandDTO.expireAt 默认 now + 10s,超时未执行即视为过期。
  • 写失败时不回显写入值:回执的执行结果为失败时不会带回一个"已写入的值",不要把"拿到 commandId"误当成"写成功"。
  • 只有 rwFlag 含写权限(WRITE_ONLY / READ_WRITE)的位号才接受写命令。

延伸阅读

  • 设备接入 — 把这条最小路径展开成真实驱动的完整接入流程
  • 数据与命令 — 采集落库与读写命令在两条平面上的完整机制与回执语义
  • CLI 使用指南dc3 CLI 的完整命令面与脚本化、AI 接入用法

Released under the AGPL-3.0 License · 基于 AGPL-3.0 协议发布