- TAG [uartscopeclean] eingefügt, über den alle Logs gelöscht werden
- Pauseknopf und Eingabezeile hinzugefügt, um Kommandos an den Pi zu schicken
This commit is contained in:
@@ -26,6 +26,7 @@ set(SOURCES
|
||||
src/connectdialog.cpp
|
||||
src/v4l2worker.cpp
|
||||
src/videowidget.cpp
|
||||
src/sendbar.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
@@ -38,6 +39,7 @@ set(HEADERS
|
||||
include/connectdialog.h
|
||||
include/v4l2worker.h
|
||||
include/videowidget.h
|
||||
include/sendbar.h
|
||||
)
|
||||
|
||||
qt6_add_resources(RESOURCES resources.qrc)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "tagwidget.h"
|
||||
#include "videowidget.h"
|
||||
#include "connectdialog.h"
|
||||
#include "sendbar.h"
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -64,6 +65,7 @@ private:
|
||||
TableView *m_tableView = nullptr;
|
||||
TagWidget *m_tagWidget = nullptr;
|
||||
VideoWidget *m_videoWidget= nullptr;
|
||||
SendBar *m_sendBar = nullptr;
|
||||
|
||||
QLabel *m_portLabel = nullptr;
|
||||
QLabel *m_stateLabel = nullptr;
|
||||
|
||||
78
include/sendbar.h
Normal file
78
include/sendbar.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QPoint>
|
||||
|
||||
// SendBar sits below the Raw/Table view and the HDMI preview and lets the
|
||||
// user talk BACK to the firmware over UART/TCP, for debugging:
|
||||
//
|
||||
// [ Pause ] [ ...free-text input...................... ] [Senden] [ending v]
|
||||
//
|
||||
// - The left button sends a fixed, user-configurable byte sequence (right-
|
||||
// click it to change the label and the code - e.g. a single control byte
|
||||
// or a short command string). Handy for a "pause"/"step" command that's
|
||||
// sent identically every time.
|
||||
// - The input line sends whatever is typed the moment Enter is pressed
|
||||
// (or the "Senden" button is clicked), with a selectable line ending
|
||||
// appended.
|
||||
//
|
||||
// SendBar itself never touches the serial device - it only emits
|
||||
// sendRequested() with the raw bytes to write; MainWindow forwards those to
|
||||
// SerialWorker::writeData().
|
||||
class SendBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SendBar(QWidget *parent = nullptr);
|
||||
|
||||
// Persisted configuration accessors, used by MainWindow::save/restoreSettings.
|
||||
QString buttonLabel() const { return m_buttonLabel; }
|
||||
QString buttonCode() const { return m_buttonCode; } // raw, as typed (with escapes)
|
||||
int lineEndingIndex() const;
|
||||
|
||||
void setButtonLabel(const QString &label);
|
||||
void setButtonCode(const QString &code);
|
||||
void setLineEndingIndex(int index);
|
||||
|
||||
// Turns C-style escapes (\n \r \t \0 \\ and \xHH) in `text` into the
|
||||
// actual bytes they represent. Exposed as static so it can be reused/
|
||||
// unit-tested independently of the widget.
|
||||
static QByteArray unescape(const QString &text);
|
||||
|
||||
public slots:
|
||||
// Enables/disables the send controls - there is no point letting the
|
||||
// user try to send anything while nothing is connected.
|
||||
void setConnected(bool connected);
|
||||
|
||||
signals:
|
||||
// Raw bytes ready to be written to the serial/network device.
|
||||
void sendRequested(const QByteArray &data);
|
||||
// The button label/code or the line-ending choice changed - MainWindow
|
||||
// listens to this to persist it immediately, consistent with how the
|
||||
// Tag filter and Screenshot folder dialogs already behave.
|
||||
void configChanged();
|
||||
|
||||
private slots:
|
||||
void onPauseClicked();
|
||||
void onPauseContextMenu(const QPoint &pos);
|
||||
void onSendClicked();
|
||||
void configureButton();
|
||||
|
||||
private:
|
||||
void setupUi();
|
||||
void updatePauseButtonUi();
|
||||
void sendInputLine();
|
||||
|
||||
QPushButton *m_pauseBtn = nullptr;
|
||||
QLineEdit *m_inputEdit = nullptr;
|
||||
QPushButton *m_sendBtn = nullptr;
|
||||
QComboBox *m_endingCombo = nullptr;
|
||||
|
||||
QString m_buttonLabel = tr("Pause");
|
||||
QString m_buttonCode = QStringLiteral("PAUSE\\n");
|
||||
};
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
|
||||
// SerialWorker lives in its own QThread and owns the actual data source,
|
||||
@@ -44,6 +45,10 @@ public slots:
|
||||
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);
|
||||
@@ -53,6 +58,12 @@ signals:
|
||||
// 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();
|
||||
|
||||
@@ -38,11 +38,24 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
connect(m_worker, &SerialWorker::tagDetected, this, &MainWindow::onTagDetected);
|
||||
connect(m_worker, &SerialWorker::screenshotRequested, this, &MainWindow::onScreenshotRequested);
|
||||
connect(m_worker, &SerialWorker::clearScreen, this, &MainWindow::onClearScreen);
|
||||
// [UARTSCOPECLEAR] control tag from the firmware: wipe everything, same
|
||||
// as an ANSI clear-screen - lets a baremetal project guarantee a fresh
|
||||
// UARTScope state right when it boots.
|
||||
connect(m_worker, &SerialWorker::uartscopeClearRequested, this, &MainWindow::clearAllViews);
|
||||
connect(m_worker, &SerialWorker::portOpened, this, &MainWindow::onPortOpened);
|
||||
connect(m_worker, &SerialWorker::portClosed, this, &MainWindow::onPortClosed);
|
||||
connect(m_worker, &SerialWorker::reconnecting, this, &MainWindow::onReconnecting);
|
||||
connect(m_worker, &SerialWorker::errorOccurred, this, &MainWindow::onError);
|
||||
|
||||
// Send bar (Pause button + input line): forward whatever it wants sent
|
||||
// to the worker thread. Queued since m_worker lives in m_thread.
|
||||
connect(m_sendBar, &SendBar::sendRequested, this, [this](const QByteArray &data) {
|
||||
QMetaObject::invokeMethod(m_worker, "writeData",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QByteArray, data));
|
||||
});
|
||||
connect(m_sendBar, &SendBar::configChanged, this, &MainWindow::saveSettings);
|
||||
|
||||
m_thread->start();
|
||||
}
|
||||
|
||||
@@ -79,7 +92,16 @@ void MainWindow::doShutdown()
|
||||
|
||||
void MainWindow::setupUi()
|
||||
{
|
||||
m_mainSplitter = new QSplitter(Qt::Horizontal, this);
|
||||
// Central widget: the existing raw/table/tag/video splitter on top,
|
||||
// plus the send bar (Pause button + input line) spanning the full
|
||||
// width underneath - below the log AND the HDMI preview, not tucked
|
||||
// into just one side.
|
||||
auto *central = new QWidget(this);
|
||||
auto *centralLayout = new QVBoxLayout(central);
|
||||
centralLayout->setContentsMargins(0, 0, 0, 0);
|
||||
centralLayout->setSpacing(0);
|
||||
|
||||
m_mainSplitter = new QSplitter(Qt::Horizontal, central);
|
||||
m_mainSplitter->setHandleWidth(5);
|
||||
auto *mainSplitter = m_mainSplitter;
|
||||
|
||||
@@ -137,7 +159,12 @@ void MainWindow::setupUi()
|
||||
mainSplitter->setStretchFactor(0, 3);
|
||||
mainSplitter->setStretchFactor(1, 1);
|
||||
|
||||
setCentralWidget(mainSplitter);
|
||||
centralLayout->addWidget(mainSplitter, 1);
|
||||
|
||||
m_sendBar = new SendBar(central);
|
||||
centralLayout->addWidget(m_sendBar);
|
||||
|
||||
setCentralWidget(central);
|
||||
}
|
||||
|
||||
void MainWindow::setupToolBar()
|
||||
@@ -298,6 +325,8 @@ void MainWindow::onPortOpened()
|
||||
.arg(m_lastConfig.baudRate));
|
||||
}
|
||||
statusBar()->clearMessage();
|
||||
if (m_sendBar)
|
||||
m_sendBar->setConnected(true);
|
||||
}
|
||||
|
||||
void MainWindow::onPortClosed()
|
||||
@@ -307,6 +336,8 @@ void MainWindow::onPortClosed()
|
||||
m_stateLabel->setText(tr("○ Disconnected"));
|
||||
m_stateLabel->setStyleSheet(QString());
|
||||
m_portLabel->setText(QString());
|
||||
if (m_sendBar)
|
||||
m_sendBar->setConnected(false);
|
||||
}
|
||||
|
||||
void MainWindow::onReconnecting(int attempt)
|
||||
@@ -381,6 +412,13 @@ tag monitor, and the video preview frame):
|
||||
|
||||
uart_printf("\033[2J\033[H"); // standard VT100 clear-screen + cursor home
|
||||
|
||||
Alternative: the [UARTSCOPECLEAR] control tag does the exact same full
|
||||
reset (raw output, table, tag monitor, video frame). No value needed -
|
||||
handy to fire once right at boot so every run starts from an absolutely
|
||||
clean UARTScope state, regardless of whatever the previous run left behind:
|
||||
|
||||
uart_printf("[UARTSCOPECLEAR]\n");
|
||||
|
||||
────────────────────────────────────────────────────────────
|
||||
2. TAG MONITOR (side panel, one widget per tag)
|
||||
────────────────────────────────────────────────────────────
|
||||
@@ -467,7 +505,26 @@ Notes:
|
||||
log of every capture in addition to the saved image files.
|
||||
|
||||
────────────────────────────────────────────────────────────
|
||||
6. COMPLETE EXAMPLE (Chica / Amiga hardware emulation)
|
||||
6. SENDING DATA BACK TO THE FIRMWARE (debugging)
|
||||
────────────────────────────────────────────────────────────
|
||||
UARTScope is no longer read-only. Below the Raw/Table view and the HDMI
|
||||
preview there is a send bar:
|
||||
|
||||
• A "Pause" button sends a fixed, user-configurable byte sequence every
|
||||
time it's clicked (right-click the button in UARTScope to change the
|
||||
label and the code - escape sequences like \n \r \t \xHH are
|
||||
supported, e.g. a single control byte to pause/step your firmware).
|
||||
• An input line sends whatever is typed the instant Enter is pressed
|
||||
(or the "Senden" button is clicked), with a selectable line ending
|
||||
(none / LF / CRLF / CR) appended.
|
||||
|
||||
Both are only active while a port/connection is open. On the firmware
|
||||
side this is just ordinary incoming UART data - read it the same way you
|
||||
would read from any other serial console (e.g. a blocking or interrupt-
|
||||
driven UART receive), there's no special protocol UARTScope expects here.
|
||||
|
||||
────────────────────────────────────────────────────────────
|
||||
7. COMPLETE EXAMPLE (Chica / Amiga hardware emulation)
|
||||
────────────────────────────────────────────────────────────
|
||||
void uart_status_update(void) {
|
||||
uart_printf("[WDG] uptime=%lu free=%lu load=%d temp=%d\n",
|
||||
@@ -493,6 +550,16 @@ void trigger_memdump_capture(void) {
|
||||
|
||||
// Clear screen from firmware when you want a fresh start:
|
||||
// uart_printf("\033[2J\033[H");
|
||||
// or, equivalently:
|
||||
// uart_printf("[UARTSCOPECLEAR]\n");
|
||||
|
||||
// Fire once right at boot, before anything else is printed, so every run
|
||||
// starts from a completely clean UARTScope - log, table, tag monitor and
|
||||
// video preview all wiped, no leftovers from a previous run/crash:
|
||||
void uart_init(void) {
|
||||
hal_uart_setup(115200);
|
||||
uart_printf("[UARTSCOPECLEAR]\n");
|
||||
}
|
||||
)UARTSCOPE";
|
||||
|
||||
auto *dlg = new QDialog(this);
|
||||
@@ -720,6 +787,12 @@ void MainWindow::saveSettings()
|
||||
|
||||
s.setValue("screenshot/autoDir", m_screenshotDir);
|
||||
|
||||
if (m_sendBar) {
|
||||
s.setValue("sendbar/buttonLabel", m_sendBar->buttonLabel());
|
||||
s.setValue("sendbar/buttonCode", m_sendBar->buttonCode());
|
||||
s.setValue("sendbar/lineEnding", m_sendBar->lineEndingIndex());
|
||||
}
|
||||
|
||||
s.sync(); // flush to disk immediately (important since we also call
|
||||
// this mid-session from configureTagFilter())
|
||||
}
|
||||
@@ -753,4 +826,13 @@ void MainWindow::restoreSettings()
|
||||
if (m_videoWidget)
|
||||
m_videoWidget->setAutoScreenshotDir(m_screenshotDir);
|
||||
updateScreenshotDirTooltip();
|
||||
|
||||
if (m_sendBar) {
|
||||
if (s.contains("sendbar/buttonLabel"))
|
||||
m_sendBar->setButtonLabel(s.value("sendbar/buttonLabel").toString());
|
||||
if (s.contains("sendbar/buttonCode"))
|
||||
m_sendBar->setButtonCode(s.value("sendbar/buttonCode").toString());
|
||||
if (s.contains("sendbar/lineEnding"))
|
||||
m_sendBar->setLineEndingIndex(s.value("sendbar/lineEnding").toInt());
|
||||
}
|
||||
}
|
||||
|
||||
223
src/sendbar.cpp
Normal file
223
src/sendbar.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#include "sendbar.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QFont>
|
||||
|
||||
SendBar::SendBar(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUi();
|
||||
}
|
||||
|
||||
void SendBar::setupUi()
|
||||
{
|
||||
auto *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(6, 4, 6, 4);
|
||||
|
||||
m_pauseBtn = new QPushButton(m_buttonLabel, this);
|
||||
m_pauseBtn->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_pauseBtn, &QPushButton::clicked, this, &SendBar::onPauseClicked);
|
||||
connect(m_pauseBtn, &QPushButton::customContextMenuRequested,
|
||||
this, &SendBar::onPauseContextMenu);
|
||||
layout->addWidget(m_pauseBtn);
|
||||
|
||||
m_inputEdit = new QLineEdit(this);
|
||||
m_inputEdit->setPlaceholderText(
|
||||
tr("Text eingeben und Enter drücken, um über UART zu senden…"));
|
||||
connect(m_inputEdit, &QLineEdit::returnPressed, this, &SendBar::sendInputLine);
|
||||
layout->addWidget(m_inputEdit, 1);
|
||||
|
||||
m_sendBtn = new QPushButton(tr("Senden"), this);
|
||||
connect(m_sendBtn, &QPushButton::clicked, this, &SendBar::onSendClicked);
|
||||
layout->addWidget(m_sendBtn);
|
||||
|
||||
m_endingCombo = new QComboBox(this);
|
||||
m_endingCombo->addItem(tr("kein Zeilenende"));
|
||||
m_endingCombo->addItem(tr("\\n (LF)"));
|
||||
m_endingCombo->addItem(tr("\\r\\n (CRLF)"));
|
||||
m_endingCombo->addItem(tr("\\r (CR)"));
|
||||
m_endingCombo->setCurrentIndex(1); // \n ist der übliche Default für UART-Konsolen
|
||||
m_endingCombo->setToolTip(tr("Zeilenende, das an den über die Eingabezeile\ngesendeten Text angehängt wird"));
|
||||
connect(m_endingCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &SendBar::configChanged);
|
||||
layout->addWidget(m_endingCombo);
|
||||
|
||||
updatePauseButtonUi();
|
||||
|
||||
// Solange nichts verbunden ist, gibt es nichts zu senden.
|
||||
setConnected(false);
|
||||
}
|
||||
|
||||
void SendBar::updatePauseButtonUi()
|
||||
{
|
||||
if (!m_pauseBtn)
|
||||
return;
|
||||
m_pauseBtn->setText(m_buttonLabel);
|
||||
m_pauseBtn->setToolTip(
|
||||
tr("Sendet über UART: %1\n(Rechtsklick zum Ändern)").arg(m_buttonCode));
|
||||
}
|
||||
|
||||
// ── public accessors ────────────────────────────────────────────────────────
|
||||
|
||||
int SendBar::lineEndingIndex() const
|
||||
{
|
||||
return m_endingCombo ? m_endingCombo->currentIndex() : 1;
|
||||
}
|
||||
|
||||
void SendBar::setButtonLabel(const QString &label)
|
||||
{
|
||||
m_buttonLabel = label.isEmpty() ? tr("Pause") : label;
|
||||
updatePauseButtonUi();
|
||||
}
|
||||
|
||||
void SendBar::setButtonCode(const QString &code)
|
||||
{
|
||||
m_buttonCode = code;
|
||||
updatePauseButtonUi();
|
||||
}
|
||||
|
||||
void SendBar::setLineEndingIndex(int index)
|
||||
{
|
||||
if (m_endingCombo && index >= 0 && index < m_endingCombo->count())
|
||||
m_endingCombo->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
// ── slots ────────────────────────────────────────────────────────────────
|
||||
|
||||
void SendBar::setConnected(bool connected)
|
||||
{
|
||||
if (m_pauseBtn) m_pauseBtn->setEnabled(connected);
|
||||
if (m_inputEdit) m_inputEdit->setEnabled(connected);
|
||||
if (m_sendBtn) m_sendBtn->setEnabled(connected);
|
||||
// m_endingCombo bleibt immer bedienbar - reine Konfiguration.
|
||||
}
|
||||
|
||||
void SendBar::onPauseClicked()
|
||||
{
|
||||
emit sendRequested(unescape(m_buttonCode));
|
||||
}
|
||||
|
||||
void SendBar::onPauseContextMenu(const QPoint &pos)
|
||||
{
|
||||
QMenu menu(this);
|
||||
QAction *cfgAction = menu.addAction(tr("Code konfigurieren…"));
|
||||
connect(cfgAction, &QAction::triggered, this, &SendBar::configureButton);
|
||||
menu.exec(m_pauseBtn->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SendBar::onSendClicked()
|
||||
{
|
||||
sendInputLine();
|
||||
}
|
||||
|
||||
void SendBar::sendInputLine()
|
||||
{
|
||||
const QString text = m_inputEdit->text();
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
QByteArray data = text.toUtf8();
|
||||
switch (m_endingCombo->currentIndex()) {
|
||||
case 1: data += "\n"; break;
|
||||
case 2: data += "\r\n"; break;
|
||||
case 3: data += "\r"; break;
|
||||
default: break; // 0 = kein Zeilenende
|
||||
}
|
||||
|
||||
emit sendRequested(data);
|
||||
m_inputEdit->clear();
|
||||
}
|
||||
|
||||
void SendBar::configureButton()
|
||||
{
|
||||
auto *dlg = new QDialog(this);
|
||||
dlg->setWindowTitle(tr("Button konfigurieren"));
|
||||
dlg->setMinimumWidth(380);
|
||||
|
||||
auto *layout = new QVBoxLayout(dlg);
|
||||
|
||||
auto *infoLabel = new QLabel(
|
||||
tr("Der Code wird beim Klick als Rohbytes über UART gesendet.<br>"
|
||||
"Escape-Sequenzen werden unterstützt: <tt>\\n \\r \\t \\\\ \\xHH</tt>"), dlg);
|
||||
infoLabel->setWordWrap(true);
|
||||
layout->addWidget(infoLabel);
|
||||
|
||||
auto *form = new QFormLayout();
|
||||
auto *labelEdit = new QLineEdit(m_buttonLabel, dlg);
|
||||
auto *codeEdit = new QLineEdit(m_buttonCode, dlg);
|
||||
QFont mono("Monospace");
|
||||
mono.setStyleHint(QFont::Monospace);
|
||||
codeEdit->setFont(mono);
|
||||
form->addRow(tr("Beschriftung:"), labelEdit);
|
||||
form->addRow(tr("Code:"), codeEdit);
|
||||
layout->addLayout(form);
|
||||
|
||||
auto *btnBox = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dlg);
|
||||
connect(btnBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept);
|
||||
connect(btnBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject);
|
||||
layout->addWidget(btnBox);
|
||||
|
||||
if (dlg->exec() == QDialog::Accepted) {
|
||||
const QString newLabel = labelEdit->text().trimmed();
|
||||
m_buttonLabel = newLabel.isEmpty() ? tr("Pause") : newLabel;
|
||||
m_buttonCode = codeEdit->text();
|
||||
updatePauseButtonUi();
|
||||
emit configChanged();
|
||||
}
|
||||
dlg->deleteLater();
|
||||
}
|
||||
|
||||
// ── static helper ────────────────────────────────────────────────────────
|
||||
|
||||
QByteArray SendBar::unescape(const QString &text)
|
||||
{
|
||||
QByteArray out;
|
||||
out.reserve(text.size());
|
||||
|
||||
int i = 0;
|
||||
while (i < text.size()) {
|
||||
const QChar c = text.at(i);
|
||||
if (c == QLatin1Char('\\') && i + 1 < text.size()) {
|
||||
const QChar next = text.at(i + 1);
|
||||
bool handled = true;
|
||||
switch (next.toLatin1()) {
|
||||
case 'n': out.append('\n'); break;
|
||||
case 'r': out.append('\r'); break;
|
||||
case 't': out.append('\t'); break;
|
||||
case '0': out.append('\0'); break;
|
||||
case '\\': out.append('\\'); break;
|
||||
case 'x':
|
||||
if (i + 3 < text.size()) {
|
||||
bool ok = false;
|
||||
const int val = text.mid(i + 2, 2).toInt(&ok, 16);
|
||||
if (ok) {
|
||||
out.append(static_cast<char>(val));
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
handled = false;
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
if (handled) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Kein erkanntes Escape (oder ein einzelnes '\' am Ende): das
|
||||
// Zeichen unverändert als UTF-8 übernehmen.
|
||||
out += QString(c).toUtf8();
|
||||
++i;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ void SerialWorker::openPort(const QString &portName, qint32 baudRate,
|
||||
m_port->setStopBits(stopBits);
|
||||
m_port->setFlowControl(flowControl);
|
||||
|
||||
if (!m_port->open(QIODevice::ReadOnly)) {
|
||||
if (!m_port->open(QIODevice::ReadWrite)) {
|
||||
emit errorOccurred(tr("Cannot open %1: %2").arg(portName, m_port->errorString()));
|
||||
if (m_autoReconnect)
|
||||
scheduleReconnect();
|
||||
@@ -164,6 +164,13 @@ void SerialWorker::stopLogging()
|
||||
}
|
||||
}
|
||||
|
||||
void SerialWorker::writeData(const QByteArray &data)
|
||||
{
|
||||
if (!m_device || data.isEmpty())
|
||||
return;
|
||||
m_device->write(data);
|
||||
}
|
||||
|
||||
// ── private slots ───────────────────────────────────────────────────────────
|
||||
|
||||
void SerialWorker::onReadyRead()
|
||||
@@ -256,7 +263,7 @@ void SerialWorker::tryReconnect()
|
||||
m_port->setStopBits(m_stopBits);
|
||||
m_port->setFlowControl(m_flowControl);
|
||||
|
||||
if (m_port->open(QIODevice::ReadOnly)) {
|
||||
if (m_port->open(QIODevice::ReadWrite)) {
|
||||
m_device = m_port;
|
||||
m_idleFlushTimer->stop();
|
||||
m_buffer.clear();
|
||||
@@ -410,6 +417,13 @@ void SerialWorker::processLine(const QString &line)
|
||||
// reaction time with the manual screenshot button.
|
||||
if (tag == QLatin1String("SCREENSHOT"))
|
||||
emit screenshotRequested(value);
|
||||
|
||||
// [UARTSCOPECLEAR] is a control tag (no value expected): fire
|
||||
// immediately so a baremetal project can guarantee a completely
|
||||
// fresh UARTScope state right as it boots, regardless of whatever
|
||||
// was left over from a previous run/crash.
|
||||
if (tag == QLatin1String("UARTSCOPECLEAR"))
|
||||
emit uartscopeClearRequested();
|
||||
}
|
||||
|
||||
emit newLine(line);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: diabolus <your@email.com>
|
||||
pkgname=uartscope
|
||||
pkgver=1.1.0.r0.g36923e1
|
||||
pkgver=1.1.0.r1.gef335f2
|
||||
pkgrel=1
|
||||
pkgdesc="Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect"
|
||||
arch=('x86_64' 'aarch64')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,12 +3,12 @@
|
||||
pkgname = uartscope
|
||||
pkgbase = uartscope
|
||||
xdata = pkgtype=pkg
|
||||
pkgver = 1.1.0.r0.g36923e1-1
|
||||
pkgver = 1.1.0.r1.gef335f2-1
|
||||
pkgdesc = Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect
|
||||
url = https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||
builddate = 1787031398
|
||||
builddate = 1787701264
|
||||
packager = Unknown Packager
|
||||
size = 422641
|
||||
size = 434929
|
||||
arch = x86_64
|
||||
license = MIT
|
||||
conflict = uartscope
|
||||
|
||||
Binary file not shown.
Submodule uartscope-git/src/uartscope updated: 36923e1646...ef335f2d9e
@@ -1,3 +1,3 @@
|
||||
36923e16461bc15086c84187790dbcb5f0a5030f not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||
ef335f2d9eaa98572ab305e5830a0372474163e8 not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||
cc102c93eb17f7b910d8e74c3505f198bed77f10 not-for-merge tag 'v1.0.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||
36923e16461bc15086c84187790dbcb5f0a5030f not-for-merge tag 'v1.1.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||
|
||||
Reference in New Issue
Block a user