Browse Source

新增serial_clear_recv_buffer_head()函数

niujiuru 18 hours ago
parent
commit
869cd8964f
2 changed files with 415 additions and 398 deletions
  1. 338 324
      swapi/subjects/serial/serial.c
  2. 77 74
      swapi/subjects/serial/serial.h

+ 338 - 324
swapi/subjects/serial/serial.c

@@ -1,324 +1,338 @@
-#include "swapi.h"
-#include "swmem.h"
-#include "swthrd.h"
-#include "swtcp.h"
-#include "swstring.h"
-#include "serial.h"
-
-struct serial_t
-{
-    /* fd */
-    int fd;
-    /* 名称 */
-    char name[128];
-    /* 波特率 */
-    int baudrate;
-    /* 校检位: "odd"-奇校检; "even"-偶校检; "no"-无校检 */
-    char parityCheck[5];
-
-    /* 原有配置 */
-    struct termios oldtio;
-    bool bGetOldtioOK;
-
-    /* 数据接收线程句柄 */
-    void *hRecvThrd;
-    /* 数据接收缓存 */
-    unsigned char recvBuf[65535];
-    /* 已接收的字节数 */
-    int nRecvBytes;
-    /* 处理已接收的数据(用户回调) */
-    PSerialRecvHandler pRecvHandler;
-    /* 无数据空闲时执行(用户回调) */
-    PSerialIdleHandler pIdleHandler;
-
-    /* 日志打印前缀 */
-    char log_prefix[MAX_LINE_CHARS];
-    /* 日志打印缓存 */
-    char *pLogRecvBuf; // 堆内存
-};
-
-/* 数据接收处理(线程回调) */
-static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam);
-
-/* 打开串口(8个数据位1个停止位), NULL失败, NOT NULL成功 */
-void *serial_open(const char *serialName, int baudrate, const char *parityCheck /* "odd"-奇校检; "even"-偶校检; NULL或其它无效值-无校检 */,
-                  PSerialRecvHandler pRecvHandler, PSerialIdleHandler pIdleHandler, void *pUserData)
-{
-    struct serial_t *pSerial;
-    struct termios newtio;
-    int nSpeed;
-
-    /* 1, 创建串口对象 */
-    if(!serialName) return NULL;
-    pSerial = (struct serial_t *)sw_heap_malloc(sizeof(struct serial_t));
-    if(!pSerial) return NULL;
-    else
-    {
-        memset(pSerial, 0, sizeof(struct serial_t));
-        strncpy(pSerial->name, serialName, sizeof(pSerial->name)-1);
-    }
-
-    /* 2, 打开串口 */
-    pSerial->fd = open(pSerial->name, O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC);
-    if(pSerial->fd == -1) goto ErrorP;
-
-    if(flock(pSerial->fd, LOCK_EX | LOCK_NB) == -1) goto ErrorP;
-
-    /* 3, 保存当前串口配置 */
-    if(tcgetattr(pSerial->fd, &pSerial->oldtio) == -1) goto ErrorP;
-    else pSerial->bGetOldtioOK = true;
-
-    /* 4, 设置阻塞 */
-    if(fcntl(pSerial->fd, F_SETFL, 0) == -1) goto ErrorP;
-
-    /* 5, 设置参数(8 databits, no parity, 1 stopbit, 1 second read timeout) */
-    pSerial->baudrate = baudrate;
-    switch(pSerial->baudrate)
-    {
-        case      0 : nSpeed = B0;       break;
-        case     50 : nSpeed = B50;      break;
-        case     75 : nSpeed = B75;      break;
-        case    110 : nSpeed = B110;     break;
-        case    134 : nSpeed = B134;     break;
-        case    150 : nSpeed = B150;     break;
-        case    200 : nSpeed = B200;     break;
-        case    300 : nSpeed = B300;     break;
-        case    600 : nSpeed = B600;     break;
-        case   1200 : nSpeed = B1200;    break;
-        case   1800 : nSpeed = B1800;    break;
-        case   2400 : nSpeed = B2400;    break;
-        case   4800 : nSpeed = B4800;    break;
-        case   9600 : nSpeed = B9600;    break;
-        case  19200 : nSpeed = B19200;   break;
-        case  38400 : nSpeed = B38400;   break;
-        case  57600 : nSpeed = B57600;   break;
-        case 115200 : nSpeed = B115200;  break;
-        case 230400 : nSpeed = B230400;  break;
-        default     : goto ErrorP;
-    }
-    memset(&newtio, 0, sizeof(newtio));
-    newtio.c_cflag = nSpeed | CS8 | CLOCAL | CREAD; // 设置波特率、数据带宽、本地连接、接收使能
-    newtio.c_cflag &= ~CSTOPB; // 一个停止位
-    if(xstrcasecmp(parityCheck, "odd") == 0)
-    {  // 使能奇校检
-        newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD;
-        newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "odd");
-    }
-    else if(xstrcasecmp(parityCheck, "even") == 0)
-    { // 使能偶校检
-        newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD;
-        newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "even");
-    }
-    else
-    { // 无校检
-        newtio.c_cflag &= ~PARENB;
-        newtio.c_iflag = IGNPAR; strcpy(pSerial->parityCheck, "no");
-    }
-    newtio.c_oflag = 0;
-    newtio.c_lflag = 0;
-    newtio.c_cc[VMIN] = 0;  // read block untill n bytes are received, 阻塞模式有效
-    newtio.c_cc[VTIME] = 1; // read block untill a timer expires(单位: 百毫秒, 十分之一秒), 阻塞模式有效
-    if(tcflush(pSerial->fd, TCIOFLUSH) == -1) goto ErrorP; // 清空串口输入、输出缓存
-    if(tcsetattr(pSerial->fd, TCSANOW, &newtio) == -1) goto ErrorP; // 激活新配置
-
-    /* 6, 生成日志打印前缀 */
-    if(xstrcasecmp(pSerial->parityCheck, "odd") == 0) sprintf(pSerial->log_prefix, "[%s:%d(odd parity)]", pSerial->name, pSerial->baudrate);
-    else if(xstrcasecmp(pSerial->parityCheck, "even") == 0) sprintf(pSerial->log_prefix, "[%s:%d(even parity)]", pSerial->name, pSerial->baudrate);
-    else sprintf(pSerial->log_prefix, "[%s:%d(no parity)]", pSerial->name, pSerial->baudrate);
-
-    /* 7, 申请日志打印缓存 */
-    pSerial->pLogRecvBuf = (char *)sw_heap_malloc(sizeof(pSerial->log_prefix)+MAX_LINE_CHARS+sizeof(pSerial->recvBuf)*2);
-    if(!pSerial->pLogRecvBuf) goto ErrorP;
-
-    /* 8, 打开串口数据接收线程 */
-    pSerial->pRecvHandler = pRecvHandler; pSerial->pIdleHandler = pIdleHandler; // 用户回调
-    pSerial->hRecvThrd = sw_thrd_create(pSerial->name, THREAD_DEFAULT_PRIORITY, 4*1024*1024, serial_data_recv_proc, (unsigned long)pSerial, (unsigned long)pUserData);
-    if(!pSerial->hRecvThrd) goto ErrorP;
-    else sw_thrd_resume(pSerial->hRecvThrd); // 激活数据接收线程
-
-    return (void *)pSerial;
-
-    /* 9, 错误 */
-ErrorP:
-    serial_close(pSerial, 0);
-    return NULL;
-}
-
-/* 关闭串口 */
-void serial_close(void *hSerial, int timeout)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-
-    if(pSerial && pSerial->hRecvThrd) sw_thrd_destroy(pSerial->hRecvThrd, timeout); // 销毁数据接收线程
-
-    if(pSerial->pLogRecvBuf) sw_heap_free((void *)pSerial->pLogRecvBuf); // 释放日志打印缓存
-
-    if(pSerial && pSerial->fd != -1)
-    {
-        if(pSerial->bGetOldtioOK) tcsetattr(pSerial->fd, TCSANOW, &pSerial->oldtio); // 恢复原有的串口配置
-        close(pSerial->fd); // 关闭串口
-    }
-
-    if(pSerial) sw_heap_free((void *)pSerial); // 释放内存, 销毁串口对象
-}
-
-/* 获取日志打印前缀 */
-char *serial_get_log_prefix(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial) return pSerial->log_prefix;
-    else return NULL;
-}
-
-/* 发送数据, -1失败, >=0发送的字节数 */
-int serial_send_data(const void *hSerial, const unsigned char *data, int len)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial && pSerial->fd != -1)
-    {
-        int ret = write(pSerial->fd, data, len);
-        if(ret < 0) sw_log_error("write error(ret=%d, errno=%d, %s), \"%s\" serial port send failed!!", 
-                                 ret, errno, strerror(errno), pSerial->name);
-        return ret;
-    }
-    else return -1;
-}
-
-/* 取得接收缓存区, NULL失败, NOT NULL成功 */
-const unsigned char *serial_get_recv_buffer(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial) return pSerial->recvBuf;
-    else return NULL;
-}
-
-/* 取得接收缓存区中当前有效数据的字节数, -1失败, 返回值>=0 */
-int serial_get_recv_buffer_bytes(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial) return pSerial->nRecvBytes;
-    else return -1;
-}
-
-/* 清空接收缓存区 */
-void serial_clear_recv_buffer(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial)
-    {
-        memset(pSerial->recvBuf, 0, sizeof(pSerial->recvBuf));
-        pSerial->nRecvBytes = 0;
-    }
-}
-
-/* 打印接收缓存区 */
-void serial_printf_recv_buffer(const void *hSerial, int logLevel)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial)
-    {
-        pSerial->pLogRecvBuf[0] = '\0'; // 复位日志缓存
-        sprintf(pSerial->pLogRecvBuf, "%s the received buffer has %d bytes (hex)",
-                pSerial->log_prefix, pSerial->nRecvBytes);
-        if(pSerial->nRecvBytes > 0) { strcat(pSerial->pLogRecvBuf, ":\n");
-        xstrfromhex(pSerial->recvBuf, pSerial->nRecvBytes, pSerial->pLogRecvBuf, NULL, NULL); }
-        else strcat(pSerial->pLogRecvBuf, ".");
-        sw_log_writeWithLevel(logLevel, __FILE__, -1, "%s", pSerial->pLogRecvBuf);
-    }
-}
-
-/* 判断接收线程是否还在 */
-bool serial_recvThrd_isAlive(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    return pSerial ? sw_thrd_isAlive(pSerial->hRecvThrd) : false;
-}
-
-/* 暂停接收线程接收数据 */
-void serial_recvThrd_pause(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial) sw_thrd_pause(pSerial->hRecvThrd);
-}
-
-/* 继续接收线程接收数据 */
-void serial_recvThrd_resume(const void *hSerial)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial) sw_thrd_resume(pSerial->hRecvThrd);
-}
-
-#ifdef _DEBUG
-/* 向接收缓存中写入数据(一般用于仿真串口输入, 模拟测试时使用) */
-void serial_write_recv_buffer(const void *hSerial, const unsigned char *hex, int hexCnt)
-{
-    struct serial_t *pSerial = (struct serial_t *)hSerial;
-    if(pSerial && hex && hexCnt > 0 && hexCnt < sizeof(pSerial->recvBuf))
-    {
-        memcpy(pSerial->recvBuf+pSerial->nRecvBytes, hex, hexCnt);
-        pSerial->nRecvBytes += hexCnt;
-    }
-    sw_thrd_delay(1000); // 延时1秒钟, 等待数据被回调处理, 否则会累积(模拟仿真和真实触发还是有区别)
-}
-#endif
-
-/* 检测串口状态, timeout单位为ms */
-#define serial_select sw_tcp_select
-
-/* 数据接收处理(线程回调), "返回值 < 0"时表示处理失败, 将不再调用该接收回调函数, "返回值 >= 0"时表示
-   处理成功, 延时一段"返回值"时间(ms)后继续调用该接收回调函数 */
-static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam)
-{
-    struct serial_t *pSerial = (struct serial_t *)wParam;
-    fd_set rSet;
-    int nRecvBufSize = sizeof(pSerial->recvBuf), nInterval, nRet;
-
-RecvP:
-    if(!sw_thrd_isAlive(pSerial->hRecvThrd)) return -1;
-
-    nRet = serial_select(pSerial->fd, &rSet, NULL, NULL, 0);
-    if(nRet == 0)
-    { // 串口无数据
-NoneP:
-        if(pSerial->pIdleHandler) nInterval = pSerial->pIdleHandler(wParam, lParam); // 触发无数据时的用户回调
-        else nInterval = 1;
-        goto EndP;
-    }
-    else if(nRet > 0 && FD_ISSET(pSerial->fd, &rSet))
-    { // 串口有数据
-        if(pSerial->nRecvBytes >= nRecvBufSize)
-        { // 判断接收缓存是否溢出
-            sw_log_warn("\"%s\" serial port receiving buffer overflow, the earliest one byte(%02x) will discard!",
-                        pSerial->name, pSerial->recvBuf[0]);
-            memmove(pSerial->recvBuf, pSerial->recvBuf+1, nRecvBufSize-1); // 丢弃最早的一个字节
-            pSerial->nRecvBytes = nRecvBufSize-1; // 新的已接收字节数
-        }
-
-        nRet = read(pSerial->fd, pSerial->recvBuf+pSerial->nRecvBytes, 1); // 读一个字节
-        if(nRet == 1) pSerial->nRecvBytes += 1; // 已接收字节数累加1
-        else if(nRet == 0 && errno == ETIMEDOUT) goto NoneP; // 读数据超时
-        else
-        { // 读一个字节错误
-            sw_log_error("read one byte error(ret=%d, errno=%d), \"%s\" serial port receiving thread abort!!",
-                         nRet, errno, pSerial->name);
-            return -1;
-        }
-
-        if(pSerial->pRecvHandler)
-        { // 处理已接收的数据
-            nInterval = pSerial->pRecvHandler(wParam, lParam); // 触发有数据时的用户回调
-            if(nInterval < 0) goto EndP;
-        }
-
-        goto RecvP; // 继续接收后续数据
-    }
-    else if(nRet < 0 && errno != EINTR)
-    { // 有错误发生
-        sw_log_error("select error(%s), \"%s\" serial port receiving thread abort!!", strerror(errno), pSerial->name);
-        return -1;
-    }
-    else nInterval = 1; // 不满足上述组合判断条件时
-
-EndP:
-    return nInterval;
-}
+#include "swapi.h"
+#include "swmem.h"
+#include "swthrd.h"
+#include "swtcp.h"
+#include "swstring.h"
+#include "serial.h"
+
+struct serial_t
+{
+    /* fd */
+    int fd;
+    /* 名称 */
+    char name[128];
+    /* 波特率 */
+    int baudrate;
+    /* 校检位: "odd"-奇校检; "even"-偶校检; "no"-无校检 */
+    char parityCheck[5];
+
+    /* 原有配置 */
+    struct termios oldtio;
+    bool bGetOldtioOK;
+
+    /* 数据接收线程句柄 */
+    void *hRecvThrd;
+    /* 数据接收缓存 */
+    unsigned char recvBuf[65535];
+    /* 已接收的字节数 */
+    int nRecvBytes;
+    /* 处理已接收的数据(用户回调) */
+    PSerialRecvHandler pRecvHandler;
+    /* 无数据空闲时执行(用户回调) */
+    PSerialIdleHandler pIdleHandler;
+
+    /* 日志打印前缀 */
+    char log_prefix[MAX_LINE_CHARS];
+    /* 日志打印缓存 */
+    char *pLogRecvBuf; // 堆内存
+};
+
+/* 数据接收处理(线程回调) */
+static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam);
+
+/* 打开串口(8个数据位1个停止位), NULL失败, NOT NULL成功 */
+void *serial_open(const char *serialName, int baudrate, const char *parityCheck /* "odd"-奇校检; "even"-偶校检; NULL或其它无效值-无校检 */,
+                  PSerialRecvHandler pRecvHandler, PSerialIdleHandler pIdleHandler, void *pUserData)
+{
+    struct serial_t *pSerial;
+    struct termios newtio;
+    int nSpeed;
+
+    /* 1, 创建串口对象 */
+    if(!serialName) return NULL;
+    pSerial = (struct serial_t *)sw_heap_malloc(sizeof(struct serial_t));
+    if(!pSerial) return NULL;
+    else
+    {
+        memset(pSerial, 0, sizeof(struct serial_t));
+        strncpy(pSerial->name, serialName, sizeof(pSerial->name)-1);
+    }
+
+    /* 2, 打开串口 */
+    pSerial->fd = open(pSerial->name, O_RDWR | O_NOCTTY | O_NDELAY | O_CLOEXEC);
+    if(pSerial->fd == -1) goto ErrorP;
+
+    if(flock(pSerial->fd, LOCK_EX | LOCK_NB) == -1) goto ErrorP;
+
+    /* 3, 保存当前串口配置 */
+    if(tcgetattr(pSerial->fd, &pSerial->oldtio) == -1) goto ErrorP;
+    else pSerial->bGetOldtioOK = true;
+
+    /* 4, 设置阻塞 */
+    if(fcntl(pSerial->fd, F_SETFL, 0) == -1) goto ErrorP;
+
+    /* 5, 设置参数(8 databits, no parity, 1 stopbit, 1 second read timeout) */
+    pSerial->baudrate = baudrate;
+    switch(pSerial->baudrate)
+    {
+        case      0 : nSpeed = B0;       break;
+        case     50 : nSpeed = B50;      break;
+        case     75 : nSpeed = B75;      break;
+        case    110 : nSpeed = B110;     break;
+        case    134 : nSpeed = B134;     break;
+        case    150 : nSpeed = B150;     break;
+        case    200 : nSpeed = B200;     break;
+        case    300 : nSpeed = B300;     break;
+        case    600 : nSpeed = B600;     break;
+        case   1200 : nSpeed = B1200;    break;
+        case   1800 : nSpeed = B1800;    break;
+        case   2400 : nSpeed = B2400;    break;
+        case   4800 : nSpeed = B4800;    break;
+        case   9600 : nSpeed = B9600;    break;
+        case  19200 : nSpeed = B19200;   break;
+        case  38400 : nSpeed = B38400;   break;
+        case  57600 : nSpeed = B57600;   break;
+        case 115200 : nSpeed = B115200;  break;
+        case 230400 : nSpeed = B230400;  break;
+        default     : goto ErrorP;
+    }
+    memset(&newtio, 0, sizeof(newtio));
+    newtio.c_cflag = nSpeed | CS8 | CLOCAL | CREAD; // 设置波特率、数据带宽、本地连接、接收使能
+    newtio.c_cflag &= ~CSTOPB; // 一个停止位
+    if(xstrcasecmp(parityCheck, "odd") == 0)
+    {  // 使能奇校检
+        newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD;
+        newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "odd");
+    }
+    else if(xstrcasecmp(parityCheck, "even") == 0)
+    { // 使能偶校检
+        newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD;
+        newtio.c_iflag |= INPCK; strcpy(pSerial->parityCheck, "even");
+    }
+    else
+    { // 无校检
+        newtio.c_cflag &= ~PARENB;
+        newtio.c_iflag = IGNPAR; strcpy(pSerial->parityCheck, "no");
+    }
+    newtio.c_oflag = 0;
+    newtio.c_lflag = 0;
+    newtio.c_cc[VMIN] = 0;  // read block untill n bytes are received, 阻塞模式有效
+    newtio.c_cc[VTIME] = 1; // read block untill a timer expires(单位: 百毫秒, 十分之一秒), 阻塞模式有效
+    if(tcflush(pSerial->fd, TCIOFLUSH) == -1) goto ErrorP; // 清空串口输入、输出缓存
+    if(tcsetattr(pSerial->fd, TCSANOW, &newtio) == -1) goto ErrorP; // 激活新配置
+
+    /* 6, 生成日志打印前缀 */
+    if(xstrcasecmp(pSerial->parityCheck, "odd") == 0) sprintf(pSerial->log_prefix, "[%s:%d(odd parity)]", pSerial->name, pSerial->baudrate);
+    else if(xstrcasecmp(pSerial->parityCheck, "even") == 0) sprintf(pSerial->log_prefix, "[%s:%d(even parity)]", pSerial->name, pSerial->baudrate);
+    else sprintf(pSerial->log_prefix, "[%s:%d(no parity)]", pSerial->name, pSerial->baudrate);
+
+    /* 7, 申请日志打印缓存 */
+    pSerial->pLogRecvBuf = (char *)sw_heap_malloc(sizeof(pSerial->log_prefix)+MAX_LINE_CHARS+sizeof(pSerial->recvBuf)*2);
+    if(!pSerial->pLogRecvBuf) goto ErrorP;
+
+    /* 8, 打开串口数据接收线程 */
+    pSerial->pRecvHandler = pRecvHandler; pSerial->pIdleHandler = pIdleHandler; // 用户回调
+    pSerial->hRecvThrd = sw_thrd_create(pSerial->name, THREAD_DEFAULT_PRIORITY, 4*1024*1024, serial_data_recv_proc, (unsigned long)pSerial, (unsigned long)pUserData);
+    if(!pSerial->hRecvThrd) goto ErrorP;
+    else sw_thrd_resume(pSerial->hRecvThrd); // 激活数据接收线程
+
+    return (void *)pSerial;
+
+    /* 9, 错误 */
+ErrorP:
+    serial_close(pSerial, 0);
+    return NULL;
+}
+
+/* 关闭串口 */
+void serial_close(void *hSerial, int timeout)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+
+    if(pSerial && pSerial->hRecvThrd) sw_thrd_destroy(pSerial->hRecvThrd, timeout); // 销毁数据接收线程
+
+    if(pSerial->pLogRecvBuf) sw_heap_free((void *)pSerial->pLogRecvBuf); // 释放日志打印缓存
+
+    if(pSerial && pSerial->fd != -1)
+    {
+        if(pSerial->bGetOldtioOK) tcsetattr(pSerial->fd, TCSANOW, &pSerial->oldtio); // 恢复原有的串口配置
+        close(pSerial->fd); // 关闭串口
+    }
+
+    if(pSerial) sw_heap_free((void *)pSerial); // 释放内存, 销毁串口对象
+}
+
+/* 获取日志打印前缀 */
+char *serial_get_log_prefix(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial) return pSerial->log_prefix;
+    else return NULL;
+}
+
+/* 发送数据, -1失败, >=0发送的字节数 */
+int serial_send_data(const void *hSerial, const unsigned char *data, int len)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial && pSerial->fd != -1)
+    {
+        int ret = write(pSerial->fd, data, len);
+        if(ret < 0) sw_log_error("write error(ret=%d, errno=%d, %s), \"%s\" serial port send failed!!", 
+                                 ret, errno, strerror(errno), pSerial->name);
+        return ret;
+    }
+    else return -1;
+}
+
+/* 取得接收缓存区, NULL失败, NOT NULL成功 */
+const unsigned char *serial_get_recv_buffer(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial) return pSerial->recvBuf;
+    else return NULL;
+}
+
+/* 取得接收缓存区中当前有效数据的字节数, -1失败, 返回值>=0 */
+int serial_get_recv_buffer_bytes(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial) return pSerial->nRecvBytes;
+    else return -1;
+}
+
+/* 清空接收缓存区 */
+void serial_clear_recv_buffer(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial)
+    {
+        memset(pSerial->recvBuf, 0, sizeof(pSerial->recvBuf));
+        pSerial->nRecvBytes = 0;
+    }
+}
+
+/* 删除接收缓存区 */
+void serial_clear_recv_buffer_head(const void *hSerial, int nBytes)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(!pSerial || nBytes <= 0) return;
+
+    if(nBytes < pSerial->nRecvBytes)
+    {
+        memmove(pSerial->recvBuf, pSerial->recvBuf + nBytes, pSerial->nRecvBytes - nBytes);
+        pSerial->nRecvBytes -= nBytes;
+    }
+    else serial_clear_recv_buffer(pSerial);
+}
+
+/* 打印接收缓存区 */
+void serial_printf_recv_buffer(const void *hSerial, int logLevel)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial)
+    {
+        pSerial->pLogRecvBuf[0] = '\0'; // 复位日志缓存
+        sprintf(pSerial->pLogRecvBuf, "%s the received buffer has %d bytes (hex)",
+                pSerial->log_prefix, pSerial->nRecvBytes);
+        if(pSerial->nRecvBytes > 0) { strcat(pSerial->pLogRecvBuf, ":\n");
+        xstrfromhex(pSerial->recvBuf, pSerial->nRecvBytes, pSerial->pLogRecvBuf, NULL, NULL); }
+        else strcat(pSerial->pLogRecvBuf, ".");
+        sw_log_writeWithLevel(logLevel, __FILE__, -1, "%s", pSerial->pLogRecvBuf);
+    }
+}
+
+/* 判断接收线程是否还在 */
+bool serial_recvThrd_isAlive(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    return pSerial ? sw_thrd_isAlive(pSerial->hRecvThrd) : false;
+}
+
+/* 暂停接收线程接收数据 */
+void serial_recvThrd_pause(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial) sw_thrd_pause(pSerial->hRecvThrd);
+}
+
+/* 继续接收线程接收数据 */
+void serial_recvThrd_resume(const void *hSerial)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial) sw_thrd_resume(pSerial->hRecvThrd);
+}
+
+#ifdef _DEBUG
+/* 向接收缓存中写入数据(一般用于仿真串口输入, 模拟测试时使用) */
+void serial_write_recv_buffer(const void *hSerial, const unsigned char *hex, int hexCnt)
+{
+    struct serial_t *pSerial = (struct serial_t *)hSerial;
+    if(pSerial && hex && hexCnt > 0 && hexCnt < sizeof(pSerial->recvBuf))
+    {
+        memcpy(pSerial->recvBuf+pSerial->nRecvBytes, hex, hexCnt);
+        pSerial->nRecvBytes += hexCnt;
+    }
+    sw_thrd_delay(1000); // 延时1秒钟, 等待数据被回调处理, 否则会累积(模拟仿真和真实触发还是有区别)
+}
+#endif
+
+/* 检测串口状态, timeout单位为ms */
+#define serial_select sw_tcp_select
+
+/* 数据接收处理(线程回调), "返回值 < 0"时表示处理失败, 将不再调用该接收回调函数, "返回值 >= 0"时表示
+   处理成功, 延时一段"返回值"时间(ms)后继续调用该接收回调函数 */
+static int serial_data_recv_proc(unsigned long wParam, unsigned long lParam)
+{
+    struct serial_t *pSerial = (struct serial_t *)wParam;
+    fd_set rSet;
+    int nRecvBufSize = sizeof(pSerial->recvBuf), nInterval, nRet;
+
+RecvP:
+    if(!sw_thrd_isAlive(pSerial->hRecvThrd)) return -1;
+
+    nRet = serial_select(pSerial->fd, &rSet, NULL, NULL, 0);
+    if(nRet == 0)
+    { // 串口无数据
+NoneP:
+        if(pSerial->pIdleHandler) nInterval = pSerial->pIdleHandler(wParam, lParam); // 触发无数据时的用户回调
+        else nInterval = 1;
+        goto EndP;
+    }
+    else if(nRet > 0 && FD_ISSET(pSerial->fd, &rSet))
+    { // 串口有数据
+        if(pSerial->nRecvBytes >= nRecvBufSize)
+        { // 判断接收缓存是否溢出
+            sw_log_warn("\"%s\" serial port receiving buffer overflow, the earliest one byte(%02x) will discard!",
+                        pSerial->name, pSerial->recvBuf[0]);
+            memmove(pSerial->recvBuf, pSerial->recvBuf+1, nRecvBufSize-1); // 丢弃最早的一个字节
+            pSerial->nRecvBytes = nRecvBufSize-1; // 新的已接收字节数
+        }
+
+        nRet = read(pSerial->fd, pSerial->recvBuf+pSerial->nRecvBytes, 1); // 读一个字节
+        if(nRet == 1) pSerial->nRecvBytes += 1; // 已接收字节数累加1
+        else if(nRet == 0 && errno == ETIMEDOUT) goto NoneP; // 读数据超时
+        else
+        { // 读一个字节错误
+            sw_log_error("read one byte error(ret=%d, errno=%d), \"%s\" serial port receiving thread abort!!",
+                         nRet, errno, pSerial->name);
+            return -1;
+        }
+
+        if(pSerial->pRecvHandler)
+        { // 处理已接收的数据
+            nInterval = pSerial->pRecvHandler(wParam, lParam); // 触发有数据时的用户回调
+            if(nInterval < 0) goto EndP;
+        }
+
+        goto RecvP; // 继续接收后续数据
+    }
+    else if(nRet < 0 && errno != EINTR)
+    { // 有错误发生
+        sw_log_error("select error(%s), \"%s\" serial port receiving thread abort!!", strerror(errno), pSerial->name);
+        return -1;
+    }
+    else nInterval = 1; // 不满足上述组合判断条件时
+
+EndP:
+    return nInterval;
+}

