Преглед изворни кода

优化串口数据读取!防止垃圾数据!

lwk пре 18 часа
родитељ
комит
83820dd7ef
1 измењених фајлова са 66 додато и 47 уклоњено
  1. 66 47
      SCF/SerialSCF/SerialSCF.cpp

+ 66 - 47
SCF/SerialSCF/SerialSCF.cpp

@@ -26,15 +26,15 @@ SerialSCF::~SerialSCF() {
 int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize,
     uint32_t Parity, uint32_t StopBits) {
     std::cout << "Trying to open serial port: " << pCom << std::endl;
-    // 打开串口设备 (非阻塞模式)
+    // 鎵撳紑涓插彛璁惧� (闈為樆濉炴ā寮�)
     hCom = open(pCom, O_RDWR | O_NOCTTY);//| O_NONBLOCK
     if (hCom < 0) {
-        // 记录错误日志
+        // 璁板綍閿欒�鏃ュ織
         return SCF_OPEN_FAILED;
     }
 
     std::cout << "Serial port " << pCom << " opened successfully (file descriptor: " << hCom << ")" << std::endl;
-    // 保存原始串口设置
+    // 淇濆瓨鍘熷�涓插彛璁剧疆
     if (tcgetattr(hCom, &m_OriginalTermios) != 0) {
         std::cout << "Failed to get original termios attributes for port: " << pCom << std::endl;
         close(hCom);
@@ -42,11 +42,11 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
         return SCF_OPEN_FAILED;
     }
 
-    // 配置新的串口设置
+    // 閰嶇疆鏂扮殑涓插彛璁剧疆
     memset(&m_CurrentTermios, 0, sizeof(termios));
-    m_CurrentTermios.c_cflag |= CLOCAL | CREAD;  // 本地连接,启用接收
+    m_CurrentTermios.c_cflag |= CLOCAL | CREAD;  // 鏈�湴杩炴帴锛屽惎鐢ㄦ帴鏀�
     std::cout << "Configuring serial port attributes..." << std::endl;
-    // 设置波特率
+    // 璁剧疆娉㈢壒鐜�
     speed_t speed;
     switch (BaudRate) {
     case 50:     speed = B50; break;
@@ -82,7 +82,7 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
     cfsetospeed(&m_CurrentTermios, speed);
     std::cout << "Set baud rate to: " << BaudRate << std::endl;
 
-    // 设置数据位
+    // 璁剧疆鏁版嵁浣�
     m_CurrentTermios.c_cflag &= ~CSIZE;
     switch (ByteSize) {
     case 5: m_CurrentTermios.c_cflag |= CS5; break;
@@ -97,7 +97,7 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
     }
     std::cout << "Set data bits to: " << ByteSize << std::endl;
 
-    // 设置校验位
+    // 璁剧疆鏍¢獙浣�
     switch (Parity) {
     case 0: // None
         m_CurrentTermios.c_cflag &= ~PARENB;
@@ -120,7 +120,7 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
         return SCF_PARAMETER_ERR;
     }
 
-    // 设置停止位
+    // 璁剧疆鍋滄�浣�
     switch (StopBits) {
     case 1:
         m_CurrentTermios.c_cflag &= ~CSTOPB;
@@ -137,16 +137,16 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
         return SCF_PARAMETER_ERR;
     }
 
-    // 设置原始模式 (禁用规范模式)
+    // 璁剧疆鍘熷�妯″紡 (绂佺敤瑙勮寖妯″紡)
     m_CurrentTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
-    m_CurrentTermios.c_oflag &= ~OPOST;  // 原始输出
+    m_CurrentTermios.c_oflag &= ~OPOST;  // 鍘熷�杈撳嚭
     std::cout << "Set serial port to raw mode" << std::endl;
 
-    // 设置超时和最小读取字节数
-    m_CurrentTermios.c_cc[VMIN] = 0;     // 非阻塞模式
-    m_CurrentTermios.c_cc[VTIME] = m_ReadTimeout / 100;  // 超时时间 (十分之一秒)
+    // 璁剧疆瓒呮椂鍜屾渶灏忚�鍙栧瓧鑺傛暟
+    m_CurrentTermios.c_cc[VMIN] = 0;     // 闈為樆濉炴ā寮�
+    m_CurrentTermios.c_cc[VTIME] = m_ReadTimeout / 100;  // 瓒呮椂鏃堕棿 (鍗佸垎涔嬩竴绉�)
     std::cout << "Set read timeout to: " << m_ReadTimeout << "ms" << std::endl;
-    // 刷新缓冲区并应用新设置
+    // 鍒锋柊缂撳啿鍖哄苟搴旂敤鏂拌�缃�
     tcflush(hCom, TCIOFLUSH);
     if (tcsetattr(hCom, TCSANOW, &m_CurrentTermios) != 0) {
         close(hCom);
@@ -155,7 +155,7 @@ int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize
     }
     std::cout << "Serial port settings applied successfully" << std::endl;
 
-    // 清除所有缓冲区
+    // 娓呴櫎鎵€鏈夌紦鍐插尯
     tcflush(hCom, TCIOFLUSH);
     std::cout << "Serial port buffers flushed" << std::endl;
 
@@ -174,7 +174,7 @@ int SerialSCF::Connect(ResDataObject& Connectprameters, PacketParser callback,
         std::string ConnectionType = (const char*)Connectprameters["type"];
         std::string ThisType = GetConnectionType();
 
-        // 转换为小写比较
+        // 杞�崲涓哄皬鍐欐瘮杈�
         std::transform(ConnectionType.begin(), ConnectionType.end(), ConnectionType.begin(), ::tolower);
         std::transform(ThisType.begin(), ThisType.end(), ThisType.begin(), ::tolower);
 
@@ -199,12 +199,12 @@ int SerialSCF::Connect(ResDataObject& Connectprameters, PacketParser callback,
         ret = ConnectCOM(m_PortName.c_str(), m_BaudRate, m_ByteSize, m_Parity, m_StopBits);
         if (ret == SCF_SUCCEED) {
             std::cout << "COM port connection successful." << std::endl;
-            // 初始化日志
+            // 鍒濆�鍖栨棩蹇�
             std::string NewFileName = "com/s_" + m_PortName;
             std::cout << "Initializing SCF with log file: " << NewFileName << std::endl;
             InitSCF(NewFileName.c_str());
 
-            // 调用基类连接方法
+            // 璋冪敤鍩虹被杩炴帴鏂规硶
             ret = SCF::Connect(Connectprameters, callback, TransferType, CommunicateTimeout);
         }
     }
@@ -219,50 +219,50 @@ void SerialSCF::Disconnect() {
     SCF::Disconnect();
 
     if (hCom >= 0) {
-        // 恢复原始串口设置
+        // 鎭㈠�鍘熷�涓插彛璁剧疆
         tcsetattr(hCom, TCSANOW, &m_OriginalTermios);
 
-        // 关闭串口
+        // 鍏抽棴涓插彛
         close(hCom);
         hCom = -1;
     }
 }
 
 bool SerialSCF::isConnected() {
-    // 1. 基础检查:文件描述符已无效,直接返回断开
+    // 1. 鍩虹�妫€鏌ワ細鏂囦欢鎻忚堪绗﹀凡鏃犳晥锛岀洿鎺ヨ繑鍥炴柇寮€
     if (hCom < 0) {
         std::cout << "[Debug] isConnected: hCom invalid (value: " << hCom << ")" << std::endl;
         return false;
     }
 
-    // 2. 检查文件描述符有效性(处理EIO和EBADF)
+    // 2. 妫€鏌ユ枃浠舵弿杩扮�鏈夋晥鎬э紙澶勭悊EIO鍜孍BADF锛�
     int fdCheck = fcntl(hCom, F_GETFD);
     if (fdCheck == -1) {
         std::cout << "[Debug] isConnected: fcntl failed | errno=" << errno
             << " (" << (errno == EIO ? "EIO(I/O error)" : (errno == EBADF ? "EBADF(Bad file)" : "Unknown")) << ")" << std::endl;
-        // 关键判断:用户环境中EIO=5(设备删除)、EBADF=9(描述符无效)均视为断开
+        // 鍏抽敭鍒ゆ柇锛氱敤鎴风幆澧冧腑EIO=5锛堣�澶囧垹闄わ級銆丒BADF=9锛堟弿杩扮�鏃犳晥锛夊潎瑙嗕负鏂�紑
         if (errno == EIO || errno == EBADF) {
-            hCom = -1;  // 重置描述符状态,避免后续误判
+            hCom = -1;  // 閲嶇疆鎻忚堪绗︾姸鎬侊紝閬垮厤鍚庣画璇�垽
             return false;
         }
-        // 其他非致命错误(如临时权限问题)不判定为断开
+        // 鍏朵粬闈炶嚧鍛介敊璇�紙濡備复鏃舵潈闄愰棶棰橈級涓嶅垽瀹氫负鏂�紑
     }
 
-    // 3. 检查串口硬件状态(核心适配虚拟端口删除后的EIO错误)
+    // 3. 妫€鏌ヤ覆鍙g‖浠剁姸鎬侊紙鏍稿績閫傞厤铏氭嫙绔�彛鍒犻櫎鍚庣殑EIO閿欒�锛�
     int portStatus;
     int ioctlRet = ioctl(hCom, TIOCMGET, &portStatus);
     if (ioctlRet == -1) {
         std::cout << "[Debug] isConnected: ioctl(TIOCMGET) failed | errno=" << errno
             << " (" << (errno == EIO ? "EIO(I/O error)" : (errno == EBADF ? "EBADF(Bad file)" : "Unknown")) << ")" << std::endl;
-        // 同样处理EIO和EBADF,覆盖虚拟端口删除场景
+        // 鍚屾牱澶勭悊EIO鍜孍BADF锛岃�鐩栬櫄鎷熺�鍙e垹闄ゅ満鏅�
         if (errno == EIO || errno == EBADF) {
             hCom = -1;
             return false;
         }
-        // 临时I/O错误(如设备短暂无响应)不判定为断开
+        // 涓存椂I/O閿欒�锛堝�璁惧�鐭�殏鏃犲搷搴旓級涓嶅垽瀹氫负鏂�紑
     }
 
-    // 4. 所有检查通过,确认连接有效
+    // 4. 鎵€鏈夋�鏌ラ€氳繃锛岀‘璁よ繛鎺ユ湁鏁�
     return true;
 }
 
@@ -280,14 +280,14 @@ int SerialSCF::SendPacket(const char* pPacket, uint32_t length, uint32_t timeout
         std::cout << "SendPacket failed: Not connected to serial port." << std::endl;
         return SCF_DISCONNETED;
     }
-    // 验证输入参数
+    // 楠岃瘉杈撳叆鍙傛暟
     if (pPacket == nullptr || length == 0) {
         std::cout << "SendPacket failed: Invalid packet (nullptr or zero length)." << std::endl;
         return SCF_WRITE_FAILED;
     }
 
     std::cout << "Starting to send packet. Length: " << length << " bytes, Timeout: " << timeout << " ms." << std::endl;
-    // 设置写超时
+    // 璁剧疆鍐欒秴鏃�
     struct timeval tv;
     tv.tv_sec = timeout / 1000;
     tv.tv_usec = (timeout % 1000) * 1000;
@@ -299,7 +299,7 @@ int SerialSCF::SendPacket(const char* pPacket, uint32_t length, uint32_t timeout
 
     uint32_t bytesWritten = 0;
     while (bytesWritten < length) {
-        // 等待串口可写
+        // 绛夊緟涓插彛鍙�啓
         int selectRet = select(hCom + 1, NULL, &write_fds, NULL, &tv);
         if (selectRet <= 0) {
             if (selectRet == 0) {
@@ -311,17 +311,17 @@ int SerialSCF::SendPacket(const char* pPacket, uint32_t length, uint32_t timeout
                 return SCF_WRITE_FAILED;
             }
         }
-        // 检查串口是否可写
+        // 妫€鏌ヤ覆鍙f槸鍚﹀彲鍐�
         if (!FD_ISSET(hCom, &write_fds)) {
             std::cout << "SendPacket warning: Serial port not ready for writing, retrying..." << std::endl;
             continue;
         }
-        // 写入数据
+        // 鍐欏叆鏁版嵁
         ssize_t ret = write(hCom, pPacket + bytesWritten, length - bytesWritten);
         if (ret < 0) {
             if (errno == EAGAIN || errno == EWOULDBLOCK) {
                 std::cout << "SendPacket temporary error: Resource busy (EAGAIN/EWOULDBLOCK). Retrying..." << std::endl;
-                usleep(10000);  // 10ms等待后重试
+                usleep(10000);  // 10ms绛夊緟鍚庨噸璇�
                 continue;
             }
             else {
@@ -343,7 +343,16 @@ int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) {
         return SCF_DISCONNETED;
     }
 
-    // 设置读超时
+    if (!pPacket || length == 0) {
+        std::cout << "[Debug] Invalid parameters: pPacket=" << (void*)pPacket << ", length=" << length << std::endl;
+        return SCF_PARAMETER_ERR;
+    }
+
+    // 娓呯┖缂撳啿鍖猴紝闃叉�杩斿洖鍨冨溇鏁版嵁
+    memset(pPacket, 0, length);
+
+    
+    // 璁剧疆璇昏秴鏃�
     struct timeval tv;
     tv.tv_sec = timeout / 1000;
     tv.tv_usec = (timeout % 1000) * 1000;
@@ -352,14 +361,14 @@ int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) {
     FD_ZERO(&read_fds);
     FD_SET(hCom, &read_fds);
 
-    // 等待数据到达
+    // 绛夊緟鏁版嵁鍒拌揪
     int selectRet = select(hCom + 1, &read_fds, NULL, NULL, &tv);
     if (selectRet <= 0) {
-        // 超时或错误
+        // 瓒呮椂鎴栭敊璇�
         return (selectRet == 0) ? SCF_TIMEOUT : SCF_READ_FAILED;
     }
 
-    // 读取可用数据
+    // 璇诲彇鍙�敤鏁版嵁
     int bytesAvailable = 0;
     if (ioctl(hCom, FIONREAD, &bytesAvailable) < 0) {
         std::cout << "[Debug] ioctl failed, errno: " << errno << std::endl;
@@ -370,26 +379,36 @@ int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) {
         return SCF_READ_FAILED;
     }
 
-    // 确保不超过缓冲区大小
+    // 濡傛灉娌℃湁鏁版嵁鍙��锛岃繑鍥炶秴鏃讹紙select宸茬粡琛ㄦ槑鏈夋暟鎹�紝浣唅octl璇存病鏈夛紝杩欐槸寮傚父鎯呭喌锛�
+    if (bytesAvailable <= 0) {
+        std::cout << "[Debug] ioctl reports no data available (bytesAvailable=" << bytesAvailable << "), but select indicated ready" << std::endl;
+        return SCF_TIMEOUT;
+    }
+
+     // 纭�繚涓嶈秴杩囩紦鍐插尯澶у皬
     uint32_t bytesToRead = std::min(static_cast<uint32_t>(bytesAvailable), length);
 
-    // 读取数据
+    // 鍐嶆�纭��瑕佽�鍙栫殑瀛楄妭鏁颁笉涓�0
+    if (bytesToRead == 0) {
+        std::cout << "[Debug] bytesToRead is 0, length=" << length << ", bytesAvailable=" << bytesAvailable << std::endl;
+        return SCF_TIMEOUT;
+    }
+
+     // 璇诲彇鏁版嵁
     ssize_t ret = read(hCom, pPacket, bytesToRead);
     if (ret < 0) {
         std::cout << "[Debug] Read failed, errno: " << errno << std::endl;
         return SCF_READ_FAILED;
     }
     else if (ret == 0) {
-        std::cout << "[Debug] Read 0 bytes (maybe data split)" << std::endl;
-        return 0;
+        std::cout << "[Debug] Read 0 bytes (maybe data split or EOF)" << std::endl;
+        return SCF_TIMEOUT;  // 杩斿洖瓒呮椂鑰屼笉鏄�0锛岄伩鍏嶄笂灞傝�鍒�
     }
     else {
-        // 原有的成功日志
+       // 鍘熸湁鐨勬垚鍔熸棩蹇椫�
         std::cout << "[Serial Port: " << m_PortName << "] Read " << ret << " bytes." << std::endl;
         return ret;
     }
-
-    return ret;
 }
 
 SERIALSCF_C_API SCF* GetSCF() {