# 变更日志

## 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` 只负责计费，遵循单一职责原则，提高了代码的健壮性和可维护性。 