Skip to content

Commit f1d0a85

Browse files
authored
Send script blocking return refactor (UniversalRobots#520)
The method now throws in any case where it would previously return false.
1 parent ca25b13 commit f1d0a85

13 files changed

Lines changed: 420 additions & 128 deletions

File tree

doc/architecture/primary_client.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ Method signature:
3333
);
3434

3535
| The ``sendScriptBlocking`` method will also accept valid URScript code, but blocks until the execution result of the given program is available.
36-
| Prior to transferring the program it will first check that the robot is in a state where it can execute programs, if not it returns false.
36+
| Prior to transferring the program it will first check that the robot is in a state where it can execute programs, otherwise an exception is thrown.
3737
| If the robot is ready, the program is then transferred, and the method will wait for the robot to report that the program has either started, finished or encountered an error.
38-
| If the program has not started within the given ``timeout``, the method returns false.
39-
| If the robot encounters an error or runtime exception during program execution the method also returns false.
40-
| If ``fail_on_warnings`` is true, it will also return false, if the robot reports a warning during program execution. Note: protective stops are reported as warnings by the robot.
41-
| The method only returns true if the program is successfully executed on the robot.
38+
| If the program has not started within the given ``timeout``, the method throws an exception.
39+
| If the robot encounters an error or runtime exception during program execution the method also throws an exception.
40+
| If ``fail_on_warnings`` is true, it will also throw an exception, if the robot reports a warning during program execution. Note: protective stops are reported as warnings by the robot.
41+
| If no exceptions are thrown, the script has been executed successfully.
4242
| This method also accepts secondary programs, but no feedback is available for those, so it will behave similarly to the ``sendScript`` method in those cases, except for the pre-transfer checks.
43+
| The exact exceptions that are thrown in various cases can be seen in the `primary client header file <https://github.com/UniversalRobots/Universal_Robots_Client_Library/blob/master/include/ur_client_library/primary/primary_client.h>`_.

