Skip to content

Commit 8ad298f

Browse files
authored
PrimaryClient: Add methods to unlock protective stop and stop the program (#292)
Add functionalities to stop the running program and to unlock a protective stop through the primary client.
1 parent ce6aec2 commit 8ad298f

File tree

3 files changed

+169
-2
lines changed

3 files changed

+169
-2
lines changed

include/ur_client_library/primary/primary_client.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,33 @@ class PrimaryClient
128128
void commandBrakeRelease(const bool validate = true,
129129
const std::chrono::milliseconds timeout = std::chrono::seconds(30));
130130

131+
/*!
132+
* \brief Commands the robot to unlock the protective stop
133+
*
134+
* \param validate If true, the function will block until the protective stop is released or the
135+
* timeout passed by.
136+
* \param timeout The maximum time to wait for the robot to confirm it is no longer protective
137+
* stopped.
138+
*
139+
* \throws urcl::UrException if the command cannot be sent to the robot.
140+
* \throws urcl::TimeoutException if the robot doesn't unlock the protective stop within the
141+
* given timeout.
142+
*/
143+
void commandUnlockProtectiveStop(const bool validate = true,
144+
const std::chrono::milliseconds timeout = std::chrono::milliseconds(5000));
145+
146+
/*!
147+
* /brief Stop execution of a running or paused program
148+
*
149+
* \param validate If true, the function will block until the robot has stopped or the timeout
150+
* passed by.
151+
* \param timeout The maximum time to wait for the robot to stop the program.
152+
*
153+
* \throws urcl::UrException if the command cannot be sent to the robot.
154+
* \throws urcl::TimeoutException if the robot doesn't stop the program within the given timeout.
155+
*/
156+
void commandStop(const bool validate = true, const std::chrono::milliseconds timeout = std::chrono::seconds(2));
157+
131158
/*!
132159
* \brief Get the latest robot mode.
133160
*
@@ -144,6 +171,36 @@ class PrimaryClient
144171
return static_cast<RobotMode>(consumer_->getRobotModeData()->robot_mode_);
145172
}
146173

174+
/*!
175+
* \brief Get the latest robot mode data.
176+
*
177+
* The robot's mode data will be updated in the background. This will always show the latest received
178+
* state independent of the time that has passed since receiving it. The return value of this
179+
* will be a nullptr if no data has been received yet.
180+
*/
181+
std::shared_ptr<RobotModeData> getRobotModeData()
182+
{
183+
return consumer_->getRobotModeData();
184+
}
185+
186+
/*!
187+
* \brief Query if the robot is protective stopped.
188+
*
189+
* The robot's protective_stop state will be updated in the background. This will always show the latest received
190+
* state independent of the time that has passed since receiving it.
191+
*
192+
* \throws UrException when no robot mode data has been received, yet.
193+
*/
194+
bool isRobotProtectiveStopped()
195+
{
196+
std::shared_ptr<RobotModeData> robot_mode_data = consumer_->getRobotModeData();
197+
if (robot_mode_data == nullptr)
198+
{
199+
throw UrException("Robot mode data is a nullptr. Probably it hasn't been received, yet.");
200+
}
201+
return robot_mode_data->is_protective_stopped_;
202+
}
203+
147204
private:
148205
/*!
149206
* \brief Reconnects the primary stream used to send program to the robot.

src/primary/primary_client.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ void PrimaryClient::commandPowerOff(const bool validate, const std::chrono::mill
193193
{
194194
waitFor([this]() { return getRobotMode() == RobotMode::POWER_OFF; }, timeout);
195195
}
196-
catch (const std::exception&)
196+
catch (const TimeoutException&)
197197
{
198198
throw TimeoutException("Robot did not power off within the given timeout", timeout);
199199
}
@@ -212,12 +212,61 @@ void PrimaryClient::commandBrakeRelease(const bool validate, const std::chrono::
212212
{
213213
waitFor([this]() { return getRobotMode() == RobotMode::RUNNING; }, timeout);
214214
}
215-
catch (const std::exception&)
215+
catch (const TimeoutException&)
216216
{
217217
throw TimeoutException("Robot did not release the brakes within the given timeout", timeout);
218218
}
219219
}
220220
}
221221

222+
void PrimaryClient::commandUnlockProtectiveStop(const bool validate, const std::chrono::milliseconds timeout)
223+
{
224+
if (!sendScript("set unlock protective stop"))
225+
{
226+
throw UrException("Failed to send unlock protective stop command to robot");
227+
}
228+
if (validate)
229+
{
230+
try
231+
{
232+
waitFor([this]() { return consumer_->getRobotModeData()->is_protective_stopped_ == false; }, timeout);
233+
}
234+
catch (const TimeoutException&)
235+
{
236+
throw TimeoutException("Robot did not unlock the protective stop within the given timeout", timeout);
237+
}
238+
}
239+
}
240+
241+
void PrimaryClient::commandStop(const bool validate, const std::chrono::milliseconds timeout)
242+
{
243+
std::shared_ptr<RobotModeData> robot_mode_data = consumer_->getRobotModeData();
244+
if (robot_mode_data == nullptr)
245+
{
246+
throw UrException("Stopping a program while robot state is unknown. This should not happen");
247+
}
248+
249+
if (!sendScript("stop program"))
250+
{
251+
throw UrException("Failed to send the command `stop program` to robot");
252+
}
253+
if (validate)
254+
{
255+
try
256+
{
257+
waitFor(
258+
[this]() {
259+
return !consumer_->getRobotModeData()->is_program_running_ &&
260+
!consumer_->getRobotModeData()->is_program_paused_;
261+
},
262+
timeout);
263+
}
264+
catch (const TimeoutException&)
265+
{
266+
throw TimeoutException("Robot did not stop the program within the given timeout", timeout);
267+
}
268+
}
269+
}
270+
222271
} // namespace primary_interface
223272
} // namespace urcl

tests/test_primary_client.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,71 @@ TEST_F(PrimaryClientTest, test_power_cycle_commands)
110110
EXPECT_NO_THROW(waitFor([this]() { return client_->getRobotMode() == RobotMode::RUNNING; }, timeout));
111111
}
112112

113+
TEST_F(PrimaryClientTest, test_unlock_protective_stop)
114+
{
115+
EXPECT_NO_THROW(client_->start());
116+
EXPECT_NO_THROW(client_->commandBrakeRelease(true, std::chrono::milliseconds(20000)));
117+
client_->sendScript("protective_stop()");
118+
EXPECT_NO_THROW(waitFor([this]() { return client_->isRobotProtectiveStopped(); }, std::chrono::milliseconds(1000)));
119+
// This will not happen immediately
120+
EXPECT_THROW(client_->commandUnlockProtectiveStop(true, std::chrono::milliseconds(1)), TimeoutException);
121+
122+
// It is not allowed to unlock the protective stop immediately
123+
std::this_thread::sleep_for(std::chrono::seconds(5));
124+
EXPECT_NO_THROW(client_->commandUnlockProtectiveStop());
125+
}
126+
113127
TEST_F(PrimaryClientTest, test_uninitialized_primary_client)
114128
{
115129
// The client is not started yet, so the robot mode should be UNKNOWN
116130
ASSERT_EQ(client_->getRobotMode(), RobotMode::UNKNOWN);
131+
ASSERT_THROW(client_->isRobotProtectiveStopped(), UrException);
132+
}
133+
134+
TEST_F(PrimaryClientTest, test_stop_command)
135+
{
136+
// Without started communication the latest robot mode data is a nullptr
137+
EXPECT_THROW(client_->commandStop(), UrException);
138+
139+
EXPECT_NO_THROW(client_->start());
140+
EXPECT_NO_THROW(client_->commandPowerOff());
141+
EXPECT_NO_THROW(client_->commandBrakeRelease());
142+
143+
const std::string script_code = "def test_fun():\n"
144+
" while True:\n"
145+
" textmsg(\"still running\")\n"
146+
" sleep(1.0)\n"
147+
" sync()\n"
148+
" end\n"
149+
"end";
150+
151+
EXPECT_TRUE(client_->sendScript(script_code));
152+
waitFor([this]() { return client_->getRobotModeData()->is_program_running_; }, std::chrono::seconds(5));
153+
154+
EXPECT_NO_THROW(client_->commandStop());
155+
EXPECT_FALSE(client_->getRobotModeData()->is_program_running_);
156+
157+
// Without a program running it should not throw an exception
158+
EXPECT_NO_THROW(client_->commandStop());
159+
160+
EXPECT_TRUE(client_->sendScript(script_code));
161+
waitFor([this]() { return client_->getRobotModeData()->is_program_running_; }, std::chrono::seconds(5));
162+
EXPECT_THROW(client_->commandStop(true, std::chrono::milliseconds(1)), TimeoutException);
163+
EXPECT_NO_THROW(waitFor(
164+
[this]() {
165+
return !client_->getRobotModeData()->is_program_running_ && !client_->getRobotModeData()->is_program_paused_;
166+
},
167+
std::chrono::seconds(5)));
168+
169+
// without validation
170+
EXPECT_TRUE(client_->sendScript(script_code));
171+
waitFor([this]() { return client_->getRobotModeData()->is_program_running_; }, std::chrono::seconds(5));
172+
EXPECT_NO_THROW(client_->commandStop(false));
173+
EXPECT_NO_THROW(waitFor(
174+
[this]() {
175+
return !client_->getRobotModeData()->is_program_running_ && !client_->getRobotModeData()->is_program_paused_;
176+
},
177+
std::chrono::seconds(5)));
117178
}
118179

119180
int main(int argc, char* argv[])

0 commit comments

Comments
 (0)