525 lines
12 KiB
C++
525 lines
12 KiB
C++
#include "lruserialport.h"
|
|
|
|
#include <QEvent>
|
|
|
|
#include <qDebug>
|
|
|
|
static int lru_serial_port_id=1;
|
|
|
|
//#define USE_QT_PORT
|
|
|
|
#ifdef USE_QT_PORT
|
|
class LruSerialPort::Implementation
|
|
{
|
|
public:
|
|
QSerialPort realPort;
|
|
|
|
Implemetation(LruSerialPort&)
|
|
{
|
|
}
|
|
};
|
|
|
|
#else
|
|
|
|
#include <QThread>
|
|
#include <windows.h>
|
|
|
|
#include <string.h>
|
|
|
|
#define QGSERIALPORT_RX_BUFFER_SIZE 128
|
|
|
|
enum { RX_BUFFER_SIZE=QGSERIALPORT_RX_BUFFER_SIZE, RX_BUFFERS=32, RX_BUFFER_MASK=(RX_BUFFERS-1)};
|
|
|
|
static QEvent::Type rxEventType=QEvent::None;
|
|
|
|
class ReceiverHander
|
|
{
|
|
public:
|
|
virtual bool process_data(char* data, unsigned int len)=0;
|
|
};
|
|
|
|
static QString getLastErrorMsg() {
|
|
LPWSTR bufPtr = NULL;
|
|
DWORD err = GetLastError();
|
|
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, err, 0, (LPWSTR)&bufPtr, 0, NULL);
|
|
const QString result =
|
|
(bufPtr) ? QString::fromUtf16((const ushort*)bufPtr).trimmed() :
|
|
QString("Unknown Error %1").arg(err);
|
|
LocalFree(bufPtr);
|
|
return result;
|
|
}
|
|
|
|
|
|
static QString portFullPath(const QString& port)
|
|
{
|
|
return QString("\\\\.\\")+port;
|
|
}
|
|
|
|
class QgSerialPort: public QThread
|
|
{
|
|
public:
|
|
struct {
|
|
volatile HANDLE hCom;
|
|
HANDLE hEvent;
|
|
HANDLE hBreak;
|
|
HANDLE hRead;
|
|
HANDLE hWrite;
|
|
HANDLE hEmpty;
|
|
OVERLAPPED rx_overlap;
|
|
OVERLAPPED tx_overlap;
|
|
OVERLAPPED tx_empty_overlap;
|
|
|
|
DWORD waitCommEvent;
|
|
|
|
QObject* listener;
|
|
|
|
ReceiverHander* receiverHandler;
|
|
} cx;
|
|
|
|
LruSerialPort& sp;
|
|
QString portName;
|
|
bool is_open;
|
|
|
|
DWORD s_parity;
|
|
DWORD s_baud;
|
|
|
|
QgSerialPort(LruSerialPort& sport):
|
|
sp(sport),
|
|
is_open(false),
|
|
s_parity(EVENPARITY),
|
|
s_baud(CBR_9600)
|
|
{
|
|
memset(&cx, 0, sizeof cx);
|
|
|
|
cx.hEvent=CreateEvent(0, FALSE, FALSE, 0);
|
|
cx.hRead=CreateEvent(0, TRUE, FALSE, 0);
|
|
cx.hWrite=CreateEvent(0, TRUE, TRUE, 0);
|
|
cx.hBreak=CreateEvent(0, FALSE, FALSE, 0);
|
|
cx.hEmpty=CreateEvent(0, TRUE, FALSE, 0);
|
|
|
|
cx.rx_overlap.hEvent=cx.hRead;
|
|
cx.tx_overlap.hEvent=cx.hWrite;
|
|
|
|
if (rxEventType==QEvent::None)
|
|
rxEventType=(QEvent::Type)QEvent::registerEventType();
|
|
|
|
cx.hCom=INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
void close()
|
|
{
|
|
please_terminate=true;
|
|
CloseHandle(cx.hWrite);
|
|
CloseHandle(cx.hRead);
|
|
}
|
|
|
|
~QgSerialPort()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void setPortName(const QString name)
|
|
{
|
|
portName=name;
|
|
}
|
|
void setFlowControl(QSerialPort::FlowControl)
|
|
{
|
|
}
|
|
|
|
void setDataBits(QSerialPort::DataBits)
|
|
{
|
|
}
|
|
|
|
void setReadBufferSize(int/*n*/)
|
|
{
|
|
}
|
|
|
|
void setParity(QSerialPort::Parity p)
|
|
{
|
|
s_parity= p==QSerialPort::EvenParity ? EVENPARITY : NOPARITY;
|
|
}
|
|
|
|
|
|
void setBaudRate(QSerialPort::BaudRate b)
|
|
{
|
|
s_baud= b==QSerialPort::Baud9600 ? CBR_9600: CBR_115200;
|
|
}
|
|
|
|
bool open(QIODevice::OpenMode)
|
|
{
|
|
QString name=portFullPath(portName);
|
|
cx.hCom=CreateFileA(portName.toLatin1().constData(),
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
0,
|
|
0,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
0);
|
|
if (cx.hCom==INVALID_HANDLE_VALUE)
|
|
{
|
|
qDebug()<<"POER Error:"<<name<<getLastErrorMsg();
|
|
return false;
|
|
}
|
|
DCB dcb;
|
|
/*BOOL fSuccess=*/GetCommState(cx.hCom, &dcb);
|
|
dcb.fOutxCtsFlow=FALSE;
|
|
dcb.fOutxDsrFlow=FALSE;
|
|
dcb.fDtrControl=FALSE;
|
|
dcb.BaudRate=s_baud; //CBR_9600;
|
|
dcb.ByteSize=8;
|
|
dcb.Parity=s_parity; //EVENPARITY;
|
|
dcb.StopBits=ONESTOPBIT;
|
|
SetCommState(cx.hCom, &dcb);
|
|
|
|
COMMTIMEOUTS tm;
|
|
memset(&tm, 0, sizeof tm);
|
|
tm.ReadIntervalTimeout=10; //MAXDWORD; //50;
|
|
tm.ReadTotalTimeoutMultiplier=10; //MAXDWORD; //50;
|
|
tm.ReadTotalTimeoutConstant=100000;
|
|
|
|
tm.WriteTotalTimeoutConstant=10;
|
|
tm.WriteTotalTimeoutMultiplier=10;
|
|
/*
|
|
tm.ReadIntervalTimeout=100;
|
|
tm.ReadTotalTimeoutConstant=10000;
|
|
tm.ReadTotalTimeoutMultiplier=10;
|
|
tm.WriteTotalTimeoutConstant=1000;
|
|
tm.WriteTotalTimeoutMultiplier=10;
|
|
*/
|
|
SetCommTimeouts(cx.hCom, &tm);
|
|
SetupComm(cx.hCom, 2048, 2048);
|
|
|
|
is_open=true;
|
|
start();
|
|
|
|
SetEvent(cx.hEvent);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isOpen() const
|
|
{
|
|
return is_open;
|
|
}
|
|
|
|
bool write_immediate(unsigned char c)
|
|
{
|
|
DWORD write_out;
|
|
BOOL res=WriteFile(cx.hCom, &c, 1, &write_out, &cx.tx_overlap);
|
|
return res || ERROR_IO_PENDING==GetLastError();
|
|
}
|
|
|
|
bool write_(const void* data, unsigned int len)
|
|
{
|
|
static bool pending_op=false;
|
|
static char buffer[3000];
|
|
const void* p;
|
|
|
|
if (cx.hCom==INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
DWORD write_out;
|
|
|
|
if (pending_op)
|
|
{
|
|
BOOL res=GetOverlappedResult(cx.hCom, &cx.tx_overlap, &write_out, TRUE);
|
|
if (!res)
|
|
return false;
|
|
}
|
|
|
|
pending_op=false;
|
|
|
|
if (len<=sizeof buffer)
|
|
{
|
|
memcpy(buffer, data, len);
|
|
p=buffer;
|
|
}
|
|
else
|
|
p=data;
|
|
|
|
BOOL res=WriteFile(cx.hCom, p, len, &write_out, &cx.tx_overlap);
|
|
if (!res)
|
|
{
|
|
if (ERROR_IO_PENDING==GetLastError())
|
|
{
|
|
if (p==data) //Wait immediatly
|
|
{
|
|
BOOL res=GetOverlappedResult(cx.hCom, &cx.tx_overlap, &write_out, TRUE);
|
|
if (res && write_out==len)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
pending_op=true;
|
|
}
|
|
return true;
|
|
}
|
|
else if (write_out==len)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
volatile int rx_consumed[RX_BUFFERS];
|
|
volatile bool please_terminate;
|
|
volatile bool please_break;
|
|
|
|
void run()
|
|
{
|
|
please_terminate=false;
|
|
please_break=false;
|
|
|
|
char buffer[RX_BUFFERS][RX_BUFFER_SIZE];
|
|
unsigned int current_buffer=0;
|
|
|
|
memset((void*)rx_consumed, 0, sizeof rx_consumed);
|
|
|
|
//unsigned int max_in_len=1;
|
|
//setPriority(QThread::HighPriority);
|
|
HANDLE waitHandles[4];
|
|
|
|
waitHandles[0]=cx.hRead;
|
|
waitHandles[1]=cx.hBreak;
|
|
waitHandles[2]=cx.hEmpty;
|
|
|
|
waitHandles[2]=0;
|
|
const unsigned int wait_event_num=2;
|
|
|
|
unsigned int tempWaitCommEvent=0;
|
|
|
|
for(;!please_terminate;)
|
|
{
|
|
qDebug()<<"SP Listener: wait...";
|
|
|
|
BOOL res=WaitForSingleObject(cx.hEvent, INFINITE);
|
|
if (res!=WAIT_OBJECT_0)
|
|
return;
|
|
|
|
please_break=false;
|
|
qDebug()<<"SP Listener: started...";
|
|
|
|
for(;cx.hCom;)
|
|
{
|
|
DWORD read_in=0;
|
|
DWORD ok=ReadFile(cx.hCom, buffer[current_buffer], sizeof buffer[0], &read_in, &cx.rx_overlap);
|
|
if (!ok)
|
|
{
|
|
read_in=0;
|
|
DWORD err=GetLastError();
|
|
if (err==ERROR_IO_PENDING)
|
|
{
|
|
//ok=WaitForSingleObject(cx.hRead, INFINITE);
|
|
ok=WaitForMultipleObjects(wait_event_num, waitHandles, FALSE, INFINITE);
|
|
if (ok==WAIT_OBJECT_0)
|
|
{
|
|
err=GetOverlappedResult(cx.hCom, &cx.rx_overlap, &read_in, FALSE);
|
|
if (!err)
|
|
read_in=0;
|
|
}
|
|
else if (ok==WAIT_OBJECT_0+2) //ComEvent
|
|
{
|
|
err=GetOverlappedResult(cx.hCom, &cx.tx_empty_overlap, &read_in, FALSE);
|
|
if (!err)
|
|
read_in=0;
|
|
else
|
|
{
|
|
if (cx.waitCommEvent & EV_TXEMPTY)
|
|
tempWaitCommEvent=1;
|
|
else
|
|
tempWaitCommEvent=0;
|
|
}
|
|
}
|
|
else//if (ok>=WAIT_ABANDONED_0 && ok<=WAIT_ABANDONED_0+1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cx.hCom==INVALID_HANDLE_VALUE && please_break)
|
|
break;
|
|
if (please_terminate)
|
|
break;
|
|
|
|
if (read_in)
|
|
{
|
|
DWORD errorWord=0;
|
|
ClearCommError(cx.hCom, &errorWord, 0);
|
|
errorWord&=~(CE_OVERRUN|CE_RXOVER);
|
|
if (errorWord)
|
|
{
|
|
tempWaitCommEvent=0;
|
|
continue;
|
|
}
|
|
if (cx.receiverHandler)
|
|
{
|
|
if (!cx.receiverHandler->process_data(buffer[current_buffer], read_in))
|
|
{
|
|
tempWaitCommEvent=0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
rx_consumed[current_buffer]=1;
|
|
Q_EMIT sp.asynchReadyRead(current_buffer, buffer[current_buffer], read_in, tempWaitCommEvent);
|
|
//sp.propagateData(current_buffer, buffer[current_buffer], read_in, tempWaitCommEvent);
|
|
unsigned int next_buffer=(current_buffer+1) & RX_BUFFER_MASK;
|
|
if (rx_consumed[next_buffer]==0)
|
|
current_buffer=next_buffer;
|
|
}
|
|
tempWaitCommEvent=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
QByteArray pendingData;
|
|
|
|
void flush()
|
|
{
|
|
}
|
|
|
|
int bytesAvailable() const
|
|
{
|
|
return pendingData.size();
|
|
}
|
|
|
|
QByteArray readAll()
|
|
{
|
|
QByteArray tmp=pendingData;
|
|
pendingData.clear();
|
|
return tmp;
|
|
}
|
|
|
|
qint64 write(const QByteArray& d)
|
|
{
|
|
write_(d.constData(), d.size());
|
|
return d.size();
|
|
}
|
|
qint64 write(const char* d, int len)
|
|
{
|
|
write_(d, len);
|
|
return len;
|
|
}
|
|
};
|
|
|
|
class LruSerialPort::Implementation
|
|
{
|
|
public:
|
|
QgSerialPort realPort;
|
|
|
|
Implementation(LruSerialPort& lru):
|
|
realPort(lru)
|
|
{
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
LruSerialPort::LruSerialPort(const QString & name, deviceID device, QSerialPort::BaudRate baud, QSerialPort::Parity parity):
|
|
//QSerialPort(),
|
|
port_name(name),
|
|
portId(lru_serial_port_id),
|
|
realPortOk(false),
|
|
inFilter(0),
|
|
enabled(true),
|
|
chars_in(0),
|
|
chars_out(0),
|
|
p_(* new Implementation(*this))
|
|
{
|
|
if (!name.isEmpty() && (!name.startsWith('<')))
|
|
{
|
|
p_.realPort.setPortName(name);
|
|
p_.realPort.setFlowControl(QSerialPort::NoFlowControl);
|
|
p_.realPort.setDataBits(QSerialPort::Data8);
|
|
p_.realPort.setReadBufferSize(256);
|
|
p_.realPort.setParity(parity);
|
|
p_.realPort.setBaudRate(baud);
|
|
p_.realPort.open(QIODevice::ReadWrite);
|
|
realPortOk=p_.realPort.isOpen();
|
|
qDebug()<<"Open"<<name<<baud<<parity<<realPortOk;
|
|
#ifdef USE_QT_PORT
|
|
connect(&p_.realPort, &QSerialPort::readyRead, this, [this]()
|
|
{
|
|
Q_EMIT readyRead();
|
|
});
|
|
#else
|
|
connect(this, &LruSerialPort::asynchReadyRead, this, [this](unsigned int index, const char* data,int len, unsigned int)
|
|
{
|
|
p_.realPort.pendingData.append(data, len);
|
|
p_.realPort.rx_consumed[index]=0;
|
|
Q_EMIT readyRead();
|
|
}, Qt::QueuedConnection);
|
|
#endif
|
|
}
|
|
++lru_serial_port_id;
|
|
numDevice = device;
|
|
}
|
|
|
|
void LruSerialPort::close()
|
|
{
|
|
p_.realPort.close();
|
|
}
|
|
|
|
void LruSerialPort::setEnabled(bool enable)
|
|
{
|
|
enabled=enable;
|
|
}
|
|
|
|
void LruSerialPort::setFilter(LruSerialFilter* filter)
|
|
{
|
|
inFilter=filter;
|
|
}
|
|
|
|
QByteArray LruSerialPort::readAll()
|
|
{
|
|
if (p_.realPort.bytesAvailable()<1)
|
|
return QByteArray();
|
|
|
|
return p_.realPort.readAll();
|
|
}
|
|
|
|
QByteArray LruSerialPort::readData()
|
|
{
|
|
if (!realPortOk)
|
|
return QByteArray();
|
|
|
|
if (p_.realPort.bytesAvailable()<1)
|
|
return QByteArray();
|
|
|
|
QByteArray data=p_.realPort.readAll();
|
|
chars_in+=data.size();
|
|
if (enabled)
|
|
{
|
|
bool ok=true;
|
|
if (inFilter)
|
|
ok=inFilter->serialFilterIn(*this, data);
|
|
if (ok)
|
|
return data;
|
|
}
|
|
return QByteArray();
|
|
}
|
|
|
|
qint64 LruSerialPort::write(const QByteArray& data)
|
|
{
|
|
chars_out+=data.size();
|
|
if (enabled && realPortOk)
|
|
return p_.realPort.write(data);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void LruSerialPort::writeChar(char c)
|
|
{
|
|
++chars_out;
|
|
if (enabled && realPortOk)
|
|
p_.realPort.write(&c, 1);
|
|
}
|
|
|
|
void LruSerialPort::flush()
|
|
{
|
|
if (realPortOk)
|
|
p_.realPort.flush();
|
|
}
|