123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- #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) {
- // 打开串口设备 (非阻塞模式)
- hCom = open(pCom, O_RDWR | O_NOCTTY | O_NONBLOCK);
- if (hCom < 0) {
- // 记录错误日志
- return SCF_OPEN_FAILED;
- }
- // 保存原始串口设置
- if (tcgetattr(hCom, &m_OriginalTermios) != 0) {
- close(hCom);
- hCom = -1;
- return SCF_OPEN_FAILED;
- }
- // 配置新的串口设置
- memset(&m_CurrentTermios, 0, sizeof(termios));
- m_CurrentTermios.c_cflag |= CLOCAL | CREAD; // 本地连接,启用接收
- // 设置波特率
- 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:
- close(hCom);
- hCom = -1;
- return SCF_PARAMETER_ERR;
- }
- cfsetispeed(&m_CurrentTermios, speed);
- cfsetospeed(&m_CurrentTermios, speed);
- // 设置数据位
- 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:
- close(hCom);
- hCom = -1;
- return SCF_PARAMETER_ERR;
- }
- // 设置校验位
- switch (Parity) {
- case 0: // None
- m_CurrentTermios.c_cflag &= ~PARENB;
- break;
- case 1: // Odd
- m_CurrentTermios.c_cflag |= PARENB;
- m_CurrentTermios.c_cflag |= PARODD;
- break;
- case 2: // Even
- m_CurrentTermios.c_cflag |= PARENB;
- m_CurrentTermios.c_cflag &= ~PARODD;
- break;
- default:
- close(hCom);
- hCom = -1;
- return SCF_PARAMETER_ERR;
- }
- // 设置停止位
- switch (StopBits) {
- case 1:
- m_CurrentTermios.c_cflag &= ~CSTOPB;
- break;
- case 2:
- m_CurrentTermios.c_cflag |= CSTOPB;
- break;
- default:
- close(hCom);
- hCom = -1;
- return SCF_PARAMETER_ERR;
- }
- // 设置原始模式 (禁用规范模式)
- m_CurrentTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- m_CurrentTermios.c_oflag &= ~OPOST; // 原始输出
- // 设置超时和最小读取字节数
- m_CurrentTermios.c_cc[VMIN] = 0; // 非阻塞模式
- m_CurrentTermios.c_cc[VTIME] = m_ReadTimeout / 100; // 超时时间 (十分之一秒)
- // 刷新缓冲区并应用新设置
- tcflush(hCom, TCIOFLUSH);
- if (tcsetattr(hCom, TCSANOW, &m_CurrentTermios) != 0) {
- close(hCom);
- hCom = -1;
- return SCF_OPEN_FAILED;
- }
- // 清除所有缓冲区
- tcflush(hCom, TCIOFLUSH);
- return SCF_SUCCEED;
- }
- int SerialSCF::Connect(ResDataObject& Connectprameters, PacketParser callback,
- SCF_TRANSFERTYPE TransferType, uint32_t CommunicateTimeout) {
- int ret = SCF_FAILED;
- 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);
- if (ConnectionType != ThisType) {
- 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"];
- ret = ConnectCOM(m_PortName.c_str(), m_BaudRate, m_ByteSize, m_Parity, m_StopBits);
- if (ret == SCF_SUCCEED) {
- // 初始化日志
- std::string NewFileName = "com/s_" + m_PortName;
- 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() {
- return (hCom >= 0);
- }
- 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()) {
- return SCF_DISCONNETED;
- }
- // 设置写超时
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
- 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) {
- // 超时或错误
- return (selectRet == 0) ? SCF_TIMEOUT : SCF_WRITE_FAILED;
- }
- // 写入数据
- ssize_t ret = write(hCom, pPacket + bytesWritten, length - bytesWritten);
- if (ret < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- // 短暂等待后重试
- usleep(10000); // 10ms
- continue;
- }
- return SCF_WRITE_FAILED;
- }
- bytesWritten += ret;
- }
- return bytesWritten;
- }
- int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) {
- if (!isConnected()) {
- return SCF_DISCONNETED;
- }
- // 设置读超时
- 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) {
- return SCF_READ_FAILED;
- }
- // 确保不超过缓冲区大小
- uint32_t bytesToRead = std::min(static_cast<uint32_t>(bytesAvailable), length);
- // 读取数据
- ssize_t ret = read(hCom, pPacket, bytesToRead);
- if (ret < 0) {
- return SCF_READ_FAILED;
- }
- 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);
- }
|