2026-03-31 15:46:04 +08:00
|
|
|
|
#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开始递增
|
|
|
|
|
|
*/
|
2026-04-03 12:14:43 +08:00
|
|
|
|
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地址
|
2026-03-31 15:46:04 +08:00
|
|
|
|
|
2026-05-08 18:17:26 +08:00
|
|
|
|
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]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-31 15:46:04 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @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();
|
2026-05-08 18:17:26 +08:00
|
|
|
|
if (!buf)
|
|
|
|
|
|
{
|
2026-03-31 15:46:04 +08:00
|
|
|
|
return ERR_MEM;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-03-31 15:46:04 +08:00
|
|
|
|
ip4_addr_t *dst_ip = NULL;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
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;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
default:
|
|
|
|
|
|
printf("Invalid stake index\r\n");
|
2026-05-06 15:22:21 +08:00
|
|
|
|
netbuf_delete(buf);
|
2026-05-08 18:17:26 +08:00
|
|
|
|
return ERR_VAL; // ← 修复 return 无返回值
|
2026-05-06 15:22:21 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
void *buf_data = netbuf_alloc(buf, len); // ← 在 netbuf 内部分配内存
|
|
|
|
|
|
if (!buf_data)
|
|
|
|
|
|
{
|
2026-05-06 15:22:21 +08:00
|
|
|
|
netbuf_delete(buf);
|
|
|
|
|
|
return ERR_MEM;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
memcpy(buf_data, data, len); // ← 拷贝数据,不再依赖外部指针
|
|
|
|
|
|
|
2026-03-31 15:46:04 +08:00
|
|
|
|
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
|
|
|
|
|
|
{
|
2026-04-30 17:16:01 +08:00
|
|
|
|
printf("Missing 'code' field from \r\n");
|
2026-03-31 15:46:04 +08:00
|
|
|
|
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';
|
2026-04-30 17:16:01 +08:00
|
|
|
|
// 队列满,释放数据内存
|
2026-03-31 15:46:04 +08:00
|
|
|
|
if (xQueueSend(UDP_Message_Queue, &msg, 0) != pdPASS)
|
|
|
|
|
|
{
|
2026-05-06 15:22:21 +08:00
|
|
|
|
vPortFree(msg.data);
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
if (stake_index > MAX_CHARGER_COUNT)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
g_charger_manager.charger_piles[stake_index - 1].is_udp_online = true; // 设置桩为本地在线状态
|
|
|
|
|
|
|
|
|
|
|
|
cJSON *root = NULL;
|
2026-04-30 17:16:01 +08:00
|
|
|
|
uint8_t *str = NULL;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
2026-04-30 17:16:01 +08:00
|
|
|
|
cJSON_AddStringToObject(root, "cmd", "online");
|
|
|
|
|
|
cJSON_AddStringToObject(root, "type", "response");
|
2026-03-31 15:46:04 +08:00
|
|
|
|
|
|
|
|
|
|
str = cJSON_Print(root);
|
|
|
|
|
|
udp_send_response(stake_index, str, strlen(str));
|
|
|
|
|
|
free(str);
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
|
2026-04-30 17:16:01 +08:00
|
|
|
|
printf("南向:对电桩 %d 上电回复成功\r\n", stake_index);
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-30 17:16:01 +08:00
|
|
|
|
* @brief 解析充电桩心跳指令
|
|
|
|
|
|
* @note 回复心跳应答
|
2026-03-31 15:46:04 +08:00
|
|
|
|
* @param stake_index 桩索引
|
|
|
|
|
|
* @param json_pack json数据包
|
|
|
|
|
|
*/
|
2026-04-30 17:16:01 +08:00
|
|
|
|
void local_on_cmd_callback_heartbeat_response(uint8_t stake_index, cJSON *json_pack)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
if (stake_index > MAX_CHARGER_COUNT)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 心跳unpack
|
|
|
|
|
|
|
2026-04-30 17:16:01 +08:00
|
|
|
|
cJSON *gun_array = cJSON_GetObjectItem(json_pack, "gun");
|
|
|
|
|
|
// 直接判断 type 字段
|
2026-05-06 15:22:21 +08:00
|
|
|
|
if (gun_array == NULL || gun_array->type != cJSON_Array)
|
2026-04-30 17:16:01 +08:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
2026-05-06 15:22:21 +08:00
|
|
|
|
if (!gun)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2026-04-30 17:16:01 +08:00
|
|
|
|
cJSON *id = cJSON_GetObjectItem(gun, "id");
|
|
|
|
|
|
cJSON *state = cJSON_GetObjectItem(gun, "state");
|
2026-05-06 15:22:21 +08:00
|
|
|
|
|
|
|
|
|
|
if (!id || !state)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
// if (id->valueint == 1) pile->gun1_state = state->valueint;
|
|
|
|
|
|
// if (id->valueint == 2) pile->gun2_state = state->valueint;
|
2026-04-30 17:16:01 +08:00
|
|
|
|
printf(" └── [info] 桩%d 枪%d state=%d\r\n", stake_index, id->valueint, state->valueint);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 心跳回复组包
|
|
|
|
|
|
cJSON *root = NULL;
|
2026-04-30 17:16:01 +08:00
|
|
|
|
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);
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 解析充电桩实时数据指令
|
|
|
|
|
|
* @note 回复实时数据应答
|
|
|
|
|
|
* @param stake_index 桩索引
|
|
|
|
|
|
* @param json_pack json数据包
|
|
|
|
|
|
*/
|
|
|
|
|
|
void local_on_cmd_callback_realtime_data_response(uint8_t stake_index, cJSON *json_pack)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
if (stake_index > MAX_CHARGER_COUNT)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
return;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
printf(" └── [error] 缺少必要字段 transaction_id/gun_id/state\r\n");
|
|
|
|
|
|
return;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
// 检查交易序列号长度是否为32位
|
|
|
|
|
|
if (strlen(transaction_id->valuestring) != 32)
|
2026-03-31 15:46:04 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
printf(" └── [error] transaction_id长度错误: %d\r\n", (int)strlen(transaction_id->valuestring));
|
|
|
|
|
|
return;
|
2026-03-31 15:46:04 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
// 检查枪号是否有效
|
|
|
|
|
|
uint8_t gun_idx = (uint8_t)gun_id->valueint;
|
|
|
|
|
|
if (gun_idx == 0 || gun_idx > MAX_GUN_PER_CHARGER)
|
2026-05-06 15:22:21 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
printf(" └── [error] 无效的gun_id: %d\r\n", gun_idx);
|
2026-05-06 15:22:21 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
2026-05-06 15:22:21 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
printf("Failed to create JSON object for stake %d\r\n", stake_index);
|
|
|
|
|
|
return;
|
2026-05-06 15:22:21 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
/* 添加一条字符串类型的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);
|
2026-05-06 15:22:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @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;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 转换交易ID:16位数组转字符串
|
2026-05-08 18:17:26 +08:00
|
|
|
|
char transaction_id_str[33] = {0}; // 32位 + 结束符
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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);
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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");
|
2026-05-08 18:17:26 +08:00
|
|
|
|
cJSON_AddStringToObject(root, "transaction_id", transaction_id_str); // 使用转换后的32位字符串
|
2026-05-06 15:22:21 +08:00
|
|
|
|
cJSON_AddNumberToObject(root, "gun_id", gun_id);
|
|
|
|
|
|
cJSON_AddStringToObject(root, "type", "request");
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
/* 添加限制金额(根据实际业务设置)*/
|
2026-05-08 18:17:26 +08:00
|
|
|
|
cJSON_AddNumberToObject(root, "limit_amount", 00.00);
|
2026-05-06 15:22:21 +08:00
|
|
|
|
|
|
|
|
|
|
/* 创建计费模型数组 */
|
|
|
|
|
|
billing_array = cJSON_CreateArray();
|
|
|
|
|
|
if (billing_array == NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
printf("Failed to create billing array\r\n");
|
|
|
|
|
|
cJSON_Delete(root);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-08 18:17:26 +08:00
|
|
|
|
/* 计费时段:生成简化格式 [start_time, end_time, electricity_fee, service_fee] */
|
2026-05-06 15:22:21 +08:00
|
|
|
|
int i = 0;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
while (i < 48) // 48个半小时时段
|
2026-05-06 15:22:21 +08:00
|
|
|
|
{
|
|
|
|
|
|
uint8_t current_type = g_charger_manager.fee_model_global.fee_num[i];
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 跳过无效费率
|
|
|
|
|
|
if (current_type == 0xFF)
|
|
|
|
|
|
{
|
|
|
|
|
|
i++;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 查找连续相同费率的结束位置
|
|
|
|
|
|
int j = i;
|
|
|
|
|
|
while (j < 48 && g_charger_manager.fee_model_global.fee_num[j] == current_type)
|
|
|
|
|
|
{
|
|
|
|
|
|
j++;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算起始时间(小时.半小时: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)
|
2026-05-06 15:22:21 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
end_time = 0.0f;
|
2026-05-06 15:22:21 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 根据费率类型设置电费和服务费
|
|
|
|
|
|
float electricity_fee = 0.0f;
|
|
|
|
|
|
float service_fee = 0.0f;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
switch (current_type)
|
|
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
case 0x00: // 尖时
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
case 0x01: // 峰时
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
case 0x02: // 平时
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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;
|
2026-05-08 18:17:26 +08:00
|
|
|
|
case 0x03: // 谷时
|
2026-05-06 15:22:21 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建计费时段数组 [start_time, end_time, electricity_fee, service_fee]
|
|
|
|
|
|
if (electricity_fee > 0 || service_fee > 0)
|
2026-05-06 15:22:21 +08:00
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
billing_item = cJSON_CreateArray();
|
2026-05-06 15:22:21 +08:00
|
|
|
|
if (billing_item != NULL)
|
|
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
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);
|
2026-05-06 15:22:21 +08:00
|
|
|
|
cJSON_AddItemToArray(billing_array, billing_item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
|
|
|
|
|
i = j; // 跳到下一个不同费率段
|
2026-05-06 15:22:21 +08:00
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 将数组添加到根对象
|
|
|
|
|
|
cJSON_AddItemToObject(root, "billing_model", billing_array);
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
// 转换为JSON字符串并发送
|
|
|
|
|
|
str = cJSON_Print(root);
|
|
|
|
|
|
if (str != NULL)
|
|
|
|
|
|
{
|
2026-05-08 18:17:26 +08:00
|
|
|
|
// #ifdef DEBUG
|
|
|
|
|
|
// printf("Send start command JSON: %s\r\n", str);
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
udp_send_response(stake_index, str, strlen(str));
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
free(str);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
printf("Failed to print JSON for stake %d\r\n", stake_index);
|
|
|
|
|
|
}
|
2026-05-08 18:17:26 +08:00
|
|
|
|
|
2026-05-06 15:22:21 +08:00
|
|
|
|
cJSON_Delete(root);
|
2026-05-08 18:17:26 +08:00
|
|
|
|
printf("Southbound: Start command sent to stake %d, gun %d, total %d billing periods\r\n",
|
2026-05-06 15:22:21 +08:00
|
|
|
|
stake_index, gun_id, (int)cJSON_GetArraySize(billing_array));
|
2026-05-08 18:17:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
|
|
|
}
|