123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #include "SerialSCF.h"
- #include <cstring>
- #include <fcntl.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/select.h>
- #include <errno.h>
- #include <algorithm>
- 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<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 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<SCF*>(p);
- }
- SERIALSCF_C_API void ReleaseSCF(SCF* p) {
- delete static_cast<SerialSCF*>(p);
- }
|