device manager: Add support for -d cmdline option (driver scan options)
authorGerhard Sittig <gerhard.sittig@gmx.net>
Sun, 25 Jun 2017 17:56:33 +0000 (19:56 +0200)
committerUwe Hermann <uwe@hermann-uwe.de>
Tue, 27 Jun 2017 11:20:06 +0000 (13:20 +0200)
The previous implementation had support to auto-detect devices and to
connect to and pick devices by filling in dialogs, optionally providing
scan options that did not apply to auto-detection. This commit extends
the existing support by introducing a -d command line option similar to
sigrok-cli.

In the absence of the -d command line option, behaviour is identical to
the previous implementation. When -d is provided, the specified driver
is excluded from the auto-detection phase, and another scan is executed
afterwards where the user specified scan options take effect. This shall
result in least interaction and highest reliability of device detection,
while flexibility is increased.

Here are examples of what the -d command line option can do:

  $ pulseview -d ols:conn=/dev/ttyACM0
  $ pulseview -d fx2lafw
  $ pulseview -d demo:logic_channels=32:analog_channels=8

This fixes bug #953.

main.cpp
pv/devicemanager.cpp
pv/devicemanager.hpp

index ce7db5fadcc2a18a05d567a0ac284cfe7065595f..f7c722b1aec63a6e47d440a565bb23824fadd461 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -66,6 +66,7 @@ void usage()
                "Application Options:\n"
                "  -V, --version                   Show release version\n"
                "  -l, --loglevel                  Set libsigrok/libsigrokdecode loglevel\n"
+               "  -d, --driver                    Specify the device driver to use\n"
                "  -i, --input-file                Load input from file\n"
                "  -I, --input-format              Input format\n"
                "  -c, --clean                     Don't restore previous sessions on startup\n"
@@ -76,7 +77,7 @@ int main(int argc, char *argv[])
 {
        int ret = 0;
        shared_ptr<sigrok::Context> context;
-       string open_file, open_file_format;
+       string open_file, open_file_format, driver;
        bool restore_sessions = true;
 
        Application a(argc, argv);
@@ -93,6 +94,7 @@ int main(int argc, char *argv[])
                        {"help", no_argument, nullptr, 'h'},
                        {"version", no_argument, nullptr, 'V'},
                        {"loglevel", required_argument, nullptr, 'l'},
+                       {"driver", required_argument, nullptr, 'd'},
                        {"input-file", required_argument, nullptr, 'i'},
                        {"input-format", required_argument, nullptr, 'I'},
                        {"clean", no_argument, nullptr, 'c'},
@@ -100,7 +102,7 @@ int main(int argc, char *argv[])
                };
 
                const int c = getopt_long(argc, argv,
-                       "l:Vhc?i:I:", long_options, nullptr);
+                       "l:Vhc?d:i:I:", long_options, nullptr);
                if (c == -1)
                        break;
 
@@ -132,6 +134,10 @@ int main(int argc, char *argv[])
                        break;
                }
 
+               case 'd':
+                       driver = optarg;
+                       break;
+
                case 'i':
                        open_file = optarg;
                        break;
@@ -174,7 +180,7 @@ int main(int argc, char *argv[])
 
                try {
                        // Create the device manager, initialise the drivers
-                       pv::DeviceManager device_manager(context);
+                       pv::DeviceManager device_manager(context, driver);
 
                        // Initialise the main window
                        pv::MainWindow w(device_manager);
index ac6ce6fb433da3fa29eefa9ba551c043c70f63b5..15e791e128eb8e542ec705459be51d4ec41dd5ba 100644 (file)
@@ -36,6 +36,7 @@
 #include <boost/filesystem.hpp>
 
 #include <pv/devices/hardwaredevice.hpp>
+#include <pv/util.hpp>
 
 using std::bind;
 using std::list;
@@ -45,7 +46,10 @@ using std::placeholders::_2;
 using std::shared_ptr;
 using std::string;
 using std::unique_ptr;
+using std::vector;
 
+using Glib::ustring;
+using Glib::Variant;
 using Glib::VariantBase;
 
 using sigrok::ConfigKey;
@@ -54,20 +58,39 @@ using sigrok::Driver;
 
 namespace pv {
 
-DeviceManager::DeviceManager(shared_ptr<Context> context) :
+DeviceManager::DeviceManager(shared_ptr<Context> context, std::string driver) :
        context_(context)
 {
        unique_ptr<QProgressDialog> progress(new QProgressDialog("",
-               QObject::tr("Cancel"), 0, context->drivers().size()));
+               QObject::tr("Cancel"), 0, context->drivers().size() + 1));
        progress->setWindowModality(Qt::WindowModal);
        progress->setMinimumDuration(1);  // To show the dialog immediately
 
        int entry_num = 1;
 
+       /*
+        * Check the presence of an optional user spec for device scans.
+        * Determine the driver name and options (in generic format) when
+        * applicable.
+        */
+       std::string user_name;
+       vector<std::string> user_opts;
+       if (!driver.empty()) {
+               user_opts = pv::util::split_string(driver, ":");
+               user_name = user_opts.front();
+               user_opts.erase(user_opts.begin());
+       }
+
+       /*
+        * Scan for devices. No specific options apply here, this is
+        * best effort auto detection.
+        */
        for (auto entry : context->drivers()) {
                progress->setLabelText(QObject::tr("Scanning for %1...")
                        .arg(QString::fromStdString(entry.first)));
 
+               if (entry.first == user_name)
+                       continue;
                driver_scan(entry.second, map<const ConfigKey *, VariantBase>());
 
                progress->setValue(entry_num++);
@@ -75,6 +98,48 @@ DeviceManager::DeviceManager(shared_ptr<Context> context) :
                if (progress->wasCanceled())
                        break;
        }
+
+       /*
+        * Optionally run another scan with potentially more specific
+        * options when requested by the user. This is motivated by
+        * several different uses: It can find devices that are not
+        * covered by the above auto detection (UART, TCP). It can
+        * prefer one out of multiple found devices, and have this
+        * device pre-selected for new sessions upon user's request.
+        */
+       user_spec_device_.reset();
+       if (!driver.empty()) {
+               shared_ptr<sigrok::Driver> scan_drv;
+               map<const ConfigKey *, VariantBase> scan_opts;
+
+               /*
+                * Lookup the device driver name.
+                */
+               map<string, shared_ptr<Driver>> drivers = context->drivers();
+               auto entry = drivers.find(user_name);
+               scan_drv = (entry != drivers.end()) ? entry->second : nullptr;
+
+               /*
+                * Convert generic string representation of options
+                * to the driver specific data types.
+                */
+               if (scan_drv && !user_opts.empty()) {
+                       auto drv_opts = scan_drv->scan_options();
+                       scan_opts = drive_scan_options(user_opts, drv_opts);
+               }
+
+               /*
+                * Run another scan for the specified driver, passing
+                * user provided scan options this time.
+                */
+               list< shared_ptr<devices::HardwareDevice> > found;
+               if (scan_drv) {
+                       found = driver_scan(scan_drv, scan_opts);
+                       if (!found.empty())
+                               user_spec_device_ = found.front();
+               }
+       }
+       progress->setValue(entry_num++);
 }
 
 const shared_ptr<sigrok::Context>& DeviceManager::context() const
