This commit is contained in:
2025-06-16 08:52:40 +08:00
commit e53dc4a080
23 changed files with 1725 additions and 0 deletions

249
Serial/SerialPort.cpp Normal file
View File

@ -0,0 +1,249 @@
#include "SerialPort.h"
#include <stdexcept>
#include <system_error>
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<uint8_t> &data) {
#ifdef _WIN32
DWORD bytesWritten;
if (!WriteFile(fd_, data.data(), static_cast<DWORD>(data.size()), &bytesWritten, NULL)) {
throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed");
}
return static_cast<ssize_t>(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<uint8_t> SerialPort::read() {
#ifdef _WIN32
std::vector<uint8_t> buffer(256);
DWORD bytesRead;
if (!ReadFile(fd_, buffer.data(), static_cast<DWORD>(buffer.size()), &bytesRead, NULL)) {
throw std::system_error(GetLastError(), std::system_category(), "ReadFile failed");
}
buffer.resize(bytesRead);
return buffer;
#else
std::vector<uint8_t> 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_.join();
}
}
ssize_t SerialPort::write(unsigned char *str, int size) {
#ifdef _WIN32
DWORD bytesWritten;
if (!WriteFile(fd_, str, static_cast<DWORD>(size), &bytesWritten, NULL)) {
throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed");
}
return static_cast<ssize_t>(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<DWORD>(data.size()), &bytesWritten, NULL)) {
throw std::system_error(GetLastError(), std::system_category(), "WriteFile failed");
}
return static_cast<ssize_t>(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
}