边缘端自动与手工接入对接指南.md 23 KB

智慧停车边缘端自动与手工接入对接指南

版本:v1.0

适用对象:车牌识别相机、道闸控制器、边缘网关固件和设备代理开发人员

配套文档:设备端对接文档.md边缘设备自动接入实施方案.md

1. 对接目标

边缘端在首次安装、恢复出厂或更换管理系统地址后,需要支持以下两种接入方式:

  1. UDP 自动发现:设备不知道管理系统地址时,在局域网监听发现请求,返回自己的身份和 HTTPS 配置地址。管理系统随后通过 HTTPS 完成身份确认和配置下发。
  2. 手工 IP 接入:UDP 广播不可用或设备不支持发现协议时,管理员在管理系统页面输入设备 IP 和 HTTPS 端口。设备仍通过相同的 HTTPS 身份和配置接口完成接入,不得因为地址是手工输入而跳过身份确认。

两种方式完成配置后,边缘端都必须主动连接 MQTT Broker,并发布 retained 的 gate.state.v1。管理系统只有收到该状态消息后,才把设备标记为在线。

发现和配置通道只用于接入,不承载车牌、图片、开闸指令等正常业务数据。设备上线、状态、识别事件、开闸指令和回执使用 MQTT;抓拍图片由边缘端通过 HTTPS 主动上传到管理系统,MQTT 事件只引用上传成功返回的 image_id

2. 边缘端必须实现的能力

2.1 固定身份

设备出厂时必须生成并永久保存以下信息:

字段 要求
device_id 永久硬件 ID,不能由管理员输入、修改或因 IP 变化而改变;建议使用安全芯片序列号或出厂序列号
device_code 初始可以为空或使用出厂临时编码;管理员绑定业务通道后会下发正式编码
device_type 例如 lpr-gategate-controllercamera
device_model 固件型号
firmware_version 当前固件版本
设备签名密钥 每台设备独立的 Ed25519 私钥,私钥不得通过 UDP、日志或 MQTT 暴露

device_id 是设备身份主键。设备发现响应里的 device_id、HTTPS 身份接口返回的 device_id 和 MQTT 状态对应的设备编码必须能够关联;发现阶段和 HTTPS 阶段出现不同永久 ID 时必须拒绝继续配置。

2.2 HTTPS 配置服务

设备必须在配置端口监听 HTTPS,默认端口为 8443,实际端口可由固件配置。证书可以是出厂自签名证书,但证书必须稳定保存,不能每次启动随机生成。

必须提供以下接口:

方法 路径 用途
GET /api/v1/identity 返回设备公开身份、证书/公钥指纹和是否需要配对码
POST /api/v1/pair 校验一次性配对码,建立本次管理系统信任关系
POST /api/v1/provision 原子保存 MQTT 和停车场通道配置

当前管理系统会先读取 /api/v1/identity,再由管理员核对证书指纹并调用 /api/v1/pair,最后调用 /api/v1/provision。接口必须只返回必要信息,不返回私钥、MQTT 长期密码或其他设备密钥。

2.3 MQTT 客户端

设备必须支持 MQTT 3.1.1、QoS 1、Keep Alive 30 秒、LWT 和 retained 消息。设备是 MQTT 客户端,管理系统是 Broker;设备不得等待管理系统反向连接设备。

3. UDP 自动发现对接

3.1 监听规则

  • 传输协议:IPv4 UDP。
  • 监听地址:0.0.0.0:31001
  • 单个 UDP 包最大处理长度:4KB;超过长度直接丢弃。
  • 设备只响应合法、未过期的 discovery.request.v1
  • 响应必须单播回请求报文的源 IP 和源端口,不要再次广播。
  • 不要在 UDP 请求或响应中携带 MQTT 密码、私钥、配对码或停车场业务数据。
  • UDP 监听应在设备尚未配置 MQTT 时工作;设备已经在线时是否响应由设备策略决定,但不能自动覆盖已生效配置。

