Commit 4a1617f43c5bf752d800c4d5fb75d1ec995f3c70

Authored by David Graeff
Committed by David Gräff
1 parent 3ccfb5b2

Rework device selection

* Use own QDialog class SelectDevice and clean up main.cpp.
* USBDevice: Introduce a unique usb id for a connected device
* USBDevice: Introduce iteration field, to remember in which find round a device was found
cmake/CPackInfos.cmake
@@ -44,13 +44,17 @@ else() @@ -44,13 +44,17 @@ else()
44 ) 44 )
45 45
46 set(ENV{LANG} "en_US") 46 set(ENV{LANG} "en_US")
47 - execute_process(  
48 - COMMAND ${GIT_EXECUTABLE} log -n 10 "--date=format:%a %b %d %Y" "--pretty=format:* %ad %aN <%aE> %h - %s"  
49 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}  
50 - RESULT_VARIABLE CMD_RESULT  
51 - OUTPUT_VARIABLE CHANGELOG  
52 - OUTPUT_STRIP_TRAILING_WHITESPACE  
53 - ) 47 + if(GIT_VERSION_STRING VERSION_LESS 2.6)
  48 + set(CHANGELOG "")
  49 + else()
  50 + execute_process(
  51 + COMMAND ${GIT_EXECUTABLE} log -n 10 "--date=format:%a %b %d %Y" "--pretty=format:* %ad %aN <%aE> %h - %s"
  52 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  53 + RESULT_VARIABLE CMD_RESULT
  54 + OUTPUT_VARIABLE CHANGELOG
  55 + OUTPUT_STRIP_TRAILING_WHITESPACE
  56 + )
  57 + endif()
54 file(WRITE "${CMAKE_BINARY_DIR}/changelog" "${CHANGELOG}") 58 file(WRITE "${CMAKE_BINARY_DIR}/changelog" "${CHANGELOG}")
55 endif() 59 endif()
56 60
openhantek/res/application.qrc
1 <RCC> 1 <RCC>
2 <qresource prefix="/"> 2 <qresource prefix="/">
3 <file alias="openhantek.png">images/openhantek.png</file> 3 <file alias="openhantek.png">images/openhantek.png</file>
  4 + <file alias="switch.png">images/switch.png</file>
4 </qresource> 5 </qresource>
5 <qresource prefix="/actions"> 6 <qresource prefix="/actions">
6 <file alias="open.png">images/actions/open.png</file> 7 <file alias="open.png">images/actions/open.png</file>
openhantek/res/images/switch.png 0 → 100644

120 KB

openhantek/src/main.cpp
1 // SPDX-License-Identifier: GPL-2.0+ 1 // SPDX-License-Identifier: GPL-2.0+
2 2
3 -#include <QApplication>  
4 #include <QDebug> 3 #include <QDebug>
5 -#include <QDesktopWidget> 4 +#include <QApplication>
6 #include <QLibraryInfo> 5 #include <QLibraryInfo>
7 -#include <QListWidget>  
8 #include <QLocale> 6 #include <QLocale>
9 -#include <QMessageBox>  
10 -#include <QPushButton>  
11 -#include <QTimer>  
12 #include <QTranslator> 7 #include <QTranslator>
13 -#include <QVBoxLayout> 8 +
14 #include <iostream> 9 #include <iostream>
15 #include <memory> 10 #include <memory>
16 -  
17 #include <libusb-1.0/libusb.h> 11 #include <libusb-1.0/libusb.h>
18 12
19 #include "analyse/dataanalyzer.h" 13 #include "analyse/dataanalyzer.h"
20 #include "hantekdsocontrol.h" 14 #include "hantekdsocontrol.h"
21 #include "mainwindow.h" 15 #include "mainwindow.h"
22 #include "settings.h" 16 #include "settings.h"
23 -#include "usb/finddevices.h"  
24 -#include "usb/uploadFirmware.h"  
25 #include "usb/usbdevice.h" 17 #include "usb/usbdevice.h"
26 #include "dsomodel.h" 18 #include "dsomodel.h"
  19 +#include "selectdevice/selectdevice.h"
