Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user