PulseView  unreleased development snapshot
A Qt-based sigrok GUI
devicemanager.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "devicemanager.hpp"
21 #include "session.hpp"
22 
23 #include <cassert>
24 #include <functional>
25 #include <memory>
26 #include <sstream>
27 #include <stdexcept>
28 #include <string>
29 
30 #include <libsigrokcxx/libsigrokcxx.hpp>
31 
32 #include <QApplication>
33 #include <QDebug>
34 #include <QObject>
35 #include <QProgressDialog>
36 
37 #include <boost/filesystem.hpp>
38 
40 #include <pv/util.hpp>
41 
42 using std::bind;
43 using std::list;
44 using std::map;
45 using std::placeholders::_1;
46 using std::placeholders::_2;
47 using std::shared_ptr;
48 using std::string;
49 using std::unique_ptr;
50 using std::vector;
51 
52 using Glib::VariantBase;
53 
54 using sigrok::ConfigKey;
55 using sigrok::Context;
56 using sigrok::Driver;
57 
58 namespace pv {
59 
60 DeviceManager::DeviceManager(shared_ptr<Context> context,
61  std::string driver, bool do_scan) :
62  context_(context)
63 {
64  unique_ptr<QProgressDialog> progress(new QProgressDialog("",
65  QObject::tr("Cancel"), 0, context->drivers().size() + 1));
66  progress->setWindowModality(Qt::WindowModal);
67  progress->setMinimumDuration(1); // To show the dialog immediately
68 
69  int entry_num = 1;
70 
71  /*
72  * Check the presence of an optional user spec for device scans.
73  * Determine the driver name and options (in generic format) when
74  * applicable.
75  */
76  std::string user_name;
77  vector<std::string> user_opts;
78  if (!driver.empty()) {
79  user_opts = pv::util::split_string(driver, ":");
80  user_name = user_opts.front();
81  user_opts.erase(user_opts.begin());
82  }
83 
84  /*
85  * Scan for devices. No specific options apply here, this is
86  * best effort auto detection.
87  */
88  for (auto& entry : context->drivers()) {
89  if (!do_scan)
90  break;
91 
92  // Skip drivers we won't scan anyway
93  if (!driver_supported(entry.second))
94  continue;
95 
96  progress->setLabelText(QObject::tr("Scanning for devices that driver %1 can access...")
97  .arg(QString::fromStdString(entry.first)));
98 
99  if (entry.first == user_name)
100  continue;
101  driver_scan(entry.second, map<const ConfigKey *, VariantBase>());
102 
103  progress->setValue(entry_num++);
104  QApplication::processEvents();
105  if (progress->wasCanceled())
106  break;
107  }
108 
109  /*
110  * Optionally run another scan with potentially more specific
111  * options when requested by the user. This is motivated by
112  * several different uses: It can find devices that are not
113  * covered by the above auto detection (UART, TCP). It can
114  * prefer one out of multiple found devices, and have this
115  * device pre-selected for new sessions upon user's request.
116  */
117  user_spec_device_.reset();
118  if (!driver.empty()) {
119  shared_ptr<sigrok::Driver> scan_drv;
120  map<const ConfigKey *, VariantBase> scan_opts;
121 
122  /*
123  * Lookup the device driver name.
124  */
125  map<string, shared_ptr<Driver>> drivers = context->drivers();
126  auto entry = drivers.find(user_name);
127  scan_drv = (entry != drivers.end()) ? entry->second : nullptr;
128 
129  /*
130  * Convert generic string representation of options
131  * to the driver specific data types.
132  */
133  if (scan_drv && !user_opts.empty()) {
134  auto drv_opts = scan_drv->scan_options();
135  scan_opts = drive_scan_options(user_opts, drv_opts);
136  }
137 
138  /*
139  * Run another scan for the specified driver, passing
140  * user provided scan options this time.
141  */
142  list< shared_ptr<devices::HardwareDevice> > found;
143  if (scan_drv) {
144  found = driver_scan(scan_drv, scan_opts);
145  if (!found.empty())
146  user_spec_device_ = found.front();
147  }
148  }
149  progress->setValue(entry_num++);
150 }
151 
152 const shared_ptr<sigrok::Context>& DeviceManager::context() const
153 {
154  return context_;
155 }
156 
157 shared_ptr<Context> DeviceManager::context()
158 {
159  return context_;
160 }
161 
162 const list< shared_ptr<devices::HardwareDevice> >&
164 {
165  return devices_;
166 }
167 
171 shared_ptr<devices::HardwareDevice>
173 {
174  return user_spec_device_;
175 }
176 
185 map<const ConfigKey *, Glib::VariantBase>
186 DeviceManager::drive_scan_options(vector<string> user_spec,
187  set<const ConfigKey *> driver_opts)
188 {
189  map<const ConfigKey *, Glib::VariantBase> result;
190 
191  for (auto& entry : user_spec) {
192  /*
193  * Split key=value specs. Accept entries without separator
194  * (for simplified boolean specifications).
195  */
196  string key, val;
197  size_t pos = entry.find("=");
198  if (pos == std::string::npos) {
199  key = entry;
200  val = "";
201  } else {
202  key = entry.substr(0, pos);
203  val = entry.substr(pos + 1);
204  }
205 
206  /*
207  * Skip user specifications that are not a member of the
208  * driver's set of supported options. Have the text format
209  * input spec converted to the required driver specific type.
210  */
211  const ConfigKey *cfg;
212  try {
213  cfg = ConfigKey::get_by_identifier(key);
214  if (!cfg)
215  continue;
216  if (driver_opts.find(cfg) == driver_opts.end())
217  continue;
218  } catch (...) {
219  continue;
220  }
221  result[cfg] = cfg->parse_string(val);
222  }
223 
224  return result;
225 }
226 
227 bool DeviceManager::driver_supported(shared_ptr<Driver> driver) const
228 {
229  /*
230  * We currently only support devices that can deliver samples at
231  * a fixed samplerate (i.e. oscilloscopes and logic analysers).
232  *
233  * @todo Add support for non-monotonic devices (DMMs, sensors, etc).
234  */
235  const auto keys = driver->config_keys();
236 
237  return keys.count(ConfigKey::LOGIC_ANALYZER) | keys.count(ConfigKey::OSCILLOSCOPE);
238 }
239 
240 list< shared_ptr<devices::HardwareDevice> >
242  shared_ptr<Driver> driver, map<const ConfigKey *, VariantBase> drvopts)
243 {
244  list< shared_ptr<devices::HardwareDevice> > driver_devices;
245 
246  assert(driver);
247 
248  if (!driver_supported(driver))
249  return driver_devices;
250 
251  // Remove any device instances from this driver from the device
252  // list. They will not be valid after the scan.
253  devices_.remove_if([&](shared_ptr<devices::HardwareDevice> device) {
254  return device->hardware_device()->driver() == driver; });
255 
256  try {
257  // Do the scan
258  auto devices = driver->scan(drvopts);
259 
260  // Add the scanned devices to the main list, set display names and sort.
261  for (shared_ptr<sigrok::HardwareDevice>& device : devices) {
262  const shared_ptr<devices::HardwareDevice> d(
263  new devices::HardwareDevice(context_, device));
264  driver_devices.push_back(d);
265  }
266 
267  devices_.insert(devices_.end(), driver_devices.begin(),
268  driver_devices.end());
269  devices_.sort(bind(&DeviceManager::compare_devices, this, _1, _2));
270  driver_devices.sort(bind(
271  &DeviceManager::compare_devices, this, _1, _2));
272 
273  } catch (const sigrok::Error &e) {
274  qWarning() << QApplication::tr("Error when scanning device driver '%1': %2").
275  arg(QString::fromStdString(driver->name()), e.what());
276  }
277 
278  return driver_devices;
279 }
280 
281 const map<string, string> DeviceManager::get_device_info(
282  shared_ptr<devices::Device> device)
283 {
284  map<string, string> result;
285 
286  assert(device);
287 
288  const shared_ptr<sigrok::Device> sr_dev = device->device();
289  if (sr_dev->vendor().length() > 0)
290  result["vendor"] = sr_dev->vendor();
291  if (sr_dev->model().length() > 0)
292  result["model"] = sr_dev->model();
293  if (sr_dev->version().length() > 0)
294  result["version"] = sr_dev->version();
295  if (sr_dev->serial_number().length() > 0)
296  result["serial_num"] = sr_dev->serial_number();
297  if (sr_dev->connection_id().length() > 0)
298  result["connection_id"] = sr_dev->connection_id();
299 
300  return result;
301 }
302 
303 const shared_ptr<devices::HardwareDevice> DeviceManager::find_device_from_info(
304  const map<string, string> search_info)
305 {
306  shared_ptr<devices::HardwareDevice> last_resort_dev;
307  map<string, string> dev_info;
308 
309  for (shared_ptr<devices::HardwareDevice> dev : devices_) {
310  assert(dev);
311  dev_info = get_device_info(dev);
312 
313  // If present, vendor and model always have to match.
314  if (dev_info.count("vendor") > 0 && search_info.count("vendor") > 0)
315  if (dev_info.at("vendor") != search_info.at("vendor"))
316  continue;
317 
318  if (dev_info.count("model") > 0 && search_info.count("model") > 0)
319  if (dev_info.at("model") != search_info.at("model"))
320  continue;
321 
322  // Most unique match: vendor/model/serial_num (but don't match a S/N of 0)
323  if ((dev_info.count("serial_num") > 0) && (dev_info.at("serial_num") != "0")
324  && search_info.count("serial_num") > 0)
325  if (dev_info.at("serial_num") == search_info.at("serial_num") &&
326  dev_info.at("serial_num") != "0")
327  return dev;
328 
329  // Second best match: vendor/model/connection_id
330  if (dev_info.count("connection_id") > 0 &&
331  search_info.count("connection_id") > 0)
332  if (dev_info.at("connection_id") == search_info.at("connection_id"))
333  return dev;
334 
335  // Last resort: vendor/model/version
336  if (dev_info.count("version") > 0 &&
337  search_info.count("version") > 0)
338  if (dev_info.at("version") == search_info.at("version") &&
339  dev_info.at("version") != "0")
340  return dev;
341 
342  // For this device, we merely have a vendor/model match.
343  last_resort_dev = dev;
344  }
345 
346  // If there wasn't even a vendor/model/version match, we end up here.
347  // This is usually the case for devices with only vendor/model data.
348  // The selected device may be wrong with multiple such devices attached
349  // but it is the best we can do at this point. After all, there may be
350  // only one such device and we do want to select it in this case.
351  return last_resort_dev;
352 }
353 
354 bool DeviceManager::compare_devices(shared_ptr<devices::Device> a,
355  shared_ptr<devices::Device> b)
356 {
357  assert(a);
358  assert(b);
359  return a->display_name(*this).compare(b->display_name(*this)) < 0;
360 }
361 
362 } // namespace pv
manual pdf if(NOT EXISTS"${CMAKE_CURRENT_BINARY_DIR}/images") message(STATUS"creating symlink for manual's images/ subdirectory") execute_process(COMMAND $
Definition: CMakeLists.txt:42
shared_ptr< devices::HardwareDevice > user_spec_device_
bool compare_devices(shared_ptr< devices::Device > a, shared_ptr< devices::Device > b)
const shared_ptr< devices::HardwareDevice > find_device_from_info(const map< string, string > search_info)
list< shared_ptr< devices::HardwareDevice > > devices_
const shared_ptr< sigrok::Context > & context() const
const list< shared_ptr< devices::HardwareDevice > > & devices() const
DeviceManager(shared_ptr< sigrok::Context > context, std::string driver, bool do_scan)
const map< string, string > get_device_info(const shared_ptr< devices::Device > device)
vector< string > split_string(string text, string separator)
Definition: util.cpp:268
static map< const ConfigKey *, Glib::VariantBase > drive_scan_options(vector< string > user_spec, set< const ConfigKey * > driver_opts)
shared_ptr< sigrok::Context > context_
list< shared_ptr< devices::HardwareDevice > > driver_scan(shared_ptr< sigrok::Driver > driver, map< const sigrok::ConfigKey *, Glib::VariantBase > drvopts)
bool driver_supported(shared_ptr< sigrok::Driver > driver) const
shared_ptr< devices::HardwareDevice > user_spec_device() const