27 20
28 using namespace Hantek; 21 using namespace Hantek;
29 22
30 -void showMessage(const QString &message) {  
31 - QMessageBox::information(nullptr, QCoreApplication::translate("", "No connection established!"), message);  
32 -}  
33 -  
34 /// \brief Initialize resources and translations and show the main window. 23 /// \brief Initialize resources and translations and show the main window.
35 int main(int argc, char *argv[]) { 24 int main(int argc, char *argv[]) {
36 //////// Set application information //////// 25 //////// Set application information ////////
@@ -53,97 +42,17 @@ int main(int argc, char *argv[]) { @@ -53,97 +42,17 @@ int main(int argc, char *argv[]) {
53 } 42 }
54 43
55 //////// Find matching usb devices //////// 44 //////// Find matching usb devices ////////
56 - libusb_context *context; 45 + libusb_context *context = nullptr;
57 int error = libusb_init(&context); 46 int error = libusb_init(&context);
58 -  
59 if (error) { 47 if (error) {
60 - showMessage(QCoreApplication::translate("", "Can't initalize USB: %1").arg(libUsbErrorString(error))); 48 + SelectDevice().showLibUSBFailedDialogModel(error);
61 return -1; 49 return -1;
62 } 50 }
  51 + std::unique_ptr<USBDevice> device = SelectDevice().showSelectDeviceModal(context);
63 52
64 - FindDevices findDevices;  
65 - std::list<std::unique_ptr<USBDevice>> devices = findDevices.findDevices();  
66 -  
67 - if (devices.empty()) {  
68 - showMessage(QCoreApplication::translate("", "No Hantek oscilloscope found. Please check if your "  
69 - "device is supported by this software, is connected, "  
70 - "in the right mode (oscilloscope mode) and if the "  
71 - "driver is correctly installed. Refer to the <a "  
72 - "href='https://github.com/OpenHantek/openhantek/"  
73 - "'>website</a> for help: %1")  
74 - .arg(findDevices.getErrorMessage()));  
75 - return -1;  
76 - }  
77 -  
78 - //////// Upload firmwares for all connected devices ////////  
79 - for (const auto &i : devices) {  
80 - QString modelName = QString::fromStdString(i->getModel()->name);  
81 - if (i->needsFirmware()) {  
82 - UploadFirmware uf;  
83 - uf.startUpload(i.get());  
84 - }  
85 - }  
86 - devices.clear();  
87 -  
88 -#define TR(str) QCoreApplication::translate("Firmware upload dialog", str)  
89 -  
90 - //////// Select device - Autoselect if only one device is ready ////////  
91 - std::unique_ptr<QDialog> dialog = std::unique_ptr<QDialog>(new QDialog);  
92 - QListWidget *w = new QListWidget(dialog.get());  
93 -  
94 - devices = findDevices.findDevices();  
95 - for (auto &i : devices) {  
96 - QString modelName = QString::fromStdString(i->getModel()->name);  
97 -  
98 - if (i->needsFirmware()) {  
99 - if (UploadFirmware().startUpload(&*i)) {  
100 - w->addItem(TR("%1: Upload failed").arg(modelName));  
101 - } else {  
102 - w->addItem(TR("%1: Upload failed").arg(modelName));  
103 -  
104 - }  
105 - continue;  
106 - }  
107 - QString errorMessage;  
108 - if (i->connectDevice(errorMessage)) {  
109 - w->addItem(TR("%1: Ready").arg(modelName));  
110 - w->setCurrentRow(w->count() - 1);  
111 - } else {  
112 - w->addItem(TR("%1: %2").arg(modelName).arg(findDevices.getErrorMessage()));  
113 - }  
114 - }  
115 -  
116 - if (w->currentRow() == -1 || devices.size() > 1) {  
117 - QPushButton *btn = new QPushButton(QCoreApplication::translate("", "Connect to first device"), dialog.get());  
118 - dialog->move(QApplication::desktop()->screen()->rect().center() - w->rect().center());  
119 - dialog->setWindowTitle(QCoreApplication::translate("", "Firmware upload"));  
120 - dialog->setLayout(new QVBoxLayout());  
121 - dialog->layout()->addWidget(w);  
122 - dialog->layout()->addWidget(btn);  
123 - btn->connect(btn, &QPushButton::clicked, QCoreApplication::instance(), &QCoreApplication::quit);  
124 - dialog->show();  
125 - openHantekApplication.exec();  
126 - dialog->close();  
127 - }  
128 - int selectedDevice = w->currentRow();  
129 - dialog.reset(nullptr);  
130 -  
131 - std::unique_ptr<USBDevice> device;  
132 - int indexCounter = 0;  
133 - for (auto &i : devices) {  
134 - if (indexCounter == selectedDevice) {  
135 - device = std::move(i);  
136 - break;  
137 - }  
138 - }  
139 - devices.clear();  
140 -  
141 - if (device == nullptr || device->needsFirmware() || !device->isConnected()) {  
142 - showMessage(QCoreApplication::translate("", "A device was found, but the "  
143 - "firmware upload seem to have "  
144 - "failed or the connection "  
145 - "could not be established: %1")  
146 - .arg(findDevices.getErrorMessage())); 53 + QString errorMessage;
  54 + if (device == nullptr || !device->connectDevice(errorMessage)) {
  55 + libusb_exit(context);
147 return -1; 56 return -1;
148 } 57 }
149 58
openhantek/src/selectdevice/devicelistentry.h 0 → 100644
  1 +#pragma once
  2 +
  3 +#include <QString>
  4 +#include "usb/usbdevice.h"
  5 +
  6 +/**
  7 + * Represents an entry in the {@link DevicesListModel}.
  8 + */
  9 +struct DeviceListEntry {
  10 + UniqueUSBid id;
  11 + QString name;
  12 + bool canConnect;
  13 + bool needFirmware;
  14 + QString errorMessage;
  15 + QString getStatus() const {
  16 + return errorMessage.size()? errorMessage : (canConnect?"Ready":(needFirmware?"Firmware upload":"Cannot connect"));
  17 + }
  18 +};
openhantek/src/selectdevice/deviceslistmodel.cpp 0 → 100644
  1 +#include "deviceslistmodel.h"
  2 +#include "usb/finddevices.h"
  3 +#include "usb/uploadFirmware.h"
  4 +#include "dsomodel.h"
  5 +#include <QColor>
  6 +
  7 +DevicesListModel::DevicesListModel(FindDevices *findDevices) :findDevices(findDevices) {}
  8 +
  9 +int DevicesListModel::rowCount(const QModelIndex &) const
  10 +{
  11 + return entries.size();
  12 +}
  13 +
  14 +int DevicesListModel::columnCount(const QModelIndex &parent) const
  15 +{
  16 + return 2;
  17 +}
  18 +
  19 +QVariant DevicesListModel::headerData(int section, Qt::Orientation orientation, int role) const
  20 +{
  21 + if (orientation == Qt::Vertical)
  22 + return QAbstractTableModel::headerData(section, orientation, role);
  23 + if (role == Qt::DisplayRole) {
  24 + switch(section) {
  25 + case 0: return tr("Devicename");
  26 + case 1: return tr("Status");
  27 + default: return QVariant();
  28 + }
  29 + }
  30 + return QAbstractTableModel::headerData(section, orientation, role);
  31 +}
  32 +
  33 +QVariant DevicesListModel::data(const QModelIndex &index, int role) const
  34 +{
  35 + if (!index.isValid()) return QVariant();
  36 + if (role==Qt::UserRole) return QVariant::fromValue(entries[index.row()].id);
  37 + else if (role==Qt::UserRole+1) return QVariant::fromValue(entries[index.row()].canConnect);
  38 + else if (role==Qt::UserRole+2) return QVariant::fromValue(entries[index.row()].needFirmware);
  39 +
  40 + if (role == Qt::DisplayRole) {
  41 + if (index.column() == 0) {
  42 + return entries[index.row()].name;
  43 + } else if (index.column() == 1) {
  44 + return entries[index.row()].getStatus();
  45 + }
  46 + }
  47 +
  48 + if (role == Qt::BackgroundRole) {
  49 + if (entries[index.row()].canConnect) return QColor(Qt::darkGreen).lighter();
  50 + else if (entries[index.row()].needFirmware) return QColor(Qt::yellow).lighter();
  51 + }
  52 +
  53 + return QVariant();
  54 +}
  55 +
  56 +void DevicesListModel::updateDeviceList()
  57 +{
  58 + beginResetModel();
  59 + entries.clear();
  60 + endResetModel();
  61 + const FindDevices::DeviceList* devices = findDevices->getDevices();
  62 + beginInsertRows(QModelIndex(),0,devices->size());
  63 + for (auto &i : *devices) {
  64 + DeviceListEntry entry;
  65 + entry.name= QString::fromStdString(i.second->getModel()->name);
  66 + entry.id = i.first;
  67 + if (i.second->needsFirmware()) {
  68 + UploadFirmware uf;
  69 + if (!uf.startUpload(i.second.get())) {
  70 + entry.errorMessage = uf.getErrorMessage();
  71 + }
  72 + entry.needFirmware = true;
  73 + } else if (i.second->connectDevice(entry.errorMessage)) {
  74 + entry.canConnect = true;
  75 + i.second->disconnectFromDevice();
  76 + } else {
  77 + entry.canConnect = false;
  78 + }
  79 + entries.push_back(entry);
  80 + }
  81 + endInsertRows();
  82 +}
openhantek/src/selectdevice/deviceslistmodel.h 0 → 100644
  1 +#pragma once
  2 +
  3 +#include <QAbstractTableModel>
  4 +#include "devicelistentry.h"
  5 +
  6 +class FindDevices;
  7 +
  8 +/**
  9 + * Provides a Model for the Qt Model/View concept. The {@see FindDevices} is required
  10 + * to update the list of available devices.
  11 + */
  12 +class DevicesListModel: public QAbstractTableModel {
  13 +public:
  14 + DevicesListModel(FindDevices* findDevices);
  15 + // QAbstractItemModel interface
  16 + virtual int rowCount(const QModelIndex &parent) const override;
  17 + virtual int columnCount(const QModelIndex &parent) const override;
  18 + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
  19 + virtual QVariant data(const QModelIndex &index, int role) const override;
  20 + void updateDeviceList();
  21 +private:
  22 + std::vector<DeviceListEntry> entries;
  23 + FindDevices* findDevices;
  24 +};
openhantek/src/selectdevice/selectdevice.cpp 0 → 100644
  1 +#include <libusb-1.0/libusb.h>
  2 +
  3 +#include "utils/printutils.h"
  4 +#include "selectdevice.h"
  5 +#include "usb/uploadFirmware.h"
  6 +#include "usb/finddevices.h"
  7 +#include "dsomodel.h"
  8 +#include <QApplication>
  9 +#include <QTimer>
  10 +#include <QAbstractTableModel>
  11 +#include <QHeaderView>
  12 +#include <QFile>
  13 +
  14 +#include "devicelistentry.h"
  15 +#include "deviceslistmodel.h"
  16 +
  17 +SelectDevice::SelectDevice() {
  18 + btn = new QPushButton(this);
  19 + w = new QTableView(this);
  20 + label = new QLabel();
  21 + label->setWordWrap(true);
  22 + move(QApplication::desktop()->screen()->rect().center() - w->rect().center());
  23 + setWindowTitle(tr("Select device"));
  24 + setLayout(new QVBoxLayout());
  25 + layout()->addWidget(w);
  26 + layout()->addWidget(label);
  27 + layout()->addWidget(btn);
  28 + qRegisterMetaType<UniqueUSBid>();
  29 + connect(btn, &QPushButton::clicked, [this]() {
  30 + if (w->currentIndex().isValid()) {
  31 + selectedDevice = w->currentIndex().data(Qt::UserRole).value<UniqueUSBid>();
  32 + }
  33 + QCoreApplication::instance()->quit();
  34 + });
  35 +}
  36 +
  37 +std::unique_ptr<USBDevice> SelectDevice::showSelectDeviceModal(libusb_context *&context)
  38 +{
  39 +
  40 + std::unique_ptr<FindDevices> findDevices = std::unique_ptr<FindDevices>(new FindDevices(context));
  41 + std::unique_ptr<DevicesListModel> model = std::unique_ptr<DevicesListModel>(new DevicesListModel(findDevices.get()));
  42 + w->setModel(model.get());
  43 + w->verticalHeader()->hide();
  44 + w->horizontalHeader()->hide();
  45 + w->setSelectionBehavior(QAbstractItemView::SelectRows);
  46 + // w->setColumnWidth(1,60);
  47 + w->horizontalHeader()->setStretchLastSection(true);
  48 + connect(w->selectionModel(), &QItemSelectionModel::currentChanged, this, &SelectDevice::currentChanged);
  49 +
  50 + QTimer timer;
  51 + timer.setInterval(1000);
  52 + connect(&timer, &QTimer::timeout, [this, &model, &findDevices]() {
  53 + if (findDevices->updateDeviceList())
  54 + model->updateDeviceList();
  55 + if (model->rowCount(QModelIndex())) {
  56 + w->setVisible(true);
  57 + label->setVisible(false);
  58 + currentChanged(w->currentIndex(),QModelIndex());
  59 + } else {
  60 + QString generalMessage = tr("<p>OpenHantek did not find any compatible devices.</p>"
  61 + "<p><img align='right' height='150' src='qrc:///switch.png'>"
  62 + "Don't forget to switch your device into oscilloscope mode if it has multiple modes.</p>"
  63 + );
  64 +#if defined(Q_OS_WIN)
  65 + generalMessage = generalMessage.arg("Please make sure you have installed the windows usb driver correctly");
  66 +#elif defined(Q_OS_LINUX)
  67 + QFile file("/lib/udev/rules.d/60-hantek.rules");
  68 + if (!file.exists()) {
  69 + generalMessage += tr("<p>Please make sure you have copied the udev rules file to <b>%1</b> for correct USB access permissions.</p>").arg(file.fileName());
  70 + }
  71 +#else
  72 +#endif
  73 + generalMessage += tr("<p>Visit the build and run instruction "
  74 + "<a href='https://github.com/OpenHantek/openhantek/blob/master/docs/build.md'>website</a> for help.</p>");
  75 + makeErrorDialog(generalMessage);
  76 + }
  77 + if (!w->currentIndex().isValid()) {
  78 + currentChanged(QModelIndex(),QModelIndex());
  79 + }
  80 + });
  81 + timer.start();
  82 + QCoreApplication::sendEvent(&timer, new QTimerEvent(timer.timerId())); // immediate timer event
  83 +
  84 + show();
  85 + QCoreApplication::instance()->exec();
  86 + timer.stop();
  87 + close();
  88 +
  89 + return findDevices->takeDevice(selectedDevice);
  90 +}
  91 +
  92 +void SelectDevice::showLibUSBFailedDialogModel(int error)
  93 +{
  94 + makeErrorDialog(tr("Can't initalize USB: %1").arg(libUsbErrorString(error)));
  95 + show();
  96 + QCoreApplication::instance()->exec();
  97 + close();
  98 +}
  99 +
  100 +void SelectDevice::makeErrorDialog(const QString &message)
  101 +{
  102 + w->setVisible(false);
  103 + label->setText(message);
  104 + label->setVisible(true);
  105 +}
  106 +
  107 +void SelectDevice::currentChanged(const QModelIndex &current, const QModelIndex &)
  108 +{
  109 + if (!current.isValid()) {
  110 + btn->setText(tr("Quit application"));
  111 + btn->setEnabled(true);
  112 + return;
  113 + }
  114 + if (current.data(Qt::UserRole+1).toBool()) {
  115 + btn->setText(tr("Use device %1").arg(current.sibling(current.row(),0).data(Qt::DisplayRole).toString()));
  116 + btn->setEnabled(true);
  117 + } else {
  118 + btn->setEnabled(false);
  119 + if (current.data(Qt::UserRole+2).toBool()) {
  120 + btn->setText(tr("Upload in progress..."));
  121 + } else {
  122 + btn->setText(tr("Connection failed!"));
  123 + }
  124 + }
  125 +}
openhantek/src/selectdevice/selectdevice.h 0 → 100644
  1 +#pragma once
  2 +
  3 +#include <QTableView>
  4 +#include <QPushButton>
  5 +#include <QVBoxLayout>
  6 +#include <QDesktopWidget>
  7 +#include <QDialog>
  8 +#include <QLabel>
  9 +#include <memory>
  10 +#include "usb/usbdevice.h"
  11 +
  12 +struct libusb_context;
  13 +
  14 +/**
  15 + * Offers the user a selection dialog. If you call any of the -Modal methods,
  16 + * the method will block and show a dialog for selection or for a usb error
  17 + * message. The method returns as soon as the user closes the dialog.
  18 + *
  19 + * An example to get a device:
  20 + * std::unique_ptr<USBDevice> device = SelectDevice().showSelectDeviceModal(context);
  21 + */
  22 +class SelectDevice: public QDialog {
  23 +public:
  24 + SelectDevice();
  25 + std::unique_ptr<USBDevice> showSelectDeviceModal(libusb_context *&context);
  26 + void showLibUSBFailedDialogModel(int error);
  27 +private:
  28 + void makeErrorDialog(const QString& message);
  29 + void updateDeviceList();
  30 + void currentChanged(const QModelIndex &current, const QModelIndex &previous);
  31 + QTableView *w;
  32 + QLabel *label;
  33 + QPushButton *btn;
  34 + UniqueUSBid selectedDevice = 0;
  35 +};
openhantek/src/usb/finddevices.cpp
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 #include <QList> 7 #include <QList>
8 #include <QTemporaryFile> 8 #include <QTemporaryFile>
9 9
  10 +#include <algorithm>
10 #include "ezusb.h" 11 #include "ezusb.h"
11 #include "utils/printutils.h" 12 #include "utils/printutils.h"
12 #include <libusb-1.0/libusb.h> 13 #include <libusb-1.0/libusb.h>
@@ -16,50 +17,64 @@ @@ -16,50 +17,64 @@
16 FindDevices::FindDevices(libusb_context *context) : context(context) {} 17 FindDevices::FindDevices(libusb_context *context) : context(context) {}
17 18
18 // Iterate through all usb devices 19 // Iterate through all usb devices
19 -std::list<std::unique_ptr<USBDevice>> FindDevices::findDevices() {  
20 - std::list<std::unique_ptr<USBDevice>> devices;  
21 - 20 +int FindDevices::updateDeviceList() {
22 libusb_device **deviceList; 21 libusb_device **deviceList;
23 ssize_t deviceCount = libusb_get_device_list(context, &deviceList); 22 ssize_t deviceCount = libusb_get_device_list(context, &deviceList);
24 if (deviceCount < 0) { 23 if (deviceCount < 0) {
25 - errorMessage = QCoreApplication::translate("", "Failed to get device list");  
26 - return devices; 24 + return (int) deviceCount;
27 } 25 }
28 26
29 - noAccessDevices = false;  
30 - int noAccessDeviceCount = 0; 27 + ++findIteration;
  28 + int changes = 0;
31 29
32 for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) { 30 for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) {
33 libusb_device *device = deviceList[deviceIterator]; 31 libusb_device *device = deviceList[deviceIterator];
34 // Get device descriptor 32 // Get device descriptor
35 struct libusb_device_descriptor descriptor; 33 struct libusb_device_descriptor descriptor;
36 - if (libusb_get_device_descriptor(device, &descriptor) < 0) continue; 34 + libusb_get_device_descriptor(device, &descriptor);
  35 +
  36 + DeviceList::const_iterator inList = devices.find(USBDevice::computeUSBdeviceID(device));
  37 +
  38 + if (inList != devices.end()) {
  39 + inList->second->setFindIteration(findIteration);
  40 + continue;
  41 + }
37 42
38 for (DSOModel* model : supportedModels) { 43 for (DSOModel* model : supportedModels) {
39 // Check VID and PID for firmware flashed devices 44 // Check VID and PID for firmware flashed devices
40 - if (descriptor.idVendor == model->vendorID && descriptor.idProduct == model->productID) {  
41 - devices.push_back(std::unique_ptr<USBDevice>(new USBDevice(model, device)));  
42 - break;  
43 - } 45 + bool supported = descriptor.idVendor == model->vendorID && descriptor.idProduct == model->productID;
44 // Devices without firmware have different VID/PIDs 46 // Devices without firmware have different VID/PIDs
45 - if (descriptor.idVendor == model->vendorIDnoFirmware && descriptor.idProduct == model->productIDnoFirmware) {  
46 - devices.push_back(std::unique_ptr<USBDevice>(new USBDevice(model, device)));  
47 - break; 47 + supported |= descriptor.idVendor == model->vendorIDnoFirmware && descriptor.idProduct == model->productIDnoFirmware;
  48 + if (supported) {
  49 + ++changes;
  50 + devices[USBDevice::computeUSBdeviceID(device)] = std::unique_ptr<USBDevice>(new USBDevice(model, device, findIteration));
48 } 51 }
49 } 52 }
50 } 53 }
51 54
52 - if (noAccessDeviceCount == deviceCount) {  
53 - noAccessDevices = true;  
54 - errorMessage =  
55 - QCoreApplication::translate("", "Please make sure to have read/write access to your usb device. On "  
56 - "linux you need to install the correct udev file for example."); 55 + // Remove non existing devices
  56 + for (DeviceList::iterator it=devices.begin();it!=devices.end();) {
  57 + if (it->second->getFindIteration() != findIteration) {
  58 + ++changes;
  59 + it = devices.erase(it);
  60 + } else {
  61 + ++it;
  62 + }
57 } 63 }
58 64
59 libusb_free_device_list(deviceList, true); 65 libusb_free_device_list(deviceList, true);
60 - return devices; 66 +
  67 + return changes;
61 } 68 }
62 69
63 -const QString &FindDevices::getErrorMessage() const { return errorMessage; } 70 +const FindDevices::DeviceList* FindDevices::getDevices()
  71 +{
  72 + return &devices;
  73 +}
