115 lines
4.3 KiB
C++
115 lines
4.3 KiB
C++
#pragma once
|
|
#include <QObject>
|
|
#include <QThread>
|
|
#include <QSerialPort>
|
|
#include <QTcpSocket>
|
|
#include <QAbstractSocket>
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
|
|
// SerialWorker lives in its own QThread and owns the actual data source,
|
|
// which is either a QSerialPort (real/virtual serial device) or a
|
|
// QTcpSocket (for firmware/emulators that send their debug UART over a
|
|
// TCP connection, e.g. FS-UAE's serial-over-socat setup). Both are
|
|
// QIODevice subclasses, so the byte-parsing pipeline below (line
|
|
// buffering, ANSI stripping, tag detection, logging) is shared as-is
|
|
// between the two transports; only opening/closing/reconnect handling
|
|
// differs.
|
|
//
|
|
// It emits newLine() for every complete line received,
|
|
// tagDetected() when a line contains a recognised tag like [WDG],
|
|
// and clearScreen() when an ANSI clear-screen sequence is received.
|
|
// Auto-reconnect: if the connection drops unexpectedly, the worker
|
|
// retries every reconnectIntervalMs until success or closePort().
|
|
class SerialWorker : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit SerialWorker(QObject *parent = nullptr);
|
|
~SerialWorker();
|
|
|
|
void setAutoReconnect(bool enabled) { m_autoReconnect = enabled; }
|
|
void setReconnectInterval(int ms) { m_reconnectIntervalMs = ms; }
|
|
|
|
public slots:
|
|
void openPort(const QString &portName, qint32 baudRate,
|
|
QSerialPort::DataBits dataBits,
|
|
QSerialPort::Parity parity,
|
|
QSerialPort::StopBits stopBits,
|
|
QSerialPort::FlowControl flowControl);
|
|
void openNetwork(const QString &host, quint16 port);
|
|
void closePort();
|
|
void setLogFile(const QString &path);
|
|
void stopLogging();
|
|
|
|
signals:
|
|
void newLine(const QString &line);
|
|
void tagDetected(const QString &tag, const QString &value);
|
|
void clearScreen();
|
|
void portOpened();
|
|
void portClosed();
|
|
void reconnecting(int attempt);
|
|
void errorOccurred(const QString &message);
|
|
|
|
private slots:
|
|
void onReadyRead();
|
|
void onPortError(QSerialPort::SerialPortError err);
|
|
void onSocketConnected();
|
|
void onSocketDisconnected();
|
|
void onSocketError(QAbstractSocket::SocketError err);
|
|
void tryReconnect();
|
|
void flushScanTail();
|
|
|
|
private:
|
|
enum class ConnectionMode { Serial, Network };
|
|
|
|
void processRawData(const QByteArray &data);
|
|
void appendToLineBuffer(const QByteArray &toProcess);
|
|
void processLine(const QString &line);
|
|
void scheduleReconnect();
|
|
|
|
ConnectionMode m_mode = ConnectionMode::Serial;
|
|
|
|
QSerialPort *m_port = nullptr;
|
|
QTcpSocket *m_socket = nullptr;
|
|
// Points at whichever of m_port/m_socket is the currently active data
|
|
// source; onReadyRead() reads from this without caring which it is.
|
|
QIODevice *m_device = nullptr;
|
|
|
|
QFile *m_logFile = nullptr;
|
|
QTextStream *m_logStream = nullptr;
|
|
QString m_buffer;
|
|
QByteArray m_scanTail; // carries partial ANSI sequences across reads
|
|
|
|
// If no further bytes arrive shortly after some data was held back in
|
|
// m_scanTail (to safely detect a possibly-split ANSI sequence), this
|
|
// timer fires and flushes that tail through the normal line-processing
|
|
// path anyway. Without this, the last line(s) of a burst can sit stuck
|
|
// in m_scanTail indefinitely if the sender goes quiet (e.g. a device
|
|
// that streams a final line and then stops) and only surface once new
|
|
// bytes eventually arrive (e.g. after a reboot).
|
|
QTimer *m_idleFlushTimer = nullptr;
|
|
static constexpr int kIdleFlushMs = 50;
|
|
|
|
QTimer *m_reconnectTimer = nullptr;
|
|
bool m_autoReconnect = true;
|
|
bool m_userDisconnected = false;
|
|
int m_reconnectIntervalMs = 2000;
|
|
int m_reconnectAttempt = 0;
|
|
|
|
// Serial-specific connection parameters
|
|
QString m_portName;
|
|
qint32 m_baudRate = 115200;
|
|
QSerialPort::DataBits m_dataBits = QSerialPort::Data8;
|
|
QSerialPort::Parity m_parity = QSerialPort::NoParity;
|
|
QSerialPort::StopBits m_stopBits = QSerialPort::OneStop;
|
|
QSerialPort::FlowControl m_flowControl = QSerialPort::NoFlowControl;
|
|
|
|
// Network-specific connection parameters
|
|
QString m_networkHost;
|
|
quint16 m_networkPort = 0;
|
|
};
|