examples/send_script.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,10 @@ def example_fun():
4545
relative_move = p[0,-0.1,0,0,0,0]
4646
movel(pose_trans(current_pose, relative_move), t=1)
4747
end)""";
48-
49-
if (client.sendScriptBlocking(fully_defined_script))
50-
{
51-
// The function definition can also be omitted
52-
// A function name will then be auto generated
53-
client.sendScriptBlocking(R"(textmsg("Successful program execution"))");
54-
}
48+
client.sendScriptBlocking(fully_defined_script);
49+
// The function definition can also be omitted
50+
// A function name will then be auto generated
51+
client.sendScriptBlocking(R"(textmsg("Successful program execution"))");
5552
// A script-function name can also be passed to the method
5653
// A timeout can also be given to limit the wait for the passed function to start. If timeout = 0, it will
5754
// wait indefinitely.
@@ -66,18 +63,26 @@ end
6663
)";
6764
client.sendScriptBlocking(secondary_script);
6865

69-
// Sending wrong script code will result in a clear error
66+
// Sending wrong script code will result in an exception with a clear explanation
7067
const std::string bad_script_code = R"""(
7168
def bad_code():
7269
current_pose = get_target_tcp_pose()
7370
movel(current_pos) # note pose vs pos
7471
end)""";
7572
URCL_LOG_INFO("Sending bad script code...");
76-
bool success = client.sendScriptBlocking(bad_script_code);
73+
try
7774
{
78-
std::stringstream ss;
79-
ss << "Execution of bad code successful? " << std::boolalpha << success;
80-
URCL_LOG_INFO("%s", ss.str().c_str());
75+
client.sendScriptBlocking(bad_script_code);
76+
}
77+
catch (const RobotRuntimeException& exc)
78+
{
79+
URCL_LOG_INFO("Caught expected runtime exception from sendScriptBlocking");
80+
URCL_LOG_INFO(exc.what());
81+
}
82+
catch (const UrException& exc)
83+
{
84+
URCL_LOG_ERROR("Caught unexpected exception from sendScriptBlocking");
85+
URCL_LOG_ERROR(exc.what());
8186
}
8287

8388
// We can also send script code without any checks
@@ -87,7 +92,7 @@ end)""";
8792
// E.g. sending the bad script here will not give us any information
8893
// The return value will only tell us that the script code has been sent to the robot.
8994
URCL_LOG_INFO("Sending bad script code without feedback...");
90-
success = client.sendScript(bad_script_code);
95+
bool success = client.sendScript(bad_script_code);
9196
{
9297
std::stringstream ss;
9398
ss << "Bad code sent to robot successfully? " << std::boolalpha << success;

include/ur_client_library/exceptions.h

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
#include <optional>
3434
#include <stdexcept>
3535
#include <sstream>
36+
#include <vector>
3637
#include "ur/version_information.h"
38+
#include "ur_client_library/ur/datatypes.h"
3739

3840
#ifdef _WIN32
3941
# define NOMINMAX
@@ -336,5 +338,133 @@ class ScriptCodeSyntaxException : public UrException
336338
return std::runtime_error::what();
337339
}
338340
};
341+
342+
class RobotModeException : public UrException
343+
{
344+
public:
345+
explicit RobotModeException() = delete;
346+
347+
explicit RobotModeException(const std::string& operation, const RobotMode& required, const RobotMode& actual)
348+
: std::runtime_error("Incorrect robot mode: " + robotModeString(actual))
349+
{
350+
std::stringstream ss;
351+
ss << "Robot is in incorrect mode for the requested operation: " << operation << "\n"
352+
<< "Required robot mode: " << urcl::robotModeString(required) << " (" << int(required) << ") \n"
353+
<< "Actual robot mode: " << urcl::robotModeString(actual) << " (" << int(actual) << ")";
354+
text_ = ss.str();
355+
}
356+
357+
virtual ~RobotModeException() = default;
358+
359+
virtual const char* what() const noexcept override
360+
{
361+
return text_.c_str();
362+
}
363+
364+
private:
365+
std::string text_;
366+
};
367+
368+
class SafetyModeException : public UrException
369+
{
370+
public:
371+
explicit SafetyModeException() = delete;
372+
373+
explicit SafetyModeException(const std::string& operation, const std::vector<urcl::SafetyMode>& options,
374+
const urcl::SafetyMode& actual)
375+
: std::runtime_error("Incorrect safety mode: " + safetyModeString(actual))
376+
{
377+
std::stringstream ss;
378+
ss << "Robot is in incorrect safety mode for the requested operation: " << operation << "\n"
379+
<< "Safety mode should be one of: \n";
380+
381+
for (auto mode : options)
382+
{
383+
ss << urcl::safetyModeString(mode) << " (" << int(mode) << ")\n";
384+
}
385+
ss << "\n"
386+
<< "Actual safety mode: " << urcl::safetyModeString(actual) << " (" << int(actual) << ")";
387+
text_ = ss.str();
388+
}
389+
390+
virtual ~SafetyModeException() = default;
391+
392+
virtual const char* what() const noexcept override
393+
{
394+
return text_.c_str();
395+
}
396+
397+
private:
398+
std::string text_;
399+
};
400+
401+
class StreamNotConnectedException : public UrException
402+
{
403+
public:
404+
explicit StreamNotConnectedException() = delete;
405+
406+
explicit StreamNotConnectedException(const std::string& text) : std::runtime_error(text)
407+
{
408+
}
409+
410+
virtual ~StreamNotConnectedException() = default;
411+
412+
virtual const char* what() const noexcept override
413+
{
414+
return std::runtime_error::what();
415+
}
416+
};
417+
418+
class RobotRuntimeException : public UrException
419+
{
420+
public:
421+
explicit RobotRuntimeException() = delete;
422+
423+
explicit RobotRuntimeException(const std::string& text) : std::runtime_error(text)
424+
{
425+
}
426+
427+
virtual ~RobotRuntimeException() = default;
428+
429+
virtual const char* what() const noexcept override
430+
{
431+
return std::runtime_error::what();
432+
}
433+
};
434+
435+
class ReadOnlyInterfaceException : public UrException
436+
{
437+
public:
438+
explicit ReadOnlyInterfaceException() = delete;
439+
440+
explicit ReadOnlyInterfaceException(const std::string& text) : std::runtime_error(text)
441+
{
442+
}
443+
444+
virtual ~ReadOnlyInterfaceException() = default;
445+
446+
virtual const char* what() const noexcept override
447+
{
448+
return std::runtime_error::what();
449+
}
450+
};
451+
452+
class RobotErrorCodeException : public UrException
453+
{
454+
public:
455+
explicit RobotErrorCodeException() = delete;
456+
457+
explicit RobotErrorCodeException(const std::string& text) : std::runtime_error(text)
458+
{
459+
}
460+
461+
virtual ~RobotErrorCodeException() = default;
462+
463+
virtual const char* what() const noexcept override
464+
{
465+
return std::runtime_error::what();
466+
}
467+
};
468+
339469
} // namespace urcl
340470
#endif // ifndef UR_CLIENT_LIBRARY_EXCEPTIONS_H_INCLUDED

