From 494fcf5d13a252125e07e83a0a8844747e6d6a2d Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Mon, 15 Jun 2026 13:09:40 +0000 Subject: [PATCH 01/14] Add retry on read only interface Move send and monitoring to separate function Rename a variable to avoid shadowing --- .../primary/primary_client.h | 3 +- src/primary/primary_client.cpp | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index c4f57c924..fcd09e1cb 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -138,7 +138,7 @@ class PrimaryClient */ void sendScriptBlocking(const std::string& program, std::string script_name = "", std::chrono::milliseconds start_timeout = std::chrono::seconds(1), - bool fail_on_warnings = true); + bool fail_on_warnings = true, bool retry_on_readonly_interface = true); bool checkCalibration(const std::string& checksum); @@ -362,6 +362,7 @@ class PrimaryClient ScriptInfo prepare_script(std::string script, std::string script_name); std::vector strip_comments_and_whitespace(std::vector script_lines); std::string truncate_script_name(std::string candidate_name); + void send_script_monitor_execution(ScriptInfo script_info, std::chrono::milliseconds timeout, bool fail_on_warnings); PrimaryParser parser_; std::shared_ptr consumer_; diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index c53af8540..46d2922e7 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -138,17 +138,18 @@ bool PrimaryClient::safetyModeAllowsExecution() } void PrimaryClient::sendScriptBlocking(const std::string& program, std::string script_name, - std::chrono::milliseconds timeout, bool fail_on_warnings) + std::chrono::milliseconds timeout, bool fail_on_warnings, + bool retry_on_readonly_interface) { ScriptInfo script_info = prepare_script(program, script_name); RobotMode robot_mode = getRobotMode(); std::chrono::milliseconds robot_mode_timeout(1000); - auto start = std::chrono::system_clock::now(); + auto start_time = std::chrono::system_clock::now(); while (robot_mode == RobotMode::UNKNOWN) { auto now = std::chrono::system_clock::now(); - if (std::chrono::duration_cast(now - start).count() > robot_mode_timeout.count()) + if (std::chrono::duration_cast(now - start_time).count() > robot_mode_timeout.count()) { throw TimeoutException("Robot mode not received within timeout. ", robot_mode_timeout); } @@ -172,6 +173,31 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s throw SafetyModeException("Script execution via primary interface", allowed_modes, getSafetyMode()); } + + try + { + send_script_monitor_execution(script_info, timeout, fail_on_warnings); + } + catch (const ReadOnlyInterfaceException& exc) + { + if (retry_on_readonly_interface) + { + URCL_LOG_INFO("Script execution failed due to the primary interface being read-only. Reconnecting primary " + "interface and retrying once."); + stop(); + start(); + send_script_monitor_execution(script_info, timeout, fail_on_warnings); + } + else + { + throw; + } + } +} + +void PrimaryClient::send_script_monitor_execution(ScriptInfo script_info, std::chrono::milliseconds timeout, + bool fail_on_warnings) +{ // Clear runtime exception { std::scoped_lock lock(runtime_exception_mutex_); @@ -191,6 +217,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s throw StreamNotConnectedException("Script could not be sent to the robot. Ensure that the primary interface is " "connected."); } + // No feedback from secondary programs, so we assume success if (script_info.script_type == ScriptTypes::SEC) { From 40d2ac786856c0d70f0eec65126f2a8171df4993 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Mon, 15 Jun 2026 13:10:04 +0000 Subject: [PATCH 02/14] Add tests to retry with fake server --- tests/test_primary_client.cpp | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tests/test_primary_client.cpp b/tests/test_primary_client.cpp index 2fe840c72..74810b72c 100644 --- a/tests/test_primary_client.cpp +++ b/tests/test_primary_client.cpp @@ -666,12 +666,42 @@ TEST_F(PrimaryClientFakeTest, test_send_script_to_read_only_server) // Make the fake server send an error code message with code 210 (read-only primary interface) when it receives a // script server_->setScriptCallback([this, script_code]([[maybe_unused]] const std::string& payload) { - ASSERT_TRUE( - server_->sendErrorCodeMessage(210, 0, ReportLevel::VIOLATION, "Simulated read-only primary interface error")); + if (payload.find(script_code) != std::string::npos) + { + ASSERT_TRUE( + server_->sendErrorCodeMessage(210, 0, ReportLevel::VIOLATION, "Simulated read-only primary interface error")); + } }); + // Fails even with retry EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false), ReadOnlyInterfaceException); + + // Fails with no retry + EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false, false), + ReadOnlyInterfaceException); + + bool retry = false; + // Send C210 on first try, then succeed + server_->setScriptCallback([this, script_code, &retry]([[maybe_unused]] const std::string& payload) { + if (!retry) + { + ASSERT_TRUE( + server_->sendErrorCodeMessage(210, 0, ReportLevel::VIOLATION, "Simulated read-only primary interface error")); + retry = true; + } + else + { + if (payload.find(script_code) != std::string::npos) + { + server_->sendKeyMessage("PROGRAM_XXX_STARTED", "test_fun"); + ASSERT_EQ(payload, "def test_fun():\n " + script_code + "\nend\n\n"); + server_->sendKeyMessage("PROGRAM_XXX_STOPPED", "test_fun"); + } + } + }); + // Succeeds on retry + EXPECT_NO_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false)); } TEST_F(PrimaryClientFakeTest, test_send_script_blocking_timeout_on_no_response) From 8fda841393e4fb7eb68aee9c0d541cc13e3262f3 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Mon, 15 Jun 2026 13:58:00 +0000 Subject: [PATCH 03/14] Rearrange test, so a retry would result in failed test --- tests/test_primary_client.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/test_primary_client.cpp b/tests/test_primary_client.cpp index 74810b72c..b130e8625 100644 --- a/tests/test_primary_client.cpp +++ b/tests/test_primary_client.cpp @@ -677,29 +677,28 @@ TEST_F(PrimaryClientFakeTest, test_send_script_to_read_only_server) EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false), ReadOnlyInterfaceException); - // Fails with no retry - EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false, false), - ReadOnlyInterfaceException); - bool retry = false; // Send C210 on first try, then succeed server_->setScriptCallback([this, script_code, &retry]([[maybe_unused]] const std::string& payload) { - if (!retry) + if (!retry && payload.find(script_code) != std::string::npos) { ASSERT_TRUE( server_->sendErrorCodeMessage(210, 0, ReportLevel::VIOLATION, "Simulated read-only primary interface error")); retry = true; } - else + else if (payload.find(script_code) != std::string::npos) { - if (payload.find(script_code) != std::string::npos) - { - server_->sendKeyMessage("PROGRAM_XXX_STARTED", "test_fun"); - ASSERT_EQ(payload, "def test_fun():\n " + script_code + "\nend\n\n"); - server_->sendKeyMessage("PROGRAM_XXX_STOPPED", "test_fun"); - } + server_->sendKeyMessage("PROGRAM_XXX_STARTED", "test_fun"); + ASSERT_EQ(payload, "def test_fun():\n " + script_code + "\nend\n\n"); + server_->sendKeyMessage("PROGRAM_XXX_STOPPED", "test_fun"); } }); + + // Fails with no retry + EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false, false), + ReadOnlyInterfaceException); + + retry = false; // Succeeds on retry EXPECT_NO_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false)); } From c38189cc0c4961f068cae76eac09ada4c89ebf22 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 06:34:58 +0000 Subject: [PATCH 04/14] Changed log message --- src/primary/primary_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index 46d2922e7..531380342 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -182,7 +182,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s { if (retry_on_readonly_interface) { - URCL_LOG_INFO("Script execution failed due to the primary interface being read-only. Reconnecting primary " + URCL_LOG_INFO("Script execution failed due to the primary interface being read-only. Restarting primary " "interface and retrying once."); stop(); start(); From ec4b8c98af0e5b88604682062660a7f780963dd9 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 06:50:51 +0000 Subject: [PATCH 05/14] Rename timeout parameter to match header file --- src/primary/primary_client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index 531380342..3ce41c128 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -138,7 +138,7 @@ bool PrimaryClient::safetyModeAllowsExecution() } void PrimaryClient::sendScriptBlocking(const std::string& program, std::string script_name, - std::chrono::milliseconds timeout, bool fail_on_warnings, + std::chrono::milliseconds start_timeout, bool fail_on_warnings, bool retry_on_readonly_interface) { ScriptInfo script_info = prepare_script(program, script_name); @@ -176,7 +176,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s try { - send_script_monitor_execution(script_info, timeout, fail_on_warnings); + send_script_monitor_execution(script_info, start_timeout, fail_on_warnings); } catch (const ReadOnlyInterfaceException& exc) { @@ -186,7 +186,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s "interface and retrying once."); stop(); start(); - send_script_monitor_execution(script_info, timeout, fail_on_warnings); + send_script_monitor_execution(script_info, start_timeout, fail_on_warnings); } else { From 49be49ade8c3b64884a5928108351899f734bb68 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 07:38:22 +0000 Subject: [PATCH 06/14] Const and references --- include/ur_client_library/primary/primary_client.h | 5 +++-- src/primary/primary_client.cpp | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index fcd09e1cb..9b0ef5675 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -138,7 +138,7 @@ class PrimaryClient */ void sendScriptBlocking(const std::string& program, std::string script_name = "", std::chrono::milliseconds start_timeout = std::chrono::seconds(1), - bool fail_on_warnings = true, bool retry_on_readonly_interface = true); + bool fail_on_warnings = true, const bool retry_on_readonly_interface = true); bool checkCalibration(const std::string& checksum); @@ -362,7 +362,8 @@ class PrimaryClient ScriptInfo prepare_script(std::string script, std::string script_name); std::vector strip_comments_and_whitespace(std::vector script_lines); std::string truncate_script_name(std::string candidate_name); - void send_script_monitor_execution(ScriptInfo script_info, std::chrono::milliseconds timeout, bool fail_on_warnings); + void send_script_monitor_execution(const ScriptInfo& script_info, const std::chrono::milliseconds& timeout, + const bool fail_on_warnings); PrimaryParser parser_; std::shared_ptr consumer_; diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index 3ce41c128..f45c54ddc 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -139,7 +139,7 @@ bool PrimaryClient::safetyModeAllowsExecution() void PrimaryClient::sendScriptBlocking(const std::string& program, std::string script_name, std::chrono::milliseconds start_timeout, bool fail_on_warnings, - bool retry_on_readonly_interface) + const bool retry_on_readonly_interface) { ScriptInfo script_info = prepare_script(program, script_name); @@ -195,8 +195,8 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s } } -void PrimaryClient::send_script_monitor_execution(ScriptInfo script_info, std::chrono::milliseconds timeout, - bool fail_on_warnings) +void PrimaryClient::send_script_monitor_execution(const ScriptInfo& script_info, + const std::chrono::milliseconds& timeout, const bool fail_on_warnings) { // Clear runtime exception { From 41ab91f5b62a684084ce86b86f7b8df677b25ba0 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 07:43:33 +0000 Subject: [PATCH 07/14] Add retry_on_readonly_interface to docstring --- include/ur_client_library/primary/primary_client.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index 9b0ef5675..5082f1492 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -123,6 +123,10 @@ class PrimaryClient * \param fail_on_warnings Whether or not the function should report a failure, if the robot reports a warning-level * error during execution. Default true * + * \param retry_on_readonly_interface Whether to retry, if the primary interface is read-only. This will restart the + * primary interface, and then try sending the script again. If the interface is still read-only a + * ReadOnlyInterfaceException will be thrown. Default true + * * \throw urcl::ScriptCodeSyntaxException if the given script code has syntax errors, which are checked here. * \throw urcl::UrException if the stop command cannot be sent to the robot. * \throw urcl::TimeoutException if the robot doesn't stop the program within the given timeout. From b12dc18ef903b548dbcf7cf371a3d122387827b9 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 07:58:42 +0000 Subject: [PATCH 08/14] Update comment --- tests/test_primary_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_primary_client.cpp b/tests/test_primary_client.cpp index b130e8625..cecc73c82 100644 --- a/tests/test_primary_client.cpp +++ b/tests/test_primary_client.cpp @@ -710,7 +710,7 @@ TEST_F(PrimaryClientFakeTest, test_send_script_blocking_timeout_on_no_response) const std::string script_code = "textmsg(\"Still running\")"; // We do not set a script callback on the fake server, so it will not respond to the script being sent. This should - // cause sendScriptBlocking to time out and return false. + // cause sendScriptBlocking to time out and throw a TimeoutException. EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(100), false), TimeoutException); } From 7cf7d3a3fa271d2b2b0a3fd62fffa4ae3a6ea052 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 08:34:53 +0000 Subject: [PATCH 09/14] Update docs --- doc/architecture/primary_client.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/architecture/primary_client.rst b/doc/architecture/primary_client.rst index 1fc044f2d..c01835362 100644 --- a/doc/architecture/primary_client.rst +++ b/doc/architecture/primary_client.rst @@ -26,18 +26,21 @@ Method signature: .. code-block:: c++ bool sendScriptBlocking( - std::string program, + const std::string& program, std::string script_name = "", - std::chrono::milliseconds timeout = std::chrono::seconds(1), - bool fail_on_warnings = true + std::chrono::milliseconds start_timeout = std::chrono::seconds(1), + bool fail_on_warnings = true, + const bool retry_on_readonly_interface = true ); | The ``sendScriptBlocking`` method will also accept valid URScript code, but blocks until the execution result of the given program is available. | 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. | 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. -| If the program has not started within the given ``timeout``, the method throws an exception. +| If the program has not started within the given ``start_timeout``, the method throws an exception. | If the robot encounters an error or runtime exception during program execution the method also throws an exception. | 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. +| If ``retry_on_readonly_interface`` is true, the method will restart the primary interface and retry sending the program, if the primary interface is read-only. It will retry once, and if the interface is still read-only, an exception will be thrown. If false, the exception will be thrown, without restarting/retrying. | If no exceptions are thrown, the script has been executed successfully. | 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. | The exact exceptions that are thrown in various cases can be seen in the `primary client header file `_. +| Note: This method clears all stored error codes in the client during execution. From 903b6fbb41bd60562275990fe67dfb0f6c69b163 Mon Sep 17 00:00:00 2001 From: Jacob Larsen Date: Tue, 16 Jun 2026 09:02:54 +0000 Subject: [PATCH 10/14] [[maybe_unused]] exception in retry catch --- src/primary/primary_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index f45c54ddc..398a357b2 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -178,7 +178,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, std::string s { send_script_monitor_execution(script_info, start_timeout, fail_on_warnings); } - catch (const ReadOnlyInterfaceException& exc) + catch ([[maybe_unused]] const ReadOnlyInterfaceException& exc) { if (retry_on_readonly_interface) { From 31129a872cb638b5e33d2596d72a1ed5bebed92b Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 17 Jun 2026 09:44:02 +0200 Subject: [PATCH 11/14] Apply suggestions from code review Co-authored-by: Felix Exner --- tests/test_primary_client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_primary_client.cpp b/tests/test_primary_client.cpp index cecc73c82..b69f31bd1 100644 --- a/tests/test_primary_client.cpp +++ b/tests/test_primary_client.cpp @@ -674,7 +674,7 @@ TEST_F(PrimaryClientFakeTest, test_send_script_to_read_only_server) }); // Fails even with retry - EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false), + EXPECT_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false, true), ReadOnlyInterfaceException); bool retry = false; @@ -700,7 +700,7 @@ TEST_F(PrimaryClientFakeTest, test_send_script_to_read_only_server) retry = false; // Succeeds on retry - EXPECT_NO_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false)); + EXPECT_NO_THROW(client_->sendScriptBlocking(script_code, "test_fun", std::chrono::milliseconds(1000), false, true)); } TEST_F(PrimaryClientFakeTest, test_send_script_blocking_timeout_on_no_response) From a20d495395b0b8944c74cd1e66f0b5e0c4f90f79 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 17 Jun 2026 08:22:02 +0200 Subject: [PATCH 12/14] Explicitly use const parameters --- doc/architecture/primary_client.rst | 6 +++--- include/ur_client_library/primary/primary_client.h | 6 +++--- src/primary/primary_client.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/architecture/primary_client.rst b/doc/architecture/primary_client.rst index c01835362..90613e85b 100644 --- a/doc/architecture/primary_client.rst +++ b/doc/architecture/primary_client.rst @@ -27,9 +27,9 @@ Method signature: bool sendScriptBlocking( const std::string& program, - std::string script_name = "", - std::chrono::milliseconds start_timeout = std::chrono::seconds(1), - bool fail_on_warnings = true, + const std::string& script_name = "", + const std::chrono::milliseconds start_timeout = std::chrono::seconds(1), + const bool fail_on_warnings = true, const bool retry_on_readonly_interface = true ); diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index 5082f1492..9b16c4b16 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -140,9 +140,9 @@ class PrimaryClient * transferred. This can happen if the robot was recently switched from manual to remote control mode. * \throw urcl::RobotErrorCodeException if the robot encounters an error during script execution. */ - void sendScriptBlocking(const std::string& program, std::string script_name = "", - std::chrono::milliseconds start_timeout = std::chrono::seconds(1), - bool fail_on_warnings = true, const bool retry_on_readonly_interface = true); + void sendScriptBlocking(const std::string& program, const std::string& script_name = "", + const std::chrono::milliseconds start_timeout = std::chrono::seconds(1), + const bool fail_on_warnings = true, const bool retry_on_readonly_interface = true); bool checkCalibration(const std::string& checksum); diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index 398a357b2..96bb3c1d4 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -137,8 +137,8 @@ bool PrimaryClient::safetyModeAllowsExecution() } } -void PrimaryClient::sendScriptBlocking(const std::string& program, std::string script_name, - std::chrono::milliseconds start_timeout, bool fail_on_warnings, +void PrimaryClient::sendScriptBlocking(const std::string& program, const std::string& script_name, + const std::chrono::milliseconds start_timeout, const bool fail_on_warnings, const bool retry_on_readonly_interface) { ScriptInfo script_info = prepare_script(program, script_name); From 4c0eda2bd8c43e912b26f7df3dcb22ae829bf4dc Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 17 Jun 2026 08:28:51 +0200 Subject: [PATCH 13/14] Update doxygen comment --- include/ur_client_library/primary/primary_client.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index 9b16c4b16..e0c82f337 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -95,7 +95,10 @@ class PrimaryClient /*! * \brief Sends a custom script program to the robot. * - * The given code must be valid according the UR Scripting Manual. + * The given code must be valid according the UR Scripting Manual. This function doesn't give any + * feedback whether the script was executed successfully or not, it only reports whether the + * script was uploaded to the robot or not. For feedback on the execution of the script, use + * sendScriptBlocking(). * * \param program URScript code that shall be executed by the robot. * @@ -124,8 +127,12 @@ class PrimaryClient * error during execution. Default true * * \param retry_on_readonly_interface Whether to retry, if the primary interface is read-only. This will restart the - * primary interface, and then try sending the script again. If the interface is still read-only a + * primary interface connection, and then try sending the script again. If the interface is still read-only a * ReadOnlyInterfaceException will be thrown. Default true + * \note The primary interface connection is read-only when the robot is not in remote control + * mode. If the robot switches from local control mode to remote control mode, the connection + * remains read-only. In this case, reconnecting will result in a read-write primary interface connection, and the + * script can be executed successfully. * * \throw urcl::ScriptCodeSyntaxException if the given script code has syntax errors, which are checked here. * \throw urcl::UrException if the stop command cannot be sent to the robot. @@ -342,7 +349,8 @@ class PrimaryClient */ RobotSeries getRobotSeries(); - /* \brief Check if the current safety mode allows for script execution + /*! + * \brief Check if the current safety mode allows for script execution * * Safety modes allowing for execution are: NORMAL, REDUCED, RECOVERY, UNDEFINED_SAFETY_MODE */ From 572da92d9483d9d6b57bcb63de046a6847c1c8c4 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 17 Jun 2026 08:52:57 +0200 Subject: [PATCH 14/14] Fix parameter naming --- .../primary/primary_client.h | 10 ++++----- src/primary/primary_client.cpp | 22 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/ur_client_library/primary/primary_client.h b/include/ur_client_library/primary/primary_client.h index e0c82f337..8440a9ff8 100644 --- a/include/ur_client_library/primary/primary_client.h +++ b/include/ur_client_library/primary/primary_client.h @@ -371,11 +371,11 @@ class PrimaryClient void keyMessageCallback(KeyMessage& msg); void runtimeExceptionCallback(RuntimeExceptionMessage& msg); - ScriptInfo prepare_script(std::string script, std::string script_name); - std::vector strip_comments_and_whitespace(std::vector script_lines); - std::string truncate_script_name(std::string candidate_name); - void send_script_monitor_execution(const ScriptInfo& script_info, const std::chrono::milliseconds& timeout, - const bool fail_on_warnings); + ScriptInfo prepareScript(std::string script, std::string script_name); + std::vector stripCommentsAndWhitespace(std::vector script_lines); + std::string truncateScriptName(std::string candidate_name); + void sendScriptMonitorExecution(const ScriptInfo& script_info, const std::chrono::milliseconds& timeout, + const bool fail_on_warnings); PrimaryParser parser_; std::shared_ptr consumer_; diff --git a/src/primary/primary_client.cpp b/src/primary/primary_client.cpp index 96bb3c1d4..1ae6d4822 100644 --- a/src/primary/primary_client.cpp +++ b/src/primary/primary_client.cpp @@ -141,7 +141,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, const std::st const std::chrono::milliseconds start_timeout, const bool fail_on_warnings, const bool retry_on_readonly_interface) { - ScriptInfo script_info = prepare_script(program, script_name); + ScriptInfo script_info = prepareScript(program, script_name); RobotMode robot_mode = getRobotMode(); std::chrono::milliseconds robot_mode_timeout(1000); @@ -176,7 +176,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, const std::st try { - send_script_monitor_execution(script_info, start_timeout, fail_on_warnings); + sendScriptMonitorExecution(script_info, start_timeout, fail_on_warnings); } catch ([[maybe_unused]] const ReadOnlyInterfaceException& exc) { @@ -186,7 +186,7 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, const std::st "interface and retrying once."); stop(); start(); - send_script_monitor_execution(script_info, start_timeout, fail_on_warnings); + sendScriptMonitorExecution(script_info, start_timeout, fail_on_warnings); } else { @@ -195,8 +195,8 @@ void PrimaryClient::sendScriptBlocking(const std::string& program, const std::st } } -void PrimaryClient::send_script_monitor_execution(const ScriptInfo& script_info, - const std::chrono::milliseconds& timeout, const bool fail_on_warnings) +void PrimaryClient::sendScriptMonitorExecution(const ScriptInfo& script_info, const std::chrono::milliseconds& timeout, + const bool fail_on_warnings) { // Clear runtime exception { @@ -416,7 +416,7 @@ void PrimaryClient::send_script_monitor_execution(const ScriptInfo& script_info, } } -std::vector PrimaryClient::strip_comments_and_whitespace(std::vector split_script) +std::vector PrimaryClient::stripCommentsAndWhitespace(std::vector split_script) { std::vector stripped_script; for (auto line : split_script) @@ -440,7 +440,7 @@ std::vector PrimaryClient::strip_comments_and_whitespace(std::vecto return stripped_script; } -std::string PrimaryClient::truncate_script_name(const std::string candidate_name) +std::string PrimaryClient::truncateScriptName(const std::string candidate_name) { std::string final_name = candidate_name; // Limit script name length to 31, to ensure backwards compatibility @@ -453,13 +453,13 @@ std::string PrimaryClient::truncate_script_name(const std::string candidate_name return final_name; } -ScriptInfo PrimaryClient::prepare_script(std::string script, std::string script_name) +ScriptInfo PrimaryClient::prepareScript(std::string script, std::string script_name) { // Split the given script in to separate lines std::vector split_script = splitString(script, "\n"); // Remove all comments and white-space-only lines - std::vector stripped_script = strip_comments_and_whitespace(split_script); + std::vector stripped_script = stripCommentsAndWhitespace(split_script); if (stripped_script.size() == 0) { @@ -479,7 +479,7 @@ ScriptInfo PrimaryClient::prepare_script(std::string script, std::string script_ stripped_script[0].substr(0, 4).find("sec ") == script.npos) { // Check that the final name is not too long - actual_script_name = truncate_script_name(actual_script_name); + actual_script_name = truncateScriptName(actual_script_name); std::string definition = "def " + actual_script_name + "():"; std::string end = "end"; // Add indentation to the existing script code @@ -510,7 +510,7 @@ ScriptInfo PrimaryClient::prepare_script(std::string script, std::string script_ actual_script_type = ScriptTypes::SEC; } // Check that the script name is not too long, replace it, if it is - actual_script_name = truncate_script_name(name_in_script); + actual_script_name = truncateScriptName(name_in_script); if (actual_script_name.size() != name_in_script.size()) { stripped_script[0].replace(stripped_script[0].find(name_in_script), name_in_script.size(), actual_script_name);