管理系统会在本机 IPv4 网卡的定向广播地址上发送 3 次请求,间隔 500ms,收集窗口约 3 秒。设备不能假设管理系统使用固定源端口。

3.2 请求格式 discovery.request.v1

{
  "schema": "discovery.request.v1",
  "request_id": "7ba3a5ea-3ca4-45fe-b59d-1dc47b6ab5a1",
  "issued_at": 1760000000,
  "expires_at": 1760000030,
  "system_id": "smart-parking-HOST01",
  "nonce": "base64-32-random-bytes"
}

设备处理要求:

  1. schema 必须等于 discovery.request.v1
  2. request_id 必须作为本次请求关联 ID 原样带回。
  3. expires_at 必须大于当前时间;建议允许不超过 30 秒的时钟误差。
  4. nonce 必须原样带回,不能生成新的 nonce 替换它。
  5. 同一 request_id + nonce 的重复请求可以重复响应,但不能触发配置或重启 MQTT。
  6. 非法 JSON、过期请求和包长度超过 4KB 直接丢弃,不返回错误广播。

3.3 响应格式 discovery.response.v1

{
  "schema": "discovery.response.v1",
  "request_id": "7ba5a3ea-3ca4-45fe-b59d-1dc47b6ab5a1",
  "device_id": "SN-20260819-0001",
  "device_code": "EDGE-A01",
  "device_type": "lpr-gate",
  "device_model": "LPR-GW-200",
  "firmware_version": "2.1.0",
  "ip": "192.168.10.26",
  "config_url": "https://192.168.10.26:8443/api/v1/provision",
  "public_key_fingerprint": "SHA256:4A8F...",
  "signing_public_key": "base64-ed25519-public-key",
  "nonce": "base64-32-random-bytes",
  "signature": "base64-ed25519-signature"
}

字段要求:

字段 要求
ip 必须是设备当前 IPv4 地址,并且等于设备发送 UDP 响应时的本地地址;不要填管理系统地址或旧地址
config_url 必须使用 https,主机必须等于 ip;端口必须是设备实际 HTTPS 配置端口
public_key_fingerprint signing_public_key 原始 32 字节 Ed25519 公钥的 SHA-256;支持 SHA256: 前缀,推荐十六进制大写或小写均可
signing_public_key Base64 编码的 Ed25519 公钥;每台设备固定,不因每次响应变化
signature Base64 编码的 Ed25519 签名

当前管理系统验证的签名原文是以下 6 个字段按顺序以单个换行符连接,不能添加空格、JSON 格式或末尾换行:

schema
request_id
device_id
ip
config_url
nonce

例如:

discovery.response.v1
7ba5a3ea-3ca4-45fe-b59d-1dc47b6ab5a1
SN-20260819-0001
192.168.10.26
https://192.168.10.26:8443/api/v1/provision
base64-32-random-bytes

管理系统会校验 schema、request_id、nonce、UDP 源 IP、config_url 主机、签名和公钥指纹。设备不得把发现响应当作已信任或已配置;自动发现后仍必须通过 HTTPS 身份读取和管理员确认。

4. HTTPS 身份和配对对接

4.1 身份接口

管理系统请求:

GET /api/v1/identity HTTP/1.1
Host: <device-ip>:<https-port>
Accept: application/json

成功响应 HTTP 200:

{
  "device_id": "SN-20260819-0001",
  "device_code": "EDGE-A01",
  "device_type": "lpr-gate",
  "device_model": "LPR-GW-200",
  "firmware_version": "2.1.0",
  "certificate_fingerprint": "SHA256:ABCDEF...",
  "public_key_fingerprint": "SHA256:4A8F...",
  "pairing_required": true
}

