API 参考

VDM 设备接口技术规格

本文按“连接与消息格式 → 主题 → RPC → 遥测 → 告警与抓拍 → 错误码”的顺序组织。接入时先阅读连接参数、RPC 格式和主题结构,再按需要查阅具体接口。

连接参数

参数说明
协议MQTT v3.1.1
PayloadProtobuf(推荐)/ JSON

快速入门 · 型号支持范围 · GitHub 示例

RPC 格式

RPC 请求发布到 vdm/{deviceId}/rpc/req,响应从 vdm/{deviceId}/rpc/resp 接收。

请求结构:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=1)
request.get_targets.SetInParent()
payload = request.SerializeToString(deterministic=True)
{"reqId": 1, "method": "getTargets", "params": {}}
Protobuf 字段JSON 字段必填说明
req_idreqId是请求 ID。必须是非零 32-bit signed integer;设备会在响应中原样返回
request oneofmethod是RPC 方法;每条请求只设置一个 oneof 字段
oneof 中的方法消息params否RPC 参数;无参数的方法使用 Empty

调用限制:

  • 单个 RPC payload 最大 64 KiB。
  • 总 RPC 请求支持短时突发,持续高频请求会返回限流错误。
  • 标靶增删改、初始化、启动/停止测量、抓图、重启、补光灯/电机写操作使用更严格限流。
  • 集成方收到限流码 4 时,应退避重试,不要立即循环重发。

响应结构:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

response = pb.RpcResponse()
response.ParseFromString(response_payload)  # rpc/resp 收到的 Payload
assert response.schema_version == 1
assert response.req_id == 1
assert response.code == 0
targets = response.get_targets.targets
{"reqId": 1, "code": 0, "data": {"targets": []}}
Protobuf 字段JSON 字段说明
req_idreqId请求 ID,和请求中的 ID 一致
codecode结果码,0 表示成功,非 0 表示失败
response oneofdata方法对应的响应数据;成功时必须与请求方法一致

失败响应不携带方法数据;成功且无返回数据时,JSON 省略 data。错误说明见错误码。

请求和响应通过 req_id 关联。同一连接中,尚未收到响应的请求不得复用 req_id;重复 ID 会被拒绝。

主题结构

主题方向说明Protobuf 消息类型
vdm/{deviceId}/rpc/req下行RPC 指令inteagle.vdm.mqtt.v1.RpcRequest
vdm/{deviceId}/rpc/resp上行RPC 响应inteagle.vdm.mqtt.v1.RpcResponse
vdm/{deviceId}/telemetry上行遥测数据inteagle.vdm.mqtt.v1.Telemetry
vdm/{deviceId}/attributes上行属性上报inteagle.vdm.mqtt.v1.Attributes
vdm/{deviceId}/event上行事件上报inteagle.vdm.mqtt.v1.Event
vdm/{deviceId}/3A上行告警上报inteagle.vdm.mqtt.v1.Alarm
vdm/{deviceId}/image上行普通图片或告警抓拍图像包分块不使用 Protobuf,按首字节区分类型 1/2

Protobuf Schema

文件用途
inteagle_vdm_mqtt_v1.protoVDM 遥测、属性、事件、告警和 RPC 定义
inteagle_customer_mqtt_v1.descVDM descriptor set
inteagle_customer_mqtt_v1.proto.sha256.proto 文件校验值
inteagle_customer_mqtt_v1.desc.sha256descriptor set 校验值

声明 schema_version 的 V1 根消息必须设置为 1。Python 项目可以直接使用已生成的 pb2 文件 和 package 文件。

完整代码见 GitHub SDK;告警配置运行步骤见 告警接入示例。


公共字段约束

Protobuf 字段JSON 字段类型约束
target_idtargetIdstring标靶 ID,1-64 字节;去除前后空白后不能为空,不能包含控制字符,同一请求内不能重复
sensor_idsensorIduint32视觉传感器 ID。单目设备通常为 0;双目设备为 0 或 1
roi.x / roi.y相同uint32ROI 左上角像素坐标;坐标基于原始相机画面
roi.width / roi.height相同uint32ROI 宽高,必须大于 0,且 ROI 应落在当前画面范围内
distance_mdistancefloat标靶到设备的测量距离,单位 m,必须大于等于 0.1
roleroleenum标靶角色,枚举见下表;新建时默认 MP
target_modeltargetModelenum标靶型号,枚举见下表
skip_measurementskipMeasurementbool是否跳过测量。false 或不传表示参与测量;true 表示保留配置但测量流程跳过该标靶

role 枚举含义:

Protobuf 值JSON 值名称说明
TARGET_ROLE_RPRPReference Point基准点,作为位移计算参考,通常布置在稳定区域
TARGET_ROLE_MPMPMeasuring Point测点,普通待测标靶,默认角色
TARGET_ROLE_CPCPControl Point控制点,用于监测或补偿系统漂移,通常布置在稳定区域

role 使用建议:

  • MP 是默认和最常用角色。需要持续输出位移的被测位置,例如梁体、塔体、边坡或结构构件上的监测标靶,通常设置为 MP。
  • RP 用作基准点。只有同一视野内存在相对稳定、不随被测体移动的标靶时,才建议设置为 RP;如果现场没有可靠稳定参考点,不要强行设置 RP。
  • CP 用作控制点或校核点。通常布置在稳定位置,用来观察安装稳定性、温漂、整体漂移或测量质量;它不是默认的主测点。
  • 简单接入或不确定角色时,新增标靶可以不传 role,设备按 MP 处理。

target_id 未设置时,支持自动生成 ID 的接口会由设备生成 UUID;JSON 对应字段为 targetId。

targetModel 枚举含义:

Protobuf 值JSON 值说明
TARGET_MODEL_T10T10T10 标靶型号
TARGET_MODEL_T20T20T20 标靶型号
TARGET_MODEL_T50T50T50 标靶型号
TARGET_MODEL_T100T100T100 标靶型号
TARGET_MODEL_T200T200T200 标靶型号

标靶管理 RPC 字段

initRefTargets

对指定标靶执行参考位置初始化/标定。设备会根据保存的标靶配置采集图像并计算初始参考位置;请求中也可以提供 ROI、测量距离、视觉传感器或标靶型号作为本次初始化的覆盖值。调用前应保证 ROI 已对准标靶,且设备没有正在进行其它测量或初始化流程。

Protobuf 字段JSON 字段类型必填说明
targetstargetsrepeated是要初始化标靶参考位置的标靶数组,不能为空,最多 128 个
targets[].target_idtargets[].targetIdstring是标靶 ID,遵循公共字段约束;用于绑定初始化结果和后续位移数据
targets[].sensor_idtargets[].sensorIdoptional uint32否视觉传感器 ID。不传时使用已保存配置;单目设备默认 0,双目设备建议显式传 0 或 1
targets[].roitargets[].roiRoi否标靶 ROI;未保存标靶配置时必须提供
targets[].distance_mtargets[].distanceoptional float否测量距离,单位 m;未保存标靶配置时必须提供
targets[].target_modeltargets[].targetModeloptional enum否型号枚举见公共字段约束;未保存且未传入时默认 T100

标靶增删改请使用 addTargets、setTargets 或 deleteTargets。角色和跳过测量状态使用 addTargets 或 setTargets 配置。

最简请求与带覆盖值的请求:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

minimal = pb.RpcRequest(schema_version=1, req_id=1)
minimal.init_ref_targets.targets.add(target_id="1")
minimal_payload = minimal.SerializeToString(deterministic=True)

request = pb.RpcRequest(schema_version=1, req_id=2)
request.init_ref_targets.targets.add(
    target_id="1",
    sensor_id=0,
    roi=pb.Roi(x=1000, y=500, width=400, height=400),
    distance_m=5.0,
    target_model=pb.TARGET_MODEL_T100,
)
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 1,
  "method": "initRefTargets",
  "params": {
    "targets": [
      {
        "targetId": "1"
      }
    ]
  }
}

独立请求:

{
  "reqId": 2,
  "method": "initRefTargets",
  "params": {
    "targets": [
      {
        "targetId": "1",
        "sensorId": 0,
        "roi": {
          "x": 1000,
          "y": 500,
          "width": 400,
          "height": 400
        },
        "distance": 5.0,
        "targetModel": "T100"
      }
    ]
  }
}

成功时,RpcResponse.code == 0 且 response.HasField("init_ref_targets")。

成功响应表示设备已启动参考位置初始化任务;最终结果通过 event 主题上报。若至少一个标靶初始化成功,设备会同时通过 attributes 主题上报当前 target_list 摘要,initialized_at_s 为 Unix 秒时间戳。

addTargets

在已有标靶基础上添加新标靶。

Protobuf 字段JSON 字段类型必填说明
targetstargetsrepeated是标靶配置数组,不能为空,最多 128 个
targets[].target_idtargets[].targetIdoptional string否不设置时由设备生成 UUID
targets[].sensor_idtargets[].sensorIdoptional uint32双目设备必填单目设备默认 0
targets[].roitargets[].roiRoi是标靶 ROI 区域
targets[].distance_mtargets[].distancefloat是测量距离,单位 m
targets[].roletargets[].roleoptional enum否角色枚举见公共字段约束;默认 MP
targets[].target_modeltargets[].targetModeloptional enum否型号枚举见公共字段约束
targets[].skip_measurementtargets[].skipMeasurementoptional bool否是否跳过测量,默认 false
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=3)
request.add_targets.targets.add(
    target_id="2",
    sensor_id=0,
    roi=pb.Roi(x=800, y=400, width=400, height=400),
    distance_m=6.0,
    role=pb.TARGET_ROLE_MP,
    target_model=pb.TARGET_MODEL_T100,
    skip_measurement=False,
)
payload = request.SerializeToString(deterministic=True)

# 解析成功响应后读取新增标靶 ID
response = pb.RpcResponse.FromString(response_payload)
added_ids = list(response.add_targets.added_target_ids)
{
  "reqId": 3,
  "method": "addTargets",
  "params": {
    "targets": [
      {
        "targetId": "2",
        "sensorId": 0,
        "roi": {
          "x": 800,
          "y": 400,
          "width": 400,
          "height": 400
        },
        "distance": 6.0,
        "role": "MP",
        "targetModel": "T100",
        "skipMeasurement": false
      }
    ]
  }
}

getTargets

获取当前标靶列表。请求使用 Empty,成功响应返回强类型 TargetInfo:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=4)
request.get_targets.SetInParent()
payload = request.SerializeToString(deterministic=True)

response = pb.RpcResponse.FromString(response_payload)
for target in response.get_targets.targets:
    initialized_at = target.initialized_at_s if target.HasField("initialized_at_s") else None
    print(target.target_id, target.distance_m, target.role, initialized_at)
{
  "reqId": 4,
  "method": "getTargets",
  "params": {}
}
Protobuf 字段JSON 字段说明
targets[].target_idtargets[].targetId标靶 ID
targets[].sensor_idtargets[].sensorId视觉传感器 ID
targets[].roitargets[].roi标靶 ROI
targets[].distance_mtargets[].distance测量距离,单位 m
targets[].roletargets[].role标靶角色枚举
targets[].target_modeltargets[].targetModel标靶型号枚举
targets[].skip_measurementtargets[].skipMeasurement是否跳过测量
targets[].initializedtargets[].initialized是否已经初始化参考位置
targets[].initialized_at_stargets[].initializedAtoptional Unix 秒时间戳

setTargets

按 target_id 修改已有标靶。每个元素除 target_id 外,至少设置 sensor_id、roi、distance_m、role、target_model、skip_measurement 中的一个更新字段。

Protobuf 字段JSON 字段类型必填说明
targetstargetsrepeated是标靶配置数组,不能为空,最多 128 个
targets[].target_idtargets[].targetIdstring是要更新的标靶 ID
targets[].sensor_idtargets[].sensorIdoptional uint32否更新视觉传感器 ID
targets[].roitargets[].roioptional Roi否更新 ROI 区域
targets[].distance_mtargets[].distanceoptional float否更新测量距离,单位 m
targets[].roletargets[].roleoptional enum否更新 MP、RP 或 CP 角色
targets[].target_modeltargets[].targetModeloptional enum否更新 T10、T20、T50、T100 或 T200 型号
targets[].skip_measurementtargets[].skipMeasurementoptional bool否更新是否跳过测量
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=5)
request.set_targets.targets.add(
    target_id="2",
    distance_m=6.5,
    role=pb.TARGET_ROLE_CP,
    target_model=pb.TARGET_MODEL_T200,
    skip_measurement=True,
)
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 5,
  "method": "setTargets",
  "params": {
    "targets": [
      {
        "targetId": "2",
        "distance": 6.5,
        "role": "CP",
        "targetModel": "T200",
        "skipMeasurement": true
      }
    ]
  }
}

成功响应必须设置 response.set_targets;未出现在请求中的 optional 字段不会被修改。

deleteTargets

Protobuf 字段JSON 字段类型必填说明
target_idstargetIdsrepeated string是要删除的标靶 ID,不能为空,最多 128 个且不能重复
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=6)
request.delete_targets.target_ids.append("2")
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 6,
  "method": "deleteTargets",
  "params": {
    "targetIds": [
      "2"
    ]
  }
}

成功响应必须设置 response.delete_targets。


基础 RPC 字段

getAttr

Protobuf 字段JSON 字段类型必填说明
keyskeysrepeated string否最多 32 个且不能重复;空列表返回全部基础属性。key 名沿用公开属性名,例如 deviceId、deviceStatus、measureStatus、sampleFreq

status 是 measureStatus 的兼容别名。 deviceStatus 是设备整体工作状态,独立于 measureStatus(测量状态)。设备进入低功耗前,JSON 属性上报 "deviceStatus":"Sleeping";Protobuf 使用 Attributes.device_status = DEVICE_STATUS_SLEEPING。设备唤醒启动后会上报 Idle。deviceStatus 只读,不能通过 setAttr 设置。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=7)
request.get_attr.keys.extend([
    "deviceId", "deviceStatus", "measureStatus", "sampleFreq", "showROI", "showTs", "showSensorId",
])
payload = request.SerializeToString(deterministic=True)

response = pb.RpcResponse.FromString(response_payload)
attrs = response.get_attr.attributes
print(attrs.device_id, attrs.device_status, attrs.measurement_status, attrs.sample_frequency_hz)
{
  "reqId": 7,
  "method": "getAttr",
  "params": {
    "keys": [
      "deviceId",
      "deviceStatus",
      "measureStatus",
      "sampleFreq",
      "showROI",
      "showTs",
      "showSensorId"
    ]
  }
}

setAttr

Protobuf 字段JSON 字段类型必填说明
sample_frequency_hzsampleFreqoptional uint32否采样频率,单位 Hz,范围 1-60
report_metrics.valuesreportMetricsrepeated enum否当前只接受 REPORT_METRIC_DX 和 REPORT_METRIC_DY,不能为空且不能重复;缺省为两者都上报
show_roishowROIoptional bool否告警抓拍图像是否叠加 ROI
show_timestampshowTsoptional bool否告警抓拍图像是否叠加时间戳
show_sensor_idshowSensorIdoptional bool否告警抓拍图像是否叠加视觉传感器 ID

target_list 会在标靶配置变化时通过 attributes 主题上报,也可以通过 getTargets 查询;getAttr 不主动返回标靶列表。JSON 对应字段为 targets。

