- Grafische Ausgabe kann nun als Video aufgezeichnet werden

- Verbindung über TCP/IP
This commit is contained in:
2026-08-18 07:34:20 +02:00
parent a942f8385b
commit 36923e1646
16 changed files with 724 additions and 311 deletions

View File

@@ -4,16 +4,31 @@
#include <QLineEdit>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QTabWidget>
#include <QSpinBox>
#include <QSerialPort>
#include <QSerialPortInfo>
struct SerialConfig {
// Which transport to use: a real/virtual serial port, or a plain TCP
// connection (e.g. for emulators/firmware that stream their debug
// UART over the network, such as FS-UAE piped through socat).
enum class ConnectionType { Serial, Network };
ConnectionType connectionType = ConnectionType::Serial;
// Serial-specific
QString portName;
qint32 baudRate = 115200;
QSerialPort::DataBits dataBits = QSerialPort::Data8;
QSerialPort::Parity parity = QSerialPort::NoParity;
QSerialPort::StopBits stopBits = QSerialPort::OneStop;
QSerialPort::FlowControl flowControl = QSerialPort::NoFlowControl;
// Network-specific
QString networkHost;
quint16 networkPort = 0;
// Common
QString logFilePath;
};
@@ -32,11 +47,17 @@ private slots:
private:
void addPtyPorts();
QTabWidget *m_modeTabs = nullptr;
QComboBox *m_portCombo = nullptr;
QComboBox *m_baudCombo = nullptr;
QComboBox *m_dataBitsCombo = nullptr;
QComboBox *m_parityCombo = nullptr;
QComboBox *m_stopBitsCombo = nullptr;
QComboBox *m_flowCombo = nullptr;
QLineEdit *m_hostEdit = nullptr;
QSpinBox *m_portSpin = nullptr;
QLineEdit *m_logPathEdit = nullptr;
};

View File

@@ -2,17 +2,27 @@
#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 QSerialPort.
// 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 port drops unexpectedly, the worker retries
// every reconnectIntervalMs until success or closePort().
// Auto-reconnect: if the connection drops unexpectedly, the worker
// retries every reconnectIntervalMs until success or closePort().
class SerialWorker : public QObject
{
Q_OBJECT
@@ -30,6 +40,7 @@ public slots:
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();
@@ -46,16 +57,28 @@ signals:
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;
@@ -77,10 +100,15 @@ private:
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;
};

View File

@@ -1,15 +1,22 @@
#pragma once
#include <QWidget>
#include <QImage>
#include <QSize>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QThread>
#include <QProcess>
#include "v4l2worker.h"
// VideoWidget shows the live V4L2 preview and can additionally record it
// to an MP4 file. Recording works by piping raw BGRA frames into an
// external `ffmpeg` process (via QProcess/stdin), which encodes them to
// H.264/MP4 - this avoids reimplementing video encoding and just needs
// ffmpeg to be installed and on PATH.
class VideoWidget : public QWidget
{
Q_OBJECT
@@ -29,6 +36,7 @@ private slots:
void onStartStop();
void onFreeze(bool frozen);
void onScreenshot();
void onRecordToggled(bool checked);
void refreshDeviceList();
protected:
@@ -40,6 +48,9 @@ private:
void startCapture();
void stopCapture();
void updateScaled();
void startRecording();
void stopRecording();
void writeRecordingFrame(const QImage &frame);
QThread *m_thread = nullptr;
V4l2Worker *m_worker = nullptr;
@@ -54,5 +65,15 @@ private:
QPushButton *m_startBtn = nullptr;
QPushButton *m_freezeBtn = nullptr;
QPushButton *m_screenshotBtn = nullptr;
QPushButton *m_recordBtn = nullptr;
QLabel *m_infoLabel = nullptr;
// Recording state. m_recordProcess is the running ffmpeg instance (or
// nullptr when not recording); m_recordSize is the frame size the
// recording was started with, so a mid-stream resolution change (which
// ffmpeg's raw pipe can't handle) can be detected and the recording
// stopped cleanly instead of corrupting the output file.
QProcess *m_recordProcess = nullptr;
bool m_recording = false;
QSize m_recordSize;
};