字段处理:

  • device_id 必填且永久不变;为空时管理系统会拒绝设备。
  • certificate_fingerprint 应为当前 HTTPS 服务器证书 DER 原文的 SHA-256。设备返回值必须与实际 TLS 证书一致。
  • public_key_fingerprint 是设备签名公钥指纹,可与 UDP 响应中的值一致。
  • pairing_required=true 时,管理系统会要求管理员输入一次性配对码。
  • 身份接口不得因为未配置 MQTT 而失败;身份接口是首次接入的前置接口。

4.2 证书和 TLS 要求

设备必须:

  1. 只接受 HTTPS,不提供把配置数据明文放在 HTTP 或 UDP 中的兼容路径。
  2. 使用 TLS 1.2 或更高版本。
  3. 在整个设备生命周期内保持证书稳定;证书更换必须有明确的重新配对流程。
  4. 正确返回证书链和服务器名称/IP 对应信息。出厂自签名证书可以使用,但必须让管理员能够从设备标签、二维码或受控维护界面取得指纹。
  5. 对配置接口进行请求大小限制、超时限制和并发限制,防止配置服务被当作通用代理。

管理系统首次读取身份时会获取 TLS 对端证书指纹;管理员必须把页面指纹与设备标签或二维码核对。管理系统不会因为 IP 可达就自动信任设备。

4.3 配对接口

当身份响应 pairing_required=true 时,管理系统请求:

POST /api/v1/pair HTTP/1.1
Content-Type: application/json

{"pairing_code":"ONE-TIME-CODE"}

成功返回任意 2xx,建议返回:

{
  "result": "paired",
  "device_id": "SN-20260819-0001",
  "expires_at": 1760003600
}

设备实现要求:

  • 配对码必须是一次性或短时有效凭据,比较时使用常量时间比较。
  • 错误次数必须限流;连续失败不能擦除既有 MQTT 配置。
  • 成功后将本次管理系统或证书指纹标记为已配对,配对码不能再次使用。
  • 错误、过期、重复使用统一返回 4xx,并在设备安全日志中记录,不把配对码明文写入日志。
  • pairing_required=false 时,设备仍必须完成 HTTPS 证书指纹核对;不能仅凭此字段跳过管理员确认。

5. HTTPS 配置下发对接

5.1 请求格式

管理系统请求:

POST /api/v1/provision HTTP/1.1
Content-Type: application/json

当前管理系统实际发送的 JSON 字段如下:

{
  "schema": "provision.request.v1",
  "request_id": "f0f2f011-cc14-4965-8a61-9a65b3ec2f0b",
  "device_id": "SN-20260819-0001",
  "device_code": "GATE-A01",
	"mqtt": {
    "host": "192.168.10.10",
    "port": 1883,
    "tls": false,
    "client_id": "GATE-A01"
	},
	"image_upload": {
	  "url": "http://192.168.10.10:8888/device-images/upload",
	  "max_bytes": 8388608
	},
	"route": {
    "parking_lot_id": 2,
    "booth_id": 5,
    "channel_id": 8,
    "direction": "in"
  }
}

字段要求:

字段 设备端处理
schema 必须为 provision.request.v1
request_id 保存最近处理结果;相同请求重试应幂等返回,不重复生成凭据或反复重启
device_id 必须与本机永久 ID 完全一致,不一致返回 409 或 403
device_code 成功配置后作为 MQTT ClientID、消息 device_code 和业务设备编码;不能为空
mqtt.host 管理系统下发给设备的可达地址;不能把它替换为设备自身 IP
mqtt.port 1-65535;按 mqtt.tls 选择 TLS 或普通 MQTT
mqtt.tls 为 true 时必须校验 Broker 证书;不能降级为明文连接
mqtt.client_id 当前应与 device_code 相同,必须全局唯一
image_upload.url 边缘端主动上传抓拍图片的管理系统内网地址
image_upload.max_bytes 单张图片最大字节数;当前为 8MB
route.* 保存停车场、岗亭、通道数字 ID 和方向;Topic 使用这些数字 ID,不是编码字符串