@@ -93,6 +158,65 @@ DeviceManager::devices() const
        return devices_;
 }
 
+/**
+ * Get the device that was detected with user provided scan options.
+ */
+shared_ptr<devices::HardwareDevice>
+DeviceManager::user_spec_device() const
+{
+       return user_spec_device_;
+}
+
+/**
+ * Convert generic options to data types that are specific to Driver::scan().
+ *
+ * @param[in] user_spec vector of tokenized words, string format
+ * @param[in] driver_opts driver's scan options, result of Driver::scan_options()
+ *
+ * @return map of options suitable for Driver::scan()
+ */
+map<const ConfigKey *, Glib::VariantBase>
+DeviceManager::drive_scan_options(vector<string> user_spec,
+       set<const ConfigKey *> driver_opts)
+{
+       map<const ConfigKey *, Glib::VariantBase> result;
+
+       for (auto entry : user_spec) {
+               /*
+                * Split key=value specs. Accept entries without separator
+                * (for simplified boolean specifications).
+                */
+               string key, val;
+               size_t pos = entry.find("=");
+               if (pos == std::string::npos) {
+                       key = entry;
+                       val = "";
+               } else {
+                       key = entry.substr(0, pos);
+                       val = entry.substr(pos + 1);
+               }
+
+               /*
+                * Skip user specifications that are not a member of the
+                * driver's set of supported options. Have the text format
+                * input spec converted to the required driver specific type.
+                */
+               const ConfigKey *cfg;
+               try {
+                       cfg = ConfigKey::get_by_identifier(key);
+                       if (!cfg)
+                               continue;
+                       if (driver_opts.find(cfg) == driver_opts.end())
+                               continue;
+               } catch (...) {
+                       continue;
+               }
+               result[cfg] = cfg->parse_string(val);
+       }
+
+       return result;
+}
+
 list< shared_ptr<devices::HardwareDevice> >
 DeviceManager::driver_scan(
        shared_ptr<Driver> driver, map<const ConfigKey *, VariantBase> drvopts)
index 43d93a7a88f5778d0bed24bcead6ec1f2e9dc85e..2c9343001211409e8e836d95c9d5d623417ebcd2 100644 (file)
 #include <list>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
+#include <vector>
 
 using std::list;
 using std::map;
+using std::set;
 using std::shared_ptr;
 using std::string;
+using std::vector;
 
 namespace Glib {
 class VariantBase;
@@ -40,6 +44,8 @@ class Context;
 class Driver;
 }
 
+using sigrok::ConfigKey;
+
 namespace pv {
 
 namespace devices {
@@ -52,7 +58,7 @@ class Session;
 class DeviceManager
 {
 public:
-       DeviceManager(shared_ptr<sigrok::Context> context);
+       DeviceManager(shared_ptr<sigrok::Context> context, std::string driver);
 
        ~DeviceManager() = default;
 
@@ -61,6 +67,7 @@ public:
        shared_ptr<sigrok::Context> context();
 
        const list< shared_ptr<devices::HardwareDevice> >& devices() const;
+       shared_ptr<devices::HardwareDevice> user_spec_device() const;
 
        list< shared_ptr<devices::HardwareDevice> > driver_scan(
                shared_ptr<sigrok::Driver> driver,
@@ -76,9 +83,14 @@ private:
        bool compare_devices(shared_ptr<devices::Device> a,
                shared_ptr<devices::Device> b);
 
+       static map<const ConfigKey *, Glib::VariantBase>
+       drive_scan_options(vector<string> user_spec,
+               set<const ConfigKey *> driver_opts);
+
 protected:
        shared_ptr<sigrok::Context> context_;
        list< shared_ptr<devices::HardwareDevice> > devices_;
+       shared_ptr<devices::HardwareDevice> user_spec_device_;
 };
 
 } // namespace pv