From: Joel Holdsworth Date: Sat, 27 Apr 2013 20:53:21 +0000 (+0100) Subject: Added DeviceManager X-Git-Tag: pulseview-0.1.0~13 X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=commitdiff_plain;h=107ca6d350b51186d12dac9273f6ed130b3f5dd7 Added DeviceManager This class now manages the application device list, scanning for devices, and releasing them when they are no longer needed. This fixes bug #108. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index d673cf81..226cd363 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ configure_file ( set(pulseview_SOURCES main.cpp + pv/devicemanager.cpp pv/mainwindow.cpp pv/sigsession.cpp pv/data/analog.cpp diff --git a/main.cpp b/main.cpp index 715dc526..1761df6e 100644 --- a/main.cpp +++ b/main.cpp @@ -34,6 +34,7 @@ #include "signalhandler.h" #endif +#include "pv/devicemanager.h" #include "pv/mainwindow.h" #include "config.h" @@ -128,20 +129,12 @@ int main(int argc, char *argv[]) srd_decoder_load_all(); #endif - // Initialize all libsigrok drivers - sr_dev_driver **const drivers = sr_driver_list(); - for (sr_dev_driver **driver = drivers; *driver; driver++) { - if (sr_driver_init(sr_ctx, *driver) != SR_OK) { - qDebug("Failed to initialize driver %s", - (*driver)->name); - ret = 1; - break; - } - } + try { + // Create the device manager, initialise the drivers + pv::DeviceManager device_manager(sr_ctx); - if (ret == 0) { // Initialise the main window - pv::MainWindow w(open_file); + pv::MainWindow w(device_manager, open_file); w.show(); #ifdef ENABLE_SIGNALS @@ -162,6 +155,9 @@ int main(int argc, char *argv[]) // Run the application ret = a.exec(); + + } catch(std::exception e) { + qDebug() << e.what(); } #ifdef ENABLE_SIGROKDECODE diff --git a/pv/devicemanager.cpp b/pv/devicemanager.cpp new file mode 100644 index 00000000..4018c047 --- /dev/null +++ b/pv/devicemanager.cpp @@ -0,0 +1,135 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2013 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "devicemanager.h" + +#include +#include +#include +#include + +#include + +using namespace std; + +namespace pv { + +DeviceManager::DeviceManager(struct sr_context *sr_ctx) : + _sr_ctx(sr_ctx) +{ + init_drivers(); + scan_all_drivers(); +} + +DeviceManager::~DeviceManager() +{ + release_devices(); +} + +const std::list& DeviceManager::devices() const +{ + return _devices; +} + +list DeviceManager::driver_scan( + struct sr_dev_driver *const driver, GSList *const drvopts) +{ + list driver_devices; + + assert(driver); + + // Remove any device instances from this driver from the device + // list. They will not be valid after the scan. + list::iterator i = _devices.begin(); + while (i != _devices.end()) { + if ((*i)->driver == driver) + i = _devices.erase(i); + else + i++; + } + + // Clear all the old device instances from this driver + sr_dev_clear(driver); + + // Do the scan + GSList *const devices = sr_driver_scan(driver, drvopts); + for (GSList *l = devices; l; l = l->next) + driver_devices.push_back((sr_dev_inst*)l->data); + g_slist_free(devices); + driver_devices.sort(compare_devices); + + // Add the scanned devices to the main list + _devices.insert(_devices.end(), driver_devices.begin(), + driver_devices.end()); + _devices.sort(compare_devices); + + return driver_devices; +} + +void DeviceManager::init_drivers() +{ + // Initialise all libsigrok drivers + sr_dev_driver **const drivers = sr_driver_list(); + for (sr_dev_driver **driver = drivers; *driver; driver++) { + if (sr_driver_init(_sr_ctx, *driver) != SR_OK) { + throw runtime_error( + string("Failed to initialize driver ") + + string((*driver)->name)); + } + } +} + +void DeviceManager::release_devices() +{ + sr_dev_driver **const drivers = sr_driver_list(); + for (sr_dev_driver **driver = drivers; *driver; driver++) + sr_dev_clear(*driver); +} + +void DeviceManager::scan_all_drivers() +{ + // Scan all drivers for all devices. + struct sr_dev_driver **const drivers = sr_driver_list(); + for (struct sr_dev_driver **driver = drivers; *driver; driver++) + driver_scan(*driver); +} + +bool DeviceManager::compare_devices(const sr_dev_inst *const a, + const sr_dev_inst *const b) +{ + assert(a); + assert(b); + + const int vendor_cmp = strcasecmp(a->vendor, b->vendor); + if(vendor_cmp < 0) + return true; + else if(vendor_cmp > 0) + return false; + + const int model_cmp = strcasecmp(a->model, b->model); + if(model_cmp < 0) + return true; + else if(model_cmp > 0) + return false; + + return strcasecmp(a->version, b->version) < 0; +} + +} // namespace pv diff --git a/pv/devicemanager.h b/pv/devicemanager.h new file mode 100644 index 00000000..070c3cc7 --- /dev/null +++ b/pv/devicemanager.h @@ -0,0 +1,64 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2013 Joel Holdsworth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PULSEVIEW_PV_DEVICEMANAGER_H +#define PULSEVIEW_PV_DEVICEMANAGER_H + +#include + +#include + +struct sr_context; +struct sr_dev_driver; +struct sr_dev_inst; + +namespace pv { + +class DeviceManager +{ +public: + DeviceManager(struct sr_context *sr_ctx); + + ~DeviceManager(); + + const std::list& devices() const; + + std::list driver_scan( + struct sr_dev_driver *const driver, + GSList *const drvopts = NULL); + +private: + void init_drivers(); + + static void release_devices(); + + void scan_all_drivers(); + + static bool compare_devices(const sr_dev_inst *const a, + const sr_dev_inst *const b); + +private: + struct sr_context *const _sr_ctx; + std::list _devices; +}; + +} // namespace pv + +#endif // PULSEVIEW_PV_DEVICEMANAGER_H diff --git a/pv/dialogs/connect.cpp b/pv/dialogs/connect.cpp index 02941f2f..eefd41b7 100644 --- a/pv/dialogs/connect.cpp +++ b/pv/dialogs/connect.cpp @@ -18,8 +18,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "connect.h" +#include "pv/devicemanager.h" + extern "C" { /* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */ #define __STDC_FORMAT_MACROS @@ -27,13 +31,16 @@ extern "C" { #include } +using namespace std; + extern sr_context *sr_ctx; namespace pv { namespace dialogs { -Connect::Connect(QWidget *parent) : +Connect::Connect(QWidget *parent, pv::DeviceManager &device_manager) : QDialog(parent), + _device_manager(device_manager), _layout(this), _form(this), _form_layout(&_form), @@ -148,11 +155,12 @@ void Connect::scan_pressed() drvopts = g_slist_append(drvopts, src); } - GSList *const devices = sr_driver_scan(driver, drvopts); + const list devices = _device_manager.driver_scan( + driver, drvopts); - for (GSList *l = devices; l; l = l->next) { + g_slist_free_full(drvopts, (GDestroyNotify)free_drvopts); - sr_dev_inst *const sdi = (sr_dev_inst*)l->data; + BOOST_FOREACH(sr_dev_inst *const sdi, devices) { QString text; if (sdi->vendor && sdi->vendor[0]) @@ -172,9 +180,6 @@ void Connect::scan_pressed() _device_list.addItem(item); } - g_slist_free(devices); - g_slist_free_full(drvopts, (GDestroyNotify)free_drvopts); - _device_list.setCurrentRow(0); _button_box.button(QDialogButtonBox::Ok)->setDisabled(false); } diff --git a/pv/dialogs/connect.h b/pv/dialogs/connect.h index eeb108c1..257efd35 100644 --- a/pv/dialogs/connect.h +++ b/pv/dialogs/connect.h @@ -34,6 +34,9 @@ struct sr_config; struct sr_dev_inst; namespace pv { + +class DeviceManager; + namespace dialogs { class Connect : public QDialog @@ -41,7 +44,7 @@ class Connect : public QDialog Q_OBJECT public: - Connect(QWidget *parent); + Connect(QWidget *parent, pv::DeviceManager &device_manager); struct sr_dev_inst* get_selected_device() const; @@ -61,6 +64,8 @@ private: static void free_drvopts(sr_config *src); private: + pv::DeviceManager &_device_manager; + QVBoxLayout _layout; QWidget _form; diff --git a/pv/mainwindow.cpp b/pv/mainwindow.cpp index bd2f5fea..16147b14 100644 --- a/pv/mainwindow.cpp +++ b/pv/mainwindow.cpp @@ -23,6 +23,7 @@ #endif #include +#include #include #include @@ -36,6 +37,8 @@ #include #include "mainwindow.h" + +#include "devicemanager.h" #include "dialogs/about.h" #include "dialogs/connect.h" #include "toolbars/samplingbar.h" @@ -49,12 +52,15 @@ #include #include +using namespace std; namespace pv { -MainWindow::MainWindow(const char *open_file_name, +MainWindow::MainWindow(DeviceManager &device_manager, + const char *open_file_name, QWidget *parent) : - QMainWindow(parent) + QMainWindow(parent), + _device_manager(device_manager) { setup_ui(); if (open_file_name) { @@ -189,12 +195,7 @@ void MainWindow::setup_ui() _sampling_bar = new toolbars::SamplingBar(this); // Populate the device list and select the initially selected device - scan_devices(); - if(!_devices.empty()) { - struct sr_dev_inst *const initial_sdi = _devices.front(); - _sampling_bar->set_selected_device(initial_sdi); - _session.set_device(initial_sdi); - } + update_device_list(); connect(_sampling_bar, SIGNAL(device_selected()), this, SLOT(device_selected())); @@ -212,23 +213,6 @@ void MainWindow::setup_ui() } -void MainWindow::scan_devices() -{ - _devices.clear(); - - /* Scan all drivers for all devices. */ - struct sr_dev_driver **const drivers = sr_driver_list(); - for (struct sr_dev_driver **driver = drivers; *driver; driver++) { - GSList *const devices = sr_driver_scan(*driver, NULL); - for (GSList *l = devices; l; l = l->next) - _devices.push_back((sr_dev_inst*)l->data); - g_slist_free(devices); - } - - assert(_sampling_bar); - _sampling_bar->set_device_list(_devices); -} - void MainWindow::session_error( const QString text, const QString info_text) { @@ -237,6 +221,30 @@ void MainWindow::session_error( Q_ARG(QString, info_text)); } +void MainWindow::update_device_list(struct sr_dev_inst *selected_device) +{ + assert(_sampling_bar); + + const list &devices = _device_manager.devices(); + _sampling_bar->set_device_list(devices); + + if (!selected_device && !devices.empty()) { + // Fall back to the first device in the list. + selected_device = devices.front(); + + // Try and find the demo device and select that by default + BOOST_FOREACH (struct sr_dev_inst *sdi, devices) + if (strcmp(sdi->driver->name, "demo") == 0) { + selected_device = sdi; + } + } + + if (selected_device) { + _sampling_bar->set_selected_device(selected_device); + _session.set_device(selected_device); + } +} + void MainWindow::load_file(QString file_name) { const QString errorMessage( @@ -269,20 +277,12 @@ void MainWindow::on_actionOpen_triggered() void MainWindow::on_actionConnect_triggered() { - dialogs::Connect dlg(this); + dialogs::Connect dlg(this, _device_manager); if (!dlg.exec()) return; struct sr_dev_inst *const sdi = dlg.get_selected_device(); - if (sdi) { - assert(_sampling_bar); - - _devices.push_back(sdi); - _sampling_bar->set_device_list(_devices); - _sampling_bar->set_selected_device(sdi); - - _session.set_device(sdi); - } + update_device_list(sdi); } void MainWindow::on_actionQuit_triggered() diff --git a/pv/mainwindow.h b/pv/mainwindow.h index 81290203..02b792ea 100644 --- a/pv/mainwindow.h +++ b/pv/mainwindow.h @@ -37,6 +37,8 @@ class QWidget; namespace pv { +class DeviceManager; + namespace toolbars { class SamplingBar; } @@ -50,15 +52,24 @@ class MainWindow : public QMainWindow Q_OBJECT public: - explicit MainWindow(const char *open_file_name = NULL, + explicit MainWindow(DeviceManager &device_manager, + const char *open_file_name = NULL, QWidget *parent = 0); private: void setup_ui(); - void scan_devices(); void session_error(const QString text, const QString info_text); + /** + * Updates the device list in the sampling bar, and updates the + * selection. + * @param selected_device The device to select, or NULL if the + * first device in the device list should be selected. + */ + void update_device_list( + struct sr_dev_inst *selected_device = NULL); + private slots: void load_file(QString file_name); @@ -86,9 +97,9 @@ private slots: void capture_state_changed(int state); private: + DeviceManager &_device_manager; SigSession _session; - std::list _devices; pv::view::View *_view;