SetAttributesRequest 必须至少设置一个可写字段。设备 ID、型号、固件版本、分辨率、测量状态和标靶列表为只读字段,不能通过 setAttr 修改。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=8)
request.set_attr.sample_frequency_hz = 5
request.set_attr.report_metrics.values.extend([pb.REPORT_METRIC_DX, pb.REPORT_METRIC_DY])
request.set_attr.show_roi = True
request.set_attr.show_timestamp = True
request.set_attr.show_sensor_id = True
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 8,
  "method": "setAttr",
  "params": {
    "sampleFreq": 5,
    "reportMetrics": [
      "dx",
      "dy"
    ],
    "showROI": true,
    "showTs": true,
    "showSensorId": true
  }
}

syncTime

使用 NTP 同步设备系统时间。同步成功后,设备会保存本次使用的 NTP 服务器配置。

Protobuf 字段JSON 字段类型必填说明
ntp_serverntpServeroptional string否NTP 服务器;不传时使用 ntp.aliyun.com,最长 253 字节且不能包含空白
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

default_ntp = pb.RpcRequest(schema_version=1, req_id=9)
default_ntp.sync_time.SetInParent()

request = pb.RpcRequest(schema_version=1, req_id=10)
request.sync_time.ntp_server = "ntp.aliyun.com"
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 9,
  "method": "syncTime",
  "params": {}
}

独立请求:

{
  "reqId": 10,
  "method": "syncTime",
  "params": {
    "ntpServer": "ntp.aliyun.com"
  }
}

如果 NTP 服务器不可达、地址格式不合法或同步失败,设备返回非 0 code。该接口会触发网络同步和本地配置保存,属于受限流保护的写操作。

reboot / startMeasurement / stopMeasurement

这些接口使用 Empty 请求消息:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

start = pb.RpcRequest(schema_version=1, req_id=11)
start.start_measurement.SetInParent()

stop = pb.RpcRequest(schema_version=1, req_id=12)
stop.stop_measurement.SetInParent()

reboot = pb.RpcRequest(schema_version=1, req_id=13)
reboot.reboot.SetInParent()
{
  "reqId": 11,
  "method": "startMeasurement",
  "params": {}
}

独立请求:

{
  "reqId": 12,
  "method": "stopMeasurement",
  "params": {}
}

独立请求:

{
  "reqId": 13,
  "method": "reboot",
  "params": {}
}

startMeasurement 和 stopMeasurement 会触发 attributes 主题上报 measurement_status;JSON 对应字段为 measureStatus。低功耗休眠前上报独立的 device_status;JSON 对应 deviceStatus。设备断电前的上报是有界尽力发送,没有逐条云端确认;设备离线并不单独证明其处于休眠。reboot 成功后设备会断开并重新连接 MQTT,调用方应等待设备重新上线。


测量控制 RPC 字段

setLightLevel

单灯或所有灯使用统一亮度:

Protobuf 字段JSON 字段类型必填说明
all_lights_levelleveluint32是所有补光灯统一亮度挡位,范围 0-8

双目设备需要分别设置多路补光灯时,也可以传 lights:

Protobuf 字段JSON 字段类型必填说明
per_light.lightslightsrepeated是补光灯配置数组,不能为空,最多 16 个
lights[].light_idlights[].iduint32是补光灯 ID
lights[].levellights[].leveluint32是亮度挡位,范围 0-8
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

all_lights = pb.RpcRequest(schema_version=1, req_id=14)
all_lights.set_light_level.all_lights_level = 4

per_light = pb.RpcRequest(schema_version=1, req_id=15)
per_light.set_light_level.per_light.lights.add(light_id=0, level=3)
per_light.set_light_level.per_light.lights.add(light_id=1, level=6)
{
  "reqId": 14,
  "method": "setLightLevel",
  "params": {
    "level": 4
  }
}

独立请求:

{
  "reqId": 15,
  "method": "setLightLevel",
  "params": {
    "lights": [
      {
        "id": 0,
        "level": 3
      },
      {
        "id": 1,
        "level": 6
      }
    ]
  }
}

getLightLevel 使用 Empty 请求;响应通过 WhichOneof("selection") 判断返回统一亮度还是逐灯数组:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=16)
request.get_light_level.SetInParent()

response = pb.RpcResponse.FromString(response_payload)
selection = response.get_light_level.WhichOneof("selection")
{
  "reqId": 16,
  "method": "getLightLevel",
  "params": {}
}

snapshot

Protobuf 字段JSON 字段类型必填说明
sensor_idsensorIdoptional uint32否视觉传感器 ID,单目设备默认 0
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=17)
request.snapshot.sensor_id = 0
payload = request.SerializeToString(deterministic=True)
{
  "reqId": 17,
  "method": "snapshot",
  "params": {
    "sensorId": 0
  }
}

snapshot RPC 成功响应只表示已触发抓图;JPEG 二进制图片通过 vdm/{deviceId}/image 主题上报。当前标准快照图片由设备侧处理为 640x480。

showROI、showTs 和 showSensorId 控制告警抓拍图像的叠加内容,不影响 snapshot 普通快照。

型号专用 RPC

电机参数见 O1、O2-H、X1和 X2-H;ISP 参数见 X1。

ispCtl

支持 getStatus、setConfig 和 setExposurePreset 三个 action。

setConfig 的曝光时间、增益和旋转角度必须满足型号页面声明的范围;旋转角度只能为 0、90、180 或 270,单相机增益范围为 1024..16384。

ispCtl 成功时只由外层 code=0 表示成功,不重复返回 data.success。按 action 和设备能力,data 只可能包含 vdmModeEnabled、exposureLocked、exposureMode、nrLevel、enable3dnr、maxGain、 exposurePreset、exposureInfo、cameraCount、flip、mirror、rotation、frameWidth 和 frameHeight;exposureInfo 只包含 iso、expTime、aGain、dGain、ispDGain 和 aveLum。操作失败统一通过外层非零 code 返回,不携带 data。

getCruisePaths

getCruisePaths 成功时,JSON data 与 Protobuf get_cruise_paths.data 表示相同的数组结构:

[
  {
    "id": 1,
    "enabled": true,
    "order": 10,
    "cruisePoints": [
      {"id": 1, "targets": ["T01"], "xAngle": 45.0, "yAngle": -5.0}
    ]
  }
]

路径只返回 id、enabled、order、cruisePoints;巡航点只返回 id、targets、 xAngle 和 yAngle。


告警抓拍图像 RPC 字段

仅配置一个已启用的系统集成商 MQTT 连接,且未另行指定或关闭抓拍上传时,设备默认向该连接发送告警抓拍。规则须配置 SNAPSHOT 动作;多个系统集成商 MQTT 连接时需指定接收目标。系统集成商平台校验并保存图像包后调用 ackEvidencePackage 确认。

查看 GitHub 抓拍接收示例 · 查看告警配置示例

告警抓拍图像操作使用对应告警的 eventId。

getEvidenceStatus / retryEvidence

Protobuf 字段JSON 字段类型必填说明
event_ideventIduint64是触发抓拍的告警状态变化 ID;JSON 建议用十进制字符串保持精度
kindkindoptional EvidenceKind否未设置时默认为 SNAPSHOT

getEvidenceStatus 查询该次告警抓拍图像的处理结果;retryEvidence 请求设备重新发送对应图像包。两者都返回 EvidenceStatusResponse:found=false 表示未找到,found=true 时通过协议字段 evidence 返回 AlarmEvidence。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

status_request = pb.RpcRequest(schema_version=1, req_id=80)
status_request.get_evidence_status.event_id = 9001
status_request.get_evidence_status.kind = pb.EVIDENCE_KIND_SNAPSHOT

retry_request = pb.RpcRequest(schema_version=1, req_id=81)
retry_request.retry_evidence.event_id = 9001
retry_request.retry_evidence.kind = pb.EVIDENCE_KIND_SNAPSHOT
{
  "reqId": 80,
  "method": "getEvidenceStatus",
  "params": {
    "eventId": "9001",
    "kind": "SNAPSHOT"
  }
}

独立请求:

{
  "reqId": 81,
  "method": "retryEvidence",
  "params": {
    "eventId": "9001",
    "kind": "SNAPSHOT"
  }
}

ackEvidencePackage

