/*参数说明: dwBufferLength 缓冲区长度 buff 缓冲区指针 m_WaitTime 超时时长 返回值说明: 0 串口未打开 非0 缓冲区数据长度 */ UINT CSerialPorts::ReadComData(DWORD dwBufferLength,UCHAR * buff,int m_WaitTime) { if(FALSE==CheckPortOpen()) return 0; DWORD dwErr; COMSTAT comStat; //设备信息 //清错,同时填充设备信息结构体 ClearCommError(hCom,&dwErr,&comStat); DWORD dwReadLength=0; //实际读取长度 //判断最小值作为读取长度 dwReadLength=min(dwBufferLength,comStat.cbInQue); if(FALSE==ReadFile(hCom,buff,dwReadLength,&dwReadLength,&m_overLappedRead)) { if(ERROR_IO_PENDING==GetLastError()) { //结束异步IO WaitForSingleObject(m_overLappedRead.hEvent,m_WaitTime); if(FALSE==GetOverlappedResult(hCom,&m_overLappedRead,&dwReadLength,false)) dwReadLength=0; } else dwReadLength=0; } return dwReadLength; }
//设置DCB结构体 BOOL CSerialPorts::SetDCBParam() { if(FALSE==CheckPortOpen()) return FALSE; if(FALSE==GetCommState(hCom,&m_dcb)) { AfxMessageBox(_T("串口连接失败,请重新连接!")); CloseComPort(); return FALSE; } switch(m_nBaud) { case 0: m_nBaud=9600; break; case 1: m_nBaud=19200; break; case 2: m_nBaud=38400; break; case 3: m_nBaud=57600; break; case 4: m_nBaud=115200; break; } //m_dcb.BaudRate=m_nBaud; m_dcb.ByteSize=m_nDataBits; switch(m_nParity) { case 0: m_dcb.Parity=NOPARITY; break; case 1: m_dcb.Parity=ODDPARITY; break; case 2: m_dcb.Parity=EVENPARITY;break; } switch(m_nStopBits) { case 0: m_dcb.StopBits=ONESTOPBIT; break; //case 1: // m_dcb.StopBits=ONESSTOPBITS;break; case 2: m_dcb.StopBits=TWOSTOPBITS; break; } m_dcb.StopBits=m_nStopBits; SetCommState(hCom,&m_dcb); return TRUE; }
//设置端口缓冲区大小 BOOL CSerialPorts::SetPortBuffSize() { if(FALSE==CheckPortOpen()) return FALSE; if(FALSE==SetupComm(hCom,m_inputBuff,m_outputBuff)) { AfxMessageBox(_T("串口数据缓冲区设置失败,请重新连接!")); CloseComPort(); return FALSE; } else return TRUE; }
//关闭串口 void CSerialPorts::CloseComPort() { if(TRUE==CheckPortOpen()) { CloseHandle(hCom); hCom=INVALID_HANDLE_VALUE; } if(m_overLappedRead.hEvent) CloseHandle(m_overLappedRead.hEvent); if(m_overLappedWrite.hEvent) CloseHandle(m_overLappedWrite.hEvent); return; }
//超时设置 BOOL CSerialPorts::SetTimerOut() { if(FALSE==CheckPortOpen()) return FALSE; //设置两个字符到达之间的最大时间间隔 m_timeOuts.ReadIntervalTimeout=MAXDWORD; //读操作的总超时时间的计算系数(ms) m_timeOuts.WriteTotalTimeoutMultiplier=0; //读操作的总超时时间的计算常数(ms) m_timeOuts.ReadTotalTimeoutConstant=0; m_timeOuts.WriteTotalTimeoutConstant=0; m_timeOuts.WriteTotalTimeoutMultiplier=0; return TRUE; }
//打开并初始化串口 BOOL CSerialPorts::InitComPortSet(int nCom, int nBaud,int nParity) { //检查串口是否已经打开 if(TRUE==CheckPortOpen()) CloseComPort(); //设置异步读写事件 if(FALSE==SetEventToReadWrite()) AfxMessageBox(_T("串口连接设置失败,请重新连接!")); //设置波特率、串口名和校验方式 m_nBaud=nBaud; m_nParity=nParity; switch(nCom) { case 0: m_cName=_T("COM1"); break; case 1: m_cName=_T("COM2"); break; case 2: m_cName=_T("COM3"); break; case 3: m_cName=_T("COM4"); break; case 4: m_cName=_T("COM5"); break; case 5: m_cName=_T("COM6"); break; case 6: m_cName=_T("COM7"); break; case 7: m_cName=_T("COM8"); break; } //打开端口 hCom=CreateFile(m_cName, //端口名 GENERIC_READ|GENERIC_WRITE, //访问类型:读写 0, //是否共享端口,0为独占 NULL, //默认安全性 OPEN_EXISTING, //只打开而不创建 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式 NULL); //端口打开时不需要,参数为0 if(INVALID_HANDLE_VALUE==hCom) return FALSE; //设置串口缓冲区大小 SetPortBuffSize(); //设置DCB结构体 if(FALSE==SetDCBParam()) return FALSE; //设置超时结构体 SetTimerOut(); //清空串口数据缓冲区 PurgeComm(hCom,PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_TXABORT); return TRUE; }
uint8_t TSerialPort::ReadByte() { CheckPortOpen(); if (!Select(Settings.ResponseTimeout)) throw TSerialDeviceTransientErrorException("timeout"); uint8_t b; if (read(Fd, &b, 1) < 1) throw TSerialDeviceException("read() failed"); if (Dbg) { std::ios::fmtflags f(std::cerr.flags()); std::cerr << "Read: " << std::hex << std::setw(2) << std::setfill('0') << int(b) << std::endl; std::cerr.flags(f); } return b; }
void TSerialPort::Close() { CheckPortOpen(); modbus_close(Context->Inner); Fd = -1; }
int TSerialPort::ReadFrame(uint8_t* buf, int size, const std::chrono::microseconds& timeout, TFrameCompletePred frame_complete) { CheckPortOpen(); int nread = 0; while (nread < size) { if (frame_complete && frame_complete(buf, nread)) { // XXX A hack. // The problem is that if we don't pause here and the // serial client switches to another device after // processing this frame, that device may miss the frame // boundary and consider the last response (from this // device) and the query (from the master) to be single // frame. On the other hand, we don't want to use // device-specific frame timeout here as it can be quite // long. The proper solution would be perhaps ensuring // that there's a pause of at least // DeviceConfig->FrameTimeoutMs before polling each // device. usleep(DefaultFrameTimeout.count()); break; } if (!Select(!nread ? Settings.ResponseTimeout : timeout.count() < 0 ? DefaultFrameTimeout : timeout)) break; // end of the frame // We don't want to use non-blocking IO in general // (e.g. we want blocking writes), but we don't want // read() call below to block because actual frame // size is not known at this point. So we must // know how many bytes are available int nb; if (ioctl(Fd, FIONREAD, &nb) < 0) throw TSerialDeviceException("FIONREAD ioctl() failed"); if (!nb) continue; // shouldn't happen, actually if (nb > size - nread) nb = size - nread; int n = read(Fd, buf + nread, nb); if (n < 0) throw TSerialDeviceException("read() failed"); if (n < nb) // may happen only due to a kernel/driver bug throw TSerialDeviceException("short read()"); nread += nb; } if (!nread) throw TSerialDeviceTransientErrorException("request timed out"); if (Dbg) { // TBD: move this to libwbmqtt (HexDump?) std::ios::fmtflags f(std::cerr.flags()); std::cerr << "ReadFrame:" << std::hex << std::uppercase << std::setfill('0'); for (int i = 0; i < nread; ++i) { std::cerr << " " << std::setw(2) << int(buf[i]); } std::cerr << std::endl; std::cerr.flags(f); } return nread; }