SerialSCF.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #include "SerialSCF.h"
  2. #include <cstring>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <sys/ioctl.h>
  6. #include <sys/select.h>
  7. #include <errno.h>
  8. #include <algorithm>
  9. SerialSCF::SerialSCF()
  10. : hCom(-1),
  11. m_BaudRate(115200),
  12. m_ByteSize(8),
  13. m_Parity(0),
  14. m_StopBits(1),
  15. m_ReadTimeout(1000),
  16. m_WriteTimeout(1000) {
  17. memset(&m_OriginalTermios, 0, sizeof(termios));
  18. memset(&m_CurrentTermios, 0, sizeof(termios));
  19. }
  20. SerialSCF::~SerialSCF() {
  21. Disconnect();
  22. }
  23. int SerialSCF::ConnectCOM(const char* pCom, uint32_t BaudRate, uint32_t ByteSize,
  24. uint32_t Parity, uint32_t StopBits) {
  25. std::cout << "Trying to open serial port: " << pCom << std::endl;
  26. // 打开串口设备 (非阻塞模式)
  27. hCom = open(pCom, O_RDWR | O_NOCTTY);//| O_NONBLOCK
  28. if (hCom < 0) {
  29. // 记录错误日志
  30. return SCF_OPEN_FAILED;
  31. }
  32. std::cout << "Serial port " << pCom << " opened successfully (file descriptor: " << hCom << ")" << std::endl;
  33. // 保存原始串口设置
  34. if (tcgetattr(hCom, &m_OriginalTermios) != 0) {
  35. std::cout << "Failed to get original termios attributes for port: " << pCom << std::endl;
  36. close(hCom);
  37. hCom = -1;
  38. return SCF_OPEN_FAILED;
  39. }
  40. // 配置新的串口设置
  41. memset(&m_CurrentTermios, 0, sizeof(termios));
  42. m_CurrentTermios.c_cflag |= CLOCAL | CREAD; // 本地连接,启用接收
  43. std::cout << "Configuring serial port attributes..." << std::endl;
  44. // 设置波特率
  45. speed_t speed;
  46. switch (BaudRate) {
  47. case 50: speed = B50; break;
  48. case 75: speed = B75; break;
  49. case 110: speed = B110; break;
  50. case 134: speed = B134; break;
  51. case 150: speed = B150; break;
  52. case 200: speed = B200; break;
  53. case 300: speed = B300; break;
  54. case 600: speed = B600; break;
  55. case 1200: speed = B1200; break;
  56. case 1800: speed = B1800; break;
  57. case 2400: speed = B2400; break;
  58. case 4800: speed = B4800; break;
  59. case 9600: speed = B9600; break;
  60. case 19200: speed = B19200; break;
  61. case 38400: speed = B38400; break;
  62. case 57600: speed = B57600; break;
  63. case 115200: speed = B115200; break;
  64. case 230400: speed = B230400; break;
  65. case 460800: speed = B460800; break;
  66. case 500000: speed = B500000; break;
  67. case 576000: speed = B576000; break;
  68. case 921600: speed = B921600; break;
  69. case 1000000: speed = B1000000; break;
  70. default:
  71. std::cout << "Unsupported baud rate: " << BaudRate << std::endl;
  72. close(hCom);
  73. hCom = -1;
  74. return SCF_PARAMETER_ERR;
  75. }
  76. cfsetispeed(&m_CurrentTermios, speed);
  77. cfsetospeed(&m_CurrentTermios, speed);
  78. std::cout << "Set baud rate to: " << BaudRate << std::endl;
  79. // 设置数据位
  80. m_CurrentTermios.c_cflag &= ~CSIZE;
  81. switch (ByteSize) {
  82. case 5: m_CurrentTermios.c_cflag |= CS5; break;
  83. case 6: m_CurrentTermios.c_cflag |= CS6; break;
  84. case 7: m_CurrentTermios.c_cflag |= CS7; break;
  85. case 8: m_CurrentTermios.c_cflag |= CS8; break;
  86. default:
  87. std::cout << "Unsupported data bit size: " << ByteSize << std::endl;
  88. close(hCom);
  89. hCom = -1;
  90. return SCF_PARAMETER_ERR;
  91. }
  92. std::cout << "Set data bits to: " << ByteSize << std::endl;
  93. // 设置校验位
  94. switch (Parity) {
  95. case 0: // None
  96. m_CurrentTermios.c_cflag &= ~PARENB;
  97. std::cout << "Set parity to: None" << std::endl;
  98. break;
  99. case 1: // Odd
  100. m_CurrentTermios.c_cflag |= PARENB;
  101. m_CurrentTermios.c_cflag |= PARODD;
  102. std::cout << "Set parity to: Odd" << std::endl;
  103. break;
  104. case 2: // Even
  105. m_CurrentTermios.c_cflag |= PARENB;
  106. m_CurrentTermios.c_cflag &= ~PARODD;
  107. std::cout << "Set parity to: Even" << std::endl;
  108. break;
  109. default:
  110. std::cout << "Unsupported parity setting: " << Parity << std::endl;
  111. close(hCom);
  112. hCom = -1;
  113. return SCF_PARAMETER_ERR;
  114. }
  115. // 设置停止位
  116. switch (StopBits) {
  117. case 1:
  118. m_CurrentTermios.c_cflag &= ~CSTOPB;
  119. std::cout << "Set stop bits to: 1" << std::endl;
  120. break;
  121. case 2:
  122. m_CurrentTermios.c_cflag |= CSTOPB;
  123. std::cout << "Set stop bits to: 2" << std::endl;
  124. break;
  125. default:
  126. std::cout << "Unsupported stop bits setting: " << StopBits << std::endl;
  127. close(hCom);
  128. hCom = -1;
  129. return SCF_PARAMETER_ERR;
  130. }
  131. // 设置原始模式 (禁用规范模式)
  132. m_CurrentTermios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  133. m_CurrentTermios.c_oflag &= ~OPOST; // 原始输出
  134. std::cout << "Set serial port to raw mode" << std::endl;
  135. // 设置超时和最小读取字节数
  136. m_CurrentTermios.c_cc[VMIN] = 0; // 非阻塞模式
  137. m_CurrentTermios.c_cc[VTIME] = m_ReadTimeout / 100; // 超时时间 (十分之一秒)
  138. std::cout << "Set read timeout to: " << m_ReadTimeout << "ms" << std::endl;
  139. // 刷新缓冲区并应用新设置
  140. tcflush(hCom, TCIOFLUSH);
  141. if (tcsetattr(hCom, TCSANOW, &m_CurrentTermios) != 0) {
  142. close(hCom);
  143. hCom = -1;
  144. return SCF_OPEN_FAILED;
  145. }
  146. std::cout << "Serial port settings applied successfully" << std::endl;
  147. // 清除所有缓冲区
  148. tcflush(hCom, TCIOFLUSH);
  149. std::cout << "Serial port buffers flushed" << std::endl;
  150. return SCF_SUCCEED;
  151. }
  152. int SerialSCF::Connect(ResDataObject& Connectprameters, PacketParser callback,
  153. SCF_TRANSFERTYPE TransferType, uint32_t CommunicateTimeout) {
  154. int ret = SCF_FAILED;
  155. std::cout << "Starting serial port connection process..." << std::endl;
  156. try {
  157. ResDataObject params = Connectprameters;
  158. params.MakeKeyLower();
  159. std::string ConnectionType = (const char*)Connectprameters["type"];
  160. std::string ThisType = GetConnectionType();
  161. // 转换为小写比较
  162. std::transform(ConnectionType.begin(), ConnectionType.end(), ConnectionType.begin(), ::tolower);
  163. std::transform(ThisType.begin(), ThisType.end(), ThisType.begin(), ::tolower);
  164. std::cout << "Checking connection type. Expected: " << ThisType
  165. << ", Received: " << ConnectionType << std::endl;
  166. if (ConnectionType != ThisType) {
  167. std::cout << "Connection type mismatch! Returning parameter error." << std::endl;
  168. return SCF_PARAMETER_ERR;
  169. }
  170. m_PortName = (const char*)Connectprameters["port"];
  171. m_BaudRate = (uint32_t)Connectprameters["baudrate"];
  172. m_ByteSize = (uint32_t)Connectprameters["bytesize"];
  173. m_Parity = (uint32_t)Connectprameters["parity"];
  174. m_StopBits = (uint32_t)Connectprameters["stopbits"];
  175. std::cout << "Attempting to connect to COM port: " << m_PortName
  176. << " with parameters - Baudrate: " << m_BaudRate
  177. << ", Data bits: " << m_ByteSize
  178. << ", Parity: " << m_Parity
  179. << ", Stop bits: " << m_StopBits << std::endl;
  180. ret = ConnectCOM(m_PortName.c_str(), m_BaudRate, m_ByteSize, m_Parity, m_StopBits);
  181. if (ret == SCF_SUCCEED) {
  182. std::cout << "COM port connection successful." << std::endl;
  183. // 初始化日志
  184. std::string NewFileName = "com/s_" + m_PortName;
  185. std::cout << "Initializing SCF with log file: " << NewFileName << std::endl;
  186. InitSCF(NewFileName.c_str());
  187. // 调用基类连接方法
  188. ret = SCF::Connect(Connectprameters, callback, TransferType, CommunicateTimeout);
  189. }
  190. }
  191. catch (...) {
  192. ret = SCF_UNKNOWN;
  193. }
  194. return ret;
  195. }
  196. void SerialSCF::Disconnect() {
  197. SCF::Disconnect();
  198. if (hCom >= 0) {
  199. // 恢复原始串口设置
  200. tcsetattr(hCom, TCSANOW, &m_OriginalTermios);
  201. // 关闭串口
  202. close(hCom);
  203. hCom = -1;
  204. }
  205. }
  206. bool SerialSCF::isConnected() {
  207. // 1. 基础检查:文件描述符已无效,直接返回断开
  208. if (hCom < 0) {
  209. std::cout << "[Debug] isConnected: hCom invalid (value: " << hCom << ")" << std::endl;
  210. return false;
  211. }
  212. // 2. 检查文件描述符有效性(处理EIO和EBADF)
  213. int fdCheck = fcntl(hCom, F_GETFD);
  214. if (fdCheck == -1) {
  215. std::cout << "[Debug] isConnected: fcntl failed | errno=" << errno
  216. << " (" << (errno == EIO ? "EIO(I/O error)" : (errno == EBADF ? "EBADF(Bad file)" : "Unknown")) << ")" << std::endl;
  217. // 关键判断:用户环境中EIO=5(设备删除)、EBADF=9(描述符无效)均视为断开
  218. if (errno == EIO || errno == EBADF) {
  219. hCom = -1; // 重置描述符状态,避免后续误判
  220. return false;
  221. }
  222. // 其他非致命错误(如临时权限问题)不判定为断开
  223. }
  224. // 3. 检查串口硬件状态(核心适配虚拟端口删除后的EIO错误)
  225. int portStatus;
  226. int ioctlRet = ioctl(hCom, TIOCMGET, &portStatus);
  227. if (ioctlRet == -1) {
  228. std::cout << "[Debug] isConnected: ioctl(TIOCMGET) failed | errno=" << errno
  229. << " (" << (errno == EIO ? "EIO(I/O error)" : (errno == EBADF ? "EBADF(Bad file)" : "Unknown")) << ")" << std::endl;
  230. // 同样处理EIO和EBADF,覆盖虚拟端口删除场景
  231. if (errno == EIO || errno == EBADF) {
  232. hCom = -1;
  233. return false;
  234. }
  235. // 临时I/O错误(如设备短暂无响应)不判定为断开
  236. }
  237. // 4. 所有检查通过,确认连接有效
  238. return true;
  239. }
  240. const char* SerialSCF::GetConnectionType() {
  241. static const char* nType = "COM";
  242. return nType;
  243. }
  244. int SerialSCF::SendPacket(SCFPacket* pPacket, uint32_t timeout) {
  245. return SendPacket((char*)(*pPacket), pPacket->GetPacketLen(), timeout);
  246. }
  247. int SerialSCF::SendPacket(const char* pPacket, uint32_t length, uint32_t timeout) {
  248. if (!isConnected()) {
  249. std::cout << "SendPacket failed: Not connected to serial port." << std::endl;
  250. return SCF_DISCONNETED;
  251. }
  252. // 验证输入参数
  253. if (pPacket == nullptr || length == 0) {
  254. std::cout << "SendPacket failed: Invalid packet (nullptr or zero length)." << std::endl;
  255. return SCF_WRITE_FAILED;
  256. }
  257. std::cout << "Starting to send packet. Length: " << length << " bytes, Timeout: " << timeout << " ms." << std::endl;
  258. // 设置写超时
  259. struct timeval tv;
  260. tv.tv_sec = timeout / 1000;
  261. tv.tv_usec = (timeout % 1000) * 1000;
  262. std::cout << "Set write timeout: " << tv.tv_sec << "s " << tv.tv_usec << "us." << std::endl;
  263. fd_set write_fds;
  264. FD_ZERO(&write_fds);
  265. FD_SET(hCom, &write_fds);
  266. uint32_t bytesWritten = 0;
  267. while (bytesWritten < length) {
  268. // 等待串口可写
  269. int selectRet = select(hCom + 1, NULL, &write_fds, NULL, &tv);
  270. if (selectRet <= 0) {
  271. if (selectRet == 0) {
  272. std::cout << "SendPacket timeout: No data written for " << timeout << " ms. Total written: " << bytesWritten << " bytes." << std::endl;
  273. return SCF_TIMEOUT;
  274. }
  275. else {
  276. std::cout << "SendPacket failed: select() error. Return code: " << selectRet << ", Errno: " << errno << std::endl;
  277. return SCF_WRITE_FAILED;
  278. }
  279. }
  280. // 检查串口是否可写
  281. if (!FD_ISSET(hCom, &write_fds)) {
  282. std::cout << "SendPacket warning: Serial port not ready for writing, retrying..." << std::endl;
  283. continue;
  284. }
  285. // 写入数据
  286. ssize_t ret = write(hCom, pPacket + bytesWritten, length - bytesWritten);
  287. if (ret < 0) {
  288. if (errno == EAGAIN || errno == EWOULDBLOCK) {
  289. std::cout << "SendPacket temporary error: Resource busy (EAGAIN/EWOULDBLOCK). Retrying..." << std::endl;
  290. usleep(10000); // 10ms等待后重试
  291. continue;
  292. }
  293. else {
  294. std::cout << "SendPacket failed: write() error. Errno: " << errno << ", Total written: " << bytesWritten << " bytes." << std::endl;
  295. return SCF_WRITE_FAILED;
  296. }
  297. }
  298. bytesWritten += ret;
  299. std::cout << "Wrote " << ret << " bytes. Progress: " << bytesWritten << "/" << length << " bytes." << std::endl;
  300. }
  301. std::cout << "SendPacket completed successfully. Total bytes sent: " << bytesWritten << std::endl;
  302. return bytesWritten;
  303. }
  304. int SerialSCF::ReadData(char* pPacket, uint32_t length, uint32_t timeout) {
  305. if (!isConnected()) {
  306. std::cout << "[Debug] SerialSCF::ReadData:isConnected "<< std::endl;
  307. return SCF_DISCONNETED;
  308. }
  309. if (!pPacket || length == 0) {
  310. std::cout << "[Debug] Invalid parameters: pPacket=" << (void*)pPacket << ", length=" << length << std::endl;
  311. return SCF_PARAMETER_ERR;
  312. }
  313. // 清空缓冲区,防止返回垃圾数据
  314. memset(pPacket, 0, length);
  315. // 设置读超时
  316. struct timeval tv;
  317. tv.tv_sec = timeout / 1000;
  318. tv.tv_usec = (timeout % 1000) * 1000;
  319. fd_set read_fds;
  320. FD_ZERO(&read_fds);
  321. FD_SET(hCom, &read_fds);
  322. // 等待数据到达
  323. int selectRet = select(hCom + 1, &read_fds, NULL, NULL, &tv);
  324. if (selectRet <= 0) {
  325. // 超时或错误
  326. return (selectRet == 0) ? SCF_TIMEOUT : SCF_READ_FAILED;
  327. }
  328. // 读取可用数据
  329. int bytesAvailable = 0;
  330. if (ioctl(hCom, FIONREAD, &bytesAvailable) < 0) {
  331. std::cout << "[Debug] ioctl failed, errno: " << errno << std::endl;
  332. if (errno == EIO || errno == EBADF) {
  333. hCom = -1;
  334. return SCF_DISCONNETED;
  335. }
  336. return SCF_READ_FAILED;
  337. }
  338. // 如果没有数据可读,返回超时(select已经表明有数据,但ioctl说没有,这是异常情况)
  339. if (bytesAvailable <= 0) {
  340. std::cout << "[Debug] ioctl reports no data available (bytesAvailable=" << bytesAvailable << "), but select indicated ready" << std::endl;
  341. return SCF_TIMEOUT;
  342. }
  343. // 确保不超过缓冲区大小
  344. uint32_t bytesToRead = std::min(static_cast<uint32_t>(bytesAvailable), length);
  345. // 再次确认要读取的字节数不为0
  346. if (bytesToRead == 0) {
  347. std::cout << "[Debug] bytesToRead is 0, length=" << length << ", bytesAvailable=" << bytesAvailable << std::endl;
  348. return SCF_TIMEOUT;
  349. }
  350. // 读取数据
  351. ssize_t ret = read(hCom, pPacket, bytesToRead);
  352. if (ret < 0) {
  353. std::cout << "[Debug] Read failed, errno: " << errno << std::endl;
  354. return SCF_READ_FAILED;
  355. }
  356. else if (ret == 0) {
  357. std::cout << "[Debug] Read 0 bytes (maybe data split or EOF)" << std::endl;
  358. return SCF_TIMEOUT; // 返回超时而不是0,避免上层误判
  359. }
  360. else {
  361. // 原有的成功日志־
  362. std::cout << "[Serial Port: " << m_PortName << "] Read " << ret << " bytes." << std::endl;
  363. return ret;
  364. }
  365. }
  366. SERIALSCF_C_API SCF* GetSCF() {
  367. SerialSCF* p = new SerialSCF();
  368. return static_cast<SCF*>(p);
  369. }
  370. SERIALSCF_C_API void ReleaseSCF(SCF* p) {
  371. delete static_cast<SerialSCF*>(p);
  372. }