Index: denali.pro =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- denali.pro (.../denali.pro) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ denali.pro (.../denali.pro) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -287,9 +287,9 @@ sources/view/settings/VAdjustmentDGCleaningUsage.h \ sources/view/settings/VDuetRoWaterDG.h \ sources/view/settings/VCloudSync.h \ - sources/model/settings/MSDCardFile.h \ sources/view/settings/VLogSortFilterProxyModel.h \ - sources/view/settings/VSDCardFilesModel.h \ + sources/model/settings/MLogFile.h \ + sources/view/settings/VLogFilesModel.h \ \ # ---------- Views - Alarm sources/view/hd/alarm/VAlarmStatus.h \ sources/view/hd/alarm/VAlarmActiveList.h \ @@ -555,7 +555,7 @@ sources/view/settings/VCloudSync.cpp \ sources/view/settings/VAdjustmentDGCleaningUsage.cpp \ sources/view/settings/VLogSortFilterProxyModel.cpp \ - sources/view/settings/VSDCardFilesModel.cpp \ + sources/view/settings/VLogFilesModel.cpp \ \ # ---------- Views - HD - Adjustment sources/view/VAdjustmentResponseBase.cpp \ \ # ---------- Views - HD - Adjustment - common Index: sources/device/DeviceController.cpp =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -63,6 +63,12 @@ initConnections(); startTimer(_interval); + // Add watch for event driven detection of USB insertion/removal + doAddDirectoryWatch(Storage::USB_Dev_Path); + + // Do initial mount check on start-up + usbCheck(); + return true; } @@ -189,6 +195,7 @@ vDevice = device; return true; // application is deciding on the first existing drive } + device = ""; } vDevice = device; return false; @@ -231,11 +238,10 @@ * \details This event handler has been re-implemented in here * to receive timer events for the object * for the timer which has been set to _checkInterval - * Runs the usbCheck on interval */ void DeviceController::timerEvent(QTimerEvent *) { - usbCheck(); + // DO NOT run usbCheck() on interval to avoid performance impact since models are regenerated sdcardSpaceCheck(); } @@ -246,17 +252,16 @@ */ void DeviceController::usbCheck() { - QString device = ""; - usbSpaceCheck(); - if (usbSeek(device)) { - if (! _umounted ) { // avoid to mount the USB which has just been unmounted - usbMount(device); - } else { // the umount is requested? - usbUmount(USB_Mount_Point); - } + QString currentUSBDevice; + bool found = usbSeek(currentUSBDevice); + //DEBUG: qDebug()<< "Current usb device found " <start(_command, _arguments); } +public slots : + void onProcessErrored(QProcess::ProcessError err) { + LOG_DEBUG("TimedProcess error " + err); + } protected: void timerEvent(QTimerEvent *) { @@ -97,9 +103,6 @@ // friends friend class ::tst_initializations; - bool _mounted = false; - bool _umounted = false; - bool _removed = false; const int _interval = 1000; // in ms const qint8 _minRequiredAvailableSpacePercent = 10; @@ -113,6 +116,7 @@ QString _macWireless = ""; QString _macBluetooth = ""; QString _netCloudSync = ""; + QString _lastMountedDevice = ""; bool _hasThread = false; bool _hasSalt = false; Index: sources/gui/GuiGlobals.cpp =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -49,7 +49,7 @@ #include "VBluetooth.h" #include "VDuetRoWaterDG.h" #include "VCloudSync.h" -#include "VSDCardFilesModel.h" +#include "VLogFilesModel.h" #include "VLogSortFilterProxyModel.h" // states data #include "VHDOperationModeData.h" Index: sources/gui/qml/main.qml =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/gui/qml/main.qml (.../main.qml) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/gui/qml/main.qml (.../main.qml) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -46,8 +46,9 @@ import VBluetooth 0.1 import VDuetRoWaterDG 0.1 import VCloudSync 0.1 -import VSDCardFilesModel 0.1 -import VLogSortFilterProxyModel 0.1 +import VSDCardLogFilesModel 0.1 +import VUSBLogFilesModel 0.1 +import VLogSortFilterProxyModel 0.1 // States views import VHDOperationMode 0.1 import VPreTreatmentStates 0.1 @@ -148,10 +149,16 @@ VBluetooth { id: vBluetooth } VDuetRoWaterDG { id: vDuetRoWaterDG } VCloudSync { id: vCloudSync } - VSDCardFilesModel { id: vSDCardFiles } + + VSDCardLogFilesModel { id: vSDCardFilesSrcModel } VLogSortFilterProxyModel {id: vSDCardLogProxyModel - sourceModel: vSDCardFiles + sourceModel: vSDCardFilesSrcModel } + VUSBLogFilesModel { id: vUSBFilesSrcModel } + VLogSortFilterProxyModel {id: vUSBLogProxyModel + sourceModel: vUSBFilesSrcModel + } + // ---- States VHDOperationMode { id: vHDOperationMode onStandbyChanged: { Index: sources/gui/qml/pages/settings/SettingsExportLogs.qml =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/gui/qml/pages/settings/SettingsExportLogs.qml (.../SettingsExportLogs.qml) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/gui/qml/pages/settings/SettingsExportLogs.qml (.../SettingsExportLogs.qml) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -51,7 +51,7 @@ readonly property string devUnit : "MB" // no translation readonly property string devUnitLabel : " (" + devUnit + ")\n" // no translation readonly property string usbLabel : qsTr("USB Drive") - readonly property int headetRowHight : 50 + readonly property int headerRowHeight : 50 readonly property int typeIndexApplication : 0 readonly property int typeIndexService : 1 @@ -61,36 +61,9 @@ readonly property string typeLabelService : qsTr("Service" ) readonly property string typeLabelTreatment : qsTr("Treatment" ) - property bool isDevice : true //false // it has to be true to make it work on the devices. - readonly property string typePathClr : "" - readonly property string typePathPrefix : "file://" - readonly property string typePathSrc : typePathPrefix + (isDevice ? "/media/sd-card" : "/home/denali/Desktop/sd-card" ) - readonly property string typePathDst : typePathPrefix + (isDevice ? "/media/usb" : "/home/denali/Desktop/usb-disk") - readonly property string typeFolderApplication : "/log" - readonly property string typeFolderService : "/service" - readonly property string typeFolderTreatment : "/treatment" - - readonly property string typeFolderApplicationSrc : typePathSrc + typeFolderApplication - readonly property string typeFolderApplicationDst : typePathDst + typeFolderApplication - readonly property string typeFolderServiceSrc : typePathSrc + typeFolderService - readonly property string typeFolderServiceDst : typePathDst + typeFolderService - readonly property string typeFolderTreatmentSrc : typePathSrc + typeFolderTreatment - readonly property string typeFolderTreatmentDst : typePathDst + typeFolderTreatment - - readonly property var typeFilterAll : ["*"] - readonly property var typeFilterClr : [] - - confirmVisible : false - function refreshModels() { - _usbFolderColumn.clearModel() - _usbFolderColumn.updateModel() - } - function doExport() { - refreshModels() - switch (_logTypeCombo.currentIndex) { case typeIndexApplication: _GuiView.doExportLog() @@ -102,42 +75,20 @@ _GuiView.doExportTreatment() break } - - refreshModels() } property bool isUpdatePanels: false function updatePanels (vIndex) { isUpdatePanels = true -// TODO remove also when give USB a CPP model _GuiView.doExportListRemove() - _usbFolderColumn.clearModel() - - switch (vIndex) { - case typeIndexApplication: - _usbFolderColumn.currentTypeFolderApplication = typeFolderApplicationDst - break - case typeIndexService: - _usbFolderColumn.currentTypeFolderApplication = typeFolderServiceDst - break - case typeIndexTreatment: - _usbFolderColumn.currentTypeFolderApplication = typeFolderTreatmentDst - break - } - - _usbFolderColumn.updateModel() - isUpdatePanels = false } Connections { target: _GuiView - function onSdIsReadyChanged ( vValue ) { _root.updatePanels() } function onDidExportStat ( vIndex, vFileName, vPercent ) { _sdcFolderView.positionViewAtIndex(vIndex, ListView.Center) } - function onDidUSBDriveUmount ( ) { _usbFolderColumn. clearModel ( ) } - function onDidUSBDriveRemove ( ) { _usbFolderColumn. clearModel ( ) } - function onDidExportLog ( vValue ) { _usbFolderColumn. clearModel ( ) } - function onDidUSBDriveMount ( ) { _usbFolderColumn.updateModel ( ) } - function onDidExport ( ) { _usbFolderColumn.updateModel ( ) } + +// function onDidExportLog ( vValue ) { vUSBFilesSrcModel.removeAllRows() } +// function onDidExport ( ) { vUSBFilesSrcModel.refreshModel ( ) } } USBButton { id: _usbEjectButton @@ -175,14 +126,18 @@ Row { id : _logTypeRow anchors.left : parent.left width : parent.width - height : _root.headetRowHight + height : _root.headerRowHeight spacing : _logTypeCombo.width - _logTypeExportButton.width // FIXME: This combobox needs to be a global Component ComboBox { id : _logTypeCombo onCurrentIndexChanged : { - //_root.updatePanels(currentIndex) - vLogSortFilterProxyModel.setFilterLogType(currentIndex) + // Update the view to display the selected log type + vSDCardLogProxyModel.setFilterLogType(currentIndex) + vUSBLogProxyModel.setFilterLogType(currentIndex) + + // clear the export list of selection since changing log type viewing + _GuiView.doExportListRemove() //DEBUG console.log(">>>Index " + currentIndex) } enabled : ! _GuiView.exportRunning && ! isUpdatePanels @@ -300,15 +255,14 @@ } } Column { id : _sdcFolderColumn - property string currentTypeFolderApplication : _root.typeFolderApplicationSrc spacing : 5 width : _contentRect.columnWidthFolder height : parent.height anchors.verticalCenter : parent.verticalCenter Label { id : _sdcLabel text : _root.sdcLabel + ": %1 files"/*, %2 %3"*/.arg(_sdcFolderView.count) + (_GuiView.exportCount ? " [Selected: %1]".arg(_GuiView.exportCount ) : "") //.arg("__").arg(_root.dvcUnit) width : parent.width - height : _root.headetRowHight + height : _root.headerRowHeight verticalAlignment : Text.AlignVCenter } Rectangle { id : _sdcFolderRectangle @@ -352,8 +306,8 @@ _GuiView.doExportListDelete( index ) } else { - _GuiView.doExportListInsert( index , fileName ) - console.log("doExportListInsert ( " + index + "," +fileName +") filePath: " + filePath) + _GuiView.doExportListInsert( index , filePath+"/"+fileName ) + //DEBUG console.log("doExportListInsert ( " + index + "," +filePath+"/"+fileName +") filePath: " + filePath) } } @@ -417,47 +371,15 @@ } Column { id : _usbFolderColumn - property string currentTypeFolderApplication : _root.typeFolderApplicationDst - - // FIXME: there has to be a View for this, and the timer should be removed and an event driven signal should be implemented there. - function updateModel() { - _usbFolderModel.folder = currentTypeFolderApplication // FIXME: there has to be a View for this which also get changed by log type. - _usbFolderModel.nameFilters = _root.typeFilterAll - } - function clearModel() { - _usbFolderModel.folder = _root.typePathClr - _usbFolderModel.nameFilters = _root.typeFilterClr - } - - // FIXME: there has to be a View for this, and the timer should be removed and an event driven signal should be implemented there. - Timer { id: _usbUpdate - interval : 500 - repeat : true - running : _GuiView.exportRunning - onRunningChanged: { - if(!running) { - // do one last update to get the correct file size else the USB file size is displayed - // as being smaller than the same file on SD card - _usbFolderColumn. clearModel() - _usbFolderColumn.updateModel() - } - } - - onTriggered : { - _usbFolderColumn. clearModel() - _usbFolderColumn.updateModel() - } - } - spacing : 5 width : _contentRect.columnWidthFolder height : parent.height anchors.verticalCenter : parent.verticalCenter Label { id : _usbLabel - text : _root.usbLabel + ": %1 files"/*", %2 %3"*/.arg(_usbFolderModel.count)//.arg("__").arg(_root.dvcUnit) + text : _root.usbLabel + ": %1 files"/*", %2 %3"*/.arg(vUSBLogProxyModel.count)//.arg("__").arg(_root.dvcUnit) width : parent.width - height : _root.headetRowHight + height : _root.headerRowHeight verticalAlignment : Text.AlignVCenter } @@ -484,11 +406,6 @@ anchors.leftMargin : 5 anchors.rightMargin : 5 - FolderListModel { id : _usbFolderModel // FIXME: I don't like this model, it's too lazy and I don't have control over it. There has to be a Model for this. - showDirs : false - sortField : FolderListModel.Time - folder : _usbFolderColumn.currentTypeFolderApplication - } Component { id : _usbFileDelegate Row { id : _usbFileRow width : _usbFolderView.width @@ -510,15 +427,15 @@ Text { id : _usbFileSizeText clip : true width : _contentRect.columnWidthFileSize - text : Variables.sizeConverted( fileSize, 1000, 3) + text : Variables.sizeConverted( fileSize_bytes, 1000, 3) color : Colors.textMain font.pixelSize : Fonts.fontPixelTextRectExtra verticalAlignment : Text.AlignVCenter horizontalAlignment : Text.AlignRight } } } - model : _usbFolderModel + model : vUSBLogProxyModel delegate : _usbFileDelegate } } Index: sources/gui/qml/pages/settings/SettingsStack.qml =================================================================== diff -u -ra53bdf104b52cae3a49705a4cc8bd886530084c9 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/gui/qml/pages/settings/SettingsStack.qml (.../SettingsStack.qml) (revision a53bdf104b52cae3a49705a4cc8bd886530084c9) +++ sources/gui/qml/pages/settings/SettingsStack.qml (.../SettingsStack.qml) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -177,7 +177,8 @@ break case SettingsStack.ExportLogs: - vSDCardFiles.doInit() + vSDCardFilesSrcModel.doInit() + vUSBFilesSrcModel .doInit() push( _settingsExportLogs ) break Index: sources/model/settings/MLogFile.h =================================================================== diff -u --- sources/model/settings/MLogFile.h (revision 0) +++ sources/model/settings/MLogFile.h (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -0,0 +1,72 @@ +/*! + * + * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file MLogFile.h + * \author (last) Vy + * \date (last) 18-Jul-2023 + * \author (original) Vy + * \date (original) 18-Jul-2023 + * + */ +#pragma once + +// Qt +#include +#include +#include + +// Project +#include "StorageGlobals.h" +#include "Logger.h" + +// forward declarations +class tst_models; + +namespace Model { + + +/*! + * \brief The Log File class + * Provides abstraction for a Log File + * \details Holds information about the Log File such as filename and size + * + */ +class MLogFile { + +public: + struct Data + { + + public: + bool operator==(const Data &d1) { + const QString otherFilePath(QString("%1/%2").arg(d1.filePath()).arg(d1.fileName())); + QFileInfo otherFile(otherFilePath); + + if(_fileInfo.exists() && otherFile.exists()) + return _fileInfo == otherFile; + return false; + } + + explicit Data() {} + explicit Data(Storage::Logger::LogType vLogType, const QFileInfo &vFileInfo) : _fileInfo(vFileInfo), _logType(vLogType) { } + explicit Data(const QString &vFilePath) : _fileInfo(QFileInfo(vFilePath)) { } + QString filePath() const { return _fileInfo.path(); } + QString fileName() const { return _fileInfo.fileName (); } + qint64 fileSize_bytes() const { return _fileInfo.size (); } + int logType () const { return _logType; } + QDateTime lastModificationDateTime() const {return _fileInfo.fileTime(QFile::FileModificationTime);} + + private: + QFileInfo _fileInfo; + Storage::Logger::LogType _logType; + }; + +}; + +} + +typedef Model::MLogFile::Data LogFileData; Fisheye: Tag 02350a71e85f193287e6c999e816dc5ced6966f2 refers to a dead (removed) revision in file `sources/model/settings/MSDCardFile.h'. Fisheye: No comparison available. Pass `N' to diff? Index: sources/storage/FileHandler.h =================================================================== diff -u -r80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/storage/FileHandler.h (.../FileHandler.h) (revision 80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803) +++ sources/storage/FileHandler.h (.../FileHandler.h) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -91,8 +91,8 @@ FileCopyError_Enums err = eOK; QDir srcDir = QFileInfo (vSource ).absoluteDir(); QDir dstDir = QFileInfo (vDestination ).absoluteDir(); - QFile srcFile = QFile (vSource + vFileName); - QFile dstFile = QFile (vDestination + vFileName); + QFile srcFile = QFile (vSource +"/"+ vFileName); + QFile dstFile = QFile (vDestination +"/"+ vFileName); qint64 totalSize = 0; qint64 copySize = 0; quint32 chunkSize = 1024 * 2; @@ -158,7 +158,7 @@ return eOK; lErr: - qDebug() << "\nError: " << err; + qDebug() << "\nFileHandler::copyFile Error: " << err; return err; } Index: sources/storage/Logger.cpp =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/storage/Logger.cpp (.../Logger.cpp) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/storage/Logger.cpp (.../Logger.cpp) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -635,10 +635,11 @@ Gui::GuiStringIndexMapIterator it(vExportList); while ( it.hasNext() ) { it.next(); - // qDebug() << it.key() << it.value() << mSource << mDestination; + //DEBUG qDebug() << it.key() << it.value() << mSource << mDestination; auto index = it.key (); - auto filename = it.value(); - result = FileHandler::copyFile(mSource, mDestination + _logBasePathNames[vLogType], filename, ¬ifier, index); + auto filePath = it.value(); + QFileInfo file(filePath); + result = FileHandler::copyFile(file.path(), mDestination + _logBasePathNames[vLogType], file.fileName(), ¬ifier, index); } } return result >= 0; // refer to QProcess::execute(hit F1 on execute) doc. Index: sources/storage/StorageGlobals.cpp =================================================================== diff -u -r80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision 80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803) +++ sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -186,5 +186,6 @@ // USB unmount/ mount const char *USB_Unmount = "usb_unmount.sh"; const char *USB_Mount = "usb_mount.sh"; + const char *USB_Dev_Path = "/dev"; } Index: sources/storage/StorageGlobals.h =================================================================== diff -u -r80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision 80b5e8f1ebb90c03c37d90d90cd2da3bd95d6803) +++ sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -129,5 +129,5 @@ // USB mount/unmount extern const char *USB_Unmount; extern const char *USB_Mount; - + extern const char *USB_Dev_Path; } Index: sources/view/VView.h =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/view/VView.h (.../VView.h) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/view/VView.h (.../VView.h) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -120,7 +120,8 @@ REGISTER_TYPE( VBluetooth ) \ REGISTER_TYPE( VDuetRoWaterDG ) \ REGISTER_TYPE( VCloudSync ) \ - REGISTER_TYPE( VSDCardFilesModel ) \ + REGISTER_TYPE( VSDCardLogFilesModel ) \ + REGISTER_TYPE( VUSBLogFilesModel ) \ REGISTER_TYPE( VLogSortFilterProxyModel ) \ /* Alarm */ \ REGISTER_TYPE( VAlarmStatus ) \ Index: sources/view/settings/VLogFilesModel.cpp =================================================================== diff -u --- sources/view/settings/VLogFilesModel.cpp (revision 0) +++ sources/view/settings/VLogFilesModel.cpp (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -0,0 +1,249 @@ +/*! + * + * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file VLogFilesModel.cpp + * \author (last) Vy + * \date (last) 16-May-2023 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ + +// Qt +#include + +// Project +#include "VLogFilesModel.h" +#include "Logger.h" +#include "StorageGlobals.h" +#include "DeviceController.h" + + +using namespace View; + +VLogFilesModel::VLogFilesModel(QAbstractListModel *parent) : QAbstractListModel(parent) { + initConnections(); +} + +/*! + * \brief VLogFilesModel::initConnections + * Makes the necessary connections. Called inside VIEW_DEF_CLASS + */ +void VLogFilesModel::initConnections() +{ + connect(&_DeviceController , SIGNAL(didWatchDirectoryChange (const QString &)), + this , SLOT( onWatchDirectoryChange (const QString &))); + + // The model gets a total refresh when Logger class emits didRemoveLogs + connect(&_Logger , SIGNAL( didRemoveLogs (bool)), + this , SLOT(onTotalModelRefresh (bool))); +} + +/*! + * \brief VLogFilesModel::doInit + * \details Initializes the view or what needs to be done + */ +void VLogFilesModel::doInit() +{ + refreshModel(); +} + +/*! + * \brief VLogFilesModel::rowCount + * Gets the number of networks + * \param parent - (QModelIndex) the parent QModelIndex + * \return (int) - the number of networks + */ +int VLogFilesModel::rowCount(const QModelIndex & parent) const +{ + Q_UNUSED(parent); + return _logFiles.count(); +} + +/*! + * \brief VLogFilesModel::data + * Returns the sd card file properties at the specified index + * \param index (QModelIndex) contains the row of data to lookup + * \param role - (int) the property index to return. See SDCardFileDataRole + * \return (QVariant) - the value for the specified network property + */ +QVariant VLogFilesModel::data(const QModelIndex & index, int role) const +{ + if (index.row() < 0 || index.row() >= _logFiles.count()) return QVariant(); + + const LogFileData &logFile = _logFiles[index.row()]; + switch (role) + { + case FilePathRole: + return logFile.filePath(); + case FileNameRole: + return logFile.fileName(); + case FileSizeRole: + return logFile.fileSize_bytes(); + case FileLogTypeRole : + return logFile.logType(); + case FileModificationDateTimeRole: + return logFile.lastModificationDateTime(); + } + + return QVariant(); +} + +/*! + * \brief VLogFilesModel::roleNames + * Translates how to access specific properties of the data for QML from the NetworkDataRole enum + * \return (QHash) - maps enums to property names + */ +QHash VLogFilesModel::roleNames() const { + QHash roles; + roles[FilePathRole] = "filePath"; + roles[FileNameRole] = "fileName"; + roles[FileSizeRole] = "fileSize_bytes"; + roles[FileLogTypeRole] = "fileLogType"; + roles[FileModificationDateTimeRole] = "fileModDateTime"; + return roles; +} +QList VLogFilesModel::getLogListFromDir(Storage::Logger::LogType vType, const QString &vDirectory, bool vCheckPreInclude){ + QList temp; + QDirIterator dirIterator(vDirectory, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + QString absolutePath; + while(dirIterator.hasNext()) { + absolutePath = dirIterator.next(); + if(!dirIterator.fileName().isEmpty()) + { + LogFileData currentLog(vType, dirIterator.fileInfo()); + if(!vCheckPreInclude || !_logFiles.contains(currentLog)) + { + //DEBUG qDebug() << " adding " << absolutePath + dirIterator.fileName(); + temp.append(currentLog); + } + } + } + return temp; +} + +void VLogFilesModel::updateLogModel(const QString &vDirectory) +{ + Storage::Logger::LogType type = _Logger.getLogType(vDirectory); + //DEBUG: qDebug() << Q_FUNC_INFO << " dir changed : " << vDirectory << " type " << type; + + QList temp = getLogListFromDir(type, vDirectory, true); + + if(temp.count() == 0) return; // No new files + + // Update the model + beginInsertRows(QModelIndex(), _logFiles.count(), _logFiles.count() + temp.count()); + _logFiles.append(temp); + endInsertRows(); +} + +void VLogFilesModel::refreshModel() +{ qDebug()< logTypes = Storage::Logger::getLogTypeList(); + foreach(Storage::Logger::LogType type , logTypes) + { + QString folderPath = QString("%1%2").arg(Storage::Log_Folder_Base).arg(_Logger.getPathOfLogType(type)); + + // Add to systemWatcher to update for changes + _DeviceController.doAddDirectoryWatch(folderPath); + + _logFiles.append(getLogListFromDir(type, folderPath)); + + } +} + +// ============================== VUSBLogFilesModel + +VUSBLogFilesModel::VUSBLogFilesModel(QAbstractListModel *parent) + : VLogFilesModel(parent) +{ + // add some additional connections + initConnections(); +} + +void VUSBLogFilesModel::initConnections() +{ + connect(&_DeviceController , SIGNAL( didUSBDriveMount ()), + this , SLOT( refreshModel ())); + connect(&_DeviceController , SIGNAL( didUSBDriveUmount ()), + this , SLOT( removeAllRows ())); + connect(&_DeviceController , SIGNAL( didUSBDriveRemove ()), + this , SLOT( removeAllRows ())); +} + +void VUSBLogFilesModel::updateModelOnDirChange(const QString &vDirectory) +{ + if(vDirectory.contains(Storage::USB_Mount_Point)) { + // Indicates that the folder change is related to the USB mount folder + updateLogModel(vDirectory); + } +} + +void VUSBLogFilesModel::populateLogModel() +{ + const QList logTypes = Storage::Logger::getLogTypeList(); + foreach(Storage::Logger::LogType type , logTypes) + { + QString folderPath = QString("%1%2").arg(Storage::USB_Mount_Point).arg(_Logger.getPathOfLogType(type)); + + // Add to systemWatcher to update for changes + _DeviceController.doAddDirectoryWatch(folderPath); + + _logFiles.append(getLogListFromDir(type, folderPath)); + } +} Index: sources/view/settings/VLogFilesModel.h =================================================================== diff -u --- sources/view/settings/VLogFilesModel.h (revision 0) +++ sources/view/settings/VLogFilesModel.h (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -0,0 +1,103 @@ +/*! + * + * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file VLogFilesModel.h + * \author (last) Behrouz NematiPour + * \date (last) 02-Feb-2023 + * \author (original) Behrouz NematiPour + * \date (original) 11-May-2021 + * + */ +#pragma once + +// Qt +#include +#include +#include + +// Project +#include "main.h" +#include "VView.h" +#include "MLogFile.h" + +// forward declarations +class tst_views; + +namespace View { + +/*! + * \brief The VLogFilesModel class + * Interface between QML and the log files + * \details Exposes log files to QML and provides an interface to list and select the files + * References: https://doc.qt.io/qt-5.12/qtquick-modelviewsdata-cppmodels.html + * + */ + +class VLogFilesModel : public QAbstractListModel +{ + Q_OBJECT +public: + // Note: VIEW_DEC_CLASS(VLogFilesModel) requires QObject as the parent, so it's necessary to define it here + // Otherwise a VIEW_DEC_CLASS macro could allow specifying the parent class with QObject as the default + explicit VLogFilesModel(QAbstractListModel *parent = nullptr); + + enum LogFileDataRole { + FilePathRole = Qt::UserRole + 1 , + FileNameRole , + FileSizeRole , + FileLogTypeRole , + FileModificationDateTimeRole , + }; + + int rowCount (const QModelIndex &parent = QModelIndex()) const; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; + +public slots: + void doInit(); + void refreshModel(); + void removeAllRows(); + +private slots: + void onWatchDirectoryChange(const QString &vDirectory); + void onTotalModelRefresh(bool vIsStillProcessing); + +protected: + virtual void updateModelOnDirChange(const QString &vDirectory) = 0; + + virtual void initConnections(); + void updateLogModel(const QString &vDirectory); + QList getLogListFromDir(Storage::Logger::LogType vType, const QString &vDirectory, bool vCheckPreInclude = false); + + QHash roleNames() const; + QList _logFiles; + +private: + virtual void populateLogModel(); +}; + +class VSDCardLogFilesModel : public VLogFilesModel +{ +public: + explicit VSDCardLogFilesModel(QAbstractListModel *parent = nullptr); +protected: + virtual void updateModelOnDirChange(const QString &vDirectory) override; +private: + virtual void populateLogModel() override; +}; + +class VUSBLogFilesModel : public VLogFilesModel +{ +public: + explicit VUSBLogFilesModel(QAbstractListModel *parent = nullptr); +protected: + virtual void initConnections() override; + virtual void updateModelOnDirChange(const QString &vDirectory) override; + +private: + virtual void populateLogModel() override; +}; +} Index: sources/view/settings/VLogSortFilterProxyModel.cpp =================================================================== diff -u -r61f87c52ccba9ce140fd8039915e025b6c790575 -r02350a71e85f193287e6c999e816dc5ced6966f2 --- sources/view/settings/VLogSortFilterProxyModel.cpp (.../VLogSortFilterProxyModel.cpp) (revision 61f87c52ccba9ce140fd8039915e025b6c790575) +++ sources/view/settings/VLogSortFilterProxyModel.cpp (.../VLogSortFilterProxyModel.cpp) (revision 02350a71e85f193287e6c999e816dc5ced6966f2) @@ -18,7 +18,7 @@ // Project #include "VLogSortFilterProxyModel.h" -#include "VSDCardFilesModel.h" +#include "VLogFilesModel.h" using namespace View; @@ -27,6 +27,7 @@ { setDynamicSortFilter(true); // enable automatic sorting when source model changes (eg: file add/removed) + // set sort and initial filter-on type sort(0, Qt::DescendingOrder); setFilterLogType(0); } @@ -42,14 +43,14 @@ { // column index to match index placement of FileLogType in SDCardFileDataRole QModelIndex modelCellLogType = sourceModel()->index(source_row, 0, source_parent); - int rowDataLogType = sourceModel()->data(modelCellLogType, VSDCardFilesModel::FileLogTypeRole).toInt(); + int rowDataLogType = sourceModel()->data(modelCellLogType, VLogFilesModel::FileLogTypeRole).toInt(); return isLogOfType(rowDataLogType); } bool VLogSortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { - QVariant leftData = sourceModel()->data(source_left, VSDCardFilesModel::FileModificationDateTimeRole); - QVariant rightData = sourceModel()->data(source_right, VSDCardFilesModel::FileModificationDateTimeRole); + QVariant leftData = sourceModel()->data( source_left, VLogFilesModel::FileModificationDateTimeRole); + QVariant rightData = sourceModel()->data(source_right, VLogFilesModel::FileModificationDateTimeRole); // sorting by the last modification date return leftData.toDateTime() < rightData.toDateTime(); Fisheye: Tag 02350a71e85f193287e6c999e816dc5ced6966f2 refers to a dead (removed) revision in file `sources/view/settings/VSDCardFilesModel.cpp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 02350a71e85f193287e6c999e816dc5ced6966f2 refers to a dead (removed) revision in file `sources/view/settings/VSDCardFilesModel.h'. Fisheye: No comparison available. Pass `N' to diff?