Skip to content

Commit 61680ae

Browse files
Expose diagnostic error codes (#225)
This adds very basic support for a primary interface client. With this, it is possible to get a list of all recorded error codes sent from the robot. The client is intended to be further extended in the future. --------- Co-authored-by: Felix Exner <feex@universal-robots.com>
1 parent 316b1b3 commit 61680ae

15 files changed

+755
-6
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ add_library(urcl SHARED
1616
src/control/trajectory_point_interface.cpp
1717
src/control/script_command_interface.cpp
1818
src/primary/primary_package.cpp
19+
src/primary/primary_client.cpp
1920
src/primary/robot_message.cpp
2021
src/primary/robot_state.cpp
2122
src/primary/robot_message/version_message.cpp
23+
src/primary/robot_message/error_code_message.cpp
2224
src/primary/robot_state/kinematics_info.cpp
2325
src/rtde/control_package_pause.cpp
2426
src/rtde/control_package_setup_inputs.cpp

include/ur_client_library/comm/pipeline.h

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include <thread>
3030
#include <vector>
3131
#include <fstream>
32+
#include <mutex>
33+
#include <algorithm>
3234

3335
namespace urcl
3436
{
@@ -83,26 +85,54 @@ class IConsumer
8385

8486
/*!
8587
* \brief Consumer, that allows one product to be consumed by multiple arbitrary
86-
* conusmers.
88+
* consumers.
8789
*
8890
* @tparam T Type of the consumed products
8991
*/
9092
template <typename T>
9193
class MultiConsumer : public IConsumer<T>
9294
{
9395
private:
94-
std::vector<IConsumer<T>*> consumers_;
96+
std::vector<std::shared_ptr<IConsumer<T>>> consumers_;
9597

9698
public:
9799
/*!
98100
* \brief Creates a new MultiConsumer object.
99101
*
100102
* \param consumers The list of consumers that should all consume given products
101103
*/
102-
MultiConsumer(std::vector<IConsumer<T>*> consumers) : consumers_(consumers)
104+
MultiConsumer(std::vector<std::shared_ptr<IConsumer<T>>> consumers) : consumers_(consumers)
103105
{
104106
}
105107

108+
/*!
109+
* \brief Adds a new consumer to the list of consumers
110+
*
111+
* \param consumer Consumer that should be added to the list
112+
*/
113+
void addConsumer(std::shared_ptr<IConsumer<T>> consumer)
114+
{
115+
std::lock_guard<std::mutex> lk(consumer_list_);
116+
consumers_.push_back(consumer);
117+
}
118+
119+
/*!
120+
* \brief Remove a consumer from the list of consumers
121+
*
122+
* \param consumer Consumer that should be removed from the list
123+
*/
124+
void removeConsumer(std::shared_ptr<IConsumer<T>> consumer)
125+
{
126+
std::lock_guard<std::mutex> lk(consumer_list_);
127+
auto it = std::find(consumers_.begin(), consumers_.end(), consumer);
128+
if (it == consumers_.end())
129+
{
130+
URCL_LOG_ERROR("Unable to remove consumer as it is not part of the consumer list");
131+
return;
132+
}
133+
consumers_.erase(it);
134+
}
135+
106136
/*!
107137
* \brief Sets up all registered consumers.
108138
*/
@@ -153,6 +183,7 @@ class MultiConsumer : public IConsumer<T>
153183
*/
154184
bool consume(std::shared_ptr<T> product)
155185
{
186+
std::lock_guard<std::mutex> lk(consumer_list_);
156187
bool res = true;
157188
for (auto& con : consumers_)
158189
{
@@ -161,6 +192,9 @@ class MultiConsumer : public IConsumer<T>
161192
}
162193
return res;
163194
}
195+
196+
private:
197+
std::mutex consumer_list_;
164198
};
165199

166200
/*!
@@ -234,7 +268,7 @@ class INotifier
234268
};
235269

236270
/*!
237-
* \brief The Pipepline manages the production and optionally consumption of packages. Cyclically
271+
* \brief The Pipeline manages the production and optionally consumption of packages. Cyclically
238272
* the producer is called and returned packages are saved in a queue. This queue is then either also
239273
* cyclically utilized by the registered consumer or can be externally used.
240274
*

include/ur_client_library/primary/abstract_primary_consumer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "ur_client_library/log.h"
3333
#include "ur_client_library/comm/pipeline.h"
3434
#include "ur_client_library/primary/robot_message/version_message.h"
35+
#include "ur_client_library/primary/robot_message/error_code_message.h"
3536
#include "ur_client_library/primary/robot_state/kinematics_info.h"
3637

3738
namespace urcl
@@ -51,7 +52,7 @@ class AbstractPrimaryConsumer : public comm::IConsumer<PrimaryPackage>
5152
virtual ~AbstractPrimaryConsumer() = default;
5253

5354
/*!
54-
* \brief This consume method is usally being called by the Pipeline structure. We don't
55+
* \brief This consume method is usually being called by the Pipeline structure. We don't
5556
* necessarily need to know the specific package type here, as the packages themselves will take
5657
* care to be consumed with the correct function (visitor pattern).
5758
*
@@ -73,6 +74,7 @@ class AbstractPrimaryConsumer : public comm::IConsumer<PrimaryPackage>
7374
virtual bool consume(RobotState& pkg) = 0;
7475
virtual bool consume(VersionMessage& pkg) = 0;
7576
virtual bool consume(KinematicsInfo& pkg) = 0;
77+
virtual bool consume(ErrorCodeMessage& pkg) = 0;
7678

7779
private:
7880
/* data */
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
31+
#ifndef UR_CLIENT_LIBRARY_PRIMARY_CLIENT_H_INCLUDED
32+
#define UR_CLIENT_LIBRARY_PRIMARY_CLIENT_H_INCLUDED
33+
34+
#include <memory>
35+
#include <deque>
36+
37+
#include <ur_client_library/comm/stream.h>
38+
#include <ur_client_library/comm/pipeline.h>
39+
#include <ur_client_library/comm/producer.h>
40+
#include <ur_client_library/primary/abstract_primary_consumer.h>
41+
#include <ur_client_library/primary/primary_consumer.h>
42+
#include <ur_client_library/primary/primary_package.h>
43+
#include <ur_client_library/primary/primary_parser.h>
44+
45+
namespace urcl
46+
{
47+
namespace primary_interface
48+
{
49+
class PrimaryClient
50+
{
51+
public:
52+
PrimaryClient() = delete;
53+
PrimaryClient(const std::string& robot_ip, comm::INotifier& notifier);
54+
~PrimaryClient();
55+
56+
/*!
57+
* \brief Adds a primary consumer to the list of consumers
58+
*
59+
* \param primary_consumer Primary consumer that should be added to the list
60+
*/
61+
void addPrimaryConsumer(std::shared_ptr<comm::IConsumer<PrimaryPackage>> primary_consumer);
62+
63+
/*!
64+
* \brief Remove a primary consumer from the list of consumers
65+
*
66+
* \param primary_consumer Primary consumer that should be removed from the list
67+
*/
68+
void removePrimaryConsumer(std::shared_ptr<comm::IConsumer<PrimaryPackage>> primary_consumer);
69+
void start();
70+
71+
/*!
72+
* \brief Retrieves previously raised error codes from PrimaryClient. After calling this, recorded errors will be
73+
* deleted.
74+
*/
75+
std::deque<ErrorCode> getErrorCodes();
76+
77+
private:
78+
// The function is called whenever an error code message is received
79+
void errorMessageCallback(ErrorCode& code);
80+
81+
PrimaryParser parser_;
82+
std::shared_ptr<PrimaryConsumer> consumer_;
83+
std::unique_ptr<comm::MultiConsumer<PrimaryPackage>> multi_consumer_;
84+
85+
comm::INotifier notifier_;
86+
87+
comm::URStream<PrimaryPackage> stream_;
88+
std::unique_ptr<comm::URProducer<PrimaryPackage>> prod_;
89+
std::unique_ptr<comm::Pipeline<PrimaryPackage>> pipeline_;
90+
91+
std::mutex error_code_queue_mutex_;
92+
std::deque<ErrorCode> error_code_queue_;
93+
};
94+
95+
} // namespace primary_interface
96+
} // namespace urcl
97+
98+
#endif // ifndef UR_CLIENT_LIBRARY_PRIMARY_CLIENT_H_INCLUDED

0 commit comments

Comments
 (0)