From d32b31bf92875cfbd32965fa1b03a313766ceb0f Mon Sep 17 00:00:00 2001 From: coffee Date: Thu, 28 Aug 2025 15:00:18 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E5=AE=8C=E5=96=84=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/HDProtocolServer.h | 153 +++++++++++++++++ src/HDProtocolServer.c | 337 +++++++++++++++++++++++++++++++++++++ 2 files changed, 490 insertions(+) create mode 100644 include/HDProtocolServer.h create mode 100644 src/HDProtocolServer.c diff --git a/include/HDProtocolServer.h b/include/HDProtocolServer.h new file mode 100644 index 0000000..33f0e2d --- /dev/null +++ b/include/HDProtocolServer.h @@ -0,0 +1,153 @@ +/** + * 日期: 2025-08-26 + * 作者: coffee + * 描述: 调度协议, 用于多数据口接收数据和发送数据后回复数据临时调度回来 + */ + +#ifndef __HD_PROTOCOL_SERVER_H__ +#define __HD_PROTOCOL_SERVER_H__ + + +#include "HRingBuffer.h" +#include + +#ifndef __COUNT_ARGS_IMPL +#define __COUNT_ARGS_IMPL(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N +#endif +#ifndef __COUNT_ARGS +#define __COUNT_ARGS(...) __COUNT_ARGS_IMPL(dummy, ##__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#endif + +/** + * demo: + * 1. 设置调度回调函数 HDProtocolSetCallback(HScheduleCallback callback); + * 2. 注册调度数据信息数组 HDProtocolRegisterInfo(HDProtocolServerInfo *info, uint8_t src, void *data, int dataLen); + * 3. HDProtocolRun(); + */ + +// 再次发送超时时间 +#define HDPROTOCOL_DELAY_TIMEOUT (100) + +// 数据占用时间 +#define HDPROTOCOL_OCCUPY_TIMEOUT (3000) + + +enum eHDProtocolSupport +{ + kHDProtocolNone = 0x00, ///< 无协议, 不阻塞等待 + kHDProtocolFpga = 0x01, ///< fpga协议, 独占解析端 + kHDProtocolVP = 0x02, ///< vp协议, 独占解析端 +}; + +/** + * @brief 协议信息结构 + * @note 协议信息结构里面的buffer需要用户自己分配内存, 通过 HDProtocolInitInfo 初始化传递内存 + * 用户只需要读取环状缓冲区即可 + */ +typedef struct __attribute__((__packed__)) HDProtocolServerInfo +{ + HRingBufferType *readBuff; ///< 读取环状缓冲区, 内存需要用户调用 HDProtocolInitInfo 提供, 最低需要 sizeof(_HRingBufferBase) + 1 的空间 + uint8_t supportProtocol; ///< 支持的协议, 用于解析端选择, 避免多路口同时解析 + uint8_t src; ///< 源数据口 + uint8_t dst; ///< 目标数据口(如果需要始终转发可以修改该项目, 否则默认和src一样) + uint8_t tmpDst; ///< 临时回送目标数据口(用于该指令需要先请求某个数据后才能回复的情况) + uint8_t tmpReadCount : 6; ///< 临时数据最大等待回复次数 + uint8_t tmpEnable : 1; ///< 当前临时目标等待数据, 需要等待数据内部标记开启, 数据完成或者超时后自动关闭 + uint8_t delayCount; ///< 延时等待次数(默认: 1), 每次等待时间为 HSCHEDULE_DELAY_TIMEOUT +} HDProtocolServerInfo; + +/** + * @brief 读取回调函数 + * @param info 协议信息 + * param isTimeout 是否超时, 1: 表示当前数据已经超时, 协议需要重置让渡给其他解析, 0: 表示当前数据未超时 + * @return 返回协议解析长度, 返回0表示解析结束 + */ +typedef int (*HDProtocolReadCallback)(HDProtocolServerInfo *info, uint8_t isTimeout); +typedef void (*HDProtocolWriteCallback)(HDProtocolServerInfo *info, const uint8_t *data, int len); + +/** + * @brief 设置协议回调函数 + * @param readCall 读取回调函数 + * @param writeCall 写入回调函数 + */ +void HDProtocolSetCallback(HDProtocolReadCallback readCall, HDProtocolWriteCallback writeCall); + +/** + * @brief 初始化协议信息结构, 将原始缓冲器转换成环状缓冲区(需要消耗一些字节) + * @param info 协议信息结构 + * @param src 源数据口和目标数据口 + * @param data 原始缓冲区 + * @param dataLen 原始缓冲区长度 + */ +void HDProtocolInitInfo(HDProtocolServerInfo *info, uint8_t src, void *data, int dataLen); + +/** + * @brief 设置支持的协议 + * @param info 协议信息结构 + * @param len 支持的协议个数 + * @param ... 支持的协议 + */ +void _HDProtocolSetSupportProtocol(HDProtocolServerInfo *info, int len, ...); +#define HDProtocolSetSupportProtocol(info, ...) _HDProtocolSetSupportProtocol(info, __COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__) + +/** + * @brief 注册协议信息 + * @param info 协议信息结构 + * @param len 协议信息结构长度 + */ +void HDProtocolRegisterInit(HDProtocolServerInfo *info, int len); + +/** + * @brief 根据当前路由发送(用于解析数据后回送数据, 如果不是读取回调的时候调用, 则找不到对应路由报错, 返回0) + * @param data 发送数据 + * @param len 发送数据长度 + * @return 发送成功返回1 失败返回0 + */ +uint8_t HDProtocolCurrSend(const void *data, int len); + +/** + * @brief 根据目标数据口发送 + * @param dst 需要往那个目标数据口发送 + * @param data 发送数据 + * @param len 发送数据长度 + */ +void HDProtocolSendPort(uint8_t dst, const void *data, int len); + +/** + * @brief 临时路由发送(用于发送目标端口的数据后, 解析端临时更改目标端口, 通过 HDProtocolGetDest 获取目标端口) + * @param src 回送数据数据路由标记 + * @param dst 需要往那个目标数据口发送 + * @param data 发送数据 + * @param len 发送数据长度 + * @param needReadCount 需要读取次数 + * @param waitCount 等待次数 + */ +void HDProtocolTmpSend(uint8_t src, uint8_t dst, const void *data, int len, uint8_t needReadCount, uint8_t waitCount); + +/** + * @brief 获取目标数据口 + * @param info 协议信息 + * @return 目标数据口 + */ +uint8_t HDProtocolGetDest(HDProtocolServerInfo *info); + +/** + * @brief 获取当前协议信息 + * @return 当前协议信息 + */ +HDProtocolServerInfo *HDProtocolGetCurrInfo(); + +/** + * @brief 告知服务当前对应源端读取数据 + * @param src 源数据口 + * @param data 读取数据 + * @param len 读取数据长度 + */ +void HDProtocolRead(uint8_t src, const void *data, int len); + +/** + * @brief 运行调度协议 + */ +void HDProtocolRun(); + +#endif // __HD_PROTOCOL_SERVER_H__ diff --git a/src/HDProtocolServer.c b/src/HDProtocolServer.c new file mode 100644 index 0000000..45efca1 --- /dev/null +++ b/src/HDProtocolServer.c @@ -0,0 +1,337 @@ + + +#include "HDProtocolServer.h" +#include "HRingBuffer.h" +#include "HDLog.h" +#include +#include + + +#ifndef HDDelayMs +#define HDDelayMs(ms) +#endif + +#define OCCUPIED_MAX (5) + + +struct __attribute__((__packed__)) OccupiedInfo { + uint32_t occupyTime; ///< 当前数据占用时间(ms), 用于超时 + uint8_t protocol; ///< 占用的协议 + uint8_t index; ///< 占用的索引 + uint8_t enbale : 1; ///< 是否占用 + uint8_t waitOccupy : 1; ///< 是否等待占用 +}; + +struct __attribute__((__packed__)) HScheduleRegisterInfo { + HDProtocolReadCallback readCall; ///< 读取回调 + HDProtocolWriteCallback writeCall; ///< 写入回调 + HDProtocolServerInfo *info; ///< 协议信息 + uint8_t infoLen; ///< 协议信息长度 + uint8_t useIndex; ///< 当前使用的索引 +}; + +static struct HScheduleRegisterInfo sInfo; +static struct OccupiedInfo sOccupiedInfo[OCCUPIED_MAX]; + +// 判断当前协议是否占用 +static uint8_t IsOccupied(uint8_t currIndex, uint8_t protocol, uint8_t *occupiedIndex) +{ + for (int i = 0; i < OCCUPIED_MAX; ++i) { + if (sOccupiedInfo[i].enbale == 0) { + continue; + } + + if ((sOccupiedInfo[i].protocol & protocol) == 0) { + continue; + } + + // 占用索引 + if (occupiedIndex != NULL) { + *occupiedIndex = i; + } + + if (sOccupiedInfo[i].index == currIndex) { + return 0; + } + + sOccupiedInfo[i].waitOccupy = 1; + return 1; + } + + return 0; +} + +static void SetOccupied(uint8_t currIndex, uint8_t protocol, uint8_t enable) +{ + uint8_t index = OCCUPIED_MAX; + for (int i = 0; i < OCCUPIED_MAX; ++i) { + if (sOccupiedInfo[i].enbale == 0) { + if (index == OCCUPIED_MAX) { + index = i; + } + + continue; + } + + // 检查当前索引是否已经使用占用 + if (sOccupiedInfo[i].index != currIndex) { + continue; + } + + // 如果是关闭的直接关闭掉 + if (enable == 0) { + sOccupiedInfo[i].enbale = 0; + sOccupiedInfo[i].index = 0; + sOccupiedInfo[i].protocol = 0; + sOccupiedInfo[i].occupyTime = 0; + sOccupiedInfo[i].waitOccupy = 0; + return ; + } + + sOccupiedInfo[i].protocol = protocol; + sOccupiedInfo[i].occupyTime = HDLogGetTime(); + return ; + } + + if (enable == 0 || index == OCCUPIED_MAX) { + return ; + } + + sOccupiedInfo[index].enbale = 1; + sOccupiedInfo[index].index = currIndex; + sOccupiedInfo[index].protocol = protocol; + sOccupiedInfo[index].occupyTime = HDLogGetTime(); +} + +static void WaitRead(HDProtocolServerInfo *info) +{ + if (info->tmpEnable == 0) { + return ; + } + + for (int i = 0; i < info->delayCount; ++i) { + for (int j = 0; j < HDPROTOCOL_DELAY_TIMEOUT; ++j) { + HDDelayMs(1); + HDProtocolRun(); + if (info->tmpEnable == 0) { + break; + } + } + + if (info->tmpEnable == 0) { + break; + } + } + + info->tmpEnable = 0; +} + +static void ParseReadData(int index, int isTimeout) +{ + sInfo.useIndex = index; + const int len = sInfo.readCall(&sInfo.info[index], isTimeout); + sInfo.useIndex = sInfo.infoLen; + if (len == 0 && sInfo.info[index].tmpEnable) { + // 临时路由转发数据 + if (sInfo.info[index].tmpReadCount > 0) { + --sInfo.info[index].tmpReadCount; + } + + if (sInfo.info[index].tmpReadCount == 0) { + sInfo.info[index].tmpEnable = 0; + } + } + + if (isTimeout) { + sInfo.info[index].tmpEnable = 0; + } + + SetOccupied(index, sInfo.info[index].supportProtocol, len == 0 ? 0 : 1); +} + +void HDProtocolSetCallback(HDProtocolReadCallback readCall, HDProtocolWriteCallback writeCall) +{ + sInfo.readCall = readCall; + sInfo.writeCall = writeCall; +} + +void HDProtocolInitInfo(HDProtocolServerInfo *info, uint8_t src, void *data, int dataLen) +{ + if (info == NULL) { + return ; + } + + memset(info, 0, sizeof(HDProtocolServerInfo)); + info->readBuff = data; + HRingbufferInit8(info->readBuff, dataLen); + info->delayCount = 1; + info->src = src; + info->dst = src; +} + +void _HDProtocolSetSupportProtocol(HDProtocolServerInfo *info, int len, ...) +{ + if (info == NULL) { + return; + } + + va_list args; + va_start(args, len); + for (int i = 0; i < len; ++i) { + info->supportProtocol |= (1 << va_arg(args, int)); + } + + va_end(args); +} + + +void HDProtocolRegisterInit(HDProtocolServerInfo *info, int len) +{ + sInfo.info = info; + sInfo.infoLen = len; +} + +uint8_t HDProtocolCurrSend(const void *data, int len) +{ + HDProtocolServerInfo *info = HDProtocolGetCurrInfo(); + if (info == NULL) { + LogD("curr not read call"); + return 0; + } + + WaitRead(info); + sInfo.writeCall(info, (const uint8_t *)data, len); + return 1; +} + +void HDProtocolSendPort(uint8_t dst, const void *data, int len) +{ + if (sInfo.writeCall == NULL || sInfo.info == NULL) { + return; + } + + if (dst >= sInfo.infoLen) { + LogE("index[%d] is out of range[%d]", dst, sInfo.infoLen); + return; + } + + WaitRead(&sInfo.info[dst]); + sInfo.writeCall(&sInfo.info[dst], (const uint8_t *)data, len); +} + +void HDProtocolTmpSend(uint8_t src, uint8_t dst, const void *data, int len, uint8_t needReadCount, uint8_t waitCount) +{ + if (sInfo.writeCall == NULL || sInfo.info == NULL) { + return; + } + + if (src >= sInfo.infoLen) { + LogE("index[%d] is out of range[%d]", src, sInfo.infoLen); + return; + } + + WaitRead(&sInfo.info[src]); + sInfo.info[src].tmpEnable = 1; + sInfo.info[src].tmpDst = dst; + sInfo.info[src].delayCount = waitCount; + sInfo.info[src].tmpReadCount = needReadCount; + sInfo.writeCall(&sInfo.info[src], (const uint8_t *)data, len); +} + +uint8_t HDProtocolGetDest(HDProtocolServerInfo *info) +{ + if (info == NULL) { + return 0; + } + + if (info->tmpEnable) { + return info->tmpDst; + } + + return info->dst; +} + +HDProtocolServerInfo *HDProtocolGetCurrInfo() +{ + if (sInfo.useIndex >= sInfo.infoLen) { + return NULL; + } + + return &sInfo.info[sInfo.useIndex]; +} + +void HDProtocolRead(uint8_t src, const void *data, int len) +{ + if (sInfo.readCall == NULL || sInfo.info == NULL) { + return; + } + + if (src >= sInfo.infoLen) { + LogE("index[%d] is out of range[%d]", src, sInfo.infoLen); + return; + } + + if (sInfo.info[src].readBuff == NULL) { + LogE("readBuff is NULL"); + return; + } + + const uint8_t *srcData = (const uint8_t *)data; + uint8_t occupy = 0xff; + for (int i = 0; i < len; ++i) { + if (HRingBufferFull(sInfo.info[src].readBuff)) { + // 只需要检查一次 + if (occupy == 0xff) { + occupy = IsOccupied(src, sInfo.info[src].supportProtocol, NULL); + } + + // 检查是否占用 + if (occupy == 0) { + ParseReadData(src, 0); + } + } + + HRingBufferAddDataOver(sInfo.info[src].readBuff, srcData[i]); + } +} + +void HDProtocolRun() +{ + if (sInfo.readCall == NULL || sInfo.info == NULL) { + return; + } + + for (int i = 0; i < sInfo.infoLen; ++i) { + if (HRingBufferEmpty(sInfo.info[i].readBuff)) { + continue; + } + + // 有之前的数据已经占用了, 并且检查是否超时 + uint8_t occupiedIndex = OCCUPIED_MAX; + if (IsOccupied(i, sInfo.info[i].supportProtocol, &occupiedIndex)) { + // 解析被占用, 检查超时 + if (HDLogGetTime() - sOccupiedInfo[occupiedIndex].occupyTime <= HDPROTOCOL_OCCUPY_TIMEOUT) { + continue; + } + + // 通知协议超时 + ParseReadData(occupiedIndex, 1); + + // 如果还占用, 说明用户那边自己要求继续占用 + if (IsOccupied(i, sInfo.info[i].supportProtocol, &occupiedIndex)) { + continue; + } + } + + uint8_t waitTime = 0; + if (sOccupiedInfo[occupiedIndex].waitOccupy) { + // 还有其他协议等待抢占, 检查时间 + if (HDLogGetTime() - sOccupiedInfo[occupiedIndex].occupyTime > HDPROTOCOL_OCCUPY_TIMEOUT) { + waitTime = 1; + } + } + + ParseReadData(i, waitTime); + } +} +