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.
This commit is contained in:
Joel Holdsworth 2013-04-27 21:53:21 +01:00
parent f9541bde55
commit 107ca6d350
8 changed files with 275 additions and 58 deletions

View File

@ -100,6 +100,7 @@ configure_file (
set(pulseview_SOURCES
main.cpp
pv/devicemanager.cpp
pv/mainwindow.cpp
pv/sigsession.cpp
pv/data/analog.cpp

View File

@ -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

135
pv/devicemanager.cpp Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is part of the PulseView project.
*
* Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
*
* 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 <cassert>
#include <cstring>
#include <stdexcept>
#include <string>
#include <libsigrok/libsigrok.h>
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<sr_dev_inst*>& DeviceManager::devices() const
{
return _devices;
}
list<sr_dev_inst*> DeviceManager::driver_scan(
struct sr_dev_driver *const driver, GSList *const drvopts)
{
list<sr_dev_inst*> driver_devices;
assert(driver);
// Remove any device instances from this driver from the device
// list. They will not be valid after the scan.
list<sr_dev_inst*>::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

64
pv/devicemanager.h Normal file
View File

@ -0,0 +1,64 @@
/*
* This file is part of the PulseView project.
*
* Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
*
* 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 <glib.h>
#include <list>
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<sr_dev_inst*>& devices() const;
std::list<sr_dev_inst*> 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<sr_dev_inst*> _devices;
};
} // namespace pv
#endif // PULSEVIEW_PV_DEVICEMANAGER_H

View File

@ -18,8 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <boost/foreach.hpp>
#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 <libsigrok/libsigrok.h>
}
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<sr_dev_inst*> 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);
}

View File

@ -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;

View File

@ -23,6 +23,7 @@
#endif
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <QAction>
#include <QApplication>
@ -36,6 +37,8 @@
#include <QWidget>
#include "mainwindow.h"
#include "devicemanager.h"
#include "dialogs/about.h"
#include "dialogs/connect.h"
#include "toolbars/samplingbar.h"
@ -49,12 +52,15 @@
#include <glib.h>
#include <libsigrok/libsigrok.h>
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<sr_dev_inst*> &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()

View File

@ -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<sr_dev_inst*> _devices;
pv::view::View *_view;