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 
34 namespace 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,
57  &EmergencyStopWidget::startPeriodicStateUpdate,
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*
99  EmergencyStopWidget::getButtonWidget()
100  {
101  return this->getWidget();
102  }
103 
104  void
105  EmergencyStopWidget::onInitComponent()
106  {
107  usingProxy(EMERGENCY_STOP_PROXY);
108  usingTopic(EMERGENCY_STOP_TOPIC_NAME);
109  }
110 
111  void
112  EmergencyStopWidget::onConnectComponent()
113  {
114  ARMARX_INFO << "SS2 widget connected.";
115  emergencyStopMasterPrx = getProxy<EmergencyStopMasterInterfacePrx>(EMERGENCY_STOP_PROXY);
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 
131  emit startPeriodicStateUpdate();
132  }
133 
134  void
135  EmergencyStopWidget::onDisconnectComponent()
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 
141  emit stopPeriodicStateUpdate();
142  }
143 
144  void
145  EmergencyStopWidget::loadSettings(QSettings* settings)
146  {
147  ;
148  }
149 
150  void
151  EmergencyStopWidget::saveSettings(QSettings* settings)
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
164  EmergencyStopWidget::enableSS2()
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
184  EmergencyStopWidget::releaseSS2()
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
203  EmergencyStopWidget::clicked(bool checked)
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
231  EmergencyStopWidget::getDefaultName() const
232  {
233  return "EmergencyStopGuiWidget" + iceNameUUID;
234  }
235 
236 
237  EmergencyStopState
238  EmergencyStopWidget::releaseSS2OnMaster() const
239  {
240  // If emergency stop master is not available, assume that SS2 is still active and cannot
241  // be released.
242  if (not emergencyStopMasterPrx)
243  {
244  return EmergencyStopState::eEmergencyStopActive;
245  }
246 
247  try
248  {
249  return emergencyStopMasterPrx->trySetEmergencyStopState(
250  EmergencyStopState::eEmergencyStopInactive);
251  }
252  // This catch is only required as long as the stable robot workspaces are using the old
253  // interface. The changes for the new interface were done in July 2024. It can safely be
254  // removed after a few months.
255  catch (Ice::OperationNotExistException const&)
256  {
257  ARMARX_WARNING << "Cannot safely release SS2. This only happens if your ArmarX GUI "
258  "is using a newer version of the emergency stop interface than the "
259  "emergency stop master or the robot unit provides. Will use a less "
260  "safe fallback interface method now.";
261  emergencyStopMasterPrx->setEmergencyStopState(
262  EmergencyStopState::eEmergencyStopInactive);
263  return emergencyStopMasterPrx->getEmergencyStopState();
264  }
265  }
266 
267  void
268  EmergencyStopWidget::setChecked(const EmergencyStopState state)
269  {
270  switch (state)
271  {
272  case EmergencyStopState::eEmergencyStopActive:
273  button->setChecked(true);
274  button->setToolTip(QString::fromStdString(ss2_active_tooltip));
275  break;
276  case EmergencyStopState::eEmergencyStopInactive:
277  default:
278  button->setChecked(false);
279  button->setToolTip(QString::fromStdString(ss2_inactive_tooltip));
280  break;
281  }
282 
283  lastKnownEmergencyStopState = state;
284  }
285 
286  void
287  EmergencyStopWidget::updateEmergencyStopState()
288  {
289  try
290  {
291  if (emergencyStopMasterPrx)
292  {
293  setChecked(emergencyStopMasterPrx->getEmergencyStopState());
294  }
295  }
296  catch (Ice::Exception const& e)
297  {
298  ARMARX_ERROR << "Could not query SS2 state." << deactivateSpam(2);
299  setChecked(EmergencyStopState::eEmergencyStopActive);
300  }
301  }
302 
303 } // namespace armarx
armarx::EmergencyStopWidget::EmergencyStopWidget
EmergencyStopWidget(QWidget *parent=0, ArmarXMainWindow *mainWindow=0)
Definition: EmergencyStopWidget.cpp:43
armarx::ArmarXMainWindow
The ArmarXMainWindow class.
Definition: ArmarXMainWindow.h:80
EmergencyStopWidget.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28