From 3290b64a0b765a70d82039755770f77735c1326d Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Wed, 14 Sep 2022 17:07:23 -0700 Subject: [PATCH] Added logging window capability with termbin support. --- logcategories.cpp | 1 + logcategories.h | 1 + loggingwindow.cpp | 152 ++++++++++++++++++++++++++++++++++++++++ loggingwindow.h | 59 ++++++++++++++++ loggingwindow.ui | 172 ++++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 21 ++++-- wfmain.cpp | 122 +++++++++++++++++++++++++++++++- wfmain.h | 16 ++++- wfmain.ui | 9 ++- wfview.pro | 3 + 10 files changed, 547 insertions(+), 9 deletions(-) create mode 100644 loggingwindow.cpp create mode 100644 loggingwindow.h create mode 100644 loggingwindow.ui diff --git a/logcategories.cpp b/logcategories.cpp index da4c6d7..3b9e0cd 100644 --- a/logcategories.cpp +++ b/logcategories.cpp @@ -3,6 +3,7 @@ Q_LOGGING_CATEGORY(logSystem, "system") Q_LOGGING_CATEGORY(logSerial, "serial") Q_LOGGING_CATEGORY(logGui, "gui") +Q_LOGGING_CATEGORY(logUser, "user") Q_LOGGING_CATEGORY(logRig, "rig") Q_LOGGING_CATEGORY(logAudio, "audio") Q_LOGGING_CATEGORY(logUdp, "udp") diff --git a/logcategories.h b/logcategories.h index 987278d..82cca61 100644 --- a/logcategories.h +++ b/logcategories.h @@ -6,6 +6,7 @@ Q_DECLARE_LOGGING_CATEGORY(logSystem) Q_DECLARE_LOGGING_CATEGORY(logSerial) Q_DECLARE_LOGGING_CATEGORY(logGui) +Q_DECLARE_LOGGING_CATEGORY(logUser) Q_DECLARE_LOGGING_CATEGORY(logRig) Q_DECLARE_LOGGING_CATEGORY(logAudio) Q_DECLARE_LOGGING_CATEGORY(logUdp) diff --git a/loggingwindow.cpp b/loggingwindow.cpp new file mode 100644 index 0000000..9903d1b --- /dev/null +++ b/loggingwindow.cpp @@ -0,0 +1,152 @@ +#include "loggingwindow.h" +#include "ui_loggingwindow.h" + +loggingWindow::loggingWindow(QWidget *parent) : + QWidget(parent), + ui(new Ui::loggingWindow) +{ + ui->setupUi(this); + ui->logTextDisplay->setReadOnly(true); + ui->userAnnotationText->setFocus(); + ui->annotateBtn->setDefault(true); + ui->logTextDisplay->setFocusPolicy(Qt::NoFocus); + ui->annotateBtn->setFocusPolicy(Qt::NoFocus); + +#ifdef Q_OS_MAC + logFilename = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log"; + logDirectory = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0]; + +#else + logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log"; + logDirectory = QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0]; +#endif + clipboard = QApplication::clipboard(); + socket = new QTcpSocket(this); + connect(socket, SIGNAL(connected()), this, SLOT(connectedToHost())); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectedFromHost())); + connect(socket, SIGNAL(readyRead()), this, SLOT(handleDataFromLoggingHost())); + connect(socket, SIGNAL(hostFound()), this, SLOT(handleLoggingHostError())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(handleLoggingHostError(QAbstractSocket::SocketError))); +} + +loggingWindow::~loggingWindow() +{ + QMutexLocker lock(&textMutex); + delete ui; +} + +void loggingWindow::acceptLogText(QString text) +{ + QMutexLocker lock(&textMutex); + ui->logTextDisplay->appendPlainText(text); +} + +void loggingWindow::sendToTermbin() +{ + qInfo(logGui()) << "Sending data to termbin.com. Standby."; + socket->connectToHost("termbin.com", 9999); + ui->sendToPasteBtn->setDisabled(true); +} + +void loggingWindow::handleDataFromLoggingHost() +{ + qInfo(logGui()) << "Receiving data from logging host."; + QString URL; + QByteArray data = socket->readAll(); + if(data.length() < 256) + { + URL = QString(data).trimmed(); + if(!URL.isEmpty()) + { + clipboard->setText(URL); + qInfo(logGui()) << "Sent log to URL: " << URL; + msgBox.setText("Your log has been posted, and the URL has been copied to the clipboard."); + msgBox.setInformativeText(URL); + msgBox.exec(); + } + } else { + qDebug(logGui()) << "Error, return from logging host too large. Received " << data.length() << " bytes."; + } +} + +void loggingWindow::disconnectedFromHost() +{ + qInfo(logGui()) << "Disconnected from logging host"; + ui->sendToPasteBtn->setDisabled(false); +} + +void loggingWindow::connectedToHost() +{ + qInfo(logGui()) << "Connected to logging host"; + QMutexLocker lock(&textMutex); + QTextStream outText(socket); + outText << ui->logTextDisplay->toPlainText(); + outText << "\n----------\nSent from wfview version "; + outText << WFVIEW_VERSION << "\n----------\n"; +} + +void loggingWindow::handleLoggingHostError(QAbstractSocket::SocketError error) +{ + qInfo(logGui()) << "Error connecting to logging host. Check internet connection. Error code: " << error; +} + +void loggingWindow::on_clearDisplayBtn_clicked() +{ + QMutexLocker lock(&textMutex); + // Only clears the displayed text, not the log file. + ui->logTextDisplay->clear(); +} + +void loggingWindow::on_openDirBtn_clicked() +{ + QString cmd; +#ifdef Q_OS_MAC + cmd = "open " + logDirectory; +#endif +#ifdef Q_OS_LINUX + cmd = "xdg-open " + logDirectory; +#endif +#ifdef Q_OS_WIN + cmd = "start " + logDirectory; +#endif + system(cmd.toLocal8Bit().data()); +} + +void loggingWindow::on_openLogFileBtn_clicked() +{ + QString cmd; +#ifdef Q_OS_MAC + cmd = "open " + logFilename; +#endif +#ifdef Q_OS_LINUX + cmd = "xdg-open " + logFilename; +#endif +#ifdef Q_OS_WIN + cmd = "start " + logFilename; +#endif + system(cmd.toLocal8Bit().data()); +} + +void loggingWindow::on_sendToPasteBtn_clicked() +{ + sendToTermbin(); +} + +void loggingWindow::on_annotateBtn_clicked() +{ + QMutexLocker lock(&textMutex); + if(ui->userAnnotationText->text().isEmpty()) + return; + qInfo(logUser()) << ui->userAnnotationText->text(); + ui->userAnnotationText->clear(); +} + +void loggingWindow::on_userAnnotationText_returnPressed() +{ + on_annotateBtn_clicked(); +} + +void loggingWindow::on_copyPathBtn_clicked() +{ + clipboard->setText(logFilename); +} diff --git a/loggingwindow.h b/loggingwindow.h new file mode 100644 index 0000000..f75044c --- /dev/null +++ b/loggingwindow.h @@ -0,0 +1,59 @@ +#ifndef LOGGINGWINDOW_H +#define LOGGINGWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logcategories.h" + +namespace Ui { +class loggingWindow; +} + +class loggingWindow : public QWidget +{ + Q_OBJECT + +public: + explicit loggingWindow(QWidget *parent = nullptr); + ~loggingWindow(); + void acceptLogText(QString text); + +private slots: + void connectedToHost(); + void disconnectedFromHost(); + void handleDataFromLoggingHost(); + void handleLoggingHostError(QAbstractSocket::SocketError); + + void on_clearDisplayBtn_clicked(); + + void on_openDirBtn_clicked(); + + void on_openLogFileBtn_clicked(); + + void on_sendToPasteBtn_clicked(); + + void on_annotateBtn_clicked(); + + void on_userAnnotationText_returnPressed(); + + void on_copyPathBtn_clicked(); + +private: + Ui::loggingWindow *ui; + QClipboard *clipboard; + QMessageBox msgBox; + QMutex textMutex; + QString logFilename; + QString logDirectory; + QTcpSocket *socket; + void sendToTermbin(); +}; + +#endif // LOGGINGWINDOW_H diff --git a/loggingwindow.ui b/loggingwindow.ui new file mode 100644 index 0000000..81c7d62 --- /dev/null +++ b/loggingwindow.ui @@ -0,0 +1,172 @@ + + + loggingWindow + + + + 0 + 0 + 554 + 300 + + + + Form + + + + + + + Ubuntu Mono + + + + true + + + + + + + 0 + + + 0 + + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Annotation: + + + + + + + + 290 + 0 + + + + + Ubuntu Mono + + + + + + + + + 85 + 0 + + + + + 85 + 16777215 + + + + Adds user-text to the log. + + + Annotate + + + + + + + + + 0 + + + 0 + + + + + Clears the display. Does not clear the log file. + + + Clear Display + + + + + + + Makes a best-effort to ask the host system to open the log file directory. + + + Open Log Directory + + + + + + + Makes a best-effort to ask the host system to open the logfile. + + + Open Log + + + + + + + Copy the path of the log file to your clipboard. + + + Copy Path + + + + + + + <html><head/><body><p>Sends text to termbin.com. Some personal information (such as your username) is in the log file, so do not click this button unless you are ok sharing your log file. This is a quick way to receive a URL, pointing to your log file text, that you can send to other people. </p></body></html> + + + Send to Termbin.com + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/main.cpp b/main.cpp index 8e945ae..d74e959 100644 --- a/main.cpp +++ b/main.cpp @@ -12,13 +12,16 @@ #include #include "wfmain.h" + +// Copyright 2017-2022 Elliott H. Liggett #include "logcategories.h" -// Copyright 2017-2021 Elliott H. Liggett - +#ifdef BUILD_WFSERVER // Smart pointer to log file QScopedPointer m_logFile; QMutex logMutex; +#endif + bool debugMode=false; #ifdef BUILD_WFSERVER @@ -42,9 +45,9 @@ bool debugMode=false; #endif } -#endif void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); +#endif int main(int argc, char *argv[]) { @@ -65,7 +68,7 @@ int main(int argc, char *argv[]) #endif #ifdef QT_DEBUG - debugMode = true; + //debugMode = true; #endif QString serialPortCL; @@ -160,12 +163,15 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER + // Set the logging file before doing anything else. m_logFile.reset(new QFile(logFilename)); // Open the file logging m_logFile.data()->open(QFile::WriteOnly | QFile::Truncate | QFile::Text); // Set handler qInstallMessageHandler(messageHandler); +#endif qInfo(logSystem()) << version; qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL); @@ -183,7 +189,7 @@ int main(int argc, char *argv[]) w = new servermain(serialPortCL, hostCL, settingsFile); #else a.setWheelScrollLines(1); // one line per wheel click - wfmain w(serialPortCL, hostCL, settingsFile); + wfmain w(serialPortCL, hostCL, settingsFile, debugMode); w.show(); #endif @@ -191,6 +197,7 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -201,6 +208,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } QMutexLocker locker(&logMutex); QTextStream out(m_logFile.data()); + QString text; // Write the date of recording out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); @@ -229,5 +237,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt #ifdef BUILD_WFSERVER std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ").toLocal8Bit().toStdString() << msg.toLocal8Bit().toStdString() << "\n"; #endif + text = out.readAll(); out.flush(); // Clear the buffered data + //mainwindow.handleLogText(test); } +#endif diff --git a/wfmain.cpp b/wfmain.cpp index b7bcb69..0f62b6e 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -5,10 +5,22 @@ #include "rigidentities.h" #include "logcategories.h" -// This code is copyright 2017-2020 Elliott H. Liggett +// This code is copyright 2017-2022 Elliott H. Liggett // All rights reserved -wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, QWidget *parent ) : +// Log support: +//static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); +QScopedPointer m_logFile; +QMutex logMutex; +QMutex logTextMutex; +QVector logStringBuffer; +#ifdef QT_DEBUG +bool debugModeLogging = true; +#else +bool debugModeLogging = false; +#endif + +wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, bool debugMode, QWidget *parent ) : QMainWindow(parent), ui(new Ui::wfmain) { @@ -16,10 +28,14 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s QGuiApplication::setApplicationName(QString("wfview")); setWindowIcon(QIcon( QString(":resources/wfview.png"))); + this->debugMode = debugMode; + debugModeLogging = debugMode; ui->setupUi(this); setWindowTitle(QString("wfview")); + initLogging(); + this->serialPortCL = serialPortCL; this->hostCL = hostCL; @@ -29,6 +45,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s trxadj = new transceiverAdjustments(); abtBox = new aboutbox(); selRad = new selectRadio(); + logWindow = new loggingWindow(); qRegisterMetaType(); // Needs to be registered early. qRegisterMetaType(); @@ -6932,3 +6949,104 @@ void wfmain::on_colorSavePresetBtn_clicked() settings->endGroup(); settings->sync(); } + +void wfmain::on_showLogBtn_clicked() +{ + logWindow->show(); +} + +void wfmain::initLogging() +{ +#ifdef Q_OS_MAC + logFilename= QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log"; +#else + logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log"; +#endif + // Set the logging file before doing anything else. + m_logFile.reset(new QFile(logFilename)); + // Open the file logging + m_logFile.data()->open(QFile::WriteOnly | QFile::Truncate | QFile::Text); + // Set handler + qInstallMessageHandler(messageHandler); + + logCheckingTimer.setInterval(50); + connect(&logCheckingTimer, SIGNAL(timeout()), this, SLOT(logCheck())); + logCheckingTimer.start(); +} + +void wfmain::logCheck() +{ + // This is called by a timer to check for new log messages and copy + // the messages into the logWindow. + QMutexLocker locker(&logTextMutex); + int size = logStringBuffer.size(); + for(int i=0; i < size; i++) + { + handleLogText(logStringBuffer.back()); + logStringBuffer.pop_back(); + } +} + +void wfmain::handleLogText(QString text) +{ + // This function is just a pass-through + logWindow->acceptLogText(text); +} + +void wfmain::messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + // Open stream file writes + + // TODO: Fix this parameter: + + if (type == QtDebugMsg && !debugModeLogging) + { + return; + } + + QMutexLocker locker(&logMutex); + QTextStream out(m_logFile.data()); + QString text; + + // Write the date of recording + out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); + text.append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ")); + // By type determine to what level belongs message + + switch (type) + { + case QtDebugMsg: + out << "DBG "; + text.append("DBG "); + break; + case QtInfoMsg: + out << "INF "; + text.append("INF "); + break; + case QtWarningMsg: + out << "WRN "; + text.append("WRN "); + break; + case QtCriticalMsg: + out << "CRT "; + text.append("CRT "); + break; + case QtFatalMsg: + out << "FTL "; + text.append("FLT "); + break; + } + // Write to the output category of the message and the message itself + out << context.category << ": " << msg << "\n"; + out.flush(); // Clear the buffered data + + text.append(context.category); + text.append(": "); + text.append(msg); + logTextMutex.lock(); + logStringBuffer.push_back(text); + logTextMutex.unlock(); + //logStringBuffer[logStringBufferPosition%logStringBufferSize] = text; + //logStringBufferPosition++; +} + diff --git a/wfmain.h b/wfmain.h index a8120a9..88a806a 100644 --- a/wfmain.h +++ b/wfmain.h @@ -38,6 +38,7 @@ #include "aboutbox.h" #include "selectradio.h" #include "colorprefs.h" +#include "loggingwindow.h" #include #include @@ -63,10 +64,12 @@ class wfmain : public QMainWindow Q_OBJECT public: - explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, QWidget *parent = 0); + explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, bool debugMode, QWidget *parent = 0); QString serialPortCL; QString hostCL; ~wfmain(); + static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + void handleLogText(QString text); signals: // Basic to rig: @@ -292,6 +295,7 @@ private slots: void radioSelection(QList radios); void setRadioTimeDateSend(); + void logCheck(); // void on_getFreqBtn_clicked(); @@ -636,9 +640,12 @@ private slots: void on_colorSavePresetBtn_clicked(); + void on_showLogBtn_clicked(); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); + bool debugMode; QSettings *settings=Q_NULLPTR; void loadSettings(); void saveSettings(); @@ -646,6 +653,11 @@ private: void createSettingsListItems(); void connectSettingsList(); + void initLogging(); + QTimer logCheckingTimer; + int logCheckingOldPosition = 0; + QString logFilename; + QCustomPlot *plot; // line plot QCustomPlot *wf; // waterfall image QCPItemLine * freqIndicatorLine; @@ -976,6 +988,7 @@ private: transceiverAdjustments *trxadj; aboutbox *abtBox; selectRadio *selRad; + loggingWindow *logWindow; udpServer* udp = Q_NULLPTR; rigCtlD* rigCtl = Q_NULLPTR; @@ -1027,6 +1040,7 @@ Q_DECLARE_METATYPE(enum spectrumMode) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(rigstate*) +//void (*wfmain::logthistext)(QString text) = NULL; #endif // WFMAIN_H #endif diff --git a/wfmain.ui b/wfmain.ui index 7d6ac6f..234d3b5 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -3262,7 +3262,7 @@ 0 0 - 767 + 204 582 @@ -4609,6 +4609,13 @@ + + + + Log + + + diff --git a/wfview.pro b/wfview.pro index e43a11c..797e98f 100644 --- a/wfview.pro +++ b/wfview.pro @@ -147,6 +147,7 @@ win32:INCLUDEPATH += ../r8brain-free-src INCLUDEPATH += resampler SOURCES += main.cpp\ + loggingwindow.cpp \ wfmain.cpp \ commhandler.cpp \ rigcommander.cpp \ @@ -178,6 +179,7 @@ SOURCES += main.cpp\ HEADERS += wfmain.h \ colorprefs.h \ commhandler.h \ + loggingwindow.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ @@ -213,6 +215,7 @@ HEADERS += wfmain.h \ FORMS += wfmain.ui \ calibrationwindow.ui \ + loggingwindow.ui \ satellitesetup.ui \ selectradio.ui \ repeatersetup.ui \