- Grafische Ausgabe kann nun als Video aufgezeichnet werden
- Verbindung über TCP/IP
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user