Skip to content

Commit bb8235d

Browse files
authored
[primary interface] Add KeyMessage parsing (UniversalRobots#428)
Add KeyMessage to primary interface
1 parent 6a2aa33 commit bb8235d

5 files changed

Lines changed: 237 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ add_library(urcl
2626
src/primary/primary_package.cpp
2727
src/primary/primary_client.cpp
2828
src/primary/robot_message.cpp
29-
src/primary/robot_state.cpp
30-
src/primary/robot_message/version_message.cpp
3129
src/primary/robot_message/error_code_message.cpp
30+
src/primary/robot_message/key_message.cpp
31+
src/primary/robot_message/version_message.cpp
32+
src/primary/robot_state.cpp
3233
src/primary/robot_state/kinematics_info.cpp
3334
src/primary/robot_state/robot_mode_data.cpp
3435
src/primary/robot_state/configuration_data.cpp

include/ur_client_library/primary/primary_parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "ur_client_library/comm/pipeline.h"
2525
#include "ur_client_library/comm/parser.h"
2626
#include "ur_client_library/primary/package_header.h"
27+
#include "ur_client_library/primary/robot_message/key_message.h"
2728
#include "ur_client_library/primary/robot_state.h"
2829
#include "ur_client_library/primary/robot_message.h"
2930
#include "ur_client_library/primary/robot_state/kinematics_info.h"
@@ -181,6 +182,8 @@ class PrimaryParser : public comm::Parser<PrimaryPackage>
181182
return new VersionMessage(timestamp, source);
182183
case RobotMessagePackageType::ROBOT_MESSAGE_ERROR_CODE:
183184
return new ErrorCodeMessage(timestamp, source);
185+
case RobotMessagePackageType::ROBOT_MESSAGE_KEY:
186+
return new KeyMessage(timestamp, source);
184187
default:
185188
return new RobotMessage(timestamp, source);
186189
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2026 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+
//----------------------------------------------------------------------
32+
/*!\file
33+
*
34+
* \author Felix Exner feex@universal-robots.com
35+
* \date 2026-02-02
36+
*
37+
*/
38+
//----------------------------------------------------------------------
39+
40+
#ifndef UR_CLIENT_LIBRARY_PRIMARY_KEY_MESSAGE_H_INCLUDED
41+
#define UR_CLIENT_LIBRARY_PRIMARY_KEY_MESSAGE_H_INCLUDED
42+
43+
#include "ur_client_library/primary/robot_message.h"
44+
45+
namespace urcl
46+
{
47+
namespace primary_interface
48+
{
49+
/*!
50+
* \brief Representation of the primary interface's KeyMessage
51+
*/
52+
class KeyMessage : public RobotMessage
53+
{
54+
public:
55+
KeyMessage() = delete;
56+
/*!
57+
* \brief Creates a new KeyMessage object to be filled from a package.
58+
*
59+
* \param timestamp Timestamp of the package
60+
* \param source The package's source
61+
*/
62+
KeyMessage(uint64_t timestamp, int8_t source)
63+
: RobotMessage(timestamp, source, RobotMessagePackageType::ROBOT_MESSAGE_KEY)
64+
{
65+
}
66+
KeyMessage(const KeyMessage& pkg);
67+
68+
virtual ~KeyMessage() = default;
69+
70+
/*!
71+
* \brief Sets the attributes of the package by parsing a serialized representation of the
72+
* package.
73+
*
74+
* \param bp A parser containing a serialized text of the package
75+
*
76+
* \returns True, if the package was parsed successfully, false otherwise
77+
*/
78+
virtual bool parseWith(comm::BinParser& bp);
79+
80+
/*!
81+
* \brief Consume this package with a specific consumer.
82+
*
83+
* \param consumer Placeholder for the consumer calling this
84+
*
85+
* \returns true on success
86+
*/
87+
virtual bool consumeWith(AbstractPrimaryConsumer& consumer);
88+
89+
/*!
90+
* \brief Produces a human readable representation of the package object.
91+
*
92+
* \returns A string representing the object
93+
*/
94+
virtual std::string toString() const;
95+
96+
int32_t message_code_;
97+
int32_t message_argument_;
98+
uint8_t title_length_;
99+
std::string title_;
100+
std::string text_;
101+
};
102+
} // namespace primary_interface
103+
} // namespace urcl
104+
105+
#endif // ifndef UR_CLIENT_LIBRARY_PRIMARY_TEXT_MESSAGE_H_INCLUDED
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2026 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+
//----------------------------------------------------------------------
32+
/*!\file
33+
*
34+
* \author Felix Exner feex@universal-robots.com
35+
* \date 2026-02-02
36+
*
37+
*/
38+
//----------------------------------------------------------------------
39+
40+
#include "ur_client_library/primary/robot_message/key_message.h"
41+
#include "ur_client_library/primary/abstract_primary_consumer.h"
42+
43+
namespace urcl
44+
{
45+
namespace primary_interface
46+
{
47+
KeyMessage::KeyMessage(const KeyMessage& pkg)
48+
: RobotMessage(pkg.timestamp_, pkg.source_, RobotMessagePackageType::ROBOT_MESSAGE_KEY)
49+
{
50+
message_code_ = pkg.message_code_;
51+
message_argument_ = pkg.message_argument_;
52+
title_length_ = pkg.title_length_;
53+
title_ = pkg.title_;
54+
text_ = pkg.text_;
55+
}
56+
57+
bool KeyMessage::parseWith(comm::BinParser& bp)
58+
{
59+
bp.parse(message_code_);
60+
bp.parse(message_argument_);
61+
bp.parse(title_length_);
62+
bp.parse(title_, title_length_);
63+
bp.parseRemainder(text_);
64+
65+
return true; // not really possible to check dynamic size packets
66+
}
67+
68+
bool KeyMessage::consumeWith(AbstractPrimaryConsumer& consumer)
69+
{
70+
return consumer.consume(*this);
71+
}
72+
73+
std::string KeyMessage::toString() const
74+
{
75+
std::stringstream ss;
76+
ss << "KeyMessage" << std::endl;
77+
ss << "Message code: " << message_code_ << std::endl;
78+
ss << "Message argument: " << message_argument_ << std::endl;
79+
ss << "Title: " << title_ << std::endl;
80+
ss << "Text: " << text_;
81+
return ss.str();
82+
}
83+
} // namespace primary_interface
84+
} // namespace urcl

tests/test_primary_parser.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <ur_client_library/comm/bin_parser.h>
3333
#include <ur_client_library/primary/primary_parser.h>
34+
#include "ur_client_library/primary/robot_message/key_message.h"
3435

3536
using namespace urcl;
3637

@@ -145,6 +146,22 @@ const unsigned char VERSION_MESSAGE[] = {
145146
0x33, 0x33
146147
};
147148

149+
const unsigned char KEY_MESSAGE[] = {
150+
// message size
151+
0x00, 0x00, 0x00, 0x32,
152+
// message type
153+
0x14,
154+
// timestamp
155+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156+
// source
157+
0xfe,
158+
// robot_message_type
159+
0x07,
160+
// payload
161+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x5f, 0x58, 0x58, 0x58, 0x5f,
162+
0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x74, 0x65, 0x78, 0x74, 0x6d, 0x73, 0x67
163+
};
164+
148165
TEST(primary_parser, parse_calibration_data)
149166
{
150167
unsigned char raw_data[sizeof(ROBOT_STATE)];
@@ -194,6 +211,31 @@ TEST(primary_parser, parse_version_message)
194211
}
195212
}
196213

214+
TEST(primary_parser, parse_key_message)
215+
{
216+
unsigned char raw_data[sizeof(KEY_MESSAGE)];
217+
memcpy(raw_data, KEY_MESSAGE, sizeof(KEY_MESSAGE));
218+
comm::BinParser bp(raw_data, sizeof(raw_data));
219+
220+
std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
221+
primary_interface::PrimaryParser parser;
222+
ASSERT_TRUE(parser.parse(bp, products));
223+
224+
ASSERT_EQ(products.size(), 1);
225+
if (auto data = dynamic_cast<primary_interface::KeyMessage*>(products[0].get()))
226+
{
227+
EXPECT_EQ(data->message_code_, 0);
228+
EXPECT_EQ(data->message_argument_, 0);
229+
EXPECT_EQ(data->title_length_, 19);
230+
EXPECT_EQ(data->title_, "PROGRAM_XXX_STARTED");
231+
EXPECT_EQ(data->text_, "textmsg");
232+
}
233+
else
234+
{
235+
FAIL() << "Parsed package is not of type KeyMessage";
236+
}
237+
}
238+
197239
int main(int argc, char* argv[])
198240
{
199241
::testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)