Files
mqtt_power/LogBook.md
2025-06-07 21:37:31 +08:00

158 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 变更日志
## YYYY-MM-DD
- 初始化项目变更日志。
- 确认采用 Gitea Webhook + Shell 脚本的 CI/CD 方案。
- 部署脚本 (`deploy.sh`) 和相关配置已准备。
## YYYY-MM-DD (请替换为实际日期)
- 详细分析了项目整体结构,特别是单片机 (`mqtt_esp32_client.ino`) 与后端 (`springboot-init-main`) 之间基于 `README.md` 的 MQTT 通信流程和协议。
- **核心理解**:
- **主题**:
- 上行 (设备 -> 后端): `yupi_mqtt_power_project/robot/status/{spotUid}` (状态/心跳/ACK)
- 下行 (后端 -> 设备): `yupi_mqtt_power_project/robot/command/{spotUid}` (指令)
- **消息格式**: JSON。上行消息结构对应后端 `com.yupi.project.model.dto.mqtt.RobotStatusMessage`
- **关键上行消息字段**: `robotUid`, `actualRobotStatus`, `taskId` (for ACK), `status` (for ACK), `activeTaskId` (通常为 `sessionId`)。
- **关键下行消息字段**: `commandType` ("START_CHARGE", "STOP_CHARGE", 等), `taskId`, `sessionId` (for "START_CHARGE")。
- **通信流程**: 设备连接后订阅指令主题并上报初始状态。周期性上报状态和心跳。后端下发指令设备执行后通过ACK响应并更新状态。
- **`activeTaskId` 的作用**: 该字段在单片机的状态上报和ACK中被用来传递当前有效的 `sessionId`,对于后端跟踪会话状态至关重要。
- 单片机代码中包含对 `MOVE_TO_SPOT` 指令的处理,`README.md` 未明确列出,后端可能需同步支持。
- 后续 Debug 将基于此理解进行。
## YYYY-MM-DD (请替换为今天的实际日期)
- **核心逻辑变更:"到达即充电"**
- 移除了原先设想的由后端发送 `START_CHARGE` MQTT 指令来启动充电的流程。
- 新流程:机器人(单片机)在执行 `MOVE_TO_SPOT` 指令并成功到达车位后,立即将自身状态更新为 `CHARGING` 并上报。后端在收到 `MOVE_TO_SPOT` 任务的 ACK (其中应包含机器人已是 `CHARGING` 状态) 后将机器人DB状态更新为 `CHARGING`,并将充电会话状态更新为 `CHARGING_STARTED`
- **代码修改**:
- `mqtt_esp32_client.ino`:
- `MOVE_TO_SPOT` 指令处理逻辑修改:
- 收到指令后,状态变为 `MOVING` 并上报。
- 模拟移动到达后,状态变为 `CHARGING`
- 发送的 ACK 消息中 `actualRobotStatus` 反映为 `CHARGING`
- 再次上报 `CHARGING` 状态。
- `ChargingSessionServiceImpl.java`:
- `handleRobotArrival` 方法修改:
-`MOVE_TO_SPOT` 任务完成后,将充电会话状态 (`ChargingSession.status`) 直接更新为 `CHARGING_STARTED`
- 记录 `robotArrivalTime``chargeStartTime`
- 将数据库中机器人状态 (`ChargingRobot.status`) 更新为 `CHARGING`
- 确保关联的 `RobotTask` (移动任务) 被正确处理和完成。
- `README.md`:
- 更新了MQTT通信约定、消息示例、单片机开发关键逻辑以及示例充电流程以反映"到达即充电"的新业务规则。
- **待考虑/后续**:
- `ChargingSessionServiceImpl.handleChargingStart` 方法目前因 `START_CHARGE` 指令不再由后端主动发送而可能不再被主要流程调用,其定位和用途需要进一步明确或在未来重构中调整。
- 前端界面和用户交互流程可能需要相应调整,以匹配后端"到达即充电"的逻辑(例如,可能不再需要用户在机器人到达后点击"开始充电"按钮)。
## YYYY-MM-DD (自动填充日期)
### ESP32 固件与后端通信逻辑校准与文档更新
**主要变更点:**
1. **MQTT ACK 消息格式校准 (关键)**:
* **问题分析**: 通过深入分析后端代码 (`MqttMessageHandlerImpl.java``CommandTypeEnum.java`)发现后端期望的MQTT ACK消息格式与之前`README.md`及单片机固件发送的格式存在显著差异。
* 后端期望ACK中包含 `command_ack` 字段,其值为指令的**中文描述** (例如 "移动到指定点"),用于 `CommandTypeEnum.fromAck()` 方法匹配。
* 后端期望任务ID字段为 `task_id` (数字类型)。
* 后端期望成功状态字段为 `success` (布尔类型)。
* **`README.md` 更新**: 已更新 `README.md` (4.2.1节) 中的ACK消息示例以准确反映后端期望的格式。包括添加了 `command_ack`,并将 `taskId` 改为 `task_id``status` (String) 改为 `success` (Boolean)。
* **行动项 (开发者)**: **开发者需要修改 `esp32_robot_firmware/mqtt_handler.cpp` 中的 `mqtt_publish_ack` 函数使其发送的ACK JSON符合后端 `MqttMessageHandlerImpl.java` 的实际解析逻辑 (即包含中文描述的 `command_ack`,数字类型的 `task_id`,和布尔类型的 `success`)。**
2. **MQTT 下行 `MOVE_TO_SPOT` 指令校准**:
* **问题分析**: 后端 `ChargingSessionServiceImpl.java` 在发送移动指令时实际使用的payload是 `{"command":"MOVE", ...}`,而 `README.md` 先前示例为 `{"commandType":"MOVE_TO_SPOT", ...}`
* **`README.md` 更新**: 已更新 `README.md` (4.2.2节) 中移动指令的示例,以反映后端实际发送的 `command: "MOVE"`格式,并添加了说明指出单片机固件已做兼容处理。
* **行动项 (开发者, 可选)**: 为保持一致性,可考虑更新后端 `ChargingSessionServiceImpl.java`,使其发送移动指令时使用 `commandType: "MOVE_TO_SPOT"`
3. **文档整体一致性提升**:
* `README.md` 中关于单片机处理逻辑 (4.3节) 和示例流程 (4.4节) 的部分也相应作了调整以强调新的ACK格式要求和指令细节。
**代码分析涉及文件:**
* `springboot-init-main/src/main/java/com/yupi/project/service/impl/ChargingSessionServiceImpl.java` (下行指令发送)
* `springboot-init-main/src/main/java/com/yupi/project/service/impl/MqttServiceImpl.java` (下行指令封装与 `taskId` 注入)
* `springboot-init-main/src/main/java/com/yupi/project/mqtt/handler/MqttMessageHandlerImpl.java` (上行消息及ACK处理核心逻辑)
* `springboot-init-main/src/main/java/com/yupi/project/model/enums/CommandTypeEnum.java` (ACK中`command_ack`的解析方式)
* `esp32_robot_firmware/mqtt_handler.cpp` (单片机ACK发送逻辑 - **需修改**)
* `esp32_robot_firmware/esp32_robot_firmware.ino` (单片机指令回调处理)
**后续影响与建议**:
* **首要任务是修正单片机固件的ACK发送逻辑**,确保与后端正确对接。
* 在完成固件修改后建议进行完整的MQTT通信测试覆盖所有指令类型及其ACK流程。
## 2025-05-24: 后端会话管理核心逻辑强化
**主要变更与决策:**
1. **明确用户充电请求流程与并发处理:**
* 用户请求充电时 (`ChargingSessionController.requestCharging` -> `ChargingSessionServiceImpl.requestCharging` -> `assignRobotToSession`),系统会检查车位可用性、用户状态,并尝试分配空闲机器人。
* **机器人分配机制**: `ChargingRobotServiceImpl.assignIdleRobot()` 负责查找空闲机器人并将其状态更新为 `MOVING` (或类似表示已分配的状态)。此操作与会话创建、车位预定、任务创建均在一个事务内。
* **机器人繁忙处理**: 如果没有空闲机器人,`assignRobotToSession` 将返回 `null`,随后 `requestCharging` 方法会将此会话标记为 `CANCELLED_BY_SYSTEM` 并向用户抛出明确的"无可用机器人"或"机器人繁忙"的错误。**系统不会为此类请求创建等待执行的 `RobotTask`**,用户需稍后重试。这简化了后端的任务管理和状态维护。
2. **MQTT 指令发送与任务状态同步 (`MqttServiceImpl.sendCommand`):**
* **任务预创建**: `sendCommand` 方法要求调用者 (如 `ChargingSessionServiceImpl`) 必须预先创建状态为 `PENDING``RobotTask`并将任务ID (`associatedTaskId`) 传递给 `sendCommand`
* **机器人繁忙校验**: 在发送指令前,`sendCommand` 会调用 `RobotTaskServiceImpl.hasOtherPendingOrSentTask()` 检查目标机器人是否已有其他 `PENDING``SENT` 状态的任务(排除当前 `associatedTaskId`)。若是,则通常中止发送(`STOP_CHARGE` 指令除外,它具有优先权)。
* **Payload 增强**: `sendCommand` 会自动向发送的MQTT JSON payload 中添加 `taskId` 字段,其值为 `associatedTaskId`。这有助于机器人端将ACK消息与特定任务关联。
* **事务与数据一致性**: `sendCommand` 标记为 `@Transactional`
* MQTT消息成功发布后会调用 `RobotTaskServiceImpl.markTaskAsSent()` 将对应任务状态从 `PENDING` 更新为 `SENT`
* 为确保数据一致性避免MQTT已发送但任务状态更新失败`RobotTaskServiceImpl.markTaskAsSent()` 方法已被修改:**在其无法成功将任务标记为 SENT如任务不存在、原状态非PENDING、数据库更新失败会抛出 `BusinessException`**。此异常会触发 `sendCommand` 所在事务的回滚从而撤销MQTT发送如果尚未实际完成网络传输或至少确保数据库层面的一致性。
3. **会话状态流转与MQTT消息处理 (初步对齐):**
* **"到达即充电"逻辑**: 当前遵循此简化逻辑。当 `MOVE_TO_SPOT` 任务成功被机器人ACK后`ChargingSessionServiceImpl.handleRobotArrival()` 会直接将会话状态更新为 `CHARGING_STARTED`
* `MqttMessageHandler` 在收到机器人的状态回报(特别是包含 `taskId` 的ACK会首先调用 `RobotTaskService` 更新任务状态,然后基于任务结果调用 `ChargingSessionService` 的相应方法来驱动会话状态的变更。
4. **兼容性考虑**:
* 当前的修改重点在于增强后端逻辑的健壮性和数据一致性同时尽量维持现有的MQTT消息JSON结构 (仅在payload中增加 `taskId`,这是一个向后兼容的增强) 和主要的业务流程,以避免对前端和单片机代码产生破坏性变更。
* MQTT指令名称 (如 `MOVE` vs `MOVE_TO_SPOT`) 与 `CommandTypeEnum` 的映射关系需要在后端处理,以适应单片机固件的期望。
**后续关注点:**
* 详细实现 `ChargingSessionService` 中处理充电结束、取消、错误、超时的逻辑。
* 设计和实现计费服务。
* 确保 `MqttMessageHandler` 中对所有预期机器人回报的完整处理和对 `ChargingSessionService` 的正确调用。
* 审阅并完善所有相关的 `@Transactional` 注解,确保事务边界和传播行为的正确性。
## YYYY-MM-DD (请替换为今天的实际日期)
- 新建 Arduino Nano 循迹避障项目 `arduino_nano_tracker`
- 目标:实现单片机控制的循迹和避障功能。
- 硬件配置:
- TT 电机 x2 (PWM控制转速): 左电机引脚 9, 右电机引脚 10。
- TRCT5000 循迹传感器 x2: 左传感器引脚 7, 右传感器引脚 8。
- 超声波传感器 (HC-SR04): Trig 引脚 12, Echo 引脚 11。
- 180度舵机 (SG90): 引脚 3。
-`arduino_nano_tracker/` 目录下创建了 `arduino_nano_tracker.ino` 文件,并包含基础的引脚定义和 `setup()`/`loop()` 结构。
## YYYY-MM-DD (请替换为今天的实际日期)
- **前端接口路径修复**:
- 修复了 `charging_web_app``my-sessions` 页面 (`charging_web_app/src/app/(authenticated)/my-sessions/page.tsx`) 获取用户充电记录列表的接口调用。
- 问题:原请求路径为 `/session/my/list/page`,缺少了 API 代理前缀 `/api`
- 解决:将请求路径修改为 `/api/session/my/list/page`,确保请求能正确通过 Next.js 代理到后端服务。
## YYYY-MM-DD (请替换为今天的实际日期)
- **管理员会话管理功能方案调整**:
- 根据决策,从《第五阶段:管理员会话管理功能开发方案》(`springboot-init-main/doc/development_stages/stage_5_admin_session_management.md`) 中移除了管理员直接删除充电会话的功能。
- 主要移除内容包括相关的后端 Service 方法、Controller 接口定义以及前端对应的操作按钮和API调用封装。
- 此举旨在降低误操作风险并保留完整的数据记录。
- **前端管理员控制台界面调整**:
- 从管理员控制台页面 (`charging_web_app/src/app/(authenticated)/admin/dashboard/page.tsx`) 移除了"会话管理"导航卡片按钮,以匹配后端不再提供管理员直接删除会话功能的调整。
## YYYY-MM-DD (请替换为今天的实际日期)
- 新建 MG995 舵机测试项目 `mg995_servo_test`
- 目标:测试 MG995 舵机与 ESP32-S3 的集成。
- 硬件配置:
- 主控ESP32-S3
- 舵机MG995
- 连接引脚:舵机信号线连接到 GPIO 18。
-`mg995_servo_test/` 目录下创建了 `mg995_servo_test.ino` 文件,实现了舵机在 0-180 度之间往复运动的测试程序。
## YYYY-MM-DD (请替换为今天的实际日期)
- 新建 Arduino Nano 的 MG995 舵机测试项目 `mg995_nano_test`
- 目标:将舵机控制方案切换到 Arduino Nano 平台。
- 硬件配置:
- 主控Arduino Nano
- 舵机MG995
- 连接引脚:舵机信号线连接到 Nano 的 D9 PWM 引脚。
-`mg995_nano_test/` 目录下创建了 `mg995_nano_test.ino` 文件,使用标准 `Servo.h` 库实现舵机往复运动。
- **重要提示**:同样需要为 MG995 舵机提供独立的 5V 外部电源,并与 Nano 共地。
## YYYY-MM-DD (请替换为今天的实际日期)
- **Fix:** 修复了在充电会话结束时,由于在 `calculateCostAndFinalizeSession` 方法中存在冗余的车位状态检查和更新逻辑导致在特定事务场景下产生非法SQL语句 (`UPDATE parking_spot` 缺少 `SET` 子句) 的严重BUG。
- **Refactor:** 重构了 `ChargingSessionServiceImpl`,将车位释放的逻辑完全保留在 `handleChargingEnd` 方法中,确保 `calculateCostAndFinalizeSession` 只负责计费,遵循单一职责原则,提高了代码的健壮性和可维护性。