mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-06 19:20:45 +00:00
Merge branch 'master' into updater
This commit is contained in:
commit
b0d4fcd628
@ -92,7 +92,7 @@ void DeviceConnectionManager::replay(QFile* file)
|
|||||||
|
|
||||||
|
|
||||||
// Return singleton instance of DeviceConnectionManager, creating it if necessary.
|
// Return singleton instance of DeviceConnectionManager, creating it if necessary.
|
||||||
inline DeviceConnectionManager & DeviceConnectionManager::getInstance()
|
DeviceConnectionManager & DeviceConnectionManager::getInstance()
|
||||||
{
|
{
|
||||||
static DeviceConnectionManager instance;
|
static DeviceConnectionManager instance;
|
||||||
return instance;
|
return instance;
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
// connections to devices. For now it just supports serial ports.
|
// connections to devices. For now it just supports serial ports.
|
||||||
|
|
||||||
#include <QtSerialPort/QSerialPort>
|
#include <QtSerialPort/QSerialPort>
|
||||||
#include <QXmlStreamWriter>
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
@ -263,6 +262,9 @@ public:
|
|||||||
* supports Bluetooth and BLE as well as serial. Such a class might then be
|
* supports Bluetooth and BLE as well as serial. Such a class might then be
|
||||||
* used instead of port "name" between DeviceConnectionManager and clients.
|
* used instead of port "name" between DeviceConnectionManager and clients.
|
||||||
*/
|
*/
|
||||||
|
class QXmlStreamWriter;
|
||||||
|
class QXmlStreamReader;
|
||||||
|
|
||||||
class SerialPortInfo
|
class SerialPortInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -116,6 +116,14 @@ void XmlRecorder::epilogue()
|
|||||||
m_xml->writeEndElement(); // close enclosing tag
|
m_xml->writeEndElement(); // close enclosing tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XmlRecorder::flush()
|
||||||
|
{
|
||||||
|
if (m_file) {
|
||||||
|
if (!m_file->flush()) {
|
||||||
|
qWarning().noquote() << "Unable to flush XML to" << m_file->fileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
XmlReplay::XmlReplay(QFile* file, const QString & tag)
|
XmlReplay::XmlReplay(QFile* file, const QString & tag)
|
||||||
: m_tag(tag), m_file(file), m_pendingSignal(nullptr), m_parent(nullptr)
|
: m_tag(tag), m_file(file), m_pendingSignal(nullptr), m_parent(nullptr)
|
||||||
@ -416,6 +424,7 @@ void XmlReplayEvent::record(XmlRecorder* writer) const
|
|||||||
if (writer != nullptr) {
|
if (writer != nullptr) {
|
||||||
writer->lock();
|
writer->lock();
|
||||||
writer->xml() << *this;
|
writer->xml() << *this;
|
||||||
|
writer->flush();
|
||||||
writer->unlock();
|
writer->unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ public:
|
|||||||
inline QXmlStreamWriter & xml() { return *m_xml; }
|
inline QXmlStreamWriter & xml() { return *m_xml; }
|
||||||
inline void lock() { m_mutex.lock(); }
|
inline void lock() { m_mutex.lock(); }
|
||||||
inline void unlock() { m_mutex.unlock(); }
|
inline void unlock() { m_mutex.unlock(); }
|
||||||
|
void flush();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
XmlRecorder(XmlRecorder* parent, const QString & id, const QString & tag); // constructor used by substreams
|
XmlRecorder(XmlRecorder* parent, const QString & id, const QString & tag); // constructor used by substreams
|
||||||
|
183
oscar/logger.cpp
183
oscar/logger.cpp
@ -8,8 +8,9 @@
|
|||||||
* for more details. */
|
* for more details. */
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "SleepLib/preferences.h"
|
||||||
#define ASSERTS_SUCK
|
#include "version.h"
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
QThreadPool * otherThreadPool = NULL;
|
QThreadPool * otherThreadPool = NULL;
|
||||||
|
|
||||||
@ -47,12 +48,9 @@ void MyOutputHandler(QtMsgType type, const QMessageLogContext &context, const QS
|
|||||||
|
|
||||||
if (logger && logger->isRunning()) {
|
if (logger && logger->isRunning()) {
|
||||||
logger->append(msg);
|
logger->append(msg);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
|
||||||
}
|
}
|
||||||
#ifdef ASSERTS_SUCK
|
|
||||||
// else {
|
|
||||||
// fprintf(stderr, "%s\n", msg.toLocal8Bit().data());
|
|
||||||
// }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (type == QtFatalMsg) {
|
if (type == QtFatalMsg) {
|
||||||
abort();
|
abort();
|
||||||
@ -84,15 +82,65 @@ void LogThread::connectionReady()
|
|||||||
{
|
{
|
||||||
strlock.lock();
|
strlock.lock();
|
||||||
connected = true;
|
connected = true;
|
||||||
|
logTrigger.wakeAll();
|
||||||
strlock.unlock();
|
strlock.unlock();
|
||||||
qDebug() << "Logging UI initialized";
|
qDebug() << "Logging UI initialized";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LogThread::logToFile()
|
||||||
|
{
|
||||||
|
if (m_logStream) {
|
||||||
|
qWarning().noquote() << "Already logging to" << m_logFile->fileName();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString debugLog = GetLogDir() + "/debug.txt";
|
||||||
|
rotateLogs(debugLog); // keep a limited set of previous logs
|
||||||
|
|
||||||
|
strlock.lock();
|
||||||
|
m_logFile = new QFile(debugLog);
|
||||||
|
Q_ASSERT(m_logFile);
|
||||||
|
if (m_logFile->open(QFile::ReadWrite | QFile::Text)) {
|
||||||
|
m_logStream = new QTextStream(m_logFile);
|
||||||
|
}
|
||||||
|
logTrigger.wakeAll();
|
||||||
|
strlock.unlock();
|
||||||
|
|
||||||
|
if (m_logStream) {
|
||||||
|
qDebug().noquote() << "Logging to" << debugLog;
|
||||||
|
} else {
|
||||||
|
qWarning().noquote() << "Unable to open" << debugLog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogThread::~LogThread()
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&strlock);
|
||||||
|
|
||||||
|
Q_ASSERT(running == false);
|
||||||
|
if (m_logStream) {
|
||||||
|
delete m_logStream;
|
||||||
|
m_logStream = nullptr;
|
||||||
|
Q_ASSERT(m_logFile);
|
||||||
|
delete m_logFile;
|
||||||
|
m_logFile = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LogThread::logFileName()
|
||||||
|
{
|
||||||
|
if (!m_logFile) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return m_logFile->fileName();
|
||||||
|
}
|
||||||
|
|
||||||
void shutdownLogger()
|
void shutdownLogger()
|
||||||
{
|
{
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->quit();
|
logger->quit();
|
||||||
otherThreadPool->waitForDone(-1);
|
// The thread is automatically destroyed when its run() method exits.
|
||||||
|
otherThreadPool->waitForDone(-1); // wait until that happens
|
||||||
logger = NULL;
|
logger = NULL;
|
||||||
}
|
}
|
||||||
delete otherThreadPool;
|
delete otherThreadPool;
|
||||||
@ -103,47 +151,132 @@ LogThread * logger = NULL;
|
|||||||
void LogThread::append(QString msg)
|
void LogThread::append(QString msg)
|
||||||
{
|
{
|
||||||
QString tmp = QString("%1: %2").arg(logtime.elapsed(), 5, 10, QChar('0')).arg(msg);
|
QString tmp = QString("%1: %2").arg(logtime.elapsed(), 5, 10, QChar('0')).arg(msg);
|
||||||
//QStringList appears not to be threadsafe
|
appendClean(tmp);
|
||||||
strlock.lock();
|
|
||||||
buffer.append(tmp);
|
|
||||||
strlock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogThread::appendClean(QString msg)
|
void LogThread::appendClean(QString msg)
|
||||||
{
|
{
|
||||||
|
fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
|
||||||
strlock.lock();
|
strlock.lock();
|
||||||
buffer.append(msg);
|
buffer.append(msg);
|
||||||
|
logTrigger.wakeAll();
|
||||||
strlock.unlock();
|
strlock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LogThread::quit() {
|
void LogThread::quit() {
|
||||||
qDebug() << "Shutting down logging thread";
|
qDebug() << "Shutting down logging thread";
|
||||||
running = false;
|
qInstallMessageHandler(0); // Remove our logger.
|
||||||
|
|
||||||
strlock.lock();
|
strlock.lock();
|
||||||
while (!buffer.isEmpty()) {
|
running = false; // Force the thread to exit after its next iteration.
|
||||||
QString msg = buffer.takeFirst();
|
logTrigger.wakeAll(); // Trigger the final flush.
|
||||||
fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
|
strlock.unlock(); // Release the lock so that the thread can complete.
|
||||||
}
|
|
||||||
strlock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LogThread::run()
|
void LogThread::run()
|
||||||
{
|
{
|
||||||
|
QMutexLocker lock(&strlock);
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
s_LoggerRunning.unlock(); // unlock as soon as the thread begins to run
|
s_LoggerRunning.unlock(); // unlock as soon as the thread begins to run
|
||||||
do {
|
do {
|
||||||
strlock.lock();
|
logTrigger.wait(&strlock); // releases strlock while it waits
|
||||||
//int r = receivers(SIGNAL(outputLog(QString())));
|
while (connected && m_logFile && !buffer.isEmpty()) {
|
||||||
while (connected && !buffer.isEmpty()) {
|
|
||||||
QString msg = buffer.takeFirst();
|
QString msg = buffer.takeFirst();
|
||||||
fprintf(stderr, "%s\n", msg.toLocal8Bit().data());
|
if (m_logStream) {
|
||||||
emit outputLog(msg);
|
*m_logStream << msg << endl;
|
||||||
|
}
|
||||||
|
emit outputLog(msg);
|
||||||
}
|
}
|
||||||
strlock.unlock();
|
|
||||||
QThread::msleep(1000);
|
|
||||||
} while (running);
|
} while (running);
|
||||||
|
|
||||||
|
// strlock will be released when lock goes out of scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString GetLogDir()
|
||||||
|
{
|
||||||
|
static const QString LOG_DIR_NAME = "logs";
|
||||||
|
|
||||||
|
Q_ASSERT(!GetAppData().isEmpty()); // If GetLogDir gets called before GetAppData() is valid, this would point at root.
|
||||||
|
QDir oscarData(GetAppData());
|
||||||
|
Q_ASSERT(oscarData.exists());
|
||||||
|
if (!oscarData.exists(LOG_DIR_NAME)) {
|
||||||
|
oscarData.mkdir(LOG_DIR_NAME);
|
||||||
|
}
|
||||||
|
QDir logDir(oscarData.canonicalPath() + "/" + LOG_DIR_NAME);
|
||||||
|
if (!logDir.exists()) {
|
||||||
|
qWarning() << "Unable to create" << logDir.absolutePath() << "reverting to" << oscarData.canonicalPath();
|
||||||
|
logDir = oscarData;
|
||||||
|
}
|
||||||
|
Q_ASSERT(logDir.exists());
|
||||||
|
|
||||||
|
return logDir.canonicalPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rotateLogs(const QString & filePath, int maxPrevious)
|
||||||
|
{
|
||||||
|
if (maxPrevious < 0) {
|
||||||
|
if (getVersion().IsReleaseVersion()) {
|
||||||
|
maxPrevious = 1;
|
||||||
|
} else {
|
||||||
|
// keep more in testing builds
|
||||||
|
maxPrevious = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the list of rotated logs for this filePath.
|
||||||
|
QFileInfo info(filePath);
|
||||||
|
QString path = QDir(info.absolutePath()).canonicalPath();
|
||||||
|
QString base = info.baseName();
|
||||||
|
QString ext = info.completeSuffix();
|
||||||
|
if (!ext.isEmpty()) {
|
||||||
|
ext = "." + ext;
|
||||||
|
}
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
qWarning() << "Skipping log rotation, directory does not exist:" << info.absoluteFilePath();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList logs;
|
||||||
|
logs.append(filePath);
|
||||||
|
for (int i = 0; i < maxPrevious; i++) {
|
||||||
|
logs.append(QString("%1/%2.%3%4").arg(path).arg(base).arg(i).arg(ext));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the expired log.
|
||||||
|
QFileInfo expired(logs[maxPrevious]);
|
||||||
|
if (expired.exists()) {
|
||||||
|
if (expired.isDir()) {
|
||||||
|
QDir dir(expired.canonicalFilePath());
|
||||||
|
//qDebug() << "Removing expired log directory" << dir.canonicalPath();
|
||||||
|
if (!dir.removeRecursively()) {
|
||||||
|
qWarning() << "Unable to delete expired log directory" << dir.canonicalPath();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QFile file(expired.canonicalFilePath());
|
||||||
|
//qDebug() << "Removing expired log file" << file.fileName();
|
||||||
|
if (!file.remove()) {
|
||||||
|
qWarning() << "Unable to delete expired log file" << file.fileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate the remaining logs.
|
||||||
|
for (int i = maxPrevious; i > 0; i--) {
|
||||||
|
QFileInfo from(logs[i-1]);
|
||||||
|
QFileInfo to(logs[i]);
|
||||||
|
if (from.exists()) {
|
||||||
|
if (to.exists()) {
|
||||||
|
qWarning() << "Unable to rotate log:" << to.absoluteFilePath() << "exists";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//qDebug() << "Renaming" << from.absoluteFilePath() << "to" << to.absoluteFilePath();
|
||||||
|
if (!QFile::rename(from.absoluteFilePath(), to.absoluteFilePath())) {
|
||||||
|
qWarning() << "Unable to rename" << from.absoluteFilePath() << "to" << to.absoluteFilePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,25 +5,32 @@
|
|||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
void initializeLogger();
|
void initializeLogger();
|
||||||
void shutdownLogger();
|
void shutdownLogger();
|
||||||
|
|
||||||
|
QString GetLogDir();
|
||||||
|
void rotateLogs(const QString & filePath, int maxPrevious=-1);
|
||||||
|
|
||||||
|
|
||||||
void MyOutputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msgtxt);
|
void MyOutputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msgtxt);
|
||||||
|
|
||||||
class LogThread:public QObject, public QRunnable
|
class LogThread:public QObject, public QRunnable
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit LogThread() : QRunnable() { running = false; logtime.start(); connected = false; }
|
explicit LogThread() : QRunnable() { running = false; logtime.start(); connected = false; m_logFile = nullptr; m_logStream = nullptr; }
|
||||||
virtual ~LogThread() {}
|
virtual ~LogThread();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void append(QString msg);
|
void append(QString msg);
|
||||||
void appendClean(QString msg);
|
void appendClean(QString msg);
|
||||||
bool isRunning() { return running; }
|
bool isRunning() { return running; }
|
||||||
void connectionReady();
|
void connectionReady();
|
||||||
|
void logToFile();
|
||||||
|
QString logFileName();
|
||||||
|
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
@ -36,6 +43,9 @@ protected:
|
|||||||
volatile bool running;
|
volatile bool running;
|
||||||
QTime logtime;
|
QTime logtime;
|
||||||
bool connected;
|
bool connected;
|
||||||
|
class QFile* m_logFile;
|
||||||
|
class QTextStream* m_logStream;
|
||||||
|
QWaitCondition logTrigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern LogThread * logger;
|
extern LogThread * logger;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "SleepLib/profiles.h"
|
#include "SleepLib/profiles.h"
|
||||||
#include "translation.h"
|
#include "translation.h"
|
||||||
#include "SleepLib/common.h"
|
#include "SleepLib/common.h"
|
||||||
|
#include "SleepLib/deviceconnection.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -544,6 +545,16 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the data directory exists.
|
||||||
|
if (!newDir.mkpath(".")) {
|
||||||
|
QMessageBox::warning(nullptr, QObject::tr("Exiting"),
|
||||||
|
QObject::tr("Unable to create the OSCAR data folder at")+"\n"+
|
||||||
|
GetAppData());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin logging to file now that there's a data folder.
|
||||||
|
logger->logToFile();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Initialize preferences system (Don't use p_pref before this point!)
|
// Initialize preferences system (Don't use p_pref before this point!)
|
||||||
@ -636,6 +647,21 @@ int main(int argc, char *argv[]) {
|
|||||||
MD300W1Loader::Register();
|
MD300W1Loader::Register();
|
||||||
ViatomLoader::Register();
|
ViatomLoader::Register();
|
||||||
|
|
||||||
|
// Begin logging device connection activity.
|
||||||
|
QString connectionsLogDir = GetLogDir() + "/connections";
|
||||||
|
rotateLogs(connectionsLogDir); // keep a limited set of previous logs
|
||||||
|
if (!QDir(connectionsLogDir).mkpath(".")) {
|
||||||
|
qWarning().noquote() << "Unable to create directory" << connectionsLogDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile deviceLog(connectionsLogDir + "/devices.xml");
|
||||||
|
if (deviceLog.open(QFile::ReadWrite)) {
|
||||||
|
qDebug().noquote() << "Logging device connections to" << deviceLog.fileName();
|
||||||
|
DeviceConnectionManager::getInstance().record(&deviceLog);
|
||||||
|
} else {
|
||||||
|
qWarning().noquote() << "Unable to start device connection logging to" << deviceLog.fileName();
|
||||||
|
}
|
||||||
|
|
||||||
schema::setOrders(); // could be called in init...
|
schema::setOrders(); // could be called in init...
|
||||||
|
|
||||||
// Scan for user profiles
|
// Scan for user profiles
|
||||||
@ -653,7 +679,11 @@ int main(int argc, char *argv[]) {
|
|||||||
mainwin->SetupGUI();
|
mainwin->SetupGUI();
|
||||||
mainwin->show();
|
mainwin->show();
|
||||||
|
|
||||||
return a.exec();
|
int result = a.exec();
|
||||||
|
|
||||||
|
DeviceConnectionManager::getInstance().record(nullptr);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !UNITTEST_MODE
|
#endif // !UNITTEST_MODE
|
||||||
|
@ -2682,7 +2682,7 @@ void MainWindow::on_actionCreate_Card_zip_triggered()
|
|||||||
infostr = datacard.loader->loaderName();
|
infostr = datacard.loader->loaderName();
|
||||||
}
|
}
|
||||||
prefix = infostr;
|
prefix = infostr;
|
||||||
folder += QDir::separator() + infostr + ".zip";
|
folder += "/" + infostr + ".zip";
|
||||||
|
|
||||||
filename = QFileDialog::getSaveFileName(this, tr("Choose where to save zip"), folder, tr("ZIP files (*.zip)"));
|
filename = QFileDialog::getSaveFileName(this, tr("Choose where to save zip"), folder, tr("ZIP files (*.zip)"));
|
||||||
|
|
||||||
@ -2727,6 +2727,56 @@ void MainWindow::on_actionCreate_Card_zip_triggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::on_actionCreate_Log_zip_triggered()
|
||||||
|
{
|
||||||
|
QString folder;
|
||||||
|
|
||||||
|
// Note: macOS ignores this and points to OSCAR's most recently used directory for saving.
|
||||||
|
folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
|
folder += "/OSCAR-logs.zip";
|
||||||
|
QString filename = QFileDialog::getSaveFileName(this, tr("Choose where to save zip"), folder, tr("ZIP files (*.zip)"));
|
||||||
|
if (filename.isEmpty()) {
|
||||||
|
return; // aborted
|
||||||
|
}
|
||||||
|
if (!filename.toLower().endsWith(".zip")) {
|
||||||
|
filename += ".zip";
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Create zip of OSCAR diagnostic logs:" << filename;
|
||||||
|
|
||||||
|
ZipFile z;
|
||||||
|
bool ok = z.Open(filename);
|
||||||
|
if (ok) {
|
||||||
|
ProgressDialog * prog = new ProgressDialog(this);
|
||||||
|
prog->setMessage(tr("Creating zip..."));
|
||||||
|
|
||||||
|
// Build the list of files.
|
||||||
|
FileQueue files;
|
||||||
|
files.AddDirectory(GetLogDir(), "logs");
|
||||||
|
|
||||||
|
// Defer the current debug log to the end.
|
||||||
|
QString debugLog = logger->logFileName();
|
||||||
|
QString debugLogZipName;
|
||||||
|
int exists = files.Remove(debugLog, &debugLogZipName);
|
||||||
|
if (exists) {
|
||||||
|
files.AddFile(debugLog, debugLogZipName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the zip.
|
||||||
|
ok = z.AddFiles(files, prog);
|
||||||
|
z.Close();
|
||||||
|
} else {
|
||||||
|
qWarning() << "Unable to open" << filename;
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
QMessageBox::warning(nullptr, STR_MessageBox_Error,
|
||||||
|
QObject::tr("Unable to create zip!"),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_actionCreate_OSCAR_Data_zip_triggered()
|
void MainWindow::on_actionCreate_OSCAR_Data_zip_triggered()
|
||||||
{
|
{
|
||||||
QString folder;
|
QString folder;
|
||||||
@ -2734,7 +2784,7 @@ void MainWindow::on_actionCreate_OSCAR_Data_zip_triggered()
|
|||||||
// Note: macOS ignores this and points to OSCAR's most recently used directory for saving.
|
// Note: macOS ignores this and points to OSCAR's most recently used directory for saving.
|
||||||
folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
folder = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
|
|
||||||
folder += QDir::separator() + STR_AppData + ".zip";
|
folder += "/" + STR_AppData + ".zip";
|
||||||
|
|
||||||
QString filename = QFileDialog::getSaveFileName(this, tr("Choose where to save zip"), folder, tr("ZIP files (*.zip)"));
|
QString filename = QFileDialog::getSaveFileName(this, tr("Choose where to save zip"), folder, tr("ZIP files (*.zip)"));
|
||||||
|
|
||||||
@ -2749,7 +2799,6 @@ void MainWindow::on_actionCreate_OSCAR_Data_zip_triggered()
|
|||||||
qDebug() << "Create zip of OSCAR data folder:" << filename;
|
qDebug() << "Create zip of OSCAR data folder:" << filename;
|
||||||
|
|
||||||
QDir oscarData(GetAppData());
|
QDir oscarData(GetAppData());
|
||||||
QFile debugLog(oscarData.canonicalPath() + QDir::separator() + "debuglog.txt");
|
|
||||||
|
|
||||||
ZipFile z;
|
ZipFile z;
|
||||||
bool ok = z.Open(filename);
|
bool ok = z.Open(filename);
|
||||||
@ -2759,29 +2808,22 @@ void MainWindow::on_actionCreate_OSCAR_Data_zip_triggered()
|
|||||||
prog->setWindowModality(Qt::ApplicationModal);
|
prog->setWindowModality(Qt::ApplicationModal);
|
||||||
prog->open();
|
prog->open();
|
||||||
|
|
||||||
// Build the list of files and exclude any existing debug log.
|
// Build the list of files.
|
||||||
FileQueue files;
|
FileQueue files;
|
||||||
files.AddDirectory(oscarData.canonicalPath(), oscarData.dirName());
|
files.AddDirectory(oscarData.canonicalPath(), oscarData.dirName());
|
||||||
files.Remove(debugLog.fileName());
|
|
||||||
|
// Defer the current debug log to the end.
|
||||||
|
QString debugLog = logger->logFileName();
|
||||||
|
QString debugLogZipName;
|
||||||
|
int exists = files.Remove(debugLog, &debugLogZipName);
|
||||||
|
if (exists) {
|
||||||
|
files.AddFile(debugLog, debugLogZipName);
|
||||||
|
}
|
||||||
|
|
||||||
prog->setMessage(tr("Creating zip..."));
|
prog->setMessage(tr("Creating zip..."));
|
||||||
|
|
||||||
// Create the zip.
|
// Create the zip.
|
||||||
ok = z.AddFiles(files, prog);
|
ok = z.AddFiles(files, prog);
|
||||||
if (ok && z.aborted() == false) {
|
|
||||||
// Update the debug log and add it last.
|
|
||||||
ok = debugLog.open(QIODevice::WriteOnly);
|
|
||||||
if (ok) {
|
|
||||||
debugLog.write(ui->logText->toPlainText().toLocal8Bit().data());
|
|
||||||
debugLog.close();
|
|
||||||
QString debugLogName = oscarData.dirName() + "/" + QFileInfo(debugLog).fileName();
|
|
||||||
ok = z.AddFile(debugLog.fileName(), debugLogName);
|
|
||||||
if (!ok) {
|
|
||||||
qWarning() << "Unable to add debug log to zip!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
z.Close();
|
z.Close();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Unable to open" << filename;
|
qWarning() << "Unable to open" << filename;
|
||||||
|
@ -344,6 +344,8 @@ class MainWindow : public QMainWindow
|
|||||||
|
|
||||||
void on_actionCreate_Card_zip_triggered();
|
void on_actionCreate_Card_zip_triggered();
|
||||||
|
|
||||||
|
void on_actionCreate_Log_zip_triggered();
|
||||||
|
|
||||||
void on_actionCreate_OSCAR_Data_zip_triggered();
|
void on_actionCreate_OSCAR_Data_zip_triggered();
|
||||||
|
|
||||||
void on_actionReport_a_Bug_triggered();
|
void on_actionReport_a_Bug_triggered();
|
||||||
|
@ -2866,6 +2866,7 @@ p, li { white-space: pre-wrap; }
|
|||||||
<addaction name="actionShow_Performance_Counters"/>
|
<addaction name="actionShow_Performance_Counters"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionCreate_Card_zip"/>
|
<addaction name="actionCreate_Card_zip"/>
|
||||||
|
<addaction name="actionCreate_Log_zip"/>
|
||||||
<addaction name="actionCreate_OSCAR_Data_zip"/>
|
<addaction name="actionCreate_OSCAR_Data_zip"/>
|
||||||
<addaction name="actionReport_a_Bug"/>
|
<addaction name="actionReport_a_Bug"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
@ -3232,6 +3233,11 @@ p, li { white-space: pre-wrap; }
|
|||||||
<string>Create zip of CPAP data card</string>
|
<string>Create zip of CPAP data card</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionCreate_Log_zip">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create zip of OSCAR diagnostic logs</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionCreate_OSCAR_Data_zip">
|
<action name="actionCreate_OSCAR_Data_zip">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Create zip of all OSCAR data</string>
|
<string>Create zip of all OSCAR data</string>
|
||||||
|
@ -500,6 +500,7 @@ lessThan(QT_MAJOR_VERSION,5)|lessThan(QT_MINOR_VERSION,9) {
|
|||||||
|
|
||||||
# Create a debug GUI build by adding "CONFIG+=memdebug" to your qmake command
|
# Create a debug GUI build by adding "CONFIG+=memdebug" to your qmake command
|
||||||
memdebug {
|
memdebug {
|
||||||
|
CONFIG += debug
|
||||||
!win32 { # add memory checking on Linux and macOS debug builds
|
!win32 { # add memory checking on Linux and macOS debug builds
|
||||||
QMAKE_CFLAGS += -g -Werror -fsanitize=address -fno-omit-frame-pointer -fno-common -fsanitize-address-use-after-scope
|
QMAKE_CFLAGS += -g -Werror -fsanitize=address -fno-omit-frame-pointer -fno-common -fsanitize-address-use-after-scope
|
||||||
lessThan(QT_MAJOR_VERSION,5)|lessThan(QT_MINOR_VERSION,9) {
|
lessThan(QT_MAJOR_VERSION,5)|lessThan(QT_MINOR_VERSION,9) {
|
||||||
|
@ -225,7 +225,7 @@ bool FileQueue::AddFile(const QString & path, const QString & prefix)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileQueue::Remove(const QString & path)
|
int FileQueue::Remove(const QString & path, QString* outName)
|
||||||
{
|
{
|
||||||
QFileInfo fi(path);
|
QFileInfo fi(path);
|
||||||
QString canonicalPath = fi.canonicalFilePath();
|
QString canonicalPath = fi.canonicalFilePath();
|
||||||
@ -235,6 +235,13 @@ int FileQueue::Remove(const QString & path)
|
|||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
Entry & entry = i.next();
|
Entry & entry = i.next();
|
||||||
if (entry.path == canonicalPath) {
|
if (entry.path == canonicalPath) {
|
||||||
|
if (outName) {
|
||||||
|
// If the caller cares about the name, it will most likely be re-added later rather than skipped.
|
||||||
|
*outName = entry.name;
|
||||||
|
} else {
|
||||||
|
qDebug().noquote() << "skipping file:" << path;
|
||||||
|
}
|
||||||
|
|
||||||
if (fi.isDir()) {
|
if (fi.isDir()) {
|
||||||
m_dir_count--;
|
m_dir_count--;
|
||||||
} else {
|
} else {
|
||||||
@ -243,10 +250,12 @@ int FileQueue::Remove(const QString & path)
|
|||||||
}
|
}
|
||||||
i.remove();
|
i.remove();
|
||||||
removed++;
|
removed++;
|
||||||
qDebug().noquote() << "skipping file:" << path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (removed > 1) {
|
||||||
|
qWarning().noquote() << removed << "copies found in zip queue:" << path;
|
||||||
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public:
|
|||||||
~FileQueue() = default;
|
~FileQueue() = default;
|
||||||
|
|
||||||
//!brief Remove a file from the queue, return the number of instances removed.
|
//!brief Remove a file from the queue, return the number of instances removed.
|
||||||
int Remove(const QString & path);
|
int Remove(const QString & path, QString* outName=nullptr);
|
||||||
|
|
||||||
//!brief Recursively add a directory and its contents to the queue along with the prefix to be used in an archive.
|
//!brief Recursively add a directory and its contents to the queue along with the prefix to be used in an archive.
|
||||||
bool AddDirectory(const QString & path, const QString & prefix="");
|
bool AddDirectory(const QString & path, const QString & prefix="");
|
||||||
|
Loading…
Reference in New Issue
Block a user