#include #include "SerialPort.h" #include #include SerialPort::SerialPort(const std::string& device, int baudrate) : running_(false) { #ifdef _WIN32 fd_ = CreateFile(device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fd_ == INVALID_HANDLE_VALUE) { throw std::system_error(GetLastError(), std::system_category(), "Failed to open serial port"); } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength = sizeof(dcbSerialParams); if (!GetCommState(fd_, &dcbSerialParams)) { CloseHandle(fd_); throw std::system_error(GetLastError(), std::system_category(), "GetCommState failed"); } dcbSerialParams.BaudRate = baudrate; dcbSerialParams.ByteSize = 8; dcbSerialParams.Parity = NOPARITY; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.fOutxCtsFlow = FALSE; dcbSerialParams.fOutxDsrFlow = FALSE; dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; if (!SetCommState(fd_, &dcbSerialParams)) { CloseHandle(fd_); throw std::system_error(GetLastError(), std::system_category(), "SetCommState failed"); } COMMTIMEOUTS timeouts = {0}; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(fd_, &timeouts)) { CloseHandle(fd_); throw std::system_error(GetLastError(), std::system_category(), "SetCommTimeouts failed"); } #else fd_ = open(device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); if (fd_ < 0) { throw std::system_error(errno, std::generic_category(), "Failed to open serial port"); } configure(baudrate, fd_); #endif } SerialPort::~SerialPort() { stop_receiving(); close(); } void SerialPort::configure(int baudrate, int fd) { #ifdef _WIN32 // Windows配置在构造函数中已完成 #else termios tty{}; if (tcgetattr(fd, &tty) != 0) { throw std::system_error(errno, std::generic_category(), "tcgetattr failed"); } speed_t speed = getBaudRate(baudrate); cfsetispeed(&tty, speed); cfsetospeed(&tty, speed); tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; tty.c_cflag |= CREAD | CLOCAL; tty.c_lflag &= ~(ICANON | ECHO | ECHOE | IEXTEN | ISIG); tty.c_iflag &= ~(ICRNL | INLCR | IGNCR); tty.c_iflag |= IGNBRK; tty.c_oflag &= ~OPOST; tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSANOW, &tty) != 0) { throw std::system_error(errno, std::generic_category(), "tcsetattr failed"); } int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); #endif } ssize_t SerialPort::write(const std::vector& data) { #ifdef _WIN32 DWORD bytesWritten; if (!WriteFile(fd_, data.data(), static_cast(data.size()), &bytesWritten, NULL)) { throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); } return static_cast(bytesWritten); #else ssize_t result = ::write(fd_, data.data(), data.size()); if (result < 0) { throw std::system_error(errno, std::generic_category(), "write failed"); } return result; #endif } std::vector SerialPort::read() { #ifdef _WIN32 std::vector buffer(256); DWORD bytesRead; if (!ReadFile(fd_, buffer.data(), static_cast(buffer.size()), &bytesRead, NULL)) { throw std::system_error(GetLastError(), std::system_category(), "ReadFile failed"); } buffer.resize(bytesRead); return buffer; #else std::vector buffer(256); ssize_t n = ::read(fd_, buffer.data(), buffer.size()); if (n > 0) { buffer.resize(n); return buffer; } return {}; #endif } void SerialPort::close() { #ifdef _WIN32 if (fd_ != INVALID_HANDLE_VALUE) { CloseHandle(fd_); fd_ = INVALID_HANDLE_VALUE; } #else if (fd_ >= 0) { ::close(fd_); fd_ = -1; } #endif } speed_t SerialPort::getBaudRate(int baudrate) { #ifdef _WIN32 // Windows直接使用数值波特率 return baudrate; #else switch (baudrate) { case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 921600: return B921600; case 1000000: return B1000000; case 2000000: return B2000000; case 4000000: return B4000000; default: throw std::invalid_argument("Unsupported baudrate"); } #endif } void SerialPort::start_receiving(const ReceiveCallback& callback) { running_ = true; receiver_thread_ = std::thread([this, callback]() { while (running_.load(std::memory_order_relaxed)) { try { auto data = read(); if (!data.empty()) { callback(data); } } catch (const std::system_error& e) { if (!running_) break; // 正常退出 else throw; // 重新抛出其他错误 } } }); } void SerialPort::stop_receiving() { running_.store(false); #ifdef _WIN32 // Windows通过关闭句柄唤醒阻塞的ReadFile if (fd_ != INVALID_HANDLE_VALUE) { CancelIoEx(fd_, NULL); } #else // Linux通过关闭文件描述符唤醒阻塞的read if (fd_ >= 0) { ::close(fd_); fd_ = -1; } #endif if (receiver_thread_.joinable()) { receiver_thread_.detach(); } } ssize_t SerialPort::write(unsigned char* str, int size) { #ifdef _WIN32 DWORD bytesWritten; if (!WriteFile(fd_, str, static_cast(size), &bytesWritten, NULL)) { throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); } return static_cast(bytesWritten); #else ssize_t result = ::write(fd_, str, size); if (result < 0) { throw std::system_error(errno, std::generic_category(), "write failed"); } return result; #endif } ssize_t SerialPort::write(const std::string& data) { #ifdef _WIN32 DWORD bytesWritten; if (!WriteFile(fd_, data.data(), static_cast(data.size()), &bytesWritten, NULL)) { throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed"); } return static_cast(bytesWritten); #else ssize_t result = ::write(fd_, data.data(), data.size()); if (result < 0) { throw std::system_error(errno, std::generic_category(), "write failed"); } return result; #endif }