Files
uartscope/include/serialworker.h
Dany Thinnes 934f32a3bd - TAG [uartscopeclean] eingefügt, über den alle Logs gelöscht werden
- Pauseknopf und Eingabezeile hinzugefügt, um Kommandos an den Pi zu schicken
2026-09-07 14:53:54 +02:00

131 lines
5.2 KiB
C++

#pragma once
#include <QObject>
#include <QThread>
#include <QSerialPort>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QFile>
#include <QTextStream>
#include <QString>
#include <QByteArray>
#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();
// Writes raw bytes back out to the currently open device (serial port
// or TCP socket) - used by the send bar (Pause button / input line) to
// talk back to the firmware. No-op if nothing is currently open.
void writeData(const QByteArray &data);
signals:
void newLine(const QString &line);
void tagDetected(const QString &tag, const QString &value);
// Fired specifically for a [SCREENSHOT] control tag (in addition to the
// normal tagDetected() above, so it still shows up in the Tag Monitor
// history too). `filename` is whatever followed the tag, verbatim -
// the receiver is responsible for sanitizing/defaulting it.
void screenshotRequested(const QString &filename);
// Fired specifically for a [UARTSCOPECLEAR] control tag (in addition to
// the normal tagDetected() above): the firmware asks for a completely
// fresh UARTScope state, e.g. right at boot. Handled the same as an
// ANSI clear-screen - wipes Raw view, Table view, Tag Monitor and the
// video preview frame.
void uartscopeClearRequested();
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;
};