/** * @file BS_ocpp_mv_offline_bridge.h * @brief OCPP 离线 MeterValues 桥接层声明:连接应用逻辑与 `ocpp_mv_offline_flash_impl`(SPI Flash + FRAM) * * @details * - **落盘**:TCP 未连接或已连接但未登录 CSMS 时,在充电过程中周期性把测量快照写入 Flash;FRAM 映射键为 **订单开始充电时间** `StartChargeTime` 经 `xDate2Seconds` 得到的 **32 位秒值**(写入 Flash 模块的 `u32_tx_sn_lo`;第二参恒 0,与 FRAM 行 `u32_tx_sn_hi` 约定一致,非 64 位拆分),并与枪号共同区分会话(离线时设备流水号常为空,故不用设备流水号做键)。\n * - **FRAM/SPI 管理块**:由 `BS_ocpp_ctrl.c` 的 **`v_ocpp_init_data`** 调用 `v_ocpp_mv_offline_init()` 从 EEPROM 装载;每次全量 OCPP 初始化会再次执行(与 FRAM 内容一致,可幂等重入)。\n * - **补发(未结)**:未结算订单在联网且已取得平台 `transactionId` 后,由 `u8_ocpp_mv_offline_unsettled_step` 按序逐条补发 MeterValues,\n * 全部完成后再由 `BS_ocpp_ctrl` 发送 `StopTransaction`。\n * - **补发(在线会话)**:Remote 在线起充 → 充电中离线 → 再上线且 **同一 transactionId** 仍有效时,由 `u8_ocpp_mv_offline_session_replay_step` 补发离线窗口内落盘的 MV,\n * 再发在线周期 MV / `StopTransaction`。\n * - 本头文件仅在 `BS_OCPP_EN` 打开时提供 API,避免无 OCPP 工程误链。 */ #ifndef BS_OCPP_MV_OFFLINE_BRIDGE_H #define BS_OCPP_MV_OFFLINE_BRIDGE_H #include "publicdata/type.h" #include "meter_calculate/meter_calculate_impl.h" #if BS_OCPP_EN #include "flash_file_mgr/ocpp_mv_offline_flash_impl.h" #include "bs_ocpp_json_data.h" /** * @fn MeterValues_mag_offline_replay * @brief 由离线快照组 MeterValues PDU(补发;与在线 `MeterValues_mag` 共用 `j_get_ocpp_json` / 测量项配置) */ cJSON *MeterValues_mag_offline_replay(int gunNo, int transactionId, const S_OCPP_MV_OFFLINE_MV *mv); /** * @fn v_ocpp_mv_offline_bridge_private_logic * @brief 未联网或未登录周期逻辑:充电中按配置间隔采样并写入离线 MV 池 * @param[in] bs_id 平台/任务侧 OCPP 实例 id,与 `v_ocpp_private_logic(U8_T bs_flag)` 入参一致(当前实现内未使用,保留接口扩展) * @details * - 调用方:`v_ocpp_private_logic` 在 **TCP 未连接** 或 **已连接未登录** 分支末尾调用。\n * - 每枪独立节流:间隔取自 `s_ocpp_flash_cfg_data.s_ocpp_config_data.u16_MeterValueSampleInterval`(为 0 时用默认 60s)。\n * - 仅当 `u8_bs_get_flow_state(枪)==2`(充电中)且当前枪订单日志中 **开始充电时间** `StartChargeTime` 有效时落盘;\n * 键为 `xDate2Seconds(StartChargeTime)` 的 **单路 U32**(`u8_ocpp_mv_offline_mv_save` 第一参;第二参恒 0)。\n * @note 与在线 `send_mag(..., OCPP_MeterValues, ...)` 互斥由上层网络状态保证;双枪各自写入 Flash 半区(见 `ocpp_mv_offline_flash_impl.h`)。 */ void v_ocpp_mv_offline_bridge_private_logic(U8_T bs_id); /** 在线 MeterValues 发送失败时立即落盘一条离线 MV(忽略采样间隔节流) */ void v_ocpp_mv_offline_snap_gun_now(U8_T gun); /** 该枪进入新一笔充电会话时调用:清 FRAM 映射槽、复位落盘节流 */ void v_ocpp_mv_offline_on_charge_start(U8_T gun); /** * @fn v_ocpp_mv_offline_unsettled_abort * @brief 强制中止「未结订单离线 MV 补发」状态机 * @details * - 在 `v_safe_free_unsettled_order_data` 释放未结订单内存前调用,避免静态序号与已释放的 `S_LOG_DATA` 不一致。\n * - 不清 Flash、不删 FRAM 映射行(映射删除仅在补发完成或读失败跳过路径中处理)。 */ void v_ocpp_mv_offline_unsettled_abort(void); /** 未结订单在 Flash 中是否有待补发的离线 MeterValues(按 StartChargeTime 映射键) */ U8_T u8_ocpp_mv_offline_map_pending_for_log(const S_LOG_DATA *log); /** * @fn u8_ocpp_mv_offline_unsettled_step * @brief 未结补单:在已有平台 transactionId 的前提下,尝试发送下一条离线缓存的 MeterValues * @param[in] log 当前内存中的未结算订单副本指针(与 `private_ocpp_data->s_unsettled_order_data` 相同语义) * @return * - **1**:本周期已发出一条 MV、或读/校验失败已跳过序号、或 WebSocket 发送失败需下周期重试同一序号;\n * 调用方 **不得** 发送 `StopTransaction`,**不得** `free` 该订单。\n * - **0**:Flash/FRAM 中无本单映射、或已全部补发完毕并已 `map_remove`、或参数/键不合法;\n * 调用方可继续发送 `StopTransaction` 并释放订单(与现有未结流程一致)。 * @details * - 映射键:与落盘相同,由 `log->StartChargeTime` 经 `xDate2Seconds` 得到同一 **U32**;与 `u8_ocpp_mv_offline_mv_save` 写入 `u32_tx_sn_lo` 时一致。\n * - JSON 中 `transactionId`:使用 `atoi(log->u8_preTradeNo)`(**含 0**);仅 0xFF/空占位时不补发。\n * - 每调用最多 **1 条** WS 报文,避免单次周期阻塞过长。 */ U8_T u8_ocpp_mv_offline_unsettled_step(S_LOG_DATA *log); /** 中止「在线会话内」离线 MV 补发状态机(新一笔充电开始时会自动调用) */ void v_ocpp_mv_offline_session_replay_abort(void); /** 当前枪是否存在待补发的离线 MV(已有 txId,FRAM 映射非空) */ U8_T u8_ocpp_mv_offline_session_replay_pending(U8_T gun); /** * @brief 在线起充后会话内补发:用当前订单 `preTradeNo` 作 transactionId,逐条上送 Flash 中本笔充电的 MV * @return 1 本周期已发或需重试;0 无待补发或已完成(调用方可继续 StopTx / 周期 MV) */ U8_T u8_ocpp_mv_offline_session_replay_step(U8_T gun); #endif /* BS_OCPP_EN */ #endif /* BS_OCPP_MV_OFFLINE_BRIDGE_H */