diff --git a/examples/dashboard_example.cpp b/examples/dashboard_example.cpp index d50a46b59..1e32afe41 100644 --- a/examples/dashboard_example.cpp +++ b/examples/dashboard_example.cpp @@ -22,12 +22,13 @@ #include #include +#include using namespace urcl; // In a real-world example it would be better to get those values from command line parameters / a // better configuration system such as Boost.Program_options -const std::string DEFAULT_ROBOT_IP = "127.0.0.1"; +const std::string DEFAULT_ROBOT_IP = "10.53.253.22"; std::unique_ptr my_dashboard; @@ -64,6 +65,8 @@ int main(int argc, char* argv[]) return 1; } + my_dashboard->commandCloseSafetyPopup(); + // Power it on if (!my_dashboard->commandPowerOn()) { @@ -72,9 +75,9 @@ int main(int argc, char* argv[]) } // Release the brakes - if (!my_dashboard->commandBreakeRelease()) + if (!my_dashboard->commandBrakeRelease()) { - URCL_LOG_ERROR("Could not send BreakeRelease command"); + URCL_LOG_ERROR("Could not send BrakeRelease command"); return 1; } diff --git a/examples/full_driver.cpp b/examples/full_driver.cpp index 0f3175d1c..b499bee65 100644 --- a/examples/full_driver.cpp +++ b/examples/full_driver.cpp @@ -98,9 +98,9 @@ int main(int argc, char* argv[]) } // Release the brakes - if (!my_dashboard->commandBreakeRelease()) + if (!my_dashboard->commandBrakeRelease()) { - URCL_LOG_ERROR("Could not send BreakeRelease command"); + URCL_LOG_ERROR("Could not send BrakeRelease command"); return 1; } diff --git a/include/ur_client_library/ur/dashboard_client.h b/include/ur_client_library/ur/dashboard_client.h index 3e5f473d4..018c4451c 100644 --- a/include/ur_client_library/ur/dashboard_client.h +++ b/include/ur_client_library/ur/dashboard_client.h @@ -57,7 +57,7 @@ class DashboardClient : public comm::TCPSocket const int DASHBOARD_SERVER_PORT = 29999; /*! - * \brief Opens a connection to the dasboard server on the host as specified in the constructor. + * \brief Opens a connection to the dashboard server on the host as specified in the constructor. * * \returns True on successful connection, false otherwise. */ @@ -89,6 +89,17 @@ class DashboardClient : public comm::TCPSocket */ bool sendRequest(const std::string& command, const std::string& expected); + /*! + * \brief Sends command and compare it with the expected answer + * + * \param command Command that will be sent to the server. It is important, that the command sent is finished with a + * '\n' (newline) so it will be processed by the server. + * \param expected Expected replay + * + * \return Answer string as received by the server + */ + std::string sendRequestString(const std::string& command, const std::string& expected); + /*! * \brief brief Sends a command and wait until it returns the expected answer * @@ -136,7 +147,7 @@ class DashboardClient : public comm::TCPSocket * * \return True succeeded */ - bool commandBreakeRelease(); + bool commandBrakeRelease(); /*! * \brief Send Load program command @@ -147,6 +158,15 @@ class DashboardClient : public comm::TCPSocket */ bool commandLoadProgram(const std::string& program_file_name); + /*! + * \brief Send Load installation command + * + * \param installation_file_name The installation file name with the installation extension + * + * \return True succeeded + */ + bool commandLoadInstallation(const std::string& installation_file_name); + /*! * \brief Send Play program command * @@ -203,6 +223,186 @@ class DashboardClient : public comm::TCPSocket */ bool commandShutdown(); + /*! + * \brief Send Quit command + * + * \return True succeeded + */ + bool commandQuit(); + + /*! + * \brief Send Running command + * + * \return True succeeded + */ + bool commandRunning(); + + /*! + * \brief Send Is program saved command + * + * \return True succeeded + */ + bool commandIsProgramSaved(); + + /*! + * \brief Send Is in remote control command + * + * \return True succeeded + */ + bool commandIsInRemoteControl(); + + /*! + * \brief Send popup command + * + * \param popup_text The text to be shown in the popup + * + * \return True succeeded + */ + bool commandPopup(const std::string& popup_text); + + /*! + * \brief Send text to log + * + * \param log_text The text to be sent to the log + * + * \return True succeeded + */ + bool commandAddToLog(const std::string& log_text); + + /*! + * \brief Get Polyscope version + * + * \param polyscope_version The string for the polyscope version number returned + * + * \return True succeeded + */ + bool commandPolyscopeVersion(std::string& polyscope_version); + + /*! + * \brief Get Robot model + * + * \param robot_model The string for the robot model returned + * + * \return True succeeded + */ + bool commandGetRobotModel(std::string& robot_model); + + /*! + * \brief Get Serial number + * + * \param serial_number The serial number of the robot returned + * + * \return True succeeded + */ + bool commandGetSerialNumber(std::string& serial_number); + + /*! + * \brief Get Robot mode + * + * \param robot_mode The mode of the robot returned + * + * \return True succeeded + */ + bool commandRobotMode(std::string& robot_mode); + + /*! + * \brief Get Loaded Program + * + * \param loaded_program The path to the loaded program + * + * \return True succeeded + */ + bool commandGetLoadedProgram(std::string& loaded_program); + + /*! + * \brief Get Safety mode + * + * \param safety_mode The safety mode of the robot returned + * + * \return True succeeded + */ + bool commandSafetyMode(std::string& safety_mode); + + /*! + * \brief Get Safety status + * + * \param safety_status The safety status of the robot returned + * + * \return True succeeded + */ + bool commandSafetyStatus(std::string& safety_status); + + /*! + * \brief Get Program state + * + * \param program_state The program state of the robot returned + * + * \return True succeeded + */ + bool commandProgramState(std::string& program_state); + + /*! + * \brief Get Operational mode + * + * \param operational_mode The operational mode of the robot returned (Only available for e-series) + * + * \return True succeeded + */ + bool commandGetOperationalMode(std::string& operational_mode); + + /*! + * \brief Send Set operational mode command (Only available for e-series) + * + * \param operational_mode The operational mode to set on the robot + * + * \return True succeeded + */ + bool commandSetOperationalMode(const std::string& operational_mode); + + /*! + * \brief Send Clear operational mode command + * + * \return True succeeded + */ + bool commandClearOperationalMode(); + + /*! + * \brief Send Set user role command (Only available for CB3) + * + * \param user_role The user role to set on the robot + * + * \return True succeeded + */ + bool commandSetUserRole(const std::string& user_role); + + /*! + * \brief Send Get user role command (Only available for CB3) + * + * \param user_role The user role on the robot + * + * \return True succeeded + */ + bool commandGetUserRole(std::string& user_role); + + /*! + * \brief Send Generate flight report command + * + * \param report_type The report type to set for the flight report + * + * \return True succeeded + */ + bool commandGenerateFlightReport(const std::string& report_type); + + /*! + * \brief Send Generate support file command + * + * \param dir_path The path to the directory of an already existing directory location inside the programs directory, + * where the support file is saved + * + * \return True succeeded + */ + bool commandGenerateSupportFile(const std::string& dir_path); + protected: virtual bool open(int socket_fd, struct sockaddr* address, size_t address_len) { @@ -210,10 +410,29 @@ class DashboardClient : public comm::TCPSocket } private: + /*! + * \brief Parse input for comparison of sw versions + * + * \param result Parsed result + * \param input Input sw version string + */ + void parseSWVersion(int result[4], const std::string& input); + /*! + * \brief Compare two sw versions + * + * \param a Sw version string for e-series + * \param b Sw version string for cb3 + * \param c Sw version string read from robot + * + * \return If a/b is less than c return true else return false + */ + bool lessThanVersion(const std::string& a, const std::string& b, const std::string& c); bool send(const std::string& text); std::string read(); void rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r "); + bool e_series_; // Is the robot e-series or cb3 + std::string polyscope_version_; std::string host_; int port_; std::mutex write_mutex_; diff --git a/src/ur/dashboard_client.cpp b/src/ur/dashboard_client.cpp index 457cc3be8..384d6d2c9 100644 --- a/src/ur/dashboard_client.cpp +++ b/src/ur/dashboard_client.cpp @@ -32,6 +32,8 @@ #include #include +#include + namespace urcl { DashboardClient::DashboardClient(const std::string& host) : host_(host), port_(DASHBOARD_SERVER_PORT) @@ -62,6 +64,9 @@ bool DashboardClient::connect() tv.tv_usec = 0; TCPSocket::setReceiveTimeout(tv); + std::string pv; + commandPolyscopeVersion(pv); + return ret_val; } @@ -126,11 +131,23 @@ bool DashboardClient::sendRequest(const std::string& command, const std::string& bool ret = std::regex_match(response, std::regex(expected)); if (!ret) { - URCL_LOG_WARN("Expected: \"%s\", but received: \"%s\"", expected.c_str(), response.c_str()); + throw UrException("Expected: " + expected + ", but received: " + response); } return ret; } +std::string DashboardClient::sendRequestString(const std::string& command, const std::string& expected) +{ + URCL_LOG_DEBUG("Send Request: %s", command.c_str()); + std::string response = sendAndReceive(command + "\n"); + bool ret = std::regex_match(response, std::regex(expected)); + if (!ret) + { + throw UrException("Expected: " + expected + ", but received: " + response); + } + return response; +} + bool DashboardClient::waitForReply(const std::string& command, const std::string& expected, double timeout) { const unsigned int TIME_STEP_SIZE_US(100000); // 100ms @@ -154,8 +171,8 @@ bool DashboardClient::waitForReply(const std::string& command, const std::string count = count + (0.000001 * TIME_STEP_SIZE_US); } - URCL_LOG_WARN("Did not got the expected \"%s\" respone within the timeout. Last respone was: \"%s\"", - expected.c_str(), response.c_str()); + URCL_LOG_WARN("Did not got the expected \"%s\" response within the timeout. Last response was: \"%s\"", + expected.c_str(), response.c_str()); // Is a warning here so retryCommand does not throw when retrying return false; } @@ -178,65 +195,313 @@ bool DashboardClient::retryCommand(const std::string& requestCommand, const std: return false; } +void DashboardClient::parseSWVersion(int result[4], const std::string& input) +{ + std::istringstream parser(input); + parser >> result[0]; + for (int idx = 1; idx < 4; idx++) + { + parser.get(); // Skip period + parser >> result[idx]; + } +} + +bool DashboardClient::lessThanVersion(const std::string& a, const std::string& b, const std::string& c) +{ + std::string ref = e_series_ == true ? a : b; + int parsedRef[4], parsedC[4]; + parseSWVersion(parsedRef, ref); + parseSWVersion(parsedC, c); + bool ret = std::lexicographical_compare(parsedRef, parsedRef + 4, parsedC, parsedC + 4); + if (!ret) + { + throw UrException("Polyscope software version required is: " + ref + ", but actual version is: " + c); + } + return ret; +} + bool DashboardClient::commandPowerOff() { + if (!lessThanVersion("5.0.0", "3.0", polyscope_version_)) + return false; return sendRequest("power off", "Powering off") && waitForReply("robotmode", "Robotmode: POWER_OFF"); } bool DashboardClient::commandPowerOn(unsigned int timeout) { + if (!lessThanVersion("5.0.0", "3.0", polyscope_version_)) + return false; return retryCommand("power on", "Powering on", "robotmode", "Robotmode: IDLE", timeout); } -bool DashboardClient::commandBreakeRelease() +bool DashboardClient::commandBrakeRelease() { + if (!lessThanVersion("5.0.0", "3.0", polyscope_version_)) + return false; return sendRequest("brake release", "Brake releasing") && waitForReply("robotmode", "Robotmode: RUNNING"); } bool DashboardClient::commandLoadProgram(const std::string& program_file_name) { + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; return sendRequest("load " + program_file_name + "", "(?:Loading program: ).*(?:" + program_file_name + ").*") && waitForReply("programState", "STOPPED " + program_file_name); } +bool DashboardClient::commandLoadInstallation(const std::string& installation_file_name) +{ + if (!lessThanVersion("5.0.0", "3.2", polyscope_version_)) + return false; + return sendRequest("load installation " + installation_file_name, + "(?:Loading installation: ).*(?:" + installation_file_name + ").*"); +} + bool DashboardClient::commandPlay() { + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; return sendRequest("play", "Starting program") && waitForReply("programState", "(?:PLAYING ).*"); } bool DashboardClient::commandPause() { + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; return sendRequest("pause", "Pausing program") && waitForReply("programState", "(?:PAUSED ).*"); } bool DashboardClient::commandStop() { + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; return sendRequest("stop", "Stopped") && waitForReply("programState", "(?:STOPPED ).*"); } bool DashboardClient::commandClosePopup() { + if (!lessThanVersion("5.0.0", "1.6", polyscope_version_)) + return false; return sendRequest("close popup", "closing popup"); } bool DashboardClient::commandCloseSafetyPopup() { + if (!lessThanVersion("5.0.0", "3.1", polyscope_version_)) + return false; return sendRequest("close safety popup", "closing safety popup"); } bool DashboardClient::commandRestartSafety() { + if (!lessThanVersion("5.1.0", "3.7", polyscope_version_)) + return false; return sendRequest("restart safety", "Restarting safety") && waitForReply("robotmode", "Robotmode: POWER_OFF"); } bool DashboardClient::commandUnlockProtectiveStop() { + if (!lessThanVersion("5.0.0", "3.1", polyscope_version_)) + return false; return sendRequest("unlock protective stop", "Protective stop releasing"); } bool DashboardClient::commandShutdown() { + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; return sendRequest("shutdown", "Shutting down"); } +bool DashboardClient::commandQuit() +{ + if (!lessThanVersion("5.0.0", "1.4", polyscope_version_)) + return false; + return sendRequest("quit", "Disconnected"); +} + +bool DashboardClient::commandRunning() +{ + if (!lessThanVersion("5.0.0", "1.6", polyscope_version_)) + return false; + return sendRequest("running", "Program running: true"); +} + +bool DashboardClient::commandIsProgramSaved() +{ + if (!lessThanVersion("5.0.0", "1.8", polyscope_version_)) + return false; + return sendRequest("isProgramSaved", "(?:true ).*"); +} + +bool DashboardClient::commandIsInRemoteControl() +{ + if (!lessThanVersion("5.6.0", "10. Only available on e-series robot", polyscope_version_)) // Only available on + // e-series + return false; + std::string response = sendAndReceive("is in remote control\n"); + bool ret = std::regex_match(response, std::regex("true")); + return ret; +} + +bool DashboardClient::commandPopup(const std::string& popup_text) +{ + if (!lessThanVersion("5.0.0", "1.6", polyscope_version_)) + return false; + return sendRequest("popup " + popup_text, "showing popup"); +} + +bool DashboardClient::commandAddToLog(const std::string& log_text) +{ + if (!lessThanVersion("5.0.0", "1.8", polyscope_version_)) + return false; + return sendRequest("addToLog " + log_text, "Added log message"); +} + +bool DashboardClient::commandPolyscopeVersion(std::string& polyscope_version) +{ + std::string expected = "(?:URSoftware ).*"; + polyscope_version = sendRequestString("PolyscopeVersion", expected); + polyscope_version_ = polyscope_version.substr(polyscope_version.find(" ") + 1, + polyscope_version.find(" (") - polyscope_version.find(" ") - 1); + e_series_ = stoi(polyscope_version_.substr(0, 1)) >= 5; + return std::regex_match(polyscope_version, std::regex(expected)); +} + +bool DashboardClient::commandGetRobotModel(std::string& robot_model) +{ + if (!lessThanVersion("5.6.0", "3.12", polyscope_version_)) + return false; + std::string expected = "(?:UR).*"; + robot_model = sendRequestString("get robot model", expected); + return std::regex_match(robot_model, std::regex(expected)); +} + +bool DashboardClient::commandGetSerialNumber(std::string& serial_number) +{ + if (!lessThanVersion("5.6.0", "3.12", polyscope_version_)) + return false; + std::string expected = "(?:20).*"; + serial_number = sendRequestString("get serial number", expected); + return std::regex_match(serial_number, std::regex(expected)); +} + +bool DashboardClient::commandRobotMode(std::string& robot_mode) +{ + if (!lessThanVersion("5.0.0", "1.6", polyscope_version_)) + return false; + std::string expected = "(?:Robotmode: ).*"; + robot_mode = sendRequestString("robotmode", expected); + return std::regex_match(robot_mode, std::regex(expected)); +} + +bool DashboardClient::commandGetLoadedProgram(std::string& loaded_program) +{ + if (!lessThanVersion("5.0.0", "1.6", polyscope_version_)) + return false; + std::string expected = "(?:Loaded program: ).*"; + loaded_program = sendRequestString("get loaded program", expected); + return std::regex_match(loaded_program, std::regex(expected)); +} + +bool DashboardClient::commandSafetyMode(std::string& safety_mode) +{ + if (!lessThanVersion("5.0.0", "3.0", polyscope_version_)) + return false; + std::string expected = "(?:Safetymode: ).*"; + safety_mode = sendRequestString("safetymode", expected); + return std::regex_match(safety_mode, std::regex(expected)); +} + +bool DashboardClient::commandSafetyStatus(std::string& safety_status) +{ + if (!lessThanVersion("5.4.0", "3.11", polyscope_version_)) + return false; + std::string expected = "(?:Safetystatus: ).*"; + safety_status = sendRequestString("safetystatus", expected); + return std::regex_match(safety_status, std::regex(expected)); +} + +bool DashboardClient::commandProgramState(std::string& program_state) +{ + if (!lessThanVersion("5.0.0", "1.8", polyscope_version_)) + return false; + std::string expected = "(?:).*"; + program_state = sendRequestString("programState", expected); + return !std::regex_match(program_state, std::regex("(?:could not understand).*")); +} + +bool DashboardClient::commandGetOperationalMode(std::string& operational_mode) +{ + if (!lessThanVersion("5.6.0", "10. Only available on e-series robot", polyscope_version_)) // Only available on + // e-series + return false; + std::string expected = "(?:).*"; + operational_mode = sendRequestString("get operational mode", expected); + return !std::regex_match(operational_mode, std::regex("(?:could not understand).*")); +} + +bool DashboardClient::commandSetOperationalMode(const std::string& operational_mode) +{ + if (!lessThanVersion("5.0.0", "10. Only available on e-series robot", polyscope_version_)) // Only available on + // e-series + return false; + return sendRequest("set operational mode " + operational_mode, + "(?:Operational mode ).*(?:" + operational_mode + ").*"); +} + +bool DashboardClient::commandClearOperationalMode() +{ + if (!lessThanVersion("5.0.0", "10. Only available on e-series robot", polyscope_version_)) // Only available on + // e-series + return false; + return sendRequest("clear operational mode", "(?:No longer controlling the operational mode. ).*"); +} + +bool DashboardClient::commandSetUserRole(const std::string& user_role) +{ + if (!lessThanVersion("10. Only available on CB3 robot", "1.8", polyscope_version_)) // Only available on + // e-series + return false; + return sendRequest("setUserRole " + user_role, "(?:Setting user role: ).*"); +} + +bool DashboardClient::commandGetUserRole(std::string& user_role) +{ + if (!lessThanVersion("10. Only available on CB3 robot", "1.8", polyscope_version_)) // Only available on + // e-series + return false; + std::string expected = "(?:).*"; + user_role = sendRequestString("getUserRole", expected); + return !std::regex_match(user_role, std::regex("(?:could not understand).*")); +} + +bool DashboardClient::commandGenerateFlightReport(const std::string& report_type) +{ + if (!lessThanVersion("5.8.0", "3.13", polyscope_version_)) + return false; + timeval tv; + tv.tv_sec = 180; + tv.tv_usec = 0; + TCPSocket::setReceiveTimeout(tv); // Set timeout to 3 minutes as this command can take a long time to complete + bool ret = sendRequest("generate flight report " + report_type, "(?:Flight Report generated with id:).*"); + tv.tv_sec = 1; // Reset timeout to standard timeout + TCPSocket::setReceiveTimeout(tv); + return ret; +} + +bool DashboardClient::commandGenerateSupportFile(const std::string& dir_path) +{ + if (!lessThanVersion("5.8.0", "3.13", polyscope_version_)) + return false; + timeval tv; + tv.tv_sec = 600; + tv.tv_usec = 0; + TCPSocket::setReceiveTimeout(tv); // Set timeout to 10 minutes as this command can take a long time to complete + bool ret = sendRequest("generate support file " + dir_path, "(?:Completed successfully:).*"); + tv.tv_sec = 1; // Reset timeout to standard timeout + TCPSocket::setReceiveTimeout(tv); + return ret; +} + } // namespace urcl diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ed87d4882..2e3729e15 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,10 +37,18 @@ if (INTEGRATION_TESTS) gtest_add_tests(TARGET rtde_tests WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + + add_executable(dashboard_client_tests test_dashboard_client.cpp) + target_compile_options(dashboard_client_tests PRIVATE ${CXX17_FLAG}) + target_include_directories(dashboard_client_tests PRIVATE ${GTEST_INCLUDE_DIRS}) + target_link_libraries(dashboard_client_tests PRIVATE ur_client_library::urcl ${GTEST_LIBRARIES}) + gtest_add_tests(TARGET dashboard_client_tests + ) else() message(STATUS "Skipping integration tests.") endif() + add_executable(primary_parser_tests test_primary_parser.cpp) target_compile_options(primary_parser_tests PRIVATE ${CXX17_FLAG}) target_include_directories(primary_parser_tests PRIVATE ${GTEST_INCLUDE_DIRS}) diff --git a/tests/resources/dockerursim/Dockerfile b/tests/resources/dockerursim/Dockerfile deleted file mode 100644 index 253b60d34..000000000 --- a/tests/resources/dockerursim/Dockerfile +++ /dev/null @@ -1,128 +0,0 @@ -# MIT License -# -# Original from https://github.com/ahobsonsayers/DockURSim -# Copyright (c) 2019 Arran Hobson Sayers -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -FROM lsiobase/guacgui:latest - -# Set Version Information -ARG VERSION="5.8.0.10253" -LABEL build_version="URSim Version: ${VERSION}" -ENV APPNAME="URSim" - -# Set Timezone -ARG TZ="Europe/London" -ENV TZ ${TZ} - -# Setup Environment -ENV DEBIAN_FRONTEND noninteractive - -# Set Home Directory -ENV HOME /ursim - -# Set robot model - Can be UR3, UR5 or UR10 -ENV ROBOT_MODEL UR5 - -RUN \ - echo "**** Installing Dependencies ****" && \ - apt-get update && \ - apt-get install -qy --no-install-recommends \ - openjdk-8-jre psmisc && \ - # Change java alternatives so we use openjdk8 (required by URSim) not openjdk11 that comes with guacgui - update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 10000 - -# Setup JAVA_HOME -ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 - -RUN \ - echo "**** Downloading URSim ****" && \ - # Make sure we are in the root - cd / - # Download URSim Linux tar.gz -RUN \ - if [ "$VERSION" = "5.8.0.10253" ]; then \ - curl https://s3-eu-west-1.amazonaws.com/ur-support-site/69987/URSim_Linux-5.8.0.10253.tar.gz -o URSim-Linux.tar.gz; \ - elif [ "$VERSION" = "5.9.1.1031110" ]; then \ - curl https://s3-eu-west-1.amazonaws.com/ur-support-site/77055/URSim_Linux-5.9.1.1031110.tar.gz -o URSim-Linux.tar.gz; \ - elif [ "$VERSION" = "3.14.1.1031110" ]; then \ - curl https://s3-eu-west-1.amazonaws.com/ur-support-site/77041/URSim_Linux-3.14.1.1031110.tar.gz -o URSim-Linux.tar.gz; \ - else \ - echo "Unknown version passed to Dockerfile. Exiting" &&\ - exit 1; \ - fi -RUN \ - # Extract tarball - tar xvzf URSim-Linux.tar.gz && \ - #Remove the tarball - rm URSim-Linux.tar.gz && \ - # Rename the URSim folder to jus ursim - mv /ursim* /ursim - -RUN \ - echo "**** Installing URSim ****" && \ - # cd to ursim folder - cd /ursim && \ - # Make URControl and all sh files executable - chmod +x ./*.sh ./URControl && \ - # - # Stop install of unnecessary packages and install required ones quietly - sed -i 's|apt-get -y install|apt-get -qy install --no-install-recommends|g' ./install.sh && \ - # Skip xterm command. We dont have a desktop - sed -i 's|tty -s|(exit 0)|g' install.sh && \ - # Skip Check of Java Version as we have the correct installed and the command will fail - sed -i 's|needToInstallJava$|(exit 0)|g' install.sh && \ - # Skip install of desktop shortcuts - we dont have a desktop - sed -i '/for TYPE in UR3 UR5 UR10/,$ d' ./install.sh && \ - # Remove commands that are not relevant on docker as we are root user - sed -i 's|pkexec ||g' ./install.sh && \ - sed -i 's|sudo ||g' ./install.sh && \ - #sed -i 's|sudo ||g' ./ursim-certificate-check.sh && \ - # - # Install URSim - ./install.sh && \ - # - echo "Installed URSim" - -RUN \ - echo "**** Clean Up ****" && \ - rm -rf \ - /tmp/* \ - /var/lib/apt/lists/* \ - /var/tmp/* - -# Copy ursim run service script -COPY ursim /etc/services.d/ursim -COPY safety.conf.UR5 /ursim/.urcontrol/ -# Expose ports -# Guacamole web browser viewer -EXPOSE 8080 -# VNC viewer -EXPOSE 3389 -# Modbus Port -EXPOSE 502 -# Interface Ports -EXPOSE 29999 -EXPOSE 30001-30004 - -# Mount Volumes -VOLUME /ursim - -ENTRYPOINT ["/init"] diff --git a/tests/resources/dockerursim/safety.conf.UR5 b/tests/resources/dockerursim/safety.conf.UR5 deleted file mode 100644 index 908b382f4..000000000 --- a/tests/resources/dockerursim/safety.conf.UR5 +++ /dev/null @@ -1,101 +0,0 @@ -# Beware: This file is auto-generated from PolyScope. -# NOTE: The SafetyParameters section is protected by a CRC checksum, please use the supplied tool - -## SafetyParameters ## -[NormalModeSafetyLimits] -maxTcpSpeed = 1.5 -maxForce = 150.0 -maxElbowSpeed = 1.5 -maxElbowForce = 150.0 -maxStoppingDistance = 0.5 -maxStoppingTime = 0.4 -maxPower = 300.0 -maxMomentum = 25.0 -maxJointSpeed = [3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926] -minJointPosition = [6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254] -maxJointPosition = [0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988] -minJointRevolutions = [-2, -2, -2, -2, -2, -2] -maxJointRevolutions = [1, 1, 1, 1, 1, 1] -plane0 = [0.0, 0.0, 0.0, 0.0, 0] -plane1 = [0.0, 0.0, 0.0, 0.0, 0] -plane2 = [0.0, 0.0, 0.0, 0.0, 0] -plane3 = [0.0, 0.0, 0.0, 0.0, 0] -plane4 = [0.0, 0.0, 0.0, 0.0, 0] -plane5 = [0.0, 0.0, 0.0, 0.0, 0] -plane6 = [0.0, 0.0, 0.0, 0.0, 0] -plane7 = [0.0, 0.0, 0.0, 0.0, 0] -tcpOrientationVector = [0.0, 0.0, 1.0] -maximumTcpOrientationDeviation = 6.2831855 - -[ReducedModeSafetyLimits] -maxTcpSpeed = 0.75 -maxForce = 120.0 -maxElbowSpeed = 0.75 -maxElbowForce = 120.0 -maxStoppingDistance = 0.3 -maxStoppingTime = 0.3 -maxPower = 200.0 -maxMomentum = 10.0 -maxJointSpeed = [3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926] -minJointPosition = [6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254] -maxJointPosition = [0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988] -minJointRevolutions = [-2, -2, -2, -2, -2, -2] -maxJointRevolutions = [1, 1, 1, 1, 1, 1] -plane0 = [0.0, 0.0, 0.0, 0.0, 0] -plane1 = [0.0, 0.0, 0.0, 0.0, 0] -plane2 = [0.0, 0.0, 0.0, 0.0, 0] -plane3 = [0.0, 0.0, 0.0, 0.0, 0] -plane4 = [0.0, 0.0, 0.0, 0.0, 0] -plane5 = [0.0, 0.0, 0.0, 0.0, 0] -plane6 = [0.0, 0.0, 0.0, 0.0, 0] -plane7 = [0.0, 0.0, 0.0, 0.0, 0] -tcpOrientationVector = [0.0, 0.0, 1.0] -maximumTcpOrientationDeviation = 6.2831855 - -[MiscConfiguration] -teach_pendant = 1 -euromap67 = 0 - -[SafetyIOConfiguration] -emergencyStopInputA = 255 -emergencyStopInputB = 255 -reducedModeInputA = 255 -reducedModeInputB = 255 -safeguardStopResetInputA = 0 -safeguardStopResetInputB = 1 -threePositionEnablingInputA = 255 -threePositionEnablingInputB = 255 -operationalModeInputA = 255 -operationalModeInputB = 255 -systemEmergencyStopOutputA = 255 -systemEmergencyStopOutputB = 255 -robotMovingOutputA = 255 -robotMovingOutputB = 255 -robotNotStoppingOutputA = 255 -robotNotStoppingOutputB = 255 -reducedModeOutputA = 255 -reducedModeOutputB = 255 -notReducedModeOutputA = 255 -notReducedModeOutputB = 255 - -[ReducedModeTriggerPlanes] -plane0 = [0.0, 0.0, 0.0, 0.0, 0] -plane1 = [0.0, 0.0, 0.0, 0.0, 0] -plane2 = [0.0, 0.0, 0.0, 0.0, 0] -plane3 = [0.0, 0.0, 0.0, 0.0, 0] -plane4 = [0.0, 0.0, 0.0, 0.0, 0] -plane5 = [0.0, 0.0, 0.0, 0.0, 0] -plane6 = [0.0, 0.0, 0.0, 0.0, 0] -plane7 = [0.0, 0.0, 0.0, 0.0, 0] - -[WorkpieceConfiguration] -toolSpheres = [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] -toolDirectionInclination = 0.0 -toolDirectionAzimuth = 0.0 - - -## SafetyParameters ## -[Checksum] -safetyParameters = 3478627865 -majorVersion = 5 -minorVersion = 0 diff --git a/tests/test_dashboard_client.cpp b/tests/test_dashboard_client.cpp new file mode 100644 index 000000000..a20249133 --- /dev/null +++ b/tests/test_dashboard_client.cpp @@ -0,0 +1,174 @@ +// -- BEGIN LICENSE BLOCK ---------------------------------------------- +// Copyright 2022 Universal Robots A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the {copyright_holder} nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// -- END LICENSE BLOCK ------------------------------------------------ + +#include +#include +#include +#include +#define private public +#include + +using namespace urcl; + +// std::string ROBOT_IP = "192.168.56.101"; +std::string ROBOT_IP = "127.0.0.1"; + +class DashboardClientTest : public ::testing::Test +{ +protected: + void SetUp() + { + dashboard_client_.reset(new DashboardClient(ROBOT_IP)); + } + + void TearDown() + { + dashboard_client_.reset(); + } + + std::unique_ptr dashboard_client_; +}; + +TEST_F(DashboardClientTest, connect) +{ + EXPECT_TRUE(dashboard_client_->connect()); + dashboard_client_->commandCloseSafetyPopup(); +} + +TEST_F(DashboardClientTest, run_program) +{ + EXPECT_TRUE(dashboard_client_->connect()); + EXPECT_TRUE(dashboard_client_->commandLoadProgram("wait_program.urp")); + EXPECT_TRUE(dashboard_client_->commandPowerOff()); + dashboard_client_->commandClosePopup(); // Necessary for CB3 test + EXPECT_TRUE(dashboard_client_->commandPowerOn()); + EXPECT_TRUE(dashboard_client_->commandBrakeRelease()); + EXPECT_TRUE(dashboard_client_->commandPlay()); + EXPECT_TRUE(dashboard_client_->commandPause()); + EXPECT_TRUE(dashboard_client_->commandPlay()); + EXPECT_TRUE(dashboard_client_->commandStop()); + EXPECT_TRUE(dashboard_client_->commandPowerOff()); + dashboard_client_->commandClosePopup(); // Necessary for CB3 test +} + +TEST_F(DashboardClientTest, load_installation) +{ + EXPECT_TRUE(dashboard_client_->connect()); + EXPECT_TRUE(dashboard_client_->commandLoadInstallation("default.installation")); +} + +TEST_F(DashboardClientTest, not_connected) +{ + EXPECT_THROW(dashboard_client_->commandPowerOff(), UrException); +} + +TEST_F(DashboardClientTest, popup) +{ + EXPECT_TRUE(dashboard_client_->connect()); + EXPECT_TRUE(dashboard_client_->commandPopup("Test Popup")); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Give time for popup to pop up + EXPECT_TRUE(dashboard_client_->commandClosePopup()); +} + +TEST_F(DashboardClientTest, log_and_getters) +{ + std::string msg; + EXPECT_TRUE(dashboard_client_->connect()); + EXPECT_TRUE(dashboard_client_->commandAddToLog("Testing Log:")); + EXPECT_TRUE(dashboard_client_->commandPolyscopeVersion(msg)); + EXPECT_TRUE(dashboard_client_->commandAddToLog("Polyscope Version: " + msg)); + EXPECT_TRUE(dashboard_client_->commandRobotMode(msg)); + EXPECT_TRUE(dashboard_client_->commandAddToLog("Robot mode: " + msg)); + EXPECT_TRUE(dashboard_client_->commandGetLoadedProgram(msg)); + EXPECT_TRUE(dashboard_client_->commandAddToLog("Loaded program: " + msg)); + EXPECT_TRUE(dashboard_client_->commandProgramState(msg)); + EXPECT_TRUE(dashboard_client_->commandAddToLog("Program state: " + msg)); +} + +TEST_F(DashboardClientTest, flight_report_and_support_file) +{ + EXPECT_TRUE(dashboard_client_->connect()); + bool correct_polyscope_version = true; + try + { + correct_polyscope_version = + dashboard_client_->lessThanVersion("5.6.0", "3.13", dashboard_client_->polyscope_version_); + } + catch (const std::exception& e) + { + correct_polyscope_version = false; + } + + if (correct_polyscope_version) + { + EXPECT_TRUE(dashboard_client_->commandGenerateFlightReport("")); + EXPECT_TRUE(dashboard_client_->commandGenerateSupportFile(".")); + } + else + { + EXPECT_THROW(dashboard_client_->commandGenerateFlightReport(""), UrException); + EXPECT_THROW(dashboard_client_->commandGenerateSupportFile("."), UrException); + } +} + +// Only runs this test if robot is e-series +TEST_F(DashboardClientTest, e_series_version) +{ + std::string msg; + EXPECT_TRUE(dashboard_client_->connect()); + if (!dashboard_client_->e_series_) + GTEST_SKIP(); + dashboard_client_->polyscope_version_ = "5.0.0"; + EXPECT_THROW(dashboard_client_->commandSafetyStatus(msg), UrException); + dashboard_client_->polyscope_version_ = "5.5.0"; + EXPECT_TRUE(dashboard_client_->commandSafetyStatus(msg)); + EXPECT_THROW(dashboard_client_->commandSetUserRole("none"), UrException); +} + +// Only runs this test if robot is CB3 +TEST_F(DashboardClientTest, cb3_version) +{ + std::string msg; + EXPECT_TRUE(dashboard_client_->connect()); + if (dashboard_client_->e_series_) + GTEST_SKIP(); + dashboard_client_->polyscope_version_ = "1.6.0"; + EXPECT_THROW(dashboard_client_->commandIsProgramSaved(), UrException); + dashboard_client_->polyscope_version_ = "1.8.0"; + EXPECT_TRUE(dashboard_client_->commandIsProgramSaved()); + EXPECT_THROW(dashboard_client_->commandIsInRemoteControl(), UrException); +} + +int main(int argc, char* argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file