Protobuf 字段JSON 字段类型必填说明
event_ideventIduint64是触发抓拍的告警事件 ID
kindkindEvidenceKind是抓拍图片使用 SNAPSHOT
package_sha256packageSha256string是类型 2 Header 中 32 字节 packageSha256 的 64 位小写十六进制字符串
from generated import inteagle_vdm_mqtt_v1_pb2 as pb

ack_request = pb.RpcRequest(schema_version=1, req_id=82)
ack_request.ack_evidence_package.event_id = 9001
ack_request.ack_evidence_package.kind = pb.EVIDENCE_KIND_SNAPSHOT
ack_request.ack_evidence_package.package_sha256 = "0" * 64  # 替换为 Header 中的实际值
{
  "reqId": 82,
  "method": "ackEvidencePackage",
  "params": {
    "eventId": "9001",
    "kind": "SNAPSHOT",
    "packageSha256": "0000000000000000000000000000000000000000000000000000000000000000"
  }
}

只有完整 USTAR 图像包已持久化且通过长度、SHA-256 和成员安全校验时才能确认。设备只接受从原始接收该图像包的同一逻辑 MQTT 连接发出的 ACK。

告警规则与历史查询

先了解告警等级与生命周期,再查询能力、配置规则并接收告警。相关 RPC 如下:

方法RpcRequest/RpcResponse oneof 字段字段号
getAlarmCapsget_alarm_caps90
listAlarmRuleslist_alarm_rules91
applyAlarmRulesapply_alarm_rules92
getAlarmStateget_alarm_state93
listAlarmHistorylist_alarm_history94

设备级电压规则不传 targetIds,详情包含电压和阈值。标靶级规则的选择范围以 getTargets 和 getAlarmCaps 为准。

告警能力、当前状态与历史响应

get_alarm_caps.api_version / data.apiVersion 是告警管理接口版本,区别于 Protobuf 根消息的 schema_version=1。alarm_service_ready / alarmServiceReady 表示告警服务就绪,alarm_history_ready / alarmHistoryReady 表示历史查询可用;能力响应中的 levels、transitions 和 rule_types / ruleTypes 决定客户端可提供的选项。

方法请求参数(Protobuf / JSON)响应与处理方式
getAlarmCaps空请求返回服务就绪状态、规则类型、等级、动作及资源上限
listAlarmRules可选 cursor / cursoritems 为规则列表;返回 next_cursor / nextCursor 时继续分页,不自行解析游标;配置变化导致的分页冲突使用外层 code=6,此时从首页重查
getAlarmState空请求active 只包含当前活动告警;每项返回生命周期 ID、规则 ID、类型和当前等级;标靶 ID 对应 active[].target_id / active[].detail.targetId,设备级电压告警无此字段
listAlarmHistory可选 cursor、limit、alarm_id / cursor、limit、alarmIditems 按生命周期返回记录,包含该生命周期的最近一次转换;不是完整的逐事件列表。alarmId 在两种格式的查询请求中都使用十进制字符串;分页使用 next_cursor / nextCursor

listAlarmHistory.limit 缺省为 20,设备将传入值限制在 1~50;alarmId 与 cursor 不能同时传入。

历史记录的 state 为 ALARM_INCIDENT_STATE_ACTIVE=1 / ACTIVE 或 ALARM_INCIDENT_STATE_CLEARED=2 / CLEARED。结束记录省略 level,通过 transition 区分恢复与取消。last_event_id / lastEventId 对应最近一次状态变化,可与 3A 的 eventId 关联;历史 ID 字段在两种格式中均为十进制字符串。

时间单位不能混用: 3A 的 ts 是 Unix 秒;历史中的 started_at / startedAt、updated_at / updatedAt、cleared_at / clearedAt 均为 Unix 毫秒。历史抓拍摘要的 captured_at / capturedAt、updated_at / updatedAt 也保持毫秒。

applyAlarmRules:新增、更新、禁用与删除

所有配置操作都调用 applyAlarmRules,请求发到 vdm/{deviceId}/rpc/req,响应从 vdm/{deviceId}/rpc/resp 接收。建议依次执行:getAlarmCaps 查询能力 → getTargets 确认标靶 → listAlarmRules 获取现有完整规则 → applyAlarmRules 提交 → listAlarmRules 回读。

Protobuf 请求字段JSON params 字段类型说明
apply_alarm_rules.upsertupsert规则数组新增或完整替换指定规则;每项 Protobuf AlarmRuleDefinition 只设置一个规则分支
apply_alarm_rules.delete_idsdeleteIdsuint32 数组删除指定已有规则,ID 必须非零且不重复

upsert 和 deleteIds 可同时提交,合计至少一项,当前每批最多 16 项,以 getAlarmCaps.limits.batchRules 为准。当前规则配置请求体上限为 16 KiB,区别于外层 RPC 的 64 KiB 上限。不要在同一批中重复更新同一个 ID,或同时更新和删除同一个 ID;更新和删除的 ID 必须已经存在。

提交按整批校验、编译、持久化并生效:任意一项失败,整批配置不生效。返回成功表示规则已保存,是否能产生告警还取决于规则是否启用、目标是否满足测量条件及有效观测是否达到阈值。

规则公共字段

以下 Protobuf common 位于所选规则分支下,例如 displacement_limit.common;JSON 公共字段直接位于 upsert[] 对象内。

Protobuf 字段JSON 规则字段必填说明
common.idid更新时新建必须省略,由设备分配;更新必须是已有非零 uint32,不能通过指定任意新 ID 创建规则
common.namename否规则名称,当前最多 64 UTF-8 字节,以能力上限为准
common.enabledenabled否缺省为 true;显式 false 保存禁用规则,仍会校验字段合法性
common.actionsactions否默认无动作;动作类型及适用规则来自 getAlarmCaps.actions
规则 oneof 分支type是下表中的规则类型,不能在 Protobuf 的同一规则项设置多个分支

upsert 是完整规则替换,不是字段补丁。例如仅发送 {"id":12,"enabled":false} 不合法;必须带上 type 及该类型的全部必填字段。更新时省略可选字段会恢复其缺省含义,例如省略 enabled 会重新启用,省略 actions 会去掉原动作,省略数值规则的 recoverForMs 会使用默认恢复时间。

各类规则需要的字段

Protobuf 分支JSON type必填业务字段(JSON 名称)可选业务字段
displacement_limitDISP_LIMITtargetIds、metric、direction、levelsrecoverForMs
displacement_rateDISP_RATEtargetIds、metric、direction、windowMs、levelsrecoverForMs
target_lostTARGET_LOSTtargetIds、levels、recoverForMs无
voltage_low,并设置 alarm_type=ALARM_TYPE_INPUT_VOLTAGE_LOWVIN_LOWlevelsrecoverForMs
voltage_low,并设置 alarm_type=ALARM_TYPE_BATTERY_VOLTAGE_LOWVBAT_LOWlevelsrecoverForMs

Protobuf 对应字段名分别为 target_ids、metric、direction、window_ms、levels、recover_for_ms。TARGET_LOST 的等级使用 lost_for_ms / lostForMs,其余类型使用 enter 和 enter_for_ms / enterForMs。电压规则不接受 targetIds、metric、direction;速率规则通过窗口确定单位,不额外传 unit;未定义的字段会被拒绝。

返回值与重复提交

场景Protobuf RpcResponseJSON 响应
新增规则成功code=0,apply_alarm_rules.created_ids 返回新 ID{"reqId":90,"code":0,"data":{"createdIds":[12]}}
仅更新、禁用或删除成功code=0,选择 apply_alarm_rules 分支,created_ids 为空{"reqId":91,"code":0}
失败非零 code,不设置业务 response 分支例如 {"reqId":91,"code":2},不含 data

表中的 ID 仅为示例。createdIds 只包含本批新增规则的 ID,按新增项在 upsert 中出现的顺序返回,已有规则更新不会占位。reqId 只关联请求响应,不是配置事务 ID;若新增请求响应超时,先回读规则确认是否已创建,避免使用新 reqId 重发后产生重复规则。删除已经不存在的规则也会失败,不应当作无条件幂等操作。

