1. 完善协议服务
This commit is contained in:
parent
129a54b843
commit
d32b31bf92
153
include/HDProtocolServer.h
Normal file
153
include/HDProtocolServer.h
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 日期: 2025-08-26
|
||||
* 作者: coffee
|
||||
* 描述: 调度协议, 用于多数据口接收数据和发送数据后回复数据临时调度回来
|
||||
*/
|
||||
|
||||
#ifndef __HD_PROTOCOL_SERVER_H__
|
||||
#define __HD_PROTOCOL_SERVER_H__
|
||||
|
||||
|
||||
#include "HRingBuffer.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#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__
|
||||
337
src/HDProtocolServer.c
Normal file
337
src/HDProtocolServer.c
Normal file
@ -0,0 +1,337 @@
|
||||
|
||||
|
||||
#include "HDProtocolServer.h"
|
||||
#include "HRingBuffer.h"
|
||||
#include "HDLog.h"
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user