当前版本管理系统没有在 JSON 中发送 MQTT 用户名、密码、topic_prefixconfig_versionnonce 或请求签名字段。设备端必须忽略未知字段,但不能自行要求当前版本不存在的必填字段,否则会导致接入失败。生产环境启用设备级账号、双向 TLS 或配置签名时,应通过协议版本升级增加这些字段,并保持旧版本的明确兼容策略。

5.2 原子保存和响应

设备收到合法配置后必须按以下顺序处理:

  1. 校验 HTTPS 会话已通过证书指纹/配对信任,校验 device_iddevice_code、端口、Topic 坐标和方向。
  2. 校验 mqtt.host 是设备实际可达的单播地址;禁止 127.0.0.1localhost0.0.0.0、广播和组播地址。
  3. 将新配置写入临时文件或事务存储,完成校验和后原子替换正式配置。
  4. 配置写入失败时保留旧配置,并返回 4xx/5xx;不能只写入 Broker 地址而丢失通道坐标。
  5. 成功保存后返回 HTTP 200(或 202):
{
  "result": "accepted",
  "request_id": "f0f2f011-cc14-4965-8a61-9a65b3ec2f0b",
  "message": "configuration saved; mqtt reconnecting"
}

accepted 只表示设备已经保存配置,不表示已经在线。返回响应后设备应异步重启 MQTT 客户端,不要让 HTTPS 请求等待 Broker 连接完成。

建议错误响应:

HTTP 场景 设备行为
400 JSON、字段、端口或 Topic 坐标无效 不改配置,返回字段错误
401/403 未配对或权限不足 不改配置,记录安全事件
409 device_idrequest_id 或配置版本冲突 返回现有结果或冲突原因,不覆盖配置
429 配置/配对请求过于频繁 延迟重试,不修改配置
500 本地存储失败 保留旧配置,返回失败

6. 两种接入方式的设备行为差异

6.1 自动发现

设备需要额外实现 UDP 监听和签名响应。完整过程:

启动设备
  └─ 监听 UDP :31001、启动 HTTPS 身份服务
管理系统广播 discovery.request.v1
  └─ 设备校验 request_id/nonce/过期时间
  └─ 设备单播 discovery.response.v1
管理系统通过 HTTPS GET /identity
  └─ 管理员核对证书指纹和配对码
管理系统 HTTPS POST /provision
  └─ 设备原子保存 MQTT 配置并返回 accepted
设备连接 MQTT
  └─ 清除旧 LWT retained
  └─ 发布 retained gate.state.v1

UDP 响应不等于配置授权。设备不能收到发现请求后自动修改 device_code、业务通道或 MQTT 配置,所有修改必须来自已配对的 HTTPS 配置请求。

6.2 手工 IP 接入

设备不需要收到 UDP 请求,也不需要知道管理系统 IP;只要管理员能够从管理系统访问设备的 HTTPS IP 和端口即可:

设备启动 HTTPS :8443
管理员在系统输入设备 IP/端口
  └─ 管理系统 GET /api/v1/identity
  └─ 管理员核对证书指纹,输入配对码
  └─ 管理系统 POST /api/v1/pair(需要时)
  └─ 管理系统 POST /api/v1/provision
设备连接 MQTT
  └─ 清除旧 LWT retained
  └─ 发布 retained gate.state.v1

手工 IP 常用于跨 VLAN、UDP 广播被禁、无线隔离、VPN 或设备不支持发现协议的网络。设备端无需为这些场景降低 TLS、永久 ID 或配对校验要求。

7. MQTT 配置完成后的行为

7.1 连接和 Topic

设备从 provision 中读取:

Broker: mqtt[ s ]://<mqtt.host>:<mqtt.port>
ClientID: <mqtt.client_id>,当前必须等于 device_code
Keep Alive: 30 秒
QoS: 1

Topic 模板:

parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/state
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/lwt
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/cmd
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/cmd/ack
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/camera/event

设备只订阅自己的 gate/cmd。不要订阅 # 或其他停车场的 Topic。

7.2 上线顺序

