EmergencyStopWidget.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ArmarXCore::ArmarXObjects::EmergencyStop
17 * @author Stefan Reither ( stef dot reither at web dot de )
18 * @date 2016
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "EmergencyStopWidget.h"
24
25#include <QGridLayout>
26#include <QShortcut>
27#include <QTimer>
28#include <QToolTip>
29
30
31#define EMERGENCY_STOP_PROXY "EmergencyStopMaster"
32#define EMERGENCY_STOP_TOPIC_NAME "EmergencyStop"
33
34namespace armarx
35{
36
37 static const std::string ss2_active_tooltip =
38 "Release SS2 emergency stop (only possible after a short cooldown period. Shortcut: "
39 "Shift+Pause or Shift+End.";
40 static const std::string ss2_inactive_tooltip =
41 "Enable SS2 emergency stop. Shortcut: Pause or End.";
42
44 mainWindow(mainWindow),
45 iconNormal(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop.png")))),
46 iconDark(QPixmap::fromImage(QImage(QString::fromUtf8(":/icons/emergency-stop-dark.png")))),
47 lastKnownEmergencyStopState(EmergencyStopState::eEmergencyStopInactive),
48 timer(new QTimer(this))
49 {
50 qRegisterMetaType<EmergencyStopState>("EmergencyStopState");
51
52 // Redundant timer apart from the topic in order to recover from bad states if a topic
53 // message was lost.
54 timer->setInterval(std::chrono::milliseconds(1'000));
55 connect(timer, &QTimer::timeout, this, &EmergencyStopWidget::updateEmergencyStopState);
56 connect(this,
58 timer,
59 qOverload<>(&QTimer::start));
60 connect(this, &EmergencyStopWidget::stopPeriodicStateUpdate, timer, &QTimer::stop);
61 QIcon icon;
62 icon.addPixmap(iconNormal, QIcon::Normal, QIcon::Off);
63 icon.addPixmap(iconDark, QIcon::Normal, QIcon::On);
64
65 button = new QToolButton();
66 button->setCheckable(true);
67 button->setIcon(icon);
68 button->setIconSize(QSize(68, 28));
69 button->setToolTip(QString::fromStdString(ss2_active_tooltip));
70 button->setVisible(false);
71 QGridLayout* l = new QGridLayout(this->getWidget());
72 l->setMargin(0);
73 l->setContentsMargins(0, 0, 0, 0);
74 this->getWidget()->setLayout(l);
75 this->getWidget()->layout()->addWidget(button);
76
77 QShortcut* enableSS2Shortcut1 = new QShortcut(this->getWidget());
78 enableSS2Shortcut1->setContext(Qt::ApplicationShortcut);
79 enableSS2Shortcut1->setKey(Qt::Key_Pause);
80 QShortcut* enableSS2Shortcut2 = new QShortcut(this->getWidget());
81 enableSS2Shortcut2->setContext(Qt::ApplicationShortcut);
82 enableSS2Shortcut2->setKey(Qt::Key_End);
83 connect(enableSS2Shortcut1, &QShortcut::activated, this, &EmergencyStopWidget::enableSS2);
84 connect(enableSS2Shortcut2, &QShortcut::activated, this, &EmergencyStopWidget::enableSS2);
85
86 QShortcut* releaseSS2Shortcut1 = new QShortcut(this->getWidget());
87 releaseSS2Shortcut1->setContext(Qt::ApplicationShortcut);
88 releaseSS2Shortcut1->setKey(Qt::SHIFT | Qt::Key_Pause);
89 QShortcut* releaseSS2Shortcut2 = new QShortcut(this->getWidget());
90 releaseSS2Shortcut2->setContext(Qt::ApplicationShortcut);
91 releaseSS2Shortcut2->setKey(Qt::SHIFT | Qt::Key_End);
92 connect(releaseSS2Shortcut1, &QShortcut::activated, this, &EmergencyStopWidget::releaseSS2);
93 connect(releaseSS2Shortcut2, &QShortcut::activated, this, &EmergencyStopWidget::releaseSS2);
94
95 connect(button, &QPushButton::clicked, this, &EmergencyStopWidget::clicked);
96 }
97
98 QWidget*
100 {
101 return this->getWidget();
102 }
103
104 void
110
111 void
113 {
114 ARMARX_INFO << "SS2 widget connected.";
116 QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, true));
117 EmergencyStopState state = EmergencyStopState::eEmergencyStopActive;
118
119 try
120 {
121 state = emergencyStopMasterPrx->getEmergencyStopState();
122 }
123 catch (Ice::Exception const& e)
124 {
125 ARMARX_ERROR << "Could not query SS2 state." << deactivateSpam(2);
126 }
127
128 QMetaObject::invokeMethod(
129 this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, state));
130
132 }
133
134 void
136 {
137 ARMARX_IMPORTANT << "SS2 widget disconnected. This is expected if the earlier connected "
138 "robot unit shut down.";
139 QMetaObject::invokeMethod(button, "setVisible", Qt::QueuedConnection, Q_ARG(bool, false));
140
142 }
143
144 void
146 {
147 ;
148 }
149
150 void
152 {
153 ;
154 }
155
156 void
157 EmergencyStopWidget::reportEmergencyStopState(EmergencyStopState state, const Ice::Current&)
158 {
159 QMetaObject::invokeMethod(
160 this, "setChecked", Qt::QueuedConnection, Q_ARG(EmergencyStopState, state));
161 }
162
163 void
165 {
166 if (emergencyStopMasterPrx)
167 {
168 emergencyStopMasterPrx->setEmergencyStopState(EmergencyStopState::eEmergencyStopActive);
169
170 if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
171 {
172 std::string const message =
173 "SS2 already active. Press Shift+Pause or Shift+End to release SS2.";
174
175 QPoint globalPos =
176 button->mapToGlobal(QPoint(button->width() / 2, button->height() / 2));
177 QToolTip::showText(globalPos, QString::fromStdString(message), button);
178 ARMARX_INFO << message;
179 }
180 }
181 }
182
183 void
185 {
186 if (emergencyStopMasterPrx)
187 {
188 lastKnownEmergencyStopState = releaseSS2OnMaster();
189
190 if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
191 {
192 std::string const message = "SS2 cannot be released since it was just activated.";
193
194 QPoint globalPos =
195 button->mapToGlobal(QPoint(button->width() / 2, button->height() / 2));
196 QToolTip::showText(globalPos, QString::fromStdString(message), button);
197 ARMARX_INFO << message;
198 }
199 }
200 }
201
202 void
204 {
205 if (not emergencyStopMasterPrx)
206 {
207 return;
208 }
209
210 switch (lastKnownEmergencyStopState)
211 {
212 case EmergencyStopState::eEmergencyStopActive:
213 {
214 // Only release SS2 if the state we received still is "Active". Fail otherwise.
215 lastKnownEmergencyStopState = releaseSS2OnMaster();
216 if (lastKnownEmergencyStopState == EmergencyStopState::eEmergencyStopActive)
217 {
218 ARMARX_INFO << "SS2 cannot be released since it was just activated.";
219 button->setChecked(true);
220 }
221 }
222 break;
223 case EmergencyStopState::eEmergencyStopInactive:
224 // Always enable SS2 without checking.
225 enableSS2();
226 break;
227 }
228 }
229
230 std::string
232 {
233 return "EmergencyStopGuiWidget" + iceNameUUID;
234 }
235
236 EmergencyStopState
238 {
239 // If emergency stop master is not available, assume that SS2 is still active and cannot
240 // be released.
241 if (not emergencyStopMasterPrx)
242 {
243 return EmergencyStopState::eEmergencyStopActive;
244 }
245
246 try
247 {
248 return emergencyStopMasterPrx->trySetEmergencyStopState(
249 EmergencyStopState::eEmergencyStopInactive);
250 }
251 // This catch is only required as long as the stable robot workspaces are using the old
252 // interface. The changes for the new interface were done in July 2024. It can safely be
253 // removed after a few months.
254 catch (Ice::OperationNotExistException const&)
255 {
256 ARMARX_WARNING << "Cannot safely release SS2. This only happens if your ArmarX GUI "
257 "is using a newer version of the emergency stop interface than the "
258 "emergency stop master or the robot unit provides. Will use a less "
259 "safe fallback interface method now.";
260 emergencyStopMasterPrx->setEmergencyStopState(
261 EmergencyStopState::eEmergencyStopInactive);
262 return emergencyStopMasterPrx->getEmergencyStopState();
263 }
264 }
265
266 void
267 EmergencyStopWidget::setChecked(const EmergencyStopState state)
268 {
269 switch (state)
270 {
271 case EmergencyStopState::eEmergencyStopActive:
272 button->setChecked(true);
273 button->setToolTip(QString::fromStdString(ss2_active_tooltip));
274 break;
275 case EmergencyStopState::eEmergencyStopInactive:
276 default:
277 button->setChecked(false);
278 button->setToolTip(QString::fromStdString(ss2_inactive_tooltip));
279 break;
280 }
281
282 lastKnownEmergencyStopState = state;
283 }
284
285 void
286 EmergencyStopWidget::updateEmergencyStopState()
287 {
288 try
289 {
290 if (emergencyStopMasterPrx)
291 {
292 setChecked(emergencyStopMasterPrx->getEmergencyStopState());
293 }
294 }
295 catch (Ice::Exception const& e)
296 {
297 ARMARX_INFO << "Could not query SS2 state." << deactivateSpam(60);
298 setChecked(EmergencyStopState::eEmergencyStopActive);
299 }
300 }
301
302} // namespace armarx
#define EMERGENCY_STOP_TOPIC_NAME
#define EMERGENCY_STOP_PROXY
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
The ArmarXMainWindow class.
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
void onInitComponent() override
Pure virtual hook for the subclass.
void onDisconnectComponent() override
Hook for subclass.
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
void reportEmergencyStopState(EmergencyStopState, const Ice::Current &) override
EmergencyStopState releaseSS2OnMaster() const
void onConnectComponent() override
Pure virtual hook for the subclass.
EmergencyStopWidget(QWidget *parent=0, ArmarXMainWindow *mainWindow=0)
std::string getDefaultName() const override
Retrieve default name of component.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.