Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 17 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ jobs:
- ROBOT_MODEL: 'ur5e'
URSIM_VERSION: '10.7.0'
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
- ROBOT_MODEL: 'ur5e'
URSIM_VERSION: '10.11.0'
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
- ROBOT_MODEL: 'ur5e'
URSIM_VERSION: '10.12.0'
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'

steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -74,8 +80,15 @@ jobs:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
flags: ${{ matrix.env.ROBOT_MODEL }}-${{ matrix.env.URSIM_VERSION }}
- id: check_polyscopex
run: |
if [[ "${{matrix.env.URSIM_VERSION}}" == "10."* ]]; then
echo "is_polyscopex=true" >> $GITHUB_OUTPUT
else
echo "is_polyscopex=false" >> $GITHUB_OUTPUT
fi
- name: Generate URSim log files
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
run: |
nc -q 1 192.168.56.101 29999 <<END
saveLog
Expand All @@ -85,21 +98,21 @@ jobs:
docker cp ursim:/ursim/polyscope.log ursim_logs/polyscope.log
docker cp ursim:/ursim/log_history.txt ursim_logs/log_history.txt
- name: Copy flight reports
if: failure() && matrix.env.URSIM_VERSION != '10.7.0'
if: ${{ failure() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
run: |
mkdir -p ursim_logs/flightreports
docker cp ursim:/ursim/flightreports/. ursim_logs/flightreports/
- name: Upload logfiles
uses: actions/upload-artifact@v6
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
with:
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_URSim_Logs
path: ursim_logs
if-no-files-found: error
retention-days: 10
- name: Upload test artifacts
uses: actions/upload-artifact@v6
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
with:
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_test_artifacts
path: test_artifacts
Expand Down
1 change: 1 addition & 0 deletions doc/polyscope_compatibility.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ table below or checkout the latest tag before the breaking changes were introduc
- Dashboard client -- |polyscope| X received the first implementation of the Robot API
replacing the Dashboard Server in version 10.11.0. It covers robot state control and loading
and playing programs.
From version 10.12, it also supports uploading programs to the robot and downloading programs from the robot, as well as listing existing programs on the robot.
- Using external control on |polyscope| X requires another URCapX for making external control
work. This is currently in the process of being created.
See `Universal Robots External Control URCapX <https://github.com/UniversalRobots/Universal_Robots_ExternalControl_URCapX>`_
Expand Down
166 changes: 89 additions & 77 deletions examples/dashboard_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ int main(int argc, char* argv[])
{
policy = DashboardClient::ClientPolicy::G5;
}
else if (version_information->minor < 11)
else if (version_information->minor < 12)
{
URCL_LOG_ERROR("DashboardClient examples require PolyScope version 10.11.0 or higher. Exiting now.");
// We need isInRemoteControl for PolyScope X, which is only supported from version 10.12.0 on.
URCL_LOG_ERROR("DashboardClient examples require PolyScope version 10.12.0 or higher. Exiting now.");
return 0;
}

Expand All @@ -84,6 +85,13 @@ int main(int argc, char* argv[])
return 1;
}

bool robot_in_remote_control = false;
// CB3 doesn't have remote control
if (version_information->major >= 5)
{
robot_in_remote_control = my_dashboard->commandIsInRemoteControl();
}

// Bring the robot to a defined state being powered off.
if (version_information->major < 10)
{
Expand All @@ -99,106 +107,110 @@ int main(int argc, char* argv[])

my_dashboard->commandCloseSafetyPopup();
}
else
else if (robot_in_remote_control)
{
// We're ignoring errors here since
// powering off an already powered off robot will return an error.
my_dashboard->commandPowerOff();
my_dashboard->setPolyscopeVersion(*version_information);
}

// Power it on
if (!my_dashboard->commandPowerOn())
{
URCL_LOG_ERROR("Could not send Power on command");
return 1;
}

// Release the brakes
if (!my_dashboard->commandBrakeRelease())
if (version_information->major < 10 || robot_in_remote_control)
{
URCL_LOG_ERROR("Could not send BrakeRelease command");
return 1;
}

// Load existing program
std::string program_file_name_to_be_loaded("wait_program.urp");
if (version_information->major >= 10)
{
// For PolyScope X, the program doesn't have an ending
program_file_name_to_be_loaded = "wait_program";
}
if (!my_dashboard->commandLoadProgram(program_file_name_to_be_loaded))
{
URCL_LOG_ERROR("Could not load %s program", program_file_name_to_be_loaded.c_str());
return 1;
}

std::this_thread::sleep_for(std::chrono::seconds(1));

// Play loaded program
if (!my_dashboard->commandPlay())
{
URCL_LOG_ERROR("Could not play program");
return 1;
}
if (!my_dashboard->commandPowerOn())
{
URCL_LOG_ERROR("Could not send Power on command");
return 1;
}

// Pause running program
if (!my_dashboard->commandPause())
{
URCL_LOG_ERROR("Could not pause program");
return 1;
}
// Release the brakes
if (!my_dashboard->commandBrakeRelease())
{
URCL_LOG_ERROR("Could not send BrakeRelease command");
return 1;
}

// Continue
if (version_information->major >= 10)
{
// For PolyScope X, the command is called "resume"
if (!my_dashboard->commandResume())
// Load existing program
std::string program_file_name_to_be_loaded("wait_program.urp");
if (version_information->major >= 10)
{
// For PolyScope X, the program doesn't have an ending
program_file_name_to_be_loaded = "wait_program";
}
if (!my_dashboard->commandLoadProgram(program_file_name_to_be_loaded))
{
URCL_LOG_ERROR("Could not resume program");
URCL_LOG_ERROR("Could not load %s program", program_file_name_to_be_loaded.c_str());
return 1;
}
}
else
{
// For e-Series, the command is called "play"

std::this_thread::sleep_for(std::chrono::seconds(1));

// Play loaded program
if (!my_dashboard->commandPlay())
{
URCL_LOG_ERROR("Could not resume program");
URCL_LOG_ERROR("Could not play program");
return 1;
}
}

// Stop program
if (!my_dashboard->commandStop())
{
URCL_LOG_ERROR("Could not stop program");
return 1;
}
// Pause running program
if (!my_dashboard->commandPause())
{
URCL_LOG_ERROR("Could not pause program");
return 1;
}

// Power it off
if (!my_dashboard->commandPowerOff())
{
URCL_LOG_ERROR("Could not send Power off command");
return 1;
}
// Continue
if (version_information->major >= 10)
{
// For PolyScope X, the command is called "resume"
if (!my_dashboard->commandResume())
{
URCL_LOG_ERROR("Could not resume program");
return 1;
}
}
else
{
// For e-Series, the command is called "play"
if (!my_dashboard->commandPlay())
{
URCL_LOG_ERROR("Could not resume program");
return 1;
}
}

if (version_information->major < 10)
{
// Flush the log
if (!my_dashboard->commandSaveLog())
// Stop program
if (!my_dashboard->commandStop())
{
URCL_LOG_ERROR("Could not send the save log command");
URCL_LOG_ERROR("Could not stop program");
return 1;
}

// Make a raw request and save the response
std::string program_state = my_dashboard->sendAndReceive("programState");
URCL_LOG_INFO("Program state: %s", program_state.c_str());
// Power it off
if (!my_dashboard->commandPowerOff())
{
URCL_LOG_ERROR("Could not send Power off command");
return 1;
}

// The response can be checked with a regular expression
bool success = my_dashboard->sendRequest("power off", "Powering off");
URCL_LOG_INFO("Power off command success: %d", success);
if (version_information->major < 10)
{
// Flush the log
if (!my_dashboard->commandSaveLog())
{
URCL_LOG_ERROR("Could not send the save log command");
return 1;
}

// Make a raw request and save the response
std::string program_state = my_dashboard->sendAndReceive("programState");
URCL_LOG_INFO("Program state: %s", program_state.c_str());

// The response can be checked with a regular expression
bool success = my_dashboard->sendRequest("power off", "Powering off");
URCL_LOG_INFO("Power off command success: %d", success);
}
}

return 0;
Expand Down
57 changes: 57 additions & 0 deletions include/ur_client_library/ur/dashboard_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,50 @@ class DashboardClient
*/
DashboardResponse commandSaveLogWithResponse();

/*!
* \brief Get the list of programs on the robot
*
* Stores the following entries in the data field:
*
* - 'programs': std::vector<ProgramInformation>
*/
DashboardResponse commandGetProgramListWithResponse();

/*!
* \brief Upload a new program to the robot
*
* \param file_path The path to the program file on the machine where the dashboard client is running. The file will
* be uploaded to the root of the programs directory on the robot.
*
* Stores the following entries in the data field:
*
* - 'program_name': std::string
*/
DashboardResponse commandUploadProgramWithResponse(const std::string& file_path);

/*!
* \brief Update an existing program on the robot
*
* \param file_path The path to the program file on the machine where the dashboard client is running. The file will
* be uploaded to the root of the programs directory on the robot and override an already existing file with the same
* name.
*
* Stores the following entries in the data field:
*
* - 'program_name': std::string
*/
DashboardResponse commandUpdateProgramWithResponse(const std::string& file_path);

/*!
* \brief Download a program from the robot
*
* \param filename The name of the program file on the robot. This is the name as returned by
* commandGetProgramListWithResponse. \param save_path The path where the program file should be saved on the machine
* where the dashboard client is running.
*
*/
DashboardResponse commandDownloadProgramWithResponse(const std::string& filename, const std::string& save_path);

/*!
* \brief Makes sure that the dashboard_server's version is above the required version
*
Expand Down Expand Up @@ -748,6 +792,19 @@ class DashboardClient
*/
void setReceiveTimeout(const timeval& timeout);

/*!
* \brief Sets the polyscope version manually.
*
* If the dashboard client implementation is not able to query the version from the robot
* automatically, this function can be used to set it manually.
*
* \param version The version string as returned by the robot
*/
void setPolyscopeVersion(const VersionInformation& version)
{
impl_->setPolyscopeVersion(version);
}

protected:
std::shared_ptr<DashboardClientImpl> impl_;
};
Expand Down
Loading
Loading