SXXXXXXX_PyBusMonitor1553/cpp/GrifoScope/GrifoXLruMonitor/lruserialport.cpp
2025-12-17 07:59:30 +01:00

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();
}