feat: 新增 ack 机制

This commit is contained in:
2025-03-01 19:22:24 +08:00
parent 33b804785b
commit 7bb0f8f755

View File

@@ -29,10 +29,15 @@
static by_serial_t serial_port;
static by_ringbuf_t ring_buffer;
static unsigned char send_buffer[FRAME_SIZE];
static unsigned char recv_ack_data_buffer[DATA_SIZE];
static unsigned char frame_counter = 0;
static unsigned char data_len = 0;
unsigned char output_data[8192];
static unsigned char ack_flag = 0;
unsigned char output_data[8192]; // FIXME 有可能越界
static int output_len = 0;
static unsigned char ack_data_dummy[4] = {0x00, 0x00, 0x00, 0x00};
static unsigned char ack_data[DATA_SIZE - 2] = {0};
static int ack_data_len = 0;
// 定义队列结构体
typedef struct
@@ -50,6 +55,7 @@ static int stop_receiving = false; // 控制接收
// 前向声明
void *receive_thread_func(void *arg);
int generate_response(unsigned char *data, int length, int ack);
// 计算 CRC32 校验和
unsigned long calculate_crc32(const unsigned char *data, size_t length)
@@ -117,6 +123,23 @@ int parse_frame(by_ringbuf_t *ringbuf, unsigned char *output_data, int *output_l
printf("CRC mismatch! Expected: %08X, Received: %08X\n", calculated_crc, received_crc);
return -2; // CRC 校验失败,丢弃该帧,严重错误
}
if (0xFF == seq)
{
// printf("Received ACK frame!\r\n");
*output_len = 0;
if (0x1926 == *(unsigned short *)(data_segment))
{
memcpy(recv_ack_data_buffer, data_segment + 2, valid_data_len - 2);
ack_data_len = valid_data_len - 2;
printf("Received ACK frame!\r\n");
return 2;
}
memset(recv_ack_data_buffer, 0, DATA_SIZE);
return 3;
}
// 将有效数据拼接到输出缓冲区
memcpy(&output_data[*output_len], data_segment, valid_data_len);
*output_len += valid_data_len;
@@ -141,6 +164,27 @@ void *receive_thread_func(void *arg)
int parse_result = parse_frame(&ring_buffer, output_data, &output_len);
if (parse_result == 1)
{
int ack_data_len_t = 0;
if (ack_data_len > DATA_SIZE - 2)
{
ack_data_len_t = DATA_SIZE - 2;
}
else
{
ack_data_len_t = ack_data_len;
}
if (ack_data[0] == 0x00)
{
generate_response(ack_data_dummy, sizeof(ack_data_dummy), 1);
}
else
{
generate_response(ack_data, ack_data_len_t, 1);
// generate_response(ack_data_dummy, sizeof(ack_data_dummy), 1);
}
// 将解析后的数据放入队列
pthread_mutex_lock(&queue_mutex);
if ((queue_tail + 1) % QUEUE_MAX_SIZE != queue_head)
@@ -166,8 +210,21 @@ void *receive_thread_func(void *arg)
}
else if (parse_result == -2)
{
// TODO 此处不能回应 NAK必须等数据发完
output_len = 0;
break; // 数据不足或解析失败,退出循环
break; // 严重错误,丢弃包
}
else if (parse_result == 2)
{
ack_flag = 1;
printf("Received ACK frame!\r\n");
break;
}
else if (parse_result == 3)
{
ack_flag = 2;
printf("Received NACK frame!\r\n");
break;
}
}
usleep(1000); // 避免占用过多 CPU
@@ -175,6 +232,58 @@ void *receive_thread_func(void *arg)
return NULL;
}
int generate_response(unsigned char *data, int length, int ack)
{
// 响应帧结构(与普通帧相似,帧号为 0xFF数据段前 2 个字节为 ACK 标志,负载数据必须小于普通帧满数据大小,以确保被识别成孤立帧)
unsigned char send_buffer[FRAME_SIZE];
memset(send_buffer, 0, FRAME_SIZE);
// 构造帧头
unsigned short header = FRAME_HEADER;
memcpy(send_buffer, &header, 2);
// 构造帧序号和数据长度
// memcpy(send_buffer + 2, 0xFF, 1);
*(send_buffer + 2) = 0xFF;
length += 2; // 包含 ACK 标志长度
if (length > DATA_SIZE)
{
return -1; // 数据超长
}
memcpy(send_buffer + 3, &length, 1);
// magic number for ack
unsigned short ack_flag = 0x1926;
if (ack)
{
memcpy(send_buffer + HEADER_SIZE, &ack_flag, 2);
}
else
{
memset(send_buffer + HEADER_SIZE, 0, 2);
}
// 拷贝该帧对应数据段
memcpy(send_buffer + HEADER_SIZE + 2, data, length - 2);
// 计算 CRC32 校验和
unsigned long crc = calculate_crc32(send_buffer, HEADER_SIZE + DATA_SIZE + FEC_SIZE);
memcpy(send_buffer + HEADER_SIZE + DATA_SIZE + FEC_SIZE, &crc, CHECKSUM_SIZE);
unsigned short tail = FRAME_TAIL;
memcpy(send_buffer + HEADER_SIZE + DATA_SIZE + FEC_SIZE + CHECKSUM_SIZE, &tail, TAIL_SIZE);
// 发送响应帧
if (by_serial_write(&serial_port, send_buffer, FRAME_SIZE) != 0)
{
return -2; // 发送失败
}
return 0;
}
// 初始化串口
static PyObject *serial_init(PyObject *self, PyObject *args)
{
@@ -202,7 +311,7 @@ static PyObject *serial_init(PyObject *self, PyObject *args)
PyErr_SetString(PyExc_RuntimeError, "Failed to start receive thread");
return NULL;
}
Py_RETURN_NONE;
Py_RETURN_TRUE;
}
// 发送数据
@@ -250,8 +359,27 @@ static PyObject *serial_send(PyObject *self, PyObject *args)
frame_counter++;
usleep(80000);
}
unsigned short ack_timeout = 10;
// 等待 ACK 响应
while (!ack_flag)
{
usleep(100000);
if (ack_timeout-- == 0)
{
ack_flag = 0;
frame_counter = 0;
Py_RETURN_NONE;
printf("ACK timeout\r\n");
Py_RETURN_FALSE;
}
}
ack_flag = 0;
frame_counter = 0;
// Py_RETURN_TRUE;
// 返回 ACK 响应
PyObject *result = Py_BuildValue("y#", recv_ack_data_buffer, ack_data_len);
ack_data_len = 0;
return result;
}
// 接收数据
@@ -274,18 +402,67 @@ static PyObject *serial_receive(PyObject *self, PyObject *args)
return result;
}
static PyObject *serial_set_ack_data(PyObject *self, PyObject *args)
{
const char *input_data; // 用于存储输入的二进制数据
Py_ssize_t input_length; // 用于存储输入数据的长度
// 解析参数,期望接收到一个 bytes 对象
if (!PyArg_ParseTuple(args, "y#", &input_data, &input_length))
{
PyErr_SetString(PyExc_TypeError, "Expected a bytes object");
return NULL;
}
// 检查输入数据是否为空
if (input_length == 0)
{
PyErr_SetString(PyExc_ValueError, "Input data cannot be empty");
return NULL;
}
// 检查输入数据是否为空
if (input_length > (DATA_SIZE - 2))
{
PyErr_SetString(PyExc_ValueError, "Input data is too long");
return NULL;
}
// // 创建一个缓冲区用于存储处理后的数据
// char *processed_data = (char *)malloc(input_length);
// if (processed_data == NULL) {
// PyErr_SetString(PyExc_MemoryError, "Failed to allocate memory");
// return NULL;
// }
// // 将处理后的数据封装为 Python 的 bytes 对象
// PyObject *result = PyBytes_FromStringAndSize(processed_data, input_length);
// // 释放分配的内存
// free(processed_data);
// // 返回结果
// return result;
memcpy(ack_data, input_data, input_length);
ack_data_len = input_length;
Py_RETURN_NONE;
}
// 模块方法表
static PyMethodDef SerialMethods[] = {
{"init", serial_init, METH_VARARGS, "Initialize serial port"},
{"send", serial_send, METH_VARARGS, "Send data over serial port"},
{"receive", serial_receive, METH_VARARGS, "Receive data from serial port"},
{"set_ack_data", serial_set_ack_data, METH_VARARGS, "Set ACK data"},
{NULL, NULL, 0, NULL}};
// 模块定义
static struct PyModuleDef serialmodule = {
PyModuleDef_HEAD_INIT,
"serial_module",
NULL,
"A module to transfer serializable data over serial port",
-1,
SerialMethods};