Files
BR_YKC/Core/User/Task/ChargerTask.c

794 lines
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ChargerTask.h"
static struct netconn *datalink_conn; // 数据链路句柄
#define LINK_SERVER_PORT 6001 // 网关UDP服务端口
#define LINK_STAKE_PORT 6001 // 桩通讯端口
/**
* @brief 桩IP地址
* @note 桩IP地址从10.12.19.101开始递增
*/
ip4_addr_t stake_ip_1 = IPADDR4_INIT_BYTES(10, 12, 19, 101); // 桩 1 IP地址
ip4_addr_t stake_ip_2 = IPADDR4_INIT_BYTES(10, 12, 19, 102); // 桩 2 IP地址
ip4_addr_t stake_ip_3 = IPADDR4_INIT_BYTES(10, 12, 19, 103); // 桩 3 IP地址
ip4_addr_t stake_ip_4 = IPADDR4_INIT_BYTES(10, 12, 19, 104); // 桩 4 IP地址
ip4_addr_t stake_ip_5 = IPADDR4_INIT_BYTES(10, 12, 19, 105); // 桩 5 IP地址
ip4_addr_t stake_ip_6 = IPADDR4_INIT_BYTES(10, 12, 19, 106); // 桩 6 IP地址
ip4_addr_t server_ip = IPADDR4_INIT_BYTES(10, 12, 19, 107); // 上位机管理地址
/**
* @brief 将十六进制字符串转换为字节数组
* @note 输入字符串长度必须为偶数
*/
int hex_string_to_bytes(const char *hex_str, uint8_t *bytes, int len)
{
for (int i = 0; i < len; i++)
{
unsigned int val;
sscanf(hex_str + i * 2, "%02x", &val);
bytes[i] = (uint8_t)val;
}
return 0;
}
/**
* @brief 将字节数组转换为十六进制字符串
* @note 输出字符串长度必须为32
*/
void bytes_to_hex_string(const uint8_t *bytes, char *hex_str)
{
for (int i = 0; i < 16; i++)
{
sprintf(hex_str + i * 2, "%02X", bytes[i]);
}
}
/**
* @brief UDP发送
* @note 发送数据到指定桩
* @param stake_index 目标桩ID
* @param data 数据指针
* @param len 数据长度
* @return err_t 错误码
*/
err_t udp_send_response(uint8_t stake_index, uint8_t *data, u16_t len)
{
struct netbuf *buf = netbuf_new();
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;
case 7:
dst_ip = &server_ip;
break;
default:
printf("Invalid stake index\r\n");
netbuf_delete(buf);
return ERR_VAL; // ← 修复 return 无返回值
}
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;
}
void UDP_ParseTask_Function(void const *argument)
{
UdpMsg_t msg;
cJSON *root = NULL, *cmd = NULL, *id = NULL;
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); /* 等待桩通讯协议层完成*/
while (1)
{
if (xQueueReceive(UDP_Message_Queue, &msg, portMAX_DELAY) == pdPASS)
{
root = cJSON_Parse((const char *)msg.data);
if (root == NULL)
{
printf("JSON Parse Failed: %s\r\n", msg.data);
vPortFree(msg.data);
continue;
}
id = cJSON_GetObjectItem(root, "id");
cmd = cJSON_GetObjectItem(root, "cmd");
if (cmd != NULL && id != NULL)
{
const char *cmd_str = cmd->valuestring;
handle_udp_downlink((uint8_t)id->valueint, cmd_str, root);
cJSON_Delete(root);
}
else
{
printf("Missing 'code' field from \r\n");
cJSON_Delete(root);
}
vPortFree(msg.data);
msg.data = NULL;
}
}
}
/**
* @brief UPLinkTask_Function桩通讯任务
*
* @note none
*
* @param taskID : 任务ID
*
* @retval runtime : 任务周期
*/
void UDPTask_Function(void const *argument)
{
err_t recv_err;
struct netbuf *datalink_buf = NULL;
datalink_conn = netconn_new(NETCONN_UDP);
netconn_bind(datalink_conn, IP_ADDR_ANY, LINK_SERVER_PORT);
UDP_Message_Queue_Init(); // 初始化UDP接收队列
/*桩UDP通讯初始化完成 发送云快充任务通知*/
xTaskNotifyGive(YkcTaskHandle);
xTaskNotifyGive(DownLinkTaskHandle);
xTaskNotifyGive(UDP_ParseTaskHandle);
while (1)
{
/*获取任务运行状态*/
TaskRunTimeStat.UPLinkTask.threads_runtime = GetTask_RunTime(UPLinkTaskID);
TaskRunTimeStat.UPLinkTask.threads_counter = GetTask_Beatcnt(UPLinkTaskID);
TaskRunTimeStat.UPLinkTask.threads_freestack = Get_Free_Stack(UPLinkTaskID);
recv_err = netconn_recv(datalink_conn, &datalink_buf);
if (recv_err == ERR_OK && datalink_buf != NULL)
{
uint8_t *playload;
uint16_t playload_len;
netbuf_data(datalink_buf, (void *)&playload, &playload_len);
if (playload_len > 0)
{
UdpMsg_t msg;
ip_addr_copy(msg.src_ip, *netbuf_fromaddr(datalink_buf)); // 获取UDP源IP
msg.src_port = netbuf_fromport(datalink_buf); // 获取UDP源端口
msg.len = playload_len;
msg.data = (char *)pvPortMalloc(playload_len + 1);
if (msg.data != NULL)
{
memcpy(msg.data, playload, playload_len);
msg.data[playload_len] = '\0';
// 队列满,释放数据内存
if (xQueueSend(UDP_Message_Queue, &msg, 0) != pdPASS)
{
vPortFree(msg.data);
}
}
}
netbuf_delete(datalink_buf); // 释放UDP网络缓冲区
}
else
{
if (recv_err != ERR_TIMEOUT && recv_err != ERR_WOULDBLOCK)
{
printf("datalink netconn_recv err: %d\r\n", recv_err);
}
}
}
}
/**
* @brief 解析充电桩上电指令
* @note 设置桩为本地在线状态,发送上电应答
* @param stake_index 桩索引
* @param json_pack json数据包
*/
void local_on_cmd_callback_power_on(uint8_t stake_index, cJSON *json_pack)
{
if (stake_index > MAX_CHARGER_COUNT)
{
return;
}
g_charger_manager.charger_piles[stake_index - 1].is_udp_online = true; // 设置桩为本地在线状态
cJSON *root = NULL;
uint8_t *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for stake %d\r\n", stake_index);
return;
}
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "id", stake_index);
cJSON_AddStringToObject(root, "cmd", "online");
cJSON_AddStringToObject(root, "type", "response");
str = cJSON_Print(root);
udp_send_response(stake_index, str, strlen(str));
free(str);
cJSON_Delete(root);
printf("南向:对电桩 %d 上电回复成功\r\n", stake_index);
}
/**
* @brief 解析充电桩心跳指令
* @note 回复心跳应答
* @param stake_index 桩索引
* @param json_pack json数据包
*/
void local_on_cmd_callback_heartbeat_response(uint8_t stake_index, cJSON *json_pack)
{
if (stake_index > MAX_CHARGER_COUNT)
{
return;
}
// 心跳unpack
cJSON *gun_array = cJSON_GetObjectItem(json_pack, "gun");
// 直接判断 type 字段
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;
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;
printf(" └── [info] 桩%d 枪%d state=%d\r\n", stake_index, id->valueint, state->valueint);
}
// 心跳回复组包
cJSON *root = NULL;
char *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for stake %d\r\n", stake_index);
return;
}
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "id", stake_index);
cJSON_AddStringToObject(root, "cmd", "heartbeat");
cJSON_AddStringToObject(root, "type", "response");
str = cJSON_Print(root);
udp_send_response(stake_index, str, strlen(str));
free(str);
cJSON_Delete(root);
printf("南向:对电桩 %d 心跳回复成功\r\n", stake_index);
}
/**
* @brief 解析充电桩实时数据指令
* @note 回复实时数据应答
* @param stake_index 桩索引
* @param json_pack json数据包
*/
void local_on_cmd_callback_realtime_data_response(uint8_t stake_index, cJSON *json_pack)
{
if (stake_index > MAX_CHARGER_COUNT)
{
return;
}
cJSON *transaction_id = cJSON_GetObjectItem(json_pack, "transaction_id"); // 提取交易序列号
cJSON *gun_id = cJSON_GetObjectItem(json_pack, "gun_id"); // 提取枪号
cJSON *state = cJSON_GetObjectItem(json_pack, "state"); // 提取枪状态
cJSON *gun_back = cJSON_GetObjectItem(json_pack, "gun_back"); // 提取枪归位状态
cJSON *gun_insert = cJSON_GetObjectItem(json_pack, "gun_insert"); // 提取枪插入状态
cJSON *voltage = cJSON_GetObjectItem(json_pack, "voltage"); // 提取电压
cJSON *current = cJSON_GetObjectItem(json_pack, "current"); // 提取电流
cJSON *cable_temp = cJSON_GetObjectItem(json_pack, "cable_temp"); // 提取电缆温度
cJSON *cable_code = cJSON_GetObjectItem(json_pack, "cable_code"); // 提取电缆编码
cJSON *soc = cJSON_GetObjectItem(json_pack, "soc"); // 提取SOC
cJSON *battery_temp = cJSON_GetObjectItem(json_pack, "battery_temp"); // 提取电池温度
cJSON *charge_time = cJSON_GetObjectItem(json_pack, "charge_time"); // 提取充电时间
cJSON *remain_time = cJSON_GetObjectItem(json_pack, "remain_time"); // 提取剩余时间
cJSON *charge_kwh = cJSON_GetObjectItem(json_pack, "charge_kwh"); // 提取充电量
cJSON *loss_kwh = cJSON_GetObjectItem(json_pack, "loss_kwh"); // 提取损失量
cJSON *charge_amount = cJSON_GetObjectItem(json_pack, "charge_amount"); // 提取充电金额
cJSON *fault = cJSON_GetObjectItem(json_pack, "fault"); // 提取故障状态
// 检查交易序列号、枪号、状态字段是否存在
if (!transaction_id || !gun_id || !state)
{
printf(" └── [error] 缺少必要字段 transaction_id/gun_id/state\r\n");
return;
}
// 检查交易序列号长度是否为32位
if (strlen(transaction_id->valuestring) != 32)
{
printf(" └── [error] transaction_id长度错误: %d\r\n", (int)strlen(transaction_id->valuestring));
return;
}
// 检查枪号是否有效
uint8_t gun_idx = (uint8_t)gun_id->valueint;
if (gun_idx == 0 || gun_idx > MAX_GUN_PER_CHARGER)
{
printf(" └── [error] 无效的gun_id: %d\r\n", gun_idx);
return;
}
ChargerPile *pile = &g_charger_manager.charger_piles[stake_index - 1];
ChargerGun *gun = &pile->guns[gun_idx - 1];
// 更新枪状态
gun->real_time_data.status = (uint8_t)state->valueint;
// 更新枪归位状态
gun->real_time_data.gun_back = (uint8_t)gun_back->valueint;
// 更新枪插入状态
gun->real_time_data.gun_is_insert = (uint8_t)gun_insert->valueint;
// 更新电压
gun->real_time_data.out_voltage = (uint16_t)(voltage->valuedouble * 10);
// 更新电流
gun->real_time_data.out_current = (uint16_t)(current->valuedouble * 10);
// 更新电缆温度
gun->real_time_data.gun_line_temp = (uint8_t)cable_temp->valueint;
// 更新SOC
gun->real_time_data.soc = (uint8_t)soc->valueint;
// 更新电池温度
gun->real_time_data.battery_temp = (uint8_t)battery_temp->valueint;
// 更新累计充电时间
gun->real_time_data.charge_time = (uint16_t)charge_time->valueint;
// 更新剩余时间
gun->real_time_data.remain_time = (uint16_t)remain_time->valueint;
// 更新充电量
gun->real_time_data.charge_energy = (uint32_t)(charge_kwh->valuedouble * 10000);
// 更新损失量
gun->real_time_data.loss_energy = (uint32_t)(loss_kwh->valuedouble * 10000);
// 更新充电金额
gun->real_time_data.charge_money = (uint32_t)(charge_amount->valuedouble * 10000);
// 更新故障状态
gun->real_time_data.hard_fault = (uint16_t)fault->valueint;
// 回复实时数据应答
cJSON *root = NULL;
char *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for stake %d\r\n", stake_index);
return;
}
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "id", stake_index);
cJSON_AddStringToObject(root, "cmd", "realtime data");
cJSON_AddStringToObject(root, "type", "response");
str = cJSON_Print(root);
udp_send_response(stake_index, str, strlen(str));
free(str);
cJSON_Delete(root);
printf("南向:对电桩 %d 实时数据回复成功\r\n", stake_index);
}
/**
* @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;
// 转换交易ID16位数组转字符串
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", 00.00);
/* 创建计费模型数组 */
billing_array = cJSON_CreateArray();
if (billing_array == NULL)
{
printf("Failed to create billing array\r\n");
cJSON_Delete(root);
return;
}
/* 计费时段:生成简化格式 [start_time, end_time, electricity_fee, service_fee] */
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++;
}
// 计算起始时间(小时.半小时6.0=6:00, 6.5=6:30
float start_time = i * 0.5f;
float end_time = j * 0.5f;
// 跨天处理24.0显示为0.0
if (end_time == 24.0f)
{
end_time = 0.0f;
}
// 根据费率类型设置电费和服务费
float electricity_fee = 0.0f;
float service_fee = 0.0f;
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;
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;
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;
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;
break;
default:
electricity_fee = 0.0f;
service_fee = 0.0f;
break;
}
// 创建计费时段数组 [start_time, end_time, electricity_fee, service_fee]
if (electricity_fee > 0 || service_fee > 0)
{
billing_item = cJSON_CreateArray();
if (billing_item != NULL)
{
cJSON_AddNumberToObject(billing_item, NULL, start_time);
cJSON_AddNumberToObject(billing_item, NULL, end_time);
cJSON_AddNumberToObject(billing_item, NULL, electricity_fee);
cJSON_AddNumberToObject(billing_item, NULL, service_fee);
cJSON_AddItemToArray(billing_array, billing_item);
}
}
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));
}
void local_on_cmd_callback_server_login_response(cJSON *json_pack)
{
cJSON *request_id = cJSON_GetObjectItem(json_pack, "request_id");
cJSON *username = cJSON_GetObjectItem(json_pack, "username");
cJSON *password = cJSON_GetObjectItem(json_pack, "password");
cJSON *root = NULL;
char *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for server login\r\n");
return;
}
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(root, "request_id", request_id->valuestring);
if (strcmp(username->valuestring, "admin") != 0 || strcmp(password->valuestring, "123456") != 0)
{
cJSON_AddStringToObject(root, "error", "用户或密码错误 ");
cJSON_AddBoolToObject(root, "success", cJSON_False);
printf("登录失败\r\n");
}
else
{
cJSON_AddBoolToObject(root, "success", cJSON_True);
printf("登录成功\r\n");
}
str = cJSON_Print(root);
udp_send_response(7, str, strlen(str));
free(str);
cJSON_Delete(root);
}
// 处理获取状态查询指令
void local_on_cmd_callback_server_get_status_response(cJSON *json_pack)
{
cJSON *request_id = cJSON_GetObjectItem(json_pack, "request_id");
cJSON *root = NULL;
cJSON *piles = NULL;
cJSON *pile = NULL;
cJSON *guns = NULL;
cJSON *gun = NULL;
char *str = NULL;
root = cJSON_CreateObject();
if (root == NULL)
{
printf("Failed to create JSON object for get_status\r");
return;
}
cJSON_AddStringToObject(root, "request_id", request_id->valuestring);
cJSON_AddBoolToObject(root, "success", cJSON_True);
/* ── piles 数组 ── */
piles = cJSON_CreateArray();
/* 桩 1 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111558");
cJSON_AddBoolToObject(pile, "is_online", cJSON_True);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 1);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
/* 桩 2 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111559");
cJSON_AddBoolToObject(pile, "is_online", cJSON_True);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 1);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 1);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
/* 桩 3 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111560");
cJSON_AddBoolToObject(pile, "is_online", cJSON_True);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
/* 桩 4 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111561");
cJSON_AddBoolToObject(pile, "is_online", cJSON_False);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
/* 桩 5 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111562");
cJSON_AddBoolToObject(pile, "is_online", cJSON_True);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 1);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
/* 桩 6 */
pile = cJSON_CreateObject();
cJSON_AddStringToObject(pile, "serial", "32010601111563");
cJSON_AddBoolToObject(pile, "is_online", cJSON_False);
guns = cJSON_CreateArray();
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
gun = cJSON_CreateObject();
cJSON_AddNumberToObject(gun, "status", 0);
cJSON_AddItemToArray(guns, gun);
cJSON_AddItemToObject(pile, "guns", guns);
cJSON_AddItemToArray(piles, pile);
cJSON_AddItemToObject(root, "piles", piles);
/* ── 发送 ── */
str = cJSON_Print(root);
udp_send_response(7, str, strlen(str));
free(str);
cJSON_Delete(root);
printf("get_status 回复已发送 \r\n");
}
void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack)
{
if (cmd == NULL)
return;
// 处理上电指令
if (strcmp(cmd, "online") == 0)
{
printf("南向:收到电桩 %d 上电指令\r\n", id);
local_on_cmd_callback_power_on(id, json_pack);
}
// 处理心跳指令
else if (strcmp(cmd, "heartbeat") == 0)
{
printf("南向:收到电桩 %d 心跳指令\r\n", id);
local_on_cmd_callback_heartbeat_response(id, json_pack);
}
// 处理实时数据指令
else if (strcmp(cmd, "realtime data") == 0)
{
printf("南向:收到电桩 %d 实时数据指令 \r\n", id);
local_on_cmd_callback_realtime_data_response(id, json_pack);
}
/*处理上位机指令 */
// 处理登录指令
else if (strcmp(cmd, "server_login") == 0)
{
printf("上位机:收到登录指令 \r\n");
local_on_cmd_callback_server_login_response(json_pack);
}
// 处理获取状态查询指令
else if (strcmp(cmd, "server_get_status") == 0)
{
printf("上位机:收到获取状态查询指令 \r\n");
local_on_cmd_callback_server_get_status_response(json_pack);
}
else
{
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';
}