公共错误码按错误码处理:2 表示参数错误,3 表示不支持,4 表示限流,5 表示超时,6 表示配置冲突;其他非零值作为失败处理。

告警等级与规则参数

先调用 getAlarmCaps 确认服务是否就绪,以及当前固件支持的规则类型、等级、动作和资源上限。三级含义见告警等级(3A)。规则的 levels 至少配置一级,未配置的等级不参与判定;JSON 使用 ALERT、ALARM、ACTION 作为 key,Protobuf 使用 levels.alert、levels.alarm、levels.action 消息字段。

Protobuf 字段JSON 字段说明
levels.<等级>.enterlevels.<等级>.enter数值类规则的临界阈值;位移单位 mm,电压单位 V,速率单位由窗口决定
levels.<等级>.enter_for_mslevels.<等级>.enterForMs连续越限多少毫秒后进入该等级;缺省为 0,在有效观测越限时即可进入
levels.<等级>.lost_for_mslevels.<等级>.lostForMs仅用于 TARGET_LOST,连续丢失时长,必须大于 0
recover_for_msrecoverForMs整条规则共用的降级/恢复确认时长,必须大于 0;数值规则缺省为 2000 ms,可通过能力响应的 numeric_recover_for_ms / defaults.numericRecoverForMs 查询;丢失规则必须显式提供
window_mswindowMs仅用于 DISP_RATE;1000~10000 ms、步长 100 ms 对应 mm/s,3600000 对应 mm/h,86400000 对应 mm/day;具体支持范围以能力响应为准

数值规则只有一个 enter 阈值,同时用于进入和退出判断,不接受另一个 exit 阈值。判定方式如下:

类型进入条件回到当前等级安全侧已启用等级从 ALERT 到 ACTION 的配置顺序
DISP_LIMIT / DISP_RATE方向换算后的值 > enter,并满足 enterForMs值 <= enterenter 严格递增,且均大于 0
VIN_LOW / VBAT_LOW电压 < enter,并满足 enterForMs电压 >= enterenter 严格递减,电压越低等级越高
TARGET_LOST应当可见的标靶连续未检出,达到 lostForMs连续重新检出达到 recoverForMs 后恢复lostForMs 严格递增

数值告警回到安全侧后,还需连续满足 recoverForMs 才会降级或恢复;降到较低等级后仍是活动告警。丢失告警重新检出并满足恢复时间后直接恢复,不逐级降级。无效观测、乱序、观测超时或标靶当前不应可见等情况会中断未完成的计时,不能作为告警已经恢复的依据。

以下创建一个 X 方向双向位移规则,仅启用 ALERT 和 ACTION,只上报告警。阈值为说明协议用的示例值,实际值由项目监测要求确定;targetId="1" 必须是当前可见且允许配置位移规则的标靶。请求发布到 vdm/{deviceId}/rpc/req。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

request = pb.RpcRequest(schema_version=1, req_id=90)
rule = request.apply_alarm_rules.upsert.add().displacement_limit
rule.common.name = "X 方向位移"
rule.common.enabled = True
rule.target_ids.append("1")
rule.metric = pb.ALARM_METRIC_DX
rule.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
rule.levels.alert.enter = 5.0
rule.levels.alert.enter_for_ms = 1000
rule.levels.action.enter = 10.0
rule.levels.action.enter_for_ms = 1000
rule.recover_for_ms = 2000
payload = request.SerializeToString(deterministic=True)
{
  "method": "applyAlarmRules",
  "reqId": 90,
  "params": {
    "upsert": [{
      "name": "X 方向位移",
      "enabled": true,
      "type": "DISP_LIMIT",
      "targetIds": ["1"],
      "metric": "DX",
      "direction": "BIDIRECTIONAL",
      "levels": {
        "ALERT": {"enter": 5.0, "enterForMs": 1000},
        "ACTION": {"enter": 10.0, "enterForMs": 1000}
      },
      "recoverForMs": 2000
    }]
  }
}

新建规则省略 id,从响应 created_ids / createdIds 获取设备分配的 ID;更新时使用该 ID 并提交完整规则。以上规则可直接从正常触发 ACTION,也可先触发 ALERT 后升级至 ACTION,不会产生未启用的 ALARM。

actions 省略或为空数组表示只告警、不抓拍。配置 SNAPSHOT 时可通过动作的 levels、transitions 选择触发时机;省略选择器时默认使用规则已启用的等级和 TRIGGERED。JSON 显式空选择器数组会被拒绝;Protobuf repeated 选择器为空等同于省略。动作失败不会回滚告警状态。速率告警抓拍仅支持 1000~10000 ms 短窗口;小时/日窗口仅上报告警。

同一规则绑定多个标靶时,每个标靶独立计时和维护告警生命周期。更新规则参数后,已有活动告警继续使用触发时的规则定义直到恢复;删除、禁用规则或解绑标靶会取消相应活动告警。

完整更新或禁用示例

下例假设新增示例创建的规则 ID 为 12。禁用它时仍提交完整位移规则;若要更新阈值并启用,将 enabled 改为 true 并修改相应 enter。禁用活动规则会产生 CANCELLED,已有告警不会被标记成测量恢复。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

update_request = pb.RpcRequest(schema_version=1, req_id=91)
rule = update_request.apply_alarm_rules.upsert.add().displacement_limit
rule.common.id = 12  # 替换成设备实际返回的 created_ids
rule.common.name = "X 方向位移"
rule.common.enabled = False
rule.target_ids.append("1")
rule.metric = pb.ALARM_METRIC_DX
rule.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
rule.levels.alert.enter = 5.0
rule.levels.alert.enter_for_ms = 1000
rule.levels.action.enter = 10.0
rule.levels.action.enter_for_ms = 1000
rule.recover_for_ms = 2000
payload = update_request.SerializeToString(deterministic=True)
{
  "reqId": 91,
  "method": "applyAlarmRules",
  "params": {
    "upsert": [{
      "id": 12,
      "name": "X 方向位移",
      "enabled": false,
      "type": "DISP_LIMIT",
      "targetIds": ["1"],
      "metric": "DX",
      "direction": "BIDIRECTIONAL",
      "levels": {
        "ALERT": {"enter": 5.0, "enterForMs": 1000},
        "ACTION": {"enter": 10.0, "enterForMs": 1000}
      },
      "recoverForMs": 2000
    }]
  }
}

删除示例

删除只需 ID,不需要重传规则。若规则正在告警,会终止其活动生命周期。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

remove_request = pb.RpcRequest(schema_version=1, req_id=92)
remove_request.apply_alarm_rules.delete_ids.append(12)
payload = remove_request.SerializeToString(deterministic=True)
{"reqId":92,"method":"applyAlarmRules","params":{"deleteIds":[12]}}

回读与分页示例

listAlarmRules 不接受 limit 或 ID 过滤参数,页大小由设备控制。第一页不传 cursor,后续使用响应中的游标,直至响应不再携带游标。

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

list_request = pb.RpcRequest(schema_version=1, req_id=93)
list_request.list_alarm_rules.SetInParent()
# 后续页:list_request.list_alarm_rules.cursor = 上一页响应.next_cursor
payload = list_request.SerializeToString(deterministic=True)
{"reqId":93,"method":"listAlarmRules","params":{}}

JSON 后续请求将 params 替换为 {"cursor":"上一页返回的 nextCursor"},并使用新的 reqId。回读时默认值可能省略,例如缺省的 recoverForMs=2000、空动作数组和默认动作选择器;客户端应按默认语义补齐编辑表单。阈值使用浮点数,JSON 回读可能出现 0.20000000298023224 这样的二进制浮点展开;按数值及合理误差比较,不按小数字符串判断配置是否保存成功。


遥测数据

设备通过同一个 telemetry 主题上报位移数据、环境数据和设备状态数据。