连接 MQTT 后必须:

  1. CONNECT 时设置 LWT Topic 为自己的 gate/lwt,Payload 为:
   {"schema":"gate.lwt.v1","device_code":"GATE-A01"}

LWT 使用 QoS 1、retained=true。

  1. CONNACK 成功后订阅自己的 gate/cmd
  2. 向自己的 gate/lwt 发布空 retained payload,清除旧的离线遗嘱。
  3. 立即发布 retained 的 gate.state.v1
   {
     "schema":"gate.state.v1",
     "device_code":"GATE-A01",
     "state":"closed",
     "ts":1760000000
   }
  1. 后续状态变化继续发布 gate.state.v1;断开连接时由 Broker 发布 LWT。

管理系统等待最多约 30 秒接收 gate.state.v1。只有收到状态消息,设备才会从“配置已保存”变成“在线”。

7.3 道闸指令和回执

收到 gate.cmd.v1 后,设备必须校验 device_codeactionvalid_time,执行动作后在 5 秒内向 gate/cmd/ack 发布:

{
  "schema":"gate.ack.v1",
  "cmd_id":"3f2a1b9c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
  "device_code":"GATE-A01",
  "result":"success",
  "error":""
}

失败时 resultfailederror 使用可读但不包含密钥的诊断信息。相同 cmd_id 的重复命令应幂等处理,不能因为 MQTT QoS 1 重投而连续开闸。

7.4 抓拍图片 HTTP 主动上传

每次识别后,边缘端必须先通过 image_upload.url 上传图片,成功获得 image_id 后再发布 MQTT 识别事件。不得在 MQTT 中发送 Base64 图片或图片二进制数据。

POST /device-images/upload HTTP/1.1
Content-Type: multipart/form-data

表单字段:device_code(当前 MQTT 设备编码)、image_id(每张图片全局唯一 UUID)、event_id(同一次识别 UUID)、directionin / out)、image(JPEG/PNG/WebP 图片,最大 8MB)。成功响应的 data.image_id 与请求值相同。网络超时后可使用相同的 device_code + image_id 重试,管理系统会返回已有图片记录,不重复保存。

联调时管理端会记录以下结构化日志(不记录图片内容):

  • 边缘设备图片上传结果:包含 resultremote_addrdevice_codeimage_idevent_idcontent_typesizeidempotentduration
  • 边缘设备图片上传失败:包含失败阶段、来源地址、设备编码、图片 ID、耗时和错误原因。常见阶段包括设备未配置、缺少文件、大小超限、格式校验、对象存储和数据库保存。
  • 收到车辆识别 MQTT 事件车辆识别事件字段已解析:确认 MQTT 事件已经到达并展示 event_idimage_id、车牌和方向。
  • 车辆识别事件图片引用校验成功:确认 MQTT 引用已经匹配到该设备上传的图片;若失败会记录 车辆识别事件引用的图片不可用
  • 处理车辆识别事件:包含通行处理结果和总耗时。

上传成功后发布:

{
  "schema": "camera.event.v1",
  "event_id": "3e0d2bb5-74bd-4a2c-a5d8-6c3ac6472a1b",
  "image_id": "447a82ca-7e02-4dd4-9e78-d865f84e3c1d",
  "device_code": "GATE-A01",
  "plate_number": "粤A12345",
  "direction": "in",
  "ts": 1760000000
}

管理系统只接受已由同一 device_code 上传的 image_id;图片尚未上传、上传失败或属于其他设备时,该识别事件不进入通行流程。为兼容存量固件,当前管理系统暂时仍接受旧 image_path 字段,但新固件必须迁移到 image_id

8. 设备端状态机和重试

建议实现以下状态:

