Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/ur_client_library/ur/dashboard_client_implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,27 @@ class DashboardClientImpl
*/
virtual void setReceiveTimeout([[maybe_unused]] const timeval& timeout) {};

/*!
* \brief Gets the configured send timeout. If send timeout is unconfigured "normal" socket
* timeout of 1 second will be returned.
*
* \returns configured send timeout
*/
virtual timeval getConfiguredSendTimeout() const
{
timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
return tv;
}

/*!
* \brief Sets the send timeout for the socket.
*
* \param timeout The timeout to be set
*/
virtual void setSendTimeout([[maybe_unused]] const timeval& timeout) {};

/*!
* \brief Sends command and verifies that a valid answer is received.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,10 @@ class DashboardClientImplX : public DashboardClientImpl
DashboardResponse commandUpdateProgram(const std::string& file_path) override;
DashboardResponse commandDownloadProgram(const std::string& program_name, const std::string& save_path) override;

void setReceiveTimeout([[maybe_unused]] const timeval& timeout) override
{
}
// Defined in the .cpp because httplib::Client is only forward-declared in this header.
void setReceiveTimeout(const timeval& timeout) override;
void setSendTimeout(const timeval& timeout) override;
timeval getConfiguredSendTimeout() const override;

protected:
DashboardResponse performProgramUpload(
Expand All @@ -185,6 +186,8 @@ class DashboardClientImplX : public DashboardClientImpl

std::unique_ptr<httplib::Client> cli_;
VersionInformation robot_api_version_;
timeval recv_timeout_ = {10, 0};
timeval send_timeout_ = {10, 0};
};

} // namespace urcl
79 changes: 72 additions & 7 deletions src/ur/dashboard_client_implementation_x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <chrono>
#include <string>
#include <fstream>
#include <ios>
Expand All @@ -49,6 +50,37 @@ DashboardClientImplX::DashboardClientImplX(const std::string& host) : DashboardC
// Some targets have changed between versions redirecting to the correct endpoint. For this to
// work, we'll have to follow redirects, which is not the default for httplib.
cli_->set_follow_location(true);

// cpp-httplib's default read_timeout is 300 seconds. Applied unchanged, this makes any
// dashboard call hang for 5 minutes when the controller becomes unresponsive (e.g. network
// partition, paused container in tests).
//
// The 10 s default for read/write is chosen to cover blocking calls that legitimately take
// time on real hardware — brake_release, commandLoadProgram (read), commandUploadProgram
// (write), commandUpdateProgram (write), commandDownloadProgram (read) — without forcing
// each one to plumb its own timeout. commandPowerOn already accepts its own (longer)
// timeout parameter. Callers needing different limits can override via setReceiveTimeout.
Comment thread
cursor[bot] marked this conversation as resolved.
cli_->set_connection_timeout(std::chrono::seconds(5));
cli_->set_read_timeout(std::chrono::seconds(10));
cli_->set_write_timeout(std::chrono::seconds(10));
Comment thread
urfeex marked this conversation as resolved.
Outdated
}

void DashboardClientImplX::setReceiveTimeout(const timeval& timeout)
{
recv_timeout_ = timeout;
if (cli_)
{
cli_->set_read_timeout(std::chrono::seconds(timeout.tv_sec) + std::chrono::microseconds(timeout.tv_usec));
}
}

void DashboardClientImplX::setSendTimeout(const timeval& timeout)
{
send_timeout_ = timeout;
if (cli_)
{
cli_->set_write_timeout(std::chrono::seconds(timeout.tv_sec) + std::chrono::microseconds(timeout.tv_usec));
}
}

std::string DashboardClientImplX::sendAndReceive([[maybe_unused]] const std::string& text)
Expand All @@ -61,7 +93,7 @@ bool DashboardClientImplX::connect([[maybe_unused]] const size_t max_num_tries,
{
std::string endpoint = base_url_ + "/openapi.json";
// The PolyScope X Robot API doesn't require any connection prior to making calls. However, this
// check call will assure that the endpoint for making Robot API calls exist. This could fail if
// check call will assurea that the endpoint for making Robot API calls exist. This could fail if
Comment thread
urfeex marked this conversation as resolved.
Outdated
// the IP address is wrong or the robot at the IP doesn't have the necessary software version.
if (auto res = cli_->Get(endpoint))
{
Expand Down Expand Up @@ -91,10 +123,17 @@ void DashboardClientImplX::disconnect()

timeval DashboardClientImplX::getConfiguredReceiveTimeout() const
{
timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
return tv;
// If the caller has explicitly configured a receive timeout via setReceiveTimeout,
// return that. Otherwise fall back to the constructor default. See the constructor
// comment for the rationale on the 10 s default (covers brake_release / program
// load/upload/download without per-method timeouts).
Comment thread
Vinicius-T-Robotics marked this conversation as resolved.
Outdated
return recv_timeout_;
}
Comment thread
urfeex marked this conversation as resolved.

timeval DashboardClientImplX::getConfiguredSendTimeout() const
{
// Mirrors getConfiguredReceiveTimeout. Default of 10 s matches the constructor.
Comment thread
Vinicius-T-Robotics marked this conversation as resolved.
Outdated
return send_timeout_;
}

VersionInformation DashboardClientImplX::queryPolyScopeVersion()
Expand Down Expand Up @@ -145,9 +184,35 @@ DashboardResponse DashboardClientImplX::commandPowerOff()
return put("/robotstate/v1/state", R"({"action": "POWER_OFF"})");
}

DashboardResponse DashboardClientImplX::commandPowerOn([[maybe_unused]] const std::chrono::duration<double> timeout)
DashboardResponse DashboardClientImplX::commandPowerOn(const std::chrono::duration<double> timeout)
{
return put("/robotstate/v1/state", R"({"action": "POWER_ON"})");
// commandPowerOn can take significantly longer than steady-state dashboard calls (the robot
// boots, runs self-checks, etc.). Bump the read timeout to the caller-supplied value for the
// duration of the PUT, then restore — using the same save-bump-restore pattern as connect().
// The restore must run on every exit, including exceptions from inside put(), hence the
// catch(...) rethrow guard.
timeval configured_tv = getConfiguredReceiveTimeout();
// Preserve sub-second precision: duration_cast<seconds> truncates fractional values,
// so go via microseconds (the smallest unit timeval can represent) and split.
const auto pwron_us = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
timeval pwron_tv;
pwron_tv.tv_sec = static_cast<time_t>(pwron_us.count() / 1'000'000);
pwron_tv.tv_usec = static_cast<suseconds_t>(pwron_us.count() % 1'000'000);
setReceiveTimeout(pwron_tv);
Comment thread
cursor[bot] marked this conversation as resolved.

DashboardResponse response;
try
{
response = put("/robotstate/v1/state", R"({"action": "POWER_ON"})");
}
catch (...)
{
setReceiveTimeout(configured_tv);
throw;
}

setReceiveTimeout(configured_tv);
return response;
Comment thread
urfeex marked this conversation as resolved.
}

DashboardResponse DashboardClientImplX::commandBrakeRelease()
Expand Down