主题: vdm/{deviceId}/telemetry

位移数据:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Telemetry(schema_version=1)
message.displacement.sample_frequency_hz = 3
message.displacement.first_sample_timestamp_ms = 1734567890123
target = message.displacement.targets.add(target_id="1")
target.dx.extend([0.01, 0.02, 0.03])
target.dy.extend([0.00, 0.01, 0.01])
payload = message.SerializeToString(deterministic=True)
{"disp":{"t":1734567890,"f":3,"d":{"1":{"dx":[0.01,0.02,0.03],"dy":[0.00,0.01,0.01]}}}}
Protobuf 字段JSON 字段说明
sample_frequency_hzdisp.f本次位移数组的采样频率,单位 Hz。例如 5 Hz 表示相邻样本约 200 ms
first_sample_timestamp_msdisp.t本批次首个样本的 Unix 毫秒时间戳;JSON 的 disp.t 保持既有 Unix 秒格式
targets[].target_iddisp.d 的 key标靶 ID
targets[].dxdisp.d.{targetId}.dxX 方向位移数组,单位 mm
targets[].dydisp.d.{targetId}.dyY 方向位移数组,单位 mm

位移数据包含 X/Y 方向。reportMetrics=["dx"] 或 ["dy"] 时,JSON 省略未选择的方向 key,Protobuf 对应 repeated 字段为空;缺省值为 ["dx","dy"]。非空数组使用同一批次采样网格,云端按 first_sample_timestamp_ms + round(i × 1000 / sample_frequency_hz) 还原第 i 个样本时间。

环境数据:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Telemetry(schema_version=1)
message.environment.ts = 1781686005
message.environment.temperature_c = 38.57
message.environment.humidity_percent_rh = 25.86
message.environment.pressure_hpa = 1002.8
payload = message.SerializeToString(deterministic=True)
{"env":{"t":1781686005,"temperature":38.57,"humidity":25.86,"pressure":1002.8}}
Protobuf 字段JSON 字段说明
environment.tsenv.tUnix 时间戳,单位秒
environment.temperature_cenv.temperature温度,单位 °C
environment.humidity_percent_rhenv.humidity湿度,单位 %RH
environment.pressure_hpaenv.pressure气压,单位 hPa

设备状态数据:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Telemetry(schema_version=1)
message.device_status.ts = 1781686005
message.device_status.input_voltage_v = 12.11
message.device_status.lte_dbm = -71
payload = message.SerializeToString(deterministic=True)
{"device":{"t":1781686005,"vIn":12.11,"lteDbm":-71}}
Protobuf 字段JSON 字段说明
device_status.tsdevice.tUnix 时间戳,单位秒
device_status.input_voltage_vdevice.vIn设备主电源输入电压,额定工作电压 12V,单位 V
device_status.lte_dbmdevice.lteDbmLTE 接收信号强度,单位 dBm

device_status 的公开状态字段以本表为准。除时间戳外,仅开放 vIn 和 lteDbm;二者均无有效采集值时不发送设备状态消息。

Protobuf 解析时使用 HasField("displacement")、HasField("environment") 或 HasField("device_status") 判断消息体。选择 JSON 时,对应判断 disp、env 或 device。


告警上报

设备通过主题 vdm/{deviceId}/3A 上报告警生命周期事件。连接选择 Protobuf 时使用 inteagle.vdm.mqtt.v1.Alarm;选择 JSON 时使用本节的 JSON 字段。

告警等级(3A)

严重程度从低到高为 ALERT < ALARM < ACTION。等级表示当前风险程度,是否触发抓拍等动作由规则的 actions 另行配置。

Protobuf 枚举数值JSON level含义展示建议
ALARM_LEVEL_ALERT1ALERT预警,关注变化趋势黄色
ALARM_LEVEL_ALARM2ALARM报警,需要处理橙色
ALARM_LEVEL_ACTION3ACTION行动级/紧急,需要立即响应红色

颜色是界面展示建议,不是设备传输字段。

level 为可选字段:TRIGGERED、ESCALATED、DEESCALATED 和活动状态的 SYNCED 携带当前等级;RECOVERED、CANCELLED 不设置 level。Python 应使用 HasField("level") 判断是否存在,不能把读取到的默认值 0 当成一个“正常等级”。JSON 结束事件直接省略 level,不要要求它为 "NORMAL" 或 null。

告警类型

Protobuf 枚举数值JSON type含义与适用范围Protobuf detail 分支
ALARM_TYPE_DISPLACEMENT_LIMIT1DISP_LIMIT位移超限,真实标靶限 MPdisplacement
ALARM_TYPE_DISPLACEMENT_RATE_LIMIT2DISP_RATE位移变化速率超限,真实标靶限 MPdisplacement_rate
ALARM_TYPE_TARGET_LOST3TARGET_LOST应当可见的标靶持续丢失,适用 MP/RP/CPtarget_lost
ALARM_TYPE_INPUT_VOLTAGE_LOW4VIN_LOW设备输入电压过低voltage
ALARM_TYPE_BATTERY_VOLTAGE_LOW5VBAT_LOW设备电池电压过低voltage

实际可配置范围以 getAlarmCaps 为准。设备离线由云端根据连接或最后在线时间判定,不属于设备主动上报的上述五种类型。

生命周期转换

Protobuf 枚举数值JSON transition含义alarmId 与 level
ALARM_TRANSITION_TRIGGERED1TRIGGERED首次满足某个启用等级的条件新 alarmId,携带进入的等级
ALARM_TRANSITION_ESCALATED2ESCALATED升至更高的启用等级,可跨级原 alarmId,携带升级后的等级
ALARM_TRANSITION_DEESCALATED3DEESCALATED降至较低的启用等级,仍未恢复原 alarmId,携带降级后的等级
ALARM_TRANSITION_RECOVERED4RECOVERED满足恢复条件,生命周期结束原 alarmId,省略等级
ALARM_TRANSITION_CANCELLED5CANCELLED规则删除、禁用或标靶解绑等操作终止告警原 alarmId,省略等级;不表示测量值已恢复
ALARM_TRANSITION_SYNCED6SYNCED显式同步已有活动状态原 alarmId,携带当前等级;不算再次触发

设备状态不变化时不重复产生告警,也没有周期性告警心跳;重试使用原 eventId。云端不能因一段时间没有收到 3A 消息就自动判为恢复,重连后可用 getAlarmState 核对当前活动告警,用 listAlarmHistory 查询历史。

例如只启用 ALERT 和 ACTION 时,一个生命周期可以是:

stateDiagram-v2
    [*] --> 正常
    正常 --> ALERT: TRIGGERED
    正常 --> ACTION: TRIGGERED,直接满足高等级
    ALERT --> ACTION: ESCALATED
    ACTION --> ALERT: DEESCALATED
    ALERT --> 结束: RECOVERED 或 CANCELLED
    ACTION --> 结束: RECOVERED 或 CANCELLED
    结束 --> [*]

图中的“正常”和“结束”用于说明生命周期,不是 AlarmLevel 枚举。下次重新触发会创建新的 alarmId。

消息字段与类型详情

Protobuf 字段JSON 字段类型与说明
schema_version无uint32,当前必须为 1
event_ideventIduint64 / 十进制字符串,一次状态变化的 ID
alarm_idalarmIduint64 / 十进制字符串,一次完整生命周期的 ID
rule_idruleIduint32,对应触发告警的规则 ID
alarm_typetypeAlarmType / 上表的业务类型字符串
levelleveloptional AlarmLevel / 大写等级字符串,结束事件不设置
transitiontransitionAlarmTransition / 上表的转换字符串
tstsuint64 / Number,Unix 秒时间戳
detail(oneof)detail与告警类型匹配的详情;Protobuf 只设置对应分支

各类型的详情如下,表中 Protobuf 字段相对于其分支列出:

