CP控制V1.0
This commit is contained in:
250
Serial/SerialPort.cpp
Normal file
250
Serial/SerialPort.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
#include <thread>
|
||||
#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_.detach();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user