Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# 4G 模块 AT 指令与配置功能说明(EC200A)
|
||||
|
||||
本目录实现 **Quectel EC200A** 与 MCU 的 AT 交互:**蜂窝注册 → PDP → TCP/TLS 业务**,以及 **FTP(S) 远程升级** 与 **GetDiagnostics 日志上传**。下文按 **AT 流程**、**透传 / 非透传**(含 **TCP 与 FTP 分 PDP**)、**FTP 升级与日志两套状态机**、**plat_comm / OCPP 衔接**、**配置宏与排障** 说明。
|
||||
|
||||
---
|
||||
|
||||
## 目录与文件速览
|
||||
|
||||
| 文件 | 作用 |
|
||||
|------|------|
|
||||
| `ec200a_4g_cfg.h` | 透传开关、TCP/FTP **分 PDP**、PlatComm FTP 与平台间隔、**QFREAD 分片**、**QFTPGET(COM) 分片收包**(与 QFREAD 同思路的轮询等待)、**QFWRITE 分片轮询**、**UFS 单次读写块 `EC200A_FTP_UFS_RW_CHUNK_BYTES`(默认 4096)**、FTP 期间是否暂缓 **QISEND**、日志块大小、日志收尾到 **QISEND** 前延时等 |
|
||||
| `ec200a_4g.h` / `ec200a_4g.c` | 初始化状态机、UART 收发、`QIOPEN`/`QSSLOPEN`、非透传 **`QISEND`/`QIRD`**(明文 TCP)与 **`QSSLSEND`/`QSSLRECV`**(`wss`/`QSSLOPEN`)、软重连等 |
|
||||
| `ec200a_4g_ftp.h` / `ec200a_4g_ftp.c` | **`u8_ec200a_4g_ftp_update`**(固件升级)、**`u8_ec200a_4g_ftp_log_upload`**(诊断日志)、**`v_ftp_ctrl_init`** |
|
||||
|
||||
上层业务(如 OCPP WebSocket)经 **`bs_connect_impl.c`** 的 **`v_bs_send` / `u16_bs_recv`** 接入 4G;FTP 调度由 **`bs_public_impl.c`** 的 **`v_ftp_update_action`** 在 PlatComm 中周期调用。OCPP 侧置位 **`u8_updata_flag`**、**`u8_log_file_upload`** 等与 **`BS_ocpp_ctrl.c`** 协同(见第四节)。
|
||||
|
||||
---
|
||||
|
||||
## 一、AT 指令流程(蜂窝初始化与建链)
|
||||
|
||||
### 1.1 前置:`v_4g_data_init`
|
||||
|
||||
应用解析配置里的服务器 URL(域名、端口、APN、`ws://`/`wss://`)填入 **`S_4G_CONFIG_PARA`**,再调用 **`v_4g_data_init`**,内部 **`s8_cat_reboot(0)`** 完成模组上电/复位类动作,随后周期性调用 **`s8_ec200a_4g_init`** 推进状态机。
|
||||
|
||||
### 1.2 主初始化路径(`s8_ec200a_4g_init`)
|
||||
|
||||
状态枚举 **`E_EC200A_4G_INIT_STEP`**,典型顺序:命令模式 → SIM → CCID → CFUN → CGREG → **业务 PDP `QICSGP`/`QIACT`(`EC200A_TCP_PDP_CTX_ID`)** → 可选 TLS 配置 → **`QIOPEN`/`QSSLOPEN`** → 完成。
|
||||
|
||||
非透传在打开 Socket 前会 **`s8_ec200a_qisde_apply()`**(`AT+QISDE`),再 **`s8_ec200a_qiopen_accumulate`** 等。
|
||||
|
||||
### 1.3 建链与 access_mode
|
||||
|
||||
- **`EC200A_TCP_QIOPEN_ACCESS_MODE`**:透传为 **2**(等 `CONNECT`),缓冲模式为 **0**(轮询 `+QIOPEN`/`+QSSLOPEN` 错误码)。
|
||||
- 业务 Socket 使用 **`EC200A_TCP_PDP_CTX_ID`**(默认 **1**),与 FTP 所用 PDP **必须不同**(见 `ec200a_4g_cfg.h` 编译期校验)。
|
||||
|
||||
### 1.4 超时与恢复
|
||||
|
||||
状态机末尾根据 **`u8_out_cnt`** 累加;过久会打日志、回退 **`STEP_SWCHTO_CMD_MODE`** 并可能 **`s8_cat_reboot`**。非透传下 TCP 断连可先做 **QICLOSE+QIOPEN 软重连**,连续失败后再全量 **`v_bs_4G_data_init`**(见 `ec200a_4g_cfg.h` 中 **`EC200A_TCP_SOFT_REOPEN_FAIL_MAX`**)。
|
||||
|
||||
---
|
||||
|
||||
## 二、透传与非透传(缓冲模式)
|
||||
|
||||
### 2.1 配置关系(`ec200a_4g_cfg.h` 摘要)
|
||||
|
||||
| 宏 | 作用 |
|
||||
|----|------|
|
||||
| **`EC200A_TCP_USE_TRANSPARENT_MODE`** | **1** 透传;**0** 缓冲(`QISEND`/`QIRD`) |
|
||||
| **`EC200A_TCP_PDP_CTX_ID` / `EC200A_FTP_PDP_CTX_ID`** | 业务 TCP 与 FTP **分 PDP**(默认 1 / 2),**禁止相等** |
|
||||
| **`EC200A_FTP_TO_PLATFORM_GAP_MS`** | 每轮 PlatComm 内 **`v_ftp_update_action` 一步之后**、平台收发循环前的间隔 (ms),减轻 FTP AT 与 **QISEND** 粘连 |
|
||||
| **`EC200A_FTP_QFREAD_*`** | 非透传下 **`AT+QFREAD`**(日志读 UFS)及 **`AT+QFTPGET`→`COM:`** 固件分片:应答在单周期内 **短 `mSleep` + 收包累加** 直至 `\r\nOK`;**QFTPGET** 侧等待轮次约为 **`EC200A_FTP_QFREAD_TOTAL_WAITS` 的 12 倍**(FTP 拉网侧数据较慢,见 `s16_qftpget_com_at_slice`) |
|
||||
| **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** | 模组 UFS 单次 **`AT+QFREAD` / `AT+QFWRITE`** 数据负载,同时也是 **固件 `AT+QFTPGET="…","COM:",off,len` 的单片长度**(默认 **4096 = 1024×4**);须为 Flash **`PACKET_SIZE`(1024)** 的整数倍(编译期校验)。固件下载每片经 **CONNECT 负载** 解析后,按 **`upgrade_write_packet`** 与 **`QFREAD` 读 UFS 写 Flash** 相同的 **flash_base + 1024 子包** 逻辑写入 |
|
||||
| **`EC200A_FTP_QFWRITE_*`** | 非透传下 **`AT+QFWRITE`**:CONNECT→发负载→`\r\nOK` 跨多轮 PlatComm **短等轮询**(`QFWRITE_WAIT_MS` / `QFWRITE_WAITS_PER_PLAT_ROUND` / `QFWRITE_TOTAL_WAITS`),避免单周期长时间阻塞 UART |
|
||||
| **`EC200A_FTP_LOG_CHUNK_SIZE` / `EC200A_FTP_LOG_QFWRITE_MAX_BYTES`** | 默认与 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 对齐;日志从 FATFS 读块与单次 QFWRITE 负载一致,**一步一块**、包间回到 AT 态 |
|
||||
| **`EC200A_BLOCK_QISEND_DURING_4G_FTP`** | **1**(默认):`u8_updata_flag` 且 4G 模组 FTP 时 **`v_bs_send`/`ws_send` 暂缓 QISEND**,QFTPPUT 大文件期间避免与 AT 抢 UART;**0** 尝试穿插(仍可能 `no prompt >`)。**勿**仅用 `u8_log_file_upload` 判暂缓(recv 早于 CallResult 置位会挡应答)。 |
|
||||
| **`EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS`** | 日志上传收尾(**`QFTPCLOSE`**、`v_ftp_ctrl_init`、排空 RX)后,首帧 OCPP **QISEND** 前短延时,减轻 **`break off=0`** |
|
||||
| **`EC200A_FTP_LOG_DONE_REQUEST_BS_RENEW`** | 日志上传 **`STEP_FTP_INIT_OK`**:是否 **`v_set_bs_renew`**(默认 **0**,不重连 OCPP);置 **1** 时 FTP 后软重开 TCP 并 **重登** |
|
||||
| **`EC200A_TCP_APP_RW_MAX`** / **`EC200A_QIRD_AT_READ_MAX`** / **`EC200A_QISEND_AT_LEN_MAX`** | 应用单次读写与 Quectel AT 上限(1500 / 1460)钳位 |
|
||||
|
||||
### 2.2 平台统一收发(`bs_connect_impl.c`)
|
||||
|
||||
**`u8_netType == 0`(4G)** 时:`v_bs_send` → 透传 **`v_4g_send_data`** / 非透传 **`v_ec200a_tcp_app_send`**;`u16_bs_recv` 同理。
|
||||
|
||||
非透传 **`s8_ec200a_qisend_once`**:先发 **`AT+QISEND=id,len`**,等 **`>`**,再发二进制负载,再等 **`SEND OK`**;发送前 **`ec200a_modem_uart_rx_drain`** 减轻粘连。详见源码注释与历史版本本文档附录(若需可从版本管理拷贝为 `4G模块AT指令配置功能说明_appendix.md`)。
|
||||
|
||||
### 2.3 TCP 与 FTP 的关系(重点)
|
||||
|
||||
| 维度 | 说明 |
|
||||
|------|------|
|
||||
| **PDP** | 业务 TCP 与 FTP 使用 **不同 context**;FTP 侧 **`QIDEACT`/`QIACT`** 只作用 **`EC200A_FTP_PDP_CTX_ID`**,**不拆**业务 TCP 所在 PDP。 |
|
||||
| **UART** | 物理 **一根** 串口;**`QISEND`/`QIRD`/`QFTP*`/`QFREAD`/`QFWRITE`** 均为 **分时串行**。 |
|
||||
| **“并存”** | 模组协议栈内 TCP 与 FTP 会话可同时存在;MCU **交替发 AT**,单时刻 UART 上只有一种事务。 |
|
||||
| **PlatComm 与联网穿插** | **`plat_comm_task.c`**:4G FTP 活跃时 **相邻两周期交替**——一周期只做 **联网**(`v_plat_comm_network_round`:双路 `recv` + 各平台 `send_ctrl`),下一周期只做 **FTP 一步**(`v_plat_comm_ftp_round`:`recv` → **`v_ftp_update_action`** → **`EC200A_FTP_TO_PLATFORM_GAP_MS`**)。配合 **QFTPGET(COM) / QFREAD / QFWRITE 分包 + 短等**,FTP 传输期间 **OCPP 仍可周期性收发包**(非透传路径)。 |
|
||||
|
||||
---
|
||||
|
||||
## 三、FTP:固件升级与日志上传(两套路径)
|
||||
|
||||
**`u8_ec200a_4g_ftp_update`** 与 **`u8_ec200a_4g_ftp_log_upload`** 共用全局 **`e_ec200a_ftp_init_step`** 与同一套 **`STEP_FTP_*`** 枚举,但 **跳转序列不同**;进入任一路径前通常 **`v_ftp_ctrl_init()`** 将步进复位到 **`STEP_FTP_SWCHTO_CMD_MODE`**(或由业务在 **`GetDiagnostics_res`** 等里显式调用)。
|
||||
|
||||
**`v_ftp_update_action`(`bs_public_impl.c`)** 分支逻辑:
|
||||
|
||||
1. 若 **`s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 1`**(且编译开启 OCPP):走 **`u8_ec200a_4g_ftp_log_upload()`**(4G);网线且未实现日志 FTP 时会清标志并上报失败。
|
||||
2. **否则**:走 **`u8_ec200a_4g_ftp_update()`**(4G 固件)或 LwIP 升级状态机。
|
||||
|
||||
因此 **`u8_log_file_upload` 必须与 `u8_updata_flag` 配合正确**:若误清 **`u8_log_file_upload`**,会在 **`u8_updata_flag` 仍为 1** 时误入 **固件升级** 分支,出现日志里的 **`[FTP-UPG]`** 与 **`RX pkt(0)/(3)`** 等异常(**`BS_ocpp_ctrl.c`** 中已去掉在 **`u8_updata_flag==0` 时无条件清 `u8_log_file_upload`** 的逻辑)。
|
||||
|
||||
### 3.1 固件远程升级(`u8_ec200a_4g_ftp_update`)
|
||||
|
||||
- **入口**:PlatComm 周期调用 **`v_ftp_update_action`** → **`u8_ec200a_4g_ftp_update()`**(当 **`u8_log_file_upload==0`** 且 **`t_ftp_update_info.u8_netType==0`**)。
|
||||
- **日志标签**:**`[FTP-UPG] step=…`**(与日志上传的 **`ftp test …`** 区分)。
|
||||
- **配置**:**`T_FTP_UPDATE_INFO t_ftp_update_info`**(URL、主机、端口、路径、**固件文件名**、账号等)。
|
||||
|
||||
**远端对象路径**:由 **`s32_ftp_fill_remote_get_object()`** 将 **`u8_path` + `u8_file_name`** 拼成 **`s_ftp_remote_get_obj`**(如 `/home/ftpuser/ftpfile/firmware/CCU601E.bin`),供 **`QFTPSIZE`** 与 **`QFTPGET`** 使用,**避免仅 CWD 到绝对路径失败时无法取文件**。
|
||||
|
||||
**`QFTPCWD`**:路径过长则跳过;先 **`AT+QFTPCWD="<path>"`**,失败再试 **去掉前导 `/`**;仍失败则 **打日志并返回成功步进(1)**,后续依赖 **全路径 `QFTPSIZE` / `QFTPGET`**。
|
||||
|
||||
**与源码一致的步骤顺序(摘要)**:
|
||||
|
||||
| 顺序 | 枚举步 | 行为要点 |
|
||||
|------|--------|----------|
|
||||
| 1 | `STEP_FTP_SWCHTO_CMD_MODE` | **`s8_work_mode_cmd`**:退出透传、**`AT`** |
|
||||
| 2–3 | `SET_CGREG` / `QUERY_CGREG` | PS 注册 |
|
||||
| 4–5 | `SET_QIDEACT` / `SET_QIACT` | 仅 **FTP PDP**:去激活 → **`QICSGP`+`QIACT`** |
|
||||
| 6–10 | `FTP_CFG_*` | **`QFTPCFG`**:contextid、account、filetype、transmode、rsptimeout |
|
||||
| 11 | `QFTPOPEN` | 登录 FTP 服务器 |
|
||||
| 12 | `QFTPCWD` | 切换远程目录(失败时仍可继续,见上) |
|
||||
| 13 | `GET_QFTPSIZE` | **`QFTPSIZE`** 对 **`s_ftp_remote_get_obj`** 取远程大小 → **`u32_file_size`**;成功则按 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 计算 **`u16_packSize` / `u16_packNum`**,**`u16_packIndex=0`** |
|
||||
| 14 | `QFDEL` | 清模组 **UFS** 旧文件(与日志/杂项共用空间,仍建议执行) |
|
||||
| 15 | `GET_QFTPGET` | 循环 **`AT+QFTPGET="<remote>","COM:",offset,len`**:**非透传** 用 **`s16_qftpget_com_at_slice`**(与 **`s16_qfread_at_slice`** 同思路等 **OK**);**透传** 用 **`s16_4g_Send_Recv_Ftp_Get`**。首片前 **`v_upgrade_header_data(0)`**;**`parse_4g_ftp_response`** 取 CONNECT 与 **OK** 间二进制,按与 **`s8_ec200a_qfread`** 相同的 **`flash_base` + 1024 子包** 调 **`upgrade_write_packet`**。**返回 0** 表示尚有分片;**1** 表示全部写完 |
|
||||
| 16 | `STEP_FTP_SAVE` / `INIT_OK` | **`v_upgrade_header_data(1)`** + **`v_sys_reboot()`** |
|
||||
|
||||
**已不再作为升级主路径**:**`QFLST` → `QFOPEN` → `QFSEEK` → `QFREAD`(从 UFS 整包下载后再读)**;源码中相关 **`case`** 仍保留,供其它场景或对照,**正常 OCPP 固件升级不会走入**。
|
||||
|
||||
**超时**:全局 **`u8_out_cnt`** 超过约 **3 分钟** 清 **`u8_updata_flag`**、打 **`FTP TO, exit upg`**;**`QUERY_CGREG`** 另有 **120 次** 强制跳转。
|
||||
|
||||
### 3.2 GetDiagnostics 日志上传(`u8_ec200a_4g_ftp_log_upload`)
|
||||
|
||||
- **入口**:**`u8_log_file_upload==1`** 时由 **`v_ftp_update_action`** 调用。
|
||||
- **日志标签**:**`ftp test <步号>`**。
|
||||
- **典型流程**:命令模式 → **UFS `QFDEL`** → 本地日志信息(**`s8_get_log_info`**:按 **`EC200A_FTP_LOG_CHUNK_SIZE`** 等收紧 **`u16_packSize`**)→ **`QFOPEN`** 在 UFS 上覆盖打开日志文件 → **`STEP_FTP_FTP_WRITE_LOG_UFS`**:**`QFSEEK` + `QFWRITE`** 从 FATFS **按块(默认 4096)** 写入模组 UFS;**`QFWRITE` 在单周期内仅短等轮询**,未收齐 **CONNECT/OK** 时 **`s8_ec200a_write_log_data` 返回进行中**,下一 FTP 半周期续传(**不采用整段 `QFUPL` 透传写文件`**,避免长时间占 UART)→ **`QFLST`** →(与升级类似的)**CGREG / FTP PDP `QIDEACT`/`QIACT`** → **`QFTPCFG` 链** → **`QFTPOPEN`/`QFTPCWD`** → **`QFTPPUT`** 上传到远端 → **`AT+QFTPCLOSE`**(**`s8_ec200a_qftpclose`**)→ **`STEP_FTP_INIT_OK`** 等待若干周期 → 清 **`u8_updata_flag`**、**`u8_log_file_upload`**、**`v_ftp_ctrl_init`**,并在非透传下 **排空 RX + `EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS` 延时 + 再 drain**,减轻紧接着的 **Uploaded** / **StatusNotification** 出现 **`break off=0`**。
|
||||
|
||||
**`EC200A_FTP_LOG_CHUNK_SIZE` / `EC200A_FTP_LOG_QFWRITE_MAX_BYTES`**:默认与 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`** 一致;控制从 FATFS 单次读取与单次 **QFWRITE** 负载。注意 **读缓冲 + 过滤缓冲** 的 RAM 占用(过滤后长度 ≤ 当块读长)。
|
||||
|
||||
### 3.3 与「误走升级分支」的鉴别
|
||||
|
||||
| 现象 | 含义 |
|
||||
|------|------|
|
||||
| 日志 **`[FTP-UPG]`** + **`QFTPGET(COM)` / `step` 至 `GET_QFTPGET`** | 正在跑 **`u8_ec200a_4g_ftp_update`** |
|
||||
| 日志 **`ftp test`** | 正在跑 **`u8_ec200a_4g_ftp_log_upload`** |
|
||||
| GetDiagnostics 后出现 **`[FTP-UPG]`** 且 **`RX pkt(0)/(3)`** 卡进度 | 多为 **`u8_log_file_upload` 被清 0** 仍 **`u8_updata_flag==1`**,已按上节在 OCPP 侧修复 |
|
||||
|
||||
---
|
||||
|
||||
## 四、与 plat_comm、OCPP、互斥策略的衔接
|
||||
|
||||
### 4.1 `plat_comm_task.c`
|
||||
|
||||
- **`u8_ftp_transfer_active`**:任一路 **`u8_updata_flag!=0`**;若开启 OCPP,在 **4G** 且 **`u8_log_file_upload==1`** 时,即使 **`u8_updata_flag` 仍为 0**(GetDiagnostics **CallResult 尚未置位 `updata_flag`** 的窗口),也视为 **FTP 活跃**,以便调度 **`v_ftp_update_action`** 跑日志状态机。
|
||||
- **`ftp4g`**:**`u8_ftp_transfer_active && t_ftp_update_info.u8_netType==0`**。为真时:使用 **静态相位 `s_plat_comm_net_ftp_phase`** —— **奇偶半周期交替**:一半周期只跑 **`v_plat_comm_network_round`**(联网收发),另一半周期只跑 **`v_plat_comm_ftp_round`**(开头双路 **`v_bs_recv_data`** → **`v_ftp_update_action` 一步** → **`EC200A_FTP_TO_PLATFORM_GAP_MS`**)。这样 **FTP 与 OCPP 不在同一 PlatComm 周期内叠执行**,与 **`QFTPGET(COM)`/`QFREAD`/`QFWRITE` 分包 + 短等** 叠加后,**FTP 传输过程中仍可稳定进行联网数据收发**。
|
||||
- **网线 FTP**:**`u8_ftp_transfer_active && netType!=0`** 时独占整周期 **`continue`**(与 4G 交替策略不同)。
|
||||
|
||||
### 4.2 `u8_bs_ftp_ocpp_strict_mux`(`plat_comm_task.h` / `v_plat_data_init`)
|
||||
|
||||
- **默认 0**:不在 FTP 期间强行拦截 OCPP **`v_bs_send`**;大文件上传请依赖 **`EC200A_BLOCK_QISEND_DURING_4G_FTP`(默认已 1)** 暂缓 QISEND,否则易 **`no prompt >`**。若仍要业务帧与 FTP 硬穿插可 **`#define EC200A_BLOCK_QISEND_DURING_4G_FTP 0`** 并拉大 **`EC200A_FTP_TO_PLATFORM_GAP_MS`**,或运行时置 **`u8_bs_ftp_ocpp_strict_mux=1`**。
|
||||
- **为 1**:**`bs_connect_impl.c`** 在 **模组 FTP 传输请求有效** 且目标为 OCPP 通道时 **return** 掉发送;**`bs_public_impl.c`** 在 OCPP **4G TX busy** 时暂缓 **`v_ftp_update_action`**。与 **GetDiagnostics** 的 **「先 CallResult / Uploading,再置 `u8_updata_flag`」** 顺序配合使用(见 **`BS_ocpp_ctrl.c`**)。
|
||||
|
||||
### 4.3 OCPP 要点(文件在 `ocpp/`,此处仅列衔接)
|
||||
|
||||
- **GetDiagnostics**:**`GetDiagnostics_mag`** 解析 URL 并置 **`u8_log_file_upload`**;**`GetDiagnostics_res`** 准备 **`fileName`**、**`v_ftp_ctrl_init`**,**不在组帧前**置 **`u8_updata_flag`**,避免与 strict mux 组合导致 **CallResult 发不出**。**`send_mag`** 在 **CallResult `ws_send` 成功** 后置 **`u8_diag_notify_pending=Uploading`**;**`ocpp_send_mag_ctrl`** 在 **Uploading** 的 **`send_mag` 之后** 再置 **`u8_updata_flag=1`**。与 **4G 分包 FTP、PlatComm 半周期交替** 的衔接说明见 **`ocpp/OCPP功能模块说明.md`** **第 9.1 节**。
|
||||
- **`v_ocpp_private_logic`**:不再在 **`u8_updata_flag==0`** 时无条件 **`u8_log_file_upload=0`**,避免误切 **固件升级** 分支。
|
||||
|
||||
### 4.4 透传模式下的 FTP
|
||||
|
||||
透传(**`EC200A_TCP_USE_TRANSPARENT_MODE==1`**)下,**日志写 UFS 的 `QFOPEN`/`QFSEEK`/`QFWRITE` 路径不参与编译**(相关步进会失败或超时退出);**固件升级** 仍使用 **`QFTPGET`→`COM:`** 分片 + **`s16_4g_Send_Recv_Ftp_Get`**,与缓冲模式相比 UART 占用特征不同,需单独测试。
|
||||
|
||||
---
|
||||
|
||||
## 五、配置与排障建议
|
||||
|
||||
| 问题倾向 | 建议 |
|
||||
|-----------|------|
|
||||
| **`4G QISEND: no prompt >` / `break off=0`** | 保持 **`EC200A_BLOCK_QISEND_DURING_4G_FTP=1`**(默认);增大 **`EC200A_FTP_TO_PLATFORM_GAP_MS`**;确认 **日志已走 `QFOPEN+QFSEEK+QFWRITE` 分包**(勿再依赖长时 **`QFUPL`** 透传);固件已走 **`QFTPGET(COM)`** 分片;可调 **`EC200A_FTP_UFS_RW_CHUNK_BYTES`**(须为 1024 倍数)、**`EC200A_FTP_QFREAD_*`**(同时影响 QFTPGET 片内等待上限);或 **`u8_bs_ftp_ocpp_strict_mux=1`** |
|
||||
| **`QSSLOPEN` 成功但 `AT+QISEND` 立刻 ERROR** | **`wss` 须用 `AT+QSSLSEND` / `AT+QSSLRECV`**,不能对 SSL socket 发 **`QISEND`/`QIRD`**(固件 `ec200a_4g.c` 已按 `use_tls` 分支) |
|
||||
| **`QFTPCWD` 失败但需升级** | 当前实现会在 CWD 失败后仍继续;若仍 **QFTPSIZE/QFTPGET** 失败,核对 **FTP 上实际路径** 与 **`s_ftp_remote_get_obj`** 拼接是否一致(含前导 `/`) |
|
||||
| **日志上传结束后首包 OCPP 失败** | 确认 **`QFTPCLOSE`**;调大 **`EC200A_FTP_LOG_DONE_TO_QISEND_DELAY_MS`**;若确认 TCP 已死且可接受重登,再设 **`EC200A_FTP_LOG_DONE_REQUEST_BS_RENEW=1`** |
|
||||
| **GetDiagnostics 走成 `[FTP-UPG]`** | 确认 **`u8_log_file_upload`** 未被异常清除;**`v_ftp_update_action`** 是否进入 **`u8_log_file_upload` 分支** |
|
||||
| **双 PDP** | 修改 context 时同时覆盖 **TCP/FTP** 两宏且保持 **不等** |
|
||||
| **QIRD ERROR** | 检查 **`want`** 是否超 **1500**(已由宏钳位) |
|
||||
|
||||
---
|
||||
|
||||
## 六、自检清单(FTP 升级相关)
|
||||
|
||||
- [ ] **`EC200A_TCP_PDP_CTX_ID` ≠ `EC200A_FTP_PDP_CTX_ID`**
|
||||
- [ ] 固件升级日志为 **`[FTP-UPG]`**;**`QFTPSIZE`** 使用 **远端全路径**;**`QFTPGET(COM)`** 分片日志 **`pkt i/N`**;**全部片完成后** 直接进入 **`STEP_FTP_SAVE`**(不经 **UFS QFLST/QFREAD** 主路径)
|
||||
- [ ] 日志上传为 **`ftp test`**,写 UFS 阶段为 **`QFOPEN` + 循环 `QFSEEK`/`QFWRITE`(块长默认 4096)**,上传结束有 **`QFTPCLOSE`** 与收尾 **drain + 延时**
|
||||
- [ ] PlatComm:**4G FTP** 时周期头 **recv + FTP + gap**,且 **每平台 send 后 recv**
|
||||
- [ ] OCPP:**CallResult → Uploading → `u8_updata_flag`** 顺序;**不误清 `u8_log_file_upload`**
|
||||
|
||||
---
|
||||
|
||||
*路径:`CCU601E_D/app/plat_comm/4g_module/4G模块AT指令配置功能说明.md`*
|
||||
*若需保留旧版「2.5 QISEND/QIRD 字节级长说明」,可从版本管理历史中拷贝为 `4G模块AT指令配置功能说明_appendix.md`。*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file bs_wh_gm5.h
|
||||
* @brief WH-GM5 4G 模组 AT 驱动
|
||||
*
|
||||
* 负责模组初始化、APN/Socket 配置、信号查询及串口通信。
|
||||
* 通信串口:USART6(COM_PORT_BS)。
|
||||
*/
|
||||
#ifndef _BS_WH_GM5_H_
|
||||
#define _BS_WH_GM5_H_
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "main.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "usart.h"
|
||||
#include "plat_comm/plat_comm_task.h"
|
||||
|
||||
#define COM_PORT_BS E_USART_6 //4G 模组 WH-GM5 通信串口
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STEP_SWCHTO_CMD_MODE = 0, //切换到指令模式
|
||||
STEP_MODE_NET_ON, //设置工作模式为网络透传模式(默认net)
|
||||
STEP_UAT_ON, //使能串口AT指令(默认ON),
|
||||
STEP_UAT_SET_PACK_TIME,
|
||||
STEP_SET_PWD, //设置指令密码(默认usr.cn)
|
||||
STEP_SET_APN, //设置APN
|
||||
STEP_SET_SDPEN, //设置套接字分发
|
||||
STEP_SCKA_ON, //设置socketA使能(默认ON)
|
||||
STEP_SET_SCKA_PARA, //设置socketA参数
|
||||
STEP_SCKB_ON, //设置socketB使能(默认ON)
|
||||
STEP_SCKB_OFF, //设置socketB关闭(OFF)
|
||||
STEP_SET_SCKB_PARA, //设置socketB参数
|
||||
STEP_HEART_OFF, //设置心跳包不使能
|
||||
STEP_SAVE, //保存当前设置并重启
|
||||
STEP_INIT_OK, //设置完成
|
||||
}E_WH_GM5_INIT_STEP;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_Sim_Type; //APN类型(0:移动,1:联通,2:电信)
|
||||
U8_T u8_socketB_en; //socketB使能 默认套接字分发功能使能 - 使能分发时,组帧解帧需要添加帧头;不使能直接透传模式,将数据上送平台,但无法区分多平台
|
||||
|
||||
U8_T *socketA_IP; //域名或者IP+端口号 例如 www.baidu.com:1234
|
||||
U8_T *socketB_IP; //is NULL 则不存在socket
|
||||
U8_T *apn_str;
|
||||
|
||||
U16_T socketA_port; //端口号
|
||||
U16_T socketB_port;
|
||||
U8_T use_tls; //是否启用模块内置 TLS:0=TCP,1=TLS(wss)
|
||||
}S_4G_CONFIG_PARA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_out_cnt; //超时计数
|
||||
|
||||
E_WH_GM5_INIT_STEP e_wh_gm5_init_step; //无线通信模块初始化步骤标志
|
||||
|
||||
U8_T u8_Sim_Card[NUM_20]; //SIM卡号
|
||||
U8_T u8_4g_Version[NUM_20]; //4g版本
|
||||
|
||||
S_4G_CONFIG_PARA s_4g_para;
|
||||
}S_4G_CTRL_DATA;
|
||||
|
||||
|
||||
|
||||
//结构体1 -- 用于内部逻辑控制和数据存储
|
||||
//结构体2 -- 用于联网参数存储-获取
|
||||
|
||||
|
||||
S8_T s8_UAT_check_signal_quality(void);
|
||||
S8_T s8_check_number(U8_T *data); //查询SIM卡号
|
||||
S8_T s8_check_socket_LinkState(U8_T u8_id);
|
||||
S8_T s8_WH_GM5_init(void);
|
||||
|
||||
U8_T u8_UAT_CheckConnState(void); //通过串口AT指令(密码ieszt)查询模块网络连接状态
|
||||
void v_wh_gm5_data_init(S_4G_CONFIG_PARA *s_4g_para);
|
||||
|
||||
void v_4g_uart_cache_clear(void);
|
||||
|
||||
U8_T v_GM5_get_sim(U8_T *data); /**< 获取 SIM 卡号,有数据返回 1 */
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
|
||||
#ifndef EC200A_4G_H
|
||||
#define EC200A_4G_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "plat_comm/4g_module/ec200a_4g_cfg.h"
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef enum
|
||||
{
|
||||
STEP_SWCHTO_CMD_MODE = 0, //切换到指令模式
|
||||
STEP_QUERY_SIM_STATE, //查询SIM卡状态
|
||||
STEP_QUERY_CCID, //查询sim卡号
|
||||
STEP_SET_CFUN, //设置功能模式
|
||||
|
||||
STEP_SET_CGREG, //设置PS域 网络注册状态
|
||||
STEP_QUERY_CGREG, //查询PS域 网络注册状态
|
||||
STEP_SET_QICSGP_APN, //设置TCP场景参数 设置APN
|
||||
STEP_SET_CGQMIN, //设置服务质量参数(最低可接受的)
|
||||
STEP_SET_CGQREQ, //设置服务质量参数(请求的)
|
||||
STEP_QUERY_CSQ, //查询信号强度
|
||||
STEP_SET_QIACT, //激活PDP场景
|
||||
STEP_SET_SSL_CFG, //TLS 配置(QSSLCFG 等,仅在需要 TLS 时执行)
|
||||
STEP_SET_SOCKET_AND_OPEN, //设置socket 参数并打开(TCP 或 TLS)
|
||||
|
||||
STEP_SAVE, //保存当前设置并重启
|
||||
STEP_INIT_OK, //设置完成
|
||||
STEP_INIT_DEBUG, // 调试
|
||||
}E_EC200A_4G_INIT_STEP;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_Sim_Type; //APN类型(0:移动,1:联通,2:电信)
|
||||
U8_T u8_socketB_en; //socketB使能 默认套接字分发功能使能 - 使能分发时,组帧解帧需要添加帧头;不使能直接透传模式,将数据上送平台,但无法区分多平台
|
||||
|
||||
U8_T *socketA_IP; //域名或者IP+端口号 例如 www.baidu.com:1234
|
||||
U8_T *socketB_IP; //is NULL 则不存在socket
|
||||
U8_T *apn_str;
|
||||
|
||||
U16_T socketA_port; //端口号
|
||||
U16_T socketB_port;
|
||||
U8_T use_tls; //是否启用模块内置 TLS:0=TCP,1=TLS(wss)
|
||||
}S_4G_CONFIG_PARA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_out_cnt; //超时计数
|
||||
U8_T u8_csq; //信号强度
|
||||
E_EC200A_4G_INIT_STEP e_4g_init_step; //无线通信模块初始化步骤标志
|
||||
|
||||
U8_T u8_Sim_Card[NUM_20]; //SIM卡号
|
||||
U8_T u8_4g_Version[NUM_20]; //4g版本
|
||||
|
||||
U32_T u32_outtime; //超时计数 单位秒
|
||||
U8_T u8_tcp_socket_id; // 非透传:QISEND/QIRD 使用的 connectID(明文 TCP=0;TLS 与 QSSLOPEN 的 clientID 一致)
|
||||
S_4G_CONFIG_PARA s_4g_para;
|
||||
}S_4G_CTRL_DATA;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
S8_T s8_ec200a_4g_init(void);
|
||||
void v_4g_data_init(S_4G_CONFIG_PARA *s_4g_para);
|
||||
|
||||
void v_4g_send_data(U8_T u8_id, U8_T *u8_sendBuf , U16_T u16_len);
|
||||
U16_T u16_4g_recv_data(U8_T *u8_recvBuf ,U16_T u16_len);
|
||||
/** 排空模组 RX FIFO(丢弃);FTP / QISEND 等共用 UART 去粘连 */
|
||||
void v_4g_uart_rx_discard_fifo(void);
|
||||
|
||||
#if !EC200A_TCP_USE_TRANSPARENT_MODE
|
||||
/** 非透传:经 AT+QISEND 发送 TCP/TLS 负载(按 EC200A_TCP_APP_RW_MAX 分包;详见 ec200a_4g.c) */
|
||||
void v_ec200a_tcp_app_send(U8_T u8_id, U8_T *u8_sendBuf, U16_T u16_len);
|
||||
/** 非透传:AT+QIRD 读模组 Socket 缓冲(单次至多 EC200A_TCP_APP_RW_MAX;大帧需上层循环) */
|
||||
U16_T u16_ec200a_tcp_app_recv(U8_T *u8_recvBuf, U16_T u16_len);
|
||||
/** 模组已在 STEP_INIT_OK 且 PDP 正常时,仅 AT+QICLOSE/QSSLCLOSE + QIOPEN/QSSLOPEN 重建业务 Socket(不拆 PDP) */
|
||||
S8_T s8_ec200a_tcp_socket_close(void);
|
||||
S8_T s8_ec200a_tcp_socket_soft_reopen(void);
|
||||
#endif
|
||||
U8_T u8_ec200a_modem_tcp_stack_ok(void);
|
||||
|
||||
U8_T u8_get_sim_card(U8_T *u8_data ,U8_T buf_len);
|
||||
U8_T u8_UAT_check_signal_quality(void);
|
||||
|
||||
void v_4g_log_data_send_data(U8_T *u8_sendBuf , U16_T u16_len);
|
||||
|
||||
S8_T s8_ec200a_qiact(void);
|
||||
/** 按 context_id 配置/激活/去激活 PDP,供 FTP 与业务 TCP 分场景(见 ec200a_4g_cfg.h) */
|
||||
S8_T s8_ec200a_qicsgp_ctx(U8_T context_id);
|
||||
S8_T s8_ec200a_qiact_ctx(U8_T context_id);
|
||||
S8_T s8_ec200a_qideact_ctx(U8_T context_id);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EC200A_4G_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
#ifndef EC200A_4G_CFG_H
|
||||
#define EC200A_4G_CFG_H
|
||||
|
||||
#include "usart.h"
|
||||
|
||||
/**
|
||||
* @file ec200a_4g_cfg.h
|
||||
* @brief EC200A 模组:串口选择、TCP/FTP/TLS 场景、FTP 轮询与 UFS 块长、非透传 TCP 分包与缓冲派生。
|
||||
* 可在包含本头或 `ec200a_4g.h` 之前用 `#define 宏名 值` 覆盖下列默认值。
|
||||
*/
|
||||
|
||||
/* ========== 模组 UART 与调试(`ec200a_4g.c` 发送/排空等共用) ========== */
|
||||
|
||||
/** 1:在 `v_4g_send_data` / `u16_4g_recv_data` 等路径打印调试信息;0:关闭。开启时 printf 须走调试口,勿与模组口 COM_PORT_BS 共用 TX。 */
|
||||
#ifndef TEST_BS_PRINTF_FLAG
|
||||
#define TEST_BS_PRINTF_FLAG (0)
|
||||
#endif
|
||||
|
||||
/** 与 Quectel 模组通信的 USART 逻辑编号(见 `usart.h` 中 `E_USART_ID`,默认 UART12 接 4G)。 */
|
||||
#ifndef COM_PORT_BS
|
||||
#define COM_PORT_BS E_USART_6
|
||||
#endif
|
||||
|
||||
/** `v_4g_send_data` 第一形参占位 ID;当前实现未区分多路,固定 0xFF 即可。 */
|
||||
#ifndef ID_4G
|
||||
#define ID_4G (0xFFU)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 1) 透传 / QIOPEN 访问模式 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** Socket 数据路径:1 = UART 透传数据态;0 = 缓冲/命令模式(AT+QISEND / AT+QIRD)。 */
|
||||
#ifndef EC200A_TCP_USE_TRANSPARENT_MODE
|
||||
#define EC200A_TCP_USE_TRANSPARENT_MODE (0)
|
||||
#endif
|
||||
|
||||
/** AT+QIOPEN / AT+QSSLOPEN 最后一参「access_mode」:透传为 2,缓冲模式为 0(随 `EC200A_TCP_USE_TRANSPARENT_MODE` 自动选择)。 */
|
||||
#if EC200A_TCP_USE_TRANSPARENT_MODE
|
||||
#define EC200A_TCP_QIOPEN_ACCESS_MODE (2)
|
||||
#else
|
||||
#define EC200A_TCP_QIOPEN_ACCESS_MODE (0)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 2) PDP:业务 TCP vs FTP;TLS 与业务 TCP 同 PDP */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** 业务 TCP(QIOPEN/QSSLOPEN、模组 QIACT)使用的 PDP context ID。 */
|
||||
#ifndef EC200A_TCP_PDP_CTX_ID
|
||||
#define EC200A_TCP_PDP_CTX_ID (1U)
|
||||
#endif
|
||||
|
||||
/** FTP(QFTPCFG、QICSGP、QIACT、QIDEACT)使用的 PDP context ID;须与 `EC200A_TCP_PDP_CTX_ID` 不同,避免 FTP 去激活时拆掉业务 TCP。 */
|
||||
#ifndef EC200A_FTP_PDP_CTX_ID
|
||||
#define EC200A_FTP_PDP_CTX_ID (2U)
|
||||
#endif
|
||||
|
||||
/* 编译期约束:TCP 与 FTP 不得共用同一 PDP,否则 FTP 侧 QIDEACT/重配会拆掉业务 TCP。 */
|
||||
#if (EC200A_TCP_PDP_CTX_ID) == (EC200A_FTP_PDP_CTX_ID)
|
||||
#error EC200A_FTP_PDP_CTX_ID must not equal EC200A_TCP_PDP_CTX_ID
|
||||
#endif
|
||||
|
||||
/** TLS(QSSL*)使用的 PDP 与业务 TCP 相同,与 `EC200A_TCP_PDP_CTX_ID` 保持一致。 */
|
||||
#define EC200A_SSL_PDP_CTX_ID EC200A_TCP_PDP_CTX_ID
|
||||
|
||||
/** AT+QSSLCFG 等命令中的 SSL context 编号(模组侧 ctx)。 */
|
||||
#ifndef EC200A_SSL_CTX_ID
|
||||
#define EC200A_SSL_CTX_ID (1)
|
||||
#endif
|
||||
|
||||
/** AT+QSSLOPEN 使用的 clientID / 套接字索引(与明文 TCP 的 connectID 区分)。 */
|
||||
#ifndef EC200A_SSL_CLIENT_ID
|
||||
#define EC200A_SSL_CLIENT_ID (4)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 3) FTP:AT+QFREAD / AT+QFWRITE 轮询(单次 sleep 与总等待次数) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** QFREAD/QFWRITE 收 UART 时,每轮循环内单次 `mSleep` 间隔(毫秒)。 */
|
||||
#ifndef EC200A_FTP_UART_SLICE_WAIT_MS
|
||||
#define EC200A_FTP_UART_SLICE_WAIT_MS (25U)
|
||||
#endif
|
||||
|
||||
/** 单次 `s16_qfread_at_slice` 调用内,最多执行「sleep + 收包」的次数;超时仍未见应答则本次读失败。 */
|
||||
#ifndef EC200A_FTP_QFREAD_TOTAL_WAITS
|
||||
#define EC200A_FTP_QFREAD_TOTAL_WAITS (80U)
|
||||
#endif
|
||||
|
||||
/** 单次 `s8_ec200a_qfwrite_log_bin` 调用内,等待 CONNECT/OK 等阶段的最多次数;超出则本次写失败。 */
|
||||
#ifndef EC200A_FTP_QFWRITE_TOTAL_WAITS
|
||||
#define EC200A_FTP_QFWRITE_TOTAL_WAITS (200U)
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 4) GetDiagnostics / UFS:QFUPL 与单次读写块长 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** `AT+QFUPL` 第三参数:UART 侧无数据输入超时(秒),手册范围 1~65535;分片发送时须大于单 PlatComm 周期可能间隔。 */
|
||||
#ifndef EC200A_FTP_QFUPL_AT_TIMEOUT_SEC
|
||||
#define EC200A_FTP_QFUPL_AT_TIMEOUT_SEC (120U)
|
||||
#endif
|
||||
|
||||
/** QFUPL 数据态下,单次向模组 UART 写入负载的上限(字节);0 表示按整块发送的旧策略(由上层决定)。 */
|
||||
#ifndef EC200A_FTP_QFUPL_UART_SLICE_MAX
|
||||
#define EC200A_FTP_QFUPL_UART_SLICE_MAX (512U)
|
||||
#endif
|
||||
|
||||
/** 模组 UFS 单次 `AT+QFREAD` / `AT+QFWRITE` 数据负载(字节)。FTP 升级 QFREAD 缓冲、日志 FAT 读块与 QFWRITE 上限均以此为界;须为 Flash `PACKET_SIZE` 的倍数且 ≤65535。 */
|
||||
#ifndef EC200A_FTP_UFS_RW_CHUNK_BYTES
|
||||
#define EC200A_FTP_UFS_RW_CHUNK_BYTES (4096U)
|
||||
#endif
|
||||
|
||||
#if !EC200A_TCP_USE_TRANSPARENT_MODE
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 5) 非透传 TCP:软重连、QISDE、应用分包、模组 AT 上限与接收环形区派生尺寸 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/** 连续「仅 QICLOSE+QIOPEN 软重连」失败达到此次数后,才走全量 `v_bs_4G_data_init` 类驻网恢复。 */
|
||||
#ifndef EC200A_TCP_SOFT_REOPEN_FAIL_MAX
|
||||
#define EC200A_TCP_SOFT_REOPEN_FAIL_MAX (5U)
|
||||
#endif
|
||||
|
||||
/** AT+QISDE:1 将 QISEND 的 TCP 负载回显到模组 UART(调试用);0 关闭(初始化通常下发 AT+QISDE=0)。 */
|
||||
#ifndef EC200A_QISDE_ECHO
|
||||
#define EC200A_QISDE_ECHO (0U)
|
||||
#endif
|
||||
|
||||
/** 应用层单次 TCP 读/写意向上限(字节);`ec200a_4g.c` 内会再钳位到 `EC200A_QIRD_AT_READ_MAX` / `EC200A_QISEND_AT_LEN_MAX`。 */
|
||||
#ifndef EC200A_TCP_APP_RW_MAX
|
||||
#define EC200A_TCP_APP_RW_MAX (2048U)
|
||||
#endif
|
||||
|
||||
/** AT+QIRD 第二参数「欲读长度」上限(Quectel 文档常见上限 1500);超过模组可能直接 ERROR。 */
|
||||
#ifndef EC200A_QIRD_AT_READ_MAX
|
||||
#define EC200A_QIRD_AT_READ_MAX (1500U)
|
||||
#endif
|
||||
|
||||
/** AT+QISEND 单次发送负载上限(字节,Quectel 常见上限 1460)。 */
|
||||
#ifndef EC200A_QISEND_AT_LEN_MAX
|
||||
#define EC200A_QISEND_AT_LEN_MAX (1460U)
|
||||
#endif
|
||||
|
||||
/** 读到 +QIRD: 0(缓冲无数据)后额外延时(毫秒),减轻空轮询;0 表示不延时。 */
|
||||
#ifndef EC200A_QIRD_EMPTY_POLL_DELAY_MS
|
||||
#define EC200A_QIRD_EMPTY_POLL_DELAY_MS (0U)
|
||||
#endif
|
||||
|
||||
/** 存放 AT+QIRD 应答及 +QIRD: 前缀等 URC 的缓冲上限:`EC200A_TCP_APP_RW_MAX` + 固定余量(字节)。 */
|
||||
#define EC200A_QIRD_RSP_MAX ((EC200A_TCP_APP_RW_MAX) + 384U)
|
||||
|
||||
/** 非透传下 QIOPEN/QSSLOPEN 应答在单任务内拼接用的累计缓冲大小(字节)。 */
|
||||
#define EC200A_QIOPEN_ACCUM_SZ (640U)
|
||||
|
||||
#if EC200A_QISDE_ECHO
|
||||
/** QISDE=1 时:等待 `>` / SEND OK 的环形接收区容量(字节),需容纳回显的应用数据。 */
|
||||
#define EC200A_QISEND_WAIT_RB_SZ ((EC200A_TCP_APP_RW_MAX) * 2U + 512U)
|
||||
/** QISDE=1 时:`memmove` 收缩接收区时保留的尾部字节数,避免截断半条 URC。 */
|
||||
#define EC200A_QISEND_RB_TAIL_KEEP ((EC200A_TCP_APP_RW_MAX) / 2U + 256U)
|
||||
#else
|
||||
/** QISDE=0 时:等待 QISEND 提示与应答的接收缓冲容量(字节)。 */
|
||||
#define EC200A_QISEND_WAIT_RB_SZ (896U)
|
||||
/** QISDE=0 时:接收区收缩时保留的尾部字节数(字节)。 */
|
||||
#define EC200A_QISEND_RB_TAIL_KEEP (384U)
|
||||
#endif
|
||||
|
||||
#endif /* !EC200A_TCP_USE_TRANSPARENT_MODE */
|
||||
|
||||
#endif /* EC200A_4G_CFG_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
|
||||
#ifndef EC200A_4G_FTP_H
|
||||
#define EC200A_4G_FTP_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "publicdata/type.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
U8_T u8_ec200a_4g_ftp_update();
|
||||
U8_T u8_ec200a_4g_ftp_log_upload();
|
||||
|
||||
void v_ftp_ctrl_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* EC200A_4G_FTP_H */
|
||||
@@ -0,0 +1,365 @@
|
||||
# IES 积成平台通信移植方案(CCU601_A → CCU601E_D)
|
||||
|
||||
本文档基于 **`CCU601_A_20240601\app\platform`** 中积成智通(IESZT)协议实现,对照 **`CCU601E_D\app\plat_comm`** 现有联网框架(`plat_comm_task` + `bs_connect_impl` + `bs_public_impl`),给出在 E 工程中**新增 IES 平台连接**的分阶段实现方案。
|
||||
|
||||
---
|
||||
|
||||
## 0. 备份说明
|
||||
|
||||
已在本机创建工程目录备份(不含 `MDK-ARM\output` 编译产物,以缩短拷贝时间):
|
||||
|
||||
| 路径 |
|
||||
|------|
|
||||
| `d:\Desktop\NewProject\cursor\EuropeanStandard\CCU601E_D_backup_20260519` |
|
||||
|
||||
如需完整含编译输出的备份,可在资源管理器中自行复制整个 `CCU601E_D` 目录。
|
||||
|
||||
---
|
||||
|
||||
## 1. 源工程(CCU601_A)IES 模块概览
|
||||
|
||||
### 1.1 文件与职责
|
||||
|
||||
| 文件 | 行级规模(约) | 职责 |
|
||||
|------|----------------|------|
|
||||
| **`BS_ies_ctrl.h`** | 协议常量 + `S_IES_CTRL_DATA` | 帧类型码 `NT_*`、数据对象、对外 API |
|
||||
| **`BS_ies_ctrl.c`** | ~560 行 | 回调注册、TCP/4G 建链、登录、收包解帧、私有超时逻辑 |
|
||||
| **`BS_ies_interface.h/c`** | ~1800+ 行 | **组帧/解帧**:登录、心跳、费率、实时数据、账单、远程启停、对时、FTP 升级、二维码等 |
|
||||
| **`BS_Task.c/h`** | ~1150 行 | **平台任务总线**:周期发送、双枪调度、`bs_register_callback`、`Task_BS` 主循环 |
|
||||
| **`FtpUpdate.c`** | FTP 升级 | 与 `NT_UPDATE_AUTO(0x90)` 配合(A 侧旧 FTP 流程) |
|
||||
| **`BS_WH_GM5.c`** | 4G 模组 | A 工程 **WH-GM5** 透传/AT(**E 工程为 EC200A,不可直接搬**) |
|
||||
|
||||
### 1.2 协议特征(与 OCPP 差异)
|
||||
|
||||
积成协议为 **二进制定长头 + 变长数据域**,非 JSON/WebSocket。
|
||||
|
||||
```
|
||||
帧起始 0x68 | 长度 2B | 序号 1B | 设备地址 16B ASCII | 加密 1B | 数据对象 1B | 帧类型 1B | 数据域 N | 校验 1B
|
||||
```
|
||||
|
||||
- **协议版本**:`NT_PTL_VERSION = 37`(V3.5)
|
||||
- **帧类型码**:登录 `0x01/0x02`、心跳 `0x03/0x04`、充电实时 `0x11/0x13`、账单 `0x47/0x48`、远程启停 `0x44~0x46`、对时 `0x76`、计费 `0x7D/0x7E`、FTP `0x90` 等(见 `BS_ies_ctrl.h`)
|
||||
- **校验**:序号域至数据域末字节 **累加和**(`u8_crc_checkSum`)
|
||||
- **粘包**:`v_bs_unpack_handle_ieszt` 按 `0x68` 搜头、长度域算整帧长
|
||||
|
||||
### 1.3 A 侧任务模型
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Task_BS
|
||||
TCP[v_bs_tcp_connect]
|
||||
LOG[v_ies_logo_on / v_bs_register_req]
|
||||
SEND[v_bs_send_ctrl]
|
||||
RECV[v_ies_recv_data → unpack]
|
||||
PRIV[v_ies_private_crel]
|
||||
end
|
||||
subgraph callbacks
|
||||
IF[BS_ies_interface 组帧]
|
||||
end
|
||||
SEND -->|E_BS_CALLBACK_CMD| IF
|
||||
IF -->|com_read / eth_send| LINK[4G WH_GM5 / 以太网 socket]
|
||||
```
|
||||
|
||||
- **发送**:`BS_Task.c` 中 `v_bs_send_ctrl(bs_flag)` 按周期/标志调用 `v_bs_callback(cmd, gun)`,IES 在 `v_bs_ies_register_callback()` 中注册具体组帧函数。
|
||||
- **接收**:`v_ies_recv_data()` → `v_bs_unpack_handle_ieszt()` → `v_bs_rx_cmd_hanle()` 按 `u8_recvBuf[22]` 帧类型分发。
|
||||
- **登录成功**:`v_bs_register_ack()` 内置 `u8_register_flag = 1`,并置 `bs_ctrl_data[].u8_log_on_flag`(与 E 的 `u8_log_on_flag` 概念一致)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标工程(CCU601E_D)现状
|
||||
|
||||
### 2.1 已有挂钩(未实现)
|
||||
|
||||
| 位置 | 内容 |
|
||||
|------|------|
|
||||
| **`public_define.h`** | `BS_IES_EN` 默认 **0** |
|
||||
| **`plat_comm_task.c`** | `MasterFuncArray` 已预留 **`"ieszt"`** 槽位及 7 个回调函数名 |
|
||||
| **`plat_comm_task.h`** | `#include "plat_comm/ieszt/bs_ies_ctrl.h"`、`S_BS_IES_CTRL_DATA s_ies_data` |
|
||||
| **目录** | **`plat_comm/ieszt/` 尚不存在**(编译开启 `BS_IES_EN` 会缺文件) |
|
||||
|
||||
E 侧 **`MasterFuncPtl`** 比 A 多一项 **`v_bs_recv_unpack`**(OCPP 未用;IES 可将解帧放在 `v_ies_recv_data` 内,**`v_ies_recv_unpack` 可留空**)。
|
||||
|
||||
### 2.2 E 侧应复用的基础设施(禁止重复造轮子)
|
||||
|
||||
| 模块 | 用途 |
|
||||
|------|------|
|
||||
| **`plat_comm_task.c`** | `v_Plat_Comm` 主循环、FTP/联网分时、`v_set_bs_renew` |
|
||||
| **`bs_connect_impl.c`** | **`u8_tcp_connect`**、**`v_bs_send`**、**`u16_bs_recv`**(4G EC200A + 网口 LwIP/TLS) |
|
||||
| **`bs_public_impl.c`** | 枪状态、**`S_LOG_DATA`** 订单、启停 **`v_bs_charge_action_ctrl`**、未结/掉电 **`s8_plat_get_unsettled_order`** |
|
||||
| **`meter_calculate`** | 计量订单、EEPROM 临时单、Flash 未结索引 |
|
||||
| **`flowctrl`** | `u8_bs_get_flow_state`(E 已有,替代 A 的 `e_get_flow_rt_state`) |
|
||||
| **`v_ftp_update_action`** | OCPP/平台 FTP 升级;IES `0x90` 需映射到 **`t_ftp_update_info`** + `u8_updata_flag` |
|
||||
|
||||
### 2.3 A → E 关键差异对照
|
||||
|
||||
| 维度 | CCU601_A | CCU601E_D |
|
||||
|------|----------|-----------|
|
||||
| RTOS / 任务 | `Task_BS` + `GetCurSecond` | `v_Plat_Comm` + `get_current_seconds` |
|
||||
| 4G | WH-GM5、`com_read(COM_PORT_BS)` | **EC200A**、`v_ec200a_tcp_app_send/recv` |
|
||||
| 网口 | `eth_connect` / `CreatSocket` 直连 | **`lwip_tcp_connect`** + `v_bs_eth_close_socket` |
|
||||
| 订单结构 | **`CHARGE_LOG_T`** | **`S_LOG_DATA`** |
|
||||
| 双平台控制块 | **`bs_ctrl_data[MAX_BS_NUM]`** | **`s_bs_task_ctrl.s_bs_ctrl[]`** + 可选 **`s_ies_data`** |
|
||||
| 平台协议 | SDK + IES 二选一或双开 | **OCPP** + 可选 **IES**(`MAX_BS_NUM=2`) |
|
||||
| 日志 | `logMsg_bs` | **`Plat_Comm_LOG` / `MYLOG_MSG`** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 目标架构(移植后)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph PlatComm
|
||||
PC[v_Plat_Comm]
|
||||
M[MasterFuncArray slot: ieszt]
|
||||
end
|
||||
subgraph ieszt
|
||||
CTRL[bs_ies_ctrl.c]
|
||||
IF[bs_ies_interface.c]
|
||||
SEND[v_ies_send_ctrl]
|
||||
end
|
||||
subgraph impl
|
||||
CONN[bs_connect_impl]
|
||||
PUB[bs_public_impl]
|
||||
end
|
||||
PC --> M
|
||||
M --> CTRL
|
||||
SEND --> IF
|
||||
IF --> CONN
|
||||
CTRL --> PUB
|
||||
CONN --> EC200A[4G EC200A]
|
||||
CONN --> LWIP[LwIP TCP/TLS]
|
||||
```
|
||||
|
||||
**原则**
|
||||
|
||||
1. **IES 只实现「积成二进制协议」**;TCP/4G/网口/TLS **一律走 `bs_connect_impl`**。
|
||||
2. **业务数据只通过 `bs_public_impl` + `meter_calculate`** 读写,不在 IES 模块内直接碰 flow 内部结构。
|
||||
3. **周期发送**可移植 A 的 `E_BS_CALLBACK_CMD` 模型,但计时器挂到 **`s_bs_task_ctrl.s_bs_ctrl[ies_id]`** 或独立 **`S_BS_IES_CTRL_DATA`**,由 **`v_ies_send_ctrl`** 在已登录分支执行(与 OCPP 的 `v_ocpp_send_ctrl` 同级)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 建议目录与文件清单
|
||||
|
||||
在 **`CCU601E_D/app/plat_comm/ieszt/`**(运行时说明见 **`ieszt/IES积成平台功能说明.md`**,与 **`平台联网任务功能说明.md`** 联动)新建:
|
||||
|
||||
| 文件 | 来源 | 说明 |
|
||||
|------|------|------|
|
||||
| **`bs_ies_ctrl.h`** | 自 A `BS_ies_ctrl.h` 改编 | 帧类型码、`S_BS_IES_CTRL_DATA`(合并原 `S_IES_CTRL_DATA` + 必要定时器) |
|
||||
| **`bs_ies_ctrl.c`** | 自 A `BS_ies_ctrl.c` 重构 | 去掉 WH_GM5/CreatSocket;改用 `u8_tcp_connect(id)`、`v_bs_send`、`u16_bs_recv` |
|
||||
| **`bs_ies_interface.h`** | 自 A `BS_ies_interface.h` | 组帧/解帧 API |
|
||||
| **`bs_ies_interface.c`** | 自 A `BS_ies_interface.c` | 大文件;发送改为 `v_bs_send`;桩号从 `u8_get_str_data(E_STR_PILE_NO)` 获取 |
|
||||
| **`bs_ies_send.c`**(可选) | 自 A `BS_Task.c` 抽取 | `v_ies_send_ctrl`、`v_ies_register_callback`、双枪周期逻辑,减小单文件体积 |
|
||||
| **`bs_ies_adapter.c/h`**(建议新建) | 新写 | **`S_LOG_DATA` ↔ 账单组帧字段**、停止原因 **E_FAULT_* → IESZT_REASON** |
|
||||
|
||||
**不迁入 E 工程(或仅参考)**
|
||||
|
||||
- `BS_WH_GM5.c/h` → 使用现有 **`ec200a_4g.c`**
|
||||
- `BS_sdk_*` → 与 IES 无关
|
||||
- A 版 `FtpUpdate.c` 中与 OCPP 重复部分 → 统一 **`v_ftp_update_action`** + `ec200a_4g_ftp` / `lwip_ftp_upgrade`
|
||||
|
||||
---
|
||||
|
||||
## 5. 接口映射(MasterFuncPtl 七件套)
|
||||
|
||||
| 回调 | 实现要点 |
|
||||
|------|----------|
|
||||
| **`v_ies_init_data(U8_T id)`** | `id` 写入 `s_ies_data.u8_bs_flag`;`v_ies_register_callback()`;清 `s_ies_data`;**不要**调用 `v_bs_4G_data_init`(由 `u8_tcp_connect` 统一处理);帧序号 `u16_msgSerialNumber=0` |
|
||||
| **`v_ies_tcp_connect(void)`** | 返回 **`u8_tcp_connect(s_ies_data.u8_bs_flag)`** 结果;删除 A 中 `v_WH_GM5_init`、独立 `CreatSocket` |
|
||||
| **`v_ies_logo_on(void)`** | 周期发 **`v_bs_register_req()`**(约 20s);**`v_bs_register_ack`** 内置置位后返回 1;登录超时 6 次 → **`v_set_bs_renew(id)`** + 仅重置 IES 私有数据(**勿全量 `v_ocpp_init_data`**) |
|
||||
| **`v_ies_recv_data(void)`** | `u16_bs_recv(id, buf, len)`;若 `u8_updata_flag` 且 4G FTP 活跃则可直接 return(与 A 一致) |
|
||||
| **`v_ies_recv_unpack(id, buf, len)`** | **建议空实现**;解帧已在 `v_ies_recv_data` 完成。若未来 UART 分包由外部注入,再迁入 `v_bs_unpack_handle_ieszt` |
|
||||
| **`v_ies_send_ctrl(void)`** | 移植 A **`v_bs_send_ctrl(ies_flag)`**:登录后心跳、费率、空闲/充电实时、表底、告警、账单、即插即充、离线单等 |
|
||||
| **`v_ies_private_logic(U8_T id)`** | 心跳无应答计数、登录态维护、**`v_cheak_state_change` 类逻辑**(改调 E 的 `v_bs_log_txFlag_*`)、离线订单 **`s8_plat_get_unsettled_order` 路径**(可选与 OCPP 互斥) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 数据与业务适配层(`bs_ies_adapter`)
|
||||
|
||||
### 6.1 订单 / 账单(`NT_BILL_UPLOAD 0x47`)
|
||||
|
||||
A:`v_get_log_data` / `v_gunTmpChgRcd_rd` + `CHARGE_LOG_T` → `v_bs_bill_pack`。
|
||||
|
||||
E 建议:
|
||||
|
||||
```c
|
||||
S_LOG_DATA log;
|
||||
u8_plat_get_real_order(&log, gun) 或 s8_plat_get_unsettled_order(&log, 0);
|
||||
v_ies_bill_pack_from_log(gun, &log);
|
||||
```
|
||||
|
||||
- **`u8_preTradeNo`**:IES 账单字段与 A 一致处直接 `memcpy`;无平台流水号时用设备流水号或占位规则与运营确认。
|
||||
- **停充原因**:`log.e_stop_reason[0]` 经 **`v_ies_map_stop_reason_to_ieszt()`** 转为 `IESZT_REASON_E`(A 已在 `BS_Task.c` 有大表,可迁到 adapter)。
|
||||
|
||||
### 6.2 远程启停(`0x44` / `0x46`)
|
||||
|
||||
A:`v_bs_rmt_start/stop` 内调 flow/UI。
|
||||
|
||||
E:
|
||||
|
||||
```c
|
||||
S_BS_CHAR_CTRL ctrl;
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.u16_ctrl_res = E_NORMAL_1002; /* 或映射平台原因 */
|
||||
v_bs_charge_action_ctrl(gun, E_CHAR_CTRL_STOP, &ctrl);
|
||||
```
|
||||
|
||||
启机需组 **`S_CHAR_LOG_START_DATA`** 后 `E_CHAR_CTRL_START`,与 OCPP RemoteStart 路径对齐,避免双协议冲突(见 §8)。
|
||||
|
||||
### 6.3 对时(`NT_CLK_SYNC_REQ 0x76`)
|
||||
|
||||
A:写 RTC。
|
||||
|
||||
E:**`v_bs_set_time(&Comm_Time)`**(`bs_public_impl` 已有)。
|
||||
|
||||
### 6.4 计费模型(`0x05/0x06/0x7D/0x7E`)
|
||||
|
||||
A:写费率 Flash/共享区。
|
||||
|
||||
E:对接 **`meter_calculate` / 费率存储**(`u32_meterlog_get_sys_data(E_METERLOG_GET_DATA_FEE_*)` 或现有配置接口),需与产品确认 IES 费率 PDU 与 E 侧 96 时段模型是否一致。
|
||||
|
||||
### 6.5 实时数据(`0x11` / `0x13`)
|
||||
|
||||
字段来源改为:
|
||||
|
||||
- 电压电流 SOC:`u32_bs_get_sys_data` / `u64_bs_get_sys_data`
|
||||
- 枪状态:`u8_bs_get_flow_state`、`E_BS_GET_SYS_DATA_GUN_STATE`
|
||||
- 电量:`E_BS_GET_SYS_DATA_MEMTER_ELECT`
|
||||
|
||||
### 6.6 FTP 升级(`NT_UPDATE_AUTO 0x90`)
|
||||
|
||||
解析平台下发的 FTP 参数 → 填充 **`t_ftp_update_info`** → 置对应槽位 **`u8_updata_flag=1`** → 由现有 **`v_ftp_update_action`** 执行(4G/网口分支已有)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 配置与编译集成
|
||||
|
||||
### 7.1 宏与工程配置
|
||||
|
||||
```c
|
||||
/* public_define.h */
|
||||
#define BS_IES_EN (1) /* 使能积成平台 */
|
||||
#define BS_OCPP_EN (1) /* 可与 OCPP 双平台:槽位 0=OCPP,1=IES,由 PLAT_EN 决定 */
|
||||
```
|
||||
|
||||
- Keil / CMake:将 **`plat_comm/ieszt/*.c`** 加入 **PlatComm** 分组。
|
||||
- 确认 **`plat_comm_task.h`** 中 `S_BS_IES_CTRL_DATA` 与 `bs_ies_ctrl.h` 定义一致(或 typedef 别名)。
|
||||
|
||||
### 7.2 系统参数(与 A 对齐项)
|
||||
|
||||
| 配置项 | A 来源 | E 建议 |
|
||||
|--------|--------|--------|
|
||||
| 主站 IP/端口 | `u8_remote_ip` / `u16_remote_port` | 继续用 **`t_sys_fix_cfg`**;IES **不用** WebSocket URL,可用 **纯 `tcp://ip:port`** 或单独 **IES 服务器配置项**(若与 OCPP 域名不同,需在配置表增加字段,避免与 `u8_serverA_domain_name` 混用) |
|
||||
| 桩号 16 字节 | `u8_pile_serial_num` | `u8_get_str_data(E_STR_PILE_NO)` |
|
||||
| 平台使能 | `bs_type[]` 初始化 | `E_BS_GET_SYS_DATA_PLAT_EN` per 槽位 |
|
||||
| 联网方式 | `e_network_type` | `E_BS_GET_SYS_DATA_NET_TYPE` |
|
||||
|
||||
### 7.3 `MasterFuncArray` 注册示例
|
||||
|
||||
与现有 `plat_comm_task.c` 一致,确保 **`BS_IES_EN`** 时槽位与 **`PLAT_EN`** 配置对应,例如:
|
||||
|
||||
- 槽位 0:OCPP(`BS_OCPP_EN`)
|
||||
- 槽位 1:IES(`BS_IES_EN`)
|
||||
|
||||
双平台同时使能时,**两路独立 `u8_TCP_connect_flag` / `u8_log_on_flag`**,互不覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 8. 双平台共存策略(OCPP + IES)
|
||||
|
||||
| 风险 | 策略 |
|
||||
|------|------|
|
||||
| 同一枪被 OCPP RemoteStart 与 IES `0x44` 同时控制 | 产品层约定:**仅一个平台为主**;或 `PLAT_EN` 互斥。代码层在 `v_bs_rmt_start` 中判断 **`u8_bs_get_flow_state==0`** 且 OCPP 未在充。 |
|
||||
| 离线订单双路上送 | E 未结索引统一 **`unsettled_order_mng`**;IES 掉电单走 **`s8_plat_get_unsettled_order`** 时与 OCPP **互斥处理**(同一单不两协议各 Stop 一次)。 |
|
||||
| 4G UART 占用 | OCPP 与 IES **共用 `bs_connect_impl`** 时,**不能两槽位同时 `u8_netType==0` 各建一条 Socket**,除非运营确认为双连接;通常 **一机一路 4G 一个 TCP** → **双平台只选其一或网口+4G 分流**。 |
|
||||
| FTP 与 IES 收包 | 保持 **`plat_comm_task` FTP/联网分时**;IES **`v_ies_recv_data`** 在 FTP 周期不解析 IES 帧(与 A 相同 guard)。 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 分阶段实施计划
|
||||
|
||||
### 阶段 1:骨架可编译(1~2 天)
|
||||
|
||||
- [ ] 创建 `plat_comm/ieszt/`,迁入 **`bs_ies_ctrl.h`**、精简 **`bs_ies_ctrl.c`**(`init/tcp_connect/logo_on/recv/private` 空壳 + `u8_tcp_connect`)
|
||||
- [ ] **`BS_IES_EN=1`** 编译通过;`v_ies_recv_unpack` 空实现
|
||||
- [ ] 槽位 **`bs_enable=1`** 时能进 `v_Plat_Comm` 建链日志
|
||||
|
||||
### 阶段 2:链路 + 登录心跳(2~3 天)
|
||||
|
||||
- [ ] 迁入 **`bs_ies_interface.c`** 中:`u8_bs_pack_frm_head`、`v_bs_register_req/ack`、`v_bs_heart/ack`、`u8_crc_checkSum`
|
||||
- [ ] 收包粘包 **`v_bs_unpack_handle_ieszt`**
|
||||
- [ ] 实机验证:**Login 0x02**、**Heartbeat 0x04**、平台对时(若有)
|
||||
|
||||
### 阶段 3:运营关键帧(3~5 天)
|
||||
|
||||
- [ ] **`v_bs_rtd_free` / `v_bs_rtd_chging`** + `v_ies_send_ctrl` 周期表(对齐 A:心跳 20s、充电 30s、空闲 180s 等)
|
||||
- [ ] **`v_bs_bill_pack`** + adapter 从 **`S_LOG_DATA`**
|
||||
- [ ] **`v_bs_rmt_start/stop`** → `v_bs_charge_action_ctrl`
|
||||
- [ ] **`v_bs_bill_upload_ack`** → `v_bs_platform_settlement_confirm` / 清 EEPROM 临时单
|
||||
|
||||
### 阶段 4:增强与对齐(按需)
|
||||
|
||||
- [ ] 计费模型 `0x7D/0x7E`、二维码 `0x08/0x0A`、故障 `0x15`、表底 `0x31`
|
||||
- [ ] FTP `0x90` → `t_ftp_update_info`
|
||||
- [ ] 掉电未结:与 **`meter_calculate` EEPROM 恢复**、**`s8_plat_get_unsettled_order`** 联调
|
||||
- [ ] 双枪 `u8_bs_twoGnu_send` 逻辑迁入
|
||||
|
||||
### 阶段 5:测试与文档
|
||||
|
||||
- [ ] 用例:4G / 网线、单枪/双枪、远程启停、断网重连、充电中离线、掉电上电补单
|
||||
- [ ] 更新 **`平台联网任务功能说明.md`** 增加 IES 章节
|
||||
|
||||
---
|
||||
|
||||
## 10. 接收发送伪代码(E 风格)
|
||||
|
||||
```c
|
||||
void v_ies_recv_data(void)
|
||||
{
|
||||
U8_T id = s_ies_data.u8_bs_flag;
|
||||
U8_T buf[512];
|
||||
U16_T n;
|
||||
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[id].u8_updata_flag != 0U
|
||||
&& s_bs_task_ctrl.s_bs_ctrl[id].u8_netType == 0U) {
|
||||
return;
|
||||
}
|
||||
n = u16_bs_recv(id, buf, sizeof(buf));
|
||||
if (n > 0U) {
|
||||
v_bs_unpack_handle_ieszt(buf, (S16_T)n);
|
||||
}
|
||||
}
|
||||
|
||||
static void v_ies_send_frame(U8_T *frm, U16_T len)
|
||||
{
|
||||
v_bs_send(s_ies_data.u8_bs_flag, frm, len);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试验收标准(摘要)
|
||||
|
||||
| 编号 | 项 | 通过标准 |
|
||||
|------|-----|----------|
|
||||
| T1 | TCP | `PlatComm` 打印 `tcp connect`,平台侧可见连接 |
|
||||
| T2 | 登录 | 收到 `0x02`,`u8_log_on_flag=1`,桩号与配置一致 |
|
||||
| T3 | 心跳 | 周期 `0x03`,平台 `0x04`,断链后 `v_set_bs_renew` 可恢复 |
|
||||
| T4 | 充电实时 | 充电中 `0x11`、空闲 `0x13` 周期上送,数据与电表一致 |
|
||||
| T5 | 账单 | 停充后 `0x47`,平台 `0x48` 后本地未结清除 |
|
||||
| T6 | 远程停 | 平台 `0x46` 后枪停止,原因码正确 |
|
||||
| T7 | 掉电 | 有 txId 的订单恢复后账单 **transactionId 非 0**(与 OCPP 掉电修复一致) |
|
||||
|
||||
---
|
||||
|
||||
## 12. 关联文档
|
||||
|
||||
| 文档 | 路径 |
|
||||
|------|------|
|
||||
| E 联网任务总览 | `plat_comm/平台联网任务功能说明.md` |
|
||||
| OCPP 模块 | `plat_comm/ocpp/OCPP功能模块说明.md` |
|
||||
| 4G EC200A | `plat_comm/4g_module/4G模块AT指令配置功能说明.md` |
|
||||
| 源 IES 协议头 | `CCU601_A_20240601/app/platform/BS_ies_ctrl.h` |
|
||||
|
||||
---
|
||||
|
||||
*文档版本:2026-05-19;随 `plat_comm/ieszt` 落地情况更新勾选清单。*
|
||||
@@ -0,0 +1,14 @@
|
||||
# gwsdk — 国网平台 SDK(预留)
|
||||
|
||||
**当前未参与 CCU621 编译。**
|
||||
|
||||
- `public_define.h` 中 `BS_GWSDK_EN = 0`
|
||||
- `GD32H759.uvprojx` 未添加本目录下 `.c` 文件
|
||||
- 依赖第三方头:`infra_defs.h`、`protocol.h` 等(PCU606 侧 SDK 包)
|
||||
|
||||
启用前需:
|
||||
|
||||
1. 将 SDK 库与头文件路径加入 Keil Include
|
||||
2. 将 `bs_gwsdk_ctrl.c`、`bs_gwsdk_interface.c` 加入工程
|
||||
3. 将 `gwsdk_interface.c` 中 `ccu_comm/ccu_interface.h` 改为 `plat_comm/impl/bs_local_data.h`
|
||||
4. 设置 `BS_GWSDK_EN=1`,并与 `BS_IES_EN` 互斥或按产品配置二选一
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
|
||||
#ifndef BS_GWSDK_CTRL_H
|
||||
#define BS_GWSDK_CTRL_H
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "publicdata/type.h"
|
||||
|
||||
#if (BS_GWSDK_EN)
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
#define GWSDK_TIMERCNT_NUM (3 + 5*GUN_MAX_CNT) //计时计数 -每一个需主动上送的报文总数
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef enum{
|
||||
SDK_TIMER_REQUSET = 0, //校时
|
||||
SDK_TIMER_FEE_REQ, //费率请求
|
||||
SDK_TIMER_DC_DEV_INFO, //直流设备属性上报
|
||||
|
||||
|
||||
SDK_TIMER_REAL_WORK_GUN, //工作实时状态 数量跟随枪数量 - 包含工作状态和空闲状态两种
|
||||
SDK_TIMER_TRADE_INFO = SDK_TIMER_REAL_WORK_GUN + GUN_MAX_CNT, //订单信息上送 数量跟随枪数量
|
||||
SDK_TIMER_PROPERTY_BMS = SDK_TIMER_TRADE_INFO + GUN_MAX_CNT, //BMS 属性周期上送
|
||||
SDK_TIMER_METER_INFO = SDK_TIMER_PROPERTY_BMS + GUN_MAX_CNT, //电表低值上送
|
||||
SDK_TIMER_FAULT_INFO = SDK_TIMER_METER_INFO + GUN_MAX_CNT, //故障信息上报
|
||||
}SDK_SEND_TIMER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_bs_flag; //处于双平台架构中的哪个 用于映射控制结构体的地址
|
||||
S8_T gw_connet_flag; //联网标识
|
||||
|
||||
U8_T u8_bs_state_type; //维护状态
|
||||
U8_T u8_bs_reboot_flag; //平台重启标志 - 要求收到重启命令时,需回复后再重启
|
||||
U8_T u8_bs_gun2_notup_cnt; //2枪订单未上传计时
|
||||
|
||||
U8_T txFlag_synTime; //是否已校时标识 0否 1是
|
||||
U8_T txFlag_askFeeMode; //请求费率
|
||||
|
||||
U8_T txFlag_firwareInfo; //固件信息上送标志
|
||||
U8_T txFlag_versInfo; //版本信息上送标志
|
||||
U8_T txFlag_cfgInfoUpdate; //配置信息更新请求
|
||||
U8_T txFlag_devMduInfoEvt; //设备组件信息上报
|
||||
|
||||
U8_T u8_Meter_ack[2]; //电表召测标志
|
||||
|
||||
U8_T u8_sim_id[20]; //sim卡号
|
||||
|
||||
U16_T u16_work_info_period; //工作实时状态 上送周期
|
||||
U16_T u16_free_info_period; //空闲实时状态 上送周期
|
||||
U16_T ChargeCnt; //充电序号计次
|
||||
U16_T OperateCnt; //操作序号计次 生成订单流水号时使用
|
||||
|
||||
struct
|
||||
{
|
||||
U8_T u8_real_state_cnt; //充电实时状态 上送计数
|
||||
U16_T u16_charge_cnt; //充电次数
|
||||
U32_T u32_charge_time; //充电时间
|
||||
}s_gwsdk_gun[GUN_MAX_CNT];
|
||||
|
||||
struct
|
||||
{
|
||||
U16_T u16_timerCnt; //周期发送计数
|
||||
U16_T u16_timeNUM ; //计数周期值 单位秒
|
||||
}s_timerCnt[GWSDK_TIMERCNT_NUM];
|
||||
}S_GWSDK_CTRL_DATA;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void v_sdk_init_data(U8_T bs_flag);
|
||||
U8_T v_sdk_tcp_connect(void);
|
||||
U8_T v_sdk_logo_on(void);
|
||||
void v_sdk_recv_data(void);
|
||||
void v_sdk_send_ctrl(void);
|
||||
void v_sdk_private_crel(U8_T u8_flag);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* BS_GWSDK_CTRL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
|
||||
#ifndef BS_GWSDK_INTERFACE_H
|
||||
#define BS_GWSDK_INTERFACE_H
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#if (BS_GWSDK_EN)
|
||||
|
||||
#include "infra_defs.h"
|
||||
#include "protocol_data_def.h"
|
||||
#include "interface.h"
|
||||
#include "protocol.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void gw_get_firmware_data(evs_event_firmware_info *fireware_info ); //获取固件信息
|
||||
void gw_get_ver_data(evs_event_ver_info *ver_info); //获取版本信息
|
||||
void gw_get_devInfo_data(evs_event_devmdu_info* devInfo_data); //获取设备组件信息
|
||||
void gw_get_alarm_data(evs_event_alarm *event_alarm ,U8_T u8_gunNo); //获取故障告警信息
|
||||
void gw_getPilePropertyData(evs_property_dcPile* dcProperty); //获取直流桩设备属性
|
||||
void gw_get_gunlink_data(evs_event_pile_stutus_change *gunlink_state,U8_T u8_gunNo); //获取枪连接状态
|
||||
void gw_get_stopResult_info(U8_T gunNo,evs_event_stopCharge* event_stopResult); //枪停止结果上送
|
||||
void gw_get_startResult_info(U8_T gunNo,evs_event_startResult* event_startResult) ;//枪启动结果上送
|
||||
void gw_get_DC_meter_data(evs_property_meter* meterData,U8_T u8_gunNo); //获取直流电表底值数据
|
||||
void gw_get_dc_BMS_Data(evs_property_BMS* BMSData ,U8_T u8_gunNo); //获取直流桩BMS属性;
|
||||
void gw_get_log_info(U8_T flag ,U8_T gunNo,evs_event_tradeInfo* tradeInfo_data);
|
||||
void gw_get_Auth_Data(evs_event_startCharge* AuthData ,U8_T u8_gunNo); //获取即插即充所需数据
|
||||
void gw_get_dc_nonWork(evs_property_dc_nonWork* gunData,U8_T u8_gunNo); //获取直流非充电中状态数据
|
||||
void gw_get_dc_work(evs_property_dc_work* gunData,U8_T u8_gunNo); //获取直流充电中状态数据
|
||||
|
||||
U8_T u8_feeModle_save(evs_service_issue_feeModel *param);
|
||||
U8_T u8_gwsdk_start_charge_ctrl(evs_service_startCharge *param);
|
||||
U8_T u8_gwsdk_stop_chaege_ctrl(evs_service_stopCharge *param);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* BS_GWSDK_INTERFACE_H */
|
||||
@@ -0,0 +1,200 @@
|
||||
# IES 积成平台功能说明(CCU601E_D / plat_comm/ieszt)
|
||||
|
||||
本文档描述 **积成智通 IESZT** 二进制协议在 E 工程中的落地方案、与 **PlatComm 联网任务** 的挂接关系,以及 **`S_BS_IES_CTRL_DATA`** 各字段含义。
|
||||
上层任务状态机、双平台槽位、FTP 分时等见 **[《平台联网任务功能说明.md》](../平台联网任务功能说明.md)**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块定位
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **目录** | `app/plat_comm/ieszt/` |
|
||||
| **编译宏** | **`BS_IES_EN`**(`public_define.h`) |
|
||||
| **PlatComm 槽位** | `MasterFuncArray` 名称 **`"ieszt"`**(`plat_comm_task.c`);与 OCPP 并存时通常为槽位 **0**,OCPP 为槽位 **1**(以 `PLAT_EN` 配置为准) |
|
||||
| **全局数据** | **`s_bs_task_ctrl.s_ies_data`**(`plat_comm_task.h`,类型 **`S_BS_IES_CTRL_DATA`**) |
|
||||
| **传输层** | 与 OCPP 共用 **`bs_connect_impl`**:`u8_tcp_connect` / `v_bs_send` / `u16_bs_recv`(**明文 TCP**,无 WebSocket) |
|
||||
| **当前阶段** | **登录/心跳** + **校时** + **费率** + **二维码 0x08/0x0A** |
|
||||
|
||||
---
|
||||
|
||||
## 2. 源文件与功能分区
|
||||
|
||||
与 **`BS_ocpp_ctrl.c`** 相同,`.c` 内用 **`#if 1 //公共` / `#if 1 //发送` / `#if 1 //接收` / `#if 1 //外部控制使用`** 折叠分区(便于 Keil 大纲跳转与整段屏蔽调试)。
|
||||
|
||||
| 文件 | `#if 1` 分区 | 内容 |
|
||||
|------|----------------|------|
|
||||
| **`bs_ies_ctrl.c`** | 公共 | `v_ies_timer_add`、`u8_ies_timer_ctrl`、定时器初值、`v_ies_reset_ctx`、`v_ies_on_login_ok` |
|
||||
| | 发送 | `v_ies_logo_on`、`v_ies_send_ctrl` |
|
||||
| | 接收 | `v_ies_recv_data`、`v_ies_recv_unpack` |
|
||||
| | 外部控制使用 | `v_ies_init_data`、`v_ies_tcp_connect`、`v_ies_private_logic` |
|
||||
| **`impl/bs_public_impl.c`** | — | **`u8_get_str_data` / `u8_set_str_data`**、**`u32_bs_get_sys_data` / `u8_set_sys_data`**(含 `E_STR_QR_DISPLAY`、`E_BS_GET_SYS_DATA_UNIX_TIMESTAMP`) |
|
||||
| **`bs_ies_interface.c`** | 公共 | 组帧头、CRC、`v_ies_finish_frame_and_send` |
|
||||
| | 发送 | `v_ies_register_req`、`v_ies_heart_req`、`v_ies_fee_req`、`u8_ies_send_ack` |
|
||||
| | 接收 | 登录/心跳/校时/费率/二维码;`v_ies_unpack_handle`(经 `bs_public_impl` 读写系统) |
|
||||
| **`bs_ies_ctrl.h`** | — | 协议常量、`IES_SEND_TIMER`、`S_BS_IES_CTRL_DATA`、对外 API |
|
||||
| **`bs_ies_interface.h`** | — | 报文层对外声明 |
|
||||
|
||||
移植路线图(阶段 3+)见 **`../IES积成平台移植方案.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 与 PlatComm 的挂接(对照联网任务文档 §4)
|
||||
|
||||
### 3.1 七回调映射
|
||||
|
||||
| PlatComm 回调 | IES 实现 | 说明 |
|
||||
|---------------|----------|------|
|
||||
| **`v_bs_data_init`** | `v_ies_init_data` | 清零 **`s_ies_data`**,写入 **`u8_bs_flag`** |
|
||||
| **`v_bs_tcp_connect`** | `v_ies_tcp_connect` | 建链前清 **`u8_register_flag`**;内部 **`u8_tcp_connect(id)`** |
|
||||
| **`v_bs_logon_connect`** | `v_ies_logo_on` | 周期发 **0x01**;**`u8_register_flag==1`** 时返回 1,置槽位 **`u8_log_on_flag`** |
|
||||
| **`v_bs_recv_data`** | `v_ies_recv_data` | **`u16_bs_recv`** → **`v_ies_unpack_handle`** |
|
||||
| **`v_bs_recv_unpack`** | `v_ies_recv_unpack` | 空实现(4G 外部分包预留) |
|
||||
| **`v_bs_send_ctrl`** | `v_ies_send_ctrl` | 已登录后周期发 **0x03**、**0x05**(未获费率时) |
|
||||
| **`v_bs_private_logic`** | `v_ies_private_logic` | **`u8_heart_outTime_cnt`** 超限 → **`v_set_bs_renew`** |
|
||||
|
||||
调度顺序与 **`v_plat_comm_network_round`** 一致,见 **[平台联网任务功能说明.md §4.3](../平台联网任务功能说明.md)**。
|
||||
|
||||
### 3.2 与 `S_BS_CTRL_DATA` 的分工
|
||||
|
||||
| 数据 | 位置 | 用途 |
|
||||
|------|------|------|
|
||||
| TCP/登录标志 | **`s_bs_ctrl[i].u8_TCP_connect_flag`** / **`u8_log_on_flag`** | PlatComm 通用状态机 |
|
||||
| IES 协议态 | **`s_ies_data`** | 登录应答、心跳计数、**`s_timerCnt[]`** 周期定时、序列号等 |
|
||||
| 槽位号 | **`s_ies_data.u8_bs_flag`** | 与 **`i`**、`E_STR_PILE_NO` 第三参数、`v_bs_send(id)` 一致 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 运行态结构 `S_BS_IES_CTRL_DATA`
|
||||
|
||||
所有 IES 私有计时与计数集中在 **`s_bs_task_ctrl.s_ies_data`**(`v_ies_reset_ctx` / `memset` 时一并清零)。
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
|------|------|------|
|
||||
| **`u8_bs_flag`** | U8 | PlatComm 槽位 **0/1**;对应 **`E_BS_GET_SYS_DATA_PLAT_EN`** 与桩编号 **`E_STR_PILE_NO`** 的 `u8_dataPara` |
|
||||
| **`u8_register_out_cnt`** | U8 | 已发 **0x01**、仍未收到 **0x02** 的累计次数;超过 **`BS_IES_LOGIN_RETRY_MAX`** 触发 **`v_set_bs_renew`** |
|
||||
| **`u8_heart_outTime_cnt`** | U8 | 已发 **0x03**、仍未收到 **0x04** 的连续次数;由 **`v_ies_heart_req`** 递增、**`v_ies_heart_ack`** 清零 |
|
||||
| **`u8_register_flag`** | U8 | **1** = 平台登录成功(**0x02** 且结果字节为 0);为 1 时 **`v_ies_logo_on`** 返回 1 |
|
||||
| **`u8_syn_time_flag`** | U8 | **1** = 已完成平台校时(登录成功或收到 **0x76**) |
|
||||
| **`u8_ask_fee_flag`** | U8 | **1** = 已获取/下发费率,不再周期发 **0x05** |
|
||||
| **`u16_msgSerialNumber`** | U16 | 自发帧序列号(组帧头时 `s16_serialNum<=0` 自增) |
|
||||
| **`ulong_restart_timer`** | ULONG | 最近一次 TCP 建链成功时刻(`get_current_seconds`) |
|
||||
| **`s_timerCnt[]`** | 数组 | 与 OCPP **`OCPP_DATA_CTRL.s_timerCnt`** 同结构:`u16_timerCnt` 每秒 +1,`u16_timeNUM` 为周期(秒) |
|
||||
| **`ack_ctrl[]`** | 指针数组 | 平台下行需应答帧的异步队列(**`IES_DATA_RECV_ACK`**,同 OCPP **`ack_ctrl`**);`v_ies_send_ctrl` 每轮上送一条 |
|
||||
|
||||
### 4.1 `IES_SEND_TIMER` 与默认周期(`v_ies_init_timer_period`)
|
||||
|
||||
| 枚举 | 默认 `u16_timeNUM`(秒) | 说明 |
|
||||
|------|-------------------------|------|
|
||||
| **`IES_TIMER_LOGIN`** | `BS_IES_LOGIN_INTERVAL_S`(20) | `v_ies_logo_on` 发 0x01 |
|
||||
| **`IES_TIMER_HEART`** | `BS_IES_HEART_INTERVAL_S`(20) | `v_ies_send_ctrl` 发 0x03 |
|
||||
| **`IES_TIMER_FEE_MODULE`** | 30 | 登录后周期发 **0x05** 直至 **0x06** 或 **0x7D** 写入费率 |
|
||||
| **`IES_TIMER_FREE_DATA_GUN1/2`** | 180 | 空闲实时 0x13 |
|
||||
| **`IES_TIMER_CHG_DATA_GUN1/2`** | 30 | 充电实时 0x15 |
|
||||
| **`IES_TIMER_METER_GUN1/2`** | 3600 | 电表 |
|
||||
| **`IES_TIMER_TRADE_GUN1`** | 10 | 账单 |
|
||||
|
||||
内部接口(`bs_ies_ctrl.c`,与 OCPP 同名语义):**`v_ies_timer_add()`** 每秒递增;**`u8_ies_timer_ctrl(cmd, GET_FLAG/CLEAR_FALG)`** 查询到时或清零。
|
||||
|
||||
---
|
||||
|
||||
## 5. 登录与心跳流程
|
||||
|
||||
```text
|
||||
[TCP 未连接]
|
||||
v_ies_tcp_connect → u8_tcp_connect
|
||||
(清 u8_register_flag;CLEAR_FALG LOGIN/HEART 定时器)
|
||||
|
||||
[TCP 已连接,未登录]
|
||||
v_ies_logo_on → v_ies_timer_add
|
||||
IES_TIMER_LOGIN 到时 → v_ies_register_req
|
||||
收到 0x02 且 [BODY]==0 → v_ies_on_login_ok(含首包心跳定时到点)
|
||||
超时次数 > BS_IES_LOGIN_RETRY_MAX → v_set_bs_renew + v_ies_reset_ctx
|
||||
|
||||
[已登录,u8_log_on_flag==1]
|
||||
v_ies_send_ctrl → v_ies_timer_add
|
||||
IES_TIMER_HEART 到时 → v_ies_heart_req
|
||||
u8_ask_fee_flag==0 且 IES_TIMER_FEE_MODULE 到时 → v_ies_fee_req
|
||||
v_ies_recv_data → 0x04 → v_ies_heart_ack
|
||||
→ 0x76 → v_ies_set_clk_sync(v_bs_set_time,ack 挂入 ack_ctrl)
|
||||
→ 0x7D → 解析费率 → v_bs_save_fee_data,0x7E 挂入 ack_ctrl
|
||||
|
||||
v_ies_send_ctrl → ack_ctrl 非空 → u8_ies_send_ack → v_ies_free_ack_data
|
||||
|
||||
v_ies_private_logic → v_ies_timer_add
|
||||
u8_heart_outTime_cnt > BS_IES_HEART_MISS_MAX → v_set_bs_renew
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 0x68 帧与已实现命令
|
||||
|
||||
帧域下标见 **`bs_ies_ctrl.h`** 中 **`IES_FRM_OFF_*`**(与 A 工程 `u8_recvBuf[21/22/23]` 一致)。
|
||||
|
||||
| 类型码 | 方向 | 函数 |
|
||||
|--------|------|------|
|
||||
| **0x01** | 上行 | `v_ies_register_req` |
|
||||
| **0x02** | 下行 | `v_ies_register_ack` |
|
||||
| **0x03** | 上行 | `v_ies_heart_req` |
|
||||
| **0x04** | 下行 | `v_ies_heart_ack` |
|
||||
| **0x05** | 上行 | `v_ies_fee_req` |
|
||||
| **0x06** | 下行 | `v_ies_fee_module_ack` → `v_bs_save_fee_data` |
|
||||
| **0x07** | 上行 | `u8_ies_send_ack` 二维码下发应答 |
|
||||
| **0x08** | 下行 | `v_ies_qr_dis` → `QRDisplay[]` + Flash |
|
||||
| **0x09** | 上行 | `u8_ies_send_ack` 二维码召唤应答 |
|
||||
| **0x0A** | 下行 | `v_ies_qr_call` |
|
||||
| **0x75** | 上行 | `v_ies_clk_sync_ack` |
|
||||
| **0x76** | 下行 | `v_ies_set_clk_sync` → `v_bs_set_time` |
|
||||
| **0x7D** | 下行 | `v_ies_fee_cfg_req` |
|
||||
| **0x7E** | 上行 | `v_ies_fee_cfg_ack` |
|
||||
| 其它 | — | `v_ies_rx_cmd_handle` 打日志占位 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 配置与联调要点
|
||||
|
||||
| 配置项 | 接口 | 说明 |
|
||||
|--------|------|------|
|
||||
| 平台使能 | **`E_BS_GET_SYS_DATA_PLAT_EN`** | 槽位 0/1 与 **`MasterFuncArray`** 下标一致 |
|
||||
| 联网方式 | **`E_BS_GET_SYS_DATA_NET_TYPE`** | 0=4G,1=网口;IES 与 OCPP 共用同一套 connect |
|
||||
| 桩编号 | **`E_STR_PILE_NO`** + **`u8_bs_flag`** | 双平台时平台 A/B 各一套桩号 |
|
||||
| 枪数 / 故障 | **`E_BS_GET_SYS_DATA_GUN_NUM`**、**`NOW_FAULT_ALARM_NUM`** | 登录帧、心跳帧组帧 |
|
||||
|
||||
**双平台 + 单 4G TCP**:产品与 **`PLAT_EN`** 需约定主平台;详见移植方案「风险」章节。
|
||||
|
||||
**FTP 升级中**:`v_ies_recv_data` 在 **`u8_updata_flag!=0` 且 4G** 时跳过收包(与 OCPP 策略一致,见联网任务文档 §4.4)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 日志
|
||||
|
||||
| 宏 | 定义位置 | 说明 |
|
||||
|----|----------|------|
|
||||
| **`IES_PRINT_FLAG`** | `plat_comm_task.h` | 值 **0xFE**,与 **`OCPP_PRINT_FLAG`(0xFF)** 同类:不受 `u8_task_log_flag` 门控时默认输出 |
|
||||
| **`IES_PRINTF_LOG`** | `bs_ies_ctrl.h` | `MYLOG_MSG(IES_PRINT_FLAG, …)`;**ieszt 目录内请用此宏,勿用 `Plat_Comm_LOG`** |
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `IES: init slot=` | `v_ies_init_data` |
|
||||
| `IES: login req pile=` | 发出 0x01 |
|
||||
| `IES: login ok` / `login fail` | 0x02 解析结果 |
|
||||
| `IES: heartbeat tx cnt=` | 发出 0x03(含未应答计数) |
|
||||
| `IES: heartbeat ack` | 收到 0x04 |
|
||||
| `IES: clk sync` / `clk sync ack` | 0x76 校时 / 0x75 应答 |
|
||||
| `IES: fee module req` / `fee model saved` | 0x05 请求 / 0x06·0x7D 落盘 |
|
||||
| `IES: login timeout, renew` / `heartbeat miss, renew` | 触发 **`v_set_bs_renew`** |
|
||||
|
||||
---
|
||||
|
||||
## 9. 关联文档
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| **[平台联网任务功能说明.md](../平台联网任务功能说明.md)** | PlatComm 任务、双槽状态机、FTP 分时、**`v_set_bs_renew`** |
|
||||
| **[IES积成平台移植方案.md](../IES积成平台移植方案.md)** | A→E 分阶段移植清单 |
|
||||
| **`4g_module/4G模块AT指令配置功能说明.md`** | 4G TCP/FTP 与 PlatComm 分时 |
|
||||
| **`impl/bs_public_impl.h`** | **`E_STR_*`** / **`E_BS_GET_SYS_DATA_*`** 枚举 |
|
||||
|
||||
---
|
||||
|
||||
*文档版本:与 `plat_comm/ieszt` 源码同步;结构体字段变更时请同步更新 §4。*
|
||||
@@ -0,0 +1,43 @@
|
||||
# ieszt — 积成 IES 运营平台协议
|
||||
|
||||
规约依据:积成充电桩网络通信协议(V3.5 / V3.7 / V3.8,由 `BS_IES_TYPE` 选择)
|
||||
|
||||
## 编译开关
|
||||
|
||||
`public_define.h` 中设置:
|
||||
|
||||
```c
|
||||
#define BS_IES_EN (1) // 使能积成 IES 运营协议(CCU621 默认)
|
||||
```
|
||||
|
||||
协议版本在 `bs_ies_ctrl.h` 中配置:
|
||||
|
||||
```c
|
||||
#define BS_IES_TYPE (2) // 1=山高3.8,2=积成3.7(默认),3=积成3.5
|
||||
```
|
||||
|
||||
可与 `BS_IESYW_EN` 并存:平台 A 走 IES 运营(本目录),平台 B 走远程运维(`iesyw/`),共用 4G 模组 socketA/B 分发。
|
||||
|
||||
## 文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
| --- | --- |
|
||||
| `bs_ies_ctrl.c/h` | 任务回调、登录/心跳、周期上送调度、接收解帧 |
|
||||
| `bs_ies_interface.c/h` | 帧格式(0x68 起始)、帧类型码、组包/解包 |
|
||||
|
||||
## 主要帧类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
| --- | --- |
|
||||
| 0x01/0x02 | 登录/应答 |
|
||||
| 0x03/0x04 | 心跳/应答 |
|
||||
| 0x05/0x06 | 计费模型请求/应答 |
|
||||
| 0x07~0x0A | 二维码下发/召唤 |
|
||||
| 0x11/0x13 | 充电中/非充电实时数据 |
|
||||
| 0x15 | 故障告警 |
|
||||
| 0x31 | 表底值上送 |
|
||||
| 0x41~0x48 | 启停/账单结算 |
|
||||
| 0x76 | 对时 |
|
||||
| 0x7D/0x7E | 费率相关 |
|
||||
|
||||
详细说明见 [plat_comm模块说明.md](../plat_comm模块说明.md)。
|
||||
@@ -0,0 +1,709 @@
|
||||
|
||||
#include "plat_comm/ieszt/bs_ies_ctrl.h"
|
||||
#include "plat_comm/ieszt/bs_ies_interface.h"
|
||||
#include "plat_comm/impl/bs_connect_impl.h"
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
#if WH_GM5_EN
|
||||
#include "plat_comm/4g_module/bs_wh_gm5.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if (BS_IES_EN)
|
||||
|
||||
#define BS_TIME_1S 10 //1秒 发送周期1秒计数
|
||||
|
||||
|
||||
S_BS_IES_CTRL_DATA *private_ies_data;
|
||||
|
||||
#endif
|
||||
|
||||
#if (BS_IES_EN) //内部控制函数
|
||||
void v_ies_send(U8_T *u8_sendBuf, U32_T sendLen)
|
||||
{
|
||||
u8_sendBuf[sendLen] = u16_crc_checksum(&u8_sendBuf[3], sendLen-3); //和校验
|
||||
sendLen ++;
|
||||
|
||||
//更新帧长度域
|
||||
u8_sendBuf[1] = (sendLen-4)>>8;
|
||||
u8_sendBuf[2] = sendLen-4;
|
||||
|
||||
v_bs_send(private_ies_data->id,u8_sendBuf,sendLen);
|
||||
}
|
||||
//发送周期计数累加 1秒加一
|
||||
static void v_ies_timer_add()
|
||||
{
|
||||
U8_T i;
|
||||
static ULONG time_old = 0;
|
||||
ULONG time_ing = get_current_seconds(); //获取当前秒数
|
||||
if(time_old != time_ing)
|
||||
{
|
||||
time_old = time_ing;
|
||||
for (i = 0; i < IES_TIMERCNT_NUM; i++)
|
||||
{
|
||||
if(private_ies_data->s_timerCnt[i].u16_timerCnt < private_ies_data->s_timerCnt[i].u16_timeNUM)
|
||||
{
|
||||
private_ies_data->s_timerCnt[i].u16_timerCnt ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//获取发送周期计数到时标志
|
||||
static U8_T u8_ies_timer_ctrl(IES_SEND_TIMER cmd ,FLAG_CTLR flag)
|
||||
{
|
||||
U8_T ret = 0;
|
||||
if(flag == CLEAR_FALG) //清空对应计时计数
|
||||
{
|
||||
private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt = 0;
|
||||
ret = 1;
|
||||
}
|
||||
else if(private_ies_data->s_timerCnt[(U8_T)cmd].u16_timeNUM != 0 &&
|
||||
private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt >= private_ies_data->s_timerCnt[(U8_T)cmd].u16_timeNUM)
|
||||
{
|
||||
private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt = 0;
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (BS_IES_EN) //发送相关
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 故障信息变化检查
|
||||
*
|
||||
*
|
||||
* @return U8_T - 0未变化 1变化
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-20
|
||||
* ************************************************************************
|
||||
*/
|
||||
static U8_T u8_cheak_fault()
|
||||
{
|
||||
static U8_T u8_old_fault_num=0;
|
||||
U8_T u8_fault_num,ret = 0;
|
||||
|
||||
u8_fault_num = u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_NUM,0) +
|
||||
u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_NUM,1); //获取故障 告警数量
|
||||
|
||||
if(u8_fault_num != u8_old_fault_num)
|
||||
{
|
||||
u8_old_fault_num = u8_fault_num;
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 抢连接变化检查
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-N
|
||||
*
|
||||
* @return U8_T - 0未变化 1已经变化
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-20
|
||||
* ************************************************************************
|
||||
*/
|
||||
static U8_T u8_cheak_gunlink(U8_T u8_gunNo)
|
||||
{
|
||||
static U8_T u8_old_gunLink[GUN_MAX_CNT] = {0};
|
||||
U8_T u8_gunLink = u8_ies_get_gunLink_state(u8_gunNo); //获取枪连接状态 0未连接 1已连接
|
||||
//u8_gunLink = (u8_gunLink == 0 ? 0 : 1);
|
||||
|
||||
if(u8_gunLink != u8_old_gunLink[u8_gunNo])
|
||||
{
|
||||
//printf("%d枪变位,原状态:%d,现状态:%d\n",u8_gunNo,u8_old_gunLink[u8_gunNo],u8_gunLink);
|
||||
u8_old_gunLink[u8_gunNo] = u8_gunLink;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 抢状态变化检查
|
||||
*
|
||||
* @param[in] u8_gunNo - 抢号 0-N
|
||||
*
|
||||
* @return U8_T - 0未变化 1已经变化
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-20
|
||||
* ************************************************************************
|
||||
*/
|
||||
static U8_T u8_cheak_gunState(U8_T u8_gunNo)
|
||||
{
|
||||
static U8_T u8_old_gunState[GUN_MAX_CNT] = {0};
|
||||
U8_T u8_gunState = u8_ies_get_gun_state(u8_gunNo); //获取枪状态 0未连接 1已连接
|
||||
//u8_gunLink = (u8_gunLink == 0 ? 0 : 1);
|
||||
|
||||
if(u8_gunState != u8_old_gunState[u8_gunNo])
|
||||
{
|
||||
//printf("%d枪变位,原状态:%d,现状态:%d\n",u8_gunNo,u8_old_gunState[u8_gunNo],u8_gunState);
|
||||
u8_old_gunState[u8_gunNo] = u8_gunState;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//发送控制
|
||||
static void v_ies_send_mag_ctrl()
|
||||
{
|
||||
S_BS_CTRL_DATA *s_bs_ctrl = &s_bs_task_ctrl.s_bs_ctrl[private_ies_data->id]; //获取控制数据
|
||||
U8_T i,u8_data; //临时变量 使用前注意赋值
|
||||
U8_T u8_gunCnt = s_bs_task_ctrl.u8_gunCnt;
|
||||
U8_T u8_get_flow_state[GUN_MAX_CNT];
|
||||
static U8_T fee_flag = 0; //连接平台后直接请求一次计费模型标志
|
||||
//检查心跳周期是否到位
|
||||
if(u8_ies_timer_ctrl(IES_TIMER_Heartbeat,GET_FLAG) == 1)
|
||||
{
|
||||
v_ies_heart();
|
||||
return;
|
||||
}
|
||||
//电表底值上送
|
||||
for(i=0;i<u8_gunCnt;i++)
|
||||
{
|
||||
if(u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_MeterValues_GUN + i), GET_FLAG) == 1)
|
||||
{
|
||||
v_ies_meter_data(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//主动上送
|
||||
|
||||
//桩主动请求充电
|
||||
for(i=0;i<u8_gunCnt;i++)
|
||||
{
|
||||
if(private_ies_data->s_ctrl_flag[i].u8_charge_req == 1)
|
||||
{
|
||||
v_ies_start_req(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//请求费率
|
||||
if(fee_flag == 0 ||(private_ies_data->txFlag_askFeeMode == 0 &&
|
||||
u8_ies_timer_ctrl(IES_TIMER_FEE_REQ,GET_FLAG) == 1))
|
||||
{
|
||||
v_ies_fee_req();
|
||||
fee_flag = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
//订单上送
|
||||
for(i=0; i<u8_gunCnt; i++)
|
||||
{
|
||||
u8_data = 0;
|
||||
if(s_bs_ctrl->s_gundata[i].txFlag_tradUpload == 1)
|
||||
{
|
||||
if( s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt < 6) //充电中 只发6次
|
||||
u8_data = 1;
|
||||
}
|
||||
else
|
||||
s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt = 0;
|
||||
|
||||
if(u8_data == 1 && u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_TRADE_INFO + i), GET_FLAG) == 1)
|
||||
{
|
||||
v_ies_bill_pack(i);
|
||||
s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt ++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//故障状态上送-有故障变化时上送,无故障变化时,3分钟上送一次
|
||||
u8_data = u8_cheak_fault();
|
||||
if(u8_data == 1 || u8_ies_timer_ctrl(IES_TIMER_FAULT_GUN ,GET_FLAG) == 1)
|
||||
{
|
||||
u8_ies_timer_ctrl(IES_TIMER_FAULT_GUN ,CLEAR_FALG); //清空计数
|
||||
v_ies_fault_V35(i);
|
||||
return;
|
||||
}
|
||||
|
||||
//充电/空闲实时信息上报
|
||||
for(i=0; i<u8_gunCnt; i++)
|
||||
{
|
||||
u8_get_flow_state[i] = u8_bs_get_flow_state(i); //获取终端状态 0 空闲 1开始充电 2充电中
|
||||
if(u8_get_flow_state[i] == 2) //充电中
|
||||
{
|
||||
//当状态由变化时 前3帧按10秒发送--u8_real_state_cnt 0x00-0x0F 充电中计数 0x10-0x1F 非充电中计数
|
||||
private_ies_data->s_ies_gun[i].u8_real_state_cnt &= 0x0F;
|
||||
|
||||
if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 3)
|
||||
{
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 10; //前3帧 10秒周期上送
|
||||
}
|
||||
else
|
||||
{
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = private_ies_data->u16_work_info_period; //上送周期等于充电数据上送周期设置值
|
||||
}
|
||||
|
||||
if(u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_REAL_WORK_GUN + i), GET_FLAG) == 1)
|
||||
{
|
||||
// printf("%d枪,充电中,u8_real_state_cnt=%02x\n",i,private_ies_data->s_ies_gun[i].u8_real_state_cnt);
|
||||
v_ies_rtd_chging(i);
|
||||
if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 3)
|
||||
{
|
||||
private_ies_data->s_ies_gun[i].u8_real_state_cnt ++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//当状态由变化时 前3帧按10秒发送--u8_real_state_cnt 0x00-0x0F 充电中计数 0x10-0x1F 非充电中计数
|
||||
private_ies_data->s_ies_gun[i].u8_real_state_cnt &= 0xF0; //清除低位计数值
|
||||
if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 0x30)
|
||||
{
|
||||
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 3; //前3帧 3秒周期上送
|
||||
}
|
||||
else
|
||||
{
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = private_ies_data->u16_free_info_period; //上送周期等于充电数据上送周期设置值
|
||||
}
|
||||
|
||||
if(u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_REAL_WORK_GUN + i), GET_FLAG) == 1 //|| u8_cheak_gunlink(i)
|
||||
|| u8_cheak_gunState(i)) //延时到时间上送、枪状态改变上送
|
||||
{
|
||||
//printf("%d枪,非充电中,u8_real_state_cnt=%02x\n",i,private_ies_data->s_ies_gun[i].u8_real_state_cnt);
|
||||
v_ies_rtd_free(i);
|
||||
if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 0x30)
|
||||
{
|
||||
private_ies_data->s_ies_gun[i].u8_real_state_cnt += 0x10;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (BS_IES_EN) //接收
|
||||
|
||||
static void v_bs_rx_cmd_hanle(U8_T *u8_recvBuf) //具体报文解帧
|
||||
{
|
||||
U8_T u8_cmd = u8_recvBuf[22];
|
||||
|
||||
|
||||
switch(u8_cmd)
|
||||
{
|
||||
case NT_LOGIN_ACK: //0x02 //登录注册应答
|
||||
{
|
||||
if(private_ies_data->u8_register_flag == 0)
|
||||
{
|
||||
v_ies_register_ack(u8_recvBuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NT_TICK_ACK: //0x04 //心跳应答
|
||||
{
|
||||
v_ies_heart_ack();
|
||||
break;
|
||||
}
|
||||
|
||||
case NT_FEE_MODULE_ACK: //0x06 //计费模型应答
|
||||
{
|
||||
v_ies_fee_ack(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_MDU_DATA_ACK: //0x17 //充电桩整流模块数据应答
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
case NT_START_CHG_ACK: //0x42 //平台确认桩主动申请启动充电
|
||||
{
|
||||
v_ies_rx_start(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_REMOTE_START_REQ: //0x44 //平台远程控制启机
|
||||
{
|
||||
if(private_ies_data->txFlag_askFeeMode == 1) //未收到计费模型,不启动充电
|
||||
{
|
||||
Plat_Comm_LOG("private_ies_data->txFlag_askFeeMode=%d\n",private_ies_data->txFlag_askFeeMode);
|
||||
v_ies_rmt_start(u8_recvBuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NT_REMOTE_STOP_REQ: //0x46 //平台远程停机
|
||||
{
|
||||
v_ies_rmt_stop(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_BILL_UPLOAD_ACK: //0x48 //平台结算账单确认
|
||||
{
|
||||
v_ies_bill_upload_ack(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_USERACNTINFO_ACK: //0x4A //平台回复查询用户账单信息(未用)
|
||||
{
|
||||
//v_bs_user_acntInfo_ack(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_CARD_BILL_UPLOAD_ACK: //0x4C //平台刷卡结算订单应答
|
||||
{
|
||||
//v_bs_bill_card_ack(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_WORK_PARA_CFG_REQ: //0x72 //平台设置工作参数
|
||||
{
|
||||
//v_bs_set_wkPara(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_WORK_PARA_QUERY_REQ: //0x74 //平台查询工作参数
|
||||
{
|
||||
//v_bs_query_wkPara(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_CLK_SYNC_REQ: //0x76 //平台对时
|
||||
{
|
||||
v_ies_set_clk_sync(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_REMOTE_OPRT_REQ: //0x78 //平台远程操作
|
||||
{
|
||||
//v_bs_rmt_oprt(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_FEE_CFG_REQ: //0x7D //平台设置计费模型
|
||||
{
|
||||
v_ies_set_fee(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_UPDATE_AUTO: //0x90 //平台下发FTP升级命令
|
||||
{
|
||||
//v_ies_upgrade_data_auto(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
case NT_QR_REQ_CALL: //0x0A 平台召唤二维码
|
||||
{
|
||||
v_ies_qr_call(u8_recvBuf);
|
||||
|
||||
break;
|
||||
}
|
||||
case NT_QR_REQ_DIS: //0x08 平台下发二维码
|
||||
{
|
||||
Plat_Comm_LOG("Receive platform QR code command\n");
|
||||
// printf("收到平台下发二维码命令\n");
|
||||
// for(int i=0;i < 100;i++)
|
||||
// {
|
||||
// printf("%.2x ",u8_recvBuf[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
v_ies_qr_dis(u8_recvBuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// static void v_bs_unpack_handle_ieszt(U8_T *u8_recvBuf, U16_T u16_rcvLen) //解帧函数
|
||||
// {
|
||||
// U8_T u8_crc;
|
||||
// U16_T u16_rcvByteNum = 0; //解粘包指针
|
||||
// U16_T u16_oneFrmLen = 0; //一完整帧理论总长度
|
||||
// U16_T u16_dataLen = 0; //数据域长度
|
||||
// U8_T upackBuf[BS_RECV_BUF_LEN] = {0}; //待解析帧缓存
|
||||
|
||||
// while(u16_rcvLen >= 23) //帧头长度=23
|
||||
// {
|
||||
// if(u8_recvBuf[u16_rcvByteNum] != NT_HEADER) //找帧头
|
||||
// {
|
||||
// u16_rcvByteNum++;
|
||||
// u16_rcvLen--;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// u16_dataLen = (U16_T)((u8_recvBuf[u16_rcvByteNum+1]<<8) + u8_recvBuf[u16_rcvByteNum+2]);
|
||||
// if(u16_dataLen >= 256)
|
||||
// return;
|
||||
// u16_oneFrmLen = u16_dataLen + 4;
|
||||
|
||||
// if(u16_oneFrmLen <= u16_rcvLen) //构成完整帧
|
||||
// {
|
||||
// memcpy(&upackBuf[0], &u8_recvBuf[u16_rcvByteNum], u16_oneFrmLen);
|
||||
|
||||
// //和校验
|
||||
// u8_crc = u16_crc_checksum(&upackBuf[3], u16_oneFrmLen-4);
|
||||
// if(u8_crc == upackBuf[u16_oneFrmLen-1])
|
||||
// {
|
||||
// v_bs_rx_cmd_hanle(upackBuf); //和校验通过,解析数据
|
||||
// }
|
||||
|
||||
// u16_rcvByteNum += u16_oneFrmLen;
|
||||
// u16_rcvLen -= u16_oneFrmLen;
|
||||
// }
|
||||
// else
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
static void v_bs_unpack_handle_ieszt(U8_T *u8_recvBuf, U16_T u16_rcvLen) //解帧函数
|
||||
{
|
||||
U8_T u8_crc;
|
||||
U16_T u16_rcvByteNum = 0; //解粘包指针
|
||||
U16_T u16_oneFrmLen = 0; //一完整帧理论总长度
|
||||
U16_T u16_dataLen = 0; //数据域长度
|
||||
U8_T upackBuf[BS_RECV_BUF_LEN] = {0}; //待解析帧缓存
|
||||
|
||||
static U8_T u8_save_buf[BS_RECV_BUF_LEN*2] = {0};
|
||||
static U16_T u16_save_len = 0;
|
||||
|
||||
// 检查缓冲区是否溢出
|
||||
if(u16_rcvLen + u16_save_len >= BS_RECV_BUF_LEN*2)
|
||||
{
|
||||
// 移除前四分之一数据以腾出空间
|
||||
U16_T remove_len = u16_save_len / 4; // 计算要移除的长度
|
||||
U16_T remain_len = u16_save_len - remove_len; // 剩余数据长度
|
||||
|
||||
if(remain_len > 0) {
|
||||
// 将后面的数据向前移动,覆盖前四分之一
|
||||
memmove(u8_save_buf, u8_save_buf + remove_len, remain_len);
|
||||
}
|
||||
u16_save_len = remain_len; // 更新保存的数据长度
|
||||
}
|
||||
|
||||
// 将接收到的数据添加到缓存区中
|
||||
if(u16_rcvLen > 0) {
|
||||
memcpy(&u8_save_buf[u16_save_len], u8_recvBuf, u16_rcvLen);
|
||||
u16_save_len += u16_rcvLen;
|
||||
}
|
||||
|
||||
// 使用静态缓冲区数据进行解析
|
||||
U16_T process_len = u16_save_len; // 当前要处理的数据长度
|
||||
u16_rcvByteNum = 0; // 重置解析指针
|
||||
|
||||
while(process_len >= 23) //帧头长度=23
|
||||
{
|
||||
if(u8_save_buf[u16_rcvByteNum] != NT_HEADER) //找帧头
|
||||
{
|
||||
u16_rcvByteNum++;
|
||||
process_len--;
|
||||
continue;
|
||||
}
|
||||
|
||||
u16_dataLen = (U16_T)((u8_save_buf[u16_rcvByteNum+1]<<8) + u8_save_buf[u16_rcvByteNum+2]);
|
||||
if(u16_dataLen >= 256)
|
||||
{
|
||||
u16_rcvByteNum++;
|
||||
process_len--;
|
||||
continue; // 数据长度异常,跳过这个帧头
|
||||
}
|
||||
|
||||
u16_oneFrmLen = u16_dataLen + 4;
|
||||
|
||||
if(u16_oneFrmLen <= process_len) //构成完整帧
|
||||
{
|
||||
memcpy(&upackBuf[0], &u8_save_buf[u16_rcvByteNum], u16_oneFrmLen);
|
||||
|
||||
//和校验
|
||||
u8_crc = u16_crc_checksum(&upackBuf[3], u16_oneFrmLen-4);
|
||||
if(u8_crc == upackBuf[u16_oneFrmLen-1])
|
||||
{
|
||||
v_bs_rx_cmd_hanle(upackBuf); //和校验通过,解析数据
|
||||
}
|
||||
|
||||
u16_rcvByteNum += u16_oneFrmLen;
|
||||
process_len -= u16_oneFrmLen;
|
||||
}
|
||||
else
|
||||
break; // 数据不完整,等待下次接收更多数据
|
||||
}
|
||||
|
||||
// 将处理完的数据从静态缓冲区中移除
|
||||
if(u16_rcvByteNum > 0) // 有处理完的数据需要移除
|
||||
{
|
||||
// 计算剩余未处理数据的长度
|
||||
U16_T remaining_len = u16_save_len - u16_rcvByteNum;
|
||||
|
||||
if(remaining_len > 0)
|
||||
{
|
||||
// 将未处理的数据向前移动,覆盖已处理的数据
|
||||
memmove(u8_save_buf, u8_save_buf + u16_rcvByteNum, remaining_len);
|
||||
}
|
||||
|
||||
// 更新静态缓冲区的数据长度
|
||||
u16_save_len = remaining_len;
|
||||
|
||||
// 清空移动后空出的部分(可选,为了调试清晰)
|
||||
if(remaining_len < BS_RECV_BUF_LEN*2) {
|
||||
memset(u8_save_buf + remaining_len, 0, BS_RECV_BUF_LEN*2 - remaining_len);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (BS_IES_EN) //外部控制使用
|
||||
/****************************************************
|
||||
*函数名称: v_ies_init_data()
|
||||
*功能说明: 程序运行前 参数初始化
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数: 0失败 1成功
|
||||
****************************************************/
|
||||
void v_ies_init_data(U8_T bs_flag)
|
||||
{
|
||||
U8_T i;
|
||||
private_ies_data = &s_bs_task_ctrl.s_ies_data; //指针数据初始化
|
||||
|
||||
memset(private_ies_data,0,sizeof(S_BS_IES_CTRL_DATA));
|
||||
|
||||
private_ies_data->id = bs_flag; //平台位置
|
||||
|
||||
|
||||
// if (bs_flag == 0U) {
|
||||
// s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType =
|
||||
// (u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NET_TYPE,bs_flag) == 1U) ? 0U : 1U;
|
||||
// } else {
|
||||
// s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType =
|
||||
// (u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NET_TYPE,bs_flag) == 1U) ? 0U : 1U;
|
||||
// }
|
||||
s_bs_task_ctrl.s_bs_ctrl[bs_flag].fd = -1;
|
||||
s_bs_task_ctrl.s_bs_ctrl[bs_flag].ulong_restart_timer = get_current_seconds();
|
||||
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType == 0) //4G
|
||||
{
|
||||
v_bs_4G_data_init();
|
||||
}
|
||||
|
||||
|
||||
//周期发送控制初始化
|
||||
private_ies_data->u16_work_info_period = 30; //工作实时状态 上送周期
|
||||
private_ies_data->u16_free_info_period = 120; //空闲实时状态 上送周期
|
||||
|
||||
//周期发送控制初始化
|
||||
private_ies_data->s_timerCnt[IES_TIMER_Register].u16_timeNUM = 20; //秒 登录帧上送周期
|
||||
private_ies_data->s_timerCnt[IES_TIMER_Register].u16_timerCnt = 20; //确保立马执行登录帧
|
||||
private_ies_data->s_timerCnt[IES_TIMER_Heartbeat].u16_timeNUM = 15; //秒 心跳帧上送周期
|
||||
|
||||
private_ies_data->s_timerCnt[IES_TIMER_FEE_REQ].u16_timeNUM = 60; //秒 费率请求
|
||||
private_ies_data->s_timerCnt[IES_TIMER_FAULT_GUN].u16_timeNUM = 60*3; //秒 故障信息上报
|
||||
|
||||
for ( i = 0; i < GUN_MAX_CNT; i++)
|
||||
{
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 30; //秒 工作实时状态
|
||||
private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timerCnt = 0xFFFF;
|
||||
private_ies_data->s_timerCnt[IES_TIMER_TRADE_INFO + i].u16_timeNUM = 30; //秒 订单信息上送
|
||||
private_ies_data->s_timerCnt[IES_TIMER_MeterValues_GUN + i].u16_timeNUM = 60*30; //秒 电表低值上送
|
||||
}
|
||||
}
|
||||
/****************************************************
|
||||
*函数名称: v_ies_tcp_connect()
|
||||
*功能说明: TCP连接功能实现 - 配置4G模块
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数: 0失败 1成功
|
||||
****************************************************/
|
||||
U8_T v_ies_tcp_connect(void)
|
||||
{
|
||||
return u8_tcp_connect(private_ies_data->id);
|
||||
}
|
||||
/****************************************************
|
||||
*函数名称: v_ies_logo_on()
|
||||
*功能说明: 平台桩信息登录 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数: 0失败 1成功
|
||||
****************************************************/
|
||||
U8_T v_ies_logo_on(void)
|
||||
{
|
||||
v_ies_timer_add(); //计时计数+1
|
||||
|
||||
|
||||
//检查登录周期是否到位
|
||||
if(u8_ies_timer_ctrl(IES_TIMER_Register,GET_FLAG) == 1)
|
||||
{
|
||||
|
||||
v_ies_register_req(); //登录帧
|
||||
Plat_Comm_LOG("Send login platform packet, u8_register_flag:%d\n",private_ies_data->u8_register_flag);
|
||||
|
||||
if(private_ies_data->u8_register_out_cnt++ > 6) //两分钟登录帧未连接成功
|
||||
{
|
||||
v_set_bs_renew(private_ies_data->id); //设置主函数 联网重连
|
||||
v_ies_init_data(private_ies_data->id); //设置 子函数参数重置 重走初始化过程
|
||||
Plat_Comm_LOG("Login frame send timeout\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(private_ies_data->u8_register_flag == 1)
|
||||
{
|
||||
private_ies_data->u8_register_out_cnt = 0;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
/****************************************************
|
||||
*函数名称: v_ies_recv_data()
|
||||
*功能说明: 数据接收处理 - 控制任务周期调用 使用网口或者4G非分包功能时使用,需内部实现从串口获取数据
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ies_recv_data(void)
|
||||
{
|
||||
S16_T s16_recvLen = 0;
|
||||
U8_T u8_recvBuf[BS_RECV_BUF_LEN] = {0};
|
||||
|
||||
s16_recvLen = u16_bs_recv(private_ies_data->id,u8_recvBuf,BS_RECV_BUF_LEN);
|
||||
|
||||
if(s16_recvLen > 0)
|
||||
{
|
||||
v_bs_unpack_handle_ieszt(&u8_recvBuf[0], s16_recvLen);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 控制任务接收解析完数据后调用 4G 模块多包接收时使用-当4G使用多socket数据接收时,串口数据获取和分包功能由外部实现,函数传入参数为本平台的接收数据
|
||||
*
|
||||
* @param[in] id - 平台地址
|
||||
* @param[in] recvBuf - 接收数据
|
||||
* @param[in] u16_len - 接收数据长度
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-02
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_ies_recv_unpack(U8_T id, U8_T *recvBuf,U16_T u16_len)
|
||||
{
|
||||
v_bs_unpack_handle_ieszt(recvBuf,u16_len);
|
||||
}
|
||||
/****************************************************
|
||||
*函数名称: v_ies_send_ctrl()
|
||||
*功能说明: 发送控制函数 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ies_send_ctrl(void)
|
||||
{
|
||||
v_ies_timer_add(); //计时计数+1
|
||||
|
||||
v_ies_send_mag_ctrl(); //发送控制
|
||||
}
|
||||
/****************************************************
|
||||
*函数名称: v_ies_private_logic()
|
||||
*功能说明: 私有协议逻辑控制 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ies_private_logic(U8_T bs_flag)
|
||||
{
|
||||
if(private_ies_data->u8_heart_outTime_cnt > 3) //发送三次心跳 未回复 则进行重连
|
||||
{
|
||||
v_set_bs_renew(private_ies_data->id); //设置主函数 联网重连
|
||||
v_ies_init_data(private_ies_data->id); //设置 子函数参数重置 重走初始化过程
|
||||
//轻质化群充测试过程中发现与平台连接超时后不重连
|
||||
//检查发现iotx_linkkit_ctx_t结构体中is_opened,is_connected未复位,原因未知,为确保现场使用,此地暂时强制复位
|
||||
//v_connected_resset();
|
||||
Plat_Comm_LOG("Timeout reconnect\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file bs_ies_ctrl.h
|
||||
* @brief 积成 IES 平台协议控制层
|
||||
*
|
||||
* 定义 IES 协议私有数据结构、周期上送计时器及任务回调声明。
|
||||
* 协议版本由 BS_IES_TYPE 选择(默认积成 3.7)。
|
||||
*/
|
||||
#ifndef BS_IES_CTRL_H
|
||||
#define BS_IES_CTRL_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#if (BS_IES_EN)
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
#define IES_TIMERCNT_NUM (4 + 3*GUN_MAX_CNT) //计时计数
|
||||
#define IES_ACK_STRUCT_NUM 5 //应答结构体数量
|
||||
|
||||
/*************************************
|
||||
* 宏名称: BS_IES_TYPE
|
||||
* 宏说明: 积成平台协议版本
|
||||
* 设置说明:未定义:报错
|
||||
* 1 山高平台 3.8 2积成 3.7 3积成 3.5
|
||||
*
|
||||
**************************************/
|
||||
#define BS_IES_TYPE 2
|
||||
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef enum{
|
||||
IES_TIMER_Register = 0, //登录帧 0
|
||||
IES_TIMER_Heartbeat, //心跳 1
|
||||
IES_TIMER_FEE_REQ, //费率请求 2
|
||||
IES_TIMER_FAULT_GUN, //充电故障告警上送3
|
||||
IES_TIMER_REAL_WORK_GUN, //工作实时状态 数量跟随枪数量 - 包含工作状态和空闲状态两种 4
|
||||
//IES_TIMER_FAULT_GUN = IES_TIMER_REAL_WORK_GUN+GUN_MAX_CNT, //充电故障告警上送, 4+20 24
|
||||
IES_TIMER_MeterValues_GUN = IES_TIMER_REAL_WORK_GUN+GUN_MAX_CNT, //表底值上送, 25+20 45
|
||||
IES_TIMER_TRADE_INFO = IES_TIMER_MeterValues_GUN+GUN_MAX_CNT, //订单信息上送 数量跟随枪数量 46+20 66
|
||||
}IES_SEND_TIMER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T id; //平台位置 平台1还是平台2
|
||||
U8_T u8_register_out_cnt; //登录帧超时计数
|
||||
U8_T u8_heart_outTime_cnt; //心跳超时计时
|
||||
U8_T u8_register_flag; //是否已注册/是否联网.0否1是
|
||||
|
||||
U8_T txFlag_askFeeMode; //请求费率
|
||||
U8_T u8_time_flag; //校时标志
|
||||
U8_T u8_heart_outTime; //心跳超时计时--
|
||||
|
||||
U16_T u16_msgSerialNumber; //帧序号
|
||||
U16_T u16_work_info_period; //工作实时状态 上送周期
|
||||
U16_T u16_free_info_period; //空闲实时状态 上送周期
|
||||
|
||||
struct
|
||||
{
|
||||
U8_T u8_MeterValues_type; //数据上送标志 0无动作 1空闲数据上送 2充电中数据上送 3交易开始数据上送 4交易停止数据上送
|
||||
U8_T u8_charge_req; //充电请求标志 0 不请求充电 1 请求充电
|
||||
}s_ctrl_flag[GUN_MAX_CNT]; //数据控制标志
|
||||
|
||||
struct
|
||||
{
|
||||
U8_T u8_real_state_cnt; //充电实时状态 上送计数
|
||||
U16_T u16_charge_cnt; //充电次数
|
||||
U32_T u32_charge_time; //充电时间
|
||||
}s_ies_gun[GUN_MAX_CNT];
|
||||
|
||||
struct
|
||||
{
|
||||
U16_T u16_timerCnt; //周期发送计数
|
||||
U16_T u16_timeNUM ; //计数周期值
|
||||
}s_timerCnt[IES_TIMERCNT_NUM];
|
||||
|
||||
}S_BS_IES_CTRL_DATA;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void v_ies_init_data(U8_T bs_flag);
|
||||
U8_T v_ies_tcp_connect(void);
|
||||
U8_T v_ies_logo_on(void);
|
||||
void v_ies_recv_data(void);
|
||||
void v_ies_send_ctrl(void);
|
||||
void v_ies_private_logic(U8_T bs_flag);
|
||||
void v_ies_recv_unpack(U8_T id, U8_T *recvBuf,U16_T u16_len);
|
||||
void v_ies_send(U8_T *u8_sendBuf, U32_T sendLen);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* BS_IES_CTRL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,336 @@
|
||||
/**
|
||||
* @file bs_ies_interface.h
|
||||
* @brief 积成 IES 平台协议报文层
|
||||
*
|
||||
* 帧格式、帧类型码定义及组包/解包函数声明。
|
||||
* 帧头 0x68,设备地址 16 字节 ASCII 桩编号。
|
||||
*/
|
||||
#ifndef BS_IES_INTERFACE_H
|
||||
#define BS_IES_INTERFACE_H
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
|
||||
#include "main.h"
|
||||
#if (BS_IES_EN)
|
||||
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
/************积成通信协议帧结构*********************
|
||||
* 帧起始域 1字节 0x68
|
||||
* 帧长度域 2字节 从序列号域到帧数据域,高字节在前,低字节在后
|
||||
* 序列号域 1字节 数据包发送顺序号,从0开始,应答帧则同询问包
|
||||
* 设备地址 16字节 ASCII
|
||||
* 加密方式 1字节 0不加密,1AES加密,登录帧和确认帧加密方式为0
|
||||
* 数据对象 1字节 高2位:0直流1交流2交直流;低6位标识枪号,0代表充电机本体
|
||||
* 帧类型码 1字节
|
||||
* 帧数据域 N字节
|
||||
* 校验和 1字节 序列号域到帧数据域
|
||||
**************************************************/
|
||||
#define NT_PTL_VERSION 37 //通信协议版本V3.5
|
||||
|
||||
#define NT_HEADER 0x68 //帧头
|
||||
|
||||
//数据对象
|
||||
#if BS_TYPE_JIMO_EN //即墨平台使能,则桩类型为液冷充电桩。否则为直流桩
|
||||
#define NT_DC_PILE 3 //直流桩 //#define NT_AC_PILE 1 //交流桩 //直流桩 0积成平台 3即墨平台
|
||||
#else
|
||||
#define NT_DC_PILE 0 //直流桩 //#define NT_AC_PILE 1 //交流桩 //直流桩 0积成平台 3即墨平台
|
||||
#endif
|
||||
|
||||
#define NT_0B_MASK_H 0xC0 //高2位掩码
|
||||
#define NT_0B_MASK_L 0x3F //低6位掩码
|
||||
#define NT_ID_PILE 0 //充电桩本体
|
||||
#define NT_ID_GUN1 1 //1号充电枪
|
||||
#define NT_ID_GUN2 2 //2号充电枪
|
||||
|
||||
/************帧类型码宏定义************/
|
||||
//登录心跳帧类型码数据定义
|
||||
#define NT_LOGIN_REQ 0x01 //登录请求
|
||||
#define NT_LOGIN_ACK 0x02 //登录应答
|
||||
#define NT_TICK_REQ 0x03 //心跳请求
|
||||
#define NT_TICK_ACK 0x04 //心跳应答
|
||||
#define NT_FEE_MODULE_REQ 0x05 //计费模型请求
|
||||
#define NT_FEE_MODULE_ACK 0x06 //计费模型应答
|
||||
|
||||
#define NT_QR_REQ_DIS_ACK 0x07 //二维码下发应答
|
||||
#define NT_QR_REQ_DIS 0x08 //二维码下发
|
||||
#define NT_QR_REQ_CALL_ACK 0x09 //二维码召唤应答
|
||||
#define NT_QR_REQ_CALL 0x0A //二维码召唤帧
|
||||
|
||||
//实时数据帧类型码数据定义
|
||||
#define NT_GUN_DATA_CHGING 0x11 //直流充电枪充电中实时数据
|
||||
#define NT_GUN_DATA_FREE 0x13 //直流充电枪非充电中实时数据
|
||||
#define NT_FAULT_DATA 0x15 //直流充电桩故障告警数据上传
|
||||
#define NT_MDU_DATA_REQ 0x16 //运营平台整流模块数据读取 rectifier module整流模块
|
||||
#define NT_MDU_DATA_ACK 0x17 //充电桩整流模块数据应答
|
||||
|
||||
//表底值上送
|
||||
#define NET_METER_DATA 0x31 //表底值上送
|
||||
|
||||
//运营交互帧类型码数据定义
|
||||
#define NT_START_CHG_REQ 0x41 //充电桩主动申请启动充电
|
||||
#define NT_START_CHG_ACK 0x42 //平台确认启动充电
|
||||
#define NT_REMOTE_START_ACK 0x43 //充电桩回复远程启机
|
||||
#define NT_REMOTE_START_REQ 0x44 //平台远程控制启机
|
||||
#define NT_REMOTE_STOP_ACK 0x45 //充电桩远程停机命令回复
|
||||
#define NT_REMOTE_STOP_REQ 0x46 //平台远程停机
|
||||
#define NT_BILL_UPLOAD 0x47 //充电结算帐单上传
|
||||
#define NT_BILL_UPLOAD_ACK 0x48 //平台结算账单确认
|
||||
#define NT_USERACNTINFO_REQ 0x49 //充电桩主动申请查询用户账户信息(未用)
|
||||
#define NT_USERACNTINFO_ACK 0x4A //平台回复查询用户账单信息(未用)
|
||||
#define NT_CARD_BILL_UPLOAD 0x4B //充电桩刷卡结算订单上传
|
||||
#define NT_CARD_BILL_UPLOAD_ACK 0x4C //平台刷卡结算订单应答
|
||||
|
||||
//运营平台设置帧类型码数据定义
|
||||
#define NT_WORK_PARA_CFG_ACK 0x71 //充电桩应答工作参数设置
|
||||
#define NT_WORK_PARA_CFG_REQ 0x72 //平台设置工作参数
|
||||
#define NT_WORK_PARA_QUERY_ACK 0x73 //充电桩应答工作参数查询
|
||||
#define NT_WORK_PARA_QUERY_REQ 0x74 //平台查询工作参数
|
||||
#define NT_CLK_SYNC_ACK 0x75 //充电桩应答平台对时
|
||||
#define NT_CLK_SYNC_REQ 0x76 //平台对时
|
||||
#define NT_REMOTE_OPRT_ACK 0x77 //充电桩应答平台远程操作
|
||||
#define NT_REMOTE_OPRT_REQ 0x78 //平台远程操作
|
||||
#define NT_LCD_SHOW_ACK 0x79 //充电桩应答平台触发液晶屏显示(用户电话)
|
||||
#define NT_LCD_SHOW_REQ 0x7A //平台触发液晶屏显示内容
|
||||
#define NT_UNLOCK_CARD_REQ 0x7B //充电桩请求解锁卡
|
||||
#define NT_UNLOCK_CARD_ACK 0x7C //平台应答解锁卡请求
|
||||
#define NT_FEE_CFG_REQ 0x7D //平台设置计费模型
|
||||
#define NT_FEE_CFG_ACK 0x7E //充电桩应答模型设置
|
||||
|
||||
//远程升级数据定义
|
||||
#define NT_UPGRADE_RQ 0x80 //平台发送远程升级指令
|
||||
#define NT_UPGRADE_DATA_REQ 0x81 //桩向平台请求文件内容(多次执行,直至文件传输完毕)
|
||||
#define NT_UPGRADE_DATA_ACK 0x82 //平台应答文件内容请求
|
||||
#define NT_UPDATE_AUTO 0x90 //平台FTP升级请求
|
||||
#define NT_UPDATE_AUTO_ACK 0x93 //充电桩主动上传FTP文件传输结果
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
typedef enum /* 积成智通后台通信协议-订单上送-启机失败/停机原因 */
|
||||
{
|
||||
|
||||
|
||||
IESZT_NORMAL=0, //0正常
|
||||
IESZT_STARTING_FAULT, //1启动中有报警
|
||||
IESZT_INSU_FAIL, //2绝缘检测失败
|
||||
IESZT_DY_SWCH_FAIL, //3控制辅源电压检测切换失败
|
||||
IESZT_DY_FAIL, //4投辅源失败
|
||||
IESZT_STARTING_BATVOLT_FAIL = 6, //6启动中,检测电池电压失败
|
||||
IESZT_KM_ON_FAIL, //7合直流输出开关失败
|
||||
IESZT_MDU_STAT_FAIL, //8模块启动失败
|
||||
IESZT_CHGING_FAULT, //9充电中有报警
|
||||
IESZT_CHGING_GUN_UNCNCT, //10充电中,检测到枪未连接到车上
|
||||
IESZT_CHGING_DY_OFF, //11充电中未检测到辅源
|
||||
IESZT_CHGING_BATVOLT_FAIL, //12充电中未检测到电池电压
|
||||
IESZT_CARD, //13刷卡停机
|
||||
IESZT_NO_BALANCE, //14余额不足
|
||||
IESZT_BMS_ABNMAL, //15充电中BMS异常停机
|
||||
IESZT_BMS_FULL, //16充电中BMS充满停机
|
||||
IESZT_STARTING_UNDERPOWER, //17启动中,辅助桩功率不足
|
||||
IESZT_BMS_DMD_VOLT_OVER, //18充电中,BMS需求电压越限
|
||||
IESZT_CURR_OVER, //19充电中,过流
|
||||
IESZT_GUN_TEMP_OVER, //20充电中,枪温过温
|
||||
IESZT_BMS_TO, //21 BMS通信超时
|
||||
IESZT_BMS_COMM_INTRPUT= 24, //24 BMS通信中断
|
||||
IESZT_LH_KM_OFF, //25 充电中,联合开关未合
|
||||
IESZT_LL_KM_ON, //26 充电中,边界联络开关未分
|
||||
IESZT_APP_STOP, //27 APP停止充电
|
||||
IESZT_K1K2_OFF, //28 K1/K2闭合失败
|
||||
IESZT_LOCK_GUN_FAIL = 30, //30 锁枪失败
|
||||
IESZT_OUT_VOLT_LOW = 33, //33 启机时,输出电压不达标
|
||||
IESZT_INSU_TO, //34 绝缘检测超时
|
||||
IESZT_BSM_STOP, //35 bsm终止
|
||||
IESZT_TO= 37, //37 超时重试
|
||||
IESZT_RELIEF_ON = 42, //42 启机过程中泄放继电器非法合
|
||||
IESZT_MONEY_OK, //43 达到设定金额
|
||||
IESZT_TIME_OK, //44 达到设定时长
|
||||
IESZT_POWER_OK, //45 达到设定电量
|
||||
|
||||
IESZT_URGENCY = 50, //50 急停
|
||||
IESZT_ACS, //51 门禁
|
||||
IESZT_WATER, //52 水浸
|
||||
IESZT_FS, //53 防雷
|
||||
IESZT_OVER_TEMP, //54 环温过温
|
||||
IESZT_UNDER_TEMP, //55 环温过低
|
||||
IESZT_CARdREADER_COMM, //56 读卡器通信故障
|
||||
IESZT_LCD_COMM, //57 LCD通信故障
|
||||
IESZT_KM_ERRACT, //58 母联直流接触器拒动/误动
|
||||
IESZT_KM_ADHESION, //59 母联直流接触器粘连
|
||||
IESZT_WHM_COMM, //60 电表通信故障
|
||||
IESZT_FUSE, //61 直流熔断器故障
|
||||
IESZT_GUN_KM_ADHESION, //62 #号枪对应直流输出接触器黏连故障 - 空闲,应断开,但检测到为闭合
|
||||
IESZT_MDU_COMM_DOWN, //63 所有模块通信中断
|
||||
IESZT_PRE_CHGOT, //64 充电机预充超时
|
||||
IESZT_bms_BHM_OT, //65 BHM报文接收超时
|
||||
IESZT_BMS_BRM_OT, //66 BRM报文接收超时
|
||||
IESZT_BMS_BRM_ERR, //67 BRM报文数据异常 - 报文数据长度错误,版本协议错误
|
||||
IESZT_BMS_BCP_OT, //68 BCP报文接收超时
|
||||
IESZT_BMS_BCP_ERR, //69 BCP报文数据异常 - 报文数据长度错误,电池当前电压越限
|
||||
IESZT_BMS_BRO_00_OT, //70 BRO报文(0x00)接收超时
|
||||
IESZT_BMS_BRO_AA_OT, //71 BRO报文(0xAA)接收超时
|
||||
IESZT_BMS_BCS_OT, //72 BCS报文接收超时
|
||||
IESZT_BMS_VOLTPARA_MISMTCH, //73 电池最高允许充电电压小于充电机最小输出电压
|
||||
IESZT_BATV_OVER10, //74 绝缘检测前直流输出接触器外侧电压>=10V
|
||||
IESZT_BATV_BMSV_OVER5, //75 启动充电前直流输出接触器外侧电压与通信报文电池电压相差》=-5%
|
||||
IESZT_OUTV_BATV_1TO10, //76 预充电时输出电压不在电池电压-(1V~10V)范围内
|
||||
IESZT_VIN_ERR, //77 vin清单使能,当前车vin不在清单内
|
||||
IESZT_CHGCABINET_COMM_DOWN, //78 充电柜通信中断
|
||||
IESZT_BMS_BCL_OT, //79 电池充电需求报文BCL接收超时
|
||||
IESZT_BMS_BAT_BCS_OT, //80 电池充电总状态报文BCS接收超时
|
||||
IESZT_BMS_BSM_OT, //81 动力蓄电池状态信息BSM接收超时
|
||||
IESZT_BMS_BSM_OVERU, //82 BSM - 单体动力蓄电池电压过高
|
||||
IESZT_BMS_BSM_BELOWU, //83 BSM - 单体动力蓄电池电压过低
|
||||
IESZT_BMS_BSM_OVERSOC, //84 BSM - 整车动力蓄电池荷电状态SOC过高
|
||||
IESZT_BMS_BSM_BELOWSOC, //85 BSM - 整车动力蓄电池荷电状态SOC过低
|
||||
IESZT_BMS_BSM_OVERI, //86 BSM - 动力蓄电池充电过电流
|
||||
IESZT_BMS_BSM_OVERT, //87 BSM - 动力蓄电池温度过高
|
||||
IESZT_BMS_BSM_INSUERR, //88 BSM - 动力蓄电池绝缘状态异常
|
||||
IESZT_BMS_BSM_CONNERR, //89 BSM - 动力蓄电池组输出连接器连接状态异常
|
||||
IESZT_BMS_BSM_FORBIT, //90 BSM - 禁止充电
|
||||
IESZT_BMS_BST_OT, //91 BST报文接收超时
|
||||
IESZT_BMS_BST_U_OK, //92 BST - 达到总电压设定值
|
||||
IESZT_BMS_BST_SNGUOK, //93 BST - 达到单体电压设定值
|
||||
IESZT_BMS_BST_INSUERR, //94 BST - 绝缘故障
|
||||
IESZT_BMS_BST_CONN_OVERT, //95 BST - 输出连接器过温
|
||||
IESZT_BMS_BST_CELL_OVERT, //96 BST - BMS元件过温/输出连接器过温
|
||||
IESZT_BMS_BST_CONN_ERR, //97 BST - 充电连接器故障
|
||||
IESZT_BMS_BST_BAT_OVERT, //98 BST - 电池组温度过高
|
||||
IESZT_BMS_BST_HVRELAY_ERR, //99 BST - 高压继电器故障
|
||||
IESZT_BMS_BST_2U_ERR, //100 BST - 检测点2电压检测故障
|
||||
IESZT_BMS_BST_ELSE, //101 BST - 其他故障
|
||||
IESZT_BMS_BST_OVERI, //102 BST - 电流超过需求值
|
||||
IESZT_BMS_BST_U_ABNORML, //103 BST - 电压异常
|
||||
IESZT_BMS_BEM, //104 BEM - 错误报文
|
||||
IESZT_OUTPUT_VOLT_OVER, //105 直流输出过压
|
||||
IESZT_OUTPUT_VOLT_LOW, //106 直流输出不达标
|
||||
IESZT_GUNLOCK_ERR, //107 枪锁状态错误
|
||||
IESZT_SMOKE_ERR, //108 烟感告警
|
||||
|
||||
} IESZT_REASON_E;
|
||||
|
||||
typedef enum //星星充电-订单上送-启机失败/停机原因
|
||||
{
|
||||
STAR_PILE_STOP = 0, //0 桩主动停止
|
||||
STAR_URGENCY, //1 急停按键被按下
|
||||
STAR_WHM_COMM, //2 电表通信故障
|
||||
STAR_CARdREADER_COMM, //3 读卡器通信故障
|
||||
STAR_PE_FAULT, //4 接地故障
|
||||
STAR_MDU_COMM_DOWN, //5 电源模块通信故障
|
||||
STAR_ALL_MDU_COMM_DOWN, //6 电源模块全部通信故障
|
||||
STAR_OVER_TEMP, //7 过温故障
|
||||
STAR_ELE_LEAKAGE, //8 漏电故障
|
||||
STAR_METER_DATA_ABNOEMAL, //9 电表数据异常停止充电
|
||||
SATR_ACS, //10 舱门打开停止充电
|
||||
STAR_URGENCY_PRESS, //11 急停按键被按下(直流专用)
|
||||
|
||||
STAR_FCI = 17, //17 FCI故障-部分模块通讯故障(用作开关模块故障)
|
||||
STAR_INVALID_CURR, //18 无有效电流停止(交流充满,车载故障等)
|
||||
STAR_LOW_BLANCE, //19 余额不足停止
|
||||
STAR_GUNLINK_INTERUPTED, //20 充电连接器断开停止
|
||||
STAR_CARD_STOP, //21 刷卡停止
|
||||
STAR_BMS_STOP, //22 BMS正常停止(直流充满)
|
||||
STAR_APP_STOP, //23 手机APP或者微信停止
|
||||
STAR_SCREEN_STOP, //24 触控屏手动停止
|
||||
STAR_BMS_CONN_ERR, //25 BMS通信故障
|
||||
STAR_DC_ELECT_SHORT, //26 充电机输出短路
|
||||
STAR_MDU_HARD_ERR, //27 模块硬件故障
|
||||
STAR_OUTPUT_OVER_CURR, //28 充电机输出过流
|
||||
STAR_PILE_OVER_TEMP, //29 充电机过温保护
|
||||
STAR_OUTPUT_OVER_VOL, //30 充电机输出过压
|
||||
STAR_INPUT_OVER_VOL, //31 充电机输入过压
|
||||
STAR_INPUT_LOW_VOL, //32 充电机输入欠压
|
||||
STAR_BLEED_ERR, //33 泄放电阻放电故障(未能在规定时间电压降至60V以下)
|
||||
STAR_BEM_ALARM, //34 BEM报警
|
||||
STAR_INSUAL_ERR, //35 充电机绝缘检测故障
|
||||
STAR_GUNLINK_ERR, //36 充电枪未正确连接
|
||||
STAR_BATT_REVERSE, //37 电池反接故障
|
||||
STAR_BATT_ABNORMAL, //38 电池异常
|
||||
STAR_BSD_OVERTIME, //39 BSD超时
|
||||
STAR_BST_OVERTIME, //40 BST超时
|
||||
STAR_BCL_OVERTIME, //41 BCL超时
|
||||
STAR_BRO_OVERTIME, //42 BRO超时
|
||||
STAR_BCP_OVERTIME, //43 BCP超时
|
||||
STAR_BRM_OVERTIME, //44 BRM超时
|
||||
STAR_BMS_OTHER_ERR, //45 BMS其他故障
|
||||
STAR_BMS_VOL_ABNORMAL, //46 BMS检测电压异常
|
||||
STAR_BMS_CURR_OVER, //47 BMS检测电流过大
|
||||
STAR_BMS_BATT_OVER_TEMP, //48 BMS电池组过温
|
||||
STAR_BMS_COMPOENT_OVER_TEMP,//49 BMS元件过温
|
||||
STAR_BMS_CONNECTOR_OVER_TEMP,//50 BMS输出连接器过温故障
|
||||
STAR_BMS_INSUAL_ERR, //51 Bms绝缘故障
|
||||
STAR_CAR_STOP, //52 车载停止
|
||||
STAR_SWITCH_CHARGE_WAIT, //53 切换充等待中(用作避雷器故障)
|
||||
STAR_PLAT_CONN_ERR, //54 与后台通信故障
|
||||
STAR_PLAT_STOP, //55 后台停止
|
||||
STAR_TIME_OK, //56 达到设置充电时长停机
|
||||
STAR_POWER_OK, //57 达到设置充电电量停机
|
||||
STAR_MONEY_OK, //58 达到设置充电金额停止
|
||||
STAR_PILE_TERMAL_CONN_ERR, //59 主副板通信故障
|
||||
STAR_POWER_OUTAGE_STOP, //60 掉电停止
|
||||
STAR_K3K4_STICK, //61 辅助电源粘连
|
||||
STAR_K1K2_STICK, //62 输出继电器粘连
|
||||
STAR_BATTV_OVER10, //63 (启动前)继电器外侧电压大于10v停止充电
|
||||
STAR_PILE_BHM_MISMATCH, //64 桩输出能力不匹配(BHM)停止充电
|
||||
|
||||
STAR_NEED_OVER_MAXVOL=66, //66 需求电压或充电电压大于最高允许充电电压停止充电
|
||||
STAR_BRO_MAJOR_FAILURE, //67 BRO重大故障停止充电
|
||||
STAR_PERCHARGE_VOL_MISMATCH,//68 预充电压不匹配停止充电
|
||||
STAR_SMOKE_ERR, //69 烟雾报警(停止用)
|
||||
|
||||
STAR_CAR_MATCH_FAILED, //70 车辆匹配失败
|
||||
STAR_SOC_REACH_THRESHOLD, //71 SOC达到阀值
|
||||
|
||||
STAR_CARD_UNREGISTERED, //100 卡号未注册
|
||||
STAR_TIME_FORMAT_ABNORMAL, //101 时间格式异常
|
||||
|
||||
}STAR_REASON_E;
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void v_ies_register_req(void);
|
||||
void v_ies_heart(void);
|
||||
void v_ies_fee_req(void);
|
||||
void v_ies_rtd_free(U8_T gunNo); //空闲实时数据 0x13, 变位上送,120s
|
||||
void v_ies_rtd_chging(U8_T gunNo); //充电中实时数据 0x11 变化加间隔上送,30s
|
||||
void v_ies_meter_data(U8_T u8_gunNo); //表底值 0x31,30分钟上送一次
|
||||
void v_ies_fault_V35(U8_T u8_gunNo); //故障告警数据 0x15, V3.5协议故障不分枪
|
||||
void v_ies_bill_pack(U8_T u8_gunNo); //组上送账单
|
||||
void v_ies_start_req(U8_T u8_gunNo); //主动启动充电请求 0x41
|
||||
void v_ies_set_clk_sync_ack(void);
|
||||
void v_ies_set_fee_ack(void);
|
||||
void v_ies_upgrade_data_auto_ack(void);
|
||||
void v_ies_rmt_start_ack(U8_T u8_gunNo, U8_T serialNum ,U8_T flag); //远程启动应答 0x43, 应答帧序号跟询问帧需保持一致
|
||||
void v_ies_rmt_stop_ack(U8_T u8_gunNo, U8_T serialNum); //远程停机应答 0x45
|
||||
void v_ies_rmt_oprt_ack(U8_T *u8_opr_cmd, U8_T opr_result); //远程操作应答 0x77
|
||||
|
||||
|
||||
|
||||
|
||||
void v_ies_heart_ack(void);
|
||||
void v_ies_register_ack(U8_T *u8_recvBuf); //登陆帧应答
|
||||
void v_ies_fee_ack(U8_T *u8_recvBuf); //计费模型应答帧
|
||||
void v_ies_set_clk_sync(U8_T *u8_recvBuf); //对时
|
||||
void v_ies_set_fee(U8_T *u8_recvBuf); //计费模型设置
|
||||
void v_ies_upgrade_data_auto(U8_T *u8_recvBuf); //平台应答文件内容请求
|
||||
void v_ies_start_req(U8_T gunNo); //主动启动充电请求 0x41
|
||||
void v_ies_rx_start(U8_T *u8_recvBuf); //运营确认启动 0x42
|
||||
void v_ies_bill_upload_ack(U8_T *u8_recvBuf); //结算账单应答
|
||||
void v_ies_rmt_start(U8_T *u8_recvBuf); //远程启动
|
||||
void v_ies_rmt_stop(U8_T *u8_recvBuf); //远程停机
|
||||
void v_ies_rmt_oprt(U8_T *u8_recvBuf) ; //远程操作
|
||||
void v_ies_qr_call(U8_T *u8_recvBuf) ; //二维码召唤
|
||||
void v_ies_qr_dis(U8_T *u8_recvBuf) ; //二维码下发
|
||||
U8_T u8_ies_get_gunLink_state(U8_T u8_gunNo);
|
||||
U8_T u8_ies_get_gun_state(U8_T u8_gunNo);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* BS_IES_INTERFACE_H */
|
||||
@@ -0,0 +1,472 @@
|
||||
/**
|
||||
* @file bs_connect_impl.c
|
||||
* @brief 平台链路层实现:TCP/4G 连接、收发、4G 双 socket 分发
|
||||
*
|
||||
* - 联网参数从 t_sys_fix_cfg.s_net_cfg 读取
|
||||
* - 4G 模式经 WH-GM5 模组,支持 socketA/B 分发
|
||||
* - 以太网分支(u8_netType!=0)预留待补全
|
||||
*/
|
||||
#if EC200A_4G_EN
|
||||
#include "plat_comm/4g_module/ec200a_4g.h"
|
||||
#include "plat_comm/4g_module/ec200a_4g_cfg.h"
|
||||
#endif
|
||||
#if WH_GM5_EN
|
||||
#include "plat_comm/4g_module/bs_wh_gm5.h"
|
||||
#endif
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "plat_comm/plat_comm_task.h"
|
||||
#include "plat_comm/lwip_module/lwip_module.h"
|
||||
|
||||
#define BS_PRINTF_EN 1 //平台收发数据打印
|
||||
|
||||
ws_url_t url_info;
|
||||
|
||||
/**
|
||||
* @brief 网口 TCP:关闭指定平台通道 socket
|
||||
*/
|
||||
void v_bs_eth_close_socket(U8_T u8_id)
|
||||
{
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[u8_id].fd >= 0) {
|
||||
lwip_tcp_close((int)s_bs_task_ctrl.s_bs_ctrl[u8_id].fd);
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_id].fd = (S32_T)-1;
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_id].u8_TCP_connect_flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1 //联网参数配置
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 4G模块联网参数获取
|
||||
*
|
||||
* @param[in] s_4g_para - 4G联网参数结构体
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-01
|
||||
* ************************************************************************
|
||||
*/
|
||||
#if WH_GM5_EN
|
||||
void v_get_4G_config_para(S_4G_CONFIG_PARA *s_4g_para)
|
||||
{
|
||||
SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg;
|
||||
|
||||
|
||||
memset(s_4g_para , 0 ,sizeof(S_4G_CONFIG_PARA));
|
||||
|
||||
//s_4g_para->socketA_IP = testIP;
|
||||
s_4g_para->socketA_IP = t_sys_fix_cfg->s_net_cfg.u8_serverA_domain_name; //
|
||||
s_4g_para->socketB_IP = t_sys_fix_cfg->s_net_cfg.u8_serverB_domain_name;
|
||||
|
||||
s_4g_para->u8_Sim_Type = t_sys_fix_cfg->s_net_cfg.u_net_comm_para.bits.bit_sim_anp_type;
|
||||
s_4g_para->u8_socketB_en = t_sys_fix_cfg->s_net_cfg.u_net_comm_para.bits.bit_platB_en;
|
||||
}
|
||||
#endif /* WH_GM5_EN */
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 4G模块参数初始化
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-19
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_bs_4G_data_init(void)
|
||||
{
|
||||
#if EC200A_4G_EN
|
||||
{
|
||||
S_4G_CONFIG_PARA s_4g_para;
|
||||
SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg;
|
||||
memset(&s_4g_para, 0, sizeof(S_4G_CONFIG_PARA));
|
||||
s_4g_para.socketA_IP = t_sys_fix_cfg->s_net_cfg.u8_serverA_domain_name;
|
||||
s_4g_para.socketB_IP = t_sys_fix_cfg->s_net_cfg.u8_serverB_domain_name;
|
||||
s_4g_para.u8_Sim_Type = t_sys_fix_cfg->s_net_cfg.u_net_comm_para.bits.bit_sim_anp_type;
|
||||
s_4g_para.u8_socketB_en = t_sys_fix_cfg->s_net_cfg.u_net_comm_para.bits.bit_platB_en;
|
||||
v_4g_data_init(&s_4g_para);
|
||||
}
|
||||
#endif /* EC200A_4G_EN */
|
||||
#if WH_GM5_EN
|
||||
{
|
||||
S_4G_CONFIG_PARA s_4g_para;
|
||||
v_get_4G_config_para(&s_4g_para);
|
||||
v_wh_gm5_data_init(&s_4g_para);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - TCP 联网测试函数
|
||||
*
|
||||
*
|
||||
* @return U8_T - 0未连接TCP 1已连接
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-08-29
|
||||
* ************************************************************************
|
||||
*/
|
||||
U8_T u8_tcp_connect(U8_T u8_id)
|
||||
{
|
||||
U8_T u8_ret = 0;
|
||||
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[u8_id].u8_netType == 0) //4G联网
|
||||
{
|
||||
#if WH_GM5_EN
|
||||
// 无线模块WH_GM5初始化
|
||||
if(s8_WH_GM5_init() == 0) //如果4g配置成功
|
||||
{
|
||||
if(s8_check_socket_LinkState(u8_id) >= 0) //查询网路连接状态
|
||||
{
|
||||
mSleep(100);
|
||||
v_4g_uart_cache_clear(); //4G模块配置完毕 清除串口缓存区中的数据
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(u8_ret == 0 && u32_safe_seconds_since(s_bs_task_ctrl.s_bs_ctrl[u8_id].ulong_restart_timer) > NUM_120) //120分钟 没连上 则重新连接
|
||||
{
|
||||
v_bs_4G_data_init(); //4G模块控制数据 初始化
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_id].ulong_restart_timer = get_current_seconds();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if EC200A_4G_EN
|
||||
if(s8_ec200a_4g_init() == 0)
|
||||
{
|
||||
u8_ret = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else //网线连接
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return u8_ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if WH_GM5_EN // WH-GM5 4G收发接口
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 4G模块多端口数据接收、用于多socket分端口接收、需要传入两平台接收处理函数接口
|
||||
*
|
||||
* @param[in] socketA_unPack - socketA 接收处理函数,不存在时赋值为NULL
|
||||
* @param[in] socketB_unPack - socketB 接收处理函数,不存在时赋值为NULL
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-02
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_4g_wh5_data_divide_recv(RecvUnpackHandler socketA_unPack, RecvUnpackHandler socketB_unPack)
|
||||
{
|
||||
// 静态缓冲区用于保存残包
|
||||
static U8_T u8_recv_saveBuf[BS_RECV_BUF_LEN];
|
||||
static U16_T u16_recv_saveLen = 0;
|
||||
|
||||
// 1. 数据合并:将新数据与静态缓冲区的残包合并
|
||||
U16_T u16_totalLen = 0; // 总数据长度(静态缓冲区+新数据)
|
||||
U16_T u16_rcvByteNum = 0; // 当前解析位置
|
||||
U16_T u16_oneFrmLen = 0; // 完整帧长度
|
||||
U16_T u16_dataLen = 0; // 数据域长度
|
||||
U8_T frameType = 0; // 帧类型
|
||||
U8_T flags = 0; // 标志位
|
||||
U8_T* payloadStart = NULL; // 有效载荷起始位置
|
||||
U16_T payloadLen = 0; // 有效载荷长度
|
||||
|
||||
U8_T u8_recvBuf[BS_RECV_BUF_LEN] = {0}; //接收数据缓存区
|
||||
|
||||
// 0. 获取串口数据
|
||||
U16_T u16_recvLen = u16_usart_recv(COM_PORT_BS, u8_recvBuf, BS_RECV_BUF_LEN);
|
||||
|
||||
// 如果没有接收到数据,直接返回
|
||||
if(u16_recvLen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算新数据需要复制的长度(避免缓冲区溢出)
|
||||
U16_T actualCopyLen = u16_recvLen;
|
||||
if (u16_recv_saveLen + u16_recvLen > BS_RECV_BUF_LEN) {
|
||||
// 缓冲区溢出处理:保留新数据,丢弃最旧的数据
|
||||
U16_T overflowBytes = (u16_recv_saveLen + u16_recvLen) - BS_RECV_BUF_LEN;
|
||||
|
||||
// 移动现有数据,为新数据腾出空间
|
||||
memmove(u8_recv_saveBuf, &u8_recv_saveBuf[overflowBytes],
|
||||
u16_recv_saveLen - overflowBytes);
|
||||
u16_recv_saveLen -= overflowBytes;
|
||||
|
||||
// 更新实际可复制长度
|
||||
actualCopyLen = BS_RECV_BUF_LEN - u16_recv_saveLen;
|
||||
}
|
||||
|
||||
// 追加新数据到静态缓冲区末尾
|
||||
memcpy(u8_recv_saveBuf + u16_recv_saveLen, u8_recvBuf, actualCopyLen);
|
||||
u16_recv_saveLen += actualCopyLen;
|
||||
|
||||
// 设置总数据长度
|
||||
u16_totalLen = u16_recv_saveLen;
|
||||
|
||||
// 2. 主解析循环 - 处理完整帧
|
||||
while (u16_totalLen >= 8)
|
||||
{
|
||||
// 帧头检查
|
||||
if (u8_recv_saveBuf[u16_rcvByteNum] != 0xAA ||
|
||||
u8_recv_saveBuf[u16_rcvByteNum+1] != 0xFD ||
|
||||
u8_recv_saveBuf[u16_rcvByteNum+2] != 0x55)
|
||||
{
|
||||
// 帧头不匹配,移动搜索位置
|
||||
u16_rcvByteNum++;
|
||||
u16_totalLen--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取数据长度(大端格式)
|
||||
u16_dataLen = (U16_T)((u8_recv_saveBuf[u16_rcvByteNum+3] << 8) |
|
||||
u8_recv_saveBuf[u16_rcvByteNum+4]);
|
||||
|
||||
// 计算完整帧长度 = 头部(3) + 长度字段(2) + 元数据(3) + 数据长度
|
||||
u16_oneFrmLen = 6 + u16_dataLen;
|
||||
|
||||
// 检查帧完整性
|
||||
if (u16_oneFrmLen > u16_totalLen) {
|
||||
// 残包处理:帧不完整,等待更多数据
|
||||
break;
|
||||
}
|
||||
|
||||
// 提取关键字段
|
||||
frameType = u8_recv_saveBuf[u16_rcvByteNum + 5]; // 类型字段(0x61/0x62)
|
||||
flags = u8_recv_saveBuf[u16_rcvByteNum + 6]; // 标志位
|
||||
|
||||
// 安全获取有效载荷(跳过帧头、长度字段和类型字段)
|
||||
payloadStart = &u8_recv_saveBuf[u16_rcvByteNum + 7];
|
||||
payloadLen = u16_dataLen - 2; // 减去类型和标志位
|
||||
|
||||
// 确保有效载`` 荷在缓冲区范围内
|
||||
if ((payloadStart + payloadLen) > (u8_recv_saveBuf + BS_RECV_BUF_LEN)) {
|
||||
// 缓冲区越界错误处理:重置缓冲区
|
||||
u16_recv_saveLen = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理网络返回数据
|
||||
if ((flags & 0x80) == 0) {
|
||||
if (frameType == 0x61 && socketA_unPack != NULL) {
|
||||
// SOCKETA数据
|
||||
socketA_unPack(0, payloadStart, payloadLen);
|
||||
}
|
||||
else if (frameType == 0x62 && socketB_unPack != NULL) {
|
||||
// SOCKETB数据
|
||||
socketB_unPack(1, payloadStart, payloadLen);
|
||||
}
|
||||
//测试打印
|
||||
#if BS_PRINTF_EN
|
||||
printf("--------------plat[%d] recv\r\n",frameType - 0x60);
|
||||
for (int i = 0; i < u16_recvLen; i++)
|
||||
{
|
||||
printf("%02x ",u8_recvBuf[i]);
|
||||
}
|
||||
printf("\n---------------\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// 移动位置指针(处理完整帧)
|
||||
u16_rcvByteNum += u16_oneFrmLen;
|
||||
u16_totalLen -= u16_oneFrmLen;
|
||||
}
|
||||
|
||||
// 3. 残包处理:保存剩余数据到静态缓冲区
|
||||
if (u16_totalLen > 0) {
|
||||
// 移动剩余数据到静态缓冲区开头
|
||||
if (u16_rcvByteNum > 0) {
|
||||
memmove(u8_recv_saveBuf, &u8_recv_saveBuf[u16_rcvByteNum], u16_totalLen);
|
||||
}
|
||||
u16_recv_saveLen = u16_totalLen;
|
||||
} else {
|
||||
// 没有剩余数据,重置静态缓冲区
|
||||
u16_recv_saveLen = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 4G 模块发送数据接口
|
||||
*
|
||||
* @param[in] u8_id - 发送接口ID 0代表不使能分发功能,直接透传发送 1代表像socketA发送数据 2代表像socketB发送数据
|
||||
* @param[in] u8_sendBuf - 发送数据数组指针
|
||||
* @param[in] u16_len - 发送数据长度
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-01
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_4g_wh5_data_send(U8_T u8_id,U8_T *u8_sendBuf,U16_T u16_len)
|
||||
{
|
||||
U16_T len,u16_checkSum;
|
||||
U8_T send_buf[10]={0};
|
||||
|
||||
if(u8_id == 0) //不使用套接字分发 直接发送数据
|
||||
u16_usart_send(COM_PORT_BS, u8_sendBuf, u16_len);
|
||||
else
|
||||
{
|
||||
//套接字分发组帧头
|
||||
send_buf[0] = 0x55;
|
||||
send_buf[1] = 0xFD;
|
||||
send_buf[2] = 0xAA;
|
||||
len = u16_len + 2;
|
||||
send_buf[3] = (len >> 8) & 0xFF;
|
||||
send_buf[4] = len & 0xFF;
|
||||
send_buf[5] = 0x60 + u8_id; //SOCKETA为0x61,SOCKETB为0x62
|
||||
send_buf[6] = 0;
|
||||
|
||||
//发送帧头
|
||||
u16_usart_send(COM_PORT_BS, send_buf, 7);
|
||||
//发送数据
|
||||
u16_usart_send(COM_PORT_BS, u8_sendBuf, u16_len);
|
||||
//计算校验合 发送数据+SOCKET序号
|
||||
u16_checkSum = u16_crc_checksum(u8_sendBuf, u16_len);
|
||||
u16_checkSum = u16_checkSum + send_buf[5];
|
||||
|
||||
send_buf[0] = u16_checkSum&0xFF;
|
||||
u16_usart_send(COM_PORT_BS, send_buf, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 4G模块数据接收 接收原始数据 不进行分包解析
|
||||
*
|
||||
* @param[in] u8_recvBuf - 接收数据缓存数组
|
||||
* @param[in] u16_len - 接受数组长度
|
||||
*
|
||||
* @return U16_T -
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-02
|
||||
* ************************************************************************
|
||||
*/
|
||||
U16_T u16_4g_wh5_data_recv(U8_T *u8_recvBuf,U16_T u16_len)
|
||||
{
|
||||
U16_T u16_recvLen;
|
||||
|
||||
memset(u8_recvBuf , 0 , u16_len);
|
||||
u16_recvLen = u16_usart_recv(COM_PORT_BS, u8_recvBuf, u16_len);
|
||||
|
||||
return u16_recvLen;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1 //公共s收发函数接口
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 公共发送接口
|
||||
*
|
||||
* @param[in] u8_id - 平台位置 0-1
|
||||
* @param[in] u8_sendBuf - 发送数据
|
||||
* @param[in] u16_len - 发送数据长度
|
||||
*
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-02
|
||||
* ************************************************************************
|
||||
*/
|
||||
void v_bs_send(U8_T u8_id,U8_T *u8_sendBuf,U16_T u16_len)
|
||||
{
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[u8_id].u8_netType == 0) //4G联网
|
||||
{
|
||||
#if WH_GM5_EN
|
||||
if(s_bs_task_ctrl.u8_4g_dispense_en == 1) u8_id+=1; //分发使能 序号加一
|
||||
|
||||
v_4g_wh5_data_send(u8_id,u8_sendBuf,u16_len);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if EC200A_4G_EN
|
||||
if(s_bs_task_ctrl.u8_4g_dispense_en == 1) u8_id+=1;
|
||||
#if EC200A_TCP_USE_TRANSPARENT_MODE
|
||||
v_4g_send_data(u8_id,u8_sendBuf,u16_len);
|
||||
#else
|
||||
v_ec200a_tcp_app_send(u8_id,u8_sendBuf,u16_len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
mSleep(Plat_Send_Sleep);
|
||||
s_bs_task_ctrl.u8_sendCnt++;
|
||||
|
||||
#if BS_PRINTF_EN
|
||||
printf("\n--------------bs send\r\n");
|
||||
for (int i = 0; i < u16_len; i++)
|
||||
{
|
||||
printf("%c",u8_sendBuf[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
for (int i = 0; i < u16_len; i++)
|
||||
{
|
||||
printf("%02x ",u8_sendBuf[i]);
|
||||
}
|
||||
printf("\n---------------\r\n");
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief - 公共接收函数
|
||||
*
|
||||
* @param[in] u8_id - 平台位置 0-1
|
||||
* @param[in] u8_recvBuf - 接收数据缓存地址
|
||||
* @param[in] u16_len - 最大接收长度
|
||||
*
|
||||
* @return U16_T -
|
||||
*
|
||||
* @author jiankalka
|
||||
* @date 2025-09-02
|
||||
* ************************************************************************
|
||||
*/
|
||||
U16_T u16_bs_recv(U8_T u8_id,U8_T *u8_recvBuf,U16_T u16_len)
|
||||
{
|
||||
U16_T u16_recvLen = 0;
|
||||
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[u8_id].u8_netType == 0) //4G联网
|
||||
{
|
||||
#if WH_GM5_EN
|
||||
u16_recvLen = u16_usart_recv(COM_PORT_BS, u8_recvBuf, u16_len);
|
||||
|
||||
#endif
|
||||
|
||||
#if EC200A_4G_EN
|
||||
u16_recvLen = u16_ec200a_tcp_app_recv(u8_recvBuf, u16_len);
|
||||
#endif
|
||||
}
|
||||
else //以太网
|
||||
{
|
||||
// if(bs_task_ctrl_data.s_bs_ctrl[id].fd >= 0)
|
||||
// u16_recvLen = eth_recv((int)bs_task_ctrl_data.s_bs_ctrl[id].fd, (char *)recvBuf, BS_RECV_BUF_LEN, 1);
|
||||
}
|
||||
|
||||
#if BS_PRINTF_EN
|
||||
if(u16_recvLen == 0) return u16_recvLen;
|
||||
|
||||
printf("\n--------------bs recv\r\n");
|
||||
for (int i = 0; i < u16_recvLen; i++)
|
||||
{
|
||||
printf("%c",u8_recvBuf[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
for (int i = 0; i < u16_recvLen; i++)
|
||||
{
|
||||
printf("%02x ",u8_recvBuf[i]);
|
||||
}
|
||||
printf("\n---------------\r\n");
|
||||
#endif
|
||||
|
||||
return u16_recvLen;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
#ifndef BS_CONNECT_IMPL_H
|
||||
#define BS_CONNECT_IMPL_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "plat_comm/plat_comm_task.h"
|
||||
#if EC200A_4G_EN
|
||||
#include "plat_comm/4g_module/ec200a_4g.h"
|
||||
#endif
|
||||
#if WH_GM5_EN
|
||||
#include "plat_comm/4g_module/bs_wh_gm5.h"
|
||||
#endif
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
|
||||
|
||||
/** 4G 套接字分发接收回调:id=平台序号,buf/len=有效载荷 */
|
||||
typedef void (*RecvUnpackHandler)(U8_T id, U8_T *buf, U16_T len);
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
typedef struct {
|
||||
char host[NUM_64];
|
||||
int port;
|
||||
char path[NUM_64];
|
||||
char apn_str[NUM_32];
|
||||
int is_secure; /* 0=ws, 1=wss */
|
||||
} ws_url_t;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
void v_bs_4G_data_init(void);
|
||||
U8_T u8_tcp_connect(U8_T u8_id);
|
||||
/** 网口:关闭指定通道 socket 并清零 fd/u8_TCP_connect_flag,重连前必须调用,避免 socket 泄漏导致“再连也连不上” */
|
||||
void v_bs_eth_close_socket(U8_T u8_id);
|
||||
|
||||
void v_bs_send(U8_T u8_id,U8_T *u8_sendBuf,U16_T u16_len);
|
||||
U16_T u16_bs_recv(U8_T u8_id,U8_T *u8_recvBuf,U16_T u16_len);
|
||||
void v_4g_wh5_data_divide_recv(RecvUnpackHandler socketA_unPack, RecvUnpackHandler socketB_unPack);
|
||||
|
||||
int parse_websocket_url_simple(const char* url, ws_url_t* result) ;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BS_CONNECT_IMPL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,237 @@
|
||||
|
||||
#ifndef BS_PUBLIC_IMPL_H
|
||||
#define BS_PUBLIC_IMPL_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "fault_cheak/fault_interface.h"
|
||||
#include "app_rtc/app_rtc.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
#include "flowctrl/flow_interface.h"
|
||||
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
#define BS_BUF_SIZE_16 16
|
||||
#define BS_BUF_SIZE_32 32
|
||||
#define BS_BUF_SIZE_64 64
|
||||
#define BS_BUF_SIZE_128 128
|
||||
#define BS_BUF_SIZE_256 256
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
typedef enum
|
||||
{
|
||||
GUN_NULL = 0, //无
|
||||
GUN_1 = 0, //1枪
|
||||
GUN_2, //2枪
|
||||
}E_GUN_NO;
|
||||
typedef enum{
|
||||
GET_FLAG=0, //获取数据
|
||||
SET_FALG, //设置标志
|
||||
CLEAR_FALG, //清空标志
|
||||
}FLAG_CTLR;
|
||||
typedef union {
|
||||
U8_T u8_data;
|
||||
S8_T s8_data;
|
||||
U16_T u16_data;
|
||||
S16_T s16_data;
|
||||
U32_T u32_data;
|
||||
F32_T f32_data;
|
||||
U64_T u64_data;
|
||||
}TYPE_DATA;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
//字符串数据类型 0x7000--0x8000
|
||||
E_STR_TYPE = 0,
|
||||
E_STR_PILE_NO, //桩编号-16
|
||||
E_STR_SOFTWARE_VERSION, //软件版本号
|
||||
E_STR_HARDWARE_VERSION, //硬件版本号
|
||||
|
||||
E_STR_LOG_preTradeNo, //订单流水号 type=0,平台流水号;type=1,设备流水号
|
||||
E_STR_LOG_user_id, //订单用户标识
|
||||
E_STR_LOG_vin, //VIN 车辆信息
|
||||
|
||||
E_STR_SDK_PARA_product_key, //国网sdk 产品密钥
|
||||
E_STR_SDK_PARA_device_name, //国网sdk 资产吗
|
||||
E_STR_SDK_PARA_device_secret, //国网sdk 设备秘钥
|
||||
E_STR_SDK_PARA_device_reg_code, //国网sdk 注册码
|
||||
E_STR_SDK_PARA_device_uid, //国网sdk 出厂编码
|
||||
E_STR_SDK_PARA_firmware_version, //国网sdk 固件版本信息
|
||||
|
||||
E_STR_SDK_SIM_ID, //sim卡号
|
||||
E_STR_METER_ADDR, //获取电表号 0直流电表 1交流电表
|
||||
E_STR_FEE_ID, //计费模型ID
|
||||
E_STR_QR_DISPLAY, //枪二维码 u8_gunNo=0/1 对应枪A/B
|
||||
|
||||
}E_GET_SYS_STR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_init_flag; //初始化标志
|
||||
U8_T u8_netType; //网络类型 04G 1网线
|
||||
U8_T u8_update_server_addr[32]; //升级服务器IP
|
||||
U16_T u16_update_server_port; //升级服务器端口号
|
||||
U8_T u8_user[20]; //用户名,字符串,不足尾部补0
|
||||
U8_T u8_pwd[20]; //密码,字符串,不足尾部补0
|
||||
U8_T u8_path[64]; //路径名,字符串,不足尾部补0
|
||||
U8_T u8_file_name[32]; //文件名,字符串,不足尾部补0
|
||||
|
||||
U8_T u8_bs_ack; //平台回复报文
|
||||
U8_T u8_update_result; //升级结果
|
||||
|
||||
U32_T u32_file_size; //文件大小
|
||||
U16_T u16_packSize; //单包数据大小
|
||||
U16_T u16_packNum; //包数量
|
||||
U16_T u16_packIndex; //获取包索引号
|
||||
|
||||
U32_T u32_file_fd; //文件句柄 EC200A 4G模块使用
|
||||
}T_FTP_UPDATE_INFO;
|
||||
|
||||
/***************************
|
||||
变量功能:定义变量类型枚举,内部调用时使用统一函数接口获取不同的数据内容
|
||||
*********************************/
|
||||
typedef enum
|
||||
{
|
||||
//桩信息
|
||||
E_BS_GET_SYS_DATA_GUN_NUM = 0, //枪数量
|
||||
E_BS_GET_SYS_DATA_NET_TYPE, //联网类型 0:4G 1:网线
|
||||
E_BS_GET_SYS_DATA_PLAT_EN, //平台使能 u8_gunNo=0平台A 1平台B,0不使能 1使能
|
||||
|
||||
E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_NUM, //type=0当前故障数量/type=1当前告警数量
|
||||
E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_DATA, //获取故障内容 参数 0-N 获取故障位置数量
|
||||
|
||||
E_BS_GET_SYS_DATA_4G_signal_quality, //4G信号强度
|
||||
E_BS_GET_SYS_DATA_RATED_POWER, //额定功率 单位w
|
||||
E_BS_GET_SYS_DATA_PILE_PUT_PARA, //充电桩输出参数 单位0.1V/0.1A 0最大电压 1最小电压 2最大电流 3最小电流
|
||||
|
||||
|
||||
|
||||
//枪信息
|
||||
E_BS_GET_SYS_DATA_GUN_FLOW_STATE, //终端充电状态 0空闲 1充电启动 2绝缘启动 3绝缘计算完成 4绝缘检测模块停止 5开始预充 6预充完成 7充电中 8BMS通讯超时 9充电停止 10充电停止完成
|
||||
E_BS_GET_SYS_DATA_GUN_STATE, //枪状态信息 type=0,枪连接状态;
|
||||
//type=1,电子锁状态;
|
||||
//type=2,枪空座归位状态;
|
||||
//type=3,正母线直流输出接触器状态
|
||||
//type=4,负母线直流输出接触器状态
|
||||
E_BS_GET_SYS_DATA_GUN_TEMP_POS, //充电枪正极温度 单位0.1度 0-100最小为0
|
||||
E_BS_GET_SYS_DATA_GUN_TEMP_NEG, //充电枪负极温度 单位0.1度 0-100最小为0
|
||||
E_BS_GET_SYS_DATA_MEMTER_ELECT, //电表电量 0.001kWh
|
||||
|
||||
E_BS_GET_SYS_DATA_PILE_TEMP, //环境温度 精确到小数点后1位,-50℃偏差
|
||||
E_BS_GET_SYS_DATA_METER_CUR, //电表电流 单位0.1A
|
||||
E_BS_GET_SYS_DATA_METER_VOL, //电表电压 单位0.1V
|
||||
|
||||
//订单信息
|
||||
E_BS_GET_SYS_DATA_LOG_ENERGY, //订单电量 0.001kWh 三位小数精度 type=0,已充电量;type=1,电表起始值,type=2,电表终止值
|
||||
E_BS_GET_SYS_DATA_LOG_STOP_REASON, //充电订单停止原因
|
||||
E_BS_GET_SYS_DATA_LOG_charge_flag, //订单充电标志 0未充电 1已经充电
|
||||
E_BS_GET_SYS_DATA_SOC_DATA, //SOC 数据 type=0,当前SOC;type=1,启动时SOC;type=2,结束时SOC
|
||||
E_BS_GET_SYS_DATA_LOG_CHARGE_TIME, //充电时间 min
|
||||
|
||||
|
||||
|
||||
//bms信息
|
||||
E_BS_GET_SYS_DATA_BMS_CELL_MAX_VOL, //最高单体动力蓄电池电压
|
||||
E_BS_GET_SYS_DATA_BMS_NEED_VOL, //电压需求 单位0.1V
|
||||
E_BS_GET_SYS_DATA_BMS_NEED_CUR, //电流需求 单位0.1A
|
||||
|
||||
E_BS_GET_SYS_DATA_BMS_CHARGE_VOL, //BMS充电电压测量值
|
||||
E_BS_GET_SYS_DATA_BMS_CHARGE_CUR, //BMS充电电流测量值
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_CHARGE_MAX_VOL, //最高允许充电总电压
|
||||
E_BS_GET_SYS_DATA_BMS_CELL_CHARGE_MAX_VOL, //单体动力蓄电池最高允许充电电压
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_CHARGE_MAX_CUR, //最高允许充电电流
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_RATED_VOL, //整车动力蓄电池系统额定总电压
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_VOL, //整车动力蓄当前电池总电压
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_RATED_C10, //整车动力蓄电池系统额定容量
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_RATED_ENERY, //动力蓄电池标称总能量
|
||||
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_ID, //电池编号信息 0最高单体动力蓄电池电压所在编号 1最高动力蓄电池温度检测点编号 2最低动力蓄电池温度检测点编号
|
||||
E_BS_GET_SYS_DATA_BMS_TEMP, //单位 0.1度0-100最小为0 0 电池最高温度 1最低温度
|
||||
E_BS_GET_SYS_DATA_BMS_REMAIN_TIME, //剩余充电时间 min
|
||||
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_MAX_TEMP, //最高动力蓄电池温度
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_MIN_TEMP, //最低动力蓄电池温度
|
||||
E_BS_GET_SYS_DATA_BMS_BATT_ALLOW_MAX_TEMP, //最高允许动力蓄电池温度
|
||||
|
||||
E_BS_GET_SYS_DATA_UNIX_TIMESTAMP, //当前 RTC Unix 时间戳(秒)
|
||||
} E_GET_SYS_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U16_T u16_ctrl_res; //控制原因 启动时为启动原因 停止时为停止原因;启动原因0 无动作 1离线卡 2在线卡 3后台启动(tcu/积成)\扫码启动 4vin启动 5调试启动
|
||||
|
||||
E_CHRG_LIMIT_MOD e_start_mode; //启动充电限制方式 0 不做限制的充电(默认) 1 限制金额 2 限制电量 electricity 3 限制SOC 4 限制充电时长 5 限制功率
|
||||
U16_T u16_limit; //限制参数 默认精度 0.01
|
||||
|
||||
U32_T u32_balance_begin; //启动充电前金额 0.0001元
|
||||
|
||||
USER_TYPE_E e_user_type; //用户类型
|
||||
U8_T u8_user_id[NUM_32]; //用户标识符
|
||||
|
||||
U8_T u8_tradeNo[EVS_MAX_TRADE_LEN]; //设备流水号
|
||||
U8_T u8_preTradeNo[EVS_MAX_TRADE_LEN]; //平台订单流水号
|
||||
}S_BS_CHAR_CTRL;
|
||||
typedef enum
|
||||
{
|
||||
E_CHAR_CTRL_START = 0, //启动充电
|
||||
E_CHAR_CHANGE_LOG, //修改数据
|
||||
E_CHAR_CTRL_STOP, //停止充电
|
||||
}E_CTRL_TYPE;
|
||||
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
extern T_FTP_UPDATE_INFO t_ftp_update_info;
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
U8_T u8_get_str_data(U8_T u8_gunNo,E_GET_SYS_STR e_dataType,U8_T u8_dataPara,char *u8_getData,U8_T u8_len);
|
||||
/** 写字符串类系统参数;u8_len 为有效字节数(帧体可非 '\0' 结尾);返回 1 成功 0 失败 */
|
||||
U8_T u8_set_str_data(U8_T u8_gunNo, E_GET_SYS_STR e_dataType, U8_T u8_dataPara,
|
||||
const char *pu8_data, U8_T u8_len);
|
||||
U32_T u32_bs_get_sys_data(U8_T u8_gunNo,E_GET_SYS_DATA e_datatype,U8_T u8_dataPara);
|
||||
/** 写数值类系统参数;返回 1 成功 0 失败(如 E_BS_GET_SYS_DATA_UNIX_TIMESTAMP 校时) */
|
||||
U8_T u8_set_sys_data(U8_T u8_gunNo, E_GET_SYS_DATA e_datatype, U32_T u32_val, U8_T u8_dataPara);
|
||||
U64_T u64_bs_get_sys_data(U8_T u8_gunNo,E_GET_SYS_DATA e_datatype);
|
||||
|
||||
|
||||
U8_T u8_bs_get_flow_state(U8_T u8_gunNo);
|
||||
void v_bs_set_time(Comm_Time *currtime);
|
||||
void v_bs_save_fee_data(U8_T u8_flag, S_FEE_MODEL *s_fee_model);
|
||||
void v_bs_log_txFlag_tradUpload(U8_T u8_gunNo);
|
||||
void v_bs_log_txFlag_autoStart(U8_T u8_gunNo); //设置充电鉴权上送标志;
|
||||
|
||||
void v_bs_charge_action_ctrl(U8_T u8_gunNo,E_CTRL_TYPE e_type, S_BS_CHAR_CTRL *s_ctrl_data);
|
||||
void v_remote_gunLock_ctrl(U8_T u8_gunNo,U8_T u8_type);
|
||||
void v_remote_reboot(U8_T type) ;
|
||||
|
||||
void v_cheak_state_change(void);
|
||||
void v_ftp_update_action(void);
|
||||
|
||||
U8_T u8_bs_get_txFlag_autoRwsult(U8_T u8_gunNo) ; //对外获取鉴权结果
|
||||
U8_T u8_bs_get_log_on_flag();
|
||||
|
||||
U8_T u8_card_info_add(char *card_id);
|
||||
void v_card_info_clear();
|
||||
void v_bs_get_gun_log_data(U8_T u8_gunNo , S_LOG_DATA *s_log);
|
||||
void v_bs_gwsdk_set_para(U8_T u8_type, S_SDK_DATA *s_sdk_data);
|
||||
void v_bs_logUploadOk_updateInfo(U16_T u16_cmplte_bill_index);
|
||||
void v_bs_get_fee_part_data(U8_T u8_gunNo,U8_T flag,S_LOG_DATA *t_log,U32_T *u32_part_energy,F32_T *f32_rate,F32_T *f32_sevice);
|
||||
|
||||
S8_T s8_plat_get_unsettled_order( S_LOG_DATA *s_log,U8_T u8_type);
|
||||
void v_bs_platform_settlement_confirm(U8_T u8_gunNo);
|
||||
void v_ocpp_offline_local_authentication_check(U8_T u8_gunNo);
|
||||
U8_T u8_plat_get_real_order(S_LOG_DATA *s_log,U8_T u8_gunNo);
|
||||
|
||||
void v_get_ocpp_cfg_info(char *mag , U16_T u16_len);
|
||||
U8_T u8_set_ocpp_cfg_info(const char *key,const char *data);
|
||||
void v_ocpp_get_real_log_file_name(char *fileName,int len);
|
||||
U8_T u8_bs_get_update_flag();
|
||||
U8_T u8_bs_get_gun_disable_flag(U8_T u8_gunNo);
|
||||
void v_ocpp_set_gun_flow_real_state_init(U8_T u8_gunNo);
|
||||
/** 设置二维码更新标志(CCU621 暂未实现) */
|
||||
void v_ccu_set_qrcode_flag(U8_T u8_gunNo, U8_T flag);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BS_PUBLIC_IMPL_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @file lwip_ftp_upgrade.h
|
||||
* @brief 基于 LwIP socket 的 FTP 固件升级状态机(网线远程升级)
|
||||
*
|
||||
* 说明:
|
||||
* - 参考 ec200a_4g_ftp.c 的“状态机 + 周期调用”风格;
|
||||
* - 通过 LwIP socket 实现 FTP 控制/数据连接,按 1024 字节分包写入升级区域;
|
||||
* - 每次调用只推进一小步,不阻塞 v_Plat_Comm 线程。
|
||||
*/
|
||||
#ifndef LWIP_FTP_UPGRADE_H
|
||||
#define LWIP_FTP_UPGRADE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化 LwIP FTP 升级状态机
|
||||
*
|
||||
* 说明:
|
||||
* - 直接读取全局 t_ftp_update_info 中的 FTP 参数(server/port/user/pass/path/file_name);
|
||||
* - 清空内部静态状态结构体,关闭残留 socket,准备进入 CONNECT 步骤。
|
||||
*/
|
||||
void lwip_ftp_upgrade_init_from_tftp(void);
|
||||
|
||||
/**
|
||||
* @brief 周期推进 LwIP FTP 升级状态机(单步)
|
||||
*
|
||||
* 返回值:
|
||||
* - 0:升级进行中(下次调用继续推进);
|
||||
* - 1:升级成功完成(文件全部下载且升级头标记为 NEED_UP);
|
||||
* - <0:升级失败(FTP/网络/Flash 错误等,具体原因在日志中打印)。
|
||||
*/
|
||||
int lwip_ftp_upgrade_step(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_FTP_UPGRADE_H */
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
# LwIP FTP 升级功能说明(最终版)
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
- 在 **不依赖 4G 模块 AT 指令** 的情况下,通过 **以太网 (LwIP socket)** 实现 FTP 固件下载。
|
||||
- 复用已有的 `flash_upgrade.h` 升级区管理:
|
||||
- `upgrade_erase_program_area()` 擦除升级区域;
|
||||
- `upgrade_write_packet()` 按 `PACKET_SIZE`(1024 字节)分包写入;
|
||||
- `upgrade_success_update_header()` 更新升级头(`UPGRADE_FLAG_IDLE / UPGRADE_FLAG_NEED_UP`)。
|
||||
- 与 4G FTP 升级整体思路保持一致,但实现形式为:
|
||||
**非阻塞状态机 + 周期调用 `v_ftp_update_action()`**。
|
||||
|
||||
## 2. 模块文件结构
|
||||
|
||||
位于 `app/plat_comm/lwip_module/`:
|
||||
|
||||
- `lwip_module.[ch]`
|
||||
- 对 LwIP socket 做简单封装:`lwip_resolve_host / lwip_set_nonblock / lwip_tcp_send / lwip_tcp_recv / lwip_tcp_close` 等。
|
||||
|
||||
- `lwip_ftp_upgrade.[ch]`
|
||||
- **网线 FTP 程序远程升级的完整状态机实现**,仅保留这一对文件:
|
||||
- 控制连接(21 端口);
|
||||
- `USER/PASS` 登录;
|
||||
- `TYPE I` 二进制模式;
|
||||
- `CWD`(可选目录切换);
|
||||
- `SIZE` 查询文件大小;
|
||||
- `PASV` 被动模式解析数据通道;
|
||||
- `RETR` 下载文件并按 1024 字节分包写 Flash;
|
||||
- `QUIT` 结束会话并写入 NEED_UP 升级头。
|
||||
- 对外只暴露两个接口:
|
||||
- `void lwip_ftp_upgrade_init_from_tftp(void);`
|
||||
- `int lwip_ftp_upgrade_step(void);`
|
||||
|
||||
> 说明:旧版的 `lwip_ftp_fw_upgrade_api.[ch]`、`lwip_ftp_upgrade_flash_adapter.[ch]`、`lwip_ftp_fw_upgrade_sm.[ch]` 等文件已删除,不再使用。
|
||||
|
||||
## 3. 参数来源与触发方式
|
||||
|
||||
### 3.1 参数结构:`T_FTP_UPDATE_INFO`
|
||||
|
||||
在 `bs_public_impl.h` 中定义,关键字段如下:
|
||||
|
||||
- `u8_update_server_addr`:FTP 服务器域名 / IP;
|
||||
- `u16_update_server_port`:FTP 端口(通常 21);
|
||||
- `u8_user / u8_pwd`:用户名、密码;
|
||||
- `u8_path`:远程目录(可为空);
|
||||
- `u8_file_name`:文件名;
|
||||
- `u8_netType`:网络类型(`0=4G`,`1=网线`);
|
||||
- `u32_file_size`:文件大小(由 FTP SIZE 结果回填);
|
||||
- `u8_update_result`:升级结果(0=成功,1=失败)。
|
||||
|
||||
这些字段由 OCPP `UpdateFirmware` 命令解析函数 `parse_ftp_url()` 填充。
|
||||
|
||||
### 3.2 触发流程
|
||||
|
||||
1. OCPP 平台下发 `UpdateFirmware`,URL 形如:
|
||||
`ftp://user:pass@host:port/path/file.bin`
|
||||
→ 解析到全局 `t_ftp_update_info`。
|
||||
2. 上层把对应平台的 `u8_updata_flag` 置位。
|
||||
3. `plat_comm_task.c` 中的 `v_Plat_Comm()` 周期调用 `v_ftp_update_action()`。
|
||||
4. `v_ftp_update_action()` 内部根据:
|
||||
- `s_bs_task_ctrl.ocpp_data.u8_log_file_upload`:`1=日志上传,0=程序升级`;
|
||||
- `t_ftp_update_info.u8_netType`:`0=4G`(走 4G 模块),`1=网线`(走 LwIP FTP)。
|
||||
|
||||
## 4. 程序升级流程(网线 / LwIP)
|
||||
|
||||
当满足:
|
||||
|
||||
- `s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 0`(程序升级);
|
||||
- `t_ftp_update_info.u8_netType != 0`(网线/LwIP);
|
||||
|
||||
`v_ftp_update_action()` 中流程为:
|
||||
|
||||
```c
|
||||
if (t_ftp_update_info.u8_init_flag == 0)
|
||||
{
|
||||
if (t_ftp_update_info.u8_netType == 0) {
|
||||
v_ftp_ctrl_init(); // 4G 模块初始化
|
||||
} else {
|
||||
lwip_ftp_upgrade_init_from_tftp(); // 网线 FTP 状态机初始化
|
||||
}
|
||||
t_ftp_update_info.u8_init_flag = 1;
|
||||
}
|
||||
|
||||
if (t_ftp_update_info.u8_netType == 0) {
|
||||
u8_ec200a_4g_ftp_update(); // 4G FTP 升级
|
||||
} else {
|
||||
int ret = lwip_ftp_upgrade_step(); // LwIP FTP 升级状态机单步
|
||||
if (ret == 1) {
|
||||
// 升级成功
|
||||
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0;
|
||||
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0;
|
||||
t_ftp_update_info.u8_update_result = 0;
|
||||
} else if (ret < 0) {
|
||||
// 升级失败
|
||||
s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag = 0;
|
||||
s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag = 0;
|
||||
t_ftp_update_info.u8_update_result = 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.1 状态机步骤(`E_LWIP_FTP_STEP`)
|
||||
|
||||
`lwip_ftp_upgrade.c` 内部定义的状态机大致流程:
|
||||
|
||||
1. `LWIP_FTP_STEP_CONNECT`
|
||||
- 建立 FTP 控制连接(21 端口),并设置为非阻塞。
|
||||
2. `LWIP_FTP_STEP_WAIT_WELCOME`
|
||||
- 等待 `220` 欢迎语。
|
||||
3. `LWIP_FTP_STEP_SEND_USER / WAIT_USER`
|
||||
- 发送 `USER xxx`,等待响应;
|
||||
- 若返回 `331`,继续发送 `PASS`;否则直接进入 `TYPE`。
|
||||
4. `LWIP_FTP_STEP_SEND_PASS / WAIT_PASS`
|
||||
- 发送 `PASS xxx`,等待登录结果。
|
||||
5. `LWIP_FTP_STEP_SEND_TYPE / WAIT_TYPE`
|
||||
- 发送 `TYPE I`,切换为二进制模式。
|
||||
6. `LWIP_FTP_STEP_SEND_CWD / WAIT_CWD`(可选)
|
||||
- 如果 `u8_path` 非空,发送 `CWD <path>` 切目录。
|
||||
7. `LWIP_FTP_STEP_SEND_SIZE / WAIT_SIZE`
|
||||
- 发送 `SIZE <file>`,并从 `213` 响应中解析文件大小;
|
||||
- 回填到 `s_ctx.file_size` 和 `t_ftp_update_info.u32_file_size`。
|
||||
8. `LWIP_FTP_STEP_PREPARE_FLASH`
|
||||
- 调用 `upgrade_erase_program_area()` 擦除升级区;
|
||||
- 构造 `upgrade_header_t`,写入 `UPGRADE_FLAG_IDLE / file_size / packet_count` 等信息。
|
||||
9. `LWIP_FTP_STEP_SEND_PASV / WAIT_PASV`
|
||||
- 发送 `PASV`,解析 `227` 响应得到数据通道 IP/端口。
|
||||
10. `LWIP_FTP_STEP_CONNECT_DATA`
|
||||
- 按 `data_ip / data_port` 建立数据连接 socket。
|
||||
11. `LWIP_FTP_STEP_SEND_RETR / WAIT_RETR`
|
||||
- 发送 `RETR <file>`,等待 `150/125` 等开始传输响应。
|
||||
12. `LWIP_FTP_STEP_RECV_DATA`
|
||||
- 循环接收数据,按 1024 字节分包写入 Flash(见 4.2)。
|
||||
13. `LWIP_FTP_STEP_SEND_QUIT`
|
||||
- 发送 `QUIT`,关闭控制连接;
|
||||
- 写入 `UPGRADE_FLAG_NEED_UP` 头部,标记升级就绪。
|
||||
14. `LWIP_FTP_STEP_DONE / FAIL`
|
||||
- `DONE`:整个流程成功,`lwip_ftp_upgrade_step()` 返回 `1`;
|
||||
- `FAIL`:任意步骤出错,返回 `<0`。
|
||||
|
||||
### 4.2 分包写 Flash 与进度打印
|
||||
|
||||
- 内部使用一个 **动态申请的** 1024 字节缓存 `packet_buf`(不是全局大数组):
|
||||
- 在 `lwip_ftp_upgrade_init_from_tftp()` 中第一次调用 `pvPortMalloc(PACKET_SIZE)`(FreeRTOS 堆),后续重复使用;
|
||||
- 失败会直接打印 `"pvPortMalloc packet_buf fail"` 并进入 `FAIL`。
|
||||
- 数据通道每次 `lwip_tcp_recv()` 得到的数据都会写入 `packet_buf`:
|
||||
- 每累计满 1024 字节调用一次 `upgrade_write_packet(packet_index, packet_buf, len)`;
|
||||
- 处理最后不足一包的数据时,也会单独调用一次。
|
||||
- **接收进度打印**:
|
||||
- 每次收到数据后,都会根据 `downloaded / file_size` 计算百分比:
|
||||
```c
|
||||
Plat_Comm_LOG("lwip ftp: recv %lu/%lu (%u%%)\r\n", ...);
|
||||
```
|
||||
- 方便在串口日志中实时观察下载进度。
|
||||
|
||||
### 4.3 控制通道响应等待(最长 1 秒)
|
||||
|
||||
- 所有 FTP 控制命令的响应(`220 / 331 / 230 / 213 / 227 / 150 ...`)统一通过:
|
||||
|
||||
```c
|
||||
static int ftp_read_reply_block(int sock, uint32_t timeout_ms,
|
||||
int *out_code, char *out_msg, size_t out_msg_sz);
|
||||
```
|
||||
|
||||
- 关键特性:
|
||||
- **内部使用局部缓冲区**:
|
||||
- `char rx_buf[FTP_CTRL_RX_BUF_SIZE];`
|
||||
- `char line[FTP_LINE_MAX];`
|
||||
- 不再在全局结构体中定义大数组。
|
||||
- 每次调用最长阻塞 `timeout_ms` 毫秒(本工程中统一传入 `1000`,即 1 秒):
|
||||
- 在 1 秒内尽可能收满一条完整响应行;
|
||||
- 超时、对端关闭或错误均返回非 0,状态机进入 `FAIL`。
|
||||
|
||||
- 满足需求:
|
||||
- **“发送命令后可最长等待 1 秒接收数据,这样可以保证大部分交互在一个周期内完成”**。
|
||||
|
||||
## 5. 日志上传(4G / 预留 LwIP)
|
||||
|
||||
- 当 `s_bs_task_ctrl.ocpp_data.u8_log_file_upload == 1` 时,`v_ftp_update_action()` 走“日志上传”分支:
|
||||
- 当前仅在 `t_ftp_update_info.u8_netType == 0`(4G)时调用 `u8_ec200a_4g_ftp_log_upload()`;
|
||||
- 网线场景下会打印提示,尚未提供 LwIP 日志上传实现。
|
||||
|
||||
后续如需支持 **LwIP FTP 日志上传**,建议在 `lwip_ftp_upgrade.[ch]` 基础上扩展 `STOR/APPE` 等命令。
|
||||
|
||||
## 6. 注意事项
|
||||
|
||||
- **非阻塞总框架 + 局部 1 秒等待**:
|
||||
- 整体采用状态机 + 周期调用的方式集成在 `v_Plat_Comm` 中,不新建线程;
|
||||
- 单条 FTP 命令的响应等待时间 **最多 1 秒**,保证大部分交互能在一次 `v_ftp_update_action()` 周期内完成。
|
||||
|
||||
- **内存使用**:
|
||||
- 不再在全局结构体中定义大数组:
|
||||
- 控制通道解析使用的缓冲区全部为函数内局部变量;
|
||||
- 数据分包缓冲 `packet_buf[PACKET_SIZE]` 采用 FreeRTOS `pvPortMalloc` 动态申请,仅 1KB。
|
||||
|
||||
- **文件大小依赖 SIZE**:
|
||||
- 必须保证 FTP 服务器支持 `SIZE` 命令,否则无法写入正确的 `file_size`,升级头将不可靠。
|
||||
|
||||
- **错误处理与重试**:
|
||||
- 任意步骤出错,`lwip_ftp_upgrade_step()` 返回 `<0`;
|
||||
- `v_ftp_update_action()` 会清除 `u8_updata_flag` 并设置 `u8_update_result = 1`;
|
||||
- 上层可根据日志与结果决定是否重新触发升级。
|
||||
|
||||
## 7. 与 4G FTP 的对比
|
||||
|
||||
- **4G FTP**:
|
||||
- 文件:`ec200a_4g_ftp.c`;
|
||||
- 通过 AT 命令驱动 4G 模块内置 FTP 客户端;
|
||||
- 状态机在 `u8_ec200a_4g_ftp_update()` 内实现。
|
||||
|
||||
- **LwIP FTP(网线)**:
|
||||
- 文件:`lwip_module.c` + `lwip_ftp_upgrade.c`;
|
||||
- 使用 LwIP socket 直接实现 FTP 协议;
|
||||
- 与 4G 共用 `flash_upgrade` 升级区与头部结构;
|
||||
- 集成方式完全嵌入 `v_ftp_update_action()`,不影响 `v_Plat_Comm` 线程的主循环。
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "plat_comm/lwip_module/lwip_module.h"
|
||||
#include "lwip/sockets.h" /* LwIP socket API(socket/connect/send/recv/close) */
|
||||
#include "lwip/netdb.h" /* 域名解析 lwip_gethostbyname */
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 将主机名或 IP 字符串解析为 in_addr
|
||||
* @param[in] host - 服务器地址,可为 "192.168.1.1" 或 "www.xxx.com"
|
||||
* @param[out] out_addr - 解析得到的 IPv4 地址
|
||||
* @return 0 成功,-1 失败(非 IP 且 DNS 解析失败)
|
||||
* ************************************************************************
|
||||
*/
|
||||
int lwip_resolve_host(const char *host, struct in_addr *out_addr)
|
||||
{
|
||||
if (host == NULL || out_addr == NULL)
|
||||
return -1;
|
||||
|
||||
/* 先按 IP 字符串解析(如 "134.17.16.109") */
|
||||
unsigned long addr = inet_addr(host);
|
||||
if (addr != INADDR_NONE) {
|
||||
out_addr->s_addr = addr;
|
||||
return 0;
|
||||
}
|
||||
/* 非 IP 则按域名解析(依赖 LwIP DNS,仅 LWIP_DNS=1 时 netdb.h 才声明 lwip_gethostbyname) */
|
||||
#if LWIP_DNS
|
||||
{
|
||||
struct hostent *he = lwip_gethostbyname(host);
|
||||
if (he != NULL && he->h_addr_list[0] != NULL) {
|
||||
memcpy(&out_addr->s_addr, he->h_addr_list[0], (size_t)he->h_length);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 设置 socket 为非阻塞模式
|
||||
* @param[in] sock - socket 描述符
|
||||
* @return 0 成功,-1 失败
|
||||
* ************************************************************************
|
||||
*/
|
||||
int lwip_set_nonblock(int sock)
|
||||
{
|
||||
int flags = lwip_fcntl(sock, F_GETFL, 0);
|
||||
if (flags >= 0) {
|
||||
if (lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK) >= 0)
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 创建 TCP socket 并连接到指定地址
|
||||
* @param[in] host - 服务器地址(IP 或域名)
|
||||
* @param[in] port - 服务器端口
|
||||
* @return >=0 socket 描述符,<0 失败
|
||||
* ************************************************************************
|
||||
*/
|
||||
int lwip_tcp_connect(const char *host, uint16_t port)
|
||||
{
|
||||
struct sockaddr_in server_addr;
|
||||
struct in_addr ip_addr;
|
||||
int sock;
|
||||
|
||||
if (host == NULL || host[0] == '\0' || port == 0)
|
||||
return -1;
|
||||
|
||||
/* 创建 TCP 流式 socket */
|
||||
sock = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return -1;
|
||||
|
||||
/* 解析服务器 host(IP 或域名)为 in_addr */
|
||||
if (lwip_resolve_host(host, &ip_addr) != 0) {
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 填充服务器地址并发起连接 */
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = ip_addr.s_addr;
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
if (lwip_connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 设为非阻塞,避免 recv 阻塞任务 */
|
||||
if (lwip_set_nonblock(sock) != 0) {
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 关闭 socket
|
||||
* @param[in] sock - socket 描述符
|
||||
* ************************************************************************
|
||||
*/
|
||||
void lwip_tcp_close(int sock)
|
||||
{
|
||||
if (sock >= 0)
|
||||
lwip_close(sock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 发送数据
|
||||
* @param[in] sock - socket 描述符
|
||||
* @param[in] buf - 发送缓冲区
|
||||
* @param[in] len - 发送长度
|
||||
* @return >0 实际发送字节数,<=0 失败或对端关闭
|
||||
* ************************************************************************
|
||||
*/
|
||||
int lwip_tcp_send(int sock, const void *buf, size_t len)
|
||||
{
|
||||
if (sock < 0 || buf == NULL || len == 0)
|
||||
return -1;
|
||||
return lwip_send(sock, buf, len, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ************************************************************************
|
||||
* @brief 接收数据(非阻塞)
|
||||
* @param[in] sock - socket 描述符
|
||||
* @param[out] buf - 接收缓冲区
|
||||
* @param[in] len - 缓冲区长度
|
||||
* @return >0 实际接收字节数,0 对端关闭,<0 错误(errno 需检查)
|
||||
* ************************************************************************
|
||||
*/
|
||||
int lwip_tcp_recv(int sock, void *buf, size_t len)
|
||||
{
|
||||
if (sock < 0 || buf == NULL || len == 0)
|
||||
return -1;
|
||||
return lwip_recv(sock, buf, len, 0);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef LWIP_MODULE_H
|
||||
#define LWIP_MODULE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h> /* size_t */
|
||||
#include "lwip/sockets.h" /* LwIP socket API 和结构体定义 */
|
||||
#include "lwip/inet.h" /* LwIP inet_addr 和 INADDR_NONE 等宏 */
|
||||
|
||||
/* LwIP socket 相关宏定义(部分环境在 lwip/sockets.h 中未定义时在此兜底) */
|
||||
#ifndef F_GETFL
|
||||
#define F_GETFL 3 /* fcntl 获取文件描述符标志 */
|
||||
#endif
|
||||
#ifndef F_SETFL
|
||||
#define F_SETFL 4 /* fcntl 设置文件描述符标志 */
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#define O_NONBLOCK 0x0004 /* 非阻塞模式,recv 无数据时立即返回不阻塞任务 */
|
||||
#endif
|
||||
#ifndef EWOULDBLOCK
|
||||
#define EWOULDBLOCK 11 /* 非阻塞 recv 无数据时的 errno */
|
||||
#endif
|
||||
#ifndef EAGAIN
|
||||
#define EAGAIN 11 /* 同上,与 EWOULDBLOCK 等价 */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 将主机名或 IP 字符串解析为 in_addr
|
||||
* @param[in] host 服务器地址,可为 "192.168.1.1" 或 "www.xxx.com"
|
||||
* @param[out] out_addr 解析得到的 IPv4 地址
|
||||
* @return 0 成功,-1 失败(非 IP 且 DNS 解析失败)
|
||||
*/
|
||||
int lwip_resolve_host(const char *host, struct in_addr *out_addr);
|
||||
|
||||
/**
|
||||
* @brief 创建 TCP socket 并连接到指定地址
|
||||
* @param[in] host 服务器地址(IP 或域名)
|
||||
* @param[in] port 服务器端口
|
||||
* @return >=0 socket 描述符,<0 失败
|
||||
*/
|
||||
int lwip_tcp_connect(const char *host, uint16_t port);
|
||||
|
||||
/**
|
||||
* @brief 关闭 socket
|
||||
* @param[in] sock socket 描述符
|
||||
*/
|
||||
void lwip_tcp_close(int sock);
|
||||
|
||||
/**
|
||||
* @brief 发送数据
|
||||
* @param[in] sock socket 描述符
|
||||
* @param[in] buf 发送缓冲区
|
||||
* @param[in] len 发送长度
|
||||
* @return >0 实际发送字节数,<=0 失败或对端关闭
|
||||
*/
|
||||
int lwip_tcp_send(int sock, const void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief 接收数据(非阻塞)
|
||||
* @param[in] sock socket 描述符
|
||||
* @param[out] buf 接收缓冲区
|
||||
* @param[in] len 缓冲区长度
|
||||
* @return >0 实际接收字节数,0 对端关闭,<0 错误(errno 需检查)
|
||||
*/
|
||||
int lwip_tcp_recv(int sock, void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief 设置 socket 为非阻塞模式
|
||||
* @param[in] sock socket 描述符
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int lwip_set_nonblock(int sock);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_MODULE_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,219 @@
|
||||
#ifndef _BS_OCPP_CTRL_H_
|
||||
#define _BS_OCPP_CTRL_H_
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "publicdata/public_define.h"
|
||||
|
||||
|
||||
|
||||
#if BS_OCPP_EN //OCPP平台使能
|
||||
#include "bs_ocpp_json_data.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "main.h"
|
||||
#include "flash_file_mgr/ocpp_mv_offline_flash_impl.h"
|
||||
|
||||
#define OCPP_TIMERCNT_NUM 12 //ocpp 计时计数
|
||||
#define OCPP_ACK_STRUCT_NUM 5 //ocpp 应答结构体数量
|
||||
|
||||
#define OCPP_MALLOC pvPortMalloc
|
||||
#define OCPP_FREE vPortFree
|
||||
|
||||
typedef enum{
|
||||
OCPP_TIMER_BootNotification = 0, //登录帧
|
||||
OCPP_TIMER_Heartbeat, //心跳帧
|
||||
OCPP_TIMER_StatusNotification_GUNA, //A枪 状态上传
|
||||
OCPP_TIMER_StatusNotification_GUNB, //B枪 状态上传
|
||||
OCPP_TIMER_MeterValues_GUNA, //A枪电表数据上传
|
||||
OCPP_TIMER_MeterValues_GUNB, //B枪电表数据上传
|
||||
OCPP_TIMER_StopTransaction_GUNA, //A枪停止充电数据上传
|
||||
OCPP_TIMER_StopTransaction_GUNB, //B枪停止充电数据上传
|
||||
OCPP_TIMER_Authorize_GUNA, //A枪鉴权查询上传
|
||||
OCPP_TIMER_Authorize_GUNB, //B枪鉴权查询上传
|
||||
}OCPP_SEND_TIMER;
|
||||
|
||||
typedef enum{
|
||||
OCPP_STATE_CHANGE_FLAG = 0, //状态变位标志
|
||||
OCPP_TRADE_UPLOAD_FLAG, //交易订单上传标志
|
||||
OCPP_START_RESULT_FLAG, //启动结果上送标志
|
||||
OCPP_STOP_RESULT_FLAG, //停止结果上送标志
|
||||
OCPP_VIN_START_FLAG, //VIN鉴权请求上送标志
|
||||
OCPP_VIN_RESULT, //VIN鉴权结果
|
||||
OCPP_BILL_INDEX, //上送订单索引号
|
||||
}OCPP_CHARGE_FLAG;
|
||||
|
||||
/** DiagnosticsStatusNotification 待上送状态:外部/FTP 仅赋值;ocpp_send_mag_ctrl 监听非 0 则发送并清 0 */
|
||||
typedef enum {
|
||||
OCPP_DIAG_NOTIFY_PENDING_NONE = 0,
|
||||
OCPP_DIAG_NOTIFY_PENDING_UPLOADING,
|
||||
OCPP_DIAG_NOTIFY_PENDING_UPLOADED,
|
||||
OCPP_DIAG_NOTIFY_PENDING_UPLOAD_FAILED,
|
||||
} OCPP_DIAG_NOTIFY_PENDING_E;
|
||||
|
||||
/** FirmwareStatusNotification 待上送状态:与 Diagnostics 同套路(u8_*_notify_pending + ocpp_send_mag_ctrl) */
|
||||
typedef enum {
|
||||
OCPP_FW_NOTIFY_PENDING_NONE = 0,
|
||||
OCPP_FW_NOTIFY_PENDING_DOWNLOADING,
|
||||
OCPP_FW_NOTIFY_PENDING_DOWNLOADED,
|
||||
OCPP_FW_NOTIFY_PENDING_INSTALLING,
|
||||
OCPP_FW_NOTIFY_PENDING_INSTALLED,
|
||||
OCPP_FW_NOTIFY_PENDING_DOWNLOAD_FAILED,
|
||||
OCPP_FW_NOTIFY_PENDING_INSTALLATION_FAILED,
|
||||
} OCPP_FW_NOTIFY_PENDING_E;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T flag; //存储标志
|
||||
U8_T check_count; //查询次数
|
||||
U8_T gunNo; //抢号
|
||||
U16_T u16_cnt; /* 存储计数(回绕安全:v_clear_save_cmd 基于 max-min 差值检测回绕)*/
|
||||
OCPP_CMD cmd; //命令值
|
||||
char key[64]; //随机数密钥
|
||||
}OCPP_DATA_SAVE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
OCPP_CMD cmd; //命令值
|
||||
U8_T gunNo; //枪号
|
||||
U8_T type; //应答数据类型 不同帧自定义
|
||||
U8_T timer_cnt; //计时计数 用于超时判定
|
||||
char ack_key[64]; //回复相同秘钥
|
||||
char ack_data[32]; //应答数据内容 不同帧自定义
|
||||
}OCPP_DATA_RECV_ACK;
|
||||
|
||||
/** 未结补单:先 Authorize 再 StartTransaction 的鉴权阶段 */
|
||||
typedef enum {
|
||||
OCPP_UNSETTLED_AUTH_NONE = 0,
|
||||
OCPP_UNSETTLED_AUTH_WAIT,
|
||||
OCPP_UNSETTLED_AUTH_OK
|
||||
} OCPP_UNSETTLED_AUTH_PHASE_E;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T id; //平台id 0-1
|
||||
U8_T u8_register_flag; //登录标志
|
||||
U8_T u8_ping_ack; //ping包回复标志
|
||||
|
||||
U8_T u8_unsettled_order_flag; //离线订单上送检查
|
||||
U8_T u8_reboot_flag; //重启标志 0无动作 1平台下发重启 2本地重启
|
||||
U8_T u8_log_file_upload; //日志文件上传标志
|
||||
U8_T u8_diag_notify_pending; //见 OCPP_DIAG_NOTIFY_PENDING_E,仅赋值触发;发送在 ocpp_send_mag_ctrl
|
||||
U8_T u8_fw_notify_pending; //见 OCPP_FW_NOTIFY_PENDING_E,与 u8_diag_notify_pending 并列、同发送机制
|
||||
U8_T u8_fw_reboot_pending; //Installed 已成功上送后再 v_sys_reboot(避免未上报即复位)
|
||||
U8_T u8_fw_installing_fsn_sent; //FSN(Installing) 已 sm>0 发出;STEP_FTP_SAVE 写头/复位前须为 1
|
||||
|
||||
S_LOG_DATA *s_unsettled_order_data;
|
||||
U8_T u8_unsettled_auth_phase; // 未结补单:Authorize→StartTransaction 流水线阶段,取值见 OCPP_UNSETTLED_AUTH_PHASE_E
|
||||
U32_T u32_unsettled_last_auth_s; // 未结补单:上次发出 Authorize 的秒时间戳(get_current_seconds),用于发送节流
|
||||
U32_T u32_unsettled_last_stx_s; // 未结补单:上次发出 StartTransaction 的秒时间戳(get_current_seconds),用于发送节流
|
||||
|
||||
|
||||
struct
|
||||
{
|
||||
U8_T u8_MeterValues_type; //数据上送标志 0无动作 1空闲数据上送 2充电中数据上送 3交易开始数据上送 4交易停止数据上送
|
||||
|
||||
//平台召测数据 "BootNotification","DiagnosticsStatusNotification","FirmwareStatusNotification","Heartbeat","MeterValues","StatusNotification"
|
||||
U8_T u8_get_Trigger_BootNotification;
|
||||
U8_T u8_get_Trigger_DiagnosticsStatusNotification;
|
||||
U8_T u8_get_Trigger_FirmwareStatusNotification;
|
||||
U8_T u8_get_Trigger_Heartbeat;
|
||||
U8_T u8_get_Trigger_MeterValues;
|
||||
U8_T u8_get_Trigger_StatusNotification;
|
||||
|
||||
U8_T u8_aut_cnt; //鉴权次数
|
||||
U8_T u8_StatusNotification_send_cnt; //状态上传次数(保留,周期上送已取消)
|
||||
U8_T u8_need_status_after_login; // BootNotification Accepted/Pending 后每枪需上送一次 StatusNotification
|
||||
|
||||
U8_T s_u8_mv_ws_fail_snap_req; /** MeterValues ws 发送失败时置位,下一周期强制写一条离线 MV */
|
||||
int s_ocpp_live_stop_tx_id; /** 在线已上送 StopTransaction 的 transactionId(按枪),-1 表示无 */
|
||||
}s_ctrl_flag[GUN_MAX_CNT]; //数据控制标志
|
||||
|
||||
struct
|
||||
{
|
||||
U16_T u16_timerCnt; //周期发送计数
|
||||
U16_T u16_timeNUM ; //计数周期值
|
||||
}s_timerCnt[OCPP_TIMERCNT_NUM];
|
||||
|
||||
U16_T u16_ocpp_ping_timer; //ping包周期计数
|
||||
U32_T u32_timer; //计时计数
|
||||
ULONG ulong_timer; //接收计时
|
||||
OCPP_DATA_RECV_ACK *ack_ctrl[OCPP_ACK_STRUCT_NUM];
|
||||
}OCPP_DATA_CTRL;
|
||||
|
||||
int recv_mags(char* recv_mag);
|
||||
|
||||
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_init_data()
|
||||
*功能说明: OCPP 应用层全量初始化:`v_ocpp_mv_offline_init`、清 OCPP_DATA_CTRL、Flash 配置与定时器、再走联网恢复(含 4G)
|
||||
*参数说明:
|
||||
* 上电由平台主任务调用;TCP 长时间连不上时亦调用本函数;diag/fw 等 pending 随 memset 清零
|
||||
****************************************************/
|
||||
void v_ocpp_init_data(U8_T bs_flag);
|
||||
/** 仅联网恢复:释放应答、可选 v_bs_4G_data_init、fd=-1、ulong_restart_timer;不执行 memset(OCPP_DATA_CTRL) */
|
||||
void v_ocpp_init_data_ex(U8_T bs_flag, U8_T reset_4g_modem);
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_tcp_connect()
|
||||
*功能说明: TCP连接功能实现 - 配置4G模块
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数: 0失败 1成功
|
||||
****************************************************/
|
||||
U8_T v_ocpp_tcp_connect();
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_logo_on()
|
||||
*功能说明: 平台桩信息登录 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数: 0失败 1成功
|
||||
****************************************************/
|
||||
U8_T v_ocpp_logo_on();
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_recv_data()
|
||||
*功能说明: 数据接收处理 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ocpp_recv_data();
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_send_ctrl()
|
||||
*功能说明: sdk私有协议逻辑控制 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ocpp_send_ctrl();
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_private_logic()
|
||||
*功能说明: sdk私有协议逻辑控制 -
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ocpp_private_logic(U8_T bs_flag);
|
||||
/** 仅置 u8_diag_notify_pending(Uploaded / UploadFailed);实际上送由 ocpp_send_mag_ctrl 监听该变量完成 */
|
||||
void v_ocpp_set_diag_upload_notify_pending(U8_T pending);
|
||||
/** 置 u8_fw_notify_pending;合法值见 OCPP_FW_NOTIFY_PENDING_E;实际上送由 ocpp_send_mag_ctrl */
|
||||
void v_ocpp_set_fw_notify_pending(U8_T pending);
|
||||
/** FSN(Installing) 是否已由 ocpp_send_mag_ctrl 成功发出(供 ec200a STEP_FTP_SAVE 等待) */
|
||||
U8_T u8_ocpp_fw_installing_fsn_sent(void);
|
||||
void v_ocpp_clr_fw_installing_fsn_sent(void);
|
||||
/** Installed 上送成功后需复位:置位,由 ocpp_send_mag_ctrl 在 FSN(Installed) 发出后调用 v_sys_reboot(旧流程;FTP 升级现改 EEPROM+先复位) */
|
||||
void v_ocpp_set_fw_reboot_after_installed(U8_T pending);
|
||||
/** FTP 写升级头后:FRAM 置位,复位;BootNotification Accepted/Pending 后再发 FSN Installed */
|
||||
void v_ocpp_fw_eeprom_set_installed_after_boot(void);
|
||||
/** 在线会话已上送 StopTransaction(如 Remote)后登记 txId,避免 EEPROM 掉电单重复 StopTx PowerLoss */
|
||||
void v_ocpp_mark_live_stop_tx_done(U8_T gun_idx, int transaction_id);
|
||||
/** 未结/掉电补单是否应跳过(同枪同 txId 已在线停充上送) */
|
||||
U8_T u8_ocpp_skip_unsettled_stop_for_live_tx(S_LOG_DATA *log);
|
||||
/** 未结单与当前枪正在进行的在线充电为同一会话(勿 StopTx / 勿抢 session MV 补发) */
|
||||
U8_T u8_ocpp_unsettled_is_live_charging_session(S_LOG_DATA *log);
|
||||
|
||||
/** 未结订单:preTradeNo 是否仍为占位/空(须先 Authorize→StartTransaction) */
|
||||
U8_T u8_ocpp_is_pre_trade_no_pending(const U8_T *p);
|
||||
/** 补发一条离线缓存的 MeterValues(与 send_mag 主动帧格式一致) */
|
||||
int32_t s32_ocpp_send_meter_values_replay(int gunNo, int transactionId, const S_OCPP_MV_OFFLINE_MV *mv);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,764 @@
|
||||
/**
|
||||
* @file BS_ocpp_mv_offline_bridge.c
|
||||
* @brief OCPP 离线 MeterValues 桥接实现:落盘(未连接/未登录)+ 未结订单联网后顺序补发
|
||||
*
|
||||
* @details **产品需求(明确)**\n
|
||||
* 1. **离线周期存储**:TCP 未连接或已连接但未登录 CSMS 时,充电过程中按配置周期将当前测量快照写入 SPI Flash 池,\n
|
||||
* 并在 FRAM 建立「本笔充电」与序号区间的映射。\n
|
||||
* 2. **离线订单上送**:未结订单在联网并完成 **Authorize → StartTransaction** 取得平台 **transactionId(流水号)** 后,\n
|
||||
* 在发送 `StopTransaction` 之前,须检查 Flash 中是否存有 **本次充电** 的离线 MeterValues。\n
|
||||
* 3. **如何认定「本次充电」的 MV**:落盘与补发使用 **同一规则** —— 以订单日志中的 **开始充电时间** `StartChargeTime`(`Comm_Time`)\n
|
||||
* 经 `xDate2Seconds` 得到 **32 位**开始充电秒 `u32_charge_start_sec`,并与 **枪号** 一起在 FRAM 映射中查找(落盘 API 第二参为兼容保留,恒传 0)。\n
|
||||
* 未结内存中的 `S_LOG_DATA` 与离线采样时 `v_meterlog_put_char_log_data` 取到的订单为同一会话时,**开始时间一致**则键一致,\n
|
||||
* `u8_ocpp_mv_offline_map_get` 命中的区间即为本笔充电的 MV,按序号逐条补发后再 `StopTransaction`。\n
|
||||
*
|
||||
* @details **实现划分**\n
|
||||
* 1. **键空间**:见上;离线时设备流水号常为空,故不以设备单号做键。\n
|
||||
* 2. **负载内容**:`S_OCPP_MV_OFFLINE_MV` 与 `BS_ocpp_ctrl.c` 中 `u8_get_ocpp_measurand_data` 口径对齐,补发由 `s32_ocpp_send_meter_values_replay` 组 JSON。\n
|
||||
* 3. **模块上下文**:文件内单一静态结构体 `s_ocpp_mv_bridge_ctx` 统一管理「每枪落盘节流时间戳、未结补发状态机」。\n
|
||||
* `v_ocpp_mv_offline_init` 在 **`v_ocpp_init_data`**(`BS_ocpp_ctrl.c`)中调用以装载 FRAM 管理块;`v_ocpp_mv_offline_bridge_private_logic` 不触发 Flash 层 init。\n
|
||||
* 释放未结订单内存前须 `v_ocpp_mv_offline_unsettled_abort()`,避免状态机与已释放 `S_LOG_DATA` 不一致。\n
|
||||
* 4. **两类时间源**:`get_current_seconds()` 为 **上电起单调秒**(`public_func.c`),只宜做间隔/节流;**OCPP 报文与 Flash 内 `u32_timestamp_unix`** 须用 **`GetCurrentTime` + `xDate2Seconds`**,与在线 `MeterValues_mag` 及 `StartChargeTime` 映射键同一套日历 epoch。
|
||||
*/
|
||||
|
||||
#include "BS_ocpp_mv_offline_bridge.h"
|
||||
|
||||
#if BS_OCPP_EN
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "BS_ocpp_ctrl.h"
|
||||
#include "publicdata/public_define.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
#include "flash_file_mgr/ocpp_mv_offline_flash_impl.h"
|
||||
#include "plat_comm/plat_comm_task.h"
|
||||
#include "BS_ocpp_configuration.h"
|
||||
#include "app_rtc/app_rtc.h"
|
||||
#include "bs_ocpp_json_data.h"
|
||||
|
||||
#define OCPP_OFFLINE_LOG(fmt, ...) \
|
||||
MYLOG_MSG(OCPP_PRINT_FLAG,fmt, ##__VA_ARGS__) //任务打印
|
||||
|
||||
extern int32_t s32_ocpp_send_meter_values_replay(int gunNo, int transactionId,
|
||||
const S_OCPP_MV_OFFLINE_MV *mv);
|
||||
|
||||
/* 与 BS_ocpp_ctrl.c 中 `v_ocpp_timestamp_to_str` 口径一致(离线 MV 时间戳由 unix 秒经本文件内 helper 转换) */
|
||||
static void v_mv_offline_timestamp_to_str(Comm_Time *ctTime, char *time_str)
|
||||
{
|
||||
Comm_Time time;
|
||||
|
||||
if (ctTime == NULL) {
|
||||
GetCurrentTime(&time);
|
||||
ctTime = &time;
|
||||
}
|
||||
#if (OCPP_TIMESTAMP_UTC_OUTPUT)
|
||||
else {
|
||||
memcpy(&time, ctTime, sizeof(Comm_Time));
|
||||
ctTime = &time;
|
||||
}
|
||||
v_adjust_time_with_timezone((Comm_Time *)ctTime, -8, 0);
|
||||
(void)snprintf(time_str, (size_t)OCPP_DATE_LEN, "%04u-%02u-%02uT%02u:%02u:%02uZ",
|
||||
(unsigned)ctTime->iYear, (unsigned)ctTime->ucMonth, (unsigned)ctTime->ucDay,
|
||||
(unsigned)ctTime->ucHour, (unsigned)ctTime->ucMin, (unsigned)ctTime->ucSec);
|
||||
#else
|
||||
(void)snprintf(time_str, (size_t)OCPP_DATE_LEN, "%04u-%02u-%02uT%02u:%02u:%02u+08:00",
|
||||
(unsigned)ctTime->iYear, (unsigned)ctTime->ucMonth, (unsigned)ctTime->ucDay,
|
||||
(unsigned)ctTime->ucHour, (unsigned)ctTime->ucMin, (unsigned)ctTime->ucSec);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void v_mv_offline_timestamp_unix_to_str(U32_T unix_sec, char *time_str)
|
||||
{
|
||||
Comm_Time ct;
|
||||
|
||||
xSeconds2Date((unsigned long)unix_sec, &ct);
|
||||
v_mv_offline_timestamp_to_str(&ct, time_str);
|
||||
}
|
||||
|
||||
/** 将离线 32B 快照填到单个 SampledValue(与 `u8_get_ocpp_measurand_data` 口径一致) */
|
||||
static U8_T u8_offline_mv_to_sampled(SampledValue *sv, OCPP_MEASURAND_ID e_type, const S_OCPP_MV_OFFLINE_MV *mv)
|
||||
{
|
||||
char data_str[24] = {0};
|
||||
|
||||
if (sv == NULL || mv == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
switch (e_type) {
|
||||
case ENUM_EnergyActiveImportRegister:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%lu", (unsigned long)mv->u32_meter_import_wh);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_EnergyActiveImportRegister, strlen(OCPP_STR_EnergyActiveImportRegister));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_Wh, strlen(OCPP_STR_Wh));
|
||||
return 1u;
|
||||
case ENUM_EnergyActiveImportInterval:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%lu", (unsigned long)mv->u32_energy_interval_wh);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_EnergyActiveImportInterval, strlen(OCPP_STR_EnergyActiveImportInterval));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_Wh, strlen(OCPP_STR_Wh));
|
||||
return 1u;
|
||||
case ENUM_PowerActiveImport:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%.3f", (F32_T)mv->u16_power_active_w);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_PowerActiveImport, strlen(OCPP_STR_PowerActiveImport));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_W, strlen(OCPP_STR_W));
|
||||
return 1u;
|
||||
case ENUM_PowerOffered:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%.3f", (F32_T)mv->u16_power_offered_01kw * 100.0f);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_PowerOffered, strlen(OCPP_STR_PowerOffered));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_W, strlen(OCPP_STR_W));
|
||||
return 1u;
|
||||
case ENUM_CurrentImport:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%.3f", (F32_T)mv->u16_current_import_a_x100 / 100.0f);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_CurrentImport, strlen(OCPP_STR_CurrentImport));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_A, strlen(OCPP_STR_A));
|
||||
return 1u;
|
||||
case ENUM_CurrentOffered:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%.3f", (F32_T)mv->u16_current_offered_a_x100 / 100.0f);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_CurrentOffered, strlen(OCPP_STR_CurrentOffered));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_A, strlen(OCPP_STR_A));
|
||||
return 1u;
|
||||
case ENUM_Voltage:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%.3f", (F32_T)mv->u16_voltage_v_x10 / 10.0f);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_Voltage, strlen(OCPP_STR_Voltage));
|
||||
memcpy(sv->location, OCPP_STR_Outlet, strlen(OCPP_STR_Outlet));
|
||||
memcpy(sv->unit, OCPP_STR_V, strlen(OCPP_STR_V));
|
||||
return 1u;
|
||||
case ENUM_SoC:
|
||||
(void)snprintf(data_str, sizeof(data_str), "%u", (unsigned)mv->u8_soc);
|
||||
memcpy(sv->value, data_str, strlen(data_str));
|
||||
memcpy(sv->format, OCPP_STR_Raw, strlen(OCPP_STR_Raw));
|
||||
memcpy(sv->measurand, OCPP_STR_SOC, strlen(OCPP_STR_SOC));
|
||||
memcpy(sv->location, OCPP_STR_EV, strlen(OCPP_STR_EV));
|
||||
memcpy(sv->unit, OCPP_STR_Percent, strlen(OCPP_STR_Percent));
|
||||
if (mv->u8_soc_data_type == 1u) {
|
||||
memcpy(sv->context, OCPP_STR_TransactionBegin, strlen(OCPP_STR_TransactionBegin));
|
||||
} else if (mv->u8_soc_data_type == 2u) {
|
||||
memcpy(sv->context, OCPP_STR_TransactionEnd, strlen(OCPP_STR_TransactionEnd));
|
||||
} else {
|
||||
memcpy(sv->context, OCPP_STR_SamplePeriodic, strlen(OCPP_STR_SamplePeriodic));
|
||||
}
|
||||
return 1u;
|
||||
default:
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *MeterValues_mag_offline_replay(int gunNo, int transactionId, const S_OCPP_MV_OFFLINE_MV *mv)
|
||||
{
|
||||
MeterValuesRequest json_data;
|
||||
char time_str[30] = {0};
|
||||
U8_T i;
|
||||
U8_T u8_data_num = 0;
|
||||
|
||||
if (mv == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
(void)memset(&json_data, 0, sizeof(json_data));
|
||||
json_data.connectorId = gunNo + 1;
|
||||
json_data.transactionId = transactionId;
|
||||
v_mv_offline_timestamp_unix_to_str(mv->u32_timestamp_unix, time_str);
|
||||
(void)memcpy(json_data.meterValue[0].timestamp, time_str, strlen(time_str));
|
||||
|
||||
for (i = 0; i < (U8_T)ENUM_measurand_Num; i++) {
|
||||
if ((s_ocpp_flash_cfg_data.s_ocpp_config_data.u8_MeterValuesMeasurandNeed[i] & E_CFG_MVMN_MeterValuesSampledData)
|
||||
== E_CFG_MVMN_MeterValuesSampledData) {
|
||||
if (u8_offline_mv_to_sampled(&json_data.meterValue[0].sampledValue[u8_data_num], (OCPP_MEASURAND_ID)i, mv) != 0u) {
|
||||
if (++u8_data_num >= MeterValuesRequest_SV_NUM) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (u8_data_num == 0u) {
|
||||
return NULL;
|
||||
}
|
||||
return j_get_ocpp_json(OCPP_MeterValues, &json_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 离线 MV 桥接模块级 RAM 上下文(本文件内静态单例)
|
||||
* @details 集中存放:\n
|
||||
* - `last_mv_save_sec[]`:每枪上次**成功**落盘的单调秒,用于 `MeterValueSampleInterval` 节流;\n
|
||||
* - `unsettled`:未结订单补发 MV 时的状态机(与 `s_unsettled_order_data` 生命周期独立,释放订单前须 abort)。
|
||||
*/
|
||||
/** 离线 MV 顺序补发状态(未结补单 / 在线会话内补发共用) */
|
||||
typedef struct {
|
||||
U8_T active; /**< 非 0:正处于某笔单的补发区间 `[seq_next, seq_end]` 内 */
|
||||
U8_T gun; /**< 当前补发枪号 */
|
||||
U32_T u32_charge_start_sec; /**< `xDate2Seconds(StartChargeTime)`,与 FRAM `u32_tx_sn_lo` 一致 */
|
||||
int i32_txid; /**< 本笔 OCPP transactionId(会话补发在读订单失败时沿用) */
|
||||
U32_T seq_start;
|
||||
U32_T seq_next;
|
||||
U32_T seq_end;
|
||||
} S_OCPP_MV_REPLAY_CTX;
|
||||
|
||||
typedef struct {
|
||||
U32_T last_mv_save_sec[GUN_MAX_CNT]; /**< 每枪上次成功落盘时的 `get_current_seconds()`(单调秒),仅用于采样周期节流 */
|
||||
S_OCPP_MV_REPLAY_CTX unsettled; /**< 未结订单 StopTx 前补发 */
|
||||
S_OCPP_MV_REPLAY_CTX session; /**< 在线起充、中途离线再上线:用已有 transactionId 补发 */
|
||||
} S_OCPP_MV_OFFLINE_BRIDGE_CTX;
|
||||
|
||||
/** 模块唯一静态上下文;静态存储期整体零初始化 */
|
||||
static S_OCPP_MV_OFFLINE_BRIDGE_CTX s_ocpp_mv_bridge_ctx;
|
||||
/** 每枪当前是否已为本笔充电完成起充准备(flow 离开充电后清零) */
|
||||
static U8_T s_u8_mv_charge_session_armed[GUN_MAX_CNT];
|
||||
/** 上一周期 BMS flow,用于停充边沿补写一条 MV */
|
||||
static U8_T s_u8_mv_prev_bs_flow[GUN_MAX_CNT];
|
||||
/** map full 日志节流(单调秒) */
|
||||
static U32_T s_u32_mv_map_full_log_sec[GUN_MAX_CNT];
|
||||
/**
|
||||
* @fn v_ocpp_mv_charge_start_sec_from_time
|
||||
* @brief 用订单「开始充电时间」得到与 FRAM 映射一致的 **单路** 32 位键(`xDate2Seconds`)
|
||||
* @param[in] ct 指向 `S_LOG_DATA.StartChargeTime` 的日历时间(与未结订单 `log` 中字段同源)
|
||||
* @param[out] out_sec 成功时为 `xDate2Seconds(ct)`;无效日历或未写入时为 0
|
||||
* @details 双枪在同一秒启动时 `*out_sec` 可能相同,依赖 `S_OCPP_MV_OFFLINE_MAP_ROW.u8_gun_no` 区分;\n
|
||||
* 调用 `u8_ocpp_mv_offline_mv_save` / `map_get` 时第二参固定传 0(与 FRAM 行 `u32_tx_sn_hi` 约定一致,非 64 位拆分)。\n
|
||||
* 对 `ct` 做简单范围检查(年月日非全零、月 1–12、日 1–31),避免未刷新的日志参与映射。
|
||||
*/
|
||||
static void v_ocpp_mv_charge_start_sec_from_time(const Comm_Time *ct, U32_T *out_sec)
|
||||
{
|
||||
unsigned int sec;
|
||||
|
||||
if (out_sec != NULL) {
|
||||
*out_sec = 0u;
|
||||
}
|
||||
if (ct == NULL || out_sec == NULL) {
|
||||
return;
|
||||
}
|
||||
if (ct->iYear == 0u && ct->ucMonth == 0u && ct->ucDay == 0u) {
|
||||
return;
|
||||
}
|
||||
if (ct->ucMonth == 0u || ct->ucMonth > 12u) {
|
||||
return;
|
||||
}
|
||||
if (ct->ucDay == 0u || ct->ucDay > 31u) {
|
||||
return;
|
||||
}
|
||||
sec = xDate2Seconds((Comm_Time *)ct);
|
||||
*out_sec = (U32_T)sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn u8_ocpp_mv_fill_snapshot
|
||||
* @brief 从实时系统数据填充一条待写入 Flash 的 `S_OCPP_MV_OFFLINE_MV` 快照
|
||||
* @param[in] gun 枪索引 `0..GUN_MAX_CNT-1`,与 `u8_ocpp_mv_offline_mv_save` 的 `u8_gun_no` 一致
|
||||
* @param[out] out 输出快照;成功时魔数、版本、时间戳及测量字段均写入
|
||||
* @return 1 成功;0 参数非法或枪号越界
|
||||
* @details 各字段数据源与 `u8_get_ocpp_measurand_data` 对齐说明:\n
|
||||
* - `u32_timestamp_unix`:`GetCurrentTime` + `xDate2Seconds`,与在线 `MeterValues_mag` 的日历时间同源,供补发时 `v_mv_offline_timestamp_unix_to_str` 组 ISO8601(**勿用** `get_current_seconds()`,其为上电起算单调秒)。\n
|
||||
* - `u32_meter_import_wh`:电表累计 Wh(`E_BS_GET_SYS_DATA_MEMTER_ELECT`/10 截断 U32)\n
|
||||
* - `u32_energy_interval_wh`:本事务段增量 Wh(当前累计 − `E_BS_GET_SYS_DATA_LOG_ENERGY` type=1)\n
|
||||
* - `u16_voltage_v_x10` / 电流:`METER_VOL`、`METER_CUR`(电流存 0.01A:原 0.1A×10)\n
|
||||
* - `u16_power_active_w`:由本快照已写入的 `u16_voltage_v_x10`(0.1V)与 `u16_current_import_a_x100`(0.01A)整数估算:`W = vol×cur / 1000`,饱和 65535\n
|
||||
* - `u16_power_offered_01kw` / `u16_current_offered_a_x100`:桩额定功率 0.1kW 单位、桩最大输出电流\n
|
||||
* - SOC:`E_BS_GET_SYS_DATA_SOC_DATA` 当前值,`u8_soc_data_type` 固定 0(周期采样)
|
||||
*/
|
||||
static U8_T u8_ocpp_mv_fill_snapshot(U8_T gun, S_OCPP_MV_OFFLINE_MV *out)
|
||||
{
|
||||
U64_T u64_wh; /**< 电表累计能量 Wh(64 位中间量,避免乘除溢出) */
|
||||
U32_T start_wh; /**< 事务起始电表累计 Wh,来自日志能量 type=1 */
|
||||
U64_T delta; /**< 事务段内增量 Wh = 当前累计 − 起始 */
|
||||
Comm_Time now_ct; /**< 当前 RTC 日历(东八区),与 `BS_ocpp_ctrl.c` 中 `v_ocpp_timestamp_to_str(NULL,…)` 一致 */
|
||||
|
||||
if (out == NULL || gun >= GUN_MAX_CNT) {
|
||||
return 0u;
|
||||
}
|
||||
(void)memset(out, 0, sizeof(*out)); //清空快照
|
||||
out->u32_magic = OCPP_MV_OFFLINE_FLASH_MAGIC; //魔数
|
||||
out->u16_version = (U16_T)OCPP_MV_OFFLINE_FLASH_VER; //版本
|
||||
GetCurrentTime(&now_ct);
|
||||
out->u32_timestamp_unix = (U32_T)xDate2Seconds(&now_ct); /* 日历 epoch 秒,非 get_current_seconds 上电秒 */
|
||||
|
||||
u64_wh = u64_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_MEMTER_ELECT) / 10u; //电表累计能量 Wh
|
||||
out->u32_meter_import_wh = (U32_T)((u64_wh > 0xFFFFFFFFULL) ? 0xFFFFFFFFu : u64_wh); //电表累计能量 Wh 截断
|
||||
|
||||
start_wh = u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_LOG_ENERGY, 1u); //事务起始电表累计 Wh
|
||||
delta = (out->u32_meter_import_wh > start_wh) ? ((U64_T)out->u32_meter_import_wh - (U64_T)start_wh) : 0u; //事务段内增量 Wh
|
||||
out->u32_energy_interval_wh = (U32_T)((delta > 0xFFFFFFFFULL) ? 0xFFFFFFFFu : delta); //事务段内增量 Wh 截断
|
||||
|
||||
out->u16_voltage_v_x10 = u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_METER_VOL, 0u); //输出电压 0.1V
|
||||
out->u16_current_import_a_x100 = (U16_T)(u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_METER_CUR, 0u) * 10u); //输出电流 0.01A
|
||||
{
|
||||
U32_T p_w; /**< `W = (0.1V×值)×(0.01A×值) = u16_voltage_v_x10 * u16_current_import_a_x100 / 1000` */
|
||||
|
||||
p_w = (U32_T)out->u16_voltage_v_x10 * (U32_T)out->u16_current_import_a_x100 / 1000u;
|
||||
if (p_w > 65535u) {
|
||||
p_w = 65535u;
|
||||
}
|
||||
out->u16_power_active_w = (U16_T)p_w;
|
||||
}
|
||||
out->u16_power_offered_01kw = (U16_T)u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_RATED_POWER, 0u); //额定功率 0.1kW
|
||||
out->u16_current_offered_a_x100 = (U16_T)u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_PILE_PUT_PARA, 1u); //最大输出电流 0.01A
|
||||
|
||||
out->u8_soc = (U8_T)u32_bs_get_sys_data(gun, E_BS_GET_SYS_DATA_SOC_DATA, 0u); //SOC%
|
||||
out->u8_soc_data_type = 0u; //SOC 上下文:0 当前 / 1 Transaction.Begin / 2 Transaction.End
|
||||
out->u8_connector_id = (U8_T)(gun + 1u); //连接器号
|
||||
out->u8_evse_id = gun; //EVSE/枪逻辑号
|
||||
return 1u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn v_ocpp_mv_offline_bridge_private_logic
|
||||
* @brief 参见头文件声明;按枪轮询落盘离线 MV
|
||||
*/
|
||||
static U8_T u8_mv_pre_trade_no_pending(const U8_T *p)
|
||||
{
|
||||
if (p == NULL || p[0] == '\0' || p[0] == (U8_T)0xFF) {
|
||||
return 1u;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将当前枪订单快照写入 Flash(不检查 flow,供停充边沿补一条)
|
||||
* @return 1 成功;0 跳过或失败
|
||||
*/
|
||||
static U8_T u8_ocpp_mv_offline_persist_snapshot(U8_T gun, U32_T *seq_out)
|
||||
{
|
||||
S_LOG_DATA s_log;
|
||||
U32_T charge_start_sec;
|
||||
S_OCPP_MV_OFFLINE_MV mv;
|
||||
U32_T seq_local;
|
||||
U8_T sav_ret;
|
||||
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
return 0u;
|
||||
}
|
||||
if (seq_out == NULL) {
|
||||
seq_out = &seq_local;
|
||||
}
|
||||
(void)memset(&s_log, 0, sizeof(s_log));
|
||||
v_meterlog_put_char_log_data(gun, &s_log);
|
||||
v_ocpp_mv_charge_start_sec_from_time(&s_log.StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_ocpp_mv_fill_snapshot(gun, &mv) == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
sav_ret = u8_ocpp_mv_offline_mv_save(gun, charge_start_sec, 0u, &mv, seq_out);
|
||||
if (sav_ret == 0u) {
|
||||
s_ocpp_mv_bridge_ctx.last_mv_save_sec[gun] = (U32_T)get_current_seconds();
|
||||
return 1u;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void v_ocpp_mv_offline_on_charge_start(U8_T gun)
|
||||
{
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
return;
|
||||
}
|
||||
if (s_ocpp_mv_bridge_ctx.session.active != 0u
|
||||
&& s_ocpp_mv_bridge_ctx.session.gun == gun) {
|
||||
(void)memset(&s_ocpp_mv_bridge_ctx.session, 0, sizeof(s_ocpp_mv_bridge_ctx.session));
|
||||
}
|
||||
/* 勿清 FRAM 映射:同枪连续多笔离线充(A→B→A)须各自保留 StartChargeTime 键,上线后逐笔补 MV */
|
||||
s_ocpp_mv_bridge_ctx.last_mv_save_sec[gun] = 0u;
|
||||
s_u32_mv_map_full_log_sec[gun] = 0u;
|
||||
s_u8_mv_charge_session_armed[gun] = 1u;
|
||||
OCPP_OFFLINE_LOG("MV offline: charge start gun=%u (keep pending maps)\r\n", (unsigned)gun);
|
||||
}
|
||||
|
||||
void v_ocpp_mv_offline_bridge_private_logic(U8_T bs_id)
|
||||
{
|
||||
U8_T i; /**< 枪循环变量 `0..GUN_MAX_CNT-1` */
|
||||
U16_T interval_s; /**< 采样周期(秒),来自 OCPP 配置 `MeterValueSampleInterval` */
|
||||
U32_T now; /**< `get_current_seconds()`:上电起单调秒,仅用于与 `MeterValueSampleInterval` 比较节流 */
|
||||
|
||||
(void)bs_id;
|
||||
|
||||
interval_s = (U16_T)60; /* 配置为 0 或未加载时的保守默认,避免除零或过频写 Flash */
|
||||
if (s_ocpp_flash_cfg_data.s_ocpp_config_data.u16_MeterValueSampleInterval != 0u) {
|
||||
interval_s = s_ocpp_flash_cfg_data.s_ocpp_config_data.u16_MeterValueSampleInterval;
|
||||
}
|
||||
|
||||
now = (U32_T)get_current_seconds();
|
||||
for (i = 0u; i < (U8_T)GUN_MAX_CNT; i++) {
|
||||
S_LOG_DATA s_log; /**< 当前枪订单日志快照,含 `StartChargeTime` */
|
||||
U32_T charge_start_sec; /**< `xDate2Seconds(StartChargeTime)`,与 FRAM `u32_tx_sn_lo` 一致 */
|
||||
S_OCPP_MV_OFFLINE_MV mv; /**< 单条待写入 Flash 的 32B 负载 + 帧头由底层封装 */
|
||||
U32_T seq_out; /**< 本笔分配到的该枪单调序号(调试用,此处未回传上层) */
|
||||
U8_T sav_ret; /**< `u8_ocpp_mv_offline_mv_save` 返回值:0 成功,2 映射满,3 Flash,4 FRAM,5 序号不连续等 */
|
||||
|
||||
{
|
||||
U8_T bs_flow = u8_bs_get_flow_state(i);
|
||||
|
||||
/* 停充边沿:补写结束时刻 MV,避免最后一采样周期未落盘 */
|
||||
if (s_u8_mv_prev_bs_flow[i] == 2u && bs_flow != 2u) {
|
||||
U32_T seq_end = 0u;
|
||||
|
||||
if (u8_ocpp_mv_offline_persist_snapshot(i, &seq_end) != 0u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: stop snap gun=%u seq=%lu\r\n",
|
||||
(unsigned)i, (unsigned long)seq_end);
|
||||
}
|
||||
}
|
||||
s_u8_mv_prev_bs_flow[i] = bs_flow;
|
||||
if (bs_flow != 2u) { //枪未充电 跳过
|
||||
s_u8_mv_charge_session_armed[i] = 0u;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (s_u8_mv_charge_session_armed[i] == 0u) {
|
||||
U32_T resume_key = 0u;
|
||||
U32_T resume_seq = 0u;
|
||||
U16_T resume_cnt = 0u;
|
||||
|
||||
(void)memset(&s_log, 0, sizeof(s_log));
|
||||
v_meterlog_put_char_log_data(i, &s_log);
|
||||
v_ocpp_mv_charge_start_sec_from_time(&s_log.StartChargeTime, &resume_key);
|
||||
if (resume_key != 0u
|
||||
&& u8_ocpp_mv_offline_map_get(i, resume_key, 0u, &resume_seq, &resume_cnt) == 0u
|
||||
&& resume_cnt > 0u) {
|
||||
/* 链路抖动/TCP 重连:同一笔充电勿清 map,避免补发从 seq 头反复上送 */
|
||||
s_u8_mv_charge_session_armed[i] = 1u;
|
||||
OCPP_OFFLINE_LOG("MV offline: gun=%u link resume, keep map (%u条) key=0x%08lX\r\n",
|
||||
(unsigned)i, (unsigned)resume_cnt, (unsigned long)resume_key);
|
||||
} else {
|
||||
v_ocpp_mv_offline_on_charge_start(i);
|
||||
}
|
||||
}
|
||||
if (interval_s != 0u && s_ocpp_mv_bridge_ctx.last_mv_save_sec[i] != 0u
|
||||
&& (now - s_ocpp_mv_bridge_ctx.last_mv_save_sec[i]) < (U32_T)interval_s) { //时间间隔未到 跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)memset(&s_log, 0, sizeof(s_log));
|
||||
v_meterlog_put_char_log_data(i, &s_log); //获取订单日志
|
||||
v_ocpp_mv_charge_start_sec_from_time(&s_log.StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) { /* 开始充电时间无效或未写入,无法建立稳定键 */
|
||||
continue;
|
||||
}
|
||||
if (u8_ocpp_mv_fill_snapshot(i, &mv) == 0u) { //填充快照失败 跳过
|
||||
continue;
|
||||
}
|
||||
sav_ret = u8_ocpp_mv_offline_mv_save(i, charge_start_sec, 0u, &mv, &seq_out);
|
||||
if (sav_ret == 0u) {
|
||||
s_ocpp_mv_bridge_ctx.last_mv_save_sec[i] = now;
|
||||
OCPP_OFFLINE_LOG("MV offline: write ok gun=%u seq=%lu key_sec=0x%08lX unix_ts=%lu Wh=%lu Ival=%lu\r\n",
|
||||
(unsigned)i,
|
||||
(unsigned long)seq_out,
|
||||
(unsigned long)charge_start_sec,
|
||||
(unsigned long)mv.u32_timestamp_unix,
|
||||
(unsigned long)mv.u32_meter_import_wh,
|
||||
(unsigned long)mv.u32_energy_interval_wh);
|
||||
} else if (sav_ret == 2u) {
|
||||
U32_T now_log = (U32_T)get_current_seconds();
|
||||
if (s_u32_mv_map_full_log_sec[i] == 0u
|
||||
|| (now_log - s_u32_mv_map_full_log_sec[i]) >= 30u) {
|
||||
s_u32_mv_map_full_log_sec[i] = now_log;
|
||||
OCPP_OFFLINE_LOG("MV offline: map full gun=%u (FRAM rows=%u)\r\n",
|
||||
(unsigned)i, (unsigned)OCPP_MV_OFFLINE_MAP_ROWS);
|
||||
}
|
||||
} else if (sav_ret != 5u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: save err=%u gun=%u\r\n", (unsigned)sav_ret, (unsigned)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void v_ocpp_mv_offline_snap_gun_now(U8_T gun)
|
||||
{
|
||||
U32_T seq_out = 0u;
|
||||
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
return;
|
||||
}
|
||||
if (u8_bs_get_flow_state(gun) != 2u) {
|
||||
return;
|
||||
}
|
||||
if (s_u8_mv_charge_session_armed[gun] == 0u) {
|
||||
v_ocpp_mv_offline_on_charge_start(gun);
|
||||
}
|
||||
if (u8_ocpp_mv_offline_persist_snapshot(gun, &seq_out) != 0u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: ws-fail snap gun=%u seq=%lu\r\n",
|
||||
(unsigned)gun, (unsigned long)seq_out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn v_ocpp_mv_offline_unsettled_abort
|
||||
* @brief 参见头文件;将 `s_ocpp_mv_bridge_ctx.unsettled` 整块清零
|
||||
*/
|
||||
void v_ocpp_mv_offline_unsettled_abort(void)
|
||||
{
|
||||
(void)memset(&s_ocpp_mv_bridge_ctx.unsettled, 0, sizeof(s_ocpp_mv_bridge_ctx.unsettled));
|
||||
}
|
||||
|
||||
U8_T u8_ocpp_mv_offline_map_pending_for_log(const S_LOG_DATA *log)
|
||||
{
|
||||
U32_T charge_start_sec;
|
||||
U32_T seq_start;
|
||||
U16_T count;
|
||||
U8_T gun;
|
||||
|
||||
if (log == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
gun = log->u8_gunNo;
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
gun = 0u;
|
||||
}
|
||||
v_ocpp_mv_charge_start_sec_from_time(&log->StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_ocpp_mv_offline_map_get(gun, charge_start_sec, 0u, &seq_start, &count) != 0u
|
||||
|| count == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
void v_ocpp_mv_offline_session_replay_abort(void)
|
||||
{
|
||||
(void)memset(&s_ocpp_mv_bridge_ctx.session, 0, sizeof(s_ocpp_mv_bridge_ctx.session));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单步推进指定补发上下文,最多发送一条 MeterValues
|
||||
* @return 1 本周期已发或需重试;0 无映射/已全部完成
|
||||
*/
|
||||
static U8_T u8_ocpp_mv_replay_step_ctx(S_OCPP_MV_REPLAY_CTX *rp, U8_T gun, U32_T charge_start_sec, int txid,
|
||||
const char *tag)
|
||||
{
|
||||
U32_T seq_start;
|
||||
U16_T count;
|
||||
S_OCPP_MV_OFFLINE_MV mv;
|
||||
|
||||
if (rp == NULL || charge_start_sec == 0u || txid < 0) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (rp->active == 0u) {
|
||||
if (u8_ocpp_mv_offline_map_get(gun, charge_start_sec, 0u, &seq_start, &count) != 0u || count == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
rp->active = 1u;
|
||||
rp->gun = gun;
|
||||
rp->u32_charge_start_sec = charge_start_sec;
|
||||
rp->i32_txid = txid;
|
||||
rp->seq_start = seq_start;
|
||||
rp->seq_next = seq_start;
|
||||
rp->seq_end = seq_start + (U32_T)count - 1u;
|
||||
OCPP_OFFLINE_LOG("MV offline: %s queue gun=%u txId=%d key_sec=0x%08lX seq %lu-%lu (%u条)\r\n",
|
||||
tag, (unsigned)gun, txid,
|
||||
(unsigned long)charge_start_sec,
|
||||
(unsigned long)seq_start,
|
||||
(unsigned long)rp->seq_end,
|
||||
(unsigned)count);
|
||||
} else if (rp->gun != gun || rp->u32_charge_start_sec != charge_start_sec) {
|
||||
(void)memset(rp, 0, sizeof(*rp));
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (rp->seq_next > rp->seq_end) {
|
||||
(void)u8_ocpp_mv_offline_map_remove(rp->gun, rp->u32_charge_start_sec, 0u);
|
||||
(void)memset(rp, 0, sizeof(*rp));
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (u8_ocpp_mv_offline_mv_read_by_seq(rp->gun, rp->seq_next, &mv) != 0u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: %s read fail gun=%u seq=%lu slot=%lu\r\n",
|
||||
tag, (unsigned)rp->gun, (unsigned long)rp->seq_next,
|
||||
(unsigned long)(rp->seq_next % (U32_T)OCPP_MV_OFFLINE_POOL_MAX_PER_GUN));
|
||||
rp->seq_next++;
|
||||
return 1u;
|
||||
}
|
||||
if (u8_ocpp_mv_offline_mv_is_valid(&mv) == 0u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: %s read skip invalid gun=%u seq=%lu\r\n",
|
||||
tag, (unsigned)rp->gun, (unsigned long)rp->seq_next);
|
||||
rp->seq_next++;
|
||||
return 1u;
|
||||
}
|
||||
if (s32_ocpp_send_meter_values_replay((int)rp->gun, txid, &mv) <= 0) {
|
||||
OCPP_OFFLINE_LOG("MV offline: %s send fail gun=%u seq=%lu\r\n",
|
||||
tag, (unsigned)rp->gun, (unsigned long)rp->seq_next);
|
||||
return 1u;
|
||||
}
|
||||
|
||||
{
|
||||
U32_T total = rp->seq_end - rp->seq_start + 1u;
|
||||
U32_T done;
|
||||
|
||||
rp->seq_next++;
|
||||
done = rp->seq_next - rp->seq_start;
|
||||
OCPP_OFFLINE_LOG("MV offline: %s send ok gun=%u %lu/%lu seq=%lu ts=%lu Wh=%lu\r\n",
|
||||
tag, (unsigned)rp->gun,
|
||||
(unsigned long)done, (unsigned long)total,
|
||||
(unsigned long)(rp->seq_next - 1u),
|
||||
(unsigned long)mv.u32_timestamp_unix,
|
||||
(unsigned long)mv.u32_meter_import_wh);
|
||||
if (rp->seq_next > rp->seq_end) {
|
||||
OCPP_OFFLINE_LOG("MV offline: %s replay done gun=%u total=%lu\r\n",
|
||||
tag, (unsigned)rp->gun, (unsigned long)total);
|
||||
(void)u8_ocpp_mv_offline_map_remove(rp->gun, rp->u32_charge_start_sec, 0u);
|
||||
(void)memset(rp, 0, sizeof(*rp));
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
U8_T u8_ocpp_mv_offline_session_replay_pending(U8_T gun)
|
||||
{
|
||||
S_LOG_DATA log;
|
||||
U32_T charge_start_sec;
|
||||
U32_T seq_start;
|
||||
U16_T count;
|
||||
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
return 0u;
|
||||
}
|
||||
if (s_ocpp_mv_bridge_ctx.session.active != 0u) {
|
||||
return (gun == s_ocpp_mv_bridge_ctx.session.gun) ? 1u : 0u;
|
||||
}
|
||||
if (s_ocpp_mv_bridge_ctx.unsettled.active != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
(void)memset(&log, 0, sizeof(log));
|
||||
if (u8_plat_get_real_order(&log, gun) == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_mv_pre_trade_no_pending(log.u8_preTradeNo) != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
v_ocpp_mv_charge_start_sec_from_time(&log.StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_ocpp_mv_offline_map_get(gun, charge_start_sec, 0u, &seq_start, &count) != 0u || count == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
U8_T u8_ocpp_mv_offline_session_replay_step(U8_T gun)
|
||||
{
|
||||
S_LOG_DATA log;
|
||||
U32_T charge_start_sec;
|
||||
U8_T flow;
|
||||
int txid;
|
||||
|
||||
if (gun >= (U8_T)GUN_MAX_CNT) {
|
||||
return 0u;
|
||||
}
|
||||
if (s_ocpp_mv_bridge_ctx.unsettled.active != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/* 已在补发:仅在本枪推进;其它枪轮询项直接跳过,避免 gun0 空订单 pending 占满发送队列(session_0013) */
|
||||
if (s_ocpp_mv_bridge_ctx.session.active != 0u) {
|
||||
if (gun != s_ocpp_mv_bridge_ctx.session.gun) {
|
||||
return 0u;
|
||||
}
|
||||
txid = s_ocpp_mv_bridge_ctx.session.i32_txid;
|
||||
(void)memset(&log, 0, sizeof(log));
|
||||
if (u8_plat_get_real_order(&log, gun) != 0u) {
|
||||
int live_tx = atoi((const char *)log.u8_preTradeNo);
|
||||
if (live_tx > 0) {
|
||||
s_ocpp_mv_bridge_ctx.session.i32_txid = live_tx;
|
||||
txid = live_tx;
|
||||
}
|
||||
}
|
||||
return u8_ocpp_mv_replay_step_ctx(&s_ocpp_mv_bridge_ctx.session,
|
||||
s_ocpp_mv_bridge_ctx.session.gun,
|
||||
s_ocpp_mv_bridge_ctx.session.u32_charge_start_sec,
|
||||
txid, "session");
|
||||
}
|
||||
|
||||
(void)memset(&log, 0, sizeof(log));
|
||||
if (u8_plat_get_real_order(&log, gun) == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_mv_pre_trade_no_pending(log.u8_preTradeNo) != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
v_ocpp_mv_charge_start_sec_from_time(&log.StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
txid = atoi((const char *)log.u8_preTradeNo);
|
||||
|
||||
flow = u8_bs_get_flow_state(gun);
|
||||
if (flow != 2u && flow != 9u && flow != 10u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
return u8_ocpp_mv_replay_step_ctx(&s_ocpp_mv_bridge_ctx.session, gun, charge_start_sec, txid, "session");
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn u8_ocpp_mv_offline_unsettled_step
|
||||
* @brief 参见头文件;单步推进补发状态机并最多发送一条 WS MeterValues
|
||||
*/
|
||||
U8_T u8_ocpp_mv_offline_unsettled_step(S_LOG_DATA *log)
|
||||
{
|
||||
U32_T charge_start_sec;
|
||||
U8_T gun;
|
||||
int txid;
|
||||
|
||||
if (log == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_ocpp_unsettled_is_live_charging_session(log) != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
gun = log->u8_gunNo;
|
||||
if (gun >= GUN_MAX_CNT) {
|
||||
gun = 0u;
|
||||
}
|
||||
v_ocpp_mv_charge_start_sec_from_time(&log->StartChargeTime, &charge_start_sec);
|
||||
if (charge_start_sec == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_mv_pre_trade_no_pending(log->u8_preTradeNo) != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
txid = atoi((const char *)log->u8_preTradeNo);
|
||||
if (txid < 0) {
|
||||
return 0u;
|
||||
}
|
||||
if (u8_ocpp_mv_offline_map_pending_for_log(log) == 0u) {
|
||||
OCPP_OFFLINE_LOG("MV offline: unsettled no map gun=%u key_sec=0x%08lX txId=%d\r\n",
|
||||
(unsigned)gun, (unsigned long)charge_start_sec, txid);
|
||||
return 0u;
|
||||
}
|
||||
if (s_ocpp_mv_bridge_ctx.session.active != 0u
|
||||
&& s_ocpp_mv_bridge_ctx.session.gun == gun) {
|
||||
/* 同枪在线会话 MV 补发中:勿 abort session 走未结 StopTx(session_0012) */
|
||||
if (u8_ocpp_unsettled_is_live_charging_session(log) != 0u) {
|
||||
return 0u;
|
||||
}
|
||||
v_ocpp_mv_offline_session_replay_abort();
|
||||
}
|
||||
return u8_ocpp_mv_replay_step_ctx(&s_ocpp_mv_bridge_ctx.unsettled, gun, charge_start_sec, txid, "unsettled");
|
||||
}
|
||||
|
||||
#endif /* BS_OCPP_EN */
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @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 */
|
||||
@@ -0,0 +1,363 @@
|
||||
# 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 发送路径
|
||||
|
||||
1. 业务或定时器置位标志 → **`ocpp_send_mag_ctrl()`** 轮询;
|
||||
2. **`send_mag(gunNo, cmd, ack_data, type)`**:
|
||||
- 构造 **[2, messageId, Action, payload]**(主动)或 **[3, …]**(应答);
|
||||
- `switch(cmd)` 调用各 **`*_mag`** 生成 **`cJSON` body**(实际多在 **`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 接收路径
|
||||
|
||||
1. **`ocpp_recv()`**(`bs_websocket.c`)收到文本帧 → **`recv_mags()`**;
|
||||
2. 解析 JSON:区分 **CALL / CALLRESULT / CALLERROR**;
|
||||
3. **CALL**:根据 **Action** 字符串映射 **`OCPP_CMD`** → **`unpack`** 填充 **`OCPP_DATA_RECV_ACK`**,必要时 **`send_mag` 返回 CallResult**;
|
||||
4. **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. 与其它目录的典型调用关系(示意)
|
||||
|
||||
```mermaid
|
||||
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 未结逻辑:
|
||||
|
||||
1. **`u16_get_unsettled_order_count()`** 为 0 则直接返回 0(无单)。
|
||||
2. **`u8_type == 1`** 时只返回数量,不读内容。
|
||||
3. 否则 **`v_get_unsettled_order_indexes`** 取 **队首** 索引 **`currentIndex`**。
|
||||
4. **`v_read_chg_order_record(s_log, currentIndex)`** 从 Flash 读出完整 **`S_LOG_DATA`**。
|
||||
5. **`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`):
|
||||
|
||||
1. **`OCPP_MALLOC`** + **`memset`**,**`u8_unsettled_auth_phase = NONE`**,**`u32_unsettled_last_auth_s` / `u32_unsettled_last_stx_s` 清零**;
|
||||
2. **`s8_plat_get_unsettled_order(..., 0)`** 填入内容;
|
||||
3. **`u8_user_id` 为空** → **`v_safe_free_unsettled_order_data()`**,返回;
|
||||
4. **尚无流水号**(`u8_ocpp_is_pre_trade_no_pending == 1`):调用 **`u8_unsettled_pretrade_pipeline`**
|
||||
- **`OCPP_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**)。
|
||||
5. **已有流水号**(`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`** 依次:
|
||||
|
||||
1. **`v_check_temp_chg_record_valid(gunNo)`**:标志为 **`TEMP_CHG_FLAG_UNSETTLED` 或 SETTLED 类有效值** 则认为有条目;
|
||||
2. **`v_read_temp_chg_record_from_eeprom(gunNo, s_log, NULL)`** 读出 **`S_LOG_DATA`**;
|
||||
3. 强制 **`s_log->e_stop_reason[0] = E_FAULT_13073`**(异常掉电),用于 **StopTransaction** 映射为 **`PowerLoss`** 等;
|
||||
4. **`v_clear_temp_chg_record_in_eeprom(gunNo)`**:读完即清标志(下次不再重复读);
|
||||
5. **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 流程示意图(合并视角)
|
||||
|
||||
```mermaid
|
||||
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)**) | 相同 |
|
||||
|
||||
**实现层面已知耦合点**(便于排查问题):
|
||||
|
||||
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 数据是否混枪。
|
||||
2. **Flash 读单**后立即 **`u8_platform_settlement_confirm`**:若 Flash 读失败,可能导致索引已删。
|
||||
3. **`StartTransaction_res`** 未结算分支已对 **Blocked/Invalid 等** 拒绝 **`preTradeNo`** 写入;**Authorize** 非 **Accepted** 会丢弃内存上下文。
|
||||
4. **`StopTransaction` 成功后立即 `free`**,与 **`StopTransaction_res`** 无联动;若需严格「应答后再删单」,需架构层增强。
|
||||
|
||||
---
|
||||
|
||||
## 13. 维护建议
|
||||
|
||||
1. **新增 OCPP Action**:在 **`OCPP_CMD`**、**`ocpp_cmd_str`**、**`send_mag` switch**、**接收分发**、**json 组帧/解析** 五处同步扩展。
|
||||
2. **新增 Configuration Key**:在 **`bs_ocpp_str.h`**、`OCPP_CONFIG` 结构体、**Get/Change** 分支中一并添加,并处理 Flash 默认值与长度。
|
||||
3. **测量项顺序**:**`OCPP_MEASURAND_ID` 顺序不可随意插入**,否则配置位图与 **`OCPP_measurand_str`** 错位。
|
||||
4. **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`**。
|
||||
5. **补单链路变更**:同步核对 **第 10~12 节** 与 **`bs_public_impl.c` / `meter_calculate_impl.c` / `unsettled_order_mng`** 的一致性。
|
||||
6. **离线 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` 目录一致,若后续增减文件请同步更新第二节表格。*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,395 @@
|
||||
#ifndef _BS_OCPP_CONFIG_H_
|
||||
#define _BS_OCPP_CONFIG_H_
|
||||
|
||||
#include "bs_ocpp_ctrl.h"
|
||||
|
||||
|
||||
#if BS_OCPP_EN //配置参数 分可选、必选
|
||||
|
||||
//枚举 每个连接器相对于其电表(若无电表则相对于电网连接)的相序
|
||||
typedef enum
|
||||
{
|
||||
ENUM_NotApplicable, // 不适用(单相交流/直流充电桩,非三相场景)
|
||||
ENUM_Unknown, // 未知(设备未检测到相序,需人工排查接线)
|
||||
ENUM_RST, // 标准参考相序(L1-R、L2-S、L3-T,默认推荐配置)
|
||||
ENUM_RTS, // 反向参考相序(L1-R、L2-T、L3-S,相序反转需修正)
|
||||
ENUM_SRT, // 反向240度旋转(L1-S、L2-R、L3-T,特殊场景接线)
|
||||
ENUM_STR, // 标准120度旋转(L1-S、L2-T、L3-R,特殊场景接线)
|
||||
ENUM_TRS, // 标准240度旋转(L1-T、L2-R、L3-S,特殊场景接线)
|
||||
ENUM_TSR // 反向120度旋转(L1-T、L2-S、L3-R,特殊场景接线)
|
||||
} OCPP_CONNECTOR_PHASE;
|
||||
|
||||
|
||||
//枚举MeterValues测量项 顺序固定!不可变更! (定义顺序关联了配置参数结构体里相关数组的下标)
|
||||
typedef enum
|
||||
{
|
||||
// ------------------- 能量累计值(Register)- 文档7.31核心能量类 -------------------
|
||||
ENUM_EnergyActiveExportRegister, // 有功电能输出累计值(Wh)- 设备向电网输出的总有功电能累计
|
||||
ENUM_EnergyActiveImportRegister, // 有功电能输入累计值(Wh)- 电网向设备输入的总有功电能累计
|
||||
ENUM_EnergyReactiveExportRegister, // 无功电能输出累计值(VARh)- 设备向电网输出的总无功电能累计
|
||||
ENUM_EnergyReactiveImportRegister, // 无功电能输入累计值(VARh)- 电网向设备输入的总无功电能累计
|
||||
|
||||
// ------------------- 能量时段值(Interval)- 文档7.31时段能量类 -------------------
|
||||
ENUM_EnergyActiveExportInterval, // 有功电能输出时段值(Wh)- 单个时钟对齐时段内输出的有功电能
|
||||
ENUM_EnergyActiveImportInterval, // 有功电能输入时段值(Wh)- 单个时钟对齐时段内输入的有功电能
|
||||
ENUM_EnergyReactiveExportInterval, // 无功电能输出时段值(VARh)- 单个时钟对齐时段内输出的无功电能
|
||||
ENUM_EnergyReactiveImportInterval, // 无功电能输入时段值(VARh)- 单个时钟对齐时段内输入的无功电能
|
||||
|
||||
// ------------------- 功率类 - 文档7.31功率计量类 -------------------
|
||||
ENUM_PowerActiveExport, // 有功功率输出(W)- 设备向电网输出的实时有功功率
|
||||
ENUM_PowerActiveImport, // 有功功率输入(W)- 电网向设备输入的实时有功功率
|
||||
ENUM_PowerOffered, // 提供的功率(W)- 充电桩可向EV提供的最大可用有功功率
|
||||
ENUM_PowerReactiveExport, // 无功功率输出(VAR)- 设备向电网输出的实时无功功率
|
||||
ENUM_PowerReactiveImport, // 无功功率输入(VAR)- 电网向设备输入的实时无功功率
|
||||
ENUM_PowerFactor, // 功率因数(无单位,0~1)- 实时有功功率与视在功率的比值
|
||||
|
||||
// ------------------- 电流类 - 文档7.31电流计量类 -------------------
|
||||
ENUM_CurrentImport, // 输入电流(A)- 电网向设备输入的实时电流
|
||||
ENUM_CurrentExport, // 输出电流(A)- 设备向电网输出的实时电流
|
||||
ENUM_CurrentOffered, // 提供的电流(A)- 充电桩可向EV提供的最大可用电流
|
||||
|
||||
// ------------------- 电气参数类 - 文档7.31基础电气参数 -------------------
|
||||
ENUM_Voltage, // 电压(V)- 充电桩输出端的实时线电压
|
||||
ENUM_Frequency, // 电网频率(Hz)- 接入电网的实时频率(50/60Hz)
|
||||
|
||||
// ------------------- 物理/状态量类 - 文档7.31非电气计量项 -------------------
|
||||
ENUM_Temperature, // 温度(℃)- 充电桩/EV电池的实时温度(文档标注为℃/℉,默认℃)
|
||||
ENUM_SoC, // 电池剩余电量(%)- EV电池的State of Charge,0~100%
|
||||
ENUM_RPM, // 转速(转/分钟)- 冷却风扇/泵等设备的实时转速
|
||||
|
||||
// 统计最大数量标志,用于定义配置参数中数组空间的大小
|
||||
ENUM_measurand_Num,
|
||||
} OCPP_MEASURAND_ID;
|
||||
|
||||
/*
|
||||
//MeterValues测量项结构体
|
||||
typedef struct
|
||||
{
|
||||
OCPP_MEASURAND_ID enum_measurandID; //枚举变量
|
||||
const char* str_data; //对应字符串内容
|
||||
|
||||
}OCPP_MEASURAND_INFO;
|
||||
*/
|
||||
|
||||
/*
|
||||
// MeterValues测量项 结构体数组(枚举值 ↔ 标准字符串 映射表,只读不可修改。顺序对应枚举顺序,固定不可变更)
|
||||
const OCPP_MEASURAND_INFO OCPP_measurand_info[] =
|
||||
*/
|
||||
// MeterValues测量项 串数组(标准字符串 只读不可修改。顺序对应枚举顺序,固定不可变更)
|
||||
extern const char *OCPP_measurand_str[];
|
||||
|
||||
// 配置参数 结构体 前半部分必选 后半部分可选
|
||||
typedef struct
|
||||
{
|
||||
// ------------------- 必选boolean类型(U8_T) -------------------
|
||||
U8_T u8_AuthorizeRemoteTxRequests; // 远程启动交易是否需要预授权
|
||||
U8_T u8_LocalAuthorizeOffline; // 离线时是否允许本地授权启动交易
|
||||
U8_T u8_LocalPreAuthorize; // 在线时是否无需等待中央系统授权即可本地启动交易
|
||||
U8_T u8_StopTransactionOnEVSideDisconnect; // EV端断开时是否自动停止交易
|
||||
U8_T u8_StopTransactionOnInvalidId; // 收到无效授权状态时是否停止当前交易
|
||||
U8_T u8_UnlockConnectorOnEVSideDisconnect; // EV端断开时是否解锁充电点侧连接器
|
||||
U8_T u8_LocalAuthListEnabled; // 是否启用本地授权列表
|
||||
|
||||
// ------------------- 必选integer类型(U16_T) -------------------
|
||||
U16_T u16_ClockAlignedDataInterval; // 时钟对齐数据间隔(秒)
|
||||
U16_T u16_ConnectionTimeOut; // 准备状态超时自动取消交易时间(秒)
|
||||
U16_T u16_GetConfigurationMaxKeys; // 单个GetConfiguration请求最大键数量
|
||||
U16_T u16_HeartbeatInterval; // 心跳间隔(秒)
|
||||
U16_T u16_MeterValueSampleInterval; // 计量数据采样间隔(秒)
|
||||
U16_T u16_NumberOfConnectors; // 充电点物理连接器数量
|
||||
U16_T u16_ResetRetries; // 充电点复位失败后重试次数
|
||||
U16_T u16_TransactionMessageAttempts; // 交易消息提交失败后重试次数
|
||||
U16_T u16_TransactionMessageRetryInterval; // 交易消息重试间隔(秒)
|
||||
U16_T u16_LocalAuthListMaxLength; // 本地授权列表最大存储条目数
|
||||
U16_T u16_SendLocalListMaxLength; // 单个SendLocalList请求最大发送条目数
|
||||
U16_T u16_ChargeProfileMaxStackLevel; // 充电配置最大堆叠层级
|
||||
U16_T u16_ChargingScheduleMaxPeriods; // 单个充电计划最大时段数
|
||||
U16_T u16_MaxChargingProfilesInstalled; // 同时可安装的充电配置最大数量
|
||||
|
||||
// ------------------- 必选CSL类型 -------------------------------
|
||||
OCPP_CONNECTOR_PHASE enum_ConnectorPhaseRotation[3]; //各枪电表的连接相序
|
||||
/*合并 MeterValuesAlignedData、
|
||||
MeterValuesSampledData、
|
||||
StopTxnAlignedData、
|
||||
StopTxnSampledData
|
||||
MeterValues测量项在以上数据单元是否需要
|
||||
bit 0 :MeterValuesAlignedData是否需要
|
||||
bit 1 :MeterValuesSampledData是否需要
|
||||
bit 2 :StopTxnAlignedData是否需要
|
||||
bit 3 :StopTxnSampledData是否需要
|
||||
*/
|
||||
U8_T u8_MeterValuesMeasurandNeed[ENUM_measurand_Num];
|
||||
|
||||
/*支持的功能配置文件列表
|
||||
bit 0 :Core(核心) 是否支持
|
||||
bit 1 :FirmwareManagement(固件管理) 是否支持
|
||||
bit 2 :LocalAuthListManagement(本地授权列表管理) 是否支持
|
||||
bit 3 :Reservation(预约) 是否支持
|
||||
bit 4 :SmartCharging(智能充电) 是否支持
|
||||
bit 5 :RemoteTrigger(远程触发) 是否支持
|
||||
*/
|
||||
U8_T u8_SupportedFeatureProfiles;
|
||||
|
||||
/*充电调度支持的物理量列表
|
||||
bit 0 :'Current'(电流) 是否支持
|
||||
bit 1 :'Power'(功率) 是否支持
|
||||
*/
|
||||
U8_T ChargingScheduleAllowedChargingRateUnit;
|
||||
|
||||
|
||||
// ------------------- 可选boolean类型(U8_T:0=false,非0=true) -------------------
|
||||
U8_T u8_AllowOfflineTxForUnknownId; // 是否允许未知ID用户离线启动交易
|
||||
U8_T u8_AuthorizationCacheEnabled; // 是否启用本地授权缓存
|
||||
U8_T u8_ReserveConnectorZeroSupported; // 是否支持对0号连接器进行预约
|
||||
U8_T u8_ConnectorSwitch3to1PhaseSupported; // 是否支持交易期间三相切单相充电
|
||||
|
||||
// ------------------- 可选integer类型(U16_T,单位见注释) -------------------
|
||||
U16_T u16_BlinkRepeat; // 充电点指示灯闪烁次数
|
||||
U16_T u16_ConnectorPhaseRotationMaxLength; // 相位旋转配置最大条目数(补充:可选核心项)
|
||||
U16_T u16_LightIntensity; // 充电点灯光亮度(百分比)
|
||||
U16_T u16_MaxEnergyOnInvalidId; // 无效ID用户最大允许充电能量(Wh)
|
||||
U16_T u16_MeterValuesAlignedDataMaxLength; // 时钟对齐计量项列表最大长度(可选)
|
||||
U16_T u16_MeterValuesSampledDataMaxLength; // 采样计量项列表最大长度(可选)
|
||||
U16_T u16_MinimumStatusDuration; // 状态稳定后发送通知的最短持续时间(秒)
|
||||
U16_T u16_StopTxnAlignedDataMaxLength; // 交易停止时对齐计量项最大长度(可选)
|
||||
U16_T u16_StopTxnSampledDataMaxLength; // 交易停止时采样计量项最大长度(可选)
|
||||
U16_T u16_SupportedFeatureProfilesMaxLength; // 支持功能配置文件列表最大长度(可选)
|
||||
U16_T u16_WebSocketPingInterval; // WebSocket Ping间隔(秒,0表示禁用)
|
||||
|
||||
//------------------私有配置------------------------
|
||||
char QRCodeConnector1[NUM_64];
|
||||
char QRCodeConnector2[NUM_64];
|
||||
|
||||
} OCPP_CONFIG;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
S8_T s8_ChangeAvailability[GUN_MAX_CNT]; //更改枪可用性 <0 不可用 >=0 可用 ||>0时可扩充枪预约状态
|
||||
|
||||
int card_ver; //卡号版本
|
||||
|
||||
char chargePointVendor[OCPP_CiString20]; //供应商信息 可改
|
||||
}S_OCPP_CHARGE_CFG_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
Comm_Time time;
|
||||
OCPP_CONFIG s_ocpp_config_data; //数据内容
|
||||
S_OCPP_CHARGE_CFG_DATA s_ocpp_charge_data; //充电相关数据
|
||||
}S_OCPP_FLASH_CFG_DATA;
|
||||
|
||||
extern S_OCPP_FLASH_CFG_DATA s_ocpp_flash_cfg_data;
|
||||
#endif
|
||||
|
||||
#if BS_OCPP_EN //OCPP可配置参数
|
||||
/*
|
||||
U8 timeNUm;
|
||||
1、将数据类型定义枚举表
|
||||
2、创建枚举和字符串的映射结构体; 包含枚举变量、支持标志、数据类型、对应字符串内容、
|
||||
3、编写根据接收字符串查找映射表对应枚举的函数
|
||||
4、根据枚举码 使用switch 将模块搭建出来
|
||||
typedef struct
|
||||
{
|
||||
1、枚举
|
||||
2、支持标志 U8_T
|
||||
3\数据 数字/字符
|
||||
4、对应字符串内容 char*;
|
||||
|
||||
}METERV_MAG;
|
||||
const METERV_MAG met[] =
|
||||
{
|
||||
{1,“a },
|
||||
{2.},
|
||||
|
||||
|
||||
}
|
||||
0 1
|
||||
U8_T u8_action(type =0 get 。1 set ,meiju ,char *v)
|
||||
|
||||
meiju eMMC_HIGH_VOLTAGE_RANGE(const char *mag )
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
//保存unknown的Key串
|
||||
extern char *unknown_tmpbuf[GetConfiguration_NUM];
|
||||
|
||||
|
||||
//OCPP可配置参数枚举
|
||||
typedef enum
|
||||
{
|
||||
ENUM_NULL = 0,
|
||||
//Core Profile
|
||||
ENUM_ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID, /* 可选 允许未知用户ID的离线交易 (布尔: 0/1) */
|
||||
ENUM_AUTHORIZATION_CACHE_ENABLED, /* 可选 启用本地授权缓存 (布尔: 0/1) */
|
||||
ENUM_AUTHORIZE_REMOTE_TX_REQUESTS, /* 必选 允许远程启动充电事务 (布尔: 0/1) */
|
||||
ENUM_BLINK_REPEAT, /* 可选 充电桩指示灯闪烁次数 (整数: 次数) */
|
||||
ENUM_CLOCK_ALIGNED_DATA_INTERVAL, /* 必选 时钟对齐的数据上报间隔 (整数: 秒) */
|
||||
ENUM_CONNECTION_TIME_OUT, /* 必选 TCP连接超时时间 (整数: 秒) */
|
||||
ENUM_CONNECTOR_PHASE_ROTATION, /* 必选 连接器相位旋转配置 (字符串: L1/L2/L3顺序) */
|
||||
ENUM_CONNECTOR_PHASE_ROTATION_MAX_LENGTH, /* 可选 相位旋转配置最大长度 (整数: 字节数) */
|
||||
ENUM_GET_CONFIGURATION_MAX_KEYS, /* 必选 GetConfiguration请求最大返回Key数 (整数: 数量) */
|
||||
ENUM_HEARTBEAT_INTERVAL, /* 必选 心跳间隔 (整数: 秒) */
|
||||
ENUM_LIGHT_INTENSITY, /* 可选 充电桩灯光亮度 (整数: 百分比) */
|
||||
ENUM_LOCAL_AUTHORIZE_OFFLINE, /* 必选 本地离线授权模式 (布尔: 0/1) */
|
||||
ENUM_LOCAL_PRE_AUTHORIZE, /* 必选 本地预授权模式 (布尔: 0/1) */
|
||||
ENUM_MAX_ENERGY_ON_INVALID_ID, /* 可选 无效ID用户的最大允许充电能量 (整数: Wh) */
|
||||
ENUM_METER_VALUES_ALIGNED_DATA, /* 必选 对齐数据上报的测量项列表 (字符串: 逗号分隔的measurand值) */
|
||||
ENUM_METER_VALUES_ALIGNED_DATA_MAX_LENGTH, /* 可选 对齐数据列表最大长度 (整数: 字符数) */
|
||||
ENUM_METER_VALUES_SAMPLED_DATA, /* 必选 采样数据上报的测量项列表 (字符串: 逗号分隔的measurand值) */
|
||||
ENUM_METER_VALUES_SAMPLED_DATA_MAX_LENGTH, /* 可选 采样数据列表最大长度 (整数: 字符数) */
|
||||
ENUM_METER_VALUE_SAMPLE_INTERVAL, /* 必选 采样间隔 (整数: 秒) */
|
||||
ENUM_MINIMUM_STATUS_DURATION, /* 可选 状态最小持续时间 (整数: 秒) */
|
||||
ENUM_NUMBER_OF_CONNECTORS, /* 必选 充电桩连接器数量 (整数: 数量) */
|
||||
ENUM_RESET_RETRIES, /* 必选 复位重试次数 (整数: 次数) */
|
||||
ENUM_STOP_TRANSACTION_ON_EV_SIDE_DISCONNECT, /* 必选 EV端断开时停止事务 (布尔: 0/1) */
|
||||
ENUM_STOP_TRANSACTION_ON_INVALID_ID, /* 必选 无效ID时停止事务 (布尔: 0/1) */
|
||||
ENUM_STOP_TXN_ALIGNED_DATA, /* 必选 事务停止时的对齐数据项 (字符串: 逗号分隔的measurand值) */
|
||||
ENUM_STOP_TXN_ALIGNED_DATA_MAX_LENGTH, /* 可选 停止事务对齐数据最大长度 (整数: 字符数) */
|
||||
ENUM_STOP_TXN_SAMPLED_DATA, /* 必选 停止事务时的采样数据项 (字符串: 逗号分隔的measurand值) */
|
||||
ENUM_STOP_TXN_SAMPLED_DATA_MAX_LENGTH, /* 可选 停止事务采样数据最大长度 (整数: 字符数) */
|
||||
ENUM_SUPPORTED_FEATURE_PROFILES, /* 必选 支持的功能配置文件 (字符串: 逗号分隔的FeatureProfile值) */
|
||||
ENUM_SUPPORTED_FEATURE_PROFILES_MAX_LENGTH, /* 可选 功能配置文件列表最大长度 (整数: 字符数) */
|
||||
ENUM_TRANSACTION_MESSAGE_ATTEMPTS, /* 必选 事务消息重试次数 (整数: 次数) */
|
||||
ENUM_TRANSACTION_MESSAGE_RETRY_INTERVAL, /* 必选 事务消息重试间隔 (整数: 秒) */
|
||||
ENUM_UNLOCK_CONNECTOR_ON_EV_SIDE_DISCONNECT, /* 必选 EV端断开时解锁连接器 (布尔: 0/1) */
|
||||
ENUM_WEB_SOCKET_PING_INTERVAL, /* 可选 WebSocket Ping间隔 (整数: 秒) */
|
||||
|
||||
//Local Auth List Management Profile
|
||||
ENUM_LOCAL_AUTH_LIST_ENABLED, /* 必选 启用本地授权列表 (布尔: 0/1) */
|
||||
ENUM_LOCAL_AUTH_LIST_MAX_LENGTH, /* 必选 本地授权列表的最大条目数 (整数) */
|
||||
ENUM_SEND_LOCAL_LIST_MAX_LENGTH, /* 必选 发送本地列表的最大条目数 (整数) */
|
||||
|
||||
//Reservation Profile
|
||||
ENUM_RESERVE_CONNECTOR_ZERO_SUPPORTED, /* 可选 是否支持预留连接器0 (布尔: 0/1) */
|
||||
|
||||
//Smart Charging Profile
|
||||
ENUM_CHARGE_PROFILE_MAX_STACK_LEVEL, /* 必选 充电配置最大堆叠层级 (整数) */
|
||||
ENUM_CHARGING_SCHEDULE_ALLOWED_RATE_UNIT, /* 必选 充电速率允许单位 (字符串: "A"或"W") */
|
||||
ENUM_CHARGING_SCHEDULE_MAX_PERIODS, /* 必选 充电计划最大时段数 (整数) */
|
||||
ENUM_CONNECTOR_SWITCH_3TO1_PHASE_SUPPORTED, /* 可选 是否支持三相切单相充电 (布尔: 0/1) */
|
||||
ENUM_MAX_CHARGING_PROFILES_INSTALLED, /* 必选 最大可安装充电配置数 (整数) */
|
||||
|
||||
//私有配置
|
||||
ENUM_QRCODE_TEXT_CONNECTOR1, /* 枪1二维码 */
|
||||
ENUM_QRCODE_TEXT_CONNECTOR2, /* 枪2二维码 */
|
||||
|
||||
|
||||
//num-end OCPP配置参数的总数量,作为在根据字符串寻找对应枚举变量时的上限;未来添加新变量时需在此位置前添加;
|
||||
ENUM_OCPP_CONFIG,
|
||||
|
||||
|
||||
}OCPP_CONFIG_KEY_ID;
|
||||
|
||||
//OCPP可配置参数是否支持
|
||||
typedef enum
|
||||
{
|
||||
ENUM_SUPPORT_NOT = 0,
|
||||
ENUM_SUPPORT_YES,
|
||||
}OCPP_CONFIG_SUPPORT;
|
||||
|
||||
//OCPP可配置参数数据类型
|
||||
typedef enum
|
||||
{
|
||||
ENUM_TYPE_INT = 0,
|
||||
ENUM_TYPE_CSL,
|
||||
}OCPP_CONFIG_TYPE;
|
||||
|
||||
//OCPP修改配置参数返回操作结果
|
||||
typedef enum
|
||||
{
|
||||
ENUM_Rejected = 0,
|
||||
ENUM_Accepted,
|
||||
ENUM_Required,
|
||||
ENUM_NotSupported,
|
||||
}OCPP_SETCONFIG_RET;
|
||||
|
||||
//OCPP获取配置参数返回只读结果
|
||||
typedef enum
|
||||
{
|
||||
ENUM_RnW = 0,
|
||||
ENUM_R_Only,
|
||||
}OCPP_GETCONFIG_RET;
|
||||
|
||||
//OCPP可配置参数结构体
|
||||
typedef struct
|
||||
{
|
||||
OCPP_CONFIG_KEY_ID u8_keyID; //1,枚举变量
|
||||
OCPP_CONFIG_SUPPORT u8_support; //2,支持标志 0:不支持,1:支持。
|
||||
OCPP_CONFIG_TYPE u8_type; //3,数据类型 1:integer,2:CSL。
|
||||
const char* str_data; //4、对应字符串内容 char*;
|
||||
|
||||
}OCPP_CONFIG_INFO;
|
||||
|
||||
//OCPP可配置参数操作类型
|
||||
typedef enum
|
||||
{
|
||||
ENUM_TYPE_GET = 0,
|
||||
ENUM_TYPE_SET,
|
||||
}OCPP_CONFIG_OPERATE;
|
||||
|
||||
//OCPP可配置参数结构体数组
|
||||
extern const OCPP_CONFIG_INFO OCPP_config_info[];
|
||||
|
||||
/*******************************************************************************
|
||||
* 名称: u8_find_OCPP_configID
|
||||
* 功能: 根据字符串查找对应OCPP配置参数的枚举变量
|
||||
* 参数: *mag OCPP配置参数的键的字符串
|
||||
* 返回: 返回该键在OCPP_CONFIG_KEY_ID的对应枚举量,若返回ENUM_NULL则未找到该键
|
||||
* 说明:
|
||||
******************************************************************************/
|
||||
OCPP_CONFIG_KEY_ID u8_find_OCPP_configID(const char *mag);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* 名称: find_OCPP_configMag
|
||||
* 功能: 根据配置参数的枚举号 找对应字符串 并赋值
|
||||
* 参数: keyID 配置参数的枚举号
|
||||
* *mag 要赋值串的指针头
|
||||
* u16_len 被赋值的key串最大长度
|
||||
* 返回: -
|
||||
* 说明:
|
||||
******************************************************************************/
|
||||
void find_OCPP_configMag(OCPP_CONFIG_KEY_ID keyID, char *mag, U16_T u16_len);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* 名称: u8_check_OCPP_configSupport
|
||||
* 功能: 根据配置参数的枚举号 找是否支持
|
||||
* 参数: keyID 配置参数的枚举号
|
||||
* 返回: 是否支持标志
|
||||
* 说明:
|
||||
******************************************************************************/
|
||||
OCPP_CONFIG_SUPPORT u8_check_OCPP_configSupport(OCPP_CONFIG_KEY_ID keyID);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* 名称: u8_OCPP_config_operate
|
||||
* 功能: 对OCPP配置参数进行读或写
|
||||
* 参数: operate_type 操作类型,ENUM_TYPE_GET--获取参数,ENUM_TYPE_GET--设置参数
|
||||
* *key_mag 配置参数键的字符串
|
||||
* *value_mag 配置参数值的字符串
|
||||
* u16_len 配置参数值的字符串的最大长度
|
||||
* 返回: 参数修改时:0-Rejected 1-Accepted 2-RebootRequired 3-NotSupported
|
||||
* 参数获取时 0-readonly-false 1-readonly-ture
|
||||
* 说明:
|
||||
******************************************************************************/
|
||||
U8_T u8_OCPP_config_operate(OCPP_CONFIG_OPERATE operate_type, const char *key_mag, char *value_mag , U16_T u16_len);
|
||||
|
||||
|
||||
/****************************************************
|
||||
*函数名称: v_ocpp_init_config_data()
|
||||
*功能说明: 程序运行前 配置参数初始化
|
||||
*参数说明:
|
||||
* 输入参数:
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_ocpp_init_config_data();
|
||||
|
||||
void v_flash_save_ocpp_cfg_data(void);
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,528 @@
|
||||
#ifndef _BS_OCPP_JSON_DATA_H_
|
||||
#define _BS_OCPP_JSON_DATA_H_
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "publicdata/public_define.h"
|
||||
#include "bs_ocpp_str.h"
|
||||
|
||||
#if BS_OCPP_EN //OCPP平台使能
|
||||
#include "cJSON.h"
|
||||
#include <string.h>
|
||||
|
||||
#define OCPP_LOGMSG_DEBUG_EN 1 //打印标志
|
||||
#define OCPP_PRINTF_DEBUG_EN 1 //printf打印标志
|
||||
|
||||
#define MeterValuesRequest_MV_NUM 1 //电表数据组数量
|
||||
#define MeterValuesRequest_SV_NUM 8 //数据内容数量
|
||||
|
||||
#define GetConfiguration_NUM 20 //平台获取数据最大数量
|
||||
|
||||
#define StopTransactionRequest_TD_NUM 1 //停止充电 数据组数量
|
||||
#define StopTransactionRequest_SV_NUM 5 //停止充电 数据内容数量
|
||||
|
||||
#define E_CFG_MVMN_MeterValuesAlignedData (0x01)
|
||||
#define E_CFG_MVMN_MeterValuesSampledData (0x02)
|
||||
#define E_CFG_MVMN_StopTxnAlignedData (0x04)
|
||||
#define E_CFG_MVMN_StopTxnSampledData (0x08)
|
||||
|
||||
#define OCPP_DATE_LEN (30) //OCPP时间格式 数据长度 ISO 8601 格式。
|
||||
|
||||
#define OCPP_CiString20 (20+1)
|
||||
#define OCPP_CiString30 (30+1)
|
||||
#define OCPP_CiString25 (25+1)
|
||||
#define OCPP_CiString50 (50+1)
|
||||
#define OCPP_CiString100 (100+1)
|
||||
#define OCPP_CiString255 (255+1)
|
||||
#define OCPP_CiString300 (300+1)
|
||||
#define OCPP_CiString500 (500+1)
|
||||
|
||||
typedef enum{
|
||||
OCPP_NULL = 0,
|
||||
OCPP_BootNotification, //引导通知 桩->平台
|
||||
OCPP_StatusNotification, //状态通知 桩->平台
|
||||
OCPP_Heartbeat, //心跳 桩->平台
|
||||
OCPP_Authorize, //授权 桩->平台
|
||||
OCPP_MeterValues, //采样信息上传 桩->平台
|
||||
OCPP_StartTransaction, //开始交易 桩->平台
|
||||
OCPP_StopTransaction, //停止交易 桩->平台
|
||||
|
||||
OCPP_RemoteStartTransaction,//平台下发启动充电 平台->桩
|
||||
OCPP_RemoteStopTransaction, //平台下发停止充电 平台->桩
|
||||
OCPP_UnlockConnector, //解枪锁 平台->桩
|
||||
OCPP_UpdateFirmware, //固件升级 平台->桩
|
||||
OCPP_Reset, //重启 平台->桩
|
||||
OCPP_CancelReservation, //取消已经预定的充电桩 平台->桩
|
||||
OCPP_ChangeAvailability, //设置枪可用状态 平台->桩
|
||||
OCPP_ChangeConfiguration, //更改参数配置 平台->桩
|
||||
OCPP_TriggerMessage, //平台下发数据召测 平台->桩
|
||||
OCPP_GetConfiguration, //平台获取参数配置 平台->桩
|
||||
OCPP_ClearCache, //清楚桩内缓存 一般为白名单 平台->桩
|
||||
|
||||
OCPP_SendLocalList, //平台下发本地缓存列表
|
||||
OCPP_GetLocalListVersion, //获取卡号版本
|
||||
OCPP_GetDiagnostics, //获取诊断日志文件
|
||||
OCPP_DiagnosticsStatusNotification, /* 诊断日志上传状态 桩->平台 */
|
||||
OCPP_FirmwareStatusNotification, /* 固件升级状态 桩->平台 */
|
||||
}OCPP_CMD;
|
||||
|
||||
typedef enum{
|
||||
OCPP_StatusNotification_errorCode = 0, //StatusNotification errorCode
|
||||
OCPP_StatusNotification_statusCode,
|
||||
OCPP_RemoteStartTransactionResponse_statusCode,
|
||||
OCPP_RemoteStopTransactionResponse_statusCode,
|
||||
OCPP_StopTransactionRequest_reason,
|
||||
OCPP_UnlockConnectorResponse_stateCode,
|
||||
OCPP_DiagnosticsStatusNotification_status, /* 与 OCPP_DIAG_NOTIFY_PENDING_E 下标对齐:0 Idle … */
|
||||
OCPP_FirmwareStatusNotification_status, /* 与 OCPP_FW_NOTIFY_PENDING_E 下标对齐:0 Idle … */
|
||||
}OCPP_ENUM_DATA_TYPE;
|
||||
|
||||
//状态错误信息
|
||||
typedef enum{
|
||||
OCPP_ERRORCODE_ConnectorLockFailure, //连接器锁定故障
|
||||
OCPP_ERRORCODE_EVCommunicationError, //EV通讯故障
|
||||
OCPP_ERRORCODE_GroundFailure, //接地故障
|
||||
OCPP_ERRORCODE_HighTemperature, //高温
|
||||
OCPP_ERRORCODE_InternalError, //内部错误
|
||||
OCPP_ERRORCODE_LocalListConflict, //本地列表冲突
|
||||
OCPP_ERRORCODE_NoError, //无错误
|
||||
OCPP_ERRORCODE_OtherError, //其他错误
|
||||
OCPP_ERRORCODE_OverCurrentFailure, //过电流故障
|
||||
OCPP_ERRORCODE_PowerMeterFailure, //功率表故障
|
||||
OCPP_ERRORCODE_PowerSwitchFailure, //电源开关故障
|
||||
OCPP_ERRORCODE_ReaderFailure, //读卡器故障
|
||||
OCPP_ERRORCODE_ResetFailure, //重置失败
|
||||
OCPP_ERRORCODE_UnderVoltage, //电压过低
|
||||
OCPP_ERRORCODE_OverVoltage, //电压过高
|
||||
OCPP_ERRORCODE_WeakSignal, //信号弱
|
||||
}OCPP_STATUSNOTIFICATION_ERRORCODE;
|
||||
|
||||
//状态信息
|
||||
typedef enum{
|
||||
OCPP_STATUSCODE_Available, //空闲
|
||||
OCPP_STATUSCODE_Preparing, //准备
|
||||
OCPP_STATUSCODE_Charging, //充电
|
||||
OCPP_STATUSCODE_SuspendedEVSE, //暂停EVSE
|
||||
OCPP_STATUSCODE_SuspendedEV, //暂停EV
|
||||
OCPP_STATUSCODE_Finishing, //完成
|
||||
OCPP_STATUSCODE_Reserved, //预留
|
||||
OCPP_STATUSCODE_Unavailable, //不空闲
|
||||
OCPP_STATUSCODE_Faulted, //故障
|
||||
}OCPP_STATUSNOTIFICATION_STATUSCODE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//必选
|
||||
char chargePointVendor[OCPP_CiString20]; //"type": "string","maxLength": 20
|
||||
char chargePointModel[OCPP_CiString20]; //"type": "string","maxLength": 20
|
||||
//可选
|
||||
char chargePointSerialNumber[OCPP_CiString25]; //"type": "string","maxLength": 25
|
||||
char chargeBoxSerialNumber[OCPP_CiString25]; //"type": "string","maxLength": 25
|
||||
char firmwareVersion[OCPP_CiString20]; //"type": "string","maxLength": 50
|
||||
char iccid[OCPP_CiString30]; //"type": "string","maxLength": 20
|
||||
char imsi[OCPP_CiString30]; //"type": "string","maxLength": 20
|
||||
char meterType[OCPP_CiString25]; //"type": "string","maxLength": 25
|
||||
char meterSerialNumber[OCPP_CiString25]; //"type": "string","maxLength": 25
|
||||
|
||||
}BootNotificationRequest;
|
||||
typedef struct
|
||||
{
|
||||
//必选
|
||||
char status[OCPP_CiString20]; //"type": "string","enum": ["Accepted","Pending","Rejected"]
|
||||
char currentTime[OCPP_DATE_LEN]; //"type": "string","format": "date-time" 2022-09-09T16:40:00+04:00
|
||||
int interval; //"type": "integer"
|
||||
//可选
|
||||
}BootNotificationResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//必选
|
||||
int connectorId; //连接器id
|
||||
char errorCode[OCPP_CiString30]; //枚举 ocpp_errorCode 当前的错误状态代码。应当表示充电桩或连接器当前的错误情况。
|
||||
/*"ConnectorLockFailure", 充电枪锁止机构故障
|
||||
"EVCommunicationError", 与电动车通信失败
|
||||
"GroundFailure", 接地故障
|
||||
"HighTemperature", 设备温度过高
|
||||
"InternalError", 内部错误
|
||||
"LocalListConflict", 本地授权列表冲突
|
||||
"NoError", 无错误
|
||||
"OtherError", 其他未分类错误
|
||||
"OverCurrentFailure", 过流故障
|
||||
"PowerMeterFailure", 电表故障
|
||||
"PowerSwitchFailure", 电源开关故障
|
||||
"ReaderFailure", 读卡器故障
|
||||
"ResetFailure", 复位失败
|
||||
"UnderVoltage", 欠压故障
|
||||
"OverVoltage", 过压故障
|
||||
"WeakSignal" 信号弱*/
|
||||
char status[OCPP_CiString20]; //枚举 statusCode 连接器的当前状态。
|
||||
/*"Available", 可用
|
||||
"Preparing", 准备中 插枪
|
||||
"Charging", 充电中
|
||||
"SuspendedEVSE", 充电桩主动暂停
|
||||
"SuspendedEV", 电动车请求暂停
|
||||
"Finishing", 结束中
|
||||
"Reserved", 已预约
|
||||
"Unavailable", 不可用
|
||||
"Faulted" 故障*/
|
||||
//可选
|
||||
char info[OCPP_CiString30]; //与错误相关的其他自由格式的信息
|
||||
char timestamp[OCPP_DATE_LEN]; //日期时间 状态变化发生的时间戳
|
||||
char vendorId[OCPP_CiString50]; //制造商标识。
|
||||
char vendorErrorCode[OCPP_CiString50]; //制造商特定的错误代码。
|
||||
}StatusNotificationRequest;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char value[OCPP_CiString20]; //必选 计量值的实际数值,类型为字符串以支持任意长度的数值。
|
||||
char context[OCPP_CiString20]; //可选 可选字段,表示计量上下文,例如 Sample.Periodic(定期采样)或 Sample.Alarm(报警采样)
|
||||
/*"Interruption.Begin", 中断开始 标识充电过程被主动中断的起始时刻(如用户手动停止、系统故障触发)。
|
||||
"Interruption.End", 中断结束 标识中断状态恢复的时间点(如故障修复后重新启动充电)。
|
||||
"Sample.Clock", 时钟同步采样 按固定时钟时间(如整点)触发的数据采样,与时间严格对齐。
|
||||
"Sample.Periodic", 定期采样 按固定间隔(如每15分钟)触发的数据采样,与起始时间无关。
|
||||
"Transaction.Begin", 交易开始 标识充电交易的起始时刻(如用户刷卡启动充电)。
|
||||
"Transaction.End", 交易结束 标识充电交易的终止时刻(如用户拔枪或余额耗尽)。
|
||||
"Trigger", 触发器事件 表示由外部信号(如远程控制命令)触发的动作,需结合具体 triggerMessage 使用。
|
||||
"Other" 其他事件
|
||||
*/
|
||||
char format[OCPP_CiString20]; //可选 可选字段,表示计量值的格式,例如 Raw 或 SignedData。/*"Raw" 明文,"SignedData" 签名加密*/
|
||||
char measurand[OCPP_CiString50]; //可选 可选字段,表示计量对象,例如 Energy.Active.Import.Register (导入的有效能量)。
|
||||
/*"Energy.Active.Export.Register", 累计放电电量(电表读数)Wh 或 kWh
|
||||
"Energy.Active.Import.Register", 累计充电电量(电表读数)Wh 或 kWh
|
||||
"Energy.Reactive.Export.Register", 无功电能导出累计值 (单位: kvarh/varh)
|
||||
"Energy.Reactive.Import.Register", 无功电能导入累计值 (单位: kvarh/varh)
|
||||
"Energy.Active.Export.Interval", 时间间隔内有功电能导出 (单位: kWh/Wh)
|
||||
"Energy.Active.Import.Interval", 时间间隔内有功电能导入 (单位: kWh/Wh)
|
||||
"Energy.Reactive.Export.Interval", 时间间隔内无功电能导出 (单位: kvarh/varh)
|
||||
"Energy.Reactive.Import.Interval", 时间间隔内无功电能导入 (单位: kvarh/varh)
|
||||
"Power.Active.Export", 瞬时值 有功功率(放电功率)W
|
||||
"Power.Active.Import", 瞬时值 有功功率(充电功率)W
|
||||
"Power.Offered", 充电桩可提供的最大功率 (单位: W/kW)
|
||||
"Power.Reactive.Export", 瞬时无功功率导出 (单位: var/kvar)
|
||||
"Power.Reactive.Import", 瞬时无功功率导入 (单位: var/kvar)
|
||||
"Power.Factor", 功率因数 (无单位, 范围: -1.0~1.0)
|
||||
"Current.Import", 输入电流(充电电流)
|
||||
"Current.Export", 输出电流(放电电流)
|
||||
"Current.Offered", 充电桩可提供的最大电流 (单位: A)
|
||||
"Voltage", 电压(相电压或线电压)
|
||||
"Frequency", 电网频率
|
||||
"Temperature", 温度(如充电桩内部温度)
|
||||
"SoC", 电池电量状态(State of Charge)
|
||||
"RPM" 电机转速 (单位: RPM)*/
|
||||
char phase[OCPP_CiString20]; //可选 可选字段,表示电力相位,例如 L1, L2, L3。
|
||||
/*"L1",
|
||||
"L2",
|
||||
"L3",
|
||||
"N",
|
||||
"L1-N",
|
||||
"L2-N",
|
||||
"L3-N",
|
||||
"L1-L2",
|
||||
"L2-L3",
|
||||
"L3-L1"*/
|
||||
char location[OCPP_CiString20]; //可选 可选字段,表示计量装置的位置,例如 Inlet, Outlet.
|
||||
/*"Cable",电缆
|
||||
"EV",车辆
|
||||
"Inlet",入口
|
||||
"Outlet",出口
|
||||
"Body"整桩 */
|
||||
char unit[OCPP_CiString20]; //可选 可选字段,表示计量单位,例如 Wh, kWh, V, A.
|
||||
/*"Wh",
|
||||
"kWh",
|
||||
"varh",
|
||||
"kvarh",
|
||||
"W",
|
||||
"kW",
|
||||
"VA",
|
||||
"kVA",
|
||||
"var",
|
||||
"kvar",
|
||||
"A",
|
||||
"V",
|
||||
"K",
|
||||
"Celcius",
|
||||
"Celsius", 摄氏度
|
||||
"Fahrenheit", 华氏度
|
||||
"Percent" 百分比*/
|
||||
}SampledValue;
|
||||
typedef struct
|
||||
{
|
||||
int connectorId; //必须 表示充电连接器的唯一标识符。
|
||||
int transactionId; //可选 当前充电事务的唯一标识符。这是可选字段,只有在当前有正在进行的充电事务时才有值。
|
||||
struct
|
||||
{
|
||||
char timestamp[OCPP_DATE_LEN]; //必须 记录取样数据的时间,ISO 8601 格式。
|
||||
SampledValue sampledValue[MeterValuesRequest_SV_NUM]; //必须 最大同时上送3种数据 代表一个或多个取样值,每个取样值包含以下字段:
|
||||
}meterValue[MeterValuesRequest_MV_NUM]; //必须 这是一个数组,包含一组计量值。每个计量值包含一个时间戳和对应的一组取样值。
|
||||
}MeterValuesRequest;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int connectorId; //可选 指示发起交易的连接器ID,即充电桩的具体插口编号。
|
||||
char idTag[OCPP_CiString20]; //必选 标识电动汽车用户或车主的唯一ID标识,用于识别用户。。
|
||||
struct
|
||||
{
|
||||
int chargingProfileId; //必选 充电配置的唯一标识符
|
||||
int transactionId; //可选 关联的事务ID,用于将此配置与特定充电事务关联
|
||||
int stackLevel; //必选 配置优先级,数值越高优先级越高
|
||||
char chargingProfilePurpose[OCPP_CiString25]; //必选 定义配置的用途
|
||||
/* "ChargePointMaxProfile", 充电桩最大功率配置
|
||||
"TxDefaultProfile", 默认事务配置
|
||||
"TxProfile" 特定事务配置*/
|
||||
char chargingProfileKind[OCPP_CiString20]; //必选 定义配置的时间类型
|
||||
/* "Absolute", 绝对时间配置
|
||||
"Recurring", 循环配置
|
||||
"Relative" 相对时间配置*/
|
||||
char recurrencyKind[OCPP_CiString20]; //可选 仅当chargingProfileKind为"Recurring"时有效
|
||||
/* "Daily",每天
|
||||
"Weekly" 每周*/
|
||||
char validFrom[OCPP_DATE_LEN]; //可选 "date-time"配置生效的开始时间。如果不存在,则配置文件在充电桩收到后立即有效。当ChargingProfilePurpose是TxProfile时,请勿使用。
|
||||
char validTo[OCPP_DATE_LEN]; //可选 "date-time"配置失效的结束时间。如果不存在,则该配置文件在被另一个 配置文件替换之前一直有效。当ChargingProfilePurpose是TxProfile时,请勿使用。
|
||||
struct
|
||||
{
|
||||
int duration; //可选 持续时间
|
||||
char startSchedule[OCPP_DATE_LEN]; //可选 "date-time" 充电计划开始时间
|
||||
char chargingRateUnit[OCPP_CiString20]; //必选 充电速率单位
|
||||
/* "A",
|
||||
"W"*/
|
||||
struct
|
||||
{
|
||||
int startPeriod; //必选 时段开始时间(相对于充电计划开始时间的秒数)
|
||||
float limit; //必选 该时段的最大充电速率
|
||||
int numberPhases; //可选 使用的相数(1-3)
|
||||
}chargingSchedulePeriod[3]; //必选 充电计划时段数组
|
||||
float minChargingRate; //可选 最小充电速率
|
||||
}chargingSchedule; //必选 充电计划配置
|
||||
}chargingProfile; //可选 充电配置参数,用于指定充电行为。
|
||||
}RemoteStartTransactionRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted"接受,"Rejected"拒绝
|
||||
}RemoteStartTransactionResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int transactionId; //当前充电事务的唯一标识符。
|
||||
}RemoteStopTransactionRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted"接受,"Rejected"拒绝
|
||||
}RemoteStopTransactionResponse;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int connectorId; //必选 指示发起交易的连接器ID,即充电桩的具体插口编号。
|
||||
int meterStart; //必选 交易开始时的电表读数,这通常是充电开始时的初始能量值。 Wh
|
||||
int reservationId; //可选 如果交易基于事先的预定进行,此字段包含预定的ID。
|
||||
char idTag[OCPP_CiString20]; //必选 标识电动汽车用户或车主的唯一ID标识,通过RFID卡或其他识别机制获取。
|
||||
char timestamp[OCPP_DATE_LEN]; //必选 "date-time" 交易开始的时间戳,表示交易开始的准确时间。
|
||||
}StartTransactionRequest;
|
||||
typedef struct
|
||||
{
|
||||
int transactionId; //必选 中央系统生成的唯一交易ID,用于后续的交易管理和追踪。
|
||||
struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //必选 指示idTag的当前状态,例如是否被接受、是否过期等。Accepted、Blocked、Expired、Invalid、ConcurrentTx
|
||||
char expiryDate[OCPP_DATE_LEN]; //可选 idTag有效期的截止日期
|
||||
char parentIdTag[OCPP_CiString20]; //可选 如果存在关联账户,这是父idTag。
|
||||
}idTagInfo; //必选 包含对idTag的验证和状态信息,其中包括:
|
||||
}StartTransactionResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int meterStop; //必选 交易结束时的电表读数,这通常是充电结束时的最终能量值 Wh
|
||||
int transactionId; //必选 要停止的充电事务的唯一标识符
|
||||
char idTag[OCPP_CiString20]; //可选 标识电动汽车用户或车主的唯一ID标识。
|
||||
char timestamp[OCPP_DATE_LEN]; //必选 交易结束的时间戳,表示交易停止的准确时间
|
||||
char reason[OCPP_CiString20]; //可选 指示停止充电的原因,这可以包括:正常结束、紧急停止等情况。 Local、Remote、EmergencyStop、EVDisconnected、HardReset、SoftReset、deAuthorized、Expired、EVConnectTimeout、Other
|
||||
struct
|
||||
{
|
||||
char timestamp[OCPP_DATE_LEN]; //必选
|
||||
SampledValue sampledValue[StopTransactionRequest_SV_NUM]; //必选
|
||||
}transactionData[StopTransactionRequest_TD_NUM]; //可选 包含与充电交易相关的更详细的数据,如中途的数据采集点
|
||||
}StopTransactionRequest;
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //必选 指示idTag的当前状态,例如是否被接受、是否过期等。Accepted、Blocked、Expired、Invalid、ConcurrentTx
|
||||
char expiryDate[OCPP_DATE_LEN]; //可选 idTag有效期的截止日期。
|
||||
char parentIdTag[OCPP_CiString20]; //可选 果存在关联账户,这是父idTag。
|
||||
}idTagInfo;
|
||||
}StopTransactionResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int connectorId;
|
||||
}UnlockConnectorRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Unlocked","UnlockFailed","NotSupported"
|
||||
}UnlockConnectorResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int retries; //可选
|
||||
int retryInterval; //可选
|
||||
char retrieveDate[OCPP_DATE_LEN]; //必选
|
||||
char location[OCPP_CiString255]; //必选
|
||||
}UpdateFirmwareRequest;
|
||||
typedef struct
|
||||
{
|
||||
int test;
|
||||
}UpdateFirmwareResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char type[OCPP_CiString20]; // "Hard","Soft"
|
||||
}ResetRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted","Rejected"
|
||||
}ResetResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char idTag[OCPP_CiString20];
|
||||
}AuthorizeRequest;
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
char expiryDate[OCPP_DATE_LEN]; //可选
|
||||
char parentIdTag[OCPP_CiString20]; //可选
|
||||
char status[OCPP_CiString20]; //必选
|
||||
/*"Accepted", 请求已被接受
|
||||
"Blocked", 请求被拒绝(因策略限制)
|
||||
"Expired", 授权凭证已过期
|
||||
"Invalid", 凭证无效或格式错误
|
||||
"ConcurrentTx" 存在并发事务(同一车辆/用户已在充电)*/
|
||||
}idTagInfo;
|
||||
|
||||
}AuthorizeResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int reservationId;
|
||||
}CancelReservationRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted","Rejected"
|
||||
}CancelReservationResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int connectorId;
|
||||
char type[OCPP_CiString20]; //"Inoperative","Operative"
|
||||
}ChangeAvailabilityRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted","Rejected",Scheduled"
|
||||
}ChangeAvailabilityResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char key[OCPP_CiString50];
|
||||
char value[OCPP_CiString300]; //
|
||||
}ChangeConfigurationRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted","Rejected","RebootRequired","NotSupported"
|
||||
}ChangeConfigurationResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int connectorId; //可选
|
||||
char requestedMessage[OCPP_CiString50]; //必选 "BootNotification","DiagnosticsStatusNotification","FirmwareStatusNotification","Heartbeat","MeterValues","StatusNotification"
|
||||
}TriggerMessageRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20]; //"Accepted","Rejected","NotImplemented"
|
||||
}TriggerMessageResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char key[GetConfiguration_NUM][OCPP_CiString50]; //array
|
||||
}GetConfigurationRequest;
|
||||
typedef struct
|
||||
{
|
||||
char unknownKey[GetConfiguration_NUM][OCPP_CiString50]; //array
|
||||
struct
|
||||
{
|
||||
int readonly; //必选
|
||||
char key[OCPP_CiString50]; //必选
|
||||
char value[OCPP_CiString100]; //可选
|
||||
}configurationKey[GetConfiguration_NUM];
|
||||
}GetConfigurationResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int res;
|
||||
}ClearCacheRequest;
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20];
|
||||
}ClearCacheResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char idTag[OCPP_CiString30];
|
||||
struct
|
||||
{
|
||||
char expiryDate[OCPP_DATE_LEN];
|
||||
char parentIdTag[OCPP_CiString20];
|
||||
char status[OCPP_CiString20];
|
||||
}idTagInfo;
|
||||
}LocalAuthorizationList;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int listVersion;
|
||||
char updateType[OCPP_CiString30];
|
||||
|
||||
int List_num;
|
||||
LocalAuthorizationList *list;
|
||||
}SendLocalListRequest;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char status[OCPP_CiString20];
|
||||
}SendLocalListResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int listVersion;
|
||||
}GetLocalListVersionResponse;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int retries; //可选
|
||||
int retryInterval; //可选
|
||||
char location[OCPP_CiString255]; //必须
|
||||
char startTime[OCPP_DATE_LEN]; //可选
|
||||
char stopTime[OCPP_DATE_LEN]; //可选
|
||||
}GetDiagnosticsRequest;
|
||||
typedef struct
|
||||
{
|
||||
char fileName[OCPP_CiString50];
|
||||
}GetDiagnosticsResponse;
|
||||
|
||||
typedef struct {
|
||||
char status[OCPP_CiString20];
|
||||
} DiagnosticsStatusNotificationRequest;
|
||||
|
||||
/** 与 DiagnosticsStatusNotification 载荷相同,仅 Action 名不同 */
|
||||
typedef DiagnosticsStatusNotificationRequest FirmwareStatusNotificationRequest;
|
||||
|
||||
cJSON* j_get_ocpp_json(OCPP_CMD cmd , void *data);
|
||||
int get_ocpp_data(OCPP_CMD cmd , void *data ,cJSON *root);
|
||||
|
||||
OCPP_CMD e_get_recv_json_cmd(char* cmd_str);
|
||||
const char *s_get_ocpp_cmd_str(OCPP_CMD cmd);
|
||||
|
||||
const char *s_get_ocpp_enum_data_str(OCPP_ENUM_DATA_TYPE type ,int cmd);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifndef _BS_OCPP_STR_H_
|
||||
#define _BS_OCPP_STR_H_
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "publicdata/public_define.h"
|
||||
|
||||
#if BS_OCPP_EN //OCPP平台使能
|
||||
|
||||
#define OCPP_STR_SamplePeriodic "Sample.Periodic" //周期上送 Periodic delivery
|
||||
#define OCPP_STR_TransactionBegin "Transaction.Begin" //交易开始
|
||||
#define OCPP_STR_TransactionEnd "Transaction.End" //交易停止
|
||||
|
||||
#define STR_TRUE "TRUE"
|
||||
#define STR_true "true"
|
||||
#define STR_1 "1"
|
||||
#define STR_FALSE "FALSE"
|
||||
#define STR_false "false"
|
||||
#define STR_0 "0"
|
||||
|
||||
/*
|
||||
#define OCPP_STR_EnergyActiveImportRegister "Energy.Active.Import.Register" //累计充电电量 单位kWh
|
||||
#define OCPP_STR_PowerActiveImport "Power.Active.Import" //充电功率 单位W
|
||||
#define OCPP_STR_CurrentImport "Current.Import" //充电电流 单位A
|
||||
#define OCPP_STR_Voltage "Voltage" //电压 单位V
|
||||
#define OCPP_STR_SOC "SoC" //电池电量状态(State of Charge)
|
||||
#define OCPP_STR_Temperature "Temperature" //温度(桩内)
|
||||
*/
|
||||
#define OCPP_STR_EnergyActiveExportRegister "Energy.Active.Export.Register" // 有功电能输出累计值(Wh)
|
||||
#define OCPP_STR_EnergyActiveImportRegister "Energy.Active.Import.Register" // 有功电能输入累计值(Wh)
|
||||
#define OCPP_STR_EnergyReactiveExportRegister "Energy.Reactive.Export.Register" // 无功电能输出累计值(VARh)
|
||||
#define OCPP_STR_EnergyReactiveImportRegister "Energy.Reactive.Import.Register" // 无功电能输入累计值(VARh)
|
||||
#define OCPP_STR_EnergyActiveExportInterval "Energy.Active.Export.Interval" // 有功电能输出时段值(Wh)
|
||||
#define OCPP_STR_EnergyActiveImportInterval "Energy.Active.Import.Interval" // 有功电能输入时段值(Wh)
|
||||
#define OCPP_STR_EnergyReactiveExportInterval "Energy.Reactive.Export.Interval" // 无功电能输出时段值(VARh)
|
||||
#define OCPP_STR_EnergyReactiveImportInterval "Energy.Reactive.Import.Interval" // 无功电能输入时段值(VARh)
|
||||
#define OCPP_STR_PowerActiveExport "Power.Active.Export" // 有功功率输出(W)
|
||||
#define OCPP_STR_PowerActiveImport "Power.Active.Import" // 有功功率输入(W)
|
||||
#define OCPP_STR_PowerOffered "Power.Offered" // 充电桩可提供的最大有功功率(W)
|
||||
#define OCPP_STR_PowerReactiveExport "Power.Reactive.Export" // 无功功率输出(VAR)
|
||||
#define OCPP_STR_PowerReactiveImport "Power.Reactive.Import" // 无功功率输入(VAR)
|
||||
#define OCPP_STR_PowerFactor "Power.Factor" // 功率因数(无单位,0~1)
|
||||
#define OCPP_STR_CurrentImport "Current.Import" // 输入电流(A)
|
||||
#define OCPP_STR_CurrentExport "Current.Export" // 输出电流(A)
|
||||
#define OCPP_STR_CurrentOffered "Current.Offered" // 充电桩可提供的最大电流(A)
|
||||
#define OCPP_STR_Voltage "Voltage" // 实时线电压(V)
|
||||
#define OCPP_STR_Frequency "Frequency" // 电网实时频率(Hz)
|
||||
#define OCPP_STR_Temperature "Temperature" // 设备/电池实时温度(℃)
|
||||
#define OCPP_STR_SOC "SoC" // 电池剩余电量(%)
|
||||
#define OCPP_STR_RPM "RPM" // 风扇转速(转/分钟)
|
||||
|
||||
#define OCPP_STR_CurrentRequest "Current.Request" // 请求电流
|
||||
#define OCPP_STR_VoltageRequest "Voltage.Request" // 请求电压值
|
||||
|
||||
#define OCPP_STR_Raw "Raw" //明文(不加密)
|
||||
|
||||
#define OCPP_STR_Body "Body" //整桩
|
||||
#define OCPP_STR_EV "EV" //整桩
|
||||
#define OCPP_STR_Outlet "Outlet" //出口
|
||||
|
||||
#define OCPP_STR_L1 "L1" //
|
||||
#define OCPP_STR_L2 "L2" //
|
||||
#define OCPP_STR_L3 "L3" //
|
||||
|
||||
#define OCPP_STR_Wh "Wh"
|
||||
#define OCPP_STR_kWh "kWh" //
|
||||
#define OCPP_STR_W "W" //
|
||||
#define OCPP_STR_V "V" //
|
||||
#define OCPP_STR_A "A" //
|
||||
#define OCPP_STR_Celsius "Celsius" // 摄氏度
|
||||
#define OCPP_STR_Percent "Percent" //百分比*/
|
||||
|
||||
#define OCPP_STR_Accepted "Accepted" //接受
|
||||
#define OCPP_STR_Rejected "Rejected" //拒绝
|
||||
#define OCPP_STR_RebootRequired "RebootRequired" //需要重启
|
||||
#define OCPP_STR_NotSupported "NotSupported" //不支持
|
||||
|
||||
#define OCPP_STR_Blocked "Blocked" //阻塞
|
||||
#define OCPP_STR_Expired "Expired" //过期
|
||||
#define OCPP_STR_Invalid "Invalid" //无效
|
||||
#define OCPP_STR_ConcurrentTx "ConcurrentTx" //并发事务
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 核心配置参数(数据类型与功能说明)
|
||||
// --------------------------------------------------------
|
||||
#define KEY_ALLOW_OFFLINE_TX_FOR_UNKNOWN_ID "AllowOfflineTxForUnknownId" /* 可选 允许未知用户ID的离线交易 (布尔: 0/1) */
|
||||
#define KEY_AUTHORIZATION_CACHE_ENABLED "AuthorizationCacheEnabled" /* 可选 启用本地授权缓存 (布尔: 0/1) */
|
||||
#define KEY_AUTHORIZE_REMOTE_TX_REQUESTS "AuthorizeRemoteTxRequests" /* 必选 允许远程启动充电事务 (布尔: 0/1) */
|
||||
#define KEY_BLINK_REPEAT "BlinkRepeat" /* 可选 充电桩指示灯闪烁次数 (整数: 次数) */
|
||||
#define KEY_CLOCK_ALIGNED_DATA_INTERVAL "ClockAlignedDataInterval" /* 必选 时钟对齐的数据上报间隔 (整数: 秒) */
|
||||
#define KEY_CONNECTION_TIMEOUT "ConnectionTimeOut" /* 必选 TCP连接超时时间 (整数: 秒) */
|
||||
#define KEY_CONNECTOR_PHASE_ROTATION "ConnectorPhaseRotation" /* 必选 连接器相位旋转配置 (字符串: L1/L2/L3顺序) */
|
||||
#define KEY_CONNECTOR_PHASE_ROTATION_MAX_LENGTH "ConnectorPhaseRotationMaxLength" /* 可选 相位旋转配置最大长度 (整数: 字节数) */
|
||||
#define KEY_GET_CONFIGURATION_MAX_KEYS "GetConfigurationMaxKeys" /* 必选 GetConfiguration请求最大返回Key数 (整数: 数量) */
|
||||
#define KEY_HEARTBEAT_INTERVAL "HeartbeatInterval" /* 必选 心跳间隔 (整数: 秒) */
|
||||
#define KEY_LIGHT_INTENSITY "LightIntensity" /* 可选 充电桩灯光亮度 (整数: 百分比) */
|
||||
#define KEY_LOCAL_AUTHORIZE_OFFLINE "LocalAuthorizeOffline" /* 必选 本地离线授权模式 (布尔: 0/1) */
|
||||
#define KEY_LOCAL_PRE_AUTHORIZE "LocalPreAuthorize" /* 必选 本地预授权模式 (布尔: 0/1) */
|
||||
#define KEY_MAX_ENERGY_ON_INVALID_ID "MaxEnergyOnInvalidId" /* 可选 无效ID用户的最大允许充电能量 (整数: Wh) */
|
||||
#define KEY_METER_VALUES_ALIGNED_DATA "MeterValuesAlignedData" /* 必选 对齐数据上报的测量项列表 (字符串: 逗号分隔的measurand值) */
|
||||
#define KEY_METER_VALUES_ALIGNED_DATA_MAX_LENGTH "MeterValuesAlignedDataMaxLength" /* 可选 对齐数据列表最大长度 (整数: 字符数) */
|
||||
#define KEY_METER_VALUES_SAMPLED_DATA "MeterValuesSampledData" /* 必选 采样数据上报的测量项列表 (字符串: 逗号分隔的measurand值) */
|
||||
#define KEY_METER_VALUES_SAMPLED_DATA_MAX_LENGTH "MeterValuesSampledDataMaxLength" /* 可选 采样数据列表最大长度 (整数: 字符数) */
|
||||
#define KEY_METER_VALUE_SAMPLE_INTERVAL "MeterValueSampleInterval" /* 必选 采样间隔 (整数: 秒) */
|
||||
#define KEY_MINIMUM_STATUS_DURATION "MinimumStatusDuration" /* 可选 状态最小持续时间 (整数: 秒) */
|
||||
#define KEY_NUMBER_OF_CONNECTORS "NumberOfConnectors" /* 必选 充电桩连接器数量 (整数: 数量) */
|
||||
#define KEY_RESET_RETRIES "ResetRetries" /* 必选 复位重试次数 (整数: 次数) */
|
||||
#define KEY_STOP_TXN_ON_EV_SIDE_DISCONNECT "StopTransactionOnEVSideDisconnect" /* 必选 EV端断开时停止事务 (布尔: 0/1) */
|
||||
#define KEY_STOP_TXN_ON_INVALID_ID "StopTransactionOnInvalidId" /* 必选 无效ID时停止事务 (布尔: 0/1) */
|
||||
#define KEY_STOP_TXN_ALIGNED_DATA "StopTxnAlignedData" /* 必选 事务停止时的对齐数据项 (字符串: 逗号分隔的measurand值) */
|
||||
#define KEY_STOP_TXN_ALIGNED_DATA_MAX_LENGTH "StopTxnAlignedDataMaxLength" /* 可选 停止事务对齐数据最大长度 (整数: 字符数) */
|
||||
#define KEY_STOP_TXN_SAMPLED_DATA "StopTxnSampledData" /* 必选 停止事务时的采样数据项 (字符串: 逗号分隔的measurand值) */
|
||||
#define KEY_STOP_TXN_SAMPLED_DATA_MAX_LENGTH "StopTxnSampledDataMaxLength" /* 可选 停止事务采样数据最大长度 (整数: 字符数) */
|
||||
#define KEY_SUPPORTED_FEATURE_PROFILES "SupportedFeatureProfiles" /* 必选 支持的功能配置文件 (字符串: 逗号分隔的FeatureProfile值) */
|
||||
#define KEY_SUPPORTED_FEATURE_PROFILES_MAX_LENGTH "SupportedFeatureProfilesMaxLength" /* 可选 功能配置文件列表最大长度 (整数: 字符数) */
|
||||
#define KEY_TRANSACTION_MESSAGE_ATTEMPTS "TransactionMessageAttempts" /* 必选 事务消息重试次数 (整数: 次数) */
|
||||
#define KEY_TRANSACTION_MESSAGE_RETRY_INTERVAL "TransactionMessageRetryInterval" /* 必选 事务消息重试间隔 (整数: 秒) */
|
||||
#define KEY_UNLOCK_CONNECTOR_ON_EV_DISCONNECT "UnlockConnectorOnEVSideDisconnect" /* 必选 EV端断开时解锁连接器 (布尔: 0/1) */
|
||||
#define KEY_WEBSOCKET_PING_INTERVAL "WebSocketPingInterval" /* 可选 WebSocket Ping间隔 (整数: 秒) */
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 本地授权与充电配置管理
|
||||
// --------------------------------------------------------
|
||||
#define KEY_LOCAL_AUTH_LIST_ENABLED "LocalAuthListEnabled" /* 必选 启用本地授权列表 (布尔: 0/1) */
|
||||
#define KEY_LOCAL_AUTH_LIST_MAX_LENGTH "LocalAuthListMaxLength" /* 必选 本地授权列表的最大条目数 (整数) */
|
||||
#define KEY_SEND_LOCAL_LIST_MAX_LENGTH "SendLocalListMaxLength" /* 必选 发送本地列表的最大条目数 (整数) */
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 连接器与充电行为控制
|
||||
// --------------------------------------------------------
|
||||
#define KEY_RESERVE_CONNECTOR_ZERO_SUPPORTED "ReserveConnectorZeroSupported" /* 可选 是否支持预留连接器0 (布尔: 0/1) */
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 充电计划与配置限制
|
||||
// --------------------------------------------------------
|
||||
#define KEY_CHARGE_PROFILE_MAX_STACK_LEVEL "ChargeProfileMaxStackLevel" /* 必选 充电配置最大堆叠层级 (整数) */
|
||||
#define KEY_CHARGING_SCHEDULE_ALLOWED_RATE_UNIT "ChargingScheduleAllowedChargingRateUnit" /* 必选 充电速率允许单位 (字符串: "A"或"W") */
|
||||
#define KEY_CHARGING_SCHEDULE_MAX_PERIODS "ChargingScheduleMaxPeriods" /* 必选 充电计划最大时段数 (整数) */
|
||||
#define KEY_CONNECTOR_SWITCH_3TO1_PHASE_SUPPORTED "ConnectorSwitch3to1PhaseSupported" /* 可选 是否支持三相切单相充电 (布尔: 0/1) */
|
||||
#define KEY_MAX_CHARGING_PROFILES_INSTALLED "MaxChargingProfilesInstalled" /* 必选 最大可安装充电配置数 (整数) */
|
||||
|
||||
// --------------------------------------------------------
|
||||
// 私有配置
|
||||
// --------------------------------------------------------
|
||||
#define KEY_QRCODE_TEXT_CONNECTOR1 "QRCodeTextOfConnector-1" /* 枪1二维码 */
|
||||
#define KEY_QRCODE_TEXT_CONNECTOR2 "QRCodeTextOfConnector-2" /* 枪2二维码 */
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
#ifndef _BS_WEBSOCKET_H_
|
||||
#define _BS_WEBSOCKET_H_
|
||||
|
||||
#include "publicdata/type.h"
|
||||
#include "publicdata/public_define.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#if BS_OCPP_EN //OCPP平台使能
|
||||
|
||||
#define OCPP_WEBSOCKET_EN 1
|
||||
/** WebSocket HTTP 握手是否携带 Authorization: Basic(桩号:密钥);0=不携带,适用于不要求 HTTP Basic 的平台 */
|
||||
#ifndef OCPP_WS_HTTP_AUTHORIZATION_EN
|
||||
#define OCPP_WS_HTTP_AUTHORIZATION_EN 1
|
||||
#endif
|
||||
/** GET 路径是否在桩号后加尾斜杠;PLN 等为 /wsspkludev/CPID(默认 0,桩号后无 /) */
|
||||
#ifndef OCPP_WS_GET_PATH_TRAILING_SLASH
|
||||
#define OCPP_WS_GET_PATH_TRAILING_SLASH 0
|
||||
#endif
|
||||
typedef enum
|
||||
{
|
||||
WDT_NULL = 0, // 非标准数据包
|
||||
WDT_MINDATA, // 0x0:中间数据包
|
||||
WDT_TXTDATA, // 0x1:txt类型数据包
|
||||
WDT_BINDATA, // 0x2:bin类型数据包
|
||||
WDT_DISCONN, // 0x8:断开连接类型数据包 收到后需手动 close(fd)
|
||||
WDT_PING, // 0x8:ping类型数据包 ws_recv 函数内自动回复pong
|
||||
WDT_PONG, // 0xA:pong类型数据包
|
||||
} Ws_DataType;
|
||||
|
||||
int websocket_build();
|
||||
void ocpp_recv();
|
||||
|
||||
int ws_send(void* buff, int buffLen, bool mask, Ws_DataType type);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,287 @@
|
||||
#include "plat_comm/plat_comm_task.h"
|
||||
#include "plat_comm/impl/bs_connect_impl.h"
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
#if EC200A_4G_EN
|
||||
#include "plat_comm/4g_module/ec200a_4g_cfg.h"
|
||||
#endif
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "app_init/task_lock.h"
|
||||
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
|
||||
#define PLATCOMM_TASK_ID (TASK_ID_PlatComm)
|
||||
#define PLATCOMM_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(PLATCOMM_TASK_ID))
|
||||
#define PLATCOMM_LOCK_INIT() (u8_task_lock_ctrl(PLATCOMM_TASK_ID, TASK_LOCK_OP_INIT))
|
||||
#define PLATCOMM_LOCK_TAKE() (u8_task_lock_ctrl(PLATCOMM_TASK_ID, TASK_LOCK_OP_TAKE))
|
||||
#define PLATCOMM_LOCK_GIVE() (u8_task_lock_ctrl(PLATCOMM_TASK_ID, TASK_LOCK_OP_GIVE))
|
||||
#define PLATCOMM_FEED_DOG() (v_wdt_setFlag(PLATCOMM_TASK_ID))
|
||||
#define PLATCOMM_LOG(fmt, ...) MYLOG_MSG(PLATCOMM_TASK_ID, (fmt), ##__VA_ARGS__)
|
||||
|
||||
BS_TASK_CTRL_DATA s_bs_task_ctrl;
|
||||
typedef struct
|
||||
{
|
||||
const char *Master;
|
||||
void (*v_bs_data_init)(U8_T); //数据初始化
|
||||
U8_T (*v_bs_tcp_connect)(void); //tcp建立
|
||||
U8_T (*v_bs_logon_connect)(void); //平台登录
|
||||
void (*v_bs_recv_data)(void); //平台接收数据
|
||||
void (*v_bs_recv_data_divide)(U8_T,U8_T*,U16_T); //平台接收数据
|
||||
void (*v_bs_send_ctrl)(void); //平台发送逻辑控制
|
||||
void (*v_bs_private_logic)(U8_T); //私有逻辑执行函数
|
||||
} MasterFuncPtl;
|
||||
|
||||
const MasterFuncPtl MasterFuncArray[MAX_BS_NUM]= //最大2个平台
|
||||
{
|
||||
// 字符名称 数据初始化 TCP连接 登录连接 接收解析函数 发送控制函数 私有逻辑执行函数
|
||||
#if (BS_IES_EN)
|
||||
/* 积成 IESZT:bs_ies_ctrl=状态机,bs_ies_interface=0x68 组解帧;阶段1 登录+心跳 */
|
||||
{"ieszt", v_ies_init_data, v_ies_tcp_connect, v_ies_logo_on,v_ies_recv_data,v_ies_recv_unpack,v_ies_send_ctrl,v_ies_private_logic},
|
||||
#endif
|
||||
|
||||
#if (BS_GWSDK_EN)
|
||||
{"gwsdk", v_sdk_init_data, v_sdk_tcp_connect, v_sdk_logo_on,v_sdk_recv_data,NULL,v_sdk_send_ctrl,v_sdk_private_crel}, //0积成平台
|
||||
#endif
|
||||
|
||||
#if (BS_OCPP_EN) //OCPP平台使能
|
||||
{"ocpp", v_ocpp_init_data, v_ocpp_tcp_connect, v_ocpp_logo_on,v_ocpp_recv_data,NULL,v_ocpp_send_ctrl,v_ocpp_private_logic}, //1 云快充平台
|
||||
#endif
|
||||
|
||||
#if ((BS_IES_EN + BS_GWSDK_EN + BS_OCPP_EN) < MAX_BS_NUM)
|
||||
{"null", NULL, NULL, NULL, NULL, NULL, NULL, NULL},
|
||||
#endif
|
||||
};
|
||||
|
||||
/****************************************************************************系统集成改动部分-开始******************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
#if 1 //公共接口 外部调用函数
|
||||
/****************************************************
|
||||
*函数名称: v_set_bs_renew()
|
||||
*功能说明: 设置联网状态 重新连接
|
||||
*参数说明:
|
||||
* 输入参数: U8_T u8_bs 平台类型
|
||||
* 输出参数:
|
||||
****************************************************/
|
||||
void v_set_bs_renew(U8_T u8_bs)
|
||||
{
|
||||
/* 网口:必须先关闭 socket,否则只清 fd 会导致 LwIP 侧 socket 泄漏,重连几次后 connect 失败(再连也连不上) */
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[u8_bs].u8_netType != 0) {
|
||||
v_bs_eth_close_socket(u8_bs); /* 内部已置 fd=-1、u8_TCP_connect_flag=0 */
|
||||
} else {
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_bs].u8_TCP_connect_flag = 0;
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_bs].fd = (S32_T)-1;
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_bs].u8_4g_soft_tcp_reconnect = 1;
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_bs].u8_4g_tcp_soft_fail_streak = 0;
|
||||
}
|
||||
s_bs_task_ctrl.s_bs_ctrl[u8_bs].u8_log_on_flag = 0;
|
||||
|
||||
/* 勿在此处调用 v_bs_data_init:原先会触发 v_bs_4G_data_init 整网重驻网。
|
||||
* 断连后仅关 Socket + QIOPEN 由 u8_tcp_connect / v_ocpp_init_data_ex(...,0) 完成(不清 OCPP_DATA_CTRL)。
|
||||
* 上电首次 v_ocpp_init_data:`v_ocpp_mv_offline_init` + 运行态 RAM 重置 + v_ocpp_init_data_ex(...,1);
|
||||
* TCP 长时间失败在 BS_ocpp_ctrl 内调用 `v_ocpp_init_data`(与上电同路径)。 */
|
||||
}
|
||||
#endif
|
||||
|
||||
void v_plat_data_init(void)
|
||||
{
|
||||
memset(&s_bs_task_ctrl , 0, sizeof(s_bs_task_ctrl));
|
||||
|
||||
s_bs_task_ctrl.u8_gunCnt = u32_bs_get_sys_data(0, E_BS_GET_SYS_DATA_GUN_NUM, 0);
|
||||
|
||||
/* 通过 u32_bs_get_sys_data 获取平台使能:0 平台A,1 平台B */
|
||||
s_bs_task_ctrl.s_bs_ctrl[0].bs_enable = (U8_T)u32_bs_get_sys_data(0, E_BS_GET_SYS_DATA_PLAT_EN, 0);
|
||||
s_bs_task_ctrl.s_bs_ctrl[1].bs_enable = (U8_T)u32_bs_get_sys_data(1, E_BS_GET_SYS_DATA_PLAT_EN, 0);
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[0].bs_enable &&
|
||||
s_bs_task_ctrl.s_bs_ctrl[1].bs_enable)
|
||||
s_bs_task_ctrl.u8_4g_dispense_en = 1; //socketA和socketB使能 4g 数据分发使能
|
||||
|
||||
/* 通过 u32_bs_get_sys_data 获取联网类型:0 4G,1 网线 */
|
||||
s_bs_task_ctrl.s_bs_ctrl[0].u8_netType = u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NET_TYPE,0);
|
||||
s_bs_task_ctrl.s_bs_ctrl[1].u8_netType = u32_bs_get_sys_data(1,E_BS_GET_SYS_DATA_NET_TYPE,0);
|
||||
}
|
||||
|
||||
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
|
||||
/** 是否有 FTP/远程升级在跑(含 OCPP 日志上传:CallResult 前 u8_updata_flag 仍为 0 的窗口) */
|
||||
static U8_T u8_plat_comm_ftp_transfer_active(void)
|
||||
{
|
||||
U8_T active =
|
||||
(U8_T)((s_bs_task_ctrl.s_bs_ctrl[0].u8_updata_flag != 0U
|
||||
|| s_bs_task_ctrl.s_bs_ctrl[1].u8_updata_flag != 0U) ? 1U : 0U);
|
||||
#if BS_OCPP_EN
|
||||
if (active == 0U
|
||||
&& t_ftp_update_info.u8_netType == 0U
|
||||
&& s_bs_task_ctrl.ocpp_data.u8_log_file_upload != 0U) {
|
||||
active = 1U;
|
||||
}
|
||||
#endif
|
||||
return active;
|
||||
}
|
||||
|
||||
/** 各平台一轮:建链 / 登录 / send / recv / private_logic(与 FTP 步进分离,占满「联网」段) */
|
||||
static void v_plat_comm_network_round(void)
|
||||
{
|
||||
U8_T i;
|
||||
|
||||
for (i = 0; i < MAX_BS_NUM; i++) {
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].bs_enable == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].u8_TCP_connect_flag == 0) {
|
||||
if (MasterFuncArray[i].v_bs_tcp_connect != NULL) {
|
||||
s_bs_task_ctrl.s_bs_ctrl[i].u8_TCP_connect_flag =
|
||||
MasterFuncArray[i].v_bs_tcp_connect();
|
||||
}
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].u8_TCP_connect_flag) {
|
||||
Plat_Comm_LOG("[%d] tcp connect\n", i);
|
||||
}
|
||||
} else {
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].u8_log_on_flag == 0) {
|
||||
if (MasterFuncArray[i].v_bs_logon_connect != NULL) {
|
||||
s_bs_task_ctrl.s_bs_ctrl[i].u8_log_on_flag =
|
||||
MasterFuncArray[i].v_bs_logon_connect();
|
||||
}
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].u8_log_on_flag) {
|
||||
Plat_Comm_LOG("[%d] logOn is ok\n", i);
|
||||
}
|
||||
} else {
|
||||
if (MasterFuncArray[i].v_bs_send_ctrl != NULL) {
|
||||
MasterFuncArray[i].v_bs_send_ctrl();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MasterFuncArray[i].v_bs_recv_data != NULL) {
|
||||
MasterFuncArray[i].v_bs_recv_data();
|
||||
}
|
||||
if (MasterFuncArray[i].v_bs_private_logic != NULL) {
|
||||
MasterFuncArray[i].v_bs_private_logic(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** FTP 单步;4G(t_ftp_update_info.u8_netType==0)时随后双路 recv,便于收 +QFTP* 等尾 URC */
|
||||
static void v_plat_comm_ftp_round(void)
|
||||
{
|
||||
U8_T i;
|
||||
|
||||
v_ftp_update_action();
|
||||
if (t_ftp_update_info.u8_netType != 0U) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < MAX_BS_NUM; i++) {
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].bs_enable == 0) {
|
||||
continue;
|
||||
}
|
||||
if (MasterFuncArray[i].v_bs_recv_data != NULL) {
|
||||
MasterFuncArray[i].v_bs_recv_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void v_plat_comm_task(void *arg)
|
||||
{
|
||||
uint16_t sleep_time_ms;
|
||||
uint32_t last_send_time;
|
||||
uint32_t current_time;
|
||||
static uint8_t s_plat_comm_net_ftp_phase = 0U;
|
||||
|
||||
(void)arg;
|
||||
(void)PLATCOMM_LOCK_INIT();
|
||||
sleep_time_ms = PLATCOMM_SLEEP_TIME_MS();
|
||||
last_send_time = get_current_seconds();
|
||||
|
||||
v_plat_data_init();
|
||||
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[0].bs_enable != 0
|
||||
&& MasterFuncArray[0].v_bs_data_init != NULL)
|
||||
MasterFuncArray[0].v_bs_data_init(0);
|
||||
if(s_bs_task_ctrl.s_bs_ctrl[1].bs_enable != 0
|
||||
&& MasterFuncArray[1].v_bs_data_init != NULL)
|
||||
MasterFuncArray[1].v_bs_data_init(1);
|
||||
|
||||
for (;;) {
|
||||
PLATCOMM_FEED_DOG();
|
||||
current_time = get_current_seconds();
|
||||
if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
|
||||
if (PLATCOMM_LOCK_TAKE() == 1U) {
|
||||
PLATCOMM_LOG("plat comm task is runing, sleep time is %ums", sleep_time_ms);
|
||||
(void)PLATCOMM_LOCK_GIVE();
|
||||
}
|
||||
last_send_time = current_time;
|
||||
}
|
||||
#if 0 //屏蔽联网测试
|
||||
mSleep(u16_leep_time);
|
||||
// if(MasterFuncArray[0].v_bs_private_logic != NULL) //私有逻辑执行函数 例如超时计数等,注意判断网路连接状态
|
||||
// MasterFuncArray[0].v_bs_private_logic(0);
|
||||
continue;
|
||||
#endif
|
||||
|
||||
#if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D
|
||||
/* FTP 活跃时:相邻两个 Plat_Comm 周期各只做一端——周期 A 仅联网、周期 B 仅 FTP,互不叠在同一周期内。
|
||||
* 无 FTP 时:每周期只跑联网,并把相位复位为 0(下次进入 FTP 仍先联网)。4G FTP 步内为 `v_ftp_update_action` 后再双路 recv。 */
|
||||
{
|
||||
U8_T const ftp_active = u8_plat_comm_ftp_transfer_active();
|
||||
|
||||
if (ftp_active == 0U) {
|
||||
s_plat_comm_net_ftp_phase = 0U;
|
||||
v_plat_comm_network_round();
|
||||
} else if (s_plat_comm_net_ftp_phase == 0U) {
|
||||
v_plat_comm_network_round();
|
||||
s_plat_comm_net_ftp_phase = 1U;
|
||||
} else {
|
||||
v_plat_comm_ftp_round();
|
||||
s_plat_comm_net_ftp_phase = 0U;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* 遍历平台 A/B */
|
||||
for (U8_T i = 0U; i < 2U; i++) {
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].bs_enable == 0U) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 状态机:未建链 → 建链;已建链未登录 → 登录;已登录 → 周期发送 */
|
||||
if (s_bs_task_ctrl.s_bs_ctrl[i].u8_TCP_connect_flag == 0U) {
|
||||
if (MasterFuncArray[i].v_bs_tcp_connect != NULL) {
|
||||
s_bs_task_ctrl.s_bs_ctrl[i].u8_TCP_connect_flag = MasterFuncArray[i].v_bs_tcp_connect();
|
||||
}
|
||||
} else if (s_bs_task_ctrl.s_bs_ctrl[i].u8_log_on_flag == 0U) {
|
||||
if (MasterFuncArray[i].v_bs_logon_connect != NULL) {
|
||||
s_bs_task_ctrl.s_bs_ctrl[i].u8_log_on_flag = MasterFuncArray[i].v_bs_logon_connect();
|
||||
}
|
||||
} else if (MasterFuncArray[i].v_bs_send_ctrl != NULL) {
|
||||
MasterFuncArray[i].v_bs_send_ctrl();
|
||||
}
|
||||
|
||||
/* 接收:4G 双 socket 分发模式 或 单平台透传模式 */
|
||||
if (s_bs_task_ctrl.u8_4g_dispense_en != 0U) {
|
||||
v_4g_wh5_data_divide_recv(MasterFuncArray[0].v_bs_recv_data_divide, MasterFuncArray[1].v_bs_recv_data_divide);
|
||||
} else if (MasterFuncArray[i].v_bs_recv_data != NULL) {
|
||||
MasterFuncArray[i].v_bs_recv_data();
|
||||
}
|
||||
|
||||
/* 协议私有逻辑:状态变位检测、超时处理等 */
|
||||
if (MasterFuncArray[i].v_bs_private_logic != NULL) {
|
||||
MasterFuncArray[i].v_bs_private_logic(i);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* 动态 sleep:扣除本周期发送占用的时间,保证整体周期稳定 */
|
||||
if (s_bs_task_ctrl.u8_sendCnt * Plat_Send_Sleep < sleep_time_ms) {
|
||||
mSleep(sleep_time_ms - s_bs_task_ctrl.u8_sendCnt * Plat_Send_Sleep);
|
||||
} else {
|
||||
mSleep(sleep_time_ms);
|
||||
}
|
||||
s_bs_task_ctrl.u8_sendCnt = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef PLAT_COMM_TASK_H
|
||||
#define PLAT_COMM_TASK_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include "main.h"
|
||||
#include "../mylog/mylog.h"
|
||||
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
#include "plat_comm/impl/bs_connect_impl.h"
|
||||
#include "tls/bs_tls_mbed.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
|
||||
#define Plat_Comm_LOG(fmt, ...) \
|
||||
MYLOG_MSG(TASK_ID_PlatComm,fmt, ##__VA_ARGS__) // PlatComm 任务通用打印
|
||||
|
||||
#define OCPP_PRINT_FLAG MYLOG_ID_FORCE_OCPP /**< OCPP 强制打印,见 OCPP_PRINTF_LOG */
|
||||
#define IES_PRINT_FLAG MYLOG_ID_FORCE_IES /**< IES 强制打印,见 IES_PRINTF_LOG */
|
||||
|
||||
#if (BS_IES_EN)
|
||||
#include "plat_comm/ieszt/bs_ies_ctrl.h"
|
||||
#endif
|
||||
|
||||
#if (BS_GWSDK_EN)
|
||||
#include "plat_comm/gwsdk/bs_gwsdk_ctrl.h"
|
||||
#endif
|
||||
|
||||
#if BS_OCPP_EN //OCPP平台使能
|
||||
#include "plat_comm/ocpp/BS_ocpp_ctrl.h"
|
||||
#endif
|
||||
|
||||
#define Plat_Send_Sleep (50) //发送延时时间
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T bs_enable; //使能标志
|
||||
U8_T u8_TCP_connect_flag; //TCP连接建立标志
|
||||
/** 4G(u8_netType==0):v_set_bs_renew 后置位;仅关模组 Socket 再 QIOPEN,不整网重驻网 */
|
||||
U8_T u8_4g_soft_tcp_reconnect;
|
||||
U8_T u8_4g_tcp_soft_fail_streak;
|
||||
U8_T u8_log_on_flag; //登录标志
|
||||
U8_T u8_netType; //联网方式 04G 1网线
|
||||
U8_T u8_updata_flag; //远程升级标志 0未升级 1:ftp升级 2:sdk ota升级
|
||||
//联网及外部交互控制数据 放置在最外层
|
||||
struct
|
||||
{
|
||||
U8_T u8_pile_state_change_flag; //枪状态变位标志 1说明状态变化 0未变化
|
||||
U8_T txFlag_tradUpload; //交易记录上送标识
|
||||
U8_T online_flag; //重新上电标识
|
||||
U8_T txFlag_startResult; //启动结果上送标识
|
||||
U8_T txFlag_stopResult; //停止结果上送标识
|
||||
U8_T txFlag_autoStart; //即插即充鉴权发送标志
|
||||
U8_T txFlag_autoRwsult; //即插即充鉴权结果 0无意义 1鉴权成功 2鉴权失败
|
||||
|
||||
U8_T u8_bms_sendCnt; //bms属性发送次数
|
||||
U8_T u8_tradUpload_cnt; //订单发送次数
|
||||
U16_T u16_cmplte_bill_index; //上送订单索引号
|
||||
}s_gundata[GUN_MAX_CNT];
|
||||
S32_T fd; //以太网连接专用
|
||||
ULONG ulong_restart_timer; //超时时间
|
||||
#if (BS_TLS_MBEDTLS_EN)
|
||||
U8_T is_secure; //当前 TCP 是否走 TLS(0=明文 1=TLS)
|
||||
BS_TLS_CTX tls_ctx; //TLS 上下文,仅在网口+wss 时使用
|
||||
#endif
|
||||
}S_BS_CTRL_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
U8_T u8_gunCnt; //枪数量
|
||||
U8_T u8_4g_dispense_en; //4G模块串口分发使能标志
|
||||
U8_T u8_sendCnt; //发送数量
|
||||
|
||||
S_BS_CTRL_DATA s_bs_ctrl[MAX_BS_NUM];
|
||||
|
||||
#if (BS_IES_EN)
|
||||
S_BS_IES_CTRL_DATA s_ies_data;
|
||||
#endif
|
||||
|
||||
#if (BS_GWSDK_EN)
|
||||
S_GWSDK_CTRL_DATA s_gwsdk_ctrl;
|
||||
#endif
|
||||
|
||||
#if (BS_OCPP_EN) //OCPP平台使能
|
||||
OCPP_DATA_CTRL ocpp_data;
|
||||
#endif
|
||||
}BS_TASK_CTRL_DATA;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
extern BS_TASK_CTRL_DATA s_bs_task_ctrl;
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
|
||||
void v_set_bs_renew(U8_T u8_bs);
|
||||
void v_plat_comm_task(void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PLAT_COMM_TASK_H */
|
||||
@@ -0,0 +1,42 @@
|
||||
# plat_comm 模块说明
|
||||
|
||||
> 路径:`app/plat_comm/`
|
||||
> 返回主说明:[README](../../README.md)
|
||||
|
||||
## 1. 模块职责
|
||||
|
||||
- 平台通信任务模板实现;
|
||||
- 负责平台对接业务的任务框架与周期调度;
|
||||
- 为后续平台协议接入预留统一入口。
|
||||
|
||||
## 2. 关键文件
|
||||
|
||||
- `plat_comm_task.c`
|
||||
- `plat_comm_task.h`
|
||||
|
||||
## 3. 功能说明
|
||||
|
||||
- 提供标准任务模板(初始化、循环、延时、喂狗);
|
||||
- 预留平台上报/下发命令处理位置;
|
||||
- 与 `app_init` 任务表和任务锁体系保持一致。
|
||||
|
||||
## 4. 调度与对外接口
|
||||
|
||||
该模块由 FreeRTOS 任务 `PlatComm` 周期运行(单位:ms),周期来自 `app/app_init/app_init.c`:
|
||||
|
||||
- `PlatComm`:`100ms`(任务入口:`v_plat_comm_task(void *arg)`)
|
||||
|
||||
对外接口(来自 `plat_comm_task.h`):
|
||||
|
||||
- `void v_Plat_Comm_init(void)`:平台模块初始化
|
||||
- `void v_plat_comm_task(void *arg)`:平台通信任务入口
|
||||
- `U8_T u8_plat_comm_get_gun_num(void)`:获取枪数量(供通信与业务对齐)
|
||||
|
||||
## 5. 维护建议
|
||||
|
||||
- 平台协议接入时建议按“通信层/业务层”拆分实现;
|
||||
- 大报文处理建议增加缓存长度和超时保护。
|
||||
|
||||
## 6. 关联文档
|
||||
|
||||
- 主索引:[README](../../README.md)
|
||||
@@ -0,0 +1,348 @@
|
||||
# 平台联网任务功能说明(CCU601E_D / plat_comm)
|
||||
|
||||
本文档描述 **`CCU601E_D\app\plat_comm`** 目录内**平台联网任务**的整体架构、状态机、链路层与业务层分工,以及与计量、Flash、OCPP 的衔接。内容基于当前工程源码归纳;各子模块细节见文末**关联文档**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块定位
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **任务** | FreeRTOS 任务 **`TASK_ID_PlatComm`**,入口 **`v_Plat_Comm`**,周期约 **100ms**(由任务睡眠配置决定)。 |
|
||||
| **职责** | 多平台(最多 **2 路**)的 **TCP 建链 → 平台登录 → 周期收发 → 私有逻辑**;与 **FTP 远程升级 / 诊断日志上传** 分时调度;对上层屏蔽 **4G / 网线** 差异。 |
|
||||
| **协议栈** | 业务协议由 **`MasterFuncArray`** 注册(当前工程默认 **OCPP 1.6 + WebSocket**);传输由 **`bs_connect_impl`** 统一 **发送/接收**。 |
|
||||
| **全局控制块** | **`BS_TASK_CTRL_DATA s_bs_task_ctrl`**(`plat_comm_task.c` / `plat_comm_task.h`)。 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 编译开关与平台槽位
|
||||
|
||||
定义见 **`app/publicdata/public_define.h`**(摘要):
|
||||
|
||||
| 宏 | 典型值 | 含义 |
|
||||
|----|--------|------|
|
||||
| **`MAX_BS_NUM`** | 2 | 同时支持的平台通道数(槽位 0 / 1)。 |
|
||||
| **`BS_OCPP_EN`** | 1 | OCPP 协议栈参与编译。 |
|
||||
| **`BS_IES_EN`** | 1 | 积成平台(`plat_comm/ieszt/`,登录+心跳);详见 **[ieszt/IES积成平台功能说明.md](ieszt/IES积成平台功能说明.md)**。 |
|
||||
| **`BS_GWSDK_EN`** | 0 | 国网 SDK 平台(`gwsdk/`,本仓库目录可能未包含)。 |
|
||||
| **`BS_TLS_MBEDTLS_EN`** | 视工程 | 网口 **wss://** 时 MCU 侧 **mbedTLS** 封装。 |
|
||||
| **`LWIP_FTP_UPGRADE_ENABLE`** | 视工程 | 网线 **LwIP FTP** 固件下载状态机。 |
|
||||
|
||||
运行时使能与联网方式**不只看宏**,还读系统配置:
|
||||
|
||||
- **`E_BS_GET_SYS_DATA_PLAT_EN`**:`u8_gunNo` = 0 → 平台 A,= 1 → 平台 B;0 关闭、1 使能。
|
||||
- **`E_BS_GET_SYS_DATA_NET_TYPE`**:0 = **4G(EC200A)**,1 = **网线(LwIP)**。
|
||||
|
||||
初始化在 **`v_plat_data_init()`**(`plat_comm_task.c`)中写入各槽位 **`bs_enable`**、**`u8_netType`**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录结构一览
|
||||
|
||||
```
|
||||
plat_comm/
|
||||
├── plat_comm_task.c / .h # 联网任务主循环、平台函数表、重连入口
|
||||
├── 平台联网任务功能说明.md # 本文档
|
||||
├── impl/
|
||||
│ ├── bs_connect_impl.c / .h # TCP/TLS 建链、统一 v_bs_send / u16_bs_recv
|
||||
│ └── bs_public_impl.c / .h # 桩枪数据桥接、未结订单、FTP 调度、OCPP 配置读写
|
||||
├── ocpp/ # OCPP 1.6(详见 ocpp/OCPP功能模块说明.md)
|
||||
├── ieszt/ # 积成 IESZT(详见 ieszt/IES积成平台功能说明.md)
|
||||
├── 4g_module/ # EC200A AT、TCP、FTP(详见 4G模块AT指令配置功能说明.md)
|
||||
└── lwip_module/ # 网口 socket、LwIP FTP 升级(详见 lwip_ftp功能说明.md)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 联网任务主状态机(`v_Plat_Comm`)
|
||||
|
||||
### 4.1 平台函数表 `MasterFuncArray`
|
||||
|
||||
每个使能槽位绑定一组回调(**`plat_comm_task.c`**):
|
||||
|
||||
| 回调 | OCPP 实现 | 作用 |
|
||||
|------|-----------|------|
|
||||
| **`v_bs_data_init`** | `v_ocpp_init_data` | 上电/全量复位:OCPP 上下文、离线 MV FRAM、定时器等。 |
|
||||
| **`v_bs_tcp_connect`** | `v_ocpp_tcp_connect` | **TCP + WebSocket 握手**(内部先 `u8_tcp_connect`)。 |
|
||||
| **`v_bs_logon_connect`** | `v_ocpp_logo_on` | **BootNotification** 登录,成功置 **`u8_log_on_flag`**。 |
|
||||
| **`v_bs_recv_data`** | `v_ocpp_recv_data` → `ocpp_recv` | 收包并解析 JSON(CALL / CALLRESULT)。 |
|
||||
| **`v_bs_send_ctrl`** | `v_ocpp_send_ctrl` → `ocpp_send_mag_ctrl` | 周期/事件驱动主动上送(Heartbeat、MV、StopTx 等)。 |
|
||||
| **`v_bs_private_logic`** | `v_ocpp_private_logic` | 超时重连、未结订单、离线 MV 落盘、鉴权检查等。 |
|
||||
|
||||
未使能槽位(**`bs_enable == 0`**)在本轮循环中跳过。
|
||||
|
||||
**`BS_IES_EN`** 时槽位 **0** 常为 **`"ieszt"`**(`v_ies_*`),协议态在 **`s_bs_task_ctrl.s_ies_data`**(含 **`IES_SEND_TIMER` + `s_timerCnt[]`**,与 OCPP **`s_timerCnt`** 同套路),详见 **[ieszt/IES积成平台功能说明.md](ieszt/IES积成平台功能说明.md)**。
|
||||
|
||||
### 4.2 单槽位连接状态(`S_BS_CTRL_DATA`)
|
||||
|
||||
| 字段 | 含义 |
|
||||
|------|------|
|
||||
| **`u8_TCP_connect_flag`** | 1 = 传输层已就绪(OCPP 下为 TCP+WS 成功)。 |
|
||||
| **`u8_log_on_flag`** | 1 = 平台登录成功(BootNotification Accepted/Pending)。 |
|
||||
| **`u8_netType`** | 0 = 4G,1 = 网线。 |
|
||||
| **`fd`** | 网口 socket 描述符;4G 路径不使用。 |
|
||||
| **`is_secure` / `tls_ctx`** | 网口 **wss** 时 TLS 会话(**`BS_TLS_MBEDTLS_EN`**)。 |
|
||||
| **`u8_updata_flag`** | 远程升级进行中:0 无,1 FTP 固件,2 SDK OTA 等。 |
|
||||
| **`u8_4g_soft_tcp_reconnect`** | 4G:**软重连**(仅 QICLOSE+QIOPEN,不整网重驻网)。 |
|
||||
| **`s_gundata[]`** | 每枪:`txFlag_tradUpload`、`txFlag_startResult`、`txFlag_autoStart` 等与 OCPP/flow 协同。 |
|
||||
|
||||
### 4.3 每周期调度顺序(`v_plat_comm_network_round`)
|
||||
|
||||
对每个使能槽位 **i**:
|
||||
|
||||
```text
|
||||
若 u8_TCP_connect_flag == 0
|
||||
→ v_bs_tcp_connect() // 建链
|
||||
否则若 u8_log_on_flag == 0
|
||||
→ v_bs_logon_connect() // 登录
|
||||
否则
|
||||
→ v_bs_send_ctrl() // 已登录:主动发送
|
||||
|
||||
→ v_bs_recv_data() // 收包(任意阶段均可收)
|
||||
→ v_bs_private_logic(i) // 私有逻辑(未结单、离线 MV、超时等)
|
||||
```
|
||||
|
||||
日志示例:`[0] tcp connect`、`[0] logOn is ok`(`Plat_Comm_LOG`)。
|
||||
|
||||
### 4.4 与 FTP 的分时调度(4G 重点)
|
||||
|
||||
**`u8_plat_comm_ftp_transfer_active()`** 为真时(**`u8_updata_flag != 0`**,或 4G 下 **`u8_log_file_upload`** 置位):
|
||||
|
||||
- **偶数周期**:仅 **`v_plat_comm_network_round()`**(保证 OCPP 仍能 recv/send)。
|
||||
- **奇数周期**:仅 **`v_plat_comm_ftp_round()`** → **`v_ftp_update_action()`** + 双路 **`v_bs_recv_data()`**(收 **+QFTP*** 等 URC)。
|
||||
|
||||
无 FTP 时:每周期只跑联网,相位复位。
|
||||
|
||||
> 原因:4G 与 FTP **共用一根 UART**;若在同一周期内既跑大量 AT 又跑 **QISEND**,易出现粘连、**`break off=0`**。详见 **`4g_module/4G模块AT指令配置功能说明.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 5. 重连与初始化策略(`v_set_bs_renew`)
|
||||
|
||||
**`void v_set_bs_renew(U8_T u8_bs)`**(`plat_comm_task.c`)为各模块统一的「断链重连」入口:
|
||||
|
||||
| 联网方式 | 行为 |
|
||||
|----------|------|
|
||||
| **网线** | **`v_bs_eth_close_socket`**:关 socket、清 **`fd`**、**`u8_TCP_connect_flag`**,避免 LwIP **socket 泄漏**导致「再也连不上」。 |
|
||||
| **4G** | 清 TCP 标志、**`fd=-1`**,置 **`u8_4g_soft_tcp_reconnect=1`**(下次 **`u8_tcp_connect`** 走软重开 Socket)。 |
|
||||
| **公共** | **`u8_log_on_flag=0`**;**不在此处**调用 **`v_bs_data_init`**,避免每次断线都 **整模组重驻网**。 |
|
||||
|
||||
**全量 OCPP 复位**(含 **`v_ocpp_mv_offline_init`**、运行态 RAM)由 **`v_ocpp_init_data` / `v_ocpp_init_data_ex`** 在 **上电** 或 **TCP 长时间失败**(如 RX 超时 2min)时触发,与软重连区分。
|
||||
|
||||
OCPP 侧 **WS 握手失败**会关 socket 并清 **`u8_TCP_connect_flag`**,下一周期重试(**`v_ocpp_tcp_connect`**)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 连接实现层(`impl/bs_connect_impl`)
|
||||
|
||||
### 6.1 URL 解析
|
||||
|
||||
**`parse_websocket_url_simple(url, ws_url_t *)`** 解析配置中的服务器串,得到:
|
||||
|
||||
- **`host`**、**`port`**、**`path`**(WebSocket 路径)
|
||||
- **`is_secure`**:`ws://` → 0,`wss://` → 1
|
||||
- **`apn_str`**(4G 用,来自变量区 APN)
|
||||
|
||||
全局 **`url_info`** 供网口/4G 共用。
|
||||
|
||||
### 6.2 `u8_tcp_connect(u8_id)`
|
||||
|
||||
| `u8_netType` | 路径 |
|
||||
|--------------|------|
|
||||
| **0(4G)** | 若 **`u8_4g_soft_tcp_reconnect`**:`s8_ec200a_tcp_socket_soft_reopen()`;失败累计达 **`EC200A_TCP_SOFT_REOPEN_FAIL_MAX`** 再 **`v_bs_4G_data_init()`**。否则 **`s8_ec200a_4g_init()`** 推进模组状态机至 Socket 就绪。 |
|
||||
| **1(网线)** | **`eth_do_tcp_connect`**:`netif_is_link_up` 检查 → **`lwip_tcp_connect`** → 可选 **`bs_tls_client_connect`**(wss)。成功写 **`fd`**,返回 1。 |
|
||||
|
||||
### 6.3 统一收发
|
||||
|
||||
| 接口 | 4G | 网线 |
|
||||
|------|-----|------|
|
||||
| **`v_bs_send`** | 透传 **`v_4g_send_data`** / 非透传 **`v_ec200a_tcp_app_send`(QISEND/QSSLSEND)** | **`lwip_tcp_send`** 或 **`bs_tls_client_send`** |
|
||||
| **`u16_bs_recv`** | **`u16_4g_recv_data`** / **`u16_ec200a_tcp_app_recv`(QIRD/QSSLRECV)** | **`lwip_tcp_recv`** / **`bs_tls_client_recv`**;对端关闭或 errno 异常 → **`eth_close_socket`** |
|
||||
|
||||
每次 **`v_bs_send`** 后 **`mSleep(Plat_Send_Sleep)`**(默认 50ms),并累加 **`u8_sendCnt`** 用于任务末尾睡眠补偿。
|
||||
|
||||
接收缓冲区上限:**`BS_BUF_SIZE_RECV`**(约 3KB,远程升级 JSON 可能较长)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 公共实现层(`impl/bs_public_impl`)
|
||||
|
||||
面向 **flow / meter / fault / UI / OCPP** 的**统一数据与控制 API**,是「业务世界」与「平台任务」的边界。
|
||||
|
||||
### 7.1 主要能力分类
|
||||
|
||||
| 分类 | 代表接口 | 说明 |
|
||||
|------|----------|------|
|
||||
| **系统/枪数据读** | **`u32_bs_get_sys_data`**、**`u8_get_str_data`** | 枪状态、电量、SOC、订单流水号、桩编号、4G 信号等。 |
|
||||
| **充电控制** | **`v_bs_charge_action_ctrl`** | 启停充、改订单字段(含 **`preTradeNo`** 写入计量日志)。 |
|
||||
| **状态/订单标志** | **`v_bs_log_txFlag_tradUpload`**、**`u8_bs_get_flow_state`** | 与 **`s_gundata`**、flowctrl 联动。 |
|
||||
| **未结订单** | **`s8_plat_get_unsettled_order`** | 优先 **EEPROM 掉电临时单**,再 **Flash 未结索引**;OCPP 补单数据源。 |
|
||||
| **结算确认** | **`v_bs_platform_settlement_confirm`** | StopTx 成功后删未结索引。 |
|
||||
| **FTP** | **`v_ftp_update_action`** | 按 **`t_ftp_update_info`** 与 **`u8_netType`** 分发 4G / LwIP FTP。 |
|
||||
| **OCPP 配置** | **`u8_set_ocpp_cfg_info`**、**`v_get_ocpp_cfg_info`** | 读写 Flash 中的 OCPP 配置项。 |
|
||||
| **本地鉴权** | **`v_ocpp_offline_local_authentication_check`**、**`u8_card_info_add`** | 离线卡列表与即插即充。 |
|
||||
|
||||
### 7.2 未结订单来源(与掉电续传)
|
||||
|
||||
**`s8_plat_get_unsettled_order`** 顺序:
|
||||
|
||||
1. **EEPROM 临时记录**(**`v_check_temp_chg_record_valid`**):充电中每 60s 快照;真正掉电后上电读出,置 **`E_FAULT_13073`**(异常掉电)。**充电中(flow=2)不当作掉电单**,避免误 StopTx。
|
||||
2. **Flash 未结列表**(**`unsettled_order_mng`**):正常结束但未收到平台确认的订单索引。
|
||||
|
||||
计量侧:**`v_meterlog_restore_temp_from_eeprom`** 上电恢复 RAM;**`preTradeNo` 更新后立即写 EEPROM**(见 **`meter_calculate_impl.c`**)。
|
||||
|
||||
### 7.3 FTP 全局参数 `T_FTP_UPDATE_INFO`
|
||||
|
||||
由 OCPP **UpdateFirmware** / **GetDiagnostics** 解析 **`parse_ftp_url()`** 填入:
|
||||
|
||||
- 服务器地址、端口、用户、密码、路径、文件名
|
||||
- **`u8_netType`**:决定走 **4G AT FTP** 还是 **LwIP FTP**
|
||||
- **`u8_log_file_upload`**(OCPP 扩展):1 = 诊断日志上传,0 = 固件升级
|
||||
|
||||
**`v_ftp_update_action()`** 每 FTP 半周期推进一步,避免阻塞 PlatComm。
|
||||
|
||||
---
|
||||
|
||||
## 8. 4G 子系统(`4g_module/`)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| **模组** | Quectel **EC200A** |
|
||||
| **业务 TCP** | **`QIOPEN` / `QSSLOPEN`**(wss 时模组内置 TLS),PDP **`EC200A_TCP_PDP_CTX_ID`** |
|
||||
| **FTP** | 独立 PDP **`EC200A_FTP_PDP_CTX_ID`**,与业务 TCP **禁止同 context** |
|
||||
| **模式** | **`EC200A_TCP_USE_TRANSPARENT_MODE`**:透传 vs **`QISEND`/`QIRD`** 缓冲模式 |
|
||||
| **与 PlatComm** | 仅通过 **`bs_connect_impl`** 的 **`v_bs_send`/`u16_bs_recv`** 接入;FTP 由 **`v_ftp_update_action`** 驱动 |
|
||||
|
||||
**详细 AT 流程、QFTPGET/QFREAD 分片、与 OCPP 互斥宏** → **`4g_module/4G模块AT指令配置功能说明.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 9. 网口子系统(`lwip_module/`)
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| **`lwip_module.c`** | **`lwip_tcp_connect` / `send` / `recv` / `close`**,非阻塞 socket |
|
||||
| **`lwip_ftp_upgrade.c`** | 网线 **FTP 固件下载** 状态机(PASV/RETR/按 1024B 写 Flash) |
|
||||
|
||||
**`eth_do_tcp_connect`** 在 PHY **link down** 时不建连;重连前必须 **`v_bs_eth_close_socket`**。
|
||||
|
||||
**详细状态机与触发条件** → **`lwip_module/lwip_ftp功能说明.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 10. OCPP 子系统(`ocpp/`)
|
||||
|
||||
在 **`BS_OCPP_EN`** 下,OCPP 作为 **`MasterFuncArray`** 中的一套 **Master** 实现:
|
||||
|
||||
```text
|
||||
TCP (u8_tcp_connect)
|
||||
→ WebSocket 握手 (websocket_build)
|
||||
→ BootNotification (v_ocpp_logo_on)
|
||||
→ ocpp_send_mag_ctrl:Heartbeat / StatusNotification / MeterValues / Start&StopTransaction …
|
||||
→ ocpp_recv → recv_mags:平台下行与 CALLRESULT
|
||||
→ v_ocpp_private_logic:未结单 s8_unsettled_order_chaek、离线 MV 桥接、RX 超时重连
|
||||
```
|
||||
|
||||
**离线 MeterValues**:未连接或未登录时 **`BS_ocpp_mv_offline_bridge`** 写 Flash;联网后 **session 补发**(在线会话)或 **unsettled 补发**(掉电/未结单)。
|
||||
|
||||
**完整 PDU 列表、未结单状态机、session_001x 类问题修复说明** → **`ocpp/OCPP功能模块说明.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 11. 端到端数据流(示意)
|
||||
|
||||
### 11.1 正常充电上送(已登录)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph tasks
|
||||
FC[FlowCtrl / Meterfee]
|
||||
PC[PlatComm v_Plat_Comm]
|
||||
end
|
||||
subgraph ocpp
|
||||
SEND[ocpp_send_mag_ctrl / send_mag]
|
||||
WS[ws_send / bs_websocket]
|
||||
end
|
||||
subgraph link
|
||||
BS[v_bs_send / u16_bs_recv]
|
||||
TCP[u8_tcp_connect 4G or LwIP]
|
||||
end
|
||||
FC -->|订单/枪状态| PUB[bs_public_impl]
|
||||
PUB --> SEND
|
||||
PC --> SEND
|
||||
SEND --> WS --> BS --> TCP
|
||||
TCP --> CSMS[CSMS]
|
||||
CSMS --> TCP --> BS --> WS --> RECV[ocpp_recv / recv_mags]
|
||||
RECV --> PUB
|
||||
PC --> RECV
|
||||
```
|
||||
|
||||
### 11.2 掉电后补单(简化)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant M as meter/EEPROM
|
||||
participant P as bs_public_impl
|
||||
participant O as BS_ocpp_ctrl
|
||||
participant S as CSMS
|
||||
M->>P: s8_plat_get_unsettled_order
|
||||
P->>O: s_unsettled_order_data
|
||||
alt 无 preTradeNo
|
||||
O->>S: Authorize
|
||||
S->>O: Accepted
|
||||
O->>S: StartTransaction
|
||||
S->>O: transactionId
|
||||
end
|
||||
O->>S: MeterValues 离线补发
|
||||
O->>S: StopTransaction PowerLoss
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 关键全局变量与调试
|
||||
|
||||
| 符号 | 位置 | 用途 |
|
||||
|------|------|------|
|
||||
| **`s_bs_task_ctrl`** | `plat_comm_task.c` | 双平台连接态、枪标志、OCPP **`ocpp_data`**、IES **`s_ies_data`** |
|
||||
| **`t_ftp_update_info`** | `bs_public_impl.c` | FTP 升级/日志参数 |
|
||||
| **`url_info`** | `bs_connect_impl.c` | 解析后的 host/port/TLS |
|
||||
| **`private_ocpp_data`** | `BS_ocpp_ctrl.c` | OCPP 定时器、未结单指针、pending 通知 |
|
||||
| **`Plat_Comm_LOG` / `OCPP_PRINTF_LOG`** | 宏 | 任务日志(**`TASK_ID_PlatComm`** / **0xFF**) |
|
||||
|
||||
**常见日志关键字**
|
||||
|
||||
| 日志 | 含义 |
|
||||
|------|------|
|
||||
| `[n] tcp connect` | 第 n 槽传输层就绪 |
|
||||
| `[n] logOn is ok` | BootNotification 成功 |
|
||||
| `Load pwr-loss order gun=` | EEPROM 掉电单加载 |
|
||||
| `Unsettled: send StopTx` | 未结单补 StopTransaction |
|
||||
| `MV offline: session send ok` | 在线会话离线 MV 补发 |
|
||||
| `ws handshake fail` | WS 失败,将重试 TCP |
|
||||
| `End rx TO, relink` / `RX TO, relink` | 收包超时触发 **`v_set_bs_renew`** |
|
||||
|
||||
---
|
||||
|
||||
## 13. 关联文档索引
|
||||
|
||||
| 文档路径 | 内容 |
|
||||
|----------|------|
|
||||
| **`ocpp/OCPP功能模块说明.md`** | OCPP 报文、未结单、离线 MV、Boot/Heartbeat、平台下行指令 |
|
||||
| **`ieszt/IES积成平台功能说明.md`** | IES 0x68 协议、`S_BS_IES_CTRL_DATA`、登录/心跳与 PlatComm 七回调 |
|
||||
| **`4g_module/4G模块AT指令配置功能说明.md`** | EC200A 初始化、透传/非透传、FTP 与 OCPP 分时、配置宏 |
|
||||
| **`lwip_module/lwip_ftp功能说明.md`** | 网线 FTP 升级状态机、与 **`v_ftp_update_action`** 关系 |
|
||||
| **`BSP/flash_file_mgr/`**(工程内) | 订单 Flash、未结索引、OCPP 离线 MV FRAM/SPI |
|
||||
| **`app/meter_calculate/`**(工程内) | 订单 RAM、EEPROM 临时单、周期存盘 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 维护说明
|
||||
|
||||
1. **新增平台协议**:在 **`plat_comm/`** 下增加子目录,实现 **`MasterFuncPtl`** 七个回调,并在 **`MasterFuncArray`** 中注册;**`BS_*_EN`** 与 **`E_BS_GET_SYS_DATA_PLAT_EN`** 对齐。
|
||||
2. **修改建链逻辑**:优先改 **`bs_connect_impl.c`**,业务层保持 **`v_bs_send`/`u16_bs_recv`** 不变。
|
||||
3. **FTP 与 OCPP 并发**:改 **`plat_comm_task.c`** 相位策略或 **`ec200a_4g_cfg.h`** 互斥宏前,须在 **4G 实机** 验证 **QISEND** 与 **QFTP** 不互相破坏。
|
||||
4. **未结/掉电单**:同时核对 **`bs_public_impl.c`**、**`meter_calculate_impl.c`**、**`BS_ocpp_ctrl.c`** 三处,避免 EEPROM 周期快照被误判为掉电单。
|
||||
|
||||
---
|
||||
|
||||
*文档版本:与 CCU601E_D plat_comm 源码同步维护;若宏或文件名变更,请以实际编译配置为准。*
|
||||
Reference in New Issue
Block a user