include/ur_client_library/primary/primary_client.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class PrimaryClient
109109
* The given code must be valid according the UR Scripting Manual. The given script code will be automatically wrapped
110110
* in a function definition, if it is not already. Secondary programs can also be passed to this function, but must be
111111
* fully defined as a secondary program when calling. Secondary programs create no feedback, so this function will
112-
* return true as soon as the program is uploaded successfully to the robot (same as the sendScript function).
112+
* return as soon as the program is uploaded successfully to the robot (same as the sendScript function).
113113
*
114114
* \param program URScript code that shall be executed by the robot.
115115
*
@@ -126,10 +126,17 @@ class PrimaryClient
126126
* \throw urcl::ScriptCodeSyntaxException if the given script code has syntax errors, which are checked here.
127127
* \throw urcl::UrException if the stop command cannot be sent to the robot.
128128
* \throw urcl::TimeoutException if the robot doesn't stop the program within the given timeout.
129-
*
130-
* \returns true on successful execution of the script, false otherwise
129+
* \throw urcl::TimeoutException if the robot mode is not received within 1 second.
130+
* \throw urcl::TimeoutException if the program does not start within the given timeout.
131+
* \throw urcl::RobotModeException if the robot is in an incorrect mode for script execution.
132+
* \throw urcl::SafetyModeException if the robot is in an incorrect safety mode for script execution
133+
* \throw urcl::StreamNotConnectedException if the script cannot be transferred to the robot.
134+
* \throw urcl::RobotRuntimeException if the given script causes a runtime exception on the robot.
135+
* \throw urcl::ReadOnlyInterfaceException if the primary interface is in read-only mode when the script is
136+
* transferred. This can happen if the robot was recently switched from manual to remote control mode.
137+
* \throw urcl::RobotErrorCodeException if the robot encounters an error during script execution.
131138
*/
132-
bool sendScriptBlocking(const std::string& program, std::string script_name = "",
139+
void sendScriptBlocking(const std::string& program, std::string script_name = "",
133140
std::chrono::milliseconds start_timeout = std::chrono::seconds(1),
134141
bool fail_on_warnings = true);
135142

include/ur_client_library/primary/primary_consumer.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,23 +136,24 @@ class PrimaryConsumer : public AbstractPrimaryConsumer
136136

137137
switch (code.report_level)
138138
{
139-
case urcl::primary_interface::ReportLevel::DEBUG:
140-
case urcl::primary_interface::ReportLevel::DEVL_DEBUG:
141-
case urcl::primary_interface::ReportLevel::DEVL_INFO:
142-
case urcl::primary_interface::ReportLevel::DEVL_WARNING:
143-
case urcl::primary_interface::ReportLevel::DEVL_VIOLATION:
144-
case urcl::primary_interface::ReportLevel::DEVL_FAULT:
139+
case ReportLevel::DEBUG:
140+
case ReportLevel::DEVL_DEBUG:
141+
case ReportLevel::DEVL_INFO:
142+
case ReportLevel::DEVL_WARNING:
143+
case ReportLevel::DEVL_VIOLATION:
144+
case ReportLevel::DEVL_FAULT:
145+
case ReportLevel::DEVL_CRITICAL_FAULT:
145146
URCL_LOG_DEBUG(log_contents.c_str());
146147
break;
147-
case urcl::primary_interface::ReportLevel::INFO:
148+
case ReportLevel::INFO:
148149
URCL_LOG_INFO(log_contents.c_str());
149150
break;
150-
case urcl::primary_interface::ReportLevel::WARNING:
151+
case ReportLevel::WARNING:
151152
URCL_LOG_WARN(log_contents.c_str());
152153
break;
153-
default:
154-
// urcl::primary_interface::ReportLevel::VIOLATION:
155-
// urcl::primary_interface::ReportLevel::FAULT:
154+
case ReportLevel::VIOLATION:
155+
case ReportLevel::FAULT:
156+
case ReportLevel::CRITICAL_FAULT:
156157
URCL_LOG_ERROR(log_contents.c_str());
157158
break;
158159
}

include/ur_client_library/primary/robot_message/error_code_message.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,12 @@
2929
#define UR_CLIENT_LIBRARY_PRIMARY_ERROR_CODE_MESSAGE_H_INCLUDED
3030

3131
#include "ur_client_library/primary/robot_message.h"
32+
#include "ur_client_library/ur/datatypes.h"
3233

3334
namespace urcl
3435
{
3536
namespace primary_interface
3637
{
37-
enum class ReportLevel : int32_t
38-
{
39-
DEBUG = 0,
40-
INFO = 1,
41-
WARNING = 2,
42-
VIOLATION = 3,
43-
FAULT = 4,
44-
DEVL_DEBUG = 128,
45-
DEVL_INFO = 129,
46-
DEVL_WARNING = 130,
47-
DEVL_VIOLATION = 131,
48-
DEVL_FAULT = 132
49-
};
5038

5139
struct ErrorCode
5240
{

include/ur_client_library/ur/datatypes.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,54 @@ enum class RobotSeries
120120
UR_SERIES = 3
121121
};
122122

123+
enum class ReportLevel : int32_t
124+
{
125+
DEBUG = 0,
126+
INFO = 1,
127+
WARNING = 2,
128+
VIOLATION = 3,
129+
FAULT = 4,
130+
CRITICAL_FAULT = 5,
131+
DEVL_DEBUG = 128,
132+
DEVL_INFO = 129,
133+
DEVL_WARNING = 130,
134+
DEVL_VIOLATION = 131,
135+
DEVL_FAULT = 132,
136+
DEVL_CRITICAL_FAULT = 133
137+
};
138+
139+
inline std::string reportLevelString(const ReportLevel& code)
140+
{
141+
switch (code)
142+
{
143+
case ReportLevel::DEBUG:
144+
return "DEBUG";
145+
case ReportLevel::INFO:
146+
return "INFO";
147+
case ReportLevel::WARNING:
148+
return "WARNING";
149+
case ReportLevel::VIOLATION:
150+
return "VIOLATION";
151+
case ReportLevel::FAULT:
152+
return "FAULT";
153+
case ReportLevel::CRITICAL_FAULT:
154+
return "CRITICAL_FAULT";
155+
case ReportLevel::DEVL_DEBUG:
156+
return "DEVL_DEBUG";
157+
case ReportLevel::DEVL_INFO:
158+
return "DEVL_INFO";
159+
case ReportLevel::DEVL_WARNING:
160+
return "DEVL_WARNING";
161+
case ReportLevel::DEVL_VIOLATION:
162+
return "DEVL_VIOLATION";
163+
case ReportLevel::DEVL_FAULT:
164+
return "DEVL_FAULT";
165+
case ReportLevel::DEVL_CRITICAL_FAULT:
166+
return "DEVL_CRITICAL_FAULT";
167+
}
168+
throw std::invalid_argument("Unknown report level: " + std::to_string(static_cast<int>(code)));
169+
}
170+
123171
inline std::string robotModeString(const RobotMode& mode)
124172
{
125173
switch (mode)

0 commit comments

Comments
 (0)