Hopefully you can all see the featured image. It’s a screen shot of our little application so far. No, I didn’t make it pretty. That is not the purpose. Here is a slightly different shot of the same screen. This is the way it looks when the system is slow and the database is being created.
The little circle with bars is an animated gif I generated on this site. When the main window catches the loading signal it executes this slot.
void MainWindow::loading() { cout << "loading() slot called " << endl; ui->busyMovieLabel->setVisible(true); ui->loadingLabel->setVisible(true); ui->genDBReportBtn->hide(); movie = new QMovie(":/main/graphics/ajax-loader.gif"); ui->busyMovieLabel->setMovie(movie); movie->start(); }
Since I’m not worried about resources this was the quick and easy method. In tiny resourced embedded systems I typically use a series of images and a timer in their own little class.
Before we jump into the code, here is the output. I hand edited it down to just the minimum so we can discuss it.
Current device count: 9 Number of possible configurations: 1 Device Class: 9 VendorID: 32903 ProductID: 36 Vendor: Product: Serial Number: Number of possible configurations: 1 Device Class: 9 VendorID: 7531 ProductID: 2 Vendor: Product: EHCI Host Controller Serial Number: 0000:00:1d.0 Number of possible configurations: 1 Device Class: 0 VendorID: 2362 ProductID: 9488 Vendor: Product: Serial Number: Number of possible configurations: 1 Device Class: 0 VendorID: 1500 ProductID: 43037 Vendor: Product: Serial Number: AA8BF02FAUF8PRH6 Number of possible configurations: 1 Device Class: 9 VendorID: 32903 ProductID: 36 Vendor: Product: Serial Number: Number of possible configurations: 1 Device Class: 9 VendorID: 7531 ProductID: 2 Vendor: Product: EHCI Host Controller Serial Number: 0000:00:1a.0 Number of possible configurations: 1 Device Class: 9 VendorID: 7531 ProductID: 3 Vendor: Product: Linux 4.13.0-43-generic xhci-hcd Serial Number: 0000:00:14.0 Number of possible configurations: 1 Device Class: 0 VendorID: 1309 ProductID: 2 Vendor: Product: Back-UPS RS 1000G FW:869.L3 .D USB FW:L3 Serial Number: 3B1241X11465 Number of possible configurations: 1 Device Class: 9 VendorID: 7531 ProductID: 2 Vendor: Product: xHCI Host Controller Serial Number: 0000:00:14.0 ============================= Number of possible configurations: 1 Device Class: 9 VendorID: 32903 Intel Corp. ProductID: 36 Integrated Rate Matching Hub Number of possible configurations: 1 Device Class: 9 VendorID: 7531 Linux Foundation ProductID: 2 2.0 root hub Number of possible configurations: 1 Device Class: 0 VendorID: 2362 Pixart Imaging, Inc. ProductID: 9488 Optical Mouse Number of possible configurations: 1 Device Class: 0 VendorID: 1500 Lexar Media, Inc. ProductID: 43037 LJDTT16G [JumpDrive 16GB] Number of possible configurations: 1 Device Class: 9 VendorID: 32903 Intel Corp. ProductID: 36 Integrated Rate Matching Hub Number of possible configurations: 1 Device Class: 9 VendorID: 7531 Linux Foundation ProductID: 2 2.0 root hub Number of possible configurations: 1 Device Class: 9 VendorID: 7531 Linux Foundation ProductID: 3 3.0 root hub Number of possible configurations: 1 Device Class: 0 VendorID: 1309 American Power Conversion ProductID: 2 Uninterruptible Power Supply Number of possible configurations: 1 Device Class: 9 VendorID: 7531 Linux Foundation ProductID: 2 2.0 root hub
Above the line of === is the trimmed output of our libusb code. Below that line is still using libusb to obtain the device descriptors, but looking up vendor and product information in a database this application creates. Yes, the file usb.ids is available on the Internet and locally on most Linux systems. Yes, there are various hashing techniques to locating values in it, but, most of those techniques are used by people who never learned how to use a database. Eventually I will get around to writing the code which pulls the file down to create the database from it, but, we aren’t there yet.
Database access and creation is being provided by a singleton class. Yes, I know there is a deep religious cult against singleton classes, but they have their uses. In a production type environment a database would provide an external service your application simply connected with and used. This application needs to be self contained and is mostly for educational purposes.
#ifndef LOGIKALUSBINFO_H #define LOGIKALUSBINFO_H /* * Copyright (c) 2018 Roland Hughes and Logical Solutions ALL RIGHTS RESERVED * * This code is "as-is" without any warranty expressed or implied. You may not modify or distribute it * without having purchased the right to do so from the copyright holder. This code is provided for * demonstration purposes only. Usage of it implies your express agreement to hold both the author and * copyright holder harmless from damages both real and perceived. */ #include #include #include #include class LogikalUSBInfo final : public QObject { Q_OBJECT public: static LogikalUSBInfo* getInstance(); void initializeDatabase(QString destDir="", QString destName="", QString usbIdsPath=""); QString fileName() { return QDir(databasePath).filePath(dbName);} void setDatabasePath(QString nuPath) { databasePath = nuPath;} void setDbName(QString nuName) { dbName = nuName;} bool isDBReady() {return (databaseInitialized && databaseIsCurrent());} QString vendorName(QString vendorID); QString deviceName(QString vendorID, QString productID); // Remove the no-nos for a singleton class // LogikalUSBInfo(LogikalUSBInfo const&) = delete; // copy constructor LogikalUSBInfo(LogikalUSBInfo&&) = delete; // move constructor LogikalUSBInfo& operator=(LogikalUSBInfo const&) = delete; // copy assign LogikalUSBInfo& operator=(LogikalUSBInfo &&) = delete; // move assign signals: void loading(); void loadingComplete(); void databaseReady(); protected: explicit LogikalUSBInfo(QObject *parent = nullptr); protected slots: void databaseLoaded(); private: bool idFileLocated( QString usbIdsPath=""); bool databaseIsCurrent(); bool databaseExists(); void createNewDatabase(); void retrieveFileAndCreateDb(); bool databaseInitialized = false; QString idFilePath; QString databasePath; QString dbName = QString("usb_id_db"); const QString driverName = QString("QSQLITE"); const QString connectionName = QString("usb_ids"); QFutureWatcher watcher; }; #endif // LOGIKALUSBINFO_H
By default, with no options passed to the iniatializeDatabase() method, the database will be created in the user’s home directory. I provided some “set” methods so you could configure the location and still call initializeDatabase with no parameters to create the database in a location of your choosing.
Our isDBReady() method along with the 3 signals provide applications using this class with the ability to know the current state of the database. In our application I use the 3 signals to control what displays on the right side of our window. When the database is loading the “loading” text label and spinning wheel appear, but the button to generate the second report is hidden. When either loading completes or the database was located in a ready state during initialization we only see the button to generate the report.
vendorName() and deviceName() provide the main API. The strings passed as parameters must be zero padded HEX values because that is how they come from usb.ids.