F1CTL/Core/Src/http_server.c
2024-12-17 16:25:23 +08:00

332 lines
10 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 "http_server.h"
// HTML页面模板
#include "html_files.h"
// 解析HTTP请求
static void parse_http_request(uint8_t* buffer, uint16_t length, http_request* req)
{
char* method = strtok((char*)buffer, " ");
char* uri = strtok(NULL, " ");
if(strcmp(method, "GET") == 0) {
req->method = HTTP_GET;
} else if(strcmp(method, "POST") == 0) {
req->method = HTTP_POST;
} else {
req->method = HTTP_UNKNOWN;
}
strncpy(req->uri, uri, MAX_URI_SIZE);
// 如果是POST请求查找请求体
if(req->method == HTTP_POST) {
char* body = strstr((char*)buffer, "\r\n\r\n");
if(body) {
body += 4;
req->body = (uint8_t*)body;
req->body_length = length - (body - (char*)buffer);
}
}
}
// 发送HTTP响应
static int send_all(uint8_t sn, const uint8_t* data, uint16_t length)
{
int total_sent = 0;
while (total_sent < length) {
int sent = send(sn, data + total_sent, length - total_sent);
if (sent <= 0) {
return -1; // 发送错误
}
total_sent += sent;
}
return total_sent;
}
static void send_chunked_response_header(uint8_t sn)
{
const char* header =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n";
send_all(sn, (uint8_t*)header, strlen(header));
}
static void send_chunk(uint8_t sn, const char* data, uint16_t length)
{
char chunk_header[10];
sprintf(chunk_header, "%X\r\n", length);
send_all(sn, (uint8_t*)chunk_header, strlen(chunk_header));
send_all(sn, (uint8_t*)data, length);
send_all(sn, (uint8_t*)"\r\n", 2);
}
static void send_chunk_end(uint8_t sn)
{
send_all(sn, (uint8_t*)"0\r\n\r\n", 5);
}
#define CHUNK_SIZE 2048
// 处理配置页面请求
static void handle_config_page(uint8_t sn)
{
// 发送响应头
send_chunked_response_header(sn);
// 分块发送静态HTML内容
int total_len = strlen(index_html);
int sent = 0;
char buffer[CHUNK_SIZE];
while (sent < total_len) {
int chunk_len = (total_len - sent) > CHUNK_SIZE ? CHUNK_SIZE : (total_len - sent);
memcpy(buffer, index_html + sent, chunk_len);
send_chunk(sn, buffer, chunk_len);
sent += chunk_len;
}
// 发送结束块
send_chunk_end(sn);
}
// 解析并更新网络配置
static void update_network_config(const char* body)
{
char mac[18], ip[16], subnet[16], gateway[16];
uint32_t mac_values[6], ip_values[4], subnet_values[4], gateway_values[4];
if(sscanf(body, "mac=%02X%%3A%02X%%3A%02X%%3A%02X%%3A%02X%%3A%02X&"
"ip=%d.%d.%d.%d&"
"subnet=%d.%d.%d.%d&"
"gateway=%d.%d.%d.%d",
&mac_values[0], &mac_values[1], &mac_values[2],
&mac_values[3], &mac_values[4], &mac_values[5],
&ip_values[0], &ip_values[1], &ip_values[2], &ip_values[3],
&subnet_values[0], &subnet_values[1], &subnet_values[2], &subnet_values[3],
&gateway_values[0], &gateway_values[1], &gateway_values[2], &gateway_values[3]) == 18)
{
// 更新MAC地址
for(int i = 0; i < 6; i++) {
gWIZNETINFO.mac[i] = (uint8_t)mac_values[i];
}
// 更新IP地址
for(int i = 0; i < 4; i++) {
gWIZNETINFO.ip[i] = (uint8_t)ip_values[i];
gWIZNETINFO.sn[i] = (uint8_t)subnet_values[i];
gWIZNETINFO.gw[i] = (uint8_t)gateway_values[i];
}
// 应用新的网络配置
wizchip_setnetinfo(&gWIZNETINFO);
}
}
void http_server_init(void)
{
// 创建服务器socket
socket(HTTP_SERVER_SOCKET, Sn_MR_TCP, HTTP_SERVER_PORT, 0);
listen(HTTP_SERVER_SOCKET);
}
// 三通阀结构体
typedef struct {
uint8_t angle1; // 阀门1角度 (120/210)
uint8_t angle2; // 阀门2角度 (120/210)
} ValveStatus;
// 泵结构体
typedef struct {
uint8_t status1; // 泵1运行状态 (停止/顺时针/逆时针)
uint8_t status2; // 泵2运行状态 (停止/顺时针/逆时针)
uint8_t speed1; // 泵1速度百分比 (0-100)
uint8_t speed2; // 泵2速度百分比 (0-100)
} PumpStatus;
// 设备状态结构体
// 1.下挂设备在位状态在位为1否则为0当前为气泡感应器1个字节
// 2.两个三通阀实时角度每个角度占用1个字节只能为210或120
// 3.泵状态1表示泵顺时针转动中2表示泵逆时针转动中0表示停止两个泵共2个字节
// 4.泵速度2个字节每个泵的速度占用一个字节表示当前泵设定的速度百分比
// 5.气泡感应器读值1表示监测到气泡0表示没有气泡共1个字节
// 6.急停按键状态1个字节1为按下0为正常。
// 7.错误码1个字节用于监控当前系统总体状态如通信异常、控制异常等等具体错误码待定
// 8.初始化状态1个字节 0表示初始化执行中1表示初始化执行成功2表示初始化执行失败。
typedef struct {
uint8_t deviceStatus; // 下挂设备状态
ValveStatus valves; // 两个三通阀状态
PumpStatus pumps; // 两个泵状态
uint8_t bubbleStatus; // 气泡状态
uint8_t stopStatus; // 急停状态
uint8_t errorCode; // 错误码
uint8_t initStatus; // 初始化状态
} DeviceStatus;
DeviceStatus deviceStatus={
.deviceStatus=1,
.valves={
.angle1=0,
.angle2=0
},
.pumps={
.status1=0,
.status2=0,
.speed1=60,
.speed2=70
},
.bubbleStatus=0,
.stopStatus=0,
.errorCode=0,
.initStatus=1
};
// 发送JSON响应头
static void send_json_response_header(uint8_t sn)
{
const char* header =
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Connection: close\r\n" // 明确指示关闭连接
"Cache-Control: no-cache, no-store\r\n" // 禁止缓存
"\r\n";
send_all(sn, (uint8_t*)header, strlen(header));
}
// 处理状态请求
static void handle_status_request(uint8_t sn)
{
char json_buffer[512];
// DeviceStatus deviceStatus; // 确保能访问到deviceStatus变量
// 构建JSON响应
int len = sprintf(json_buffer,
"{"
"\"deviceStatus\":%d,"
"\"valves\":{"
"\"angle1\":%d,"
"\"angle2\":%d"
"},"
"\"pumps\":{"
"\"status1\":%d,"
"\"status2\":%d,"
"\"speed1\":%d,"
"\"speed2\":%d"
"},"
"\"bubbleStatus\":%d,"
"\"stopStatus\":%d,"
"\"errorCode\":%d,"
"\"initStatus\":%d"
"}",
deviceStatus.deviceStatus,
deviceStatus.valves.angle1,
deviceStatus.valves.angle2,
deviceStatus.pumps.status1,
deviceStatus.pumps.status2,
deviceStatus.pumps.speed1,
deviceStatus.pumps.speed2,
deviceStatus.bubbleStatus,
deviceStatus.stopStatus,
deviceStatus.errorCode,
deviceStatus.initStatus
);
// 发送响应
send_json_response_header(sn);
send_all(sn, (uint8_t*)json_buffer, len);
// 发送完成后主动关闭连接
disconnect(sn);
printf("send status success!\r\n");
}
// 处理网络配置请求
static void handle_netconfig_request(uint8_t sn)
{
char json_buffer[256];
// 构建JSON响应
int len = sprintf(json_buffer,
"{"
"\"mac\":[%d,%d,%d,%d,%d,%d],"
"\"ip\":[%d,%d,%d,%d],"
"\"sn\":[%d,%d,%d,%d],"
"\"gw\":[%d,%d,%d,%d]"
"}",
gWIZNETINFO.mac[0], gWIZNETINFO.mac[1], gWIZNETINFO.mac[2],
gWIZNETINFO.mac[3], gWIZNETINFO.mac[4], gWIZNETINFO.mac[5],
gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3],
gWIZNETINFO.sn[0], gWIZNETINFO.sn[1], gWIZNETINFO.sn[2], gWIZNETINFO.sn[3],
gWIZNETINFO.gw[0], gWIZNETINFO.gw[1], gWIZNETINFO.gw[2], gWIZNETINFO.gw[3]
);
// 发送响应
send_json_response_header(sn);
send_all(sn, (uint8_t*)json_buffer, len);
}
void http_server_task(void)
{
uint8_t buffer[2048];
uint16_t size;
uint8_t status = getSn_SR(HTTP_SERVER_SOCKET);
switch(status)
{
case SOCK_INIT:
listen(HTTP_SERVER_SOCKET);
break;
case SOCK_LISTEN:
// 等待连接
break;
case SOCK_ESTABLISHED:
{
if((size = getSn_RX_RSR(HTTP_SERVER_SOCKET)) > 0)
{
size = recv(HTTP_SERVER_SOCKET, buffer, sizeof(buffer));
if(size > 0)
{
http_request req;
parse_http_request(buffer, size, &req);
if(req.method == HTTP_GET) {
if(strcmp(req.uri, "/status") == 0) {
handle_status_request(HTTP_SERVER_SOCKET);
}
else if(strcmp(req.uri, "/netconfig") == 0) {
handle_netconfig_request(HTTP_SERVER_SOCKET);
}
else {
handle_config_page(HTTP_SERVER_SOCKET);
}
}
else if(req.method == HTTP_POST && strcmp(req.uri, "/config") == 0) {
update_network_config((char*)req.body);
send_chunked_response_header(HTTP_SERVER_SOCKET);
send_chunk(HTTP_SERVER_SOCKET,
"<html><head><meta http-equiv='refresh' content='0;url=/'></head></html>",
strlen("<html><head><meta http-equiv='refresh' content='0;url=/'></head></html>"));
send_chunk_end(HTTP_SERVER_SOCKET);
}
}
}
break;
}
case SOCK_CLOSE_WAIT:
disconnect(HTTP_SERVER_SOCKET);
break;
case SOCK_CLOSED:
socket(HTTP_SERVER_SOCKET, Sn_MR_TCP, HTTP_SERVER_PORT, 0);
break;
}
}