#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); // 桩1IP地址 ip4_addr_t stake_ip_2 = IPADDR4_INIT_BYTES(10, 12, 19, 102); // 桩2IP地址 ip4_addr_t stake_ip_3 = IPADDR4_INIT_BYTES(10, 12, 19, 103); // 桩3IP地址 ip4_addr_t stake_ip_4 = IPADDR4_INIT_BYTES(10, 12, 19, 104); // 桩4IP地址 ip4_addr_t stake_ip_5 = IPADDR4_INIT_BYTES(10, 12, 19, 105); // 桩5IP地址 ip4_addr_t stake_ip_6 = IPADDR4_INIT_BYTES(10, 12, 19, 106); // 桩6IP地址 /** * @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) { printf("[%s] %d\n", __func__, __LINE__); 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; default: printf("Invalid stake index\r\n"); return; } netbuf_ref(buf, 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\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 > 6) { return; } g_charger_manager.charger_piles[stake_index - 1].is_udp_online = true; // 设置桩为本地在线状态 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", "power_on"); cJSON_AddNumberToObject(root, "code", 1); 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_get_billing_model(uint8_t stake_index, cJSON *json_pack) { if (stake_index > 6) { return; } } void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack) { if (cmd == NULL) return; if (strcmp(cmd, "power_on") == 0) { local_on_cmd_callback_power_on(id, json_pack); } else if (strcmp(cmd, "get_billing_model") == 0) { local_on_cmd_callback_get_billing_model(id, json_pack); } else { printf("Unknown CMD: '%s' from ID %d\r\n", cmd, id); } }