diff --git a/4G/code/core/linksocket.lua b/4G/code/core/linksocket.lua index d013d81..3da6c47 100644 --- a/4G/code/core/linksocket.lua +++ b/4G/code/core/linksocket.lua @@ -4,10 +4,9 @@ local log = require "log" local sys = require "sys" local socket = require "socket" - -- 服务器配置 -local SERVER_IP = "121.43.69.62" -local SERVER_PORT = 8767 +local SERVER_IP = "129.211.170.245" +local SERVER_PORT = 9002 local MAX_CLIENTS = 6 -- 客户端状态表 diff --git a/Core/LWIP/App/lwip.c b/Core/LWIP/App/lwip.c index e0d8810..89abe9a 100644 --- a/Core/LWIP/App/lwip.c +++ b/Core/LWIP/App/lwip.c @@ -61,7 +61,7 @@ void MX_LWIP_Init(void) IP_ADDRESS[0] = 10; IP_ADDRESS[1] = 12; IP_ADDRESS[2] = 19; - IP_ADDRESS[3] = 252; + IP_ADDRESS[3] = 100; NETMASK_ADDRESS[0] = 255; NETMASK_ADDRESS[1] = 255; NETMASK_ADDRESS[2] = 255; diff --git a/Core/LWIP/Target/lwipopts.h b/Core/LWIP/Target/lwipopts.h index 2d67ff0..8ac429d 100644 --- a/Core/LWIP/Target/lwipopts.h +++ b/Core/LWIP/Target/lwipopts.h @@ -58,7 +58,7 @@ /*----- Value in opt.h for MEM_ALIGNMENT: 1 -----*/ #define MEM_ALIGNMENT 4 /*----- Default Value for MEM_SIZE: 1600 ---*/ -#define MEM_SIZE 1024*10 +#define MEM_SIZE 1024*20 /*----- Default Value for H7 devices: 0x30044000 -----*/ #define LWIP_RAM_HEAP_POINTER 0x30004000 /*----- Default Value for MEMP_NUM_PBUF: 16 ---*/ @@ -80,11 +80,11 @@ /*----- Value in opt.h for LWIP_NETIF_LINK_CALLBACK: 0 -----*/ #define LWIP_NETIF_LINK_CALLBACK 1 /*----- Value in opt.h for TCPIP_THREAD_STACKSIZE: 0 -----*/ -#define TCPIP_THREAD_STACKSIZE 1024 +#define TCPIP_THREAD_STACKSIZE 2048 /*----- Value in opt.h for TCPIP_THREAD_PRIO: 1 -----*/ #define TCPIP_THREAD_PRIO osPriorityNormal /*----- Value in opt.h for TCPIP_MBOX_SIZE: 0 -----*/ -#define TCPIP_MBOX_SIZE 6 +#define TCPIP_MBOX_SIZE 32 /*----- Value in opt.h for SLIPIF_THREAD_STACKSIZE: 0 -----*/ #define SLIPIF_THREAD_STACKSIZE 1024 /*----- Value in opt.h for SLIPIF_THREAD_PRIO: 1 -----*/ @@ -94,9 +94,9 @@ /*----- Value in opt.h for DEFAULT_THREAD_PRIO: 1 -----*/ #define DEFAULT_THREAD_PRIO 3 /*----- Value in opt.h for DEFAULT_UDP_RECVMBOX_SIZE: 0 -----*/ -#define DEFAULT_UDP_RECVMBOX_SIZE 6 +#define DEFAULT_UDP_RECVMBOX_SIZE 20 /*----- Value in opt.h for DEFAULT_TCP_RECVMBOX_SIZE: 0 -----*/ -#define DEFAULT_TCP_RECVMBOX_SIZE 6 +#define DEFAULT_TCP_RECVMBOX_SIZE 20 /*----- Value in opt.h for DEFAULT_ACCEPTMBOX_SIZE: 0 -----*/ #define DEFAULT_ACCEPTMBOX_SIZE 6 /*----- Value in opt.h for RECV_BUFSIZE_DEFAULT: INT_MAX -----*/ @@ -107,7 +107,7 @@ #define LWIP_CHECKSUM_CTRL_PER_NETIF 1 /*-----------------------------------------------------------------------------*/ /* USER CODE BEGIN 1 */ -#define MEMP_NUM_NETBUF 16 +#define MEMP_NUM_NETBUF 48 /* USER CODE END 1 */ diff --git a/Core/MDK-ARM/.eide/eide.yml b/Core/MDK-ARM/.eide/eide.yml index 99d7f3c..c23d15a 100644 --- a/Core/MDK-ARM/.eide/eide.yml +++ b/Core/MDK-ARM/.eide/eide.yml @@ -8,6 +8,7 @@ virtualFolder: name: files: - path: ../README.md + - path: ../../Doc/云快充平台协议V1.6.pdf folders: - name: Application files: [] diff --git a/Core/Middlewares/Third_Party/Ykc/server_to_charger.c b/Core/Middlewares/Third_Party/Ykc/server_to_charger.c index 1a823b3..53f6656 100644 --- a/Core/Middlewares/Third_Party/Ykc/server_to_charger.c +++ b/Core/Middlewares/Third_Party/Ykc/server_to_charger.c @@ -61,7 +61,7 @@ void on_cmd_frame_type_0X0A(uint8_t stake_index, SERVER_PACK *pack) printf("谷电费率:%d 服务费:%d\r\n", g_charger_manager.fee_model_global.valley_fee_ratio, g_charger_manager.fee_model_global.valley_service_ratio); printf("计损比例: %d%% \r\n", g_charger_manager.fee_model_global.loss_ratio); - printf("\n合并后的费率时间段:\r\n"); + printf("\n合并后的费率时间段 :\r\n"); printf("================================================================\r\n"); int i = 0; @@ -74,19 +74,19 @@ void on_cmd_frame_type_0X0A(uint8_t stake_index, SERVER_PACK *pack) switch (current_type) { case 0x00: - fee_name = "尖时"; + fee_name = " 尖时 "; break; case 0x01: - fee_name = "峰时"; + fee_name = " 峰时 "; break; case 0x02: - fee_name = "平时"; + fee_name = " 平时 "; break; case 0x03: - fee_name = "谷时"; + fee_name = " 谷时 "; break; default: - fee_name = "未知"; + fee_name = " 未知 "; break; } diff --git a/Core/User/Hal/_hal_usart.c b/Core/User/Hal/_hal_usart.c index 86db239..1965935 100644 --- a/Core/User/Hal/_hal_usart.c +++ b/Core/User/Hal/_hal_usart.c @@ -135,7 +135,7 @@ void air724_callback_fun() BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendFromISR(Air724_Message_Queue, uart1_rx_buffer, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - memset(uart1_rx_buffer, 0, UART1_RX_BUFFER_SIZE); + memset(uart1_rx_buffer, 0, UART1_RX_BUFFER_SIZE); } void rs485_callback_fun() diff --git a/Core/User/Task/ChargerTask.c b/Core/User/Task/ChargerTask.c index 4b6f7f3..8bb0974 100644 --- a/Core/User/Task/ChargerTask.c +++ b/Core/User/Task/ChargerTask.c @@ -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)); } \ No newline at end of file diff --git a/Core/User/Task/ChargerTask.h b/Core/User/Task/ChargerTask.h index 08eaa49..bbad5a2 100644 --- a/Core/User/Task/ChargerTask.h +++ b/Core/User/Task/ChargerTask.h @@ -22,6 +22,7 @@ typedef struct { //void Charger_Task_Init(void); void handle_udp_downlink(uint8_t id, const char *cmd, cJSON *json_pack); +void local_on_cmd_send_start_charging(uint8_t stake_index,uint8_t gun_id); diff --git a/Core/User/Task/YkcTask.c b/Core/User/Task/YkcTask.c index 71f9ea0..4685800 100644 --- a/Core/User/Task/YkcTask.c +++ b/Core/User/Task/YkcTask.c @@ -11,6 +11,7 @@ /* Includes ------------------------------------------------------------------*/ #include "YkcTask.h" +#include "ChargerTask.h" #include "lwip/opt.h" #include "lwip/api.h" #include "lwip/sys.h" @@ -83,7 +84,8 @@ void YkcTask_Function(void const *argument) case 3: { charger_to_server_0X13(1, 1); // 上传状态 - osDelay(15000); + local_on_cmd_send_start_charging(1,1); + osDelay(3000); } break; default: