@@ -26,37 +26,31 @@ ip4_addr_t stake_ip_6 = IPADDR4_INIT_BYTES(10, 12, 19, 106); // 桩 6 IP地址
err_t udp_send_response ( uint8_t stake_index , uint8_t * data , u16_t len )
{
struct netbuf * buf = netbuf_new ( ) ;
if ( ! buf )
{
printf ( " [%s] %d \n " , __func__ , __LINE__ ) ;
if ( ! buf ) {
return ERR_MEM ;
}
ip4_addr_t * dst_ip = NULL ;
switch ( stake_index )
{
case 1 :
dst_ip = & stake_ip_1 ;
break ;
case 2 :
dst_ip = & stake_ip_2 ;
break ;
case 3 :
dst_ip = & stake_ip_3 ;
break ;
case 4 :
dst_ip = & stake_ip_4 ;
break ;
case 5 :
dst_ip = & stake_ip_5 ;
break ;
case 6 :
dst_ip = & stake_ip_6 ;
break ;
switch ( stake_index ) {
case 1 : dst_ip = & stake_ip_1 ; break ;
case 2 : dst_ip = & stake_ip_2 ; break ;
case 3 : dst_ip = & stake_ip_3 ; break ;
case 4 : dst_ip = & stake_ip_4 ; break ;
case 5 : dst_ip = & stake_ip_5 ; break ;
case 6 : dst_ip = & stake_ip_6 ; break ;
default :
printf ( " Invalid stake index \r \n " ) ;
return ;
netbuf_delete ( buf ) ;
return ERR_VAL ; // ← 修复 return 无返回值
}
netbuf_ref ( buf , data , len ) ;
void * buf_data = netbuf_alloc ( buf , len ) ; // ← 在 netbuf 内部分配内存
if ( ! buf_data ) {
netbuf_delete ( buf ) ;
return ERR_MEM ;
}
memcpy ( buf_data , data , len ) ; // ← 拷贝数据,不再依赖外部指针
err_t err = netconn_sendto ( datalink_conn , buf , dst_ip , LINK_STAKE_PORT ) ;
netbuf_delete ( buf ) ;
return err ;
@@ -148,7 +142,7 @@ void UDPTask_Function(void const *argument)
// 队列满,释放数据内存
if ( xQueueSend ( UDP_Message_Queue , & msg , 0 ) ! = pdPASS )
{
vPortFree ( msg . data ) ;
vPortFree ( msg . data ) ;
}
}
}
@@ -213,36 +207,36 @@ void local_on_cmd_callback_heartbeat_response(uint8_t stake_index, cJSON *json_p
{
return ;
}
//心跳unpack
// 心跳unpack
cJSON * gun_array = cJSON_GetObjectItem ( json_pack , " gun " ) ;
// 直接判断 type 字段
if ( gun_array = = NULL | | gun_array - > type ! = cJSON_Array )
if ( gun_array = = NULL | | gun_array - > type ! = cJSON_Array )
{
printf ( " └── [error] 缺少 gun 数组 \r \n " ) ;
return ;
}
int gun_count = cJSON_GetArraySize ( gun_array ) ;
for ( int i = 0 ; i < gun_count ; i + + )
{
cJSON * gun = cJSON_GetArrayItem ( gun_array , i ) ;
if ( ! gun ) continue ;
if ( ! gun )
continue ;
cJSON * id = cJSON_GetObjectItem ( gun , " id " ) ;
cJSON * state = cJSON_GetObjectItem ( gun , " state " ) ;
if ( ! id | | ! state ) continue ;
// if (id->valueint == 1) pile->gun1_state = state->valueint;
// if (id->valueint == 2 ) pile->gun2 _state = state->valueint;
if ( ! id | | ! state )
continue ;
// if (id->valueint == 1 ) pile->gun1 _state = state->valueint;
// if (id->valueint == 2) pile->gun2_state = state->valueint;
printf ( " └── [info] 桩%d 枪%d state=%d \r \n " , stake_index , id - > valueint , state - > valueint ) ;
}
//心跳回复组包
cJSON * root = NULL ;
// 心跳回复组包
cJSON * root = NULL ;
char * str = NULL ;
root = cJSON_CreateObject ( ) ;
@@ -263,8 +257,6 @@ void local_on_cmd_callback_heartbeat_response(uint8_t stake_index, cJSON *json_p
cJSON_Delete ( root ) ;
printf ( " 南向:对电桩 %d 心跳回复成功 \r \n " , stake_index ) ;
}
void handle_udp_downlink ( uint8_t id , const char * cmd , cJSON * json_pack )
@@ -287,4 +279,222 @@ void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack)
{
printf ( " Unknown CMD: '%s' from ID %d \r \n " , cmd , id ) ;
}
}
/**
* @brief 将16字节的交易序列号转换为32位16进制字符串
* @param trade_serial 16字节数组
* @param output_str 输出缓冲区( 至少33字节)
*/
void trade_serial_to_string ( uint8_t * trade_serial , char * output_str )
{
if ( trade_serial = = NULL | | output_str = = NULL )
{
return ;
}
for ( int i = 0 ; i < 16 ; i + + )
{
sprintf ( & output_str [ i * 2 ] , " %02X " , trade_serial [ i ] ) ;
}
output_str [ 32 ] = ' \0 ' ;
}
/**
* @brief 主动下发启动指令
* @note 发送启动指令到充电桩,符合协议格式要求
* @param stake_index 桩索引 (1~6)
* @param gun_id 枪编码 (1~N)
*/
void local_on_cmd_send_start_charging ( uint8_t stake_index , uint8_t gun_id )
{
if ( stake_index > 6 | | stake_index = = 0 )
{
printf ( " Invalid stake index: %d \r \n " , stake_index ) ;
return ;
}
if ( gun_id = = 0 | | gun_id > MAX_GUN_PER_CHARGER )
{
printf ( " Invalid gun id: %d \r \n " , gun_id ) ;
return ;
}
// 检查是否已获取费率模型
if ( ! g_charger_manager . charger_piles [ stake_index - 1 ] . get_model )
{
printf ( " Stake %d fee model not ready \r \n " , stake_index ) ;
return ;
}
cJSON * root = NULL ;
cJSON * billing_array = NULL ;
cJSON * billing_item = NULL ;
char * str = NULL ;
// 转换交易ID: 16位数组转字符串
char transaction_id_str [ 33 ] = { 0 } ; // 32位 + 结束符
uint8_t * trade_serial = g_charger_manager . charger_piles [ stake_index - 1 ] . guns [ gun_id - 1 ] . real_time_data . trade_serial ;
trade_serial_to_string ( trade_serial , transaction_id_str ) ;
root = cJSON_CreateObject ( ) ;
if ( root = = NULL )
{
printf ( " Failed to create JSON object for stake %d \r \n " , stake_index ) ;
return ;
}
/* 添加基本字段 */
cJSON_AddNumberToObject ( root , " id " , stake_index ) ;
cJSON_AddStringToObject ( root , " cmd " , " start charging " ) ;
cJSON_AddStringToObject ( root , " transaction_id " , transaction_id_str ) ; // 使用转换后的32位字符串
cJSON_AddNumberToObject ( root , " gun_id " , gun_id ) ;
cJSON_AddStringToObject ( root , " type " , " request " ) ;
/* 添加限制金额(根据实际业务设置)*/
cJSON_AddNumberToObject ( root , " limit_amount " , 30.00 ) ;
/* 创建计费模型数组 */
billing_array = cJSON_CreateArray ( ) ;
if ( billing_array = = NULL )
{
printf ( " Failed to create billing array \r \n " ) ;
cJSON_Delete ( root ) ;
return ;
}
/* 计费时段:根据费率表生成 period_id 1:尖 2:峰 3:平 4:谷 */
int period_id = 1 ;
int i = 0 ;
while ( i < 48 ) // 48个半小时时段
{
uint8_t current_type = g_charger_manager . fee_model_global . fee_num [ i ] ;
// 跳过无效费率
if ( current_type = = 0xFF )
{
i + + ;
continue ;
}
// 查找连续相同费率的结束位置
int j = i ;
while ( j < 48 & & g_charger_manager . fee_model_global . fee_num [ j ] = = current_type )
{
j + + ;
}
// 计算起始时间( 第i个半小时段)
int start_hour = i / 2 ;
int start_min = ( i % 2 ) * 30 ;
// 计算结束时间( 第j个半小时段的起始时间)
int end_hour = j / 2 ;
int end_min = ( j % 2 ) * 30 ;
// 格式化时间字符串 "HH:MM"
char start_time [ 6 ] ;
char end_time [ 6 ] ;
sprintf ( start_time , " %02d:%02d " , start_hour , start_min ) ;
if ( end_hour = = 24 & & end_min = = 0 )
{
sprintf ( end_time , " 00:00 " ) ; // 跨天显示为00:00
}
else
{
sprintf ( end_time , " %02d:%02d " , end_hour , end_min ) ;
}
// 根据费率类型设置电费和服务费
float electricity_fee = 0.0f ;
float service_fee = 0.0f ;
int mapped_period_id = 0 ; // 映射到协议要求的period_id
switch ( current_type )
{
case 0x00 : // 尖时
electricity_fee = g_charger_manager . fee_model_global . shark_fee_ratio / 100000.0f ;
service_fee = g_charger_manager . fee_model_global . shark_service_ratio / 100000.0f ;
mapped_period_id = 1 ; // 1:尖
break ;
case 0x01 : // 峰时
electricity_fee = g_charger_manager . fee_model_global . peak_fee_ratio / 100000.0f ;
service_fee = g_charger_manager . fee_model_global . peak_service_ratio / 100000.0f ;
mapped_period_id = 2 ; // 2:峰
break ;
case 0x02 : // 平时
electricity_fee = g_charger_manager . fee_model_global . flat_fee_ratio / 100000.0f ;
service_fee = g_charger_manager . fee_model_global . flat_service_ratio / 100000.0f ;
mapped_period_id = 3 ; // 3:平
break ;
case 0x03 : // 谷时
electricity_fee = g_charger_manager . fee_model_global . valley_fee_ratio / 100000.0f ;
service_fee = g_charger_manager . fee_model_global . valley_service_ratio / 100000.0f ;
mapped_period_id = 4 ; // 4:谷
break ;
default :
electricity_fee = 0.0f ;
service_fee = 0.0f ;
mapped_period_id = 0 ;
break ;
}
// 创建计费时段对象(只有有效费率类型才添加)
if ( mapped_period_id > 0 )
{
billing_item = cJSON_CreateObject ( ) ;
if ( billing_item ! = NULL )
{
cJSON_AddNumberToObject ( billing_item , " period_id " , mapped_period_id ) ;
cJSON_AddStringToObject ( billing_item , " start_time " , start_time ) ;
cJSON_AddStringToObject ( billing_item , " end_time " , end_time ) ;
cJSON_AddNumberToObject ( billing_item , " electricity_fee " , electricity_fee ) ;
cJSON_AddNumberToObject ( billing_item , " service_fee " , service_fee ) ;
cJSON_AddItemToArray ( billing_array , billing_item ) ;
// 调试打印
# ifdef DEBUG
const char * period_name = " " ;
switch ( mapped_period_id )
{
case 1 : period_name = " Sharp " ; break ;
case 2 : period_name = " Peak " ; break ;
case 3 : period_name = " Flat " ; break ;
case 4 : period_name = " Valley " ; break ;
}
printf ( " Period %d: %s-%s [%s] electricity:%.3f service:%.3f \r \n " ,
mapped_period_id , start_time , end_time , period_name ,
electricity_fee , service_fee ) ;
# endif
}
}
i = j ; // 跳到下一个不同费率段
}
// 将数组添加到根对象
cJSON_AddItemToObject ( root , " billing_model " , billing_array ) ;
// 转换为JSON字符串并发送
str = cJSON_Print ( root ) ;
if ( str ! = NULL )
{
# ifdef DEBUG
printf ( " Send start command JSON: %s \r \n " , str ) ;
# endif
udp_send_response ( stake_index , str , strlen ( str ) ) ;
free ( str ) ;
}
else
{
printf ( " Failed to print JSON for stake %d \r \n " , stake_index ) ;
}
cJSON_Delete ( root ) ;
printf ( " Southbound: Start command sent to stake %d, gun %d, total %d billing periods \r \n " ,
stake_index , gun_id , ( int ) cJSON_GetArraySize ( billing_array ) ) ;
}