Co-authored-by: Cursor <cursoragent@cursor.com>
31 KiB
OCPP 功能模块说明(CCU601E_D)
本文档描述 CCU601E_D\app\plat_comm\ocpp 目录内各文件职责、主要数据结构及与其它模块的关系,便于维护与二次开发。全文基于工程源码归纳;协议细节以 OCPP 1.6-j 为准。
1. 模块定位与编译开关
| 项目 | 说明 |
|---|---|
| 协议 | OCPP 1.6,典型传输为 WebSocket(子协议 ocpp1.6)。 |
| 宏开关 | 全局 BS_OCPP_EN(通常为 Public_define.h 等处):为 0 时本目录多数代码不参与编译。 |
| 依赖 | cJSON(JSON 组帧/解析)、FreeRTOS 内存 pvPortMalloc / vPortFree(见 BS_ocpp_ctrl.h 中 OCPP_MALLOC)、平台任务 plat_comm(TCP/4G、充电订单接口)、计量/F meter_calculate、Flash 配置等。 |
2. 目录文件一览与职责
| 文件名 | 类型 | 职责摘要 |
|---|---|---|
| BS_ocpp_ctrl.h | 头文件 | OCPP 运行时控制结构体、定时器枚举、充电侧标志位枚举;对外 API:v_ocpp_init_data、v_ocpp_tcp_connect、v_ocpp_logo_on、v_ocpp_recv_data、v_ocpp_send_ctrl、v_ocpp_private_logic、recv_mags 等。 |
| BS_ocpp_ctrl.c | 实现 | 核心控制器:WebSocket 收发衔接、send_mag 统一发包、ocpp_send_mag_ctrl 周期/事件触发(Boot、Heartbeat、Status、MeterValues、Start/Stop、Authorize);平台下行解析(RemoteStart/Stop、ChangeConfiguration、GetConfiguration、Reset、Unlock、UpdateFirmware、SendLocalList、GetDiagnostics 等);离线/未结算订单 s8_unsettled_order_chaek;各 Action 的 CallResult 处理函数(*_res / *_unpack)。离线周期 MeterValues 的落盘与补发状态机委托 BS_ocpp_mv_offline_bridge;补发单条 PDU 由 s32_ocpp_send_meter_values_replay 调 MeterValues_mag_offline_replay(实现位于桥接模块)。源码内用 #if 1 // … 做大段分区(见 §4.0)。 |
| BS_ocpp_mv_offline_bridge.h | 头文件 | (BS_OCPP_EN 内)声明 MeterValues_mag_offline_replay、v_ocpp_mv_offline_bridge_private_logic、v_ocpp_mv_offline_unsettled_abort、u8_ocpp_mv_offline_unsettled_step;依赖 Flash 侧 ocpp_mv_offline_flash_impl.h 与 bs_ocpp_json_data.h。 |
| BS_ocpp_mv_offline_bridge.c | 实现 | 离线 MeterValues 桥接:TCP 未连接或已连接未登录时按周期把测量快照写入 SPI Flash 池(与 u8_get_ocpp_measurand_data 口径对齐);FRAM 映射键为 订单开始充电时间经 xDate2Seconds 的 32 位秒值 + 枪号;联网成功且未结单已取得平台 transactionId 后,在 StopTransaction 前按序 u8_ocpp_mv_offline_unsettled_step 补发;MeterValues_mag_offline_replay 按 u8_MeterValuesMeasurandNeed 从快照填 SampledValue 并 j_get_ocpp_json(OCPP_MeterValues, …)。v_ocpp_mv_offline_init 在 v_ocpp_init_data 中调用。详情见桥接文件头注释与 flash_file_mgr/ocpp_mv_offline_flash_impl.*。 |
| bs_ocpp_json_data.h | 头文件 | OCPP_CMD 枚举(桩→平台 / 平台→桩)、各 PDU 的 Request/Response 结构体、GetConfiguration/MeterValues 等缓冲区长度宏;供组帧与解析共用。 |
| bs_ocpp_json_data.c | 实现 | 字符串常量表:命令名、StatusNotification 的 errorCode/statusCode、StopTransaction.reason、RemoteStart 应答等;发送侧:BootNotification_json、StartTransaction_json、MeterValues_json 等 cJSON 组帧;接收侧:get_ocpp_data / ocpp_*_unpack 解析平台下行 payload;通用:j_get_ocpp_json、s_get_ocpp_cmd_str、枚举转字符串等。 |
| bs_ocpp_str.h | 头文件 | OCPP 协议中出现的 字面量(measurand、context、unit、Accepted/Blocked、以及标准 Configuration Key 名称宏 KEY_*),避免魔法字符串散落。 |
| bs_ocpp_configuration.h | 头文件 | 连接器相位枚举、MeterValues 测量项枚举 OCPP_MEASURAND_ID(顺序固定,与配置数组下标绑定);Flash 配置结构 S_OCPP_FLASH_CFG_DATA / OCPP_CONFIG 声明;测量项英文串表 OCPP_measurand_str 外部声明。 |
| bs_ocpp_configuration.c | 实现 | s_ocpp_flash_cfg_data:默认与 Flash 持久化的 OCPP 配置(Heartbeat、MeterValues 采样项、SupportedFeatureProfiles 等);GetConfiguration / ChangeConfiguration 读写逻辑;与 spi_Flash/flash_external_data 协同落地配置;测量项字符串数组 OCPP_measurand_str 定义。 |
| bs_websocket.h | 头文件 | WebSocket 帧类型(文本/PING/PONG/断开);ws_send、websocket_build、ocpp_recv 声明;可选 OCPP_WS_HTTP_AUTHORIZATION_EN(HTTP Basic)。 |
| bs_websocket.c | 实现 | RFC6455 握手(含 Sec-WebSocket-Accept、可选 Basic)、帧封装/解析;与 bs_connect_impl URL/TCP 衔接;收包后递交给 recv_mags(见 BS_ocpp_ctrl.c)。 |
3. 核心数据结构(BS_ocpp_ctrl.h)
3.1 OCPP_SEND_TIMER
周期性上报节拍:BootNotification、Heartbeat、各枪 StatusNotification、MeterValues、StopTransaction 定时、Authorize 定时等。具体周期值在运行时由配置或初始化写入 s_timerCnt[]。
3.2 OCPP_CHARGE_FLAG
与充电流程协同的软件标志(落在 plat_comm_task 的枪数据中,通过 u8_charge_flag_ctrl 访问),例如:
OCPP_STATE_CHANGE_FLAG:连接器状态变化需上报;OCPP_TRADE_UPLOAD_FLAG:订单结束待上送 StopTransaction;OCPP_START_RESULT_FLAG/OCPP_STOP_RESULT_FLAG:启动/停止结果触发;OCPP_VIN_START_FLAG:即插即充等场景的鉴权请求节拍。
3.3 OCPP_DATA_CTRL
OCPP 私有全局状态(指针 private_ocpp_data,初始化见 v_ocpp_init_data),主要包括:
- 登录与链路:
ulong_timer、超时重连等; - 离线/未结订单:
u8_unsettled_order_flag、s_unsettled_order_data(动态分配的S_LOG_DATA);补单流水线u8_unsettled_auth_phase(OCPP_UNSETTLED_AUTH_PHASE_E:先 Authorize 再 StartTransaction)、u32_unsettled_last_auth_s/u32_unsettled_last_stx_s(Authorize 节流;StartTransaction 在本单鉴权通过后仅发一次,用非零时间戳标记已发,避免平台多笔 transaction); - 每枪控制:
s_ctrl_flag[](MeterValues 召测、Trigger、鉴权计数等); - 定时器数组:
s_timerCnt[OCPP_TIMERCNT_NUM]; - 异步应答队列:
ack_ctrl[](平台多条指令并发时的应答上下文)。
3.4 OCPP_DATA_SAVE
待发 CALL 与应答匹配的 messageId/key 缓存(save_json_cmd / get_json_cmd),用于解析 CALLRESULT / CALLERROR 时找到对应枪号与命令类型。
4. BS_ocpp_ctrl.c 功能分层说明
4.0 源码分区(#if 1 标记)
为方便在单文件内浏览,BS_ocpp_ctrl.c 用大段 #if 1 // 注释 划分逻辑归属(非编译裁剪,恒为 1):
| 分区注释 | 主要内容 |
|---|---|
//公共 |
随机数、待发 messageId 队列(save_json_cmd / get_json_cmd)、发送节拍累加(v_ocpp_timer_add、u8_ocpp_timer_ctrl)、v_ocpp_timestamp_to_str、应答块分配/释放、parse_ftp_url 与 FTP URL 自测、时间差工具函数;供 FTP / 其它模块写 OCPP 侧“待发 Notify 状态” 的 v_ocpp_set_diag_upload_notify_pending、v_ocpp_set_fw_notify_pending、v_ocpp_set_fw_reboot_after_installed(写入 s_bs_task_ctrl.ocpp_data,与 DiagnosticsStatusNotification_mag / FirmwareStatusNotification_mag、send_mag 配合)。不再在控制器内保留已迁出的 unix 秒→ISO8601 辅助(补发时间戳在桥接文件内实现)。 |
//数据获取 |
枪/订单标志 u8_charge_flag_ctrl、桩状态、重启与交易上送标记、u8_get_ocpp_measurand_data、u8_get_ocpp_transaction_data、GetConfiguration 键填充、StatusNotification 变化检测与 Faulted 判定等。 |
//发送 |
各 BootNotification_mag、*_mag 组 cJSON body(含 MeterValues 在线路径)、GetConfiguration 应答体、DiagnosticsStatusNotification_mag、FirmwareStatusNotification_mag、send_mag、s32_ocpp_send_meter_values_replay(离线快照→JSON 调用桥接层 MeterValues_mag_offline_replay)、ocpp_send_mag_ctrl;部分静态函数名含 _res 实为 对外发送的 CallResult payload 组装,仍属发送段。 |
//接收 |
平台下行 CALL 解析(*_mag unpack、ocpp_analysis_data)、CALLRESULT 分发(*_res)、recv_mags。 |
未结订单与充电鉴权 |
u8_ocpp_is_pre_trade_no_pending、未结内存释放 v_safe_free_unsettled_order_data(内含 v_ocpp_mv_offline_unsettled_abort)、u8_unsettled_pretrade_pipeline、s8_unsettled_order_chaek、充电中鉴权检查 v_ocpp_private_charge_authentication_cheak。 |
//外部控制使用 |
v_ocpp_init_data / v_ocpp_init_data_ex(含 v_ocpp_mv_offline_init)、RX 超时与重连门槛 u8_ocpp_block_rx_timeout_relink、v_ocpp_tcp_connect、v_ocpp_logo_on、v_ocpp_recv_data、v_ocpp_send_ctrl、v_ocpp_private_logic。 |
内层 MeterValues_mag 中另有 #if 1 //根据枚举 MeterValues 测量项…,仅区分「按配置枚举上送」与「简化固定字段」两套实现,不单独对应上表大段。
4.1 发送路径
- 业务或定时器置位标志 →
ocpp_send_mag_ctrl()轮询; send_mag(gunNo, cmd, ack_data, type):- 构造 [2, messageId, Action, payload](主动)或 [3, …](应答);
switch(cmd)调用各*_mag生成cJSONbody(实际多在BS_ocpp_ctrl.c内静态函数,如BootNotification_mag、StartTransaction_mag,内部再调bs_ocpp_json_data.c的 json 辅助);在线 MeterValues 走MeterValues_mag;离线 Flash 中缓存的一条快照补发 走s32_ocpp_send_meter_values_replay,其 body 由MeterValues_mag_offline_replay(BS_ocpp_mv_offline_bridge.c)生成,与在线共用j_get_ocpp_json与u8_MeterValuesMeasurandNeed;- 文本输出经
ws_send发到 CSMS。
type 参数:同一 Action 不同数据源时使用,例如 StartTransaction / StopTransaction / Authorize 的 实时枪数据(0) 与 未结算内存订单 s_unsettled_order_data(1)。
4.2 接收路径
ocpp_recv()(bs_websocket.c)收到文本帧 →recv_mags();- 解析 JSON:区分 CALL / CALLRESULT / CALLERROR;
- CALL:根据 Action 字符串映射
OCPP_CMD→unpack填充OCPP_DATA_RECV_ACK,必要时send_mag返回 CallResult; - CALLRESULT:
get_json_cmd匹配 messageId → 调用对应StartTransaction_res、Authorize_res等,更新本地订单/流水号/鉴权状态。
4.3 典型已实现的平台指令(下行)
包括但不限于:RemoteStartTransaction、RemoteStopTransaction、UnlockConnector、Reset、ChangeAvailability、ChangeConfiguration、GetConfiguration、TriggerMessage、SendLocalList、GetLocalListVersion、ClearCache、UpdateFirmware、GetDiagnostics(工程内可能对 diagnostics 使用自定义 Action 名,以源码为准)。
4.4 离线 / 未结算订单(概要)
s8_unsettled_order_chaek():联网且登录后周期性调用;从s8_plat_get_unsettled_order(bs_public_impl.c)拉取 Flash 未结列表或 EEPROM 掉电临时订单,挂到s_unsettled_order_data(一次一单)。- 已有平台流水号(
u8_preTradeNo有效:非空且非0xFF占位,判定见u8_ocpp_is_pre_trade_no_pending):直接StopTransaction(type=1)上送结束侧订单信息(meterStop、timestamp、reason、transactionData 等),随后释放内存上下文。 - 尚无流水号(空或
0xFF占位):Authorize(type=1)(idTag 取订单u8_user_id)→ 仅idTagInfo.status == Accepted后StartTransaction(type=1)申请 transactionId →StartTransaction_res在桩空闲且未结副本存在时把 transactionId 写入u8_preTradeNo→ 下一周期走 StopTransaction(type=1)。补充:若本笔在离线阶段已通过BS_ocpp_mv_offline_bridge将周期 MeterValues 写入 Flash,则在 已取得平台transactionId后、发 StopTransaction 前,由u8_ocpp_mv_offline_unsettled_step按序号逐条补发(帧格式与在线send_mag/MeterValues一致),补完或映射无效后再结束事务。 - 拒绝与单次 StartTransaction:StartTransaction.conf 对 Blocked/Invalid 等未结补单立即
v_safe_free_unsettled_order_data(不重试 StartTransaction,避免平台侧多笔订单);Authorize 非 Accepted 同样丢弃内存。Authorize 带秒级节流(u32_unsettled_last_auth_s);鉴权通过后 StartTransaction 仅尝试发送一次(成功发出后u32_unsettled_last_stx_s非零 即不再发;链路ws_send失败 未置位时可下一周期再试一次发送)。
详细分场景(Flash / EEPROM、与平台报文顺序)见第 10~12 节。
详细边界条件见代码注释及同类需求文档。
5. bs_ocpp_json_data.c 功能分层说明
| 类别 | 内容 |
|---|---|
| 命令表 | ocpp_cmd_str[][] 与 OCPP_CMD 枚举对齐,用于日志与解析。 |
| 枚举串 | errorCode、status、reason、UnlockConnector 状态等,供 s_get_ocpp_enum_data_str 使用。 |
| 桩 → 平台 | *_json():把结构体填进 cJSON。 |
| 平台 → 桩 | ocpp_*_unpack():从 cJSON 填结构体,供业务判断。 |
| 入口封装 | j_get_ocpp_json:按 cmd 选择对应 json 构造函数;get_ocpp_data:解析 CallResult。 |
6. bs_ocpp_configuration.c 功能说明
- 维护全局
s_ocpp_flash_cfg_data(含OCPP_CONFIG):对标 OCPP 标准配置键,如 HeartbeatInterval、MeterValuesSampledData、AuthorizeRemoteTxRequests、StopTransactionOnInvalidId 等; - ChangeConfiguration:校验、写入 RAM + Flash;部分键需解析 CSV(测量项列表)、相位字符串等;
- GetConfiguration:按 key 列表返回 readonly/value;未知 key 记入 unknownKey;
OCPP_measurand_str[]:与OCPP_MEASURAND_ID一一对应,供 MeterValues/StopTxn 数据项映射。
7. bs_websocket.c 功能说明
- 完成 TLS/TCP 之上的 WebSocket 字节流处理(依工程
websocket_build与连接模块集成方式而定); - 握手:HTTP Upgrade → Sec-WebSocket-Accept(内置 SHA1/Base64 相关逻辑);
- 心跳:处理 Ping/Pong,业务层亦可配合应用层 Heartbeat;
- 数据:文本帧交给
recv_mags。
8. bs_ocpp_str.h 功能说明
- 协议常量:measurand、location、unit、
Sample.Periodic、Transaction.Begin/End等; - 授权/配置结果:
Accepted、Blocked、Invalid等; - 配置键名:
KEY_HEARTBEAT_INTERVAL、KEY_METER_VALUES_SAMPLED_DATA等(与bs_ocpp_configuration协同)。
避免与 bs_ocpp_json_data.h 中结构体字段混淆:str.h 侧重 字面量与 Key 名,json_data.h 侧重 PDU 结构体与枚举。
9. 与其它目录的典型调用关系(示意)
flowchart LR
subgraph plat_comm_task
T[v_ocpp_send_ctrl / v_ocpp_recv_data]
end
subgraph ocpp
C[BS_ocpp_ctrl.c]
M[BS_ocpp_mv_offline_bridge.c]
J[bs_ocpp_json_data.c]
W[bs_websocket.c]
CFG[bs_ocpp_configuration.c]
end
subgraph impl
P[bs_public_impl.c]
end
subgraph flash
F[ocpp_mv_offline_flash_impl]
end
T --> C
C --> M
C --> J
C --> W
C --> CFG
C --> P
M --> F
M --> J
plat_comm_task:周期性调用v_ocpp_private_logic/v_ocpp_send_ctrl,驱动ocpp_send_mag_ctrl;未联网或未登录分支末尾可调用v_ocpp_mv_offline_bridge_private_logic做离线 MV 周期采样落盘;bs_public_impl:枪状态、订单S_LOG_DATA、未结算订单s8_plat_get_unsettled_order、结算确认等;v_ftp_update_action在 4G 下根据u8_log_file_upload/u8_updata_flag调度ec200a_4g_ftp.c中日志或固件 FTP 状态机;meter_calculate:订单写 Flash、EEPROM 临时订单、未结算索引链表(见unsettled_order_mng)。
9.1 GetDiagnostics、4G FTP 与 PlatComm 调度(与 4g_module 衔接)
GetDiagnostics 由 BS_ocpp_ctrl.c 解析平台下发的 location(FTP URL、账号等),置 s_bs_task_ctrl.ocpp_data.u8_log_file_upload,并在 GetDiagnostics_res 中准备文件名、调用 v_ftp_ctrl_init() 复位 FTP 步进。须保证「先发出 CallResult / Uploading,再置 u8_updata_flag」,否则与 EC200A_BLOCK_QISEND_DURING_4G_FTP、u8_bs_ftp_ocpp_strict_mux 组合时可能挡掉应答(详见 4g_module/4G模块AT指令配置功能说明.md 第四节)。
4G + 非透传 时,日志上传在 ec200a_4g_ftp.c 中已改为:QFOPEN + QFSEEK + QFWRITE 按 EC200A_FTP_UFS_RW_CHUNK_BYTES(默认 4096) 分包写模组 UFS;QFWRITE 在单周期内仅短等轮询,未完成则下一 FTP 半周期 续传。plat_comm_task.c 在 FTP 活跃时对 4G 采用 相邻 PlatComm 周期「仅联网 / 仅 FTP 一步」交替,与上述分包叠加后,FTP 传输期间 OCPP 仍可周期性完成 QISEND/QIRD 与 WebSocket 业务。
v_ocpp_private_logic:不得在 u8_updata_flag==0 时无条件清除 u8_log_file_upload,否则会误入固件升级分支(日志出现 [FTP-UPG] 等异常);该约束与 4g_module/4G模块AT指令配置功能说明.md 第三节一致。
配置与排障(EC200A_FTP_*、EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS 等)以 ec200a_4g_cfg.h 与 4G模块AT指令配置功能说明.md 为准。
10. 离线未结算订单处理流程(Flash 列表)
本节描述:充电正常结束或结算链路中断后,订单已写入 Flash,且索引已进入 EEPROM「未结算列表」,待 CSMS 上线后通过 OCPP 补单:本地尚无有效 transactionId(空或 0xFF 占位)时,先 Authorize(idTag),再 StartTransaction 获取流水号并写入 u8_preTradeNo,再 StopTransaction 上送订单结束信息;若 u8_preTradeNo 已为有效流水号,则直接 StopTransaction,不再重复鉴权与 StartTransaction。
10.1 数据从哪里来(产生侧)
| 环节 | 位置 | 说明 |
|---|---|---|
| 订单落盘 | meter_calculate_impl.c → v_meterlog_flash_save_log(1, gunNo) |
订单结束时 u32_chg_order_save_record 写 Flash,得到有效 orderIndex(u32_index)。 |
| 进入未结队列 | 同上 | u8_add_unsettled_order(orderIndex)(BSP/flash_file_mgr/unsettled_order_mng.c)把该索引写入 EEPROM 未结算管理区,表示「尚未收到平台对本单的结算确认」。 |
| 典型字段 | S_LOG_DATA |
含 u8_user_id、起止电量与时间、u8_preTradeNo(从未拿到 transactionId 时可能为空;离线刷卡等场景可能为 0xFF 首字节占位,u8_ocpp_is_pre_trade_no_pending 视为须先 Authorize + StartTransaction)、u8_gunNo 等。 |
充电过程中周期性刷新 EEPROM 临时区的逻辑见 第 11 节;正常完整结束订单后,会 清除该枪 EEPROM 临时记录(v_clear_temp_chg_record_in_eeprom),并依赖 Flash 未结列表 做后续上送。
10.2 读单出口(bs_public_impl.c)
S8_T s8_plat_get_unsettled_order(S_LOG_DATA *s_log, U8_T u8_type) 在 已处理完掉电临时单 之后,走 Flash 未结逻辑:
u16_get_unsettled_order_count()为 0 则直接返回 0(无单)。u8_type == 1时只返回数量,不读内容。- 否则
v_get_unsettled_order_indexes取 队首 索引currentIndex。 v_read_chg_order_record(s_log, currentIndex)从 Flash 读出完整S_LOG_DATA。u8_platform_settlement_confirm(currentIndex):按当前实现,在 判断 readResult 之前 即从未结列表中移除该索引(若 Flash 读失败,存在「索引已删、数据未读出」风险,属实现细节,维护时需留意)。
返回值:>0 表示仍有未结条数(含本条);0 无单;-1 读单失败。
10.3 OCPP 何时启动补单(BS_ocpp_ctrl.c)
前置条件(v_ocpp_private_logic 内 已联网且已登录 CSMS):
u8_unsettled_order_flag == 0(本开机周期尚未标记「未结检查完毕」);ulong_timer - u32_timer > 10(与注释「约 30 秒」对应的节流,实际以ulong_timer单位为准),更新u32_timer后调用s8_unsettled_order_chaek()。
断网或未登录时 u8_unsettled_order_flag 被置 0,不会跑未结上送;避免无链路发 OCPP。
10.4 OCPP 状态机:s8_unsettled_order_chaek
内存载体:private_ocpp_data->s_unsettled_order_data(堆上 S_LOG_DATA,一次只处理一单)。分支判定统一使用 u8_ocpp_is_pre_trade_no_pending(u8_preTradeNo)(空串或首字节 0xFF 视为「尚无有效流水号」)。
首次取单(s_unsettled_order_data == NULL):
OCPP_MALLOC+memset,u8_unsettled_auth_phase = NONE,u32_unsettled_last_auth_s/u32_unsettled_last_stx_s清零;s8_plat_get_unsettled_order(..., 0)填入内容;u8_user_id为空 →v_safe_free_unsettled_order_data(),返回;- 尚无流水号(
u8_ocpp_is_pre_trade_no_pending == 1):调用u8_unsettled_pretrade_pipelineOCPP_UNSETTLED_AUTH_NONE:节流后send_mag(gunIdx, OCPP_Authorize, NULL, 1),Authorize_mag从s_unsettled_order_data->u8_user_id取 idTag;成功后u8_unsettled_auth_phase = WAIT;WAIT:等待Authorize_res;仅 Accepted 时置OK,否则释放订单;OK:本单在u32_unsettled_last_stx_s == 0时仅send_mag(0, OCPP_StartTransaction, NULL, 1)一次(成功后置非零时间戳,避免重复 StartTransaction 导致平台多笔订单);StartTransaction_mag用未结副本组 StartTransaction.req(connectorId = u8_gunNo+1、历史 meterStart / timestamp / idTag)。
- 已有流水号(
pending == 0):send_mag(0, OCPP_StopTransaction, NULL, 1)→v_safe_free_unsettled_order_data()。
同一单未释放时(内存中仍持有上一笔未结单):
- 仍按 步骤 4 / 5 区分;
u8_cnt(静态) 仅用于 已有流水号 时 StopTransaction 重试计数;无流水号路径由u8_unsettled_pretrade_pipeline管理(StartTransaction 每单最多成功发出一次)。
10.5 平台应答:Authorize 与 StartTransaction
Authorize_res(u8_unsettled_auth_phase == WAIT):仅idTagInfo.status == Accepted时置OCPP_UNSETTLED_AUTH_OK,否则v_safe_free_unsettled_order_data()。解析失败时WAIT回退为NONE以便重试鉴权。StartTransaction_res:若idTagInfo.status为 Blocked / Invalid / Expired / ConcurrentTx,未结补单路径不得把transactionId写入preTradeNo,并立即释放内存(不重试 StartTransaction)。
当 桩空闲(u8_get_pile_state(gunNo)==0)且s_unsettled_order_data非空 且非上述拒绝态:将data.transactionId写入u8_preTradeNo,u8_unsettled_auth_phase = NONE,下一周期上送 StopTransaction。
注意:StopTransaction 主动发包 gunNo 常为 0,但 PDU 内 connectorId / 计量 依赖 s_unsettled_order_data->u8_gunNo;Authorize 主动发包使用 订单枪号 gunIdx,以便 CALLRESULT 与 ack_data->gunNo 一致。
10.6 StopTransaction(type=1)
StopTransaction_mag(..., type==1) 使用 s_unsettled_order_data:transactionId、idTag、结束时间、meterStop、reason(内部故障码映射到 StopTransactionRequest.reason,如 Local / PowerLoss 等)。
11. 掉电(异常断电)订单处理流程(EEPROM 临时记录)
本节描述:充电过程中异常断电,来不及走完整「订单结束写 Flash + 未结索引」路径时,依赖 每枪独立 EEPROM 槽位 保存的 最后一次充电快照,上电后优先恢复并走 同一套 OCPP 补单。
11.1 数据何时写入 EEPROM(充电中刷新)
| 调用 | 说明 |
|---|---|
v_meterlog_flash_save_log(0, gunNo) |
订单未结束,周期性把当前 s_metering_ctrl.s_gun_log[gunNo] 写入 EEPROM: v_save_temp_chg_record_to_eeprom(..., TEMP_CHG_FLAG_UNSETTLED)(meter_calculate_flash_impl.c)。 |
每枪地址互不干扰(如枪 A / 枪 B 不同 EEPROM 基址)。
11.2 正常结束订单时对 EEPROM 的处理
v_meterlog_flash_save_log(1, gunNo)(订单结束分支):写 Flash 订单、加入未结索引链 后,执行 v_clear_temp_chg_record_in_eeprom(gunNo),避免「已正规入库」仍被当成掉电单重复上传。
11.3 上电读单优先级(s8_plat_get_unsettled_order 前半段)
对 gunNo = 0 .. GUN_MAX_CNT-1 依次:
v_check_temp_chg_record_valid(gunNo):标志为TEMP_CHG_FLAG_UNSETTLED或 SETTLED 类有效值 则认为有条目;v_read_temp_chg_record_from_eeprom(gunNo, s_log, NULL)读出S_LOG_DATA;- 强制
s_log->e_stop_reason[0] = E_FAULT_13073(异常掉电),用于 StopTransaction 映射为PowerLoss等; v_clear_temp_chg_record_in_eeprom(gunNo):读完即清标志(下次不再重复读);- return 1,表示有一笔待 OCPP 处理的未结算数据。
因此:掉电单总是先于 Flash 未结队列被取出;若 EEPROM 与 Flash 同时存在数据,优先消耗掉电副本。
11.4 与 OCPP 的衔接
掉电单读出后 S_LOG_DATA 同样挂在 s_unsettled_order_data 上,后续与 第 10.4~10.6 节 完全一致:
- 无有效流水号 → Authorize(type=1) → Accepted 后 StartTransaction(type=1) → 应答写入
preTradeNo→ StopTransaction(type=1); - 读单时已有流水号(少见)→ 直接 StopTransaction(type=1);
- 本实现 StopTransaction 发送后即
v_safe_free_unsettled_order_data()(与 CALLRESULT 无硬耦合)。
11.5 流程示意图(合并视角)
sequenceDiagram
participant MC as meter_calculate
participant EEPROM as EEPROM临时区
participant Flash as Flash订单+未结索引
participant IMPL as s8_plat_get_unsettled_order
participant OCPP as s8_unsettled_order_chaek
Note over MC,EEPROM: 充电中周期
MC->>EEPROM: v_save_temp_chg_record(UNSETTLED)
Note over MC,Flash: 正常结束
MC->>Flash: 写订单 + u8_add_unsettled_order
MC->>EEPROM: clear 临时记录
Note over EEPROM,OCPP: 上电/联网后
EEPROM-->>IMPL: 若有有效临时记录优先读出并 clear
Flash-->>IMPL: 否则读未结队列首条
IMPL->>OCPP: 填充 s_unsettled_order_data
OCPP->>OCPP: 无txId→Auth(1)→StartTx(1)→写preTradeNo→StopTx(1)
12. 两类订单的差异小结与维护注意
| 对比项 | Flash 离线未结订单 | EEPROM 掉电临时订单 |
|---|---|---|
| 典型成因 | 已写完订单但 CSMS 未确认结算 / StopTransaction 未成功 | 充电中断电,仅有最后一次周期快照 |
| 持久化位置 | Flash 订单文件 + EEPROM 未结索引列表 | EEPROM 按枪临时块 |
| 读取顺序 | 在掉电分支 之后 | 优先(for 循环枪号顺序) |
| 停止原因 | 以 S_LOG_DATA 内原有故障码 映射 |
读出后强制 E_FAULT_13073(映射 PowerLoss) |
| OCPP 后续 | 相同:Authorize(1) → StartTx(1) → 填 preTradeNo → StopTx(1)(有流水号则仅 StopTx(1)) | 相同 |
实现层面已知耦合点(便于排查问题):
- 未结补单:
StopTransaction/StartTransaction常用send_mag(0, …),PDU 内 connectorId / 计量 依赖s_unsettled_order_data->u8_gunNo;Authorize(type=1)使用send_mag(gunIdx, …)(订单枪号),以便 CALLRESULT 与ack_data->gunNo一致。需确认 sampled 数据是否混枪。 - Flash 读单后立即
u8_platform_settlement_confirm:若 Flash 读失败,可能导致索引已删。 StartTransaction_res未结算分支已对 Blocked/Invalid 等 拒绝preTradeNo写入;Authorize 非 Accepted 会丢弃内存上下文。StopTransaction成功后立即free,与StopTransaction_res无联动;若需严格「应答后再删单」,需架构层增强。
13. 维护建议
- 新增 OCPP Action:在
OCPP_CMD、ocpp_cmd_str、send_magswitch、接收分发、json 组帧/解析 五处同步扩展。 - 新增 Configuration Key:在
bs_ocpp_str.h、OCPP_CONFIG结构体、Get/Change 分支中一并添加,并处理 Flash 默认值与长度。 - 测量项顺序:
OCPP_MEASURAND_ID顺序不可随意插入,否则配置位图与OCPP_measurand_str错位。 - GetDiagnostics / 4G 日志 FTP:与
plat_comm/4g_module强相关;CallResult 与u8_updata_flag顺序、u8_log_file_upload勿误清、PlatComm 联网与 FTP 半周期交替、QFWRITE/QFREAD分包 等见 第 9.1 节 与app/plat_comm/4g_module/4G模块AT指令配置功能说明.md。 - 补单链路变更:同步核对 第 10~12 节 与
bs_public_impl.c/meter_calculate_impl.c/unsettled_order_mng的一致性。 - 离线 MeterValues:改动落盘键(StartChargeTime / 枪号)、FRAM 映射、快照字段或与在线 measurand 口径时,需同时核对
BS_ocpp_mv_offline_bridge.c、ocpp_mv_offline_flash_impl.*与u8_get_ocpp_measurand_data;释放未结单内存前须v_ocpp_mv_offline_unsettled_abort(见v_safe_free_unsettled_order_data)。
文档版本:2026-05 起补充 离线 MV 桥接 与 BS_ocpp_ctrl.c 分区说明;与当前 CCU601E_D app/plat_comm/ocpp 目录一致,若后续增减文件请同步更新第二节表格。