Skip to content
Snippets Groups Projects
Commit eff02153 authored by Christian Dreher's avatar Christian Dreher
Browse files

Merge branch 'fix/emergency_stop_button' into 'master'

Fix rogue emergency stop button

See merge request !99
parents b48f55fc b316a338
No related branches found
No related tags found
1 merge request!99Fix rogue emergency stop button
Pipeline #20611 passed
Pipeline: RobotAPI

#20612

    ...@@ -40,6 +40,15 @@ set(HEADERS ArmarXGuiApp.h ...@@ -40,6 +40,15 @@ set(HEADERS ArmarXGuiApp.h
    Widgets/GuiUseCaseSelector.ui Widgets/GuiUseCaseSelector.ui
    ) )
    if ( CMAKE_COMPILER_IS_GNUCC )
    # old gcc 8.4.0 on ubuntu 18.04 does not support this flag
    check_cxx_compiler_flag("-Wno-error=deprecated-enum-enum-conversion" SUPPORTS_DEPRECATED_ENUM_ENUM_CONVERSION)
    if (SUPPORTS_DEPRECATED_ENUM_ENUM_CONVERSION)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-enum-enum-conversion")
    endif()
    endif()
    find_package(OpenMP) find_package(OpenMP)
    if (OPENMP_FOUND) if (OPENMP_FOUND)
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    ......
    ...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
    #include <QGridLayout> #include <QGridLayout>
    #include <QShortcut> #include <QShortcut>
    #include <QTimer>
    #include <QToolTip>
    #define EMERGENCY_STOP_PROXY "EmergencyStopMaster" #define EMERGENCY_STOP_PROXY "EmergencyStopMaster"
    ...@@ -31,118 +33,242 @@ ...@@ -31,118 +33,242 @@
    namespace armarx namespace armarx
    { {
    static const std::string ss2_active_tooltip =
    "Release SS2 emergency stop (only possible after a short cooldown period. Shortcut: "
    "Shift+Pause or Shift+End.";
    static const std::string ss2_inactive_tooltip =
    "Enable SS2 emergency stop. Shortcut: Pause or End.";
    EmergencyStopWidget::EmergencyStopWidget(QWidget* parent, ArmarXMainWindow* mainWindow) : EmergencyStopWidget::EmergencyStopWidget(QWidget* parent, ArmarXMainWindow* mainWindow) :
    mainWindow(mainWindow), mainWindow(mainWindow),
    iconNormal(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop.png")))), iconNormal(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop.png")))),
    iconDark(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop-dark.png")))) iconDark(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop-dark.png")))),
    lastKnownEmergencyStopState(EmergencyStopState::eEmergencyStopInactive),
    timer(new QTimer(this))
    { {
    qRegisterMetaType<EmergencyStopState>("EmergencyStopState"); qRegisterMetaType<EmergencyStopState>("EmergencyStopState");
    // Redundant timer apart from the topic in order to recover from bad states if a topic
    // message was lost.
    timer->setInterval(std::chrono::milliseconds(1'000));
    connect(timer, &QTimer::timeout, this, &EmergencyStopWidget::updateEmergencyStopState);
    connect(this,
    &EmergencyStopWidget::startPeriodicStateUpdate,
    timer,
    qOverload<>(&QTimer::start));
    connect(this, &EmergencyStopWidget::stopPeriodicStateUpdate, timer, &QTimer::stop);
    QIcon icon; QIcon icon;
    icon.addPixmap(iconNormal, QIcon::Normal, QIcon::Off); icon.addPixmap(iconNormal, QIcon::Normal, QIcon::Off);
    icon.addPixmap(iconDark, QIcon::Normal, QIcon::On); icon.addPixmap(iconDark, QIcon::Normal, QIcon::On);
    QGridLayout* layout = new QGridLayout(this->getWidget());
    button = new QToolButton(); button = new QToolButton();
    button->setCheckable(true); button->setCheckable(true);
    button->setIcon(icon); button->setIcon(icon);
    button->setIconSize(QSize(68, 28)); button->setIconSize(QSize(68, 28));
    button->setToolTip(QString::fromStdString("Controls the EmergencyStop. When pressed the EmergencyStop is active. Shortcut: Pause Key")); button->setToolTip(QString::fromStdString(ss2_active_tooltip));
    button->setVisible(false); button->setVisible(false);
    layout->addWidget(button, 0, 0); QGridLayout* l = new QGridLayout(this->getWidget());
    layout->setMargin(0); l->setMargin(0);
    layout->setContentsMargins(0, 0, 0, 0); l->setContentsMargins(0, 0, 0, 0);
    this->getWidget()->setLayout(layout); this->getWidget()->setLayout(l);
    emergencyStopShortcut = new QShortcut(this->getWidget()); this->getWidget()->layout()->addWidget(button);
    emergencyStopShortcut->setContext(Qt::ApplicationShortcut);
    emergencyStopShortcut->setKey(Qt::Key_Pause); QShortcut* enableSS2Shortcut1 = new QShortcut(this->getWidget());
    connect(emergencyStopShortcut, SIGNAL(activated()), this, SLOT(clicked())); enableSS2Shortcut1->setContext(Qt::ApplicationShortcut);
    enableSS2Shortcut1->setKey(Qt::Key_Pause);
    connect(button, SIGNAL(clicked(bool)), this, SLOT(clicked(bool))); QShortcut* enableSS2Shortcut2 = new QShortcut(this->getWidget());
    std::stringstream str; enableSS2Shortcut2->setContext(Qt::ApplicationShortcut);
    str << "After EmergencyStop was activated, you have to wait " enableSS2Shortcut2->setKey(Qt::Key_End);
    << deactivationWaitPeriod.count() << " ms before you can deactivate it."; connect(enableSS2Shortcut1, &QShortcut::activated, this, &EmergencyStopWidget::enableSS2);
    button->setToolTip(QString::fromStdString(str.str())); connect(enableSS2Shortcut2, &QShortcut::activated, this, &EmergencyStopWidget::enableSS2);
    QShortcut* releaseSS2Shortcut1 = new QShortcut(this->getWidget());
    releaseSS2Shortcut1->setContext(Qt::ApplicationShortcut);
    releaseSS2Shortcut1->setKey(Qt::SHIFT | Qt::Key_Pause);
    QShortcut* releaseSS2Shortcut2 = new QShortcut(this->getWidget());
    releaseSS2Shortcut2->setContext(Qt::ApplicationShortcut);
    releaseSS2Shortcut2->setKey(Qt::SHIFT | Qt::Key_End);
    connect(releaseSS2Shortcut1, &QShortcut::activated, this, &EmergencyStopWidget::releaseSS2);
    connect(releaseSS2Shortcut2, &QShortcut::activated, this, &EmergencyStopWidget::releaseSS2);
    connect(button, &QPushButton::clicked, this, &EmergencyStopWidget::clicked);
    } }
    QWidget* EmergencyStopWidget::getButtonWidget() QWidget*
    EmergencyStopWidget::getButtonWidget()
    { {
    return this->getWidget(); return this->getWidget();
    } }
    void EmergencyStopWidget::onInitComponent() void
    EmergencyStopWidget::onInitComponent()
    { {
    usingProxy(EMERGENCY_STOP_PROXY); usingProxy(EMERGENCY_STOP_PROXY);
    usingTopic(EMERGENCY_STOP_TOPIC_NAME); usingTopic(EMERGENCY_STOP_TOPIC_NAME);
    } }
    void EmergencyStopWidget::onConnectComponent() void
    EmergencyStopWidget::onConnectComponent()
    { {
    emergencyStopMasterPrx = getProxy < EmergencyStopMasterInterfacePrx>(EMERGENCY_STOP_PROXY); ARMARX_INFO << "SS2 widget connected.";
    emergencyStopMasterPrx = getProxy<EmergencyStopMasterInterfacePrx>(EMERGENCY_STOP_PROXY);
    QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, true)); QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, true));
    QMetaObject::invokeMethod(this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, emergencyStopMasterPrx->getEmergencyStopState())); EmergencyStopState state = EmergencyStopState::eEmergencyStopActive;
    try
    {
    state = emergencyStopMasterPrx->getEmergencyStopState();
    }
    catch (Ice::Exception const& e)
    {
    ARMARX_ERROR << "Could not query SS2 state." << deactivateSpam(2);
    }
    QMetaObject::invokeMethod(
    this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, state));
    emit startPeriodicStateUpdate();
    } }
    void EmergencyStopWidget::onDisconnectComponent() void
    EmergencyStopWidget::onDisconnectComponent()
    { {
    ARMARX_IMPORTANT << "SS2 widget disconnected. This is expected if the earlier connected "
    "robot unit shut down.";
    QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, false)); QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, false));
    emit stopPeriodicStateUpdate();
    } }
    void
    EmergencyStopWidget::loadSettings(QSettings* settings)
    {
    ;
    }
    void EmergencyStopWidget::loadSettings(QSettings* settings) void
    EmergencyStopWidget::saveSettings(QSettings* settings)
    { {
    ;
    }
    void
    EmergencyStopWidget::reportEmergencyStopState(EmergencyStopState state, const Ice::Current&)
    {
    QMetaObject::invokeMethod(
    this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, state));
    } }
    void EmergencyStopWidget::saveSettings(QSettings* settings) void
    EmergencyStopWidget::enableSS2()
    { {
    if (emergencyStopMasterPrx)
    {
    emergencyStopMasterPrx->setEmergencyStopState(EmergencyStopState::eEmergencyStopActive);
    if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
    {
    std::string const message =
    "SS2 already active. Press Shift+Pause or Shift+End to release SS2.";
    QPoint globalPos =
    button->mapToGlobal(QPoint(button->width() / 2, button->height() / 2));
    QToolTip::showText(globalPos, QString::fromStdString(message), button);
    ARMARX_INFO << message;
    }
    }
    } }
    void EmergencyStopWidget::reportEmergencyStopState(EmergencyStopState state, const Ice::Current&) void
    EmergencyStopWidget::releaseSS2()
    { {
    QMetaObject::invokeMethod(this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, state)); if (emergencyStopMasterPrx)
    {
    lastKnownEmergencyStopState = emergencyStopMasterPrx->trySetEmergencyStopState(
    EmergencyStopState::eEmergencyStopInactive);
    if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
    {
    std::string const message = "SS2 cannot be released since it was just activated.";
    QPoint globalPos =
    button->mapToGlobal(QPoint(button->width() / 2, button->height() / 2));
    QToolTip::showText(globalPos, QString::fromStdString(message), button);
    ARMARX_INFO << message;
    }
    }
    } }
    void EmergencyStopWidget::clicked(bool checked) void
    EmergencyStopWidget::clicked(bool checked)
    { {
    EmergencyStopState state = emergencyStopMasterPrx->getEmergencyStopState(); if (not emergencyStopMasterPrx)
    switch (state)
    { {
    case EmergencyStopState::eEmergencyStopActive : return;
    if (clock_t::now() > timeLastActivated + deactivationWaitPeriod) }
    {
    emergencyStopMasterPrx->setEmergencyStopState(EmergencyStopState::eEmergencyStopInactive); switch (lastKnownEmergencyStopState)
    } {
    else case EmergencyStopState::eEmergencyStopActive:
    {
    // Only release SS2 if the state we received still is "Active". Fail otherwise.
    lastKnownEmergencyStopState = emergencyStopMasterPrx->trySetEmergencyStopState(
    EmergencyStopState::eEmergencyStopInactive);
    if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
    { {
    ARMARX_INFO << "SS2 cannot be released since it was just activated.";
    button->setChecked(true); button->setChecked(true);
    } }
    break; }
    case EmergencyStopState::eEmergencyStopInactive : break;
    emergencyStopMasterPrx->setEmergencyStopState(EmergencyStopState::eEmergencyStopActive); case EmergencyStopState::eEmergencyStopInactive:
    // Always enable SS2 without checking.
    enableSS2();
    break; break;
    } }
    } }
    std::string EmergencyStopWidget::getDefaultName() const std::string
    EmergencyStopWidget::getDefaultName() const
    { {
    return "EmergencyStopGuiWidget" + iceNameUUID; return "EmergencyStopGuiWidget" + iceNameUUID;
    } }
    void EmergencyStopWidget::setChecked(const EmergencyStopState state) void
    EmergencyStopWidget::setChecked(const EmergencyStopState state)
    { {
    switch (state) switch (state)
    { {
    case EmergencyStopState::eEmergencyStopActive : case EmergencyStopState::eEmergencyStopActive:
    button->setChecked(true); button->setChecked(true);
    button->setToolTip(QString::fromStdString(ss2_active_tooltip));
    break; break;
    case EmergencyStopState::eEmergencyStopInactive : case EmergencyStopState::eEmergencyStopInactive:
    default:
    button->setChecked(false); button->setChecked(false);
    timeLastActivated = clock_t::now(); button->setToolTip(QString::fromStdString(ss2_inactive_tooltip));
    break; break;
    default : }
    button->setChecked(false);
    lastKnownEmergencyStopState = state;
    }
    void
    EmergencyStopWidget::updateEmergencyStopState()
    {
    try
    {
    if (emergencyStopMasterPrx)
    {
    setChecked(emergencyStopMasterPrx->getEmergencyStopState());
    }
    }
    catch (Ice::Exception const& e)
    {
    ARMARX_ERROR << "Could not query SS2 state." << deactivateSpam(2);
    setChecked(EmergencyStopState::eEmergencyStopActive);
    } }
    } }
    }
    } // namespace armarx
    ...@@ -22,15 +22,17 @@ ...@@ -22,15 +22,17 @@
    #pragma once #pragma once
    #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h>
    #include <ArmarXCore/components/EmergencyStop/EmergencyStop.h>
    #include <ArmarXCore/core/services/tasks/PeriodicTask.h>
    #include <IceUtil/UUID.h>
    #include <QWidget>
    #include <QAction> #include <QAction>
    #include <QPushButton> #include <QPushButton>
    #include <QToolButton> #include <QToolButton>
    #include <QWidget>
    #include <IceUtil/UUID.h>
    #include <ArmarXCore/components/EmergencyStop/EmergencyStop.h>
    #include <ArmarXCore/core/services/tasks/PeriodicTask.h>
    #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h>
    class QLabel; class QLabel;
    class QGridLayout; class QGridLayout;
    ...@@ -43,13 +45,21 @@ namespace armarx ...@@ -43,13 +45,21 @@ namespace armarx
    class EmergencyStopWidget : class EmergencyStopWidget :
    public ArmarXComponentWidgetControllerTemplate<EmergencyStopWidget>, public ArmarXComponentWidgetControllerTemplate<EmergencyStopWidget>,
    public armarx::EmergencyStopListener public armarx::EmergencyStopListener
    { {
    Q_OBJECT Q_OBJECT
    public: public:
    explicit EmergencyStopWidget(QWidget* parent = 0, ArmarXMainWindow* mainWindow = 0); explicit EmergencyStopWidget(QWidget* parent = 0, ArmarXMainWindow* mainWindow = 0);
    QWidget* getButtonWidget(); QWidget* getButtonWidget();
    // void reportEmergencyStopState(EmergencyStopState, const Ice::Current&) override;
    signals:
    void startPeriodicStateUpdate();
    void stopPeriodicStateUpdate();
    public slots: public slots:
    void enableSS2();
    void releaseSS2();
    void clicked(bool = true); void clicked(bool = true);
    // ManagedIceObject interface // ManagedIceObject interface
    ...@@ -61,34 +71,35 @@ namespace armarx ...@@ -61,34 +71,35 @@ namespace armarx
    private slots: private slots:
    void setChecked(const EmergencyStopState); void setChecked(const EmergencyStopState);
    void updateEmergencyStopState();
    private: private:
    ArmarXMainWindow* mainWindow; ArmarXMainWindow* mainWindow;
    QGridLayout* layout;
    QPixmap iconNormal; QPixmap iconNormal;
    QPixmap iconDark; QPixmap iconDark;
    QToolButton* button; QToolButton* button;
    QAction* emergencyStopAction; QAction* emergencyStopAction;
    QShortcut* emergencyStopShortcut;
    EmergencyStopMasterInterfacePrx emergencyStopMasterPrx; EmergencyStopMasterInterfacePrx emergencyStopMasterPrx;
    EmergencyStopState lastKnownEmergencyStopState;
    using clock_t = std::chrono::high_resolution_clock; QTimer* timer;
    static constexpr std::chrono::milliseconds deactivationWaitPeriod{2500};
    clock_t::time_point timeLastActivated;
    // ArmarXWidgetController interface // ArmarXWidgetController interface
    public: public:
    static QString GetWidgetName() static QString
    GetWidgetName()
    { {
    return "EmergencyStopWidget"; return "EmergencyStopWidget";
    } }
    void loadSettings(QSettings* settings) override; void loadSettings(QSettings* settings) override;
    void saveSettings(QSettings* settings) override; void saveSettings(QSettings* settings) override;
    std::string iceNameUUID = IceUtil::generateUUID(); std::string iceNameUUID = IceUtil::generateUUID();
    // EmergencyStopListener interface // EmergencyStopListener interface
    public:
    void reportEmergencyStopState(EmergencyStopState, const Ice::Current&) override; void reportEmergencyStopState(EmergencyStopState, const Ice::Current&) override;
    }; };
    using EmergencyStopWidgetPtr = IceInternal::Handle <EmergencyStopWidget>;
    } using EmergencyStopWidgetPtr = IceInternal::Handle<EmergencyStopWidget>;
    } // namespace armarx
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment