From 58d8e4c6c50bb119d405d754a6fb122d932d6510 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sun, 25 Jun 2017 19:56:33 +0200 Subject: [PATCH] device manager: Add support for -d cmdline option (driver scan options) 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 | 12 +++- pv/devicemanager.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++- pv/devicemanager.hpp | 14 ++++- 3 files changed, 148 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index ce7db5fa..f7c722b1 100644 --- 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 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); diff --git a/pv/devicemanager.cpp b/pv/devicemanager.cpp index ac6ce6fb..15e791e1 100644 --- a/pv/devicemanager.cpp +++ b/pv/devicemanager.cpp @@ -36,6 +36,7 @@ #include #include +#include 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) : +DeviceManager::DeviceManager(shared_ptr context, std::string driver) : context_(context) { unique_ptr 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 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()); progress->setValue(entry_num++); @@ -75,6 +98,48 @@ DeviceManager::DeviceManager(shared_ptr 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 scan_drv; + map scan_opts; + + /* + * Lookup the device driver name. + */ + map> 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 > 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& 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 +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 +DeviceManager::drive_scan_options(vector user_spec, + set driver_opts) +{ + map 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 > DeviceManager::driver_scan( shared_ptr driver, map drvopts) diff --git a/pv/devicemanager.hpp b/pv/devicemanager.hpp index 43d93a7a..2c934300 100644 --- a/pv/devicemanager.hpp +++ b/pv/devicemanager.hpp @@ -23,12 +23,16 @@ #include #include #include +#include #include +#include 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 context); + DeviceManager(shared_ptr context, std::string driver); ~DeviceManager() = default; @@ -61,6 +67,7 @@ public: shared_ptr context(); const list< shared_ptr >& devices() const; + shared_ptr user_spec_device() const; list< shared_ptr > driver_scan( shared_ptr driver, @@ -76,9 +83,14 @@ private: bool compare_devices(shared_ptr a, shared_ptr b); + static map + drive_scan_options(vector user_spec, + set driver_opts); + protected: shared_ptr context_; list< shared_ptr > devices_; + shared_ptr user_spec_device_; }; } // namespace pv -- 2.30.2