+ 77 - 74
swapi/subjects/serial/serial.h

@@ -1,74 +1,77 @@
-/** 
- * @file serial.h
- * @brief 串口通信(同步IO方式)
- * @author niujiuru
- * @date 2014-07-14 created
- * @note 2021-04-13 打开串口时允许设置奇偶校检位
- * @note 2021-04-23 打开串口时区分开有数据和无数据两种不同状态下的用户回调
- * @note 2021-04-25 新增"serial_printf_recv_buffer()"函数, 打印接收缓存区
- * @note 2021-04-27 新增"serial_recvThrd_isAlive()"函数, 判断接收线程是否还在
- * @note 2021-08-20 新增"serial_get_log_prefix()"函数, 获取日志打印前缀字符串
- * @note 2025-10-11 打开串口时新增"__O_CLOEXEC"
- */
-#ifndef __SERIAL_H__
-#define __SERIAL_H__
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/* 数据接收处理(用户回调); "wParam"指向打开的串口句柄, "lParm"指向用户数据;
-   "返回值 < 0"时表示处理失败, 将不再调用该用户回调, "返回值 >= 0"时表示处理成功,
-   接收数据空闲时, 延时一段"返回值"时间(ms)后, 继续调用该用户回调 */
-typedef int (*PSerialRecvHandler)(unsigned long wParam, unsigned long lParam);
-
-/* 空闲接收处理(用户回调); "wParam"指向打开的串口句柄, "lParm"指向用户数据;
-   "返回值 < 0"时表示处理失败, 将不再调用该用户回调, "返回值 >= 0"时表示处理成功,
-   接收数据空闲时, 延时一段"返回值"时间(ms)后, 继续调用该用户回调 */
-typedef int (*PSerialIdleHandler)(unsigned long wParam, unsigned long lParam);
-
-/* 打开串口(8个数据位1个停止位), NULL失败, NOT NULL成功 */
-void *serial_open(const char *serialName, int baudrate, const char *parityCheck /* "odd"-奇校检; "even"-偶校检; NULL或其它无效值-无校检 */,
-                  PSerialRecvHandler pRecvHandler, PSerialIdleHandler pIdleHandler, void *pUserData);
-
-/* 关闭串口 */
-void serial_close(void *hSerial, int timeout);
-
-/* 获取日志打印前缀 */
-char *serial_get_log_prefix(const void *hSerial);
-
-/* 发送数据, -1失败, >=0发送的字节数 */
-int serial_send_data(const void *hSerial, const unsigned char *data, int len);
-
-/* 取得接收缓存区, NULL失败, NOT NULL成功 */
-const unsigned char *serial_get_recv_buffer(const void *hSerial);
-
-/* 取得接收缓存区中当前有效数据的字节数, -1失败, 返回值>=0 */
-int serial_get_recv_buffer_bytes(const void *hSerial);
-
-/* 清空接收缓存区 */
-void serial_clear_recv_buffer(const void *hSerial);
-
-/* 打印接收缓存区 */
-void serial_printf_recv_buffer(const void *hSerial, int logLevel);
-
-/* 判断接收线程是否还在 */
-bool serial_recvThrd_isAlive(const void *hSerial);
-
-/* 暂停接收线程接收数据 */
-void serial_recvThrd_pause(const void *hSerial);
-
-/* 继续接收线程接收数据 */
-void serial_recvThrd_resume(const void *hSerial);
-
-#ifdef _DEBUG
-/* 向接收缓存中写入数据(一般用于仿真串口输入, 模拟测试时使用) */
-void serial_write_recv_buffer(const void *hSerial, const unsigned char *hex, int hexCnt);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __SERIAL_H__ */
+/** 
+ * @file serial.h
+ * @brief 串口通信(同步IO方式)
+ * @author niujiuru
+ * @date 2014-07-14 created
+ * @note 2021-04-13 打开串口时允许设置奇偶校检位
+ * @note 2021-04-23 打开串口时区分开有数据和无数据两种不同状态下的用户回调
+ * @note 2021-04-25 新增"serial_printf_recv_buffer()"函数, 打印接收缓存区
+ * @note 2021-04-27 新增"serial_recvThrd_isAlive()"函数, 判断接收线程是否还在
+ * @note 2021-08-20 新增"serial_get_log_prefix()"函数, 获取日志打印前缀字符串
+ * @note 2025-10-11 打开串口时新增"__O_CLOEXEC"
+ */
+#ifndef __SERIAL_H__
+#define __SERIAL_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* 数据接收处理(用户回调); "wParam"指向打开的串口句柄, "lParm"指向用户数据;
+   "返回值 < 0"时表示处理失败, 将不再调用该用户回调, "返回值 >= 0"时表示处理成功,
+   接收数据空闲时, 延时一段"返回值"时间(ms)后, 继续调用该用户回调 */
+typedef int (*PSerialRecvHandler)(unsigned long wParam, unsigned long lParam);
+
+/* 空闲接收处理(用户回调); "wParam"指向打开的串口句柄, "lParm"指向用户数据;
+   "返回值 < 0"时表示处理失败, 将不再调用该用户回调, "返回值 >= 0"时表示处理成功,
+   接收数据空闲时, 延时一段"返回值"时间(ms)后, 继续调用该用户回调 */
+typedef int (*PSerialIdleHandler)(unsigned long wParam, unsigned long lParam);
+
+/* 打开串口(8个数据位1个停止位), NULL失败, NOT NULL成功 */
+void *serial_open(const char *serialName, int baudrate, const char *parityCheck /* "odd"-奇校检; "even"-偶校检; NULL或其它无效值-无校检 */,
+                  PSerialRecvHandler pRecvHandler, PSerialIdleHandler pIdleHandler, void *pUserData);
+
+/* 关闭串口 */
+void serial_close(void *hSerial, int timeout);
+
+/* 获取日志打印前缀 */
+char *serial_get_log_prefix(const void *hSerial);
+
+/* 发送数据, -1失败, >=0发送的字节数 */
+int serial_send_data(const void *hSerial, const unsigned char *data, int len);
+
+/* 取得接收缓存区, NULL失败, NOT NULL成功 */
+const unsigned char *serial_get_recv_buffer(const void *hSerial);
+
+/* 取得接收缓存区中当前有效数据的字节数, -1失败, 返回值>=0 */
+int serial_get_recv_buffer_bytes(const void *hSerial);
+
+/* 清空接收缓存区 */
+void serial_clear_recv_buffer(const void *hSerial);
+
+/* 删除接收缓存区 */
+void serial_clear_recv_buffer_head(const void *hSerial, int nBytes);
+
+/* 打印接收缓存区 */
+void serial_printf_recv_buffer(const void *hSerial, int logLevel);
+
+/* 判断接收线程是否还在 */
+bool serial_recvThrd_isAlive(const void *hSerial);
+
+/* 暂停接收线程接收数据 */
+void serial_recvThrd_pause(const void *hSerial);
+
+/* 继续接收线程接收数据 */
+void serial_recvThrd_resume(const void *hSerial);
+
+#ifdef _DEBUG
+/* 向接收缓存中写入数据(一般用于仿真串口输入, 模拟测试时使用) */
+void serial_write_recv_buffer(const void *hSerial, const unsigned char *hex, int hexCnt);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SERIAL_H__ */