Appearance
通信模型
本文档定义FDP-P2P的主题设计和消息结构体。
主题设计
命名规范
{namespace}/{username}/{action}| 组成部分 | 说明 | 示例 |
|---|---|---|
namespace | 应用命名空间,可自定义 | nodes、devices、myapp |
username | 节点唯一标识,通常与MQTT用户名一致 | device-001、app-user-1 |
action | 操作类型 | pending、ack、complete等 |
主题列表
| 主题 | 用途 |
|---|---|
{ns}/{username}/pending | 接收任务 |
{ns}/{username}/ack | 接收确认 |
{ns}/{username}/progress | 接收进度 |
{ns}/{username}/complete | 接收完成通知 |
{ns}/{username}/failed | 接收失败通知 |
{ns}/{username}/status | 节点状态广播 |
订阅与发布规则
接收消息(订阅自己的主题):
订阅 nodes/{我的username}/pending ← 接收任务
订阅 nodes/{我的username}/ack ← 接收确认
订阅 nodes/{我的username}/complete ← 接收完成
订阅 nodes/{我的username}/failed ← 接收失败发送消息(发布到对方主题):
发布到 nodes/{对方username}/pending → 发送任务
发布到 nodes/{对方username}/ack → 发送确认
发布到 nodes/{对方username}/complete → 发送完成
发布到 nodes/{对方username}/failed → 发送失败简化订阅
根据节点角色,可简化订阅:
- 只接收任务的设备:仅订阅
pending - 只发送任务的应用:仅订阅
ack、complete、failed - 不需要确认的场景:可不订阅
ack
消息结构体
MessagePending(任务消息)
发送任务时使用的消息结构。
json
{
"sender": "app-001",
"receiver": "device-001",
"msg_id": "550e8400-e29b-41d4-a716-446655440000",
"action": "unlock",
"time": 1700000000,
"exp": 1700000060,
"value": {"door": "front"}
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
sender | string | 是 | 发送者标识,用于回复消息 |
receiver | string | 是 | 接收者标识,决定发布到哪个主题 |
msg_id | string | 是 | 消息唯一ID,建议使用UUID |
action | string | 是 | 操作类型,业务自定义 |
time | int64 | 是 | 消息创建时间戳(秒) |
exp | int64 | 是 | 消息过期时间戳(秒) |
value | any | 否 | 业务数据,格式由action决定 |
sender字段安全提醒
sender字段可被伪造,协议层不验证其真实性。如需验证发送者身份,请使用FDP-Security安全协议。
MessageAck(确认消息)
确认任务已收到。
json
{
"msg_id": "550e8400-e29b-41d4-a716-446655440000"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msg_id | string | 是 | 对应的任务消息ID |
MessageProgress(进度消息)
报告任务执行进度。
json
{
"msg_id": "550e8400-e29b-41d4-a716-446655440000",
"value": 50
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msg_id | string | 是 | 对应的任务消息ID |
value | any | 是 | 进度信息,格式自定义 |
进度消息特点
- 完全可选,不影响任务结果
- 仅用于改善用户体验
- 建议使用 QoS 0,丢失不影响业务
- 格式由业务决定:百分比(0-100)、分数(1/5)等
MessageComplete(完成消息)
任务执行成功。
json
{
"msg_id": "550e8400-e29b-41d4-a716-446655440000",
"value": {"status": "unlocked", "battery": 85}
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msg_id | string | 是 | 对应的任务消息ID |
value | any | 否 | 返回值,格式由业务决定 |
MessageFailed(失败消息)
任务执行失败。
json
{
"msg_id": "550e8400-e29b-41d4-a716-446655440000",
"error": "door_jammed"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
msg_id | string | 是 | 对应的任务消息ID |
error | string | 是 | 错误信息 |
MessageStatus(状态消息)
节点状态广播,使用保留消息。
json
{
"time": 1700000000,
"online": true,
"battery": 85,
"rssi": -65,
"ip": "192.168.1.100",
"version": "1.2.0",
"uptime": 86400
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
time | int64 | 是 | 状态更新时间戳(秒) |
online | bool | 否 | 在线状态 |
battery | int | 否 | 电量百分比 0-100 |
rssi | int | 否 | 信号强度 dBm |
ip | string | 否 | IP地址 |
version | string | 否 | 软件版本 |
uptime | int64 | 否 | 运行时长(秒) |
data | any | 否 | 业务自定义数据 |
状态消息特点
- 使用保留消息(Retained=true),新订阅者立即收到最新状态
- 建议定期更新(如每分钟一次)
- 发送空载荷可删除保留消息
- 除
time外所有字段均可选
数据类型说明
value字段
value字段的类型由action决定,可以是:
- 基本类型:字符串、数字、布尔值
- 对象:JSON对象
- 数组:JSON数组
- 二进制:Base64编码的字符串
时间戳
所有时间戳字段均使用:
- 单位:秒(Unix时间戳)
- 类型:int64
结构体定义参考
Go
go
type MessagePending struct {
Sender string `json:"sender"`
Receiver string `json:"receiver"`
MsgId string `json:"msg_id"`
Action string `json:"action"`
Time int64 `json:"time"`
Exp int64 `json:"exp"`
Value any `json:"value,omitempty"`
}
type MessageAck struct {
MsgId string `json:"msg_id"`
}
type MessageProgress struct {
MsgId string `json:"msg_id"`
Value any `json:"value"`
}
type MessageComplete struct {
MsgId string `json:"msg_id"`
Value any `json:"value,omitempty"`
}
type MessageFailed struct {
MsgId string `json:"msg_id"`
Error string `json:"error"`
}
type MessageStatus struct {
Time int64 `json:"time"`
Online *bool `json:"online,omitempty"`
Battery *int `json:"battery,omitempty"`
Rssi *int `json:"rssi,omitempty"`
Ip string `json:"ip,omitempty"`
Version string `json:"version,omitempty"`
Uptime int64 `json:"uptime,omitempty"`
Data any `json:"data,omitempty"`
}TypeScript
typescript
interface MessagePending {
sender: string;
receiver: string;
msg_id: string;
action: string;
time: number;
exp: number;
value?: any;
}
interface MessageAck {
msg_id: string;
}
interface MessageProgress {
msg_id: string;
value: any;
}
interface MessageComplete {
msg_id: string;
value?: any;
}
interface MessageFailed {
msg_id: string;
error: string;
}
interface MessageStatus {
time: number;
online?: boolean;
battery?: number;
rssi?: number;
ip?: string;
version?: string;
uptime?: number;
data?: any;
}