From 89a36abe7fa9b720be3bdd1ab81990843b95a1f7 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Fri, 15 Feb 2019 00:04:55 -0800 Subject: [PATCH] resolved crashing issue --- 3rdparty/CMakeLists.txt | 13 +++++ CMakeLists.txt | 29 +++++----- common/BaseDiscoveryAgent.cpp | 8 +++ common/BaseDiscoveryAgent.h | 9 ++- forms/deviceloadingdialog.cpp | 14 +++++ forms/deviceloadingdialog.h | 39 +++++++++++++ forms/deviceselectdialog.cpp | 103 ++++++++++++++++++---------------- forms/deviceselectdialog.h | 1 + forms/mainwindow.cpp | 17 ++++-- forms/mbientconfigpanel.cpp | 14 ++--- forms/mbientconfigpanel.h | 3 +- forms/profiledialog.cpp | 3 +- icon.ico => icon.bmp | Bin icons/gait-icon.bmp | Bin 0 -> 64138 bytes icons/gait-icon.ico | Bin 0 -> 50062 bytes 15 files changed, 172 insertions(+), 81 deletions(-) create mode 100644 common/BaseDiscoveryAgent.cpp create mode 100644 forms/deviceloadingdialog.cpp create mode 100644 forms/deviceloadingdialog.h rename icon.ico => icon.bmp (100%) create mode 100644 icons/gait-icon.bmp create mode 100644 icons/gait-icon.ico diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 9fa523d..065c528 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -27,6 +27,13 @@ install(FILES DESTINATION "." ) + add_custom_command( + TARGET qmsgpack + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${install_dir}/bin/qmsgpackd.dll ${CMAKE_BINARY_DIR} + ) + + SET(QUAZIP_LIB_VERSION_SUFFIX 5) ExternalProject_Add(quazip GIT_REPOSITORY https://github.com/stachenov/quazip.git @@ -48,6 +55,12 @@ install(FILES DESTINATION "." ) + add_custom_command( + TARGET quazip + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${install_dir}/lib/quazip${QUAZIP_LIB_VERSION_SUFFIX}d.dll ${CMAKE_BINARY_DIR} + ) + add_dependencies(quazip_extern quazip) add_dependencies(qmsgpack_extern qmsgpack) diff --git a/CMakeLists.txt b/CMakeLists.txt index 338b20b..9edb973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,13 @@ set (SOURCES forms/sensorpanel.cpp forms/profiledialog.cpp forms/mbientconfigpanel.cpp + forms/deviceloadingdialog.cpp common/main.cpp common/metawearwrapperbase.cpp common/util.cpp - common/BluetoothAddress.cpp) + common/BluetoothAddress.cpp + common/BaseDiscoveryAgent.cpp) set(HEADERS qcustomplot.h @@ -36,6 +38,7 @@ set(HEADERS forms/mainwindow.h forms/profiledialog.h forms/mbientconfigpanel.h + forms/deviceloadingdialog.h common/metawearwrapperbase.h common/util.h @@ -48,8 +51,7 @@ set ( UIS forms/sensorpanel.ui forms/deviceselectdialog.ui forms/mbientconfigpanel.ui - forms/profiledialog.ui - ) + forms/profiledialog.ui ) set ( RESOURCES qml.qrc @@ -75,7 +77,6 @@ if (UNIX) endif (UNIX) if (MSVC) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) include_directories(platform/windows) set (PLATFORM_SOURCE @@ -126,6 +127,11 @@ if (MSVC) COMMAND ${WINDEPLOYQT_EXECUTABLE} --compiler-runtime --dir ${CMAKE_BINARY_DIR}/windeployqt_stuff $ ) + add_custom_command( + TARGET SMART POST_BUILD + COMMAND ${WINDEPLOYQT_EXECUTABLE} --compiler-runtime $ + ) + install( DIRECTORY ${CMAKE_BINARY_DIR}/windeployqt_stuff/ DESTINATION . @@ -134,20 +140,11 @@ if (MSVC) target_link_libraries(SMART wsock32 ws2_32) set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) + set(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ./) include(InstallRequiredSystemLibraries) endif(MSVC) - -install(FILES - $ - $ - $ - $ - $ - DESTINATION "." -) - install(TARGETS SMART DESTINATION "." ) @@ -178,7 +175,9 @@ set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") set(CPACK_PACKAGE_EXECUTABLES "SMART" "SMART") set(CPACK_CREATE_DESKTOP_LINKS "SMART") set(CPACK_NSIS_MODIFY_PATH "ON") -set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/icon.ico") +set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/icons\\\\gait-icon.bmp") +set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/icons/gait-icon.ico") +set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/icons/gait-icon.ico") INCLUDE(CPack) diff --git a/common/BaseDiscoveryAgent.cpp b/common/BaseDiscoveryAgent.cpp new file mode 100644 index 0000000..ee77d43 --- /dev/null +++ b/common/BaseDiscoveryAgent.cpp @@ -0,0 +1,8 @@ +#include + +void BaseDiscoveryAgent::query() { + QMap::iterator i; + for (i = _agentCache.begin(); i != _agentCache.end(); ++i) { + emit deviceDiscovered(i.value()); + } +} \ No newline at end of file diff --git a/common/BaseDiscoveryAgent.h b/common/BaseDiscoveryAgent.h index a8e9f8d..54e2794 100644 --- a/common/BaseDiscoveryAgent.h +++ b/common/BaseDiscoveryAgent.h @@ -19,19 +19,24 @@ #include #include "BluetoothAddress.h" +#include class BaseDiscoveryAgent : public QObject{ Q_OBJECT private: + QMap _agentCache; public: - BaseDiscoveryAgent(){ - + BaseDiscoveryAgent(QObject *parent = nullptr) : QObject(parent), BaseDiscoveryAgent::_agentCache(QMap()) { + connect(this, &BaseDiscoveryAgent::deviceDiscovered, [=](BluetoothAddress addr) { + _agentCache.insert(addr.getMac(), addr); + }); } ~BaseDiscoveryAgent(){ } virtual void start() = 0; + void query(); virtual void stop() = 0; signals: void deviceDiscovered(BluetoothAddress address); diff --git a/forms/deviceloadingdialog.cpp b/forms/deviceloadingdialog.cpp new file mode 100644 index 0000000..aa7c126 --- /dev/null +++ b/forms/deviceloadingdialog.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +DeviceLoadingDialog::DeviceLoadingDialog(const QList& payload, QWidget *parent ) : + QDialog(parent), m_payloads(payload){ + QVBoxLayout* verticalLayout = new QVBoxLayout(this); + + +} + +DeviceLoadingDialog::~DeviceLoadingDialog() { + +} \ No newline at end of file diff --git a/forms/deviceloadingdialog.h b/forms/deviceloadingdialog.h new file mode 100644 index 0000000..29a682a --- /dev/null +++ b/forms/deviceloadingdialog.h @@ -0,0 +1,39 @@ +/** +* Copyright 2019 GaitRehabilitation +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef DEVICE_LOADING_DIALOG_H +#define DEVICE_LOADING_DIALOG_H + +#include +#include +#include + +class MetawearWrapperBase; +class MbientConfigPanel; +class DeviceLoadingDialog : public QDialog { + Q_OBJECT +private: + QList m_payloads; + QMap m_mappings; + +public: + explicit DeviceLoadingDialog(const QList& payload,QWidget *parent = nullptr); + ~DeviceLoadingDialog(); +signals: + void OnConfigured(MetawearWrapperBase* result); + +}; +#endif \ No newline at end of file diff --git a/forms/deviceselectdialog.cpp b/forms/deviceselectdialog.cpp index 27ba1dd..3775f75 100644 --- a/forms/deviceselectdialog.cpp +++ b/forms/deviceselectdialog.cpp @@ -19,62 +19,67 @@ #include #include "common/DiscoveryAgent.h" -DeviceSelectDialog::DeviceSelectDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::DeviceSelectDialog), - m_deviceInfo(), - m_deviceBlackList(), - m_discoveryAgent(new DiscoveryAgent()){ - ui->setupUi(this); - - connect(ui->scan, &QPushButton::clicked, this,[=](){ - ui->scan->setEnabled(false); - m_discoveryAgent->start(); - }); - connect(ui->clear, &QPushButton::clicked,this,[=](){ - ui->deviceList->clear(); - m_discoveryAgent->stop(); - }); - // connect(m_discoveryAgent,&QBluetoothDeviceDiscoveryAgent::finished, this, [=](){ui->scan->setEnabled(true); }); - - // An entry is added for every device connected - qRegisterMetaType("BluetoothAddress"); - connect(m_discoveryAgent, &DiscoveryAgent::deviceDiscovered, this,[=](BluetoothAddress info){ - //if(info.name() != "MetaWear") - // return; - if(m_deviceBlackList.contains(info.getMac())) - return; - - - QString label = QString("%1 %2").arg(info.getMac()).arg(info.getTitle()); - m_deviceInfo.insert(label, info); - - QList items = ui->deviceList->findItems(label, Qt::MatchExactly); - if (items.empty()) { - QListWidgetItem *item = new QListWidgetItem(label); - //QBluetoothLocalDevice::Pairing lpairingStatus = localDevice->pairingStatus(info.address()); - //if (lpairingStatus == QBluetoothLocalDevice::Paired || lpairingStatus == QBluetoothLocalDevice::AuthorizedPaired) { - // item->setTextColor(QColor(Qt::green)); - //} else { - item->setTextColor(QColor(Qt::black)); - //} - ui->deviceList->addItem(item); - } - }); - - connect(ui->deviceList, &QListWidget::itemSelectionChanged, this,[=](){ - ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(!(ui->deviceList->selectedItems().count() > 0)); - }); - - ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); +DeviceSelectDialog::DeviceSelectDialog(QWidget * parent): + m_discoveryAgent(new DiscoveryAgent()), + QDialog(parent), + ui(new Ui::DeviceSelectDialog), + m_deviceInfo(), + m_deviceBlackList() +{ + ui->setupUi(this); + + connect(ui->scan, &QPushButton::clicked, this, [=]() { + ui->scan->setEnabled(false); + m_discoveryAgent->start(); + }); + connect(ui->clear, &QPushButton::clicked, this, [=]() { + ui->deviceList->clear(); + m_discoveryAgent->stop(); + }); + // connect(m_discoveryAgent,&QBluetoothDeviceDiscoveryAgent::finished, this, [=](){ui->scan->setEnabled(true); }); + + // An entry is added for every device connected + qRegisterMetaType("BluetoothAddress"); + connect(m_discoveryAgent, &DiscoveryAgent::deviceDiscovered, this, [=](BluetoothAddress info) { + //if(info.name() != "MetaWear") + // return; + if (m_deviceBlackList.contains(info.getMac())) + return; + + + QString label = QString("%1 %2").arg(info.getMac()).arg(info.getTitle()); + m_deviceInfo.insert(label, info); + + QList items = ui->deviceList->findItems(label, Qt::MatchExactly); + if (items.empty()) { + QListWidgetItem *item = new QListWidgetItem(label); + //QBluetoothLocalDevice::Pairing lpairingStatus = localDevice->pairingStatus(info.address()); + //if (lpairingStatus == QBluetoothLocalDevice::Paired || lpairingStatus == QBluetoothLocalDevice::AuthorizedPaired) { + // item->setTextColor(QColor(Qt::green)); + //} else { + item->setTextColor(QColor(Qt::black)); + //} + ui->deviceList->addItem(item); + } + }); + + connect(ui->deviceList, &QListWidget::itemSelectionChanged, this, [=]() { + ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(!(ui->deviceList->selectedItems().count() > 0)); + }); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); + m_discoveryAgent->query(); } -DeviceSelectDialog::~DeviceSelectDialog() { delete ui; } +DeviceSelectDialog::~DeviceSelectDialog() { + delete ui; + delete m_discoveryAgent; +} void DeviceSelectDialog::accept() { if (ui->deviceList->count() == 0) return; - m_discoveryAgent->stop(); ui->scan->setEnabled(true); diff --git a/forms/deviceselectdialog.h b/forms/deviceselectdialog.h index 72fef6e..081490f 100644 --- a/forms/deviceselectdialog.h +++ b/forms/deviceselectdialog.h @@ -37,6 +37,7 @@ class DeviceSelectDialog : public QDialog { QList m_deviceBlackList; public: + explicit DeviceSelectDialog(QWidget *parent = nullptr); ~DeviceSelectDialog() override; diff --git a/forms/mainwindow.cpp b/forms/mainwindow.cpp index 04ce340..a630cb5 100644 --- a/forms/mainwindow.cpp +++ b/forms/mainwindow.cpp @@ -20,6 +20,8 @@ #include "forms/mainwindow.h" #include "forms/profiledialog.h" +#include + #include "forms/sensorpanel.h" #include #include @@ -30,21 +32,24 @@ #include #include #include + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow),m_temporaryData(new QTemporaryDir()),m_triggerSingleShot(),m_triggerTime(0),m_updateTriggerTimer(),m_deviceIndex(0){ ui->setupUi(this); // connect(this,&MainWindow::onConnectedDevices,m_deviceSelectDialog,&DeviceSelectDialog::updateDeviceBlackList); - + connect(ui->actionAddDevice, &QAction::triggered, this,[=](){ ProfileDialog profileDialog(this); connect(&profileDialog,&ProfileDialog::onProfileSelected,this,[=](const QList& payload){ - for (int i = 0; i < payload.length(); ++i) { + DiscoveryAgent agent; + for (int i = 0; i < payload.length(); ++i) { MbientConfigPanel* panel = payload.at(i); - MetawearWrapperBase* wrapper = panel->buildWrapper(); - - auto devicePanel = registerDevice(wrapper); - devicePanel->setName(panel->getName()); + MetawearWrapperBase* wrapper = panel->buildWrapper(agent); + if (wrapper) { + auto devicePanel = registerDevice(wrapper); + devicePanel->setName(panel->getName()); + } } }); profileDialog.exec(); diff --git a/forms/mbientconfigpanel.cpp b/forms/mbientconfigpanel.cpp index a9d25ae..41afef8 100644 --- a/forms/mbientconfigpanel.cpp +++ b/forms/mbientconfigpanel.cpp @@ -262,13 +262,12 @@ void MbientConfigPanel::deserialize(QVariantMap value) } -MetawearWrapperBase* MbientConfigPanel::buildWrapper(){ +MetawearWrapperBase* MbientConfigPanel::buildWrapper(DiscoveryAgent& agent){ // clear wrapper m_wrapper = nullptr; - DiscoveryAgent discoveryAgent; - connect(&discoveryAgent, &DiscoveryAgent::deviceDiscovered, this,[this](BluetoothAddress info) { + connect(&agent, &DiscoveryAgent::deviceDiscovered, this,[this](BluetoothAddress info) { if (QString::compare(info.getMac(), ui->deviceMac->text(), Qt::CaseInsensitive) == 0) { m_wrapper = new MetawearWrapper(info); @@ -329,15 +328,16 @@ MetawearWrapperBase* MbientConfigPanel::buildWrapper(){ timer.setSingleShot(true); QEventLoop loop; - connect(&discoveryAgent,&DiscoveryAgent::finished,&loop,&QEventLoop::quit); + connect(&agent,&DiscoveryAgent::finished,&loop,&QEventLoop::quit); connect(&timer,&QTimer::timeout,&loop,&QEventLoop::quit); // run timer for 10 seconds and if no deivce does not show up then kill it timer.start(10000); - discoveryAgent.start(); + agent.query(); + agent.start(); loop.exec(); - discoveryAgent.stop(); + agent.stop(); timer.stop(); if(m_wrapper == nullptr){ QMessageBox messageBox; @@ -348,7 +348,7 @@ MetawearWrapperBase* MbientConfigPanel::buildWrapper(){ messageBox.exec(); if(messageBox.clickedButton() == lbuttonYes){ // run wrapper again and try to init wrapper - return buildWrapper(); + return buildWrapper(agent); } } diff --git a/forms/mbientconfigpanel.h b/forms/mbientconfigpanel.h index df7f1b3..8a89e31 100644 --- a/forms/mbientconfigpanel.h +++ b/forms/mbientconfigpanel.h @@ -21,6 +21,7 @@ #include #include #include <3rdparty/mbientlab/src/metawear/sensor/gyro_bmi160.h> +#include #define GYRO "gyro" #define MAC "mac" @@ -59,7 +60,7 @@ class MbientConfigPanel : public QWidget { QString getMac(); QString getName(); - MetawearWrapperBase* buildWrapper(); + MetawearWrapperBase* buildWrapper(DiscoveryAgent& agent); MbientConfigPanel(QWidget *parent = nullptr); QVariantMap serialize(); diff --git a/forms/profiledialog.cpp b/forms/profiledialog.cpp index 89003a3..f04dbfd 100644 --- a/forms/profiledialog.cpp +++ b/forms/profiledialog.cpp @@ -18,6 +18,7 @@ #include "forms/profiledialog.h" #include "forms/mbientconfigpanel.h" #include "ui_profiledialog.h" +#include #include #include @@ -34,7 +35,7 @@ #include #include -ProfileDialog::ProfileDialog(QWidget *parent):QDialog(parent), ui(new Ui::ProfileDialog) { +ProfileDialog::ProfileDialog(QWidget *parent):QDialog(parent), ui(new Ui::ProfileDialog){ ui->setupUi(this); qDebug() << QString("%1/profiles").arg(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); if(!QDir().mkpath(QString("%1/profiles").arg(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)))){ diff --git a/icon.ico b/icon.bmp similarity index 100% rename from icon.ico rename to icon.bmp diff --git a/icons/gait-icon.bmp b/icons/gait-icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..984fd159e40075329af10f643ff94130016393f2 GIT binary patch literal 64138 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 literal 0 HcmV?d00001