docs(vd_960): MQTT 协议并入 Loop 远程 OTA → V1.08 (ROADMAP P1.4 ①)
- DLD960_IoT_MQTT协议.md V1.07→V1.08: 命令表加 ota_begin/ota_data/ota_end/ ota_abort/ota_flash/ota_status/ota_report; §4.19~4.24 命令详情; §5.5 ota_report; event_report 扩展 type=ota_error; §2.2 OTA 细分错误码 (err_code); 修订记录 - DLD960_MQTT_OTA协议.md V1.01 (按修改意见): Slot A/B 100KB / 维持 Loop 现状 / 会话期间静默 / 非阻塞 tick 驱动 - README 协议索引 + 技术规格书 §5.1 协议矩阵同步 V1.08 (CRLF 保持)
This commit is contained in:
@@ -96,6 +96,19 @@ dld960/{dev_serial}/{direction}
|
||||
| 5 | 内部错误 |
|
||||
| 6 | 数据超长 |
|
||||
|
||||
### OTA 细分错误码(V1.08)
|
||||
|
||||
顶层 `code` 保持通用语义,`ota_*` 命令的细分错误经响应 `data.err_code` 表达:
|
||||
|
||||
| err_code | 场景 | 顶层 code |
|
||||
|----------|------|-----------|
|
||||
| 1 | 单片 CRC 失败 / 乱序缺片(`data.offset` 指示续传点) | 1 |
|
||||
| 2 | 会话状态不允许(未 begin / 非 downloading 收 ota_data) | 3 |
|
||||
| 3 | 安全窗口拒绝(有车压线圈) | 3 |
|
||||
| 4 | 版本冲突(同版本且非 force) | 1 |
|
||||
| 5 | 全镜像 CRC 不匹配(`data.crc_ok=false`) | 5 |
|
||||
| 6 | 暂存区写失败(SPI 异常/满) | 5 |
|
||||
|
||||
## 2.3 设备时钟同步(`ts` 语义)
|
||||
|
||||
设备无 RTC/SNTP 时间源,上电后本地时钟为**上电秒数**(从 0 递增)。为使上行数据带真实 Unix 时间:
|
||||
@@ -140,9 +153,16 @@ dld960/{dev_serial}/{direction}
|
||||
| `log_stat` | 查询脱机日志统计(事件/快照流) | srv→dev | — |
|
||||
| `log_query` | 分页拉取脱机日志(事件/快照流) | srv→dev | — |
|
||||
| `log_clear` | 清除脱机日志(事件/快照流,审计留痕) | srv→dev | — |
|
||||
| `ota_begin` | 开启 OTA 会话 / 断点续传定位 | srv→dev | — |
|
||||
| `ota_data` | OTA 分片下发(256B/片,单片 CRC32) | srv→dev | — |
|
||||
| `ota_end` | 结束 OTA 下载,全镜像 CRC32 复核 | srv→dev | — |
|
||||
| `ota_abort` | 中止 OTA 会话,释放暂存 | srv→dev | — |
|
||||
| `ota_flash` | 触发本地 ISP 刷写(仅 ready 态) | srv→dev | — |
|
||||
| `ota_status` | 查询 OTA 状态(含进度) | srv→dev | — |
|
||||
| `initialize` | 设备上电初始化登陆 | dev→srv | — |
|
||||
| `loop_data` | 线圈传感数据上报 | dev→srv | 0xC0 |
|
||||
| `event_report` | 事件上报(**平台须应答**,见 §5.3) | dev→srv | — |
|
||||
| `ota_report` | OTA 进度/结果主动上报 | dev→srv | — |
|
||||
| `heartbeat` | 设备心跳 | dev→srv | — |
|
||||
|
||||
---
|
||||
@@ -798,6 +818,246 @@ dld960/{dev_serial}/{direction}
|
||||
|
||||
---
|
||||
|
||||
## 4.19 开启 OTA 会话 `ota_begin`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
> 依据《DLD960_MQTT_OTA协议.md》设计稿 V1.01(ROADMAP P1.4 ①:Loop MCU 远程 OTA,先存后刷)。
|
||||
> 能力探测:老固件(V1.07 及以下)无 `ota_*` 命令,收到回 `code=4`;平台下发前用 `ota_status` 或 `dev_info_query.soft_ver` 判断。
|
||||
|
||||
**请求:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 401,
|
||||
"cmd": "ota_begin",
|
||||
"ts": 1719000000,
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"size": 46864,
|
||||
"crc32": 305419896,
|
||||
"version": "1.1.0",
|
||||
"force": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| data 字段 | 类型 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `target` | string | `loop`(当前支持);`dbn` 预留 |
|
||||
| `size` | uint32 | 镜像 bin 字节数(≤ 98304 = 96KB;Slot 数据区 100KB 预留 4KB 边界余量) |
|
||||
| `crc32` | uint32 | 全镜像 CRC32(十进制,算法见 §4.19.1) |
|
||||
| `version` | string | 目标固件版本(写入元数据,审计用;bootloader 不校验版本) |
|
||||
| `force` | bool | `true` = 覆盖现有暂存镜像 / 忽略版本冲突(默认 false) |
|
||||
|
||||
**设备行为:**
|
||||
1. 读暂存元数据:若已有镜像且 `size+crc32` 与本次一致 →
|
||||
- `state=ready` → 返回 `offset=size`(平台可直接 `ota_flash`)
|
||||
- `state=downloading` → 返回 `offset=received`(断点续传)
|
||||
2. 不一致 → 分配 Slot(A 当前 / B 回滚),写元数据 `state=downloading, received=0`,返回 `offset=0`
|
||||
3. `force=false` 且目标版本 == 当前运行版本 → 回 `code=1, err_code=4`(防重复刷写,可 force 绕过)
|
||||
|
||||
**响应 data:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 401,
|
||||
"cmd": "ota_begin",
|
||||
"ts": 1719000001,
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"slot": "a",
|
||||
"offset": 0,
|
||||
"received": 0,
|
||||
"size": 46864,
|
||||
"crc32": 305419896,
|
||||
"state": "downloading"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.19.1 CRC32 算法(必须双方一致)
|
||||
|
||||
标准 **CRC-32/ISO-HDLC**:poly `0x04C11DB7`(reflected `0xEDB88320`),init `0xFFFFFFFF`,refin/refout true,xorout `0xFFFFFFFF`。平台侧 Python `zlib.crc32()` / `binascii.crc32()` 即此算法;设备侧查表法。全镜像 CRC = offset 0 ~ size-1 连续;单片 CRC = 仅该 256B。
|
||||
|
||||
---
|
||||
|
||||
## 4.20 OTA 分片下发 `ota_data`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
> 单片大小 **256B** 是协议常量,不接受协商;hex 512 字符 + JSON 外壳 ≈ 640B < 设备接收缓冲 1024B。
|
||||
|
||||
**请求:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 402,
|
||||
"cmd": "ota_data",
|
||||
"ts": 1719000002,
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"offset": 0,
|
||||
"crc32": 2524764894,
|
||||
"data": "6a6173646f6e...(512 hex 字符 = 256B)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| data 字段 | 类型 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `target` | string | 同 `ota_begin` |
|
||||
| `offset` | uint32 | 本片在镜像中的绝对偏移(**256 对齐**,首片 0) |
|
||||
| `crc32` | uint32 | 本片 256B 的 CRC32(十进制) |
|
||||
| `data` | string | 256B 原始字节小写 hex,512 字符 |
|
||||
|
||||
**设备行为:**
|
||||
1. `offset == received`(顺序片)→ 单片 CRC32 校验 → 写 W25Qxx 暂存(256B 页对齐)→ `received += 256`(≥size 截断为 size)→ `code=0`
|
||||
2. `offset < received`(重复片,平台重发)→ **幂等回 `code=0`**,不重写
|
||||
3. `offset > received`(缺片/乱序)→ `code=1, err_code=1` + `data.offset=received`(指示平台从该处续传;协议不要求乱序重组)
|
||||
4. 单片 CRC 失败 → `code=1, err_code=1`(平台重发本片;连续失败平台可 `ota_abort`)
|
||||
5. 会话未开始 / 状态非 downloading → `code=3, err_code=2`
|
||||
6. `data` 非 512 hex / offset 非 256 对齐 / 超 size → `code=1` 参数错误
|
||||
|
||||
**响应:** 标准成功/失败 + data 回显 `{offset, received}`。
|
||||
|
||||
---
|
||||
|
||||
## 4.21 结束下载 `ota_end`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
|
||||
**请求:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 403,
|
||||
"cmd": "ota_end",
|
||||
"ts": 1719000003,
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"crc32": 305419896
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**设备行为:**
|
||||
1. `received != size` → `code=1` + `data.offset=received`(不完整,续传)
|
||||
2. `received == size` → 读回暂存区全镜像计算 CRC32,与 ota_begin 声明值比对
|
||||
- 一致 → 元数据 `state=ready, last_result=0` → `code=0, data={crc_ok:true}`
|
||||
- 不一致 → 元数据 `state=downloading`(保留已下载数据,可重发错片)→ `code=5, err_code=5, data={crc_ok:false}`
|
||||
|
||||
---
|
||||
|
||||
## 4.22 中止会话 `ota_abort`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 404,
|
||||
"cmd": "ota_abort",
|
||||
"ts": 1719000004,
|
||||
"data": { "target": "loop" }
|
||||
}
|
||||
```
|
||||
|
||||
设备行为:元数据 `state=aborted`,Slot 标记可覆盖;正在刷写时 abort → 停止发送后续 A7 块(Loop 端由 bootloader 超时复位回 APP 兜底)。响应标准成功。
|
||||
|
||||
---
|
||||
|
||||
## 4.23 触发刷写 `ota_flash`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
> ⚠ **会车安全关键命令**:平台应确认现场允许(无车压线圈、非高峰)再下发。
|
||||
|
||||
**请求:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 405,
|
||||
"cmd": "ota_flash",
|
||||
"ts": 1719000005,
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"slot": "a",
|
||||
"force": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| data 字段 | 类型 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `slot` | string | `a` / `b`(缺省 = 当前 ready 的槽) |
|
||||
| `force` | bool | `true` = 跳过安全窗口检查(高风险,平台授权) |
|
||||
|
||||
**设备行为(同步检查 → 异步刷写):**
|
||||
|
||||
1. **安全窗口检查**(force=false 时):4 通道 Loop 有车(VD_FLAG 任一置位)→ `code=3, err_code=3`(有车,拒绝;平台提示"车辆离开后重试")。刷写期间会阻断检测与继电器控制 → 平台建议低峰执行
|
||||
2. 元数据非 ready → `code=3`(先 `ota_end` 完成校验)
|
||||
3. 通过 → 立即回 `code=0`(异步),进入刷写:
|
||||
- 写 offlog 事件日志:`固件升级开始`(target/slot/version/size)
|
||||
- **暂停事件上报与脱机日志(会话期间)**:MQTT `event_report` 暂停发送(入队积压,16 深溢出丢最旧,会话结束恢复后补发);offlog/快照落盘暂停("升级开始"日志在暂停前写入、"升级结果"在恢复后补记)
|
||||
- 维持 Loop 现状:复位进 bootloader 后的 GPIO/继电器状态与现网 BLE OTA 升级一致,不额外干预
|
||||
- 发 `9F 01 00 01 A5 A7` 启动帧 → Loop APP 写 flag 复位 → bootloader 回 pre_ok
|
||||
- 发 A6 地址帧(`0x08003400` 4 字节大端)→ addr_ok
|
||||
- 从 W25Qxx 暂存读镜像,按 ≤254B/块发 A7(**非阻塞 tick 驱动**:每轮主循环发送 1~2 块并检查 ACK,绝不阻塞主循环,保证刷写窗口内 MQTT PINGREQ/心跳/IWDG 喂狗正常);停等 ACK,1s 超时重发 ×3
|
||||
- 末块(sub_amount=1)→ bootloader 写剩余 → 清 flag → 复位跑新 APP
|
||||
4. 进度经 `ota_report` 上行(§5.5);失败重试 ×3 仍失败 → 元数据 `state=flash_failed` + `event_report{type:ota_error}` 告警(平台必答)
|
||||
|
||||
**响应:** `code=0` 仅表示已启动,不代表刷写成功——结果以 `ota_report` / `ota_status` 为准。
|
||||
|
||||
---
|
||||
|
||||
## 4.24 查询状态 `ota_status`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
|
||||
**请求:**
|
||||
|
||||
```json
|
||||
{ "msg_id": 406, "cmd": "ota_status", "ts": 1719000006 }
|
||||
```
|
||||
|
||||
**响应 data:**
|
||||
|
||||
```json
|
||||
{
|
||||
"target": "loop",
|
||||
"state": "flashing",
|
||||
"slot": "a",
|
||||
"size": 46864,
|
||||
"received": 46864,
|
||||
"crc32": 305419896,
|
||||
"version": "1.1.0",
|
||||
"progress": { "sent": 42112, "total": 46864 },
|
||||
"last_result": 0,
|
||||
"last_error": 0
|
||||
}
|
||||
```
|
||||
|
||||
| data 字段 | 类型 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `state` | string | `idle` / `downloading` / `ready` / `flashing` / `flash_failed` / `aborted` |
|
||||
| `progress.sent` | uint32 | 刷写阶段已送 Loop 的字节数 |
|
||||
| `last_result` | uint32 | 上次刷写结果:0=无/成功,非 0=错误码 |
|
||||
| `last_error` | uint32 | 上次失败细分错误码 |
|
||||
|
||||
**设备状态机:**
|
||||
|
||||
```
|
||||
ota_begin(新会话) ota_data×N ota_end(CRC✓)
|
||||
IDLE ─────────────────▶ DOWNLOADING ────────────▶ READY
|
||||
▲ │ ▲ │
|
||||
│ ota_abort │ │ ota_end(CRC✗) │ ota_flash(安全检查✓)
|
||||
│ / flash_failed │ └──────────────┐ ▼
|
||||
└────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ (Loop 重启) ──▶ IDLE(清槽/保留)
|
||||
│
|
||||
└──失败×3──▶ FLASH_FAILED ──ota_abort/ota_begin──▶ IDLE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 5 设备主动上报
|
||||
|
||||
## 5.1 设备上电登陆信息 `initialize`
|
||||
@@ -967,6 +1227,7 @@ dld960/{dev_serial}/{direction}
|
||||
| `car_leave` | 车辆离开 | 通过时间 (×50ms) |
|
||||
| `loop_cut` | 线圈断开 | 0 |
|
||||
| `loop_restore` | 线圈恢复 | 断开持续时长 (×50ms) |
|
||||
| `ota_error` | OTA 刷写失败告警(重试×3 仍失败,V1.08) | 0x1001=启动帧无响应 / 0x1002=地址帧错误 / 0x1003=数据块 ACK 超限 / 0x1004=全镜像校验失败 / 0x1005=安全窗口拒绝后强制失败 |
|
||||
|
||||
**平台应答(srv topic,收到后必须立即回复):**
|
||||
|
||||
@@ -1040,6 +1301,39 @@ dld960/{dev_serial}/{direction}
|
||||
| `net_status` | bool | 以太网连接状态 |
|
||||
| `iot_status` | bool | MQTT 连接状态 |
|
||||
|
||||
## 5.5 OTA 进度/结果上报 `ota_report`
|
||||
|
||||
> Topic: `dld960/{sn}/dev` · QoS 1
|
||||
> 用途:OTA 会话与刷写进度/结果主动推送(V1.08)。进度可丢,结果可经 `ota_status` 兜底查询(设备侧元数据持久化 `last_result`)。
|
||||
|
||||
**上报:**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg_id": 501,
|
||||
"cmd": "ota_report",
|
||||
"ts": 1719000007,
|
||||
"data": {
|
||||
"target": "loop",
|
||||
"stage": "flashing",
|
||||
"progress": { "sent": 42112, "total": 46864 },
|
||||
"code": 0,
|
||||
"msg": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| data 字段 | 说明 |
|
||||
|-----------|------|
|
||||
| `target` | 目标:`loop`(当前支持) |
|
||||
| `stage` | `begin`(会话开启)/ `downloading`(片落盘)/ `ready`(校验通过)/ `flashing`(刷写中)/ `done`(刷写成功,Loop 已重启)/ `failed`(刷写失败) |
|
||||
| `progress` | `sent`/`total` 字节(刷写阶段) |
|
||||
| `code` | stage 相关结果码 |
|
||||
|
||||
**上报节奏**:`begin`/`ready`/`done`/`failed` 各 1 次;`flashing` 阶段按进度节流(建议每 64 块或每 8KB 一次,避免刷写期间消息风暴)。`done`/`failed` 设备侧重发 3 次(间隔 5s,同 `msg_id`/`ts`),平台去重窗口建议 10 分钟(与 `event_report` 同策略)。
|
||||
|
||||
**会话期间静默**(V1.08):OTA 会话期间(`ota_begin` ~ 结束)设备暂停 `event_report` 发送(入队积压,结束后补发)与 offlog/快照落盘("升级开始"日志暂停前写入、"升级结果"恢复后补记),保证刷写窗口内 MQTT 保活与 IWDG 喂狗不受影响。
|
||||
|
||||
---
|
||||
|
||||
# 修订记录
|
||||
@@ -1054,4 +1348,5 @@ dld960/{dev_serial}/{direction}
|
||||
| V1.05 | 2026-07-15 | 增加**设备时钟同步**(§2.3,方案B):设备无 RTC,`initialize` 上线后平台经 `report_config` 命令下发 Unix `ts`,设备据此校准,之后上行 `ts` 为真实 Unix 时间;校准前为上电秒数 | wangfq |
|
||||
| V1.06 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);§2.3 补充日志双时间戳语义(`ts_ms` 相对 + `unix_ts` 已同步,0=未同步,锚点回算规则) | wangfq |
|
||||
| V1.07 | 2026-08-18 | `log_stat` / `log_query` / `log_clear` 增加**快照流**支持(`stream=snapshot`,与 BLE 0x28/0x29/0x2A 同语义):快照统计 capacity 随芯片动态(48064~449472)、快照分页 count≤1(4 通道记录 JSON ~810B 超发送缓冲,实测修正;BLE 原始通道仍 ≤2)、快照清除审计留痕;`capacity`/`count` 类型修正为 uint32(W25Q256 事件流 130944 超 16bit) | wangfq |
|
||||
| V1.08 | 2026-08-20 | 增加 **Loop MCU 远程 OTA**(ROADMAP P1.4 ①,先存后刷):命令 `ota_begin` / `ota_data` / `ota_end` / `ota_abort` / `ota_flash` / `ota_status`(srv→dev)+ `ota_report`(dev→srv);单片 256B + 单片/全镜像 CRC32(ISO-HDLC,§4.19.1);断点续传(`ota_begin` 返回 offset);Slot A/B 双槽回滚(镜像 ≤96KB);`ota_flash` 安全窗口检查 + 非阻塞 tick 驱动刷写(刷写窗口内 MQTT 保活/IWDG 不受影响);会话期间暂停 `event_report` 发送与脱机日志落盘;`event_report` 扩展 `type=ota_error` 失败告警;§2.2 补 OTA 细分错误码(err_code);老固件兼容(`ota_*` 回 code=4) | wangfq |
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ DLD960 是一款基于环形线圈(LC 振荡)检测原理的四通道车辆
|
||||
|------|------|------|------|
|
||||
| DLD960 串口通信协议 | V1.01 | TTL | 设备管理、参数配置、数据上报 |
|
||||
| DLD960 TCP JSON 协议 | V1.03 | ETH :5960 | 密码鉴权 + 18 条命令;event_report 客户端必答(5s×3 重发);脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分) |
|
||||
| DLD960 IoT MQTT 协议 | V1.07 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步、脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分) |
|
||||
| DLD960 IoT MQTT 协议 | V1.08 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步、脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分);Loop 远程 OTA(ota_begin/ota_data/ota_end/ota_abort/ota_flash/ota_status + ota_report,256B/片 CRC32,先存后刷断点续传,Slot A/B 双槽回滚) |
|
||||
| DLD960Loop 串口协议 | V1.05 | MCU 间(内部) | 0x7F 帧、0xC0 传感上报(variation 3B 有符号) |
|
||||
| DLD960 BLE 协议 | V1.02 | 蓝牙 BLE | 帧格式 + 分包;脱机日志 OFFLOG_STAT/QUERY/CLEAR + 传感快照 SNAP_STAT/QUERY/CLEAR(0x28/0x29/0x2A),与 MQTT/TCP 同语义 |
|
||||
|
||||
@@ -186,6 +186,6 @@ DLD960 是一款基于环形线圈(LC 振荡)检测原理的四通道车辆
|
||||
|
||||
| 版本 | 修订时间 | 修订说明 | 修订人 |
|
||||
|------|----------|----------|--------|
|
||||
| V1.02 | 2026-08-19 | 配套整机发布 V1.02.03→V1.02.04:UART2 RX 0x9F OTA 透传机制说明(§2/§5/§7)、协议矩阵与版本配套矩阵更新、已知约束补充 OTA 无自动退出;存储适配(SPI 识别去厂商代码 + factory 写入恢复) | wangfq |
|
||||
| V1.02 | 2026-08-19 | 配套整机发布 V1.02.03→V1.02.04:UART2 RX 0x9F OTA 透传机制说明(§2/§5/§7)、协议矩阵与版本配套矩阵更新、已知约束补充 OTA 无自动退出;存储适配(SPI 识别去厂商代码 + factory 写入恢复);2026-08-20 协议矩阵 MQTT 同步 V1.08(Loop 远程 OTA 协议设计先行,固件待实现,Slot A/B 100KB) | wangfq |
|
||||
| V1.01 | 2026-08-18 | 配套整机发布 V1.02.01:版本矩阵更新、UART2 RX DMA 通信可靠性说明、已知约束补充 | wangfq |
|
||||
| V1.00 | 2026-07-16 | 初始版本,配套整机发布 V1.0.0 | wangfq |
|
||||
|
||||
Reference in New Issue
Block a user