状态 进入条件 退出条件
unprovisioned 出厂或恢复出厂 收到合法并已配对的配置
identity_ready HTTPS 身份服务可用 配置成功或重新配对
provisioning 正在校验/保存配置 原子保存成功或失败
provisioned 已保存 MQTT 配置 发布在线状态或配置失败
online 收到 MQTT CONNACK 并发布 gate.state.v1 MQTT 断开
offline LWT、连接失败或心跳超时 MQTT 重连并重新发布状态
failed 配置或本地存储失败 管理员修正后重新配置

重试要求:

  • MQTT 连接失败使用指数退避,建议 1s、2s、4s、8s、15s,上限 60s。
  • HTTPS 配置请求失败时保留旧的可运行 MQTT 配置;不要把设备置于无配置状态。
  • request_id 重试必须返回同一处理结果,不能重复生成凭据或重复清理数据。
  • 设备重启后从持久化配置恢复 MQTT、Topic 和业务坐标,并重新发布 retained state。
  • 恢复出厂必须显式清除 MQTT 凭据、业务坐标、配对信任和旧的请求幂等记录,但不能清除制造商永久 device_id 和签名密钥。

9. 安全要求

边缘端必须满足:

  1. device_id 永久且不可由页面输入覆盖。
  2. HTTPS 配置接口只能由已建立信任的管理系统调用;设备不能仅凭来源 IP 信任请求。
  3. 配对码一次性、短时有效、限流,日志不记录明文。
  4. 签名私钥存储在受保护区域;UDP 响应只暴露公钥和指纹。
  5. MQTT 凭据不能写入 UDP 响应、普通日志或识别事件。
  6. mqtt.tls=true 时必须验证 Broker 证书,禁止自动降级为明文。
  7. 只处理自己的 MQTT Topic;拒绝 Topic 路由坐标与本地配置不一致的指令。
  8. 配置更新必须原子化并可回滚;断电不能产生半份配置。
  9. HTTPS、UDP 和 MQTT 输入都要限制包体、字段长度和请求频率。
  10. 记录审计所需的 request ID、结果和错误码,但不得记录配对码、私钥或长期密码。

10. 联调验收清单

自动发现

  • 设备在 0.0.0.0:31001 监听并能收到定向广播。
  • 设备能校验 schema、过期时间、request ID 和 nonce。
  • 响应单播回管理系统 UDP 源地址和源端口。
  • ipconfig_url、签名和公钥指纹满足格式要求。
  • 错误签名、过期请求、伪造 request ID 和超过 4KB 的包被丢弃。
  • 自动发现后仍必须通过 HTTPS 身份、证书指纹和配对确认。

手工 IP

  • 不依赖 UDP 也能通过 HTTPS 读取身份。
  • HTTPS 端口可配置,默认 8443,证书指纹稳定。
  • 永久 ID 与设备标签/二维码一致。
  • 正确配对码成功,错误/过期/重复配对码失败并限流。
  • 未配对或 device_id 不一致的 /provision 不修改配置。

配置和 MQTT

  • /provision 成功时配置原子保存并返回 result=accepted
  • 配置失败时旧配置仍可运行。
  • 设备按下发坐标生成自己的 Topic 并只订阅自己的命令 Topic。
  • MQTT 重连后先清除 retained LWT,再发布 retained gate.state.v1
  • 管理系统在 30 秒内收到状态并显示在线。
  • gate.cmd.v1 在 5 秒内返回匹配 cmd_idgate.ack.v1
  • 断电/断网可触发 LWT,恢复后可重新上线。
  • 同一 request_idcmd_id 重试不会造成重复配置或重复开闸。

11. 版本兼容约定

当前管理系统实现以本文第 5.1 节的 provision.request.v1 字段为准,MQTT 默认可能使用匿名连接,具体地址由管理系统 mqtt.advertised-hostmqtt.advertised-port 下发。边缘端应忽略未知 JSON 字段并保留向后兼容能力。

后续启用设备级 MQTT 账号、双向 TLS、配置请求签名、expires_atnonceconfig_version 时,管理系统会增加协议版本或字段约束。设备端应将协议版本、已处理的 request_id 和配置版本持久化,以便平滑升级和幂等重试。