Commit 4a1617f43c5bf752d800c4d5fb75d1ec995f3c70
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
Showing
15 changed files
with
434 additions
and
154 deletions
cmake/CPackInfos.cmake
| ... | ... | @@ -44,13 +44,17 @@ else() |
| 44 | 44 | ) |
| 45 | 45 | |
| 46 | 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 | 58 | file(WRITE "${CMAKE_BINARY_DIR}/changelog" "${CHANGELOG}") |
| 55 | 59 | endif() |
| 56 | 60 | ... | ... |
openhantek/res/application.qrc
openhantek/res/images/switch.png
0 → 100644
120 KB
openhantek/src/main.cpp
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | 2 | |
| 3 | -#include <QApplication> | |
| 4 | 3 | #include <QDebug> |
| 5 | -#include <QDesktopWidget> | |
| 4 | +#include <QApplication> | |
| 6 | 5 | #include <QLibraryInfo> |
| 7 | -#include <QListWidget> | |
| 8 | 6 | #include <QLocale> |
| 9 | -#include <QMessageBox> | |
| 10 | -#include <QPushButton> | |
| 11 | -#include <QTimer> | |
| 12 | 7 | #include <QTranslator> |
| 13 | -#include <QVBoxLayout> | |
| 8 | + | |
| 14 | 9 | #include <iostream> |
| 15 | 10 | #include <memory> |
| 16 | - | |
| 17 | 11 | #include <libusb-1.0/libusb.h> |
| 18 | 12 | |
| 19 | 13 | #include "analyse/dataanalyzer.h" |
| 20 | 14 | #include "hantekdsocontrol.h" |
| 21 | 15 | #include "mainwindow.h" |
| 22 | 16 | #include "settings.h" |
| 23 | -#include "usb/finddevices.h" | |
| 24 | -#include "usb/uploadFirmware.h" | |
| 25 | 17 | #include "usb/usbdevice.h" |
| 26 | 18 | #include "dsomodel.h" |
| 19 | +#include "selectdevice/selectdevice.h" | |
| 27 | 20 | |
| 28 | 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 | 23 | /// \brief Initialize resources and translations and show the main window. |
| 35 | 24 | int main(int argc, char *argv[]) { |
| 36 | 25 | //////// Set application information //////// |
| ... | ... | @@ -53,97 +42,17 @@ int main(int argc, char *argv[]) { |
| 53 | 42 | } |
| 54 | 43 | |
| 55 | 44 | //////// Find matching usb devices //////// |
| 56 | - libusb_context *context; | |
| 45 | + libusb_context *context = nullptr; | |
| 57 | 46 | int error = libusb_init(&context); |
| 58 | - | |
| 59 | 47 | if (error) { |
| 60 | - showMessage(QCoreApplication::translate("", "Can't initalize USB: %1").arg(libUsbErrorString(error))); | |
| 48 | + SelectDevice().showLibUSBFailedDialogModel(error); | |
| 61 | 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 | 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 ¤t, 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 ¤t, 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 | 7 | #include <QList> |
| 8 | 8 | #include <QTemporaryFile> |
| 9 | 9 | |
| 10 | +#include <algorithm> | |
| 10 | 11 | #include "ezusb.h" |
| 11 | 12 | #include "utils/printutils.h" |
| 12 | 13 | #include <libusb-1.0/libusb.h> |
| ... | ... | @@ -16,50 +17,64 @@ |
| 16 | 17 | FindDevices::FindDevices(libusb_context *context) : context(context) {} |
| 17 | 18 | |
| 18 | 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 | 21 | libusb_device **deviceList; |
| 23 | 22 | ssize_t deviceCount = libusb_get_device_list(context, &deviceList); |
| 24 | 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 | 30 | for (ssize_t deviceIterator = 0; deviceIterator < deviceCount; ++deviceIterator) { |
| 33 | 31 | libusb_device *device = deviceList[deviceIterator]; |
| 34 | 32 | // Get device descriptor |
| 35 | 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 | 43 | for (DSOModel* model : supportedModels) { |
| 39 | 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 | 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 | 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 | 4 | |
| 5 | 5 | #include <QString> |
| 6 | 6 | #include <memory> |
| 7 | +#include <map> | |
| 8 | +#include <list> | |
| 7 | 9 | |
| 8 | 10 | #include "definitions.h" |
| 9 | 11 | #include "usbdevice.h" |
| ... | ... | @@ -11,19 +13,30 @@ |
| 11 | 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 | 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 | 38 | private: |
| 26 | 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 | 14 | |
| 15 | 15 | #include "dsomodel.h" |
| 16 | 16 | |
| 17 | +#define TR(str) QCoreApplication::translate("UploadFirmware", str) | |
| 18 | + | |
| 17 | 19 | bool UploadFirmware::startUpload(USBDevice *device) { |
| 18 | 20 | if (device->isConnected() || !device->needsFirmware()) return false; |
| 19 | 21 | |
| ... | ... | @@ -22,7 +24,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { |
| 22 | 24 | int errorCode = libusb_open(device->getRawDevice(), &handle); |
| 23 | 25 | if (errorCode != LIBUSB_SUCCESS) { |
| 24 | 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 | 28 | return false; |
| 27 | 29 | } |
| 28 | 30 | |
| ... | ... | @@ -41,7 +43,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { |
| 41 | 43 | libusb_set_auto_detach_kernel_driver(handle, 1); |
| 42 | 44 | int status = libusb_claim_interface(handle, 0); |
| 43 | 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 | 47 | libusb_close(handle); |
| 46 | 48 | return false; |
| 47 | 49 | } |
| ... | ... | @@ -50,7 +52,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { |
| 50 | 52 | status = ezusb_load_ram(handle, temp_loader_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 0); |
| 51 | 53 | |
| 52 | 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 | 56 | libusb_release_interface(handle, 0); |
| 55 | 57 | libusb_close(handle); |
| 56 | 58 | return false; |
| ... | ... | @@ -60,7 +62,7 @@ bool UploadFirmware::startUpload(USBDevice *device) { |
| 60 | 62 | status = ezusb_load_ram(handle, temp_firmware_path->fileName().toUtf8().constData(), FX_TYPE_FX2, IMG_TYPE_HEX, 1); |
| 61 | 63 | |
| 62 | 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 | 66 | libusb_release_interface(handle, 0); |
| 65 | 67 | libusb_close(handle); |
| 66 | 68 | return false; |
| ... | ... | @@ -72,3 +74,4 @@ bool UploadFirmware::startUpload(USBDevice *device) { |
| 72 | 74 | } |
| 73 | 75 | |
| 74 | 76 | const QString &UploadFirmware::getErrorMessage() const { return errorMessage; } |
| 77 | + | ... | ... |
openhantek/src/usb/uploadFirmware.h
openhantek/src/usb/usbdevice.cpp
| ... | ... | @@ -11,8 +11,14 @@ |
| 11 | 11 | #include "models.h" |
| 12 | 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 | 22 | libusb_ref_device(device); |
| 17 | 23 | libusb_get_device_descriptor(device, &descriptor); |
| 18 | 24 | } |
| ... | ... | @@ -59,7 +65,10 @@ bool USBDevice::connectDevice(QString &errorMessage) { |
| 59 | 65 | return true; |
| 60 | 66 | } |
| 61 | 67 | |
| 62 | -USBDevice::~USBDevice() { connectionLost(); } | |
| 68 | +USBDevice::~USBDevice() { | |
| 69 | + disconnectFromDevice(); | |
| 70 | + device = nullptr; | |
| 71 | +} | |
| 63 | 72 | |
| 64 | 73 | int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn) { |
| 65 | 74 | int errorCode = libusb_claim_interface(this->handle, interfaceDescriptor->bInterfaceNumber); |
| ... | ... | @@ -82,7 +91,8 @@ int USBDevice::claimInterface(const libusb_interface_descriptor *interfaceDescri |
| 82 | 91 | return LIBUSB_SUCCESS; |
| 83 | 92 | } |
| 84 | 93 | |
| 85 | -void USBDevice::connectionLost() { | |
| 94 | +void USBDevice::disconnectFromDevice() { | |
| 95 | + if (!device) return; | |
| 86 | 96 | libusb_unref_device(device); |
| 87 | 97 | |
| 88 | 98 | if (!this->handle) return; |
| ... | ... | @@ -93,7 +103,7 @@ void USBDevice::connectionLost() { |
| 93 | 103 | |
| 94 | 104 | // Close device handle |
| 95 | 105 | libusb_close(this->handle); |
| 96 | - this->handle = 0; | |
| 106 | + this->handle = nullptr; | |
| 97 | 107 | |
| 98 | 108 | emit deviceDisconnected(); |
| 99 | 109 | } |
| ... | ... | @@ -104,6 +114,16 @@ bool USBDevice::needsFirmware() { |
| 104 | 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 | 127 | int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigned int length, int attempts, |
| 108 | 128 | unsigned int timeout) { |
| 109 | 129 | if (!this->handle) return LIBUSB_ERROR_NO_DEVICE; |
| ... | ... | @@ -113,7 +133,7 @@ int USBDevice::bulkTransfer(unsigned char endpoint, unsigned char *data, unsigne |
| 113 | 133 | for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) |
| 114 | 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 | 137 | if (errorCode < 0) |
| 118 | 138 | return errorCode; |
| 119 | 139 | else |
| ... | ... | @@ -211,7 +231,7 @@ int USBDevice::controlTransfer(unsigned char type, unsigned char request, unsign |
| 211 | 231 | for (int attempt = 0; (attempt < attempts || attempts == -1) && errorCode == LIBUSB_ERROR_TIMEOUT; ++attempt) |
| 212 | 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 | 235 | return errorCode; |
| 216 | 236 | } |
| 217 | 237 | |
| ... | ... | @@ -276,6 +296,11 @@ int USBDevice::getPacketSize() { |
| 276 | 296 | |
| 277 | 297 | libusb_device *USBDevice::getRawDevice() const { return device; } |
| 278 | 298 | |
| 299 | +unsigned long USBDevice::getUniqueUSBDeviceID() const | |
| 300 | +{ | |
| 301 | + return uniqueUSBdeviceID; | |
| 302 | +} | |
| 303 | + | |
| 279 | 304 | const DSOModel* USBDevice::getModel() const { return model; } |
| 280 | 305 | |
| 281 | 306 | void USBDevice::setEnableBulkTransfer(bool enable) { allowBulkTransfer = enable; } | ... | ... |
openhantek/src/usb/usbdevice.h
| ... | ... | @@ -14,21 +14,31 @@ |
| 14 | 14 | |
| 15 | 15 | class DSOModel; |
| 16 | 16 | |
| 17 | +typedef unsigned long UniqueUSBid; | |
| 18 | + | |
| 17 | 19 | /// \brief This class handles the USB communication with an usb device that has |
| 18 | 20 | /// one in and one out endpoint. |
| 19 | 21 | class USBDevice : public QObject { |
| 20 | 22 | Q_OBJECT |
| 21 | 23 | |
| 22 | 24 | public: |
| 23 | - USBDevice(DSOModel* model, libusb_device *device); | |
| 25 | + USBDevice(DSOModel* model, libusb_device *device, unsigned findIteration = 0); | |
| 24 | 26 | ~USBDevice(); |
| 25 | 27 | bool connectDevice(QString &errorMessage); |
| 28 | + void disconnectFromDevice(); | |
| 26 | 29 | |
| 27 | 30 | /// \brief Check if the oscilloscope is connected. |
| 28 | 31 | /// \return true, if a connection is up. |
| 29 | 32 | bool isConnected(); |
| 30 | 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 | 42 | // Various methods to handle USB transfers |
| 33 | 43 | |
| 34 | 44 | /// \brief Bulk transfer to/from the oscilloscope. |
| ... | ... | @@ -57,16 +67,31 @@ class USBDevice : public QObject { |
| 57 | 67 | int getConnectionSpeed(); |
| 58 | 68 | int getPacketSize(); |
| 59 | 69 | |
| 70 | + /** | |
| 71 | + * @return Returns the raw libusb device | |
| 72 | + */ | |
| 60 | 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 | 88 | /// \brief Get the oscilloscope model. |
| 62 | 89 | /// \return The ::Model of the connected Hantek DSO. |
| 63 | 90 | const DSOModel *getModel() const; |
| 64 | 91 | void setEnableBulkTransfer(bool enable); |
| 65 | 92 | void overwriteInPacketLength(int len); |
| 66 | - | |
| 67 | 93 | protected: |
| 68 | 94 | int claimInterface(const libusb_interface_descriptor *interfaceDescriptor, int endpointOut, int endPointIn); |
| 69 | - void connectionLost(); | |
| 70 | 95 | |
| 71 | 96 | // Command buffers |
| 72 | 97 | ControlBeginCommand beginCommandControl; |
| ... | ... | @@ -76,6 +101,8 @@ class USBDevice : public QObject { |
| 76 | 101 | struct libusb_device_descriptor descriptor; |
| 77 | 102 | libusb_device *device; ///< The USB handle for the oscilloscope |
| 78 | 103 | libusb_device_handle *handle = nullptr; |
| 104 | + unsigned findIteration; | |
| 105 | + const unsigned long uniqueUSBdeviceID; | |
| 79 | 106 | int interface; |
| 80 | 107 | int outPacketLength; ///< Packet length for the OUT endpoint |
| 81 | 108 | int inPacketLength; ///< Packet length for the IN endpoint | ... | ... |