Skip to content

Commit 542731a

Browse files
urfeexURJala
andauthored
Add more RobotAPI commands to PolyScope X Dashboard Client (UniversalRobots#435)
This adds the endpoints as added in [PolyScope 10.12.0](https://www.universal-robots.com/articles/ur/release-notes/release-note-software-version-1012x/) --------- Co-authored-by: Jacob Larsen <jala@universal-robots.com>
1 parent 08c33c3 commit 542731a

18 files changed

Lines changed: 5532 additions & 139 deletions

.github/workflows/ci.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ jobs:
2929
- ROBOT_MODEL: 'ur5e'
3030
URSIM_VERSION: '10.7.0'
3131
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
32+
- ROBOT_MODEL: 'ur5e'
33+
URSIM_VERSION: '10.11.0'
34+
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
35+
- ROBOT_MODEL: 'ur5e'
36+
URSIM_VERSION: '10.12.0'
37+
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
3238

3339
steps:
3440
- uses: actions/checkout@v6
@@ -42,6 +48,15 @@ jobs:
4248
PROGRAM_FOLDER: ${{matrix.env.PROGRAM_FOLDER}}
4349
- name: install-pips
4450
run: pip install pandas lxml
51+
- id: check_polyscopex
52+
run: |
53+
if [[ "${{matrix.env.URSIM_VERSION}}" == "10."* ]]; then
54+
echo "is_polyscopex=true" >> $GITHUB_OUTPUT
55+
else
56+
echo "is_polyscopex=false" >> $GITHUB_OUTPUT
57+
fi
58+
- name: setup chrome
59+
uses: browser-actions/setup-chrome@v2
4560
- name: configure
4661
run: mkdir build && cd build && cmake .. -DBUILDING_TESTS=1 -DINTEGRATION_TESTS=1 -DWITH_ASAN=ON
4762
env:
@@ -52,6 +67,9 @@ jobs:
5267
run: cmake --build build --config Debug
5368
- name: Create folder for test artifacts
5469
run: mkdir -p test_artifacts
70+
- name: Access PolyScope
71+
if: ${{ steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
72+
run: chrome --no-sandbox --disable-settuid-sandbox --headless=new 192.168.56.101 &
5573
- name: test
5674
run: cd build && ctest --output-on-failure --output-junit junit.xml
5775
env:
@@ -75,7 +93,7 @@ jobs:
7593
token: ${{ secrets.CODECOV_TOKEN }}
7694
flags: ${{ matrix.env.ROBOT_MODEL }}-${{ matrix.env.URSIM_VERSION }}
7795
- name: Generate URSim log files
78-
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
96+
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
7997
run: |
8098
nc -q 1 192.168.56.101 29999 <<END
8199
saveLog
@@ -85,21 +103,21 @@ jobs:
85103
docker cp ursim:/ursim/polyscope.log ursim_logs/polyscope.log
86104
docker cp ursim:/ursim/log_history.txt ursim_logs/log_history.txt
87105
- name: Copy flight reports
88-
if: failure() && matrix.env.URSIM_VERSION != '10.7.0'
106+
if: ${{ failure() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
89107
run: |
90108
mkdir -p ursim_logs/flightreports
91109
docker cp ursim:/ursim/flightreports/. ursim_logs/flightreports/
92110
- name: Upload logfiles
93111
uses: actions/upload-artifact@v6
94-
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
112+
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
95113
with:
96114
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_URSim_Logs
97115
path: ursim_logs
98116
if-no-files-found: error
99117
retention-days: 10
100118
- name: Upload test artifacts
101119
uses: actions/upload-artifact@v6
102-
if: always() && matrix.env.URSIM_VERSION != '10.7.0'
120+
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'false' }}
103121
with:
104122
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_test_artifacts
105123
path: test_artifacts

doc/polyscope_compatibility.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ table below or checkout the latest tag before the breaking changes were introduc
4545
- Dashboard client -- |polyscope| X received the first implementation of the Robot API
4646
replacing the Dashboard Server in version 10.11.0. It covers robot state control and loading
4747
and playing programs.
48+
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.
4849
- Using external control on |polyscope| X requires another URCapX for making external control
4950
work. This is currently in the process of being created.
5051
See `Universal Robots External Control URCapX <https://github.com/UniversalRobots/Universal_Robots_ExternalControl_URCapX>`_

examples/dashboard_example.cpp

Lines changed: 88 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ int main(int argc, char* argv[])
7070
{
7171
policy = DashboardClient::ClientPolicy::G5;
7272
}
73-
else if (version_information->minor < 11)
73+
else if (version_information->minor < 12)
7474
{
75-
URCL_LOG_ERROR("DashboardClient examples require PolyScope version 10.11.0 or higher. Exiting now.");
75+
// We need isInRemoteControl for PolyScope X, which is only supported from version 10.12.0 on.
76+
URCL_LOG_ERROR("DashboardClient examples require PolyScope version 10.12.0 or higher. Exiting now.");
7677
return 0;
7778
}
7879

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

88+
bool robot_in_remote_control = false;
89+
// CB3 doesn't have remote control
90+
if (version_information->major >= 5)
91+
{
92+
robot_in_remote_control = my_dashboard->commandIsInRemoteControl();
93+
}
94+
8795
// Bring the robot to a defined state being powered off.
8896
if (version_information->major < 10)
8997
{
@@ -99,106 +107,109 @@ int main(int argc, char* argv[])
99107

100108
my_dashboard->commandCloseSafetyPopup();
101109
}
102-
else
110+
else if (robot_in_remote_control)
103111
{
104112
// We're ignoring errors here since
105113
// powering off an already powered off robot will return an error.
106114
my_dashboard->commandPowerOff();
107115
}
108116

109117
// Power it on
110-
if (!my_dashboard->commandPowerOn())
111-
{
112-
URCL_LOG_ERROR("Could not send Power on command");
113-
return 1;
114-
}
115-
116-
// Release the brakes
117-
if (!my_dashboard->commandBrakeRelease())
118+
if (version_information->major < 10 || robot_in_remote_control)
118119
{
119-
URCL_LOG_ERROR("Could not send BrakeRelease command");
120-
return 1;
121-
}
122-
123-
// Load existing program
124-
std::string program_file_name_to_be_loaded("wait_program.urp");
125-
if (version_information->major >= 10)
126-
{
127-
// For PolyScope X, the program doesn't have an ending
128-
program_file_name_to_be_loaded = "wait_program";
129-
}
130-
if (!my_dashboard->commandLoadProgram(program_file_name_to_be_loaded))
131-
{
132-
URCL_LOG_ERROR("Could not load %s program", program_file_name_to_be_loaded.c_str());
133-
return 1;
134-
}
135-
136-
std::this_thread::sleep_for(std::chrono::seconds(1));
137-
138-
// Play loaded program
139-
if (!my_dashboard->commandPlay())
140-
{
141-
URCL_LOG_ERROR("Could not play program");
142-
return 1;
143-
}
120+
if (!my_dashboard->commandPowerOn())
121+
{
122+
URCL_LOG_ERROR("Could not send Power on command");
123+
return 1;
124+
}
144125

145-
// Pause running program
146-
if (!my_dashboard->commandPause())
147-
{
148-
URCL_LOG_ERROR("Could not pause program");
149-
return 1;
150-
}
126+
// Release the brakes
127+
if (!my_dashboard->commandBrakeRelease())
128+
{
129+
URCL_LOG_ERROR("Could not send BrakeRelease command");
130+
return 1;
131+
}
151132

152-
// Continue
153-
if (version_information->major >= 10)
154-
{
155-
// For PolyScope X, the command is called "resume"
156-
if (!my_dashboard->commandResume())
133+
// Load existing program
134+
std::string program_file_name_to_be_loaded("wait_program.urp");
135+
if (version_information->major >= 10)
136+
{
137+
// For PolyScope X, the program doesn't have an ending
138+
program_file_name_to_be_loaded = "wait_program";
139+
}
140+
if (!my_dashboard->commandLoadProgram(program_file_name_to_be_loaded))
157141
{
158-
URCL_LOG_ERROR("Could not resume program");
142+
URCL_LOG_ERROR("Could not load %s program", program_file_name_to_be_loaded.c_str());
159143
return 1;
160144
}
161-
}
162-
else
163-
{
164-
// For e-Series, the command is called "play"
145+
146+
std::this_thread::sleep_for(std::chrono::seconds(1));
147+
148+
// Play loaded program
165149
if (!my_dashboard->commandPlay())
166150
{
167-
URCL_LOG_ERROR("Could not resume program");
151+
URCL_LOG_ERROR("Could not play program");
168152
return 1;
169153
}
170-
}
171154

172-
// Stop program
173-
if (!my_dashboard->commandStop())
174-
{
175-
URCL_LOG_ERROR("Could not stop program");
176-
return 1;
177-
}
155+
// Pause running program
156+
if (!my_dashboard->commandPause())
157+
{
158+
URCL_LOG_ERROR("Could not pause program");
159+
return 1;
160+
}
178161

179-
// Power it off
180-
if (!my_dashboard->commandPowerOff())
181-
{
182-
URCL_LOG_ERROR("Could not send Power off command");
183-
return 1;
184-
}
162+
// Continue
163+
if (version_information->major >= 10)
164+
{
165+
// For PolyScope X, the command is called "resume"
166+
if (!my_dashboard->commandResume())
167+
{
168+
URCL_LOG_ERROR("Could not resume program");
169+
return 1;
170+
}
171+
}
172+
else
173+
{
174+
// For e-Series, the command is called "play"
175+
if (!my_dashboard->commandPlay())
176+
{
177+
URCL_LOG_ERROR("Could not resume program");
178+
return 1;
179+
}
180+
}
185181

186-
if (version_information->major < 10)
187-
{
188-
// Flush the log
189-
if (!my_dashboard->commandSaveLog())
182+
// Stop program
183+
if (!my_dashboard->commandStop())
190184
{
191-
URCL_LOG_ERROR("Could not send the save log command");
185+
URCL_LOG_ERROR("Could not stop program");
192186
return 1;
193187
}
194188

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

199-
// The response can be checked with a regular expression
200-
bool success = my_dashboard->sendRequest("power off", "Powering off");
201-
URCL_LOG_INFO("Power off command success: %d", success);
196+
if (version_information->major < 10)
197+
{
198+
// Flush the log
199+
if (!my_dashboard->commandSaveLog())
200+
{
201+
URCL_LOG_ERROR("Could not send the save log command");
202+
return 1;
203+
}
204+
205+
// Make a raw request and save the response
206+
std::string program_state = my_dashboard->sendAndReceive("programState");
207+
URCL_LOG_INFO("Program state: %s", program_state.c_str());
208+
209+
// The response can be checked with a regular expression
210+
bool success = my_dashboard->sendRequest("power off", "Powering off");
211+
URCL_LOG_INFO("Power off command success: %d", success);
212+
}
202213
}
203214

204215
return 0;

include/ur_client_library/ur/dashboard_client.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,50 @@ class DashboardClient
721721
*/
722722
DashboardResponse commandSaveLogWithResponse();
723723

724+
/*!
725+
* \brief Get the list of programs on the robot
726+
*
727+
* Stores the following entries in the data field:
728+
*
729+
* - 'programs': std::vector<ProgramInformation>
730+
*/
731+
DashboardResponse commandGetProgramListWithResponse();
732+
733+
/*!
734+
* \brief Upload a new program to the robot
735+
*
736+
* \param file_path The path to the program file on the machine where the dashboard client is running. The file will
737+
* be uploaded to the root of the programs directory on the robot.
738+
*
739+
* Stores the following entries in the data field:
740+
*
741+
* - 'program_name': std::string
742+
*/
743+
DashboardResponse commandUploadProgramWithResponse(const std::string& file_path);
744+
745+
/*!
746+
* \brief Update an existing program on the robot
747+
*
748+
* \param file_path The path to the program file on the machine where the dashboard client is running. The file will
749+
* be uploaded to the root of the programs directory on the robot and override an already existing file with the same
750+
* name.
751+
*
752+
* Stores the following entries in the data field:
753+
*
754+
* - 'program_name': std::string
755+
*/
756+
DashboardResponse commandUpdateProgramWithResponse(const std::string& file_path);
757+
758+
/*!
759+
* \brief Download a program from the robot
760+
*
761+
* \param filename The name of the program file on the robot. This is the name as returned by
762+
* commandGetProgramListWithResponse. \param save_path The path where the program file should be saved on the machine
763+
* where the dashboard client is running.
764+
*
765+
*/
766+
DashboardResponse commandDownloadProgramWithResponse(const std::string& filename, const std::string& save_path);
767+
724768
/*!
725769
* \brief Makes sure that the dashboard_server's version is above the required version
726770
*

0 commit comments

Comments
 (0)