#include "SerialSCF.h" #include #include #include #include #include #include #include SerialSCF::SerialSCF() : hCom(-1), m_BaudRate(115200), m_ByteSize(8), m_Parity(0), m_StopBits(1), m_ReadTimeout(1000), m_WriteTimeout(1000) { memset(&m_OriginalTermios, 0, sizeof(termios)); memset(&m_CurrentTermios, 0, sizeof(termios)); } SerialSCF::~SerialSCF() { Disconnect(); } 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); hCom = -1; return SCF_OPEN_FAILED; } // 配置新的串口设置 memset(&m_CurrentTermios, 0, sizeof(termios)); m_CurrentTermios.c_cflag |= CLOCAL | CREAD; // 本地连接,启用接收 std::cout << "Configuring serial port attributes..." << std::endl; // 设置波特率 speed_t speed; switch (BaudRate) { case 50: speed = B50; break; case 75: speed = B75; break; case 110: speed = B110; break; case 134: speed = B134; break; case 150: speed = B150; break; case 200: speed = B200; break; case 300: speed = B300; break; case 600: speed = B600; break; case 1200: speed = B1200; break; case 1800: speed = B1800; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 57600: speed = B57600; break; case 115200: speed = B115200; break; case 230400: speed = B230400; break; case 460800: speed = B460800; break; case 500000: speed = B500000; break; case 576000: speed = B576000; break; case 921600: speed = B921600; break; case 1000000: speed = B1000000; break; default: std::cout << "Unsupported baud rate: " << BaudRate << std::endl; close(hCom); hCom = -1; return SCF_PARAMETER_ERR; } cfsetispeed(&m_CurrentTermios, speed); 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; case 6: m_CurrentTermios.c_cflag |= CS6; break; case 7: m_CurrentTermios.c_cflag |= CS7; break; case 8: m_CurrentTermios.c_cflag |= CS8; break; default: std::cout << "Unsupported data bit size: " << ByteSize << std::endl; close(hCom); hCom = -1; return SCF_PARAMETER_ERR; } std::cout << "Set data bits to: " << ByteSize << std::endl; // 设置校验位 switch (Parity) { case 0: // None m_CurrentTermios.c_cflag &= ~PARENB; std::cout << "Set parity to: None" << std::endl; break; case 1: // Odd m_CurrentTermios.c_cflag |= PARENB; m_CurrentTermios.c_cflag |= PARODD; std::cout << "Set parity to: Odd" << std::endl; break; case 2: // Even m_CurrentTermios.c_cflag |= PARENB; m_CurrentTermios.c_cflag &= ~PARODD; std::cout << "Set parity to: Even" << std::endl; break; default: std::cout << "Unsupported parity setting: " << Parity << std::endl; close(hCom); hCom = -1; return SCF_PARAMETER_ERR; } // 设置停止位 switch (StopBits) { case 1: m_CurrentTermios.c_cflag &= ~CSTOPB; std::cout << "Set stop bits to: 1" << std::endl; break; case 2: m_CurrentTermios.c_cflag |= CSTOPB; std::cout << "Set stop bits to: 2" << std::endl; break; default: std::cout << "Unsupported stop bits setting: " << StopBits << std::endl; close(hCom); hCom = -1; return SCF_PARAMETER_ERR; } // 设置原始模式 (禁用规范模式) m_CurrentTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 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; // 超时时间 (十分之一秒) std::cout << "Set read timeout to: " << m_ReadTimeout << "ms" << std::endl; // 刷新缓冲区并应用新设置 tcflush(hCom, TCIOFLUSH); if (tcsetattr(hCom, TCSANOW, &m_CurrentTermios) != 0) { close(hCom); hCom = -1; return SCF_OPEN_FAILED; } std::cout << "Serial port settings applied successfully" << std::endl; // 清除所有缓冲区 tcflush(hCom, TCIOFLUSH); std::cout << "Serial port buffers flushed" << std::endl; return SCF_SUCCEED; } int SerialSCF::Connect(ResDataObject& Connectprameters, PacketParser callback, SCF_TRANSFERTYPE TransferType, uint32_t CommunicateTimeout) { int ret = SCF_FAILED; std::cout << "Starting serial port connection process..." << std::endl; try { ResDataObject params = Connectprameters; params.MakeKeyLower(); 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); std::cout << "Checking connection type. Expected: " << ThisType << ", Received: " << ConnectionType << std::endl; if (ConnectionType != ThisType) { std::cout << "Connection type mismatch! Returning parameter error." << std::endl; return SCF_PARAMETER_ERR; } m_PortName = (const char*)Connectprameters["port"]; m_BaudRate = (uint32_t)Connectprameters["baudrate"]; m_ByteSize = (uint32_t)Connectprameters["bytesize"]; m_Parity = (uint32_t)Connectprameters["parity"]; m_StopBits = (uint32_t)Connectprameters["stopbits"]; std::cout << "Attempting to connect to COM port: " << m_PortName << " with parameters - Baudrate: " << m_BaudRate << ", Data bits: " << m_ByteSize << ", Parity: " << m_Parity << ", Stop bits: " << m_StopBits << std::endl; 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); } } catch (...) { ret = SCF_UNKNOWN; } return ret; } void SerialSCF::Disconnect() { SCF::Disconnect(); if (hCom >= 0) { // 恢复原始串口设置 tcsetattr(hCom, TCSANOW, &m_OriginalTermios); // 关闭串口 close(hCom); hCom = -1; } } bool SerialSCF::isConnected() { // 1. 基础检查:文件描述符已无效,直接返回断开 if (hCom < 0) { std::cout << "[Debug] isConnected: hCom invalid (value: " << hCom << ")" << std::endl; return false; } // 2. 检查文件描述符有效性(处理EIO和EBADF) 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(描述符无效)均视为断开 if (errno == EIO || errno == EBADF) { hCom = -1; // 重置描述符状态,避免后续误判 return false; } // 其他非致命错误(如临时权限问题)不判定为断开 } // 3. 检查串口硬件状态(核心适配虚拟端口删除后的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,覆盖虚拟端口删除场景 if (errno == EIO || errno == EBADF) { hCom = -1; return false; } // 临时I/O错误(如设备短暂无响应)不判定为断开 } // 4. 所有检查通过,确认连接有效 return true; } const char* SerialSCF::GetConnectionType() { static const char* nType = "COM"; return nType; } int SerialSCF::SendPacket(SCFPacket* pPacket, uint32_t timeout) { return SendPacket((char*)(*pPacket), pPacket->GetPacketLen(), timeout); } int SerialSCF::SendPacket(const char* pPacket, uint32_t length, uint32_t timeout) { if (!isConnected()) { 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; std::cout << "Set write timeout: " << tv.tv_sec << "s " << tv.tv_usec << "us." << std::endl; fd_set write_fds; FD_ZERO(&write_fds); FD_SET(hCom, &write_fds); uint32_t bytesWritten = 0; while (bytesWritten < length) { // 等待串口可写 int selectRet = select(hCom + 1, NULL, &write_fds, NULL, &tv); if (selectRet <= 0) { if (selectRet == 0) { std::cout << "SendPacket timeout: No data written for " << timeout << " ms. Total written: " << bytesWritten << " bytes." << std::endl; return SCF_TIMEOUT; } else { std::cout << "SendPacket failed: select() error. Return code: " << selectRet << ", Errno: " << errno << std::endl; return SCF_WRITE_FAILED; } } // 检查串口是否可写 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等待后重试 continue; } else { std::cout << "SendPacket failed: write() error. Errno: " << errno << ", Total written: " << bytesWritten << " bytes." << std::endl; return SCF_WRITE_FAILED; } } bytesWritten += ret; std::cout << "Wrote " << ret << " bytes. Progress: " << bytesWritten << "/" << length << " bytes." << std::endl; } std::cout << "SendPacket completed successfully. Total bytes sent: " << bytesWritten << std::endl; return bytesWritten; } int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) { if (!isConnected()) { std::cout << "[Debug] SerialSCF::ReadData:isConnected "<< std::endl; 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; fd_set read_fds; 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; if (errno == EIO || errno == EBADF) { hCom = -1; return SCF_DISCONNETED; } return SCF_READ_FAILED; } // 如果没有数据可读,返回超时(select已经表明有数据,但ioctl说没有,这是异常情况) 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(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 or EOF)" << std::endl; return SCF_TIMEOUT; // 返回超时而不是0,避免上层误判 } else { // 原有的成功日志־ std::cout << "[Serial Port: " << m_PortName << "] Read " << ret << " bytes." << std::endl; return ret; } } SERIALSCF_C_API SCF* GetSCF() { SerialSCF* p = new SerialSCF(); return static_cast(p); } SERIALSCF_C_API void ReleaseSCF(SCF* p) { delete static_cast(p); }