mirror of
https://github.com/Serial-Studio/Serial-Studio.git
synced 2025-01-23 11:32:53 +08:00
Add option to export real-time console data (@AnnabellePundaky)
This commit is contained in:
parent
2c4bf6c68b
commit
e05af1d830
@ -54,7 +54,6 @@ find_package(
|
||||
Bluetooth
|
||||
SerialPort
|
||||
Positioning
|
||||
PrintSupport
|
||||
LinguistTools
|
||||
QuickControls2
|
||||
)
|
||||
@ -98,6 +97,7 @@ set(SOURCES
|
||||
src/IO/Checksum.cpp
|
||||
src/IO/Console.cpp
|
||||
src/IO/Manager.cpp
|
||||
src/IO/ConsoleExport.cpp
|
||||
src/IO/FileTransmission.cpp
|
||||
src/IO/FrameReader.cpp
|
||||
src/JSON/FrameParser.cpp
|
||||
@ -145,6 +145,7 @@ set(HEADERS
|
||||
src/IO/Manager.h
|
||||
src/IO/HAL_Driver.h
|
||||
src/IO/Checksum.h
|
||||
src/IO/ConsoleExport.h
|
||||
src/IO/CircularBuffer.h
|
||||
src/IO/FileTransmission.h
|
||||
src/IO/FrameReader.h
|
||||
@ -223,7 +224,6 @@ target_link_libraries(
|
||||
Qt6::Bluetooth
|
||||
Qt6::SerialPort
|
||||
Qt6::Positioning
|
||||
Qt6::PrintSupport
|
||||
Qt6::QuickControls2
|
||||
|
||||
simde
|
||||
|
@ -67,11 +67,11 @@ Widgets.Pane {
|
||||
Settings {
|
||||
category: "SetupPanel"
|
||||
|
||||
property alias tabIndex: tab.currentIndex
|
||||
property alias csvExport: csvLogging.checked
|
||||
property alias driver: driverCombo.currentIndex
|
||||
property alias language: settings.language
|
||||
property alias csvExport: csvLogging.checked
|
||||
property alias tcpPlugins: settings.tcpPlugins
|
||||
property alias consoleExport: consoleLogging.checked
|
||||
property alias selectedDriver: driverCombo.currentIndex
|
||||
}
|
||||
|
||||
//
|
||||
@ -122,7 +122,7 @@ Widgets.Pane {
|
||||
}
|
||||
|
||||
//
|
||||
// Device type selector
|
||||
// Driver selection
|
||||
//
|
||||
Label {
|
||||
text: qsTr("Device Setup") + ":"
|
||||
@ -140,23 +140,6 @@ Widgets.Pane {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CSV generator
|
||||
//
|
||||
Switch {
|
||||
id: csvLogging
|
||||
Layout.leftMargin: -6
|
||||
text: qsTr("Create CSV File")
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
checked: Cpp_CSV_Export.exportEnabled
|
||||
palette.highlight: Cpp_ThemeManager.colors["csv_switch"]
|
||||
|
||||
onCheckedChanged: {
|
||||
if (Cpp_CSV_Export.exportEnabled !== checked)
|
||||
Cpp_CSV_Export.exportEnabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Spacer
|
||||
//
|
||||
@ -173,6 +156,7 @@ Widgets.Pane {
|
||||
color: Cpp_ThemeManager.colors["pane_section_label"]
|
||||
Component.onCompleted: font.capitalization = Font.AllUppercase
|
||||
} RadioButton {
|
||||
Layout.leftMargin: -6
|
||||
Layout.maximumHeight: 18
|
||||
Layout.maximumWidth: root.maxItemWidth
|
||||
text: qsTr("No Parsing (Device Sends JSON Data)")
|
||||
@ -183,6 +167,7 @@ Widgets.Pane {
|
||||
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.DeviceSendsJSON
|
||||
}
|
||||
} RadioButton {
|
||||
Layout.leftMargin: -6
|
||||
Layout.maximumHeight: 18
|
||||
Layout.maximumWidth: root.maxItemWidth
|
||||
text: qsTr("Quick Plot (Comma Separated Values)")
|
||||
@ -193,6 +178,7 @@ Widgets.Pane {
|
||||
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.QuickPlot
|
||||
}
|
||||
} RadioButton {
|
||||
Layout.leftMargin: -6
|
||||
Layout.maximumHeight: 18
|
||||
Layout.maximumWidth: root.maxItemWidth
|
||||
text: qsTr("Parse via JSON Project File")
|
||||
@ -225,6 +211,59 @@ Widgets.Pane {
|
||||
implicitHeight: 4
|
||||
}
|
||||
|
||||
//
|
||||
// Data export switches
|
||||
//
|
||||
Label {
|
||||
text: qsTr("Data Export") + ":"
|
||||
font: Cpp_Misc_CommonFonts.customUiFont(0.8, true)
|
||||
color: Cpp_ThemeManager.colors["pane_section_label"]
|
||||
Component.onCompleted: font.capitalization = Font.AllUppercase
|
||||
}
|
||||
|
||||
//
|
||||
// CSV generator
|
||||
//
|
||||
CheckBox {
|
||||
id: csvLogging
|
||||
Layout.leftMargin: -6
|
||||
Layout.maximumHeight: 18
|
||||
text: qsTr("Create CSV File")
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
checked: Cpp_CSV_Export.exportEnabled
|
||||
Layout.maximumWidth: root.maxItemWidth
|
||||
|
||||
onCheckedChanged: {
|
||||
if (Cpp_CSV_Export.exportEnabled !== checked)
|
||||
Cpp_CSV_Export.exportEnabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Console data export
|
||||
//
|
||||
CheckBox {
|
||||
id: consoleLogging
|
||||
Layout.leftMargin: -6
|
||||
Layout.maximumHeight: 18
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: qsTr("Export Console Data")
|
||||
Layout.maximumWidth: root.maxItemWidth
|
||||
checked: Cpp_IO_ConsoleExport.exportEnabled
|
||||
|
||||
onCheckedChanged: {
|
||||
if (Cpp_IO_ConsoleExport.exportEnabled !== checked)
|
||||
Cpp_IO_ConsoleExport.exportEnabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Spacer
|
||||
//
|
||||
Item {
|
||||
implicitHeight: 4
|
||||
}
|
||||
|
||||
//
|
||||
// Tab bar
|
||||
//
|
||||
|
@ -34,6 +34,7 @@ Item {
|
||||
//
|
||||
QtSettings.Settings {
|
||||
category: "DeviceSetup"
|
||||
|
||||
property alias serialDtr: serial.dtr
|
||||
property alias serialParity: serial.parity
|
||||
property alias serialBaudRate: serial.baudRate
|
||||
|
@ -152,23 +152,6 @@ Item {
|
||||
text: qsTr("Clear")
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onTriggered: root.clear()
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
}
|
||||
|
||||
MenuSeparator {}
|
||||
|
||||
MenuItem {
|
||||
opacity: enabled ? 1 : 0.5
|
||||
text: qsTr("Print")
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
onTriggered: Cpp_IO_Console.print()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
opacity: enabled ? 1 : 0.5
|
||||
text: qsTr("Save as") + "..."
|
||||
onTriggered: Cpp_IO_Console.save()
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,30 +404,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.width: 18
|
||||
icon.height: 18
|
||||
implicitHeight: 24
|
||||
Layout.maximumWidth: 32
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onClicked: Cpp_IO_Console.save()
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
icon.source: "qrc:/rcc/icons/buttons/save.svg"
|
||||
icon.color: Cpp_ThemeManager.colors["button_text"]
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.width: 18
|
||||
icon.height: 18
|
||||
implicitHeight: 24
|
||||
Layout.maximumWidth: 32
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onClicked: Cpp_IO_Console.print()
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
icon.source: "qrc:/rcc/icons/buttons/print.svg"
|
||||
icon.color: Cpp_ThemeManager.colors["button_text"]
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.width: 18
|
||||
icon.height: 18
|
||||
@ -452,7 +411,6 @@ Item {
|
||||
onClicked: root.clear()
|
||||
Layout.maximumWidth: 32
|
||||
opacity: enabled ? 1 : 0.5
|
||||
enabled: Cpp_IO_Console.saveAvailable
|
||||
icon.source: "qrc:/rcc/icons/buttons/clear.svg"
|
||||
icon.color: Cpp_ThemeManager.colors["button_text"]
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QStandardPaths>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "IO/Manager.h"
|
||||
#include "CSV/Player.h"
|
||||
@ -36,8 +36,8 @@
|
||||
#include "JSON/FrameBuilder.h"
|
||||
|
||||
/**
|
||||
* Connect JSON Parser & Serial Manager signals to begin registering JSON
|
||||
* dataframes into JSON list.
|
||||
* Constructor function, configures the path in which Serial Studio shall
|
||||
* automatically write generated CSV files.
|
||||
*/
|
||||
CSV::Export::Export()
|
||||
: m_exportEnabled(true)
|
||||
@ -49,7 +49,7 @@ CSV::Export::Export()
|
||||
}
|
||||
|
||||
/**
|
||||
* Close file & finnish write-operations before destroying the class
|
||||
* Close file & finnish write-operations before destroying the class.
|
||||
*/
|
||||
CSV::Export::~Export()
|
||||
{
|
||||
@ -57,7 +57,7 @@ CSV::Export::~Export()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the only instance of this class
|
||||
* Returns a pointer to the only instance of this class.
|
||||
*/
|
||||
CSV::Export &CSV::Export::instance()
|
||||
{
|
||||
@ -66,7 +66,7 @@ CSV::Export &CSV::Export::instance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if the CSV output file is open
|
||||
* Returns @c true if the CSV output file is open.
|
||||
*/
|
||||
bool CSV::Export::isOpen() const
|
||||
{
|
||||
@ -74,25 +74,13 @@ bool CSV::Export::isOpen() const
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if CSV export is enabled
|
||||
* Returns @c true if CSV export is enabled.
|
||||
*/
|
||||
bool CSV::Export::exportEnabled() const
|
||||
{
|
||||
return m_exportEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the current CSV file in the Explorer/Finder window
|
||||
*/
|
||||
void CSV::Export::openCurrentCsv()
|
||||
{
|
||||
if (isOpen())
|
||||
Misc::Utilities::revealFile(m_csvFile.fileName());
|
||||
else
|
||||
Misc::Utilities::showMessageBox(tr("CSV file not open"),
|
||||
tr("Cannot find CSV export file!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the signal/slot connections with the rest of the modules of the
|
||||
* application.
|
||||
@ -108,7 +96,7 @@ void CSV::Export::setupExternalConnections()
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables data export
|
||||
* Enables or disables data export.
|
||||
*/
|
||||
void CSV::Export::setExportEnabled(const bool enabled)
|
||||
{
|
||||
@ -124,7 +112,7 @@ void CSV::Export::setExportEnabled(const bool enabled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all remaining JSON frames & close the CSV file
|
||||
* Write all remaining JSON frames & close the CSV file.
|
||||
*/
|
||||
void CSV::Export::closeFile()
|
||||
{
|
||||
@ -238,8 +226,8 @@ CSV::Export::createCsvFile(const CSV::TimestampFrame &frame)
|
||||
const auto &rxTime = frame.rxDateTime;
|
||||
|
||||
// Get file name
|
||||
const auto fileName
|
||||
= rxTime.toString(QStringLiteral("yyyy_MMM_dd HH_mm_ss")) + ".csv";
|
||||
const auto fileName = rxTime.toString(QStringLiteral("yyyy_MMM_dd HH_mm_ss"))
|
||||
+ QStringLiteral(".csv");
|
||||
|
||||
// Get path
|
||||
const QString path = QStringLiteral("%1/%2/").arg(m_csvPath, data.title());
|
||||
@ -247,7 +235,7 @@ CSV::Export::createCsvFile(const CSV::TimestampFrame &frame)
|
||||
// Generate file path if required
|
||||
QDir dir(path);
|
||||
if (!dir.exists())
|
||||
dir.mkpath(".");
|
||||
dir.mkpath(QStringLiteral("."));
|
||||
|
||||
// Open file
|
||||
m_csvFile.setFileName(dir.filePath(fileName));
|
||||
@ -314,7 +302,7 @@ CSV::Export::createCsvFile(const CSV::TimestampFrame &frame)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the latest frame from the device to the output buffer
|
||||
* Appends the latest frame from the device to the output buffer.
|
||||
*/
|
||||
void CSV::Export::registerFrame(const JSON::Frame &frame)
|
||||
{
|
||||
|
@ -84,7 +84,6 @@ public:
|
||||
|
||||
public slots:
|
||||
void closeFile();
|
||||
void openCurrentCsv();
|
||||
void setupExternalConnections();
|
||||
void setExportEnabled(const bool enabled);
|
||||
|
||||
|
@ -20,17 +20,11 @@
|
||||
*/
|
||||
|
||||
#include <QFile>
|
||||
#include <QPrinter>
|
||||
#include <QDateTime>
|
||||
#include <QFileDialog>
|
||||
#include <QPrintDialog>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include "IO/Manager.h"
|
||||
#include "IO/Console.h"
|
||||
#include "Misc/Utilities.h"
|
||||
#include "Misc/Translator.h"
|
||||
#include "Misc/CommonFonts.h"
|
||||
|
||||
/**
|
||||
* Generates a hexdump of the given data
|
||||
@ -88,7 +82,7 @@ IO::Console::Console()
|
||||
, m_showTimestamp(false)
|
||||
, m_isStartingLine(true)
|
||||
, m_lastCharWasCR(false)
|
||||
, m_textBuffer(1024 * 1024)
|
||||
, m_textBuffer(10 * 1024)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
@ -110,14 +104,6 @@ bool IO::Console::echo() const
|
||||
return m_echo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if data buffer contains information that the user can export.
|
||||
*/
|
||||
bool IO::Console::saveAvailable() const
|
||||
{
|
||||
return m_textBuffer.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if a timestamp should be shown before each displayed data
|
||||
* block.
|
||||
@ -313,37 +299,6 @@ QByteArray IO::Console::hexToBytes(const QString &data)
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the user to export the information displayed on the console
|
||||
*/
|
||||
void IO::Console::save()
|
||||
{
|
||||
// No data buffer received, abort
|
||||
if (!saveAvailable())
|
||||
return;
|
||||
|
||||
// Get file name
|
||||
auto path = QFileDialog::getSaveFileName(nullptr, tr("Export Console Data"),
|
||||
QDir::homePath(),
|
||||
tr("Text Files") + " (*.txt)");
|
||||
|
||||
// Create file
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
QFile file(path);
|
||||
if (file.open(QFile::WriteOnly))
|
||||
{
|
||||
file.write(m_textBuffer.peek(m_textBuffer.size()));
|
||||
file.close();
|
||||
Misc::Utilities::revealFile(path);
|
||||
}
|
||||
|
||||
else
|
||||
Misc::Utilities::showMessageBox(tr("Error while exporting console data"),
|
||||
file.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the text displayed by the current QML text document
|
||||
*/
|
||||
@ -352,7 +307,6 @@ void IO::Console::clear()
|
||||
m_textBuffer.clear();
|
||||
m_isStartingLine = true;
|
||||
m_lastCharWasCR = false;
|
||||
Q_EMIT saveAvailableChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,35 +405,6 @@ void IO::Console::send(const QString &data)
|
||||
Manager::instance().writeData(bin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text document with current console output & prints it using native
|
||||
* system libraries/toolkits.
|
||||
*/
|
||||
void IO::Console::print()
|
||||
{
|
||||
// Create text document
|
||||
QTextDocument document;
|
||||
document.setPlainText(
|
||||
QString::fromUtf8(m_textBuffer.peek(m_textBuffer.size())));
|
||||
|
||||
// Set font
|
||||
auto font = Misc::CommonFonts::instance().customMonoFont(0.8);
|
||||
document.setDefaultFont(font);
|
||||
|
||||
// Create printer object
|
||||
QPrinter printer(QPrinter::PrinterResolution);
|
||||
printer.setFullPage(true);
|
||||
printer.setDocName(qApp->applicationDisplayName());
|
||||
printer.setPageOrientation(QPageLayout::Portrait);
|
||||
|
||||
// Show print dialog
|
||||
QPrintDialog printDialog(&printer, nullptr);
|
||||
if (printDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
document.print(&printer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables displaying a timestamp of each received data block.
|
||||
*/
|
||||
@ -544,9 +469,6 @@ void IO::Console::append(const QString &string, const bool addTimestamp)
|
||||
if (string.isEmpty())
|
||||
return;
|
||||
|
||||
// Check if we should update the save available feature
|
||||
const bool previousSaveAvailable = saveAvailable();
|
||||
|
||||
// Omit leading \n if a trailing \r was already rendered from previous payload
|
||||
auto data = string;
|
||||
if (m_lastCharWasCR && data.startsWith('\n'))
|
||||
@ -606,10 +528,6 @@ void IO::Console::append(const QString &string, const bool addTimestamp)
|
||||
// Add data to saved text buffer
|
||||
m_textBuffer.append(processedString.toUtf8());
|
||||
|
||||
// Update save avaialable
|
||||
if (saveAvailable() != previousSaveAvailable)
|
||||
Q_EMIT saveAvailableChanged();
|
||||
|
||||
// Update UI
|
||||
QMetaObject::invokeMethod(
|
||||
this, [=] { Q_EMIT displayString(processedString); },
|
||||
|
@ -48,9 +48,6 @@ class Console : public QObject
|
||||
READ showTimestamp
|
||||
WRITE setShowTimestamp
|
||||
NOTIFY showTimestampChanged)
|
||||
Q_PROPERTY(bool saveAvailable
|
||||
READ saveAvailable
|
||||
NOTIFY saveAvailableChanged)
|
||||
Q_PROPERTY(IO::Console::DataMode dataMode
|
||||
READ dataMode
|
||||
WRITE setDataMode
|
||||
@ -86,7 +83,6 @@ signals:
|
||||
void historyItemChanged();
|
||||
void textDocumentChanged();
|
||||
void showTimestampChanged();
|
||||
void saveAvailableChanged();
|
||||
void displayString(const QString &text);
|
||||
|
||||
private:
|
||||
@ -123,7 +119,6 @@ public:
|
||||
static Console &instance();
|
||||
|
||||
[[nodiscard]] bool echo() const;
|
||||
[[nodiscard]] bool saveAvailable() const;
|
||||
[[nodiscard]] bool showTimestamp() const;
|
||||
|
||||
[[nodiscard]] DataMode dataMode() const;
|
||||
@ -141,9 +136,7 @@ public:
|
||||
static QByteArray hexToBytes(const QString &data);
|
||||
|
||||
public slots:
|
||||
void save();
|
||||
void clear();
|
||||
void print();
|
||||
void historyUp();
|
||||
void historyDown();
|
||||
void setupExternalConnections();
|
||||
|
209
app/src/IO/ConsoleExport.cpp
Normal file
209
app/src/IO/ConsoleExport.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Serial Studio - https://serial-studio.github.io/
|
||||
*
|
||||
* Copyright (C) 2020-2025 Alex Spataru <https://aspatru.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later OR Commercial
|
||||
*/
|
||||
|
||||
#include "ConsoleExport.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
#include <QApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "IO/Console.h"
|
||||
#include "IO/Manager.h"
|
||||
#include "Misc/Utilities.h"
|
||||
#include "Misc/TimerEvents.h"
|
||||
|
||||
/**
|
||||
* Constructor function, configures the path in which Serial Studio shall
|
||||
* automatically write generated console log files.
|
||||
*/
|
||||
IO::ConsoleExport::ConsoleExport()
|
||||
: m_exportEnabled(true)
|
||||
{
|
||||
m_filePath = QStringLiteral("%1/%2/Console")
|
||||
.arg(QStandardPaths::writableLocation(
|
||||
QStandardPaths::DocumentsLocation),
|
||||
qApp->applicationDisplayName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Close file & finnish write-operations before destroying the class.
|
||||
*/
|
||||
IO::ConsoleExport::~ConsoleExport()
|
||||
{
|
||||
closeFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the only instance of this class.
|
||||
*/
|
||||
IO::ConsoleExport &IO::ConsoleExport::instance()
|
||||
{
|
||||
static ConsoleExport instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if the console output file is open.
|
||||
*/
|
||||
bool IO::ConsoleExport::isOpen() const
|
||||
{
|
||||
return m_file.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns @c true if console log export is enabled.
|
||||
*/
|
||||
bool IO::ConsoleExport::exportEnabled() const
|
||||
{
|
||||
return m_exportEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all remaining console data & close the output file.
|
||||
*/
|
||||
void IO::ConsoleExport::closeFile()
|
||||
{
|
||||
if (isOpen())
|
||||
{
|
||||
if (m_buffer.size() > 0)
|
||||
writeData();
|
||||
|
||||
m_file.close();
|
||||
m_textStream.setDevice(nullptr);
|
||||
|
||||
Q_EMIT openChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the signal/slot connections with the modules of the application
|
||||
* that this module depends upon.
|
||||
*/
|
||||
void IO::ConsoleExport::setupExternalConnections()
|
||||
{
|
||||
connect(&IO::Console::instance(), &IO::Console::displayString, this,
|
||||
&IO::ConsoleExport::registerData);
|
||||
connect(&IO::Manager::instance(), &IO::Manager::connectedChanged, this,
|
||||
&IO::ConsoleExport::closeFile);
|
||||
connect(&Misc::TimerEvents::instance(), &Misc::TimerEvents::timeout1Hz, this,
|
||||
&IO::ConsoleExport::writeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables data export.
|
||||
*/
|
||||
void IO::ConsoleExport::setExportEnabled(const bool enabled)
|
||||
{
|
||||
m_exportEnabled = enabled;
|
||||
Q_EMIT enabledChanged();
|
||||
|
||||
if (!exportEnabled() && isOpen())
|
||||
{
|
||||
m_buffer.clear();
|
||||
closeFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes current buffer data to the output file, and creates a new file
|
||||
* if needed.
|
||||
*/
|
||||
void IO::ConsoleExport::writeData()
|
||||
{
|
||||
// Device not connected, abort
|
||||
if (!IO::Manager::instance().connected())
|
||||
return;
|
||||
|
||||
// Export is disabled, abort
|
||||
if (!exportEnabled())
|
||||
return;
|
||||
|
||||
// Write data to the file
|
||||
if (m_buffer.size() > 0)
|
||||
{
|
||||
// Create a new file if required
|
||||
if (!isOpen())
|
||||
createFile();
|
||||
|
||||
// Write data to hard disk
|
||||
if (m_textStream.device())
|
||||
{
|
||||
m_textStream << m_buffer;
|
||||
m_textStream.flush();
|
||||
m_buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new console log output file based on the current date/time.
|
||||
*/
|
||||
void IO::ConsoleExport::createFile()
|
||||
{
|
||||
// Close current file (if any)
|
||||
if (isOpen())
|
||||
closeFile();
|
||||
|
||||
// Get filename
|
||||
const auto dateTime = QDateTime::currentDateTime();
|
||||
const auto fileName
|
||||
= dateTime.toString(QStringLiteral("yyyy_MMM_dd HH_mm_ss"))
|
||||
+ QStringLiteral(".txt");
|
||||
|
||||
// Generate file path if required
|
||||
QDir dir(m_filePath);
|
||||
if (!dir.exists())
|
||||
dir.mkpath(QStringLiteral("."));
|
||||
|
||||
// Open file
|
||||
m_file.setFileName(dir.filePath(fileName));
|
||||
if (!m_file.open(QIODeviceBase::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
Misc::Utilities::showMessageBox(tr("Console Output File Error"),
|
||||
tr("Cannot open file for writing!"));
|
||||
closeFile();
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure text stream
|
||||
m_textStream.setDevice(&m_file);
|
||||
m_textStream.setGenerateByteOrderMark(true);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
m_textStream.setCodec("UTF-8");
|
||||
#else
|
||||
m_textStream.setEncoding(QStringConverter::Utf8);
|
||||
#endif
|
||||
|
||||
// Emit signals
|
||||
Q_EMIT openChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given console data to the output buffer.
|
||||
*/
|
||||
void IO::ConsoleExport::registerData(const QString &data)
|
||||
{
|
||||
if (!data.isEmpty() && exportEnabled())
|
||||
m_buffer.append(data);
|
||||
}
|
79
app/src/IO/ConsoleExport.h
Normal file
79
app/src/IO/ConsoleExport.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Serial Studio - https://serial-studio.github.io/
|
||||
*
|
||||
* Copyright (C) 2020-2025 Alex Spataru <https://aspatru.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later OR Commercial
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFile>
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
namespace IO
|
||||
{
|
||||
class ConsoleExport : public QObject
|
||||
{
|
||||
// clang-format off
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isOpen
|
||||
READ isOpen
|
||||
NOTIFY openChanged)
|
||||
Q_PROPERTY(bool exportEnabled
|
||||
READ exportEnabled
|
||||
WRITE setExportEnabled
|
||||
NOTIFY enabledChanged)
|
||||
// clang-format on
|
||||
|
||||
signals:
|
||||
void openChanged();
|
||||
void enabledChanged();
|
||||
|
||||
private:
|
||||
explicit ConsoleExport();
|
||||
ConsoleExport(ConsoleExport &&) = delete;
|
||||
ConsoleExport(const ConsoleExport &) = delete;
|
||||
ConsoleExport &operator=(ConsoleExport &&) = delete;
|
||||
ConsoleExport &operator=(const ConsoleExport &) = delete;
|
||||
|
||||
~ConsoleExport();
|
||||
|
||||
public:
|
||||
static ConsoleExport &instance();
|
||||
|
||||
[[nodiscard]] bool isOpen() const;
|
||||
[[nodiscard]] bool exportEnabled() const;
|
||||
|
||||
public slots:
|
||||
void closeFile();
|
||||
void setupExternalConnections();
|
||||
void setExportEnabled(const bool enabled);
|
||||
|
||||
private slots:
|
||||
void writeData();
|
||||
void createFile();
|
||||
void registerData(const QString &data);
|
||||
|
||||
private:
|
||||
QFile m_file;
|
||||
QString m_buffer;
|
||||
QString m_filePath;
|
||||
bool m_exportEnabled;
|
||||
QTextStream m_textStream;
|
||||
};
|
||||
} // namespace IO
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "IO/Manager.h"
|
||||
#include "IO/Console.h"
|
||||
#include "IO/ConsoleExport.h"
|
||||
#include "IO/FileTransmission.h"
|
||||
|
||||
#include "IO/Drivers/Serial.h"
|
||||
@ -255,6 +256,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
||||
auto projectModel = &JSON::ProjectModel::instance();
|
||||
auto miscTimerEvents = &Misc::TimerEvents::instance();
|
||||
auto miscCommonFonts = &Misc::CommonFonts::instance();
|
||||
auto ioConsoleExport = &IO::ConsoleExport::instance();
|
||||
auto miscThemeManager = &Misc::ThemeManager::instance();
|
||||
auto ioBluetoothLE = &IO::Drivers::BluetoothLE::instance();
|
||||
auto ioFileTransmission = &IO::FileTransmission::instance();
|
||||
@ -294,6 +296,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
||||
c->setContextProperty("Cpp_JSON_FrameBuilder", frameBuilder);
|
||||
c->setContextProperty("Cpp_Misc_TimerEvents", miscTimerEvents);
|
||||
c->setContextProperty("Cpp_Misc_CommonFonts", miscCommonFonts);
|
||||
c->setContextProperty("Cpp_IO_ConsoleExport", ioConsoleExport);
|
||||
c->setContextProperty("Cpp_IO_FileTransmission", ioFileTransmission);
|
||||
|
||||
// Register app info with QML
|
||||
@ -318,6 +321,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
||||
ioManager->setupExternalConnections();
|
||||
projectModel->setupExternalConnections();
|
||||
frameBuilder->setupExternalConnections();
|
||||
ioConsoleExport->setupExternalConnections();
|
||||
|
||||
// Install custom message handler to redirect qDebug output to console
|
||||
qInstallMessageHandler(MessageHandler);
|
||||
|
Loading…
x
Reference in New Issue
Block a user