Skip to content

Commit 187e959

Browse files
authored
Primary client power on (UniversalRobots#289)
This allows power-cycling the robot remotely without using the dashboard server.
1 parent 23a6e42 commit 187e959

12 files changed

+458
-7
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_library(urcl
2828
src/primary/robot_message/version_message.cpp
2929
src/primary/robot_message/error_code_message.cpp
3030
src/primary/robot_state/kinematics_info.cpp
31+
src/primary/robot_state/robot_mode_data.cpp
3132
src/rtde/control_package_pause.cpp
3233
src/rtde/control_package_setup_inputs.cpp
3334
src/rtde/control_package_setup_outputs.cpp

include/ur_client_library/exceptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ class TimeoutException : public UrException
120120
{
121121
public:
122122
explicit TimeoutException() = delete;
123+
explicit TimeoutException(const std::string& text, std::chrono::milliseconds timeout) : std::runtime_error(text)
124+
{
125+
std::stringstream ss;
126+
ss << text << "(Configured timeout: " << timeout.count() / 1000.0 << " sec)";
127+
text_ = ss.str();
128+
}
129+
123130
explicit TimeoutException(const std::string& text, timeval timeout) : std::runtime_error(text)
124131
{
125132
std::stringstream ss;

include/ur_client_library/helpers.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#ifndef UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED
3030
#define UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED
3131

32+
#include <chrono>
33+
#include <functional>
3234
#ifdef _WIN32
3335

3436
# define NOMINMAX
@@ -63,5 +65,18 @@ static inline int sched_get_priority_max(int policy)
6365
namespace urcl
6466
{
6567
bool setFiFoScheduling(pthread_t& thread, const int priority);
66-
}
68+
69+
/*!
70+
* \brief Wait for a condition to be true.
71+
*
72+
* This function will wait for a condition to be true. The condition is checked in intervals of \p check_interval.
73+
* If the condition is not met after \p timeout, the function will throw a urcl::TimeoutException.
74+
*
75+
* \param condition The condition to be checked.
76+
* \param timeout The maximum time to wait for the condition to be true.
77+
* \param check_interval The interval in which the condition is checked.
78+
*/
79+
void waitFor(std::function<bool()> condition, const std::chrono::milliseconds timeout,
80+
const std::chrono::milliseconds check_interval = std::chrono::milliseconds(50));
81+
} // namespace urcl
6782
#endif // ifndef UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED

include/ur_client_library/primary/abstract_primary_consumer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "ur_client_library/primary/robot_message/version_message.h"
3535
#include "ur_client_library/primary/robot_message/error_code_message.h"
3636
#include "ur_client_library/primary/robot_state/kinematics_info.h"
37+
#include "ur_client_library/primary/robot_state/robot_mode_data.h"
3738

3839
namespace urcl
3940
{
@@ -75,6 +76,7 @@ class AbstractPrimaryConsumer : public comm::IConsumer<PrimaryPackage>
7576
virtual bool consume(VersionMessage& pkg) = 0;
7677
virtual bool consume(KinematicsInfo& pkg) = 0;
7778
virtual bool consume(ErrorCodeMessage& pkg) = 0;
79+
virtual bool consume(RobotModeData& pkg) = 0;
7880

7981
private:
8082
/* data */

include/ur_client_library/primary/primary_client.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#ifndef UR_CLIENT_LIBRARY_PRIMARY_CLIENT_H_INCLUDED
3232
#define UR_CLIENT_LIBRARY_PRIMARY_CLIENT_H_INCLUDED
3333

34+
#include <chrono>
3435
#include <memory>
3536
#include <deque>
3637

@@ -89,6 +90,60 @@ class PrimaryClient
8990

9091
bool checkCalibration(const std::string& checksum);
9192

93+
/*!
94+
* \brief Commands the robot to power on.
95+
*
96+
* \param validate If true, the function will block until the robot is powered on or the timeout
97+
* passed by.
98+
* \param timeout The maximum time to wait for the robot to confirm the power on command.
99+
*
100+
* \throws urcl::UrException if the command cannot be sent to the robot.
101+
* \throws urcl::TimeoutException if the robot doesn't power on within the given timeout.
102+
*/
103+
void commandPowerOn(const bool validate = true, const std::chrono::milliseconds timeout = std::chrono::seconds(30));
104+
105+
/*!
106+
* \brief Commands the robot to power off.
107+
*
108+
* \param validate If true, the function will block until the robot is powered off or the timeout
109+
* passed by.
110+
* \param timeout The maximum time to wait for the robot to confirm the power off command.
111+
*
112+
* \throws urcl::UrException if the command cannot be sent to the robot.
113+
* \throws urcl::TimeoutException if the robot doesn't power off within the given timeout.
114+
*/
115+
void commandPowerOff(const bool validate = true, const std::chrono::milliseconds timeout = std::chrono::seconds(30));
116+
117+
/*!
118+
* \brief Commands the robot to release the brakes
119+
*
120+
* \param validate If true, the function will block until the robot is running or the timeout
121+
* passed by.
122+
* \param timeout The maximum time to wait for the robot to confirm it is running.
123+
*
124+
* \throws urcl::UrException if the command cannot be sent to the robot.
125+
* \throws urcl::TimeoutException if the robot doesn't release the brakes within the given
126+
* timeout.
127+
*/
128+
void commandBrakeRelease(const bool validate = true,
129+
const std::chrono::milliseconds timeout = std::chrono::seconds(30));
130+
131+
/*!
132+
* \brief Get the latest robot mode.
133+
*
134+
* The robot mode will be updated in the background. This will always show the latest received
135+
* robot mode independent of the time that has passed since receiving it.
136+
*/
137+
RobotMode getRobotMode()
138+
{
139+
std::shared_ptr<RobotModeData> robot_mode_data = consumer_->getRobotModeData();
140+
if (robot_mode_data == nullptr)
141+
{
142+
return RobotMode::UNKNOWN;
143+
}
144+
return static_cast<RobotMode>(consumer_->getRobotModeData()->robot_mode_);
145+
}
146+
92147
private:
93148
/*!
94149
* \brief Reconnects the primary stream used to send program to the robot.

include/ur_client_library/primary/primary_consumer.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#define UR_CLIENT_LIBRARY_PRIMARY_CONSUMER_H_INCLUDED
3030

3131
#include "ur_client_library/primary/abstract_primary_consumer.h"
32+
#include "ur_client_library/primary/robot_state/robot_mode_data.h"
33+
#include "ur_client_library/ur/datatypes.h"
3234

3335
#include <functional>
3436
#include <mutex>
@@ -153,6 +155,14 @@ class PrimaryConsumer : public AbstractPrimaryConsumer
153155
return true;
154156
}
155157

158+
virtual bool consume(RobotModeData& pkg) override
159+
{
160+
URCL_LOG_DEBUG("Robot mode is now %s", robotModeString(static_cast<RobotMode>(pkg.robot_mode_)).c_str());
161+
std::scoped_lock lock(robot_mode_mutex_);
162+
robot_mode_ = std::make_shared<RobotModeData>(pkg);
163+
return true;
164+
}
165+
156166
/*!
157167
* \brief Set callback function which will be triggered whenever error code messages are received
158168
*
@@ -174,9 +184,23 @@ class PrimaryConsumer : public AbstractPrimaryConsumer
174184
return kinematics_info_;
175185
}
176186

187+
/*!
188+
* \brief Get the latest robot mode.
189+
*
190+
* The robot mode will be updated in the background. This will always show the latest received
191+
* robot mode independent of the time that has passed since receiving it.
192+
*/
193+
std::shared_ptr<RobotModeData> getRobotModeData()
194+
{
195+
std::scoped_lock lock(robot_mode_mutex_);
196+
return robot_mode_;
197+
}
198+
177199
private:
178200
std::function<void(ErrorCode&)> error_code_message_callback_;
179201
std::shared_ptr<KinematicsInfo> kinematics_info_;
202+
std::mutex robot_mode_mutex_;
203+
std::shared_ptr<RobotModeData> robot_mode_;
180204
};
181205

182206
} // namespace primary_interface

include/ur_client_library/primary/primary_parser.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "ur_client_library/primary/robot_state/kinematics_info.h"
3030
#include "ur_client_library/primary/robot_message/version_message.h"
3131
#include "ur_client_library/primary/robot_message/error_code_message.h"
32+
#include "ur_client_library/primary/robot_state/robot_mode_data.h"
3233

3334
namespace urcl
3435
{
@@ -150,12 +151,12 @@ class PrimaryParser : public comm::Parser<PrimaryPackage>
150151
{
151152
switch (type)
152153
{
153-
/*case robot_state_type::ROBOT_MODE_DATA:
154-
// SharedRobotModeData* rmd = new SharedRobotModeData();
154+
case RobotStateType::ROBOT_MODE_DATA:
155+
return new RobotModeData(type);
155156

156-
//return new rmd;
157-
case robot_state_type::MASTERBOARD_DATA:
158-
return new MBD;*/
157+
// return new rmd;
158+
// case robot_state_type::MASTERBOARD_DATA:
159+
// return new MBD;*/
159160
case RobotStateType::KINEMATICS_INFO:
160161
return new KinematicsInfo(type);
161162
default:
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2025 Universal Robots A/S
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright
11+
// notice, this list of conditions and the following disclaimer in the
12+
// documentation and/or other materials provided with the distribution.
13+
//
14+
// * Neither the name of the {copyright_holder} nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
// POSSIBILITY OF SUCH DAMAGE.
29+
// -- END LICENSE BLOCK ------------------------------------------------
30+
31+
#ifndef UR_CLIENT_LIBRARY_ROBOT_MODE_DATA_H_INCLUDED
32+
#define UR_CLIENT_LIBRARY_ROBOT_MODE_DATA_H_INCLUDED
33+
34+
#include <stdexcept>
35+
#include "ur_client_library/types.h"
36+
#include "ur_client_library/primary/robot_state.h"
37+
namespace urcl
38+
{
39+
namespace primary_interface
40+
{
41+
42+
/*!
43+
* \brief This messages contains data about the mode of the robot.
44+
*/
45+
class RobotModeData : public RobotState
46+
{
47+
public:
48+
RobotModeData() = delete;
49+
/*!
50+
* \brief Creates a new RobotModeData object.
51+
*
52+
* \param type The type of RobotState message received
53+
*/
54+
RobotModeData(const RobotStateType type) : RobotState(type)
55+
{
56+
}
57+
58+
/*!
59+
* \brief Creates a copy of a RobotModeData object.
60+
*
61+
* \param pkg The RobotModeData object to be copied
62+
*/
63+
RobotModeData(const RobotModeData& pkg);
64+
65+
virtual ~RobotModeData() = default;
66+
67+
/*!
68+
* \brief Sets the attributes of the package by parsing a serialized representation of the
69+
* package.
70+
*
71+
* \param bp A parser containing a serialized version of the package
72+
*
73+
* \returns True, if the package was parsed successfully, false otherwise
74+
*/
75+
virtual bool parseWith(comm::BinParser& bp);
76+
77+
/*!
78+
* \brief Consume this specific package with a specific consumer.
79+
*
80+
* \param consumer Placeholder for the consumer calling this
81+
*
82+
* \returns true on success
83+
*/
84+
virtual bool consumeWith(AbstractPrimaryConsumer& consumer);
85+
86+
/*!
87+
* \brief Produces a human readable representation of the package object.
88+
*
89+
* \returns A string representing the object
90+
*/
91+
virtual std::string toString() const;
92+
93+
uint64_t timestamp_;
94+
bool is_real_robot_connected_;
95+
bool is_real_robot_enabled_;
96+
bool is_robot_power_on_;
97+
bool is_emergency_stopped_;
98+
bool is_protective_stopped_;
99+
bool is_program_running_;
100+
bool is_program_paused_;
101+
int8_t robot_mode_;
102+
uint8_t control_mode_;
103+
double target_speed_fraction_;
104+
double speed_scaling_;
105+
double target_speed_fraction_limit_;
106+
std::string reserved_;
107+
};
108+
109+
} // namespace primary_interface
110+
} // namespace urcl
111+
112+
#endif // ifndef UR_CLIENT_LIBRARY_ROBOT_MODE_DATA_H_INCLUDED

src/helpers.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
*/
2727
//----------------------------------------------------------------------
2828

29+
#include <ur_client_library/exceptions.h>
2930
#include <ur_client_library/helpers.h>
3031
#include <ur_client_library/log.h>
3132

3233
#include <cstring>
3334
#include <fstream>
3435
#include <iostream>
36+
#include <thread>
3537

3638
// clang-format off
3739
// We want to keep the URL in one line to avoid formatting issues. This will make it easier to
@@ -99,4 +101,20 @@ bool setFiFoScheduling(pthread_t& thread, const int priority)
99101
return true;
100102
#endif
101103
}
104+
105+
void waitFor(std::function<bool()> condition, const std::chrono::milliseconds timeout,
106+
const std::chrono::milliseconds check_interval)
107+
{
108+
auto start_time = std::chrono::system_clock::now();
109+
while (std::chrono::system_clock::now() - start_time < timeout)
110+
{
111+
if (condition())
112+
{
113+
return;
114+
}
115+
URCL_LOG_DEBUG("Waiting for condition to be met...");
116+
std::this_thread::sleep_for(check_interval);
117+
}
118+
throw urcl::TimeoutException("Timeout while waiting for condition to be met", timeout);
119+
}
102120
} // namespace urcl

0 commit comments

Comments
 (0)