类型Protobuf 字段 → JSON detail 字段含义
DISP_LIMITtarget_id → targetId;metric → metric;direction → direction;value_mm → value;limit_mm → limit标靶、指标、方向、观测位移和阈值,数值单位为 mm;JSON 不额外上报 unit
DISP_RATEtarget_id → targetId;metric → metric;direction → direction;value → value;limit → limit;unit → unit;window_ms → windowMs观测速率、阈值、单位和窗口长度;单位为 mm/s、mm/h 或 mm/day
TARGET_LOSTtarget_id → targetId只有标靶 ID,不携带 value、limit 或丢失时长
VIN_LOW / VBAT_LOWvalue_v → value;limit_v → limit电压和阈值,单位 V;不携带 targetId 或 unit

value、limit 及其 Protobuf 对应字段均可省略,缺失不等于 0;SYNCED 不携带这两个观测字段。详情是事件产生时冻结的数据,不能用当前规则配置反推历史事件的阈值。降级或恢复事件的 limit 表示离开等级的阈值。

当前位移和速率规则开放 ALARM_METRIC_DX=1 / DX、ALARM_METRIC_DY=2 / DY。方向枚举为:

Protobuf 枚举数值JSON direction用于比较的值
DISPLACEMENT_DIRECTION_POSITIVE1POSITIVE原始值
DISPLACEMENT_DIRECTION_NEGATIVE2NEGATIVE原始值取反
DISPLACEMENT_DIRECTION_BIDIRECTIONAL3BIDIRECTIONAL原始值的绝对值

事件中的 value 保留原始正负号,limit 使用正的阈值幅值。例如 DX + NEGATIVE 的 value=-5.2、limit=5.0 表示负 X 方向越限。

上报示例

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Alarm(
    schema_version=1,
    event_id=7,
    alarm_id=6,
    rule_id=12,
    alarm_type=pb.ALARM_TYPE_DISPLACEMENT_LIMIT,
    level=pb.ALARM_LEVEL_ALERT,
    transition=pb.ALARM_TRANSITION_TRIGGERED,
    ts=1734567890,
)
message.displacement.target_id = "1"
message.displacement.metric = pb.ALARM_METRIC_DX
message.displacement.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
message.displacement.value_mm = 5.2
message.displacement.limit_mm = 5.0
payload = message.SerializeToString(deterministic=True)
{
  "eventId": "7",
  "alarmId": "6",
  "ruleId": 12,
  "type": "DISP_LIMIT",
  "level": "ALERT",
  "transition": "TRIGGERED",
  "ts": 1734567890,
  "detail": {
    "targetId": "1",
    "metric": "DX",
    "direction": "BIDIRECTIONAL",
    "value": 5.2,
    "limit": 5.0
  }
}

alarmId 表示一次完整告警生命周期,eventId 表示该生命周期中的一次状态变化。RECOVERED 或 CANCELLED 表示该 alarmId 生命周期结束。系统集成商平台以 deviceId + alarmId 作为生命周期主键,以 deviceId + eventId 作为状态变化和抓拍图像去重键;不要根据 ID 位布局互相推导。

同一生命周期的恢复消息示例(新的 eventId,沿用 alarmId,省略 level):

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

recovered = pb.Alarm(
    schema_version=1, event_id=8, alarm_id=6, rule_id=12,
    alarm_type=pb.ALARM_TYPE_DISPLACEMENT_LIMIT,
    transition=pb.ALARM_TRANSITION_RECOVERED, ts=1734567900,
)
recovered.displacement.CopyFrom(pb.DisplacementAlarmDetail(
    target_id="1", metric=pb.ALARM_METRIC_DX,
    direction=pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL,
    value_mm=4.0, limit_mm=5.0,
))
payload = recovered.SerializeToString(deterministic=True)
{
  "eventId": "8",
  "alarmId": "6",
  "ruleId": 12,
  "type": "DISP_LIMIT",
  "transition": "RECOVERED",
  "ts": 1734567900,
  "detail": {
    "targetId": "1",
    "metric": "DX",
    "direction": "BIDIRECTIONAL",
    "value": 4.0,
    "limit": 5.0
  }
}

ackEvidencePackage 仅确认抓拍图像包已完整接收,不会确认或清除告警;ACTION 等级也不代表抓拍已成功,应通过 event 中的图像状态或 getEvidenceStatus 查询结果。


事件上报

设备通过 vdm/{deviceId}/event 上报事件。

标靶参考位置初始化结果:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Event(
    schema_version=1,
    ts=1781767001,
    event_type=pb.EVENT_TYPE_REF_INIT_RESULT,
)
message.ref_init_result.successful_target_ids.append("1")
message.ref_init_result.failed_targets.add(
    target_id="2",
    code=102,
)
payload = message.SerializeToString(deterministic=True)
{
  "type": "REF_INIT_RESULT",
  "ts": 1781767001,
  "detail": {
    "ok": ["1"],
    "fail": [
      {"id": "2", "code": 102}
    ]
  }
}

ref_init_result.successful_target_ids 为成功标靶 ID 列表,failed_targets 为失败标靶列表。 失败项携带数值错误码。JSON 对应字段为 detail.ok 和 detail.fail。

其他公共事件如下:

Protobuf EventTypeJSON type明细
EVENT_TYPE_CRUISE_REACHEDCRUISE_REACHED只包含 pathId、pointId、pan、tilt 中已知字段
EVENT_TYPE_TARGET_TRACKINGTARGET_TRACKINGtargetId 和稳定状态 LOST 或 TRACKING

规则产生的标靶丢失通过 3A 上报。

初始化时间通过 Attributes.target_list.targets[].initialized_at_s 或 getTargets 获取;JSON 对应字段为 targets[].initializedAt。


属性上报

设备通过 vdm/{deviceId}/attributes 上报属性。连接成功后会上报基础属性;属性变化时会上报增量属性。attributes 不是历史状态查询通道,订阅方需要当前标靶快照时应调用 getTargets。

基础属性示例:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Attributes(
    schema_version=1,
    device_id="XXXXXXXXXXXX",
    device_model="V1",
    firmware_version="1.2.3",
    resolution="3840x2160",
    measurement_status=pb.MEASUREMENT_STATUS_IDLE,
    sample_frequency_hz=5,
    show_roi=True,
    show_timestamp=True,
    show_sensor_id=True,
)
message.report_metrics.values.extend([
    pb.REPORT_METRIC_DX,
    pb.REPORT_METRIC_DY,
])
payload = message.SerializeToString(deterministic=True)
{
  "deviceId": "XXXXXXXXXXXX",
  "deviceModel": "V1",
  "fwVer": "1.2.3",
  "resolution": "3840x2160",
  "measureStatus": "Idle",
  "sampleFreq": 5,
  "reportMetrics": ["dx", "dy"],
  "showROI": true,
  "showTs": true,
  "showSensorId": true
}

标靶属性变化时,target_list 按精简摘要格式上报;单独调用 getTargets 只通过 RPC 响应返回,不额外触发 Attributes.target_list:

from generated import inteagle_vdm_mqtt_v1_pb2 as pb

message = pb.Attributes(schema_version=1)
target = message.target_list.targets.add(
    target_id="1",
    sensor_id=0,
    roi=pb.Roi(x=1000, y=500, width=400, height=400),
    distance_m=5.0,
    role=pb.TARGET_ROLE_MP,
    target_model=pb.TARGET_MODEL_T100,
    skip_measurement=False,
    initialized=True,
    initialized_at_s=1781767000,
)
payload = message.SerializeToString(deterministic=True)
{
  "targets": [
    {
      "targetId": "1",
      "sensorId": 0,
      "roi": {"x": 1000, "y": 500, "width": 400, "height": 400},
      "distance": 5.0,
      "role": "MP",
      "targetModel": "T100",
      "skipMeasurement": false,
      "initialized": true,
      "initializedAt": 1781767000
    }
  ]
}
Protobuf 字段JSON 字段说明
target_list.targets[].target_idtargets[].targetId标靶 ID
target_list.targets[].sensor_idtargets[].sensorId视觉传感器 ID
target_list.targets[].roitargets[].roi标靶 ROI
target_list.targets[].distance_mtargets[].distance测量距离,单位 m
target_list.targets[].roletargets[].role标靶角色枚举
target_list.targets[].target_modeltargets[].targetModel标靶型号枚举
target_list.targets[].skip_measurementtargets[].skipMeasurement是否跳过测量
target_list.targets[].initializedtargets[].initialized是否已经初始化参考位置
target_list.targets[].initialized_at_stargets[].initializedAtoptional Unix 秒时间戳

targets 属性只包含对外集成需要的摘要字段。


API 支持矩阵

APIV1V1 ProV1 LiteV2-HV2-WV2-KO1O2-HX1X2-H
基础 API
getAttr✓✓✓✓✓✓✓✓✓✓
setAttr✓✓✓✓✓✓✓✓✓✓
reboot✓✓✓✓✓✓✓✓✓✓
syncTime✓✓✓✓✓✓✓✓✓✓
标靶管理
initRefTargets✓✓✓✓✓✓✓✓✓✓
addTargets✓✓✓✓✓✓✓✓✓✓
getTargets✓✓✓✓✓✓✓✓✓✓
setTargets✓✓✓✓✓✓✓✓✓✓
deleteTargets✓✓✓✓✓✓✓✓✓✓
测量控制
startMeasurement✓✓✓✓✓✓✓✓✓✓
stopMeasurement✓✓✓✓✓✓✓✓✓✓
setLightLevel✓✓✓✓✓✓✓✓✓✓
getLightLevel✓✓✓✓✓✓✓✓✓✓
snapshot✓✓✓✓✓✓✓✓✓✓
告警抓拍图像(0.8.5+)
getEvidenceStatus✓✓✓✓✓✓✓✓✓✓
retryEvidence✓✓✓✓✓✓✓✓✓✓
ackEvidencePackage✓✓✓✓✓✓✓✓✓✓
告警管理(JSON / Protobuf)
getAlarmCaps✓✓✓✓✓✓✓✓✓✓
listAlarmRules✓✓✓✓✓✓✓✓✓✓
device_status=pb.DEVICE_STATUS_IDLE,
applyAlarmRules✓✓✓✓✓✓✓✓✓✓
getAlarmState✓✓✓✓✓✓✓✓✓✓
listAlarmHistory✓✓✓✓✓✓✓✓✓✓
ISP 控制
ispCtl✓✓✓✓✓✓✓✓✓✓
电机控制
setMotorAngle------✓✓✓✓
getMotorAngle------✓✓✓✓
setMotorZero------✓✓✓✓
enableMotor------✓✓✓✓
disableMotor------✓✓✓✓
巡航控制
getCruisePaths------✓✓✓✓

错误码

JSON code 与 Protobuf RpcResponse.code 含义一致:0 表示成功,其他值表示失败。

“deviceStatus”: “Idle”,

码说明适用型号
0成功全部
1通用失败(无法进一步分类)全部
2参数缺失、类型错误或取值无效全部
3方法、action 或 Payload 格式不支持全部
4请求被限流全部
5请求执行超时全部
6资源状态已变化,请刷新后重试全部
100标靶不存在,或对当前连接不可见全部
102标靶初始化失败全部
104标靶丢失全部
200测量未启动全部
201测量已在运行全部
300电机不可用O1, O2-H, X1, X2-H
302电机运动中O1, O2-H, X1, X2-H
303电机到达限位X1, X2-H
310垂直电机不可用O1, O2-H
400巡航功能不可用O1, O2-H, X1, X2-H
403巡航已在运行O1, O2-H, X1, X2-H

RPC 失败仅通过外层数字 code 表示,不携带业务数据。


图片数据

设备通过 image 主题上报普通图片和告警抓拍图像包分块。该 Topic 始终使用二进制 Payload,不使用 JSON 或 Protobuf 包装。

主题: vdm/{deviceId}/image

格式分派

解析器必须先检查第 0 字节,不能把所有 image Payload 都按旧 8 字节 Header 解析:

第 0 字节格式Payload
1普通图片8 字节可扩展 Header + 完整 JPEG
2告警抓拍图像包分块76 字节固定 Header + 一个 USTAR 分块

普通图片 Header(类型 1)

普通图片继续使用可扩展 Header:

偏移长度字段说明
01version消息类型 / Header 版本,当前 0x01
11headerLenHeader 总长度(字节)
21sensorId视觉传感器 ID(单目为 0,双目为 0 或 1)
31type图片类型(见下表)
44timestampUnix 秒时间戳(uint32,大端)
headerLen剩余imageData完整 JPEG 二进制数据

图片类型 (type):

值类型说明
0snapshotRPC 触发的快照
13A告警触发的抓图
2periodic定时抓图

普通图片示例

01 08 00 01 67 72 B3 D6 FF D8 FF E0 ...
│  │  │  │  └──────────┴─ timestamp: 1735533526
│  │  │  └─ type: 1 (3A)
│  │  └─ sensorId: 0
│  └─ headerLen: 8
└─ version: 1

headerLen 可能在未来版本增加。解析时应从 headerLen 位置取 JPEG,不应将未识别的扩展字段当作图片字节。

告警抓拍图像包分块 Header

类型 2 使用 76 字节固定 Header。除单字节字段和原始 SHA-256 外,所有整数均使用大端序:

偏移长度字段说明
01messageType固定为 2
11headerLen固定为 76
21packageFormat固定为 1,表示确定性 USTAR
31evidenceKind抓拍图像固定为 1(SNAPSHOT)
48eventId触发抓拍的告警状态变化 ID,必须非 0
128packageLength完整 USTAR 包长度,范围 1..32 MiB
2032packageSha256完整 USTAR 包的 SHA-256 原始字节
524chunkIndex当前分块索引,从 0 开始
564chunkCount完整抓拍图像包的分块数
608chunkOffset当前分块在完整抓拍图像包中的字节偏移
684chunkLengthHeader 后当前分块的字节数
724flags当前必须为 0
76chunkLengthchunkDataUSTAR 分块字节

分块大小固定为 128 KiB,只有最后一块可以更短。解析器必须检查:

  • chunkCount == ceil(packageLength / 128 KiB)。
  • chunkOffset == chunkIndex × 128 KiB,chunkLength 等于该位置的预期长度。
  • Payload 总长度严格等于 76 + chunkLength,flags == 0。
  • 第一块的 TAR Header 必须包含 ustar 标识。
  • 重组后的字节数与 packageLength 相同,SHA-256 与 packageSha256 相同。
  • USTAR 第一项必须是 manifest.json,所有成员必须是安全相对路径下的普通文件。

抓拍图像包分块重组与确认

MQTT QoS 1 允许重复投递,不保证业务消息与其他 Topic 的到达顺序。推荐使用磁盘优先、有界的重组流程:

  1. 以 deviceId + eventId + packageSha256 + chunkIndex 作为分块幂等键,相同键再次到达时必须比对字节内容。
  2. 收到新分块时按 chunkOffset 写入临时文件,并限制同时未完成的事件数和磁盘占用。
  3. 全部分块到齐后校验 USTAR、总长度和 SHA-256,再原子替换为最终包文件。
  4. 完整包持久化后,保存本地接收回执并从同一 MQTT 连接调用 ackEvidencePackage。

生产实现应持久化已收分块 bitmap,使接收服务重启后可以继续去重和重组。官方示例仓库的 python/evidence_receiver.py 提供可运行的落盘、校验和 ACK 参考实现。 官方 Python、Go、Java 和 JavaScript/TypeScript SDK 均提供一致的有界磁盘重组、完整性与安全 USTAR 校验和原子接收回执能力;Python 文件另外提供可直接运行的接收进程示例。

图片规格

项目规格
格式JPEG
普通快照标准分辨率640x480
普通图片典型大小50KB - 200KB
单个告警抓拍图像包上限32 MiB
单次告警抓拍图像数上限64