64 74
65 -bool FindDevices::allDevicesNoAccessError() const { return noAccessDevices; } 75 +std::unique_ptr<USBDevice> FindDevices::takeDevice(UniqueUSBid id)
  76 +{
  77 + DeviceList::iterator i = devices.find(id);
  78 + if (i==devices.end()) return nullptr;
  79 + return std::move(i->second);
  80 +}
openhantek/src/usb/finddevices.h
@@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
4 4
5 #include <QString> 5 #include <QString>
6 #include <memory> 6 #include <memory>
  7 +#include <map>
  8 +#include <list>
7 9
8 #include "definitions.h" 10 #include "definitions.h"
9 #include "usbdevice.h" 11 #include "usbdevice.h"
@@ -11,19 +13,30 @@ @@ -11,19 +13,30 @@
11 struct libusb_context; 13 struct libusb_context;
12 14
13 /** 15 /**
14 - * @brief Search for Hantek devices and connect to the selected one. 16 + * @brief Search for Hantek devices.
  17 + * Use usually want to call `updateDeviceList` and then retrieve the list via `getDevices`.
  18 + * You can call `updateDeviceList` as often as you want.
  19 + * If you have found your favorite device, you want to call `takeDevice`. The device will
  20 + * not be available in `getDevices` anymore and this will not change with calls to `updateDeviceList`.
15 * 21 *
16 - * At the moment this class connects to the first found devic automatically. 22 + * Do not close the given usb context before this class object is destroyed.
17 */ 23 */
18 -class FindDevices { 24 +class FindDevices{
19 public: 25 public:
20 - FindDevices(libusb_context *context = nullptr);  
21 - std::list<std::unique_ptr<USBDevice>> findDevices();  
22 - const QString &getErrorMessage() const;  
23 - bool allDevicesNoAccessError() const;  
24 - 26 + typedef std::map<UniqueUSBid, std::unique_ptr<USBDevice>> DeviceList;
  27 + FindDevices(libusb_context *context);
  28 + /// Updates the device list. To clear the list, just dispose this object
  29 + /// \return If negative it represents a libusb error code otherwise the amount of updates
  30 + int updateDeviceList();
  31 + const DeviceList *getDevices();
  32 + /**
  33 + * @brief takeDevice
  34 + * @param id The unique usb id for the current bus layout
  35 + * @return A shared reference to the
  36 + */
  37 + std::unique_ptr<USBDevice> takeDevice(UniqueUSBid id);
25 private: 38 private:
26 libusb_context *context; ///< The usb context used for this device 39 libusb_context *context; ///< The usb context used for this device
27 - QString errorMessage;  
28 - bool noAccessDevices = false; 40 + DeviceList devices;
  41 + unsigned findIteration = 0;
29 }; 42 };
openhantek/src/usb/uploadFirmware.cpp
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 14
15 #include "dsomodel.h" 15 #include "dsomodel.h"
16 16
  17 +#define TR(str) QCoreApplication::translate("UploadFirmware", str)
  18 +
17 bool UploadFirmware::startUpload(USBDevice *device) { 19 bool UploadFirmware::startUpload(USBDevice *device) {
18 if (device->isConnected() || !device->needsFirmware()) return false; 20 if (device->isConnected() || !device->needsFirmware()) return false;
19 21
@@ -22,7 +24,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { @@ -22,7 +24,7 @@ bool UploadFirmware::startUpload(USBDevice *device) {
22 int errorCode = libusb_open(device->getRawDevice(), &handle); 24 int errorCode = libusb_open(device->getRawDevice(), &handle);
23 if (errorCode != LIBUSB_SUCCESS) { 25 if (errorCode != LIBUSB_SUCCESS) {
24 handle = nullptr; 26 handle = nullptr;
25 - errorMessage = QCoreApplication::translate("", "Couldn't open device: %1").arg(libUsbErrorString(errorCode)); 27 + errorMessage = TR("Couldn't open device: %1").arg(libUsbErrorString(errorCode));
26 return false; 28 return false;
27 } 29 }
28 30
@@ -41,7 +43,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { @@ -41,7 +43,7 @@ bool UploadFirmware::startUpload(USBDevice *device) {
41 libusb_set_auto_detach_kernel_driver(handle, 1); 43 libusb_set_auto_detach_kernel_driver(handle, 1);
42 int status = libusb_claim_interface(handle, 0); 44 int status = libusb_claim_interface(handle, 0);
43 if (status != LIBUSB_SUCCESS) { 45 if (status != LIBUSB_SUCCESS) {
44 - errorMessage = QString("libusb_claim_interface() failed: %1").arg(libusb_error_name(status)); 46 + errorMessage = TR("libusb_claim_interface() failed: %1").arg(libusb_error_name(status));
45 libusb_close(handle); 47 libusb_close(handle);
46 return false; 48 return false;
47 } 49 }
@@ -50,7 +52,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { @@ -50,7 +52,7 @@ bool UploadFirmware::startUpload(USBDevice *device) {
50 status = ezusb_load_ram(handle, temp_loader_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 0); 52 status = ezusb_load_ram(handle, temp_loader_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 0);
51 53
52 if (status != LIBUSB_SUCCESS) { 54 if (status != LIBUSB_SUCCESS) {
53 - errorMessage = QString("ezusb_load_ram(loader_path) failed: %1").arg(libusb_error_name(status)); 55 + errorMessage = TR("Writing the loader firmware failed: %1").arg(libusb_error_name(status));
54 libusb_release_interface(handle, 0); 56 libusb_release_interface(handle, 0);
55 libusb_close(handle); 57 libusb_close(handle);
56 return false; 58 return false;
@@ -60,7 +62,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { @@ -60,7 +62,7 @@ bool UploadFirmware::startUpload(USBDevice *device) {
60 status = ezusb_load_ram(handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 1); 62 status = ezusb_load_ram(handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 1);
61 63
62 if (status != LIBUSB_SUCCESS) { 64 if (status != LIBUSB_SUCCESS) {
63 - errorMessage = QString("ezusb_load_ram(firmware_path) failed: %1").arg(libusb_error_name(status)); 65 + errorMessage = TR("Writing the main firmware failed: %1").arg(libusb_error_name(status));
64 libusb_release_interface(handle, 0); 66 libusb_release_interface(handle, 0);
65 libusb_close(handle); 67 libusb_close(handle);
66 return false; 68 return false;
@@ -72,3 +74,4 @@ bool UploadFirmware::startUpload(USBDevice *device) { @@ -72,3 +74,4 @@ bool UploadFirmware::startUpload(USBDevice *device) {
72 } 74 }
73 75
74 const QString &UploadFirmware::getErrorMessage() const { return errorMessage; } 76 const QString &UploadFirmware::getErrorMessage() const { return errorMessage; }
  77 +
openhantek/src/usb/uploadFirmware.h
@@ -14,7 +14,6 @@ class UploadFirmware { @@ -14,7 +14,6 @@ class UploadFirmware {
14 public: 14 public:
15 bool startUpload(USBDevice *device); 15 bool startUpload(USBDevice *device);
16 const QString &getErrorMessage() const; 16 const QString &getErrorMessage() const;
17 -  
18 private: 17 private:
19 QString errorMessage; 18 QString errorMessage;
20 }; 19 };
openhantek/src/usb/usbdevice.cpp
@@ -11,8 +11,14 @@ @@ -11,8 +11,14 @@
11 #include "models.h" 11 #include "models.h"
12 #include "utils/printutils.h" 12 #include "utils/printutils.h"
13 13
  14 +UniqueUSBid USBDevice::computeUSBdeviceID(libusb_device *device) {
  15 + UniqueUSBid v=0;
  16 + libusb_get_port_numbers(device, (uint8_t*)&v, sizeof(v));
  17 + return v;
  18 +}
14 19
15 -USBDevice::USBDevice(DSOModel *model, libusb_device *device) : model(model), device(device) { 20 +USBDevice::USBDevice(DSOModel *model, libusb_device *device, unsigned findIteration) :
  21 + model(model), device(device), findIteration(findIteration), uniqueUSBdeviceID(computeUSBdeviceID(device)) {
16 libusb_ref_device(device); 22 libusb_ref_device(device);
17 libusb_get_device_descriptor(device, &descriptor); 23 libusb_get_device_descriptor(device, &descriptor);
18 } 24 }
@@ -59,7 +65,10 @@ bool USBDevice::connectDevice(QString &amp;errorMessage) { @@ -59,7 +65,10 @@ bool USBDevice::connectDevice(QString &amp;errorMessage) {
59 return true; 65 return true;
60 } 66 }
61 67
62 -USBDevice::~USBDevice() { connectionLost(); } 68 +USBDevice::~USBDevice() {
  69 + disconnectFromDevice();
  70 + device = nullptr;
  71 +}
63 72
64 int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn) { 73 int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn) {
65 int errorCode = libusb_claim_interface(this->handle, interfaceDescriptor->bInterfaceNumber); 74 int errorCode = libusb_claim_interface(this->handle, interfaceDescriptor->bInterfaceNumber);
@@ -82,7 +91,8 @@ int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescri @@ -82,7 +91,8 @@ int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescri
82 return LIBUSB_SUCCESS; 91 return LIBUSB_SUCCESS;
83 } 92 }
84 93
85 -void USBDevice::connectionLost() { 94 +void USBDevice::disconnectFromDevice() {
  95 + if (!device) return;
86 libusb_unref_device(device); 96 libusb_unref_device(device);
87 97
88 if (!this->handle) return; 98 if (!this->handle) return;
@@ -93,7 +103,7 @@ void USBDevice::connectionLost() { @@ -93,7 +103,7 @@ void USBDevice::connectionLost() {
93 103
94 // Close device handle 104 // Close device handle
95 libusb_close(this->handle); 105 libusb_close(this->handle);
96 - this->handle = 0; 106 + this->handle = nullptr;
97 107
98 emit deviceDisconnected(); 108 emit deviceDisconnected();
99 } 109 }
@@ -104,6 +114,16 @@ bool USBDevice::needsFirmware() { @@ -104,6 +114,16 @@ bool USBDevice::needsFirmware() {
104 return this->descriptor.idProduct != model->productID || this->descriptor.idVendor != model->vendorID; 114 return this->descriptor.idProduct != model->productID || this->descriptor.idVendor != model->vendorID;
105 } 115 }
106 116
  117 +void USBDevice::setFindIteration(unsigned iteration)
  118 +{
  119 + findIteration = iteration;
  120 +}
  121 +
  122 +unsigned USBDevice::getFindIteration() const
  123 +{
  124 + return findIteration;
  125 +}
  126 +
107 int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts, 127 int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts,
108 unsigned int timeout) { 128 unsigned int timeout) {
109 if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; 129 if (!this->handle) return LIBUSB_ERROR_NO_DEVICE;
@@ -113,7 +133,7 @@ int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigne @@ -113,7 +133,7 @@ int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigne
113 for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) 133 for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt)
114 errorCode = libusb_bulk_transfer(this->handle, endpoint, data, length, &transferred, timeout); 134 errorCode = libusb_bulk_transfer(this->handle, endpoint, data, length, &transferred, timeout);
115 135
116 - if (errorCode == LIBUSB_ERROR_NO_DEVICE) connectionLost(); 136 + if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice();
117 if (errorCode < 0) 137 if (errorCode < 0)
118 return errorCode; 138 return errorCode;
119 else 139 else
@@ -211,7 +231,7 @@ int USBDevice::controlTransfer(unsigned char type, unsigned char request, unsign @@ -211,7 +231,7 @@ int USBDevice::controlTransfer(unsigned char type, unsigned char request, unsign
211 for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) 231 for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt)
212 errorCode = libusb_control_transfer(this->handle, type, request, value, index, data, length, HANTEK_TIMEOUT); 232 errorCode = libusb_control_transfer(this->handle, type, request, value, index, data, length, HANTEK_TIMEOUT);
213 233
214 - if (errorCode == LIBUSB_ERROR_NO_DEVICE) connectionLost(); 234 + if (errorCode == LIBUSB_ERROR_NO_DEVICE) disconnectFromDevice();
215 return errorCode; 235 return errorCode;
216 } 236 }
217 237
@@ -276,6 +296,11 @@ int USBDevice::getPacketSize() { @@ -276,6 +296,11 @@ int USBDevice::getPacketSize() {
276 296
277 libusb_device *USBDevice::getRawDevice() const { return device; } 297 libusb_device *USBDevice::getRawDevice() const { return device; }
278 298
  299 +unsigned long USBDevice::getUniqueUSBDeviceID() const
  300 +{
  301 + return uniqueUSBdeviceID;
  302 +}
  303 +
279 const DSOModel* USBDevice::getModel() const { return model; } 304 const DSOModel* USBDevice::getModel() const { return model; }
280 305
281 void USBDevice::setEnableBulkTransfer(bool enable) { allowBulkTransfer = enable; } 306 void USBDevice::setEnableBulkTransfer(bool enable) { allowBulkTransfer = enable; }
openhantek/src/usb/usbdevice.h
@@ -14,21 +14,31 @@ @@ -14,21 +14,31 @@
14 14
15 class DSOModel; 15 class DSOModel;
16 16
  17 +typedef unsigned long UniqueUSBid;
  18 +
17 /// \brief This class handles the USB communication with an usb device that has 19 /// \brief This class handles the USB communication with an usb device that has
18 /// one in and one out endpoint. 20 /// one in and one out endpoint.
19 class USBDevice : public QObject { 21 class USBDevice : public QObject {
20 Q_OBJECT 22 Q_OBJECT
21 23
22 public: 24 public:
23 - USBDevice(DSOModel* model, libusb_device *device); 25 + USBDevice(DSOModel* model, libusb_device *device, unsigned findIteration = 0);
24 ~USBDevice(); 26 ~USBDevice();
25 bool connectDevice(QString &errorMessage); 27 bool connectDevice(QString &errorMessage);
  28 + void disconnectFromDevice();
26 29
27 /// \brief Check if the oscilloscope is connected. 30 /// \brief Check if the oscilloscope is connected.
28 /// \return true, if a connection is up. 31 /// \return true, if a connection is up.
29 bool isConnected(); 32 bool isConnected();
30 bool needsFirmware(); 33 bool needsFirmware();
31 34
  35 + /**
  36 + * Keep track of the find iteration on which this device was found
  37 + * @param iteration The new iteration value
  38 + */
  39 + void setFindIteration(unsigned iteration);
  40 + unsigned getFindIteration() const;
  41 +
32 // Various methods to handle USB transfers 42 // Various methods to handle USB transfers
33 43
34 /// \brief Bulk transfer to/from the oscilloscope. 44 /// \brief Bulk transfer to/from the oscilloscope.
@@ -57,16 +67,31 @@ class USBDevice : public QObject { @@ -57,16 +67,31 @@ class USBDevice : public QObject {
57 int getConnectionSpeed(); 67 int getConnectionSpeed();
58 int getPacketSize(); 68 int getPacketSize();
59 69
  70 + /**
  71 + * @return Returns the raw libusb device
  72 + */
60 libusb_device *getRawDevice() const; 73 libusb_device *getRawDevice() const;
  74 +
  75 + /**
  76 + * @return Return the unique usb device id {@link USBDevice::computeUSBdeviceID()}.
  77 + */
  78 + unsigned long getUniqueUSBDeviceID() const;
  79 + /**
  80 + * The USB bus is organised in a tree hierarchy. A device is connected to a port on a bus device,
  81 + * which is connected to a port on another bus device etc up to the root usb device.
  82 + *
  83 + * The USB 3.0 standard allows up to 7 levels with 256 devices on each level (1 Byte). We generate
  84 + * a unique number for the connected device.
  85 + */
  86 + static UniqueUSBid computeUSBdeviceID(libusb_device *device);
  87 +
61 /// \brief Get the oscilloscope model. 88 /// \brief Get the oscilloscope model.
62 /// \return The ::Model of the connected Hantek DSO. 89 /// \return The ::Model of the connected Hantek DSO.
63 const DSOModel *getModel() const; 90 const DSOModel *getModel() const;
64 void setEnableBulkTransfer(bool enable); 91 void setEnableBulkTransfer(bool enable);
65 void overwriteInPacketLength(int len); 92 void overwriteInPacketLength(int len);
66 -  
67 protected: 93 protected:
68 int claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn); 94 int claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn);
69 - void connectionLost();  
70 95
71 // Command buffers 96 // Command buffers
72 ControlBeginCommand beginCommandControl; 97 ControlBeginCommand beginCommandControl;
@@ -76,6 +101,8 @@ class USBDevice : public QObject { @@ -76,6 +101,8 @@ class USBDevice : public QObject {
76 struct libusb_device_descriptor descriptor; 101 struct libusb_device_descriptor descriptor;
77 libusb_device *device; ///< The USB handle for the oscilloscope 102 libusb_device *device; ///< The USB handle for the oscilloscope
78 libusb_device_handle *handle = nullptr; 103 libusb_device_handle *handle = nullptr;
  104 + unsigned findIteration;
  105 + const unsigned long uniqueUSBdeviceID;
79 int interface; 106 int interface;
80 int outPacketLength; ///< Packet length for the OUT endpoint 107 int outPacketLength; ///< Packet length for the OUT endpoint
81 int inPacketLength; ///< Packet length for the IN endpoint 108 int inPacketLength; ///< Packet length for the IN endpoint