mirror of
https://github.com/Serial-Studio/Serial-Studio.git
synced 2025-01-31 17:42:55 +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
|
Bluetooth
|
||||||
SerialPort
|
SerialPort
|
||||||
Positioning
|
Positioning
|
||||||
PrintSupport
|
|
||||||
LinguistTools
|
LinguistTools
|
||||||
QuickControls2
|
QuickControls2
|
||||||
)
|
)
|
||||||
@ -98,6 +97,7 @@ set(SOURCES
|
|||||||
src/IO/Checksum.cpp
|
src/IO/Checksum.cpp
|
||||||
src/IO/Console.cpp
|
src/IO/Console.cpp
|
||||||
src/IO/Manager.cpp
|
src/IO/Manager.cpp
|
||||||
|
src/IO/ConsoleExport.cpp
|
||||||
src/IO/FileTransmission.cpp
|
src/IO/FileTransmission.cpp
|
||||||
src/IO/FrameReader.cpp
|
src/IO/FrameReader.cpp
|
||||||
src/JSON/FrameParser.cpp
|
src/JSON/FrameParser.cpp
|
||||||
@ -145,6 +145,7 @@ set(HEADERS
|
|||||||
src/IO/Manager.h
|
src/IO/Manager.h
|
||||||
src/IO/HAL_Driver.h
|
src/IO/HAL_Driver.h
|
||||||
src/IO/Checksum.h
|
src/IO/Checksum.h
|
||||||
|
src/IO/ConsoleExport.h
|
||||||
src/IO/CircularBuffer.h
|
src/IO/CircularBuffer.h
|
||||||
src/IO/FileTransmission.h
|
src/IO/FileTransmission.h
|
||||||
src/IO/FrameReader.h
|
src/IO/FrameReader.h
|
||||||
@ -223,7 +224,6 @@ target_link_libraries(
|
|||||||
Qt6::Bluetooth
|
Qt6::Bluetooth
|
||||||
Qt6::SerialPort
|
Qt6::SerialPort
|
||||||
Qt6::Positioning
|
Qt6::Positioning
|
||||||
Qt6::PrintSupport
|
|
||||||
Qt6::QuickControls2
|
Qt6::QuickControls2
|
||||||
|
|
||||||
simde
|
simde
|
||||||
|
@ -67,11 +67,11 @@ Widgets.Pane {
|
|||||||
Settings {
|
Settings {
|
||||||
category: "SetupPanel"
|
category: "SetupPanel"
|
||||||
|
|
||||||
property alias tabIndex: tab.currentIndex
|
|
||||||
property alias csvExport: csvLogging.checked
|
|
||||||
property alias driver: driverCombo.currentIndex
|
|
||||||
property alias language: settings.language
|
property alias language: settings.language
|
||||||
|
property alias csvExport: csvLogging.checked
|
||||||
property alias tcpPlugins: settings.tcpPlugins
|
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 {
|
Label {
|
||||||
text: qsTr("Device Setup") + ":"
|
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
|
// Spacer
|
||||||
//
|
//
|
||||||
@ -173,6 +156,7 @@ Widgets.Pane {
|
|||||||
color: Cpp_ThemeManager.colors["pane_section_label"]
|
color: Cpp_ThemeManager.colors["pane_section_label"]
|
||||||
Component.onCompleted: font.capitalization = Font.AllUppercase
|
Component.onCompleted: font.capitalization = Font.AllUppercase
|
||||||
} RadioButton {
|
} RadioButton {
|
||||||
|
Layout.leftMargin: -6
|
||||||
Layout.maximumHeight: 18
|
Layout.maximumHeight: 18
|
||||||
Layout.maximumWidth: root.maxItemWidth
|
Layout.maximumWidth: root.maxItemWidth
|
||||||
text: qsTr("No Parsing (Device Sends JSON Data)")
|
text: qsTr("No Parsing (Device Sends JSON Data)")
|
||||||
@ -183,6 +167,7 @@ Widgets.Pane {
|
|||||||
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.DeviceSendsJSON
|
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.DeviceSendsJSON
|
||||||
}
|
}
|
||||||
} RadioButton {
|
} RadioButton {
|
||||||
|
Layout.leftMargin: -6
|
||||||
Layout.maximumHeight: 18
|
Layout.maximumHeight: 18
|
||||||
Layout.maximumWidth: root.maxItemWidth
|
Layout.maximumWidth: root.maxItemWidth
|
||||||
text: qsTr("Quick Plot (Comma Separated Values)")
|
text: qsTr("Quick Plot (Comma Separated Values)")
|
||||||
@ -193,6 +178,7 @@ Widgets.Pane {
|
|||||||
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.QuickPlot
|
Cpp_JSON_FrameBuilder.operationMode = SerialStudio.QuickPlot
|
||||||
}
|
}
|
||||||
} RadioButton {
|
} RadioButton {
|
||||||
|
Layout.leftMargin: -6
|
||||||
Layout.maximumHeight: 18
|
Layout.maximumHeight: 18
|
||||||
Layout.maximumWidth: root.maxItemWidth
|
Layout.maximumWidth: root.maxItemWidth
|
||||||
text: qsTr("Parse via JSON Project File")
|
text: qsTr("Parse via JSON Project File")
|
||||||
@ -225,6 +211,59 @@ Widgets.Pane {
|
|||||||
implicitHeight: 4
|
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
|
// Tab bar
|
||||||
//
|
//
|
||||||
|
@ -34,6 +34,7 @@ Item {
|
|||||||
//
|
//
|
||||||
QtSettings.Settings {
|
QtSettings.Settings {
|
||||||
category: "DeviceSetup"
|
category: "DeviceSetup"
|
||||||
|
|
||||||
property alias serialDtr: serial.dtr
|
property alias serialDtr: serial.dtr
|
||||||
property alias serialParity: serial.parity
|
property alias serialParity: serial.parity
|
||||||
property alias serialBaudRate: serial.baudRate
|
property alias serialBaudRate: serial.baudRate
|
||||||
|
@ -152,23 +152,6 @@ Item {
|
|||||||
text: qsTr("Clear")
|
text: qsTr("Clear")
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
onTriggered: root.clear()
|
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 {
|
Button {
|
||||||
icon.width: 18
|
icon.width: 18
|
||||||
icon.height: 18
|
icon.height: 18
|
||||||
@ -452,7 +411,6 @@ Item {
|
|||||||
onClicked: root.clear()
|
onClicked: root.clear()
|
||||||
Layout.maximumWidth: 32
|
Layout.maximumWidth: 32
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
enabled: Cpp_IO_Console.saveAvailable
|
|
||||||
icon.source: "qrc:/rcc/icons/buttons/clear.svg"
|
icon.source: "qrc:/rcc/icons/buttons/clear.svg"
|
||||||
icon.color: Cpp_ThemeManager.colors["button_text"]
|
icon.color: Cpp_ThemeManager.colors["button_text"]
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
#include "IO/Manager.h"
|
#include "IO/Manager.h"
|
||||||
#include "CSV/Player.h"
|
#include "CSV/Player.h"
|
||||||
@ -36,8 +36,8 @@
|
|||||||
#include "JSON/FrameBuilder.h"
|
#include "JSON/FrameBuilder.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect JSON Parser & Serial Manager signals to begin registering JSON
|
* Constructor function, configures the path in which Serial Studio shall
|
||||||
* dataframes into JSON list.
|
* automatically write generated CSV files.
|
||||||
*/
|
*/
|
||||||
CSV::Export::Export()
|
CSV::Export::Export()
|
||||||
: m_exportEnabled(true)
|
: 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()
|
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()
|
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
|
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
|
bool CSV::Export::exportEnabled() const
|
||||||
{
|
{
|
||||||
return m_exportEnabled;
|
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
|
* Configures the signal/slot connections with the rest of the modules of the
|
||||||
* application.
|
* 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)
|
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()
|
void CSV::Export::closeFile()
|
||||||
{
|
{
|
||||||
@ -238,8 +226,8 @@ CSV::Export::createCsvFile(const CSV::TimestampFrame &frame)
|
|||||||
const auto &rxTime = frame.rxDateTime;
|
const auto &rxTime = frame.rxDateTime;
|
||||||
|
|
||||||
// Get file name
|
// Get file name
|
||||||
const auto fileName
|
const auto fileName = rxTime.toString(QStringLiteral("yyyy_MMM_dd HH_mm_ss"))
|
||||||
= rxTime.toString(QStringLiteral("yyyy_MMM_dd HH_mm_ss")) + ".csv";
|
+ QStringLiteral(".csv");
|
||||||
|
|
||||||
// Get path
|
// Get path
|
||||||
const QString path = QStringLiteral("%1/%2/").arg(m_csvPath, data.title());
|
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
|
// Generate file path if required
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
if (!dir.exists())
|
if (!dir.exists())
|
||||||
dir.mkpath(".");
|
dir.mkpath(QStringLiteral("."));
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
m_csvFile.setFileName(dir.filePath(fileName));
|
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)
|
void CSV::Export::registerFrame(const JSON::Frame &frame)
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,6 @@ public:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void closeFile();
|
void closeFile();
|
||||||
void openCurrentCsv();
|
|
||||||
void setupExternalConnections();
|
void setupExternalConnections();
|
||||||
void setExportEnabled(const bool enabled);
|
void setExportEnabled(const bool enabled);
|
||||||
|
|
||||||
|
@ -20,17 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QPrinter>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QPrintDialog>
|
|
||||||
#include <QTextDocument>
|
|
||||||
|
|
||||||
#include "IO/Manager.h"
|
#include "IO/Manager.h"
|
||||||
#include "IO/Console.h"
|
#include "IO/Console.h"
|
||||||
#include "Misc/Utilities.h"
|
|
||||||
#include "Misc/Translator.h"
|
#include "Misc/Translator.h"
|
||||||
#include "Misc/CommonFonts.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a hexdump of the given data
|
* Generates a hexdump of the given data
|
||||||
@ -88,7 +82,7 @@ IO::Console::Console()
|
|||||||
, m_showTimestamp(false)
|
, m_showTimestamp(false)
|
||||||
, m_isStartingLine(true)
|
, m_isStartingLine(true)
|
||||||
, m_lastCharWasCR(false)
|
, m_lastCharWasCR(false)
|
||||||
, m_textBuffer(1024 * 1024)
|
, m_textBuffer(10 * 1024)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
@ -110,14 +104,6 @@ bool IO::Console::echo() const
|
|||||||
return m_echo;
|
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
|
* Returns @c true if a timestamp should be shown before each displayed data
|
||||||
* block.
|
* block.
|
||||||
@ -313,37 +299,6 @@ QByteArray IO::Console::hexToBytes(const QString &data)
|
|||||||
return array;
|
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
|
* Deletes all the text displayed by the current QML text document
|
||||||
*/
|
*/
|
||||||
@ -352,7 +307,6 @@ void IO::Console::clear()
|
|||||||
m_textBuffer.clear();
|
m_textBuffer.clear();
|
||||||
m_isStartingLine = true;
|
m_isStartingLine = true;
|
||||||
m_lastCharWasCR = false;
|
m_lastCharWasCR = false;
|
||||||
Q_EMIT saveAvailableChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -451,35 +405,6 @@ void IO::Console::send(const QString &data)
|
|||||||
Manager::instance().writeData(bin);
|
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.
|
* 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())
|
if (string.isEmpty())
|
||||||
return;
|
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
|
// Omit leading \n if a trailing \r was already rendered from previous payload
|
||||||
auto data = string;
|
auto data = string;
|
||||||
if (m_lastCharWasCR && data.startsWith('\n'))
|
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
|
// Add data to saved text buffer
|
||||||
m_textBuffer.append(processedString.toUtf8());
|
m_textBuffer.append(processedString.toUtf8());
|
||||||
|
|
||||||
// Update save avaialable
|
|
||||||
if (saveAvailable() != previousSaveAvailable)
|
|
||||||
Q_EMIT saveAvailableChanged();
|
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this, [=] { Q_EMIT displayString(processedString); },
|
this, [=] { Q_EMIT displayString(processedString); },
|
||||||
|
@ -48,9 +48,6 @@ class Console : public QObject
|
|||||||
READ showTimestamp
|
READ showTimestamp
|
||||||
WRITE setShowTimestamp
|
WRITE setShowTimestamp
|
||||||
NOTIFY showTimestampChanged)
|
NOTIFY showTimestampChanged)
|
||||||
Q_PROPERTY(bool saveAvailable
|
|
||||||
READ saveAvailable
|
|
||||||
NOTIFY saveAvailableChanged)
|
|
||||||
Q_PROPERTY(IO::Console::DataMode dataMode
|
Q_PROPERTY(IO::Console::DataMode dataMode
|
||||||
READ dataMode
|
READ dataMode
|
||||||
WRITE setDataMode
|
WRITE setDataMode
|
||||||
@ -86,7 +83,6 @@ signals:
|
|||||||
void historyItemChanged();
|
void historyItemChanged();
|
||||||
void textDocumentChanged();
|
void textDocumentChanged();
|
||||||
void showTimestampChanged();
|
void showTimestampChanged();
|
||||||
void saveAvailableChanged();
|
|
||||||
void displayString(const QString &text);
|
void displayString(const QString &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -123,7 +119,6 @@ public:
|
|||||||
static Console &instance();
|
static Console &instance();
|
||||||
|
|
||||||
[[nodiscard]] bool echo() const;
|
[[nodiscard]] bool echo() const;
|
||||||
[[nodiscard]] bool saveAvailable() const;
|
|
||||||
[[nodiscard]] bool showTimestamp() const;
|
[[nodiscard]] bool showTimestamp() const;
|
||||||
|
|
||||||
[[nodiscard]] DataMode dataMode() const;
|
[[nodiscard]] DataMode dataMode() const;
|
||||||
@ -141,9 +136,7 @@ public:
|
|||||||
static QByteArray hexToBytes(const QString &data);
|
static QByteArray hexToBytes(const QString &data);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void save();
|
|
||||||
void clear();
|
void clear();
|
||||||
void print();
|
|
||||||
void historyUp();
|
void historyUp();
|
||||||
void historyDown();
|
void historyDown();
|
||||||
void setupExternalConnections();
|
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/Manager.h"
|
||||||
#include "IO/Console.h"
|
#include "IO/Console.h"
|
||||||
|
#include "IO/ConsoleExport.h"
|
||||||
#include "IO/FileTransmission.h"
|
#include "IO/FileTransmission.h"
|
||||||
|
|
||||||
#include "IO/Drivers/Serial.h"
|
#include "IO/Drivers/Serial.h"
|
||||||
@ -255,6 +256,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
|||||||
auto projectModel = &JSON::ProjectModel::instance();
|
auto projectModel = &JSON::ProjectModel::instance();
|
||||||
auto miscTimerEvents = &Misc::TimerEvents::instance();
|
auto miscTimerEvents = &Misc::TimerEvents::instance();
|
||||||
auto miscCommonFonts = &Misc::CommonFonts::instance();
|
auto miscCommonFonts = &Misc::CommonFonts::instance();
|
||||||
|
auto ioConsoleExport = &IO::ConsoleExport::instance();
|
||||||
auto miscThemeManager = &Misc::ThemeManager::instance();
|
auto miscThemeManager = &Misc::ThemeManager::instance();
|
||||||
auto ioBluetoothLE = &IO::Drivers::BluetoothLE::instance();
|
auto ioBluetoothLE = &IO::Drivers::BluetoothLE::instance();
|
||||||
auto ioFileTransmission = &IO::FileTransmission::instance();
|
auto ioFileTransmission = &IO::FileTransmission::instance();
|
||||||
@ -294,6 +296,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
|||||||
c->setContextProperty("Cpp_JSON_FrameBuilder", frameBuilder);
|
c->setContextProperty("Cpp_JSON_FrameBuilder", frameBuilder);
|
||||||
c->setContextProperty("Cpp_Misc_TimerEvents", miscTimerEvents);
|
c->setContextProperty("Cpp_Misc_TimerEvents", miscTimerEvents);
|
||||||
c->setContextProperty("Cpp_Misc_CommonFonts", miscCommonFonts);
|
c->setContextProperty("Cpp_Misc_CommonFonts", miscCommonFonts);
|
||||||
|
c->setContextProperty("Cpp_IO_ConsoleExport", ioConsoleExport);
|
||||||
c->setContextProperty("Cpp_IO_FileTransmission", ioFileTransmission);
|
c->setContextProperty("Cpp_IO_FileTransmission", ioFileTransmission);
|
||||||
|
|
||||||
// Register app info with QML
|
// Register app info with QML
|
||||||
@ -318,6 +321,7 @@ void Misc::ModuleManager::initializeQmlInterface()
|
|||||||
ioManager->setupExternalConnections();
|
ioManager->setupExternalConnections();
|
||||||
projectModel->setupExternalConnections();
|
projectModel->setupExternalConnections();
|
||||||
frameBuilder->setupExternalConnections();
|
frameBuilder->setupExternalConnections();
|
||||||
|
ioConsoleExport->setupExternalConnections();
|
||||||
|
|
||||||
// Install custom message handler to redirect qDebug output to console
|
// Install custom message handler to redirect qDebug output to console
|
||||||
qInstallMessageHandler(MessageHandler);
|
qInstallMessageHandler(MessageHandler);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user