primary_client.cpp
Go to the documentation of this file.
1 // -- BEGIN LICENSE BLOCK ----------------------------------------------
2 // Copyright © 2024-2025 Ocado Group
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 
36 #include <chrono>
37 namespace urcl
38 {
39 namespace primary_interface
40 {
41 PrimaryClient::PrimaryClient(const std::string& robot_ip, comm::INotifier& notifier)
42  : stream_(robot_ip, UR_PRIMARY_PORT)
43 {
45 
46  consumer_.reset(new PrimaryConsumer());
47  consumer_->setErrorCodeMessageCallback(std::bind(&PrimaryClient::errorMessageCallback, this, std::placeholders::_1));
48 
49  // Configure multi consumer even though we only have one consumer as default, as this enables the user to add more
50  // consumers after the object has been created
51  std::vector<std::shared_ptr<comm::IConsumer<PrimaryPackage>>> consumers;
52  consumers.push_back(consumer_);
54 
55  pipeline_.reset(
56  new comm::Pipeline<PrimaryPackage>(*prod_, multi_consumer_.get(), "PrimaryClient Pipeline", notifier_));
57 }
58 
60 {
61  URCL_LOG_INFO("Stopping primary client pipeline");
62  pipeline_->stop();
63 }
64 
65 void PrimaryClient::start(const size_t max_num_tries, const std::chrono::milliseconds reconnection_time)
66 {
67  URCL_LOG_INFO("Starting primary client pipeline");
68  pipeline_->init(max_num_tries, reconnection_time);
69  pipeline_->run();
70 }
71 
73 {
74  pipeline_->stop();
75  stream_.close();
76 }
77 
79 {
80  multi_consumer_->addConsumer(primary_consumer);
81 }
82 
84 {
85  multi_consumer_->removeConsumer(primary_consumer);
86 }
87 
89 {
90  std::lock_guard<std::mutex> lock_guard(error_code_queue_mutex_);
91  error_code_queue_.push_back(code);
92 }
93 
94 std::deque<ErrorCode> PrimaryClient::getErrorCodes()
95 {
96  std::lock_guard<std::mutex> lock_guard(error_code_queue_mutex_);
97  std::deque<ErrorCode> error_codes;
98  error_codes = error_code_queue_;
99  error_code_queue_.clear();
100  return error_codes;
101 }
102 
103 bool PrimaryClient::sendScript(const std::string& program)
104 {
105  // urscripts (snippets) must end with a newline, or otherwise the controller's runtime will
106  // not execute them. To avoid problems, we always just append a newline here, even if
107  // there may already be one.
108  auto program_with_newline = program + '\n';
109 
110  size_t len = program_with_newline.size();
111  const uint8_t* data = reinterpret_cast<const uint8_t*>(program_with_newline.c_str());
112  size_t written;
113 
114  const auto send_script_contents = [this, program_with_newline, data, len,
115  &written](const std::string&& description) -> bool {
116  if (stream_.write(data, len, written))
117  {
118  URCL_LOG_DEBUG("Sent program to robot:\n%s", program_with_newline.c_str());
119  return true;
120  }
121  const std::string error_message = "Could not send program to robot: " + description;
122  URCL_LOG_ERROR(error_message.c_str());
123  return false;
124  };
125 
126  if (send_script_contents("initial attempt"))
127  {
128  return true;
129  }
130 
131  if (reconnectStream())
132  {
133  return send_script_contents("after reconnecting primary stream");
134  }
135 
136  return false;
137 }
138 
140 {
141  URCL_LOG_DEBUG("Closing primary stream...");
142  stream_.close();
143  if (stream_.connect())
144  {
145  URCL_LOG_DEBUG("Primary stream connected");
146  return true;
147  }
148  URCL_LOG_ERROR("Failed to reconnect primary stream!");
149  return false;
150 }
151 
152 bool PrimaryClient::checkCalibration(const std::string& checksum)
153 {
154  std::shared_ptr<primary_interface::KinematicsInfo> kin_info = consumer_->getKinematicsInfo();
155  while (kin_info == nullptr)
156  {
157  std::this_thread::sleep_for(std::chrono::seconds(1));
158  kin_info = consumer_->getKinematicsInfo();
159  }
160  URCL_LOG_DEBUG("Got calibration information from robot.");
161 
162  return kin_info->toHash() == checksum;
163 }
164 
165 void PrimaryClient::commandPowerOn(const bool validate, const std::chrono::milliseconds timeout)
166 {
167  if (!sendScript("power on"))
168  {
169  throw UrException("Failed to send power on command to robot");
170  }
171 
172  if (validate)
173  {
174  try
175  {
176  waitFor([this]() { return getRobotMode() == RobotMode::IDLE; }, timeout);
177  }
178  catch (const TimeoutException& ex)
179  {
180  throw TimeoutException("Robot did not power on within the given timeout", timeout);
181  }
182  }
183 }
184 
185 void PrimaryClient::commandPowerOff(const bool validate, const std::chrono::milliseconds timeout)
186 {
187  if (!sendScript("power off"))
188  {
189  throw UrException("Failed to send power off command to robot");
190  }
191  if (validate)
192  {
193  try
194  {
195  waitFor([this]() { return getRobotMode() == RobotMode::POWER_OFF; }, timeout);
196  }
197  catch (const TimeoutException&)
198  {
199  throw TimeoutException("Robot did not power off within the given timeout", timeout);
200  }
201  }
202 }
203 
204 void PrimaryClient::commandBrakeRelease(const bool validate, const std::chrono::milliseconds timeout)
205 {
206  if (!sendScript("set robotmode run"))
207  {
208  throw UrException("Failed to send brake release command to robot");
209  }
210  if (validate)
211  {
212  try
213  {
214  waitFor([this]() { return getRobotMode() == RobotMode::RUNNING; }, timeout);
215  }
216  catch (const TimeoutException&)
217  {
218  throw TimeoutException("Robot did not release the brakes within the given timeout", timeout);
219  }
220  }
221 }
222 
223 void PrimaryClient::commandUnlockProtectiveStop(const bool validate, const std::chrono::milliseconds timeout)
224 {
225  if (!sendScript("set unlock protective stop"))
226  {
227  throw UrException("Failed to send unlock protective stop command to robot");
228  }
229  if (validate)
230  {
231  try
232  {
233  waitFor([this]() { return consumer_->getRobotModeData()->is_protective_stopped_ == false; }, timeout);
234  }
235  catch (const TimeoutException&)
236  {
237  throw TimeoutException("Robot did not unlock the protective stop within the given timeout", timeout);
238  }
239  }
240 }
241 
242 void PrimaryClient::commandStop(const bool validate, const std::chrono::milliseconds timeout)
243 {
244  std::shared_ptr<RobotModeData> robot_mode_data = consumer_->getRobotModeData();
245  if (robot_mode_data == nullptr)
246  {
247  throw UrException("Stopping a program while robot state is unknown. This should not happen");
248  }
249 
250  if (!sendScript("stop program"))
251  {
252  throw UrException("Failed to send the command `stop program` to robot");
253  }
254  if (validate)
255  {
256  try
257  {
258  waitFor(
259  [this]() {
260  return !consumer_->getRobotModeData()->is_program_running_ &&
261  !consumer_->getRobotModeData()->is_program_paused_;
262  },
263  timeout);
264  }
265  catch (const TimeoutException&)
266  {
267  throw TimeoutException("Robot did not stop the program within the given timeout", timeout);
268  }
269  }
270 }
271 std::shared_ptr<VersionInformation> PrimaryClient::getRobotVersion(bool blocking,
272  const std::chrono::milliseconds timeout)
273 {
274  if (blocking)
275  {
276  waitFor([this]() { return consumer_->getVersionInformation() != nullptr; }, timeout);
277  }
278 
279  return consumer_->getVersionInformation();
280 }
281 
283 {
284  std::shared_ptr<ConfigurationData> configuration_data = consumer_->getConfigurationData();
285  if (configuration_data == nullptr)
286  {
287  return RobotType::UNDEFINED;
288  }
289  return static_cast<RobotType>(configuration_data->robot_type_);
290 }
291 
292 } // namespace primary_interface
293 } // namespace urcl
urcl::primary_interface::PrimaryClient::addPrimaryConsumer
void addPrimaryConsumer(std::shared_ptr< comm::IConsumer< PrimaryPackage >> primary_consumer)
Adds a primary consumer to the list of consumers.
Definition: primary_client.cpp:78
urcl::primary_interface::PrimaryClient::stream_
comm::URStream< PrimaryPackage > stream_
Definition: primary_client.h:259
urcl::primary_interface::PrimaryClient::multi_consumer_
std::unique_ptr< comm::MultiConsumer< PrimaryPackage > > multi_consumer_
Definition: primary_client.h:255
exceptions.h
urcl::primary_interface::PrimaryClient::parser_
PrimaryParser parser_
Definition: primary_client.h:253
urcl::RobotType::UNDEFINED
@ UNDEFINED
robot_message.h
urcl::primary_interface::PrimaryClient::checkCalibration
bool checkCalibration(const std::string &checksum)
Definition: primary_client.cpp:152
urcl::primary_interface::PrimaryClient::sendScript
bool sendScript(const std::string &program)
Sends a custom script program to the robot.
Definition: primary_client.cpp:103
urcl::primary_interface::PrimaryConsumer
Primary consumer implementation.
Definition: primary_consumer.h:50
urcl::comm::MultiConsumer
Consumer, that allows one product to be consumed by multiple arbitrary consumers.
Definition: pipeline.h:93
urcl::primary_interface::PrimaryClient::pipeline_
std::unique_ptr< comm::Pipeline< PrimaryPackage > > pipeline_
Definition: primary_client.h:261
urcl::primary_interface::PrimaryClient::error_code_queue_
std::deque< ErrorCode > error_code_queue_
Definition: primary_client.h:264
urcl::waitFor
void waitFor(std::function< bool()> condition, const std::chrono::milliseconds timeout, const std::chrono::milliseconds check_interval=std::chrono::milliseconds(50))
Wait for a condition to be true.
Definition: helpers.cpp:105
urcl
Definition: bin_parser.h:36
urcl::primary_interface::PrimaryClient::stop
void stop()
Definition: primary_client.cpp:72
URCL_LOG_ERROR
#define URCL_LOG_ERROR(...)
Definition: log.h:26
urcl::RobotMode::RUNNING
@ RUNNING
urcl::primary_interface::PrimaryClient::getRobotType
RobotType getRobotType()
Get the Robot type.
Definition: primary_client.cpp:282
URCL_LOG_DEBUG
#define URCL_LOG_DEBUG(...)
Definition: log.h:23
urcl::primary_interface::PrimaryClient::commandBrakeRelease
void commandBrakeRelease(const bool validate=true, const std::chrono::milliseconds timeout=std::chrono::seconds(30))
Commands the robot to release the brakes.
Definition: primary_client.cpp:204
urcl::comm::URProducer
A general producer for URPackages. Implements funcionality to produce packages by reading and parsing...
Definition: producer.h:40
urcl::primary_interface::PrimaryClient::commandStop
void commandStop(const bool validate=true, const std::chrono::milliseconds timeout=std::chrono::seconds(2))
Definition: primary_client.cpp:242
urcl::UrException
Our base class for exceptions. Specialized exceptions should inherit from those.
Definition: exceptions.h:53
urcl::primary_interface::PrimaryClient::commandUnlockProtectiveStop
void commandUnlockProtectiveStop(const bool validate=true, const std::chrono::milliseconds timeout=std::chrono::milliseconds(5000))
Commands the robot to unlock the protective stop.
Definition: primary_client.cpp:223
urcl::comm::INotifier
Parent class for notifiers.
Definition: pipeline.h:253
urcl::primary_interface::PrimaryClient::getRobotMode
RobotMode getRobotMode()
Get the latest robot mode.
Definition: primary_client.h:164
urcl::primary_interface::PrimaryClient::errorMessageCallback
void errorMessageCallback(ErrorCode &code)
Definition: primary_client.cpp:88
urcl::primary_interface::PrimaryClient::prod_
std::unique_ptr< comm::URProducer< PrimaryPackage > > prod_
Definition: primary_client.h:260
urcl::comm::Pipeline
The Pipeline manages the production and optionally consumption of packages. Cyclically the producer i...
Definition: pipeline.h:278
helpers.h
urcl::primary_interface::PrimaryClient::getRobotVersion
std::shared_ptr< VersionInformation > getRobotVersion(bool wait_for_message=true, const std::chrono::milliseconds timeout=std::chrono::seconds(2))
Get the robot's software version as Major.Minor.Bugfix.
Definition: primary_client.cpp:271
urcl::primary_interface::PrimaryClient::error_code_queue_mutex_
std::mutex error_code_queue_mutex_
Definition: primary_client.h:263
urcl::RobotMode::POWER_OFF
@ POWER_OFF
urcl::primary_interface::PrimaryClient::notifier_
comm::INotifier notifier_
Definition: primary_client.h:257
urcl::TimeoutException
A specialized exception representing that communication to the tool is not possible.
Definition: exceptions.h:121
URCL_LOG_INFO
#define URCL_LOG_INFO(...)
Definition: log.h:25
urcl::primary_interface::ErrorCode
Definition: error_code_message.h:51
urcl::primary_interface::PrimaryClient::~PrimaryClient
~PrimaryClient()
Definition: primary_client.cpp:59
urcl::primary_interface::UR_PRIMARY_PORT
static const int UR_PRIMARY_PORT
Definition: primary/package_header.h:42
urcl::primary_interface::PrimaryClient::PrimaryClient
PrimaryClient()=delete
urcl::comm::IConsumer< PrimaryPackage >
urcl::primary_interface::PrimaryClient::consumer_
std::shared_ptr< PrimaryConsumer > consumer_
Definition: primary_client.h:254
urcl::primary_interface::PrimaryClient::removePrimaryConsumer
void removePrimaryConsumer(std::shared_ptr< comm::IConsumer< PrimaryPackage >> primary_consumer)
Remove a primary consumer from the list of consumers.
Definition: primary_client.cpp:83
urcl::primary_interface::PrimaryClient::commandPowerOn
void commandPowerOn(const bool validate=true, const std::chrono::milliseconds timeout=std::chrono::seconds(30))
Commands the robot to power on.
Definition: primary_client.cpp:165
urcl::primary_interface::PrimaryClient::start
void start(const size_t max_connection_attempts=0, const std::chrono::milliseconds reconnection_timeout=urcl::comm::TCPSocket::DEFAULT_RECONNECTION_TIME)
Definition: primary_client.cpp:65
urcl::primary_interface::PrimaryClient::getErrorCodes
std::deque< ErrorCode > getErrorCodes()
Retrieves previously raised error codes from PrimaryClient. After calling this, recorded errors will ...
Definition: primary_client.cpp:94
urcl::primary_interface::PrimaryClient::commandPowerOff
void commandPowerOff(const bool validate=true, const std::chrono::milliseconds timeout=std::chrono::seconds(30))
Commands the robot to power off.
Definition: primary_client.cpp:185
urcl::primary_interface::PrimaryClient::reconnectStream
bool reconnectStream()
Reconnects the primary stream used to send program to the robot.
Definition: primary_client.cpp:139
primary_client.h
robot_state.h
urcl::RobotType
RobotType
Definition: datatypes.h:90
urcl::RobotMode::IDLE
@ IDLE


ur_client_library
Author(s): Thomas Timm Andersen, Simon Rasmussen, Felix Exner, Lea Steffen, Tristan Schnell
autogenerated on Mon May 26 2025 02:35:58