From eed58a76b948db6e47e5a1546394d70ef77d625b Mon Sep 17 00:00:00 2001 From: Tristan Schnell Date: Thu, 26 Sep 2019 14:52:43 +0200 Subject: [PATCH 01/15] added basic action client node for a trajectory following integration test --- ur_robot_driver/test/trajectory_test.py | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 ur_robot_driver/test/trajectory_test.py diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py new file mode 100755 index 000000000..ed90686df --- /dev/null +++ b/ur_robot_driver/test/trajectory_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +import sys +import time +import unittest + +import rospy +import actionlib +from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal +from trajectory_msgs.msg import JointTrajectoryPoint + +PKG = 'ur_rtde_driver' +NAME = 'trajectory_test' + + +class TrajectoryTest(unittest.TestCase): + def __init__(self, *args): + super(TrajectoryTest, self).__init__(*args) + rospy.init_node('trajectory_testing_client') + self.client = actionlib.SimpleActionClient( + '/scaled_pos_traj_controller/follow_joint_trajectory', FollowJointTrajectoryAction) + timeout = rospy.Duration(10) + try: + self.client.wait_for_server(timeout) + except rospy.exceptions.ROSException as err: + self.fail( + "Could not reach controller action. Make sure that the driver is actually running." + " Msg: {}".format(err)) + + # rospy.sleep(5) + + def test_trajectory(self): + """Test robot movement""" + goal = FollowJointTrajectoryGoal() + + goal.trajectory.joint_names = ["elbow_joint", "shoulder_lift_joint", "shoulder_pan_joint", + "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"] + position_list = [[0.0 for i in range(6)]] + position_list.append([-0.5 for i in range(6)]) + position_list.append([-1.0 for i in range(6)]) + duration_list = [6.0, 9.0, 12.0] + + for i, position in enumerate(position_list): + point = JointTrajectoryPoint() + point.positions = position + point.time_from_start = rospy.Duration(duration_list[i]) + goal.trajectory.points.append(point) + + self.client.send_goal(goal) + self.client.wait_for_result() + + self.assertEqual(self.client.get_result().error_code, 0) + + +if __name__ == '__main__': + import rostest + rostest.run(PKG, NAME, TrajectoryTest, sys.argv) From 0f4fe77044c19ef1c9f8ff66fc58d783c56bd0aa Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 26 Jun 2020 07:38:01 +0200 Subject: [PATCH 02/15] added basic action node for an IO integration test --- ur_robot_driver/test/io_test.py | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 ur_robot_driver/test/io_test.py diff --git a/ur_robot_driver/test/io_test.py b/ur_robot_driver/test/io_test.py new file mode 100755 index 000000000..0d6d86146 --- /dev/null +++ b/ur_robot_driver/test/io_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +import rospy +from ur_msgs.srv import SetIO +from ur_msgs.msg import IOStates + +def main(): + rospy.init_node('io_testing_client') + service_client = rospy.ServiceProxy('/ur_hardware_interface/set_io', SetIO) + service_client.wait_for_service() + + maximum_messages = 5 + pin = 0 + + service_client(1, pin, 0) + messages = 0 + pin_state = True + + while(pin_state): + if messages >= 5: + return False + io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) + pin_state = io_state.digital_out_states[pin].state + messages += 1 + + service_client(1, pin, 1) + messages = 0 + pin_state = False + + while(not pin_state): + if messages >= 5: + return False + io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) + pin_state = io_state.digital_out_states[pin].state + messages += 1 + + return True + +if __name__ == '__main__': + print(main()) From 5e26fc9de103d9284374b1c9fe17d9181e33b04b Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 26 Jun 2020 07:41:27 +0200 Subject: [PATCH 03/15] Moved everything to rostests Run docker ursim externally in GH action --- .github/dockerursim/.vol/default.installation | Bin 0 -> 2044 bytes .github/dockerursim/.vol/default.variables | 2 + .github/dockerursim/.vol/programs.UR5 | 1 + .github/dockerursim/Dockerfile | 121 ++++++++++++++++++ .../dockerursim/build_and_run_docker_ursim.sh | 17 +++ .github/dockerursim/safety.conf.UR5 | 101 +++++++++++++++ .github/dockerursim/ursim/run | 16 +++ .github/workflows/ci.yml | 9 ++ ur_robot_driver/CMakeLists.txt | 7 + ur_robot_driver/package.xml | 1 + ur_robot_driver/test/driver.test | 15 +++ ur_robot_driver/test/io_test.py | 80 ++++++++---- ur_robot_driver/test/switch_on_test.py | 48 +++++++ ur_robot_driver/test/trajectory_test.py | 2 +- 14 files changed, 393 insertions(+), 27 deletions(-) create mode 100644 .github/dockerursim/.vol/default.installation create mode 100644 .github/dockerursim/.vol/default.variables create mode 100755 .github/dockerursim/.vol/programs.UR5 create mode 100644 .github/dockerursim/Dockerfile create mode 100755 .github/dockerursim/build_and_run_docker_ursim.sh create mode 100644 .github/dockerursim/safety.conf.UR5 create mode 100644 .github/dockerursim/ursim/run create mode 100644 ur_robot_driver/test/driver.test create mode 100755 ur_robot_driver/test/switch_on_test.py diff --git a/.github/dockerursim/.vol/default.installation b/.github/dockerursim/.vol/default.installation new file mode 100644 index 0000000000000000000000000000000000000000..0f85fab8898d0fa7ca7af0fc5ff06085a077d2ef GIT binary patch literal 2044 zcmV!14fXAwbHz@JBs>D0`yo@mRmOk+?n}v%cT%q+? z-5@{4s=_JtX9dpFDL)^2FHA=`wZwU6KJq_WWaJ%tlS z0~DnK5I$r>T)6_=>?m=IU^3duH+<^a=lPr#G5QOpbRExhb5D5%)M|&rCFUy%v-(#8 zq;ee61!fkBaVQ2*Z!3TEQ)oxW@LZs9+c3H{)-)_8;4>1Ianxs~RC(oA@ZQhvZ`;ay zuV<(3S*_=1%l82SlVP324d*dF--Y_k zmEuBRPecB_9R0S1f9#YqChmNSesnh~Hhxq`nnM2{&AQ*$?|c20$^U!cF6f$_mAlzt zdGv@O>4JO=vaK(WkfmwX!(vG{{24LGqyqGXva#yG9SOyqp(^WSyCE-Q)fBhH_wJKm zjiJfbP20(66FBvZ)tSo`-BC>47{Kmt{!7>4lVSLXV!lLs>`lWQ1;ZN*$)5|7n%2#z z8T%iY26q_o1kP1i@WF{Hi831O(i~0^nB7B;lD3Pz8~Vf0Bf-x(35d^>`QD15Fy2rW zD6WsA73N)2#w3z;{p4kFG&&tvflX1WTUzlrMC9q+ckR~ zG(ek<-P27xv<$P~$49)QZXJR=Qq?f!q`!;FLPKoY>t09S2qbu5bOx4fcY0`JSe}V{ zw(gWMRUwSJuSUkGTaTsDBrPoD2Xo9h3AeF=5)LPJ(Esl}9`zyksheEE56{D5q2E9H!>8;l1RLgW$5me+>7eBQyjQkd6!a*bn>HY_D zrsFiUTxTr|dx?uZk)&57>hzpH1_av#F;8e(K(@jQG5uWQwd@PuF^uOlCIa(q2*U@S zsMtKukz1z`PWz1f6U0qzx!gI=rJD`7Eeo(g*(gc4|*p29ie1)u4w`@g&pQT3bFpw z3Ss2~flO;8s1TDLiQ(3T86f8rtfQ?!RHVkrWV*b%{&+iSN!EK`G41BRIr?62uF#5iZfG@H8`vyJzUbzSTSFRSikjzekbG#nJ#Mm#<>P$v|@i<&H8}=rA1VCiHSEI$2|q zv=h#Ji=1TMl3sGkEh`eFR9jHz^cjneTY9G86BQ$r1aiyM^fU1PLrKU}`0_T#_Sw=b z8$4q69LpID`$NlWD?gqFKu9Qo!ADE794vqfM8T8u94<#MlQAPoNbxg|{ z4*LC}{1LK0iFowO0`IAxq=f_k)iK-3>EE#fHTL=ogr|B~WJjhmVXbTZYFwL5ah-Ej z1KWHpu&u_xw#tERHUzezp9Xf`gWr+;e;?U@QuZ|>*v0jn6gH7%w8N0}3eoBYmF`ky zrnIaJjAHXq?tJk^r`$fB$ks6N{ggbB@ASdcG;|@|%eNR2td-`g|H7WccUe;lvh`X* z4LP<;11(|N9&&mO_Bzdz?})V3%{*tD29d8IP`DR)CLXKMmcIC(qk}J#c2Q`_-#6_M z@kN#K*!UxwkpvY{#wVuT;k zlB=r3u+Ch=QZZKLBYT;8KEBx@1{Bzn!FOefrDX+{(KaDkMEyhTLRzlHB6rj{WcMK) z6k=_Qp&w_!7W%|0ThD_&wG5J42-#7!G@}k(pOT-3Ri@Sf>bs{33E}TxtTOCr1v=VL`C!= zBqG~|W5q%w`f(m8xjm&YSgMeDh$H1IhSA9eQ>}clqEa9{^2%zgmgUxkgmaEtRBTVU zjdJ0vMcm~oU@q5 a$M`BaE{(rYxBT^{5B~!l=mv%DBme+ySOM1n literal 0 HcmV?d00001 diff --git a/.github/dockerursim/.vol/default.variables b/.github/dockerursim/.vol/default.variables new file mode 100644 index 000000000..86a46f302 --- /dev/null +++ b/.github/dockerursim/.vol/default.variables @@ -0,0 +1,2 @@ +# +#Thu May 28 16:20:13 BST 2020 diff --git a/.github/dockerursim/.vol/programs.UR5 b/.github/dockerursim/.vol/programs.UR5 new file mode 100755 index 000000000..ebd12373d --- /dev/null +++ b/.github/dockerursim/.vol/programs.UR5 @@ -0,0 +1 @@ +/ursim/programs.UR5 \ No newline at end of file diff --git a/.github/dockerursim/Dockerfile b/.github/dockerursim/Dockerfile new file mode 100644 index 000000000..9d4b2a54c --- /dev/null +++ b/.github/dockerursim/Dockerfile @@ -0,0 +1,121 @@ +# 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 BUILD_DATE="15/08/19" +ARG VERSION="5.8.0.10253" +LABEL build_version="URSim Version: ${VERSION} Build Date: ${BUILD_DATE}" +LABEL maintainer="Arran Hobson Sayers" +LABEL MAINTAINER="Arran Hobson Sayers" +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 + 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 && \ + #curl https://s3-eu-west-1.amazonaws.com/ur-support-site/54411/URSim_Linux-5.4.2.76197.tar.gz -o URSim-Linux.tar.gz && \ + # 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/.github/dockerursim/build_and_run_docker_ursim.sh b/.github/dockerursim/build_and_run_docker_ursim.sh new file mode 100755 index 000000000..981138736 --- /dev/null +++ b/.github/dockerursim/build_and_run_docker_ursim.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + + +docker build ${DIR} -t mydockerursim +docker volume create dockerursim +docker run --name="mydockerursim" -d \ + -e ROBOT_MODEL=UR5 \ + -p 8080:8080 \ + -p 29999:29999 \ + -p 30001-30004:30001-30004 \ + -v "${DIR}/.vol":/ursim/programs \ + -v dockursim:/ursim \ + --privileged \ + --cpus=1 \ + mydockerursim diff --git a/.github/dockerursim/safety.conf.UR5 b/.github/dockerursim/safety.conf.UR5 new file mode 100644 index 000000000..908b382f4 --- /dev/null +++ b/.github/dockerursim/safety.conf.UR5 @@ -0,0 +1,101 @@ +# 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/.github/dockerursim/ursim/run b/.github/dockerursim/ursim/run new file mode 100644 index 000000000..4021c48fc --- /dev/null +++ b/.github/dockerursim/ursim/run @@ -0,0 +1,16 @@ +#!/bin/execlineb -P + +s6-envdir -fn -- /var/run/s6/container_environment +importas -i ROBOT_MODEL ROBOT_MODEL + +# Redirect stderr to stdout. +fdmove -c 2 1 + +# Wait until openbox is running +if { s6-svwait -t 10000 -U /var/run/s6/services/openbox/ } + +# Set env +s6-env DISPLAY=:1 + +# Execute URSim +/ursim/start-ursim.sh ${ROBOT_MODEL} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16d86e9b7..66fe2a735 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,12 +21,21 @@ jobs: - ROS_DISTRO: kinetic UPSTREAM_WORKSPACE: .ci.rosinstall ROS_REPO: main + DOCKER_RUN_OPTS: --network bridge + BEFORE_INIT: 'apt-get update -qq && apt-get install -y iproute2 iputils-ping && ip addr && ping -c5 172.17.0.2' + IMMEDIATE_TEST_OUTPUT: true - ROS_DISTRO: melodic UPSTREAM_WORKSPACE: .ci.rosinstall ROS_REPO: main + DOCKER_RUN_OPTS: --network bridge + BEFORE_INIT: 'apt-get update -qq && apt-get install -y iproute2 iputils-ping && ip addr && ping -c5 172.17.0.2' + IMMEDIATE_TEST_OUTPUT: true steps: - uses: actions/checkout@v1 + - name: start ursim + run: | + .github/dockerursim/build_and_run_docker_ursim.sh - uses: 'ros-industrial/industrial_ci@master' env: ${{matrix.env}} diff --git a/ur_robot_driver/CMakeLists.txt b/ur_robot_driver/CMakeLists.txt index eb89d7cf5..b45f465df 100644 --- a/ur_robot_driver/CMakeLists.txt +++ b/ur_robot_driver/CMakeLists.txt @@ -141,6 +141,13 @@ add_executable(robot_state_helper target_link_libraries(robot_state_helper ${catkin_LIBRARIES} ur_robot_driver) add_dependencies(robot_state_helper ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) +if(CATKIN_ENABLE_TESTING) + find_package(rostest REQUIRED) + + add_rostest(test/driver.test) +endif() + + install(TARGETS ur_robot_driver ur_robot_driver_plugin ur_robot_driver_node robot_state_helper dashboard_client ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} diff --git a/ur_robot_driver/package.xml b/ur_robot_driver/package.xml index 78f19c4de..3c339071d 100644 --- a/ur_robot_driver/package.xml +++ b/ur_robot_driver/package.xml @@ -22,6 +22,7 @@ boost industrial_robot_status_interface + rostest actionlib control_msgs diff --git a/ur_robot_driver/test/driver.test b/ur_robot_driver/test/driver.test new file mode 100644 index 000000000..0c0244535 --- /dev/null +++ b/ur_robot_driver/test/driver.test @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/ur_robot_driver/test/io_test.py b/ur_robot_driver/test/io_test.py index 0d6d86146..94c999cb2 100755 --- a/ur_robot_driver/test/io_test.py +++ b/ur_robot_driver/test/io_test.py @@ -1,40 +1,68 @@ #!/usr/bin/env python +import sys +import time +import unittest + import rospy +import rostopic + + +PKG = 'ur_rtde_driver' +NAME = 'io_test' + + from ur_msgs.srv import SetIO from ur_msgs.msg import IOStates -def main(): - rospy.init_node('io_testing_client') - service_client = rospy.ServiceProxy('/ur_hardware_interface/set_io', SetIO) - service_client.wait_for_service() - maximum_messages = 5 - pin = 0 +class IOTest(unittest.TestCase): + def __init__(self, *args): + super(IOTest, self).__init__(*args) + rospy.init_node('io_test') + + timeout = 10 + + self.service_client = rospy.ServiceProxy('/ur_hardware_interface/set_io', SetIO) + try: + self.service_client.wait_for_service(timeout) + except rospy.exceptions.ROSException as err: + self.fail( + "Could not reach SetIO service. Make sure that the driver is actually running." + " Msg: {}".format(err)) + + def test_set_io(self): + """Test to set an IO and check whether it has been set.""" + + maximum_messages = 5 + pin = 0 + self.assertEqual(maximum_messages, 5) - service_client(1, pin, 0) - messages = 0 - pin_state = True + self.service_client(1, pin, 0) + messages = 0 + pin_state = True - while(pin_state): - if messages >= 5: - return False - io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) - pin_state = io_state.digital_out_states[pin].state - messages += 1 + while(pin_state): + if messages >= maximum_messages: + self.fail("Could not read desired state after {} messages.".format(maximum_messages)) + io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) + pin_state = io_state.digital_out_states[pin].state + messages += 1 + self.assertEqual(pin_state, 0) - service_client(1, pin, 1) - messages = 0 - pin_state = False + self.service_client(1, pin, 1) + messages = 0 + pin_state = False - while(not pin_state): - if messages >= 5: - return False - io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) - pin_state = io_state.digital_out_states[pin].state - messages += 1 + while(not pin_state): + if messages >= maximum_messages: + self.fail("Could not read desired state after {} messages.".format(maximum_messages)) + io_state = rospy.wait_for_message('/ur_hardware_interface/io_states', IOStates) + pin_state = io_state.digital_out_states[pin].state + messages += 1 + self.assertEqual(pin_state, 1) - return True if __name__ == '__main__': - print(main()) + import rostest + rostest.run(PKG, NAME, IOTest, sys.argv) diff --git a/ur_robot_driver/test/switch_on_test.py b/ur_robot_driver/test/switch_on_test.py new file mode 100755 index 000000000..56e50283b --- /dev/null +++ b/ur_robot_driver/test/switch_on_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +import sys +import time +import unittest + + +import rospy +import rostopic + + +PKG = 'ur_rtde_driver' +NAME = 'switch_on_test' + +import actionlib +from ur_dashboard_msgs.msg import SetModeAction, SetModeGoal, RobotMode + + +class IOTest(unittest.TestCase): + def __init__(self, *args): + super(IOTest, self).__init__(*args) + + rospy.init_node('switch_on_robot') + self.client = actionlib.SimpleActionClient( + '/ur_hardware_interface/set_mode', SetModeAction) + timeout = rospy.Duration(30) + try: + self.client.wait_for_server(timeout) + except rospy.exceptions.ROSException as err: + self.fail( + "Could not reach set_mode action. Make sure that the driver is actually running." + " Msg: {}".format(err)) + + def test_switch_on(self): + """Test to set an IO and check whether it has been set.""" + + goal = SetModeGoal() + goal.target_robot_mode = RobotMode.RUNNING + goal.play_program = False # we use headless mode during tests + + self.client.send_goal(goal) + self.client.wait_for_result() + + self.assertTrue(self.client.get_result().success) + + +if __name__ == '__main__': + import rostest + rostest.run(PKG, NAME, IOTest, sys.argv) diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index ed90686df..ce800861b 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -18,7 +18,7 @@ def __init__(self, *args): rospy.init_node('trajectory_testing_client') self.client = actionlib.SimpleActionClient( '/scaled_pos_traj_controller/follow_joint_trajectory', FollowJointTrajectoryAction) - timeout = rospy.Duration(10) + timeout = rospy.Duration(30) try: self.client.wait_for_server(timeout) except rospy.exceptions.ROSException as err: From a25c32da918f8d9a4eff9ebc4b9a144369791704 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 24 Jun 2020 09:01:41 +0200 Subject: [PATCH 04/15] Add a failing test I want to see whether the tests actually fail --- ur_robot_driver/test/trajectory_test.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index ce800861b..6b1568f10 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -50,6 +50,27 @@ def test_trajectory(self): self.assertEqual(self.client.get_result().error_code, 0) + def test_illegal_trajectory(self): + """Test robot movement""" + goal = FollowJointTrajectoryGoal() + + goal.trajectory.joint_names = ["elbow_joint", "shoulder_lift_joint", "shoulder_pan_joint", + "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"] + position_list = [[0.0 for i in range(6)]] + position_list.append([-0.5 for i in range(6)]) + duration_list = [6.0, 3.0] + + for i, position in enumerate(position_list): + point = JointTrajectoryPoint() + point.positions = position + point.time_from_start = rospy.Duration(duration_list[i]) + goal.trajectory.points.append(point) + + self.client.send_goal(goal) + self.client.wait_for_result() + + self.assertEqual(self.client.get_result().error_code, 0) + if __name__ == '__main__': import rostest From adbd2347332cb0063f5b4da84b220db64655db0d Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 24 Jun 2020 09:24:32 +0200 Subject: [PATCH 05/15] Added test for explicitly scaled trajectory execution --- ur_robot_driver/test/trajectory_test.py | 52 +++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index 6b1568f10..59da227f8 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -5,7 +5,7 @@ import rospy import actionlib -from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal +from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal, FollowJointTrajectoryResult from trajectory_msgs.msg import JointTrajectoryPoint PKG = 'ur_rtde_driver' @@ -45,19 +45,25 @@ def test_trajectory(self): point.time_from_start = rospy.Duration(duration_list[i]) goal.trajectory.points.append(point) + rospy.loginfo("Sending simple goal") + self.client.send_goal(goal) self.client.wait_for_result() - self.assertEqual(self.client.get_result().error_code, 0) + self.assertEqual(self.client.get_result().error_code, + FollowJointTrajectoryResult.SUCCESSFUL) + rospy.loginfo("Received result SUCCESSFUL") def test_illegal_trajectory(self): - """Test robot movement""" + """Test trajectory server. This is more of a validation test that the testing suite does the + right thing.""" goal = FollowJointTrajectoryGoal() goal.trajectory.joint_names = ["elbow_joint", "shoulder_lift_joint", "shoulder_pan_joint", "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"] position_list = [[0.0 for i in range(6)]] position_list.append([-0.5 for i in range(6)]) + # Create illegal goal by making the second point come earlier than the first duration_list = [6.0, 3.0] for i, position in enumerate(position_list): @@ -66,10 +72,48 @@ def test_illegal_trajectory(self): point.time_from_start = rospy.Duration(duration_list[i]) goal.trajectory.points.append(point) + rospy.loginfo("Sending illegal goal") + self.client.send_goal(goal) + self.client.wait_for_result() + + # As timings are illegal, we expect the result to be INVALID_GOAL + self.assertEqual(self.client.get_result().error_code, + FollowJointTrajectoryResult.INVALID_GOAL) + rospy.loginfo("Received result INVALID_GOAL") + + def test_scaled_trajectory(self): + """Test robot movement""" + goal = FollowJointTrajectoryGoal() + + goal.trajectory.joint_names = ["elbow_joint", "shoulder_lift_joint", "shoulder_pan_joint", + "wrist_1_joint", "wrist_2_joint", "wrist_3_joint"] + position_list = [[0.0 for i in range(6)]] + position_list.append([-1.0 for i in range(6)]) + duration_list = [6.0, 6.5] + + for i, position in enumerate(position_list): + point = JointTrajectoryPoint() + point.positions = position + point.time_from_start = rospy.Duration(duration_list[i]) + goal.trajectory.points.append(point) + + rospy.loginfo("Sending scaled goal without time restrictions") + self.client.send_goal(goal) + self.client.wait_for_result() + + self.assertEqual(self.client.get_result().error_code, + FollowJointTrajectoryResult.SUCCESSFUL) + rospy.loginfo("Received result SUCCESSFUL") + + # Now do the same again, but with a goal time constraint + rospy.loginfo("Sending scaled goal with time restrictions") + goal.goal_time_tolerance = rospy.Duration(0.01) self.client.send_goal(goal) self.client.wait_for_result() - self.assertEqual(self.client.get_result().error_code, 0) + self.assertEqual(self.client.get_result().error_code, + FollowJointTrajectoryResult.GOAL_TOLERANCE_VIOLATED) + rospy.loginfo("Received result GOAL_TOLERANCE_VIOLATED") if __name__ == '__main__': From bebc46e232a6d21419f3b736a11a6f0ef12f0720 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Wed, 24 Jun 2020 10:01:29 +0200 Subject: [PATCH 06/15] Use enum identifier instead of hard coded value --- ur_robot_driver/test/io_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ur_robot_driver/test/io_test.py b/ur_robot_driver/test/io_test.py index 94c999cb2..836b826c0 100755 --- a/ur_robot_driver/test/io_test.py +++ b/ur_robot_driver/test/io_test.py @@ -12,7 +12,7 @@ NAME = 'io_test' -from ur_msgs.srv import SetIO +from ur_msgs.srv import SetIO, SetIORequest from ur_msgs.msg import IOStates @@ -50,7 +50,7 @@ def test_set_io(self): messages += 1 self.assertEqual(pin_state, 0) - self.service_client(1, pin, 1) + self.service_client(SetIORequest.FUN_SET_DIGITAL_OUT, pin, 1) messages = 0 pin_state = False From 5a206cdc03ee0570c382f2261f428383ea800332 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Thu, 25 Jun 2020 09:44:29 +0200 Subject: [PATCH 07/15] specifically initialize robot before trajectory test --- ur_robot_driver/test/trajectory_test.py | 28 ++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index 59da227f8..12c829afa 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -6,6 +6,8 @@ import rospy import actionlib from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal, FollowJointTrajectoryResult +from ur_dashboard_msgs.msg import SetModeAction, SetModeGoal, RobotMode +from std_srvs.srv import Trigger, TriggerRequest from trajectory_msgs.msg import JointTrajectoryPoint PKG = 'ur_rtde_driver' @@ -26,7 +28,31 @@ def __init__(self, *args): "Could not reach controller action. Make sure that the driver is actually running." " Msg: {}".format(err)) - # rospy.sleep(5) + self.init_robot() + + def init_robot(self): + """Make sure the robot is booted and ready to receive commands""" + mode_client = actionlib.SimpleActionClient( + '/ur_hardware_interface/set_mode', SetModeAction) + timeout = rospy.Duration(30) + try: + mode_client.wait_for_server(timeout) + except rospy.exceptions.ROSException as err: + self.fail( + "Could not reach set_mode action. Make sure that the driver is actually running." + " Msg: {}".format(err)) + goal = SetModeGoal() + goal.target_robot_mode = RobotMode.RUNNING + goal.play_program = False # we use headless mode during tests + + mode_client.send_goal(goal) + mode_client.wait_for_result() + + self.assertTrue(mode_client.get_result().success) + + send_program_srv = rospy.ServiceProxy("/ur_hardware_interface/resend_robot_program", Trigger) + send_program_srv.call() + rospy.sleep(5) def test_trajectory(self): """Test robot movement""" From 0a018c52a53edf30a84a5a33d03e3ffa9b1c806b Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 26 Jun 2020 09:22:28 +0200 Subject: [PATCH 08/15] replaced legacy package name --- ur_robot_driver/test/io_test.py | 2 +- ur_robot_driver/test/switch_on_test.py | 2 +- ur_robot_driver/test/trajectory_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ur_robot_driver/test/io_test.py b/ur_robot_driver/test/io_test.py index 836b826c0..4c705374b 100755 --- a/ur_robot_driver/test/io_test.py +++ b/ur_robot_driver/test/io_test.py @@ -8,7 +8,7 @@ import rostopic -PKG = 'ur_rtde_driver' +PKG = 'ur_robot_driver' NAME = 'io_test' diff --git a/ur_robot_driver/test/switch_on_test.py b/ur_robot_driver/test/switch_on_test.py index 56e50283b..07b07b300 100755 --- a/ur_robot_driver/test/switch_on_test.py +++ b/ur_robot_driver/test/switch_on_test.py @@ -8,7 +8,7 @@ import rostopic -PKG = 'ur_rtde_driver' +PKG = 'ur_robot_driver' NAME = 'switch_on_test' import actionlib diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index 12c829afa..761a9392a 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -10,7 +10,7 @@ from std_srvs.srv import Trigger, TriggerRequest from trajectory_msgs.msg import JointTrajectoryPoint -PKG = 'ur_rtde_driver' +PKG = 'ur_robot_driver' NAME = 'trajectory_test' From e70e7d18d2c14ec4cd545d3e61afc461953b8e1b Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 26 Jun 2020 09:47:45 +0200 Subject: [PATCH 09/15] Use a test_depend for rostest --- ur_robot_driver/package.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ur_robot_driver/package.xml b/ur_robot_driver/package.xml index 3c339071d..431b72c71 100644 --- a/ur_robot_driver/package.xml +++ b/ur_robot_driver/package.xml @@ -22,7 +22,6 @@ boost industrial_robot_status_interface - rostest actionlib control_msgs @@ -52,6 +51,8 @@ ur_e_description velocity_controllers + rostest + From 02ceb925fa3149f5a43393bc5ff926e522b4d218 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 26 Jun 2020 09:54:50 +0200 Subject: [PATCH 10/15] Use a remap for the controller topic --- ur_robot_driver/test/driver.test | 3 +++ ur_robot_driver/test/trajectory_test.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ur_robot_driver/test/driver.test b/ur_robot_driver/test/driver.test index 0c0244535..0c0bc865e 100644 --- a/ur_robot_driver/test/driver.test +++ b/ur_robot_driver/test/driver.test @@ -9,6 +9,9 @@ + + + diff --git a/ur_robot_driver/test/trajectory_test.py b/ur_robot_driver/test/trajectory_test.py index 761a9392a..346b8db19 100755 --- a/ur_robot_driver/test/trajectory_test.py +++ b/ur_robot_driver/test/trajectory_test.py @@ -19,7 +19,7 @@ def __init__(self, *args): super(TrajectoryTest, self).__init__(*args) rospy.init_node('trajectory_testing_client') self.client = actionlib.SimpleActionClient( - '/scaled_pos_traj_controller/follow_joint_trajectory', FollowJointTrajectoryAction) + 'follow_joint_trajectory', FollowJointTrajectoryAction) timeout = rospy.Duration(30) try: self.client.wait_for_server(timeout) From aa9ecc2d414981aa1a041e94d19c151256e29888 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Thu, 9 Jul 2020 13:32:14 +0200 Subject: [PATCH 11/15] Added a running member to actually join the RTDEWriter thread --- .../include/ur_robot_driver/rtde/rtde_writer.h | 12 +++++++++++- ur_robot_driver/src/rtde/rtde_writer.cpp | 16 ++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ur_robot_driver/include/ur_robot_driver/rtde/rtde_writer.h b/ur_robot_driver/include/ur_robot_driver/rtde/rtde_writer.h index 6fc186ba9..93881c61f 100644 --- a/ur_robot_driver/include/ur_robot_driver/rtde/rtde_writer.h +++ b/ur_robot_driver/include/ur_robot_driver/rtde/rtde_writer.h @@ -55,7 +55,16 @@ class RTDEWriter * \param recipe The recipe to use for communication */ RTDEWriter(comm::URStream* stream, const std::vector& recipe); - ~RTDEWriter() = default; + + ~RTDEWriter() + { + running_ = false; + std::this_thread::sleep_for(std::chrono::seconds(5)); + if (writer_thread_.joinable()) + { + writer_thread_.join(); + } + } /*! * \brief Starts the writer thread, which periodically clears the queue to write packages to the * robot. @@ -120,6 +129,7 @@ class RTDEWriter uint8_t recipe_id_; moodycamel::BlockingReaderWriterQueue> queue_; std::thread writer_thread_; + bool running_; }; } // namespace rtde_interface diff --git a/ur_robot_driver/src/rtde/rtde_writer.cpp b/ur_robot_driver/src/rtde/rtde_writer.cpp index e4f15e5c5..0e8f33f69 100644 --- a/ur_robot_driver/src/rtde/rtde_writer.cpp +++ b/ur_robot_driver/src/rtde/rtde_writer.cpp @@ -32,13 +32,14 @@ namespace ur_driver namespace rtde_interface { RTDEWriter::RTDEWriter(comm::URStream* stream, const std::vector& recipe) - : stream_(stream), recipe_(recipe), queue_{ 32 } + : stream_(stream), recipe_(recipe), queue_{ 32 }, running_(false) { } void RTDEWriter::init(uint8_t recipe_id) { recipe_id_ = recipe_id; + running_ = true; writer_thread_ = std::thread(&RTDEWriter::run, this); } @@ -48,13 +49,16 @@ void RTDEWriter::run() size_t size; size_t written; std::unique_ptr package; - while (true) + while (running_) { - queue_.waitDequeue(package); - package->setRecipeID(recipe_id_); - size = package->serializePackage(buffer); - stream_->write(buffer, size, written); + if (queue_.waitDequeTimed(package, 1000000)) + { + package->setRecipeID(recipe_id_); + size = package->serializePackage(buffer); + stream_->write(buffer, size, written); + } } + LOG_DEBUG("Write thread ended."); } bool RTDEWriter::sendSpeedSlider(double speed_slider_fraction) From 1ce9215da0d1720823ad2c91373ac65361147190 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Thu, 9 Jul 2020 14:35:01 +0200 Subject: [PATCH 12/15] Throw an exception when the recipe file cannot be read --- ur_robot_driver/src/rtde/rtde_client.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ur_robot_driver/src/rtde/rtde_client.cpp b/ur_robot_driver/src/rtde/rtde_client.cpp index 60a7a52e9..decb22b99 100644 --- a/ur_robot_driver/src/rtde/rtde_client.cpp +++ b/ur_robot_driver/src/rtde/rtde_client.cpp @@ -323,6 +323,13 @@ std::vector RTDEClient::readRecipe(const std::string& recipe_file) { std::vector recipe; std::ifstream file(recipe_file); + if (file.fail()) + { + std::stringstream msg; + msg << "Opening file '" << recipe_file << "' failed with error: " << strerror(errno); + LOG_ERROR("%s", msg.str().c_str()); + throw UrException(msg.str()); + } std::string line; while (std::getline(file, line)) { From cafe282287561daa731f5906881d57db8b824332 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Thu, 9 Jul 2020 14:37:42 +0200 Subject: [PATCH 13/15] Add a gtest for RTDE client only --- ur_robot_driver/CMakeLists.txt | 5 ++ ur_robot_driver/test/resources/empty.txt | 1 + .../test/resources/rtde_input_recipe.txt | 12 +++ .../test/resources/rtde_output_recipe.txt | 27 +++++++ ur_robot_driver/test/test_rtde_client.cpp | 76 +++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 ur_robot_driver/test/resources/empty.txt create mode 100644 ur_robot_driver/test/resources/rtde_input_recipe.txt create mode 100644 ur_robot_driver/test/resources/rtde_output_recipe.txt create mode 100644 ur_robot_driver/test/test_rtde_client.cpp diff --git a/ur_robot_driver/CMakeLists.txt b/ur_robot_driver/CMakeLists.txt index b45f465df..642e10c24 100644 --- a/ur_robot_driver/CMakeLists.txt +++ b/ur_robot_driver/CMakeLists.txt @@ -145,6 +145,11 @@ if(CATKIN_ENABLE_TESTING) find_package(rostest REQUIRED) add_rostest(test/driver.test) + + catkin_add_gtest(test_rtde_client + test/test_rtde_client.cpp + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) + target_link_libraries(test_rtde_client ${catkin_LIBRARIES} ur_robot_driver) endif() diff --git a/ur_robot_driver/test/resources/empty.txt b/ur_robot_driver/test/resources/empty.txt new file mode 100644 index 000000000..8d1c8b69c --- /dev/null +++ b/ur_robot_driver/test/resources/empty.txt @@ -0,0 +1 @@ + diff --git a/ur_robot_driver/test/resources/rtde_input_recipe.txt b/ur_robot_driver/test/resources/rtde_input_recipe.txt new file mode 100644 index 000000000..c00dc059d --- /dev/null +++ b/ur_robot_driver/test/resources/rtde_input_recipe.txt @@ -0,0 +1,12 @@ +speed_slider_mask +speed_slider_fraction +standard_digital_output_mask +standard_digital_output +configurable_digital_output_mask +configurable_digital_output +tool_digital_output_mask +tool_digital_output +standard_analog_output_mask +standard_analog_output_type +standard_analog_output_0 +standard_analog_output_1 diff --git a/ur_robot_driver/test/resources/rtde_output_recipe.txt b/ur_robot_driver/test/resources/rtde_output_recipe.txt new file mode 100644 index 000000000..8d10be849 --- /dev/null +++ b/ur_robot_driver/test/resources/rtde_output_recipe.txt @@ -0,0 +1,27 @@ +timestamp +actual_q +actual_qd +speed_scaling +target_speed_fraction +runtime_state +actual_TCP_force +actual_TCP_pose +actual_digital_input_bits +actual_digital_output_bits +standard_analog_input0 +standard_analog_input1 +standard_analog_output0 +standard_analog_output1 +analog_io_types +tool_mode +tool_analog_input_types +tool_analog_input0 +tool_analog_input1 +tool_output_voltage +tool_output_current +tool_temperature +robot_mode +safety_mode +robot_status_bits +safety_status_bits +actual_current diff --git a/ur_robot_driver/test/test_rtde_client.cpp b/ur_robot_driver/test/test_rtde_client.cpp new file mode 100644 index 000000000..686e5a072 --- /dev/null +++ b/ur_robot_driver/test/test_rtde_client.cpp @@ -0,0 +1,76 @@ +// this is for emacs file handling -*- mode: c++; indent-tabs-mode: nil -*- + +// -- BEGIN LICENSE BLOCK ---------------------------------------------- +// -- END LICENSE BLOCK ------------------------------------------------ + +//---------------------------------------------------------------------- +/*!\file + * + * \author Felix Exner mauch@fzi.de + * \date 2020-07-09 + * + */ +//---------------------------------------------------------------------- + +#include + +#include + +using namespace ur_driver; + +const std::string ROBOT_IP = "172.17.0.2"; + +TEST(UrRobotDriver, rtde_handshake) +{ + comm::INotifier notifier; + std::string output_recipe = "resources/rtde_output_recipe.txt"; + std::string input_recipe = "resources/rtde_input_recipe.txt"; + rtde_interface::RTDEClient client(ROBOT_IP, notifier, output_recipe, input_recipe); + + EXPECT_TRUE(client.init()); +} + +TEST(UrRobotDriver, rtde_handshake_wrong_ip) +{ + comm::INotifier notifier; + std::string output_recipe = "resources/rtde_output_recipe.txt"; + std::string input_recipe = "resources/rtde_input_recipe.txt"; + rtde_interface::RTDEClient client("1.2.3.4", notifier, output_recipe, input_recipe); + + EXPECT_THROW(client.init(), UrException); +} + +TEST(UrRobotDriver, rtde_handshake_illegal_ip) +{ + comm::INotifier notifier; + std::string output_recipe = "resources/rtde_output_recipe.txt"; + std::string input_recipe = "resources/rtde_input_recipe.txt"; + rtde_interface::RTDEClient client("abcd", notifier, output_recipe, input_recipe); + + EXPECT_THROW(client.init(), UrException); +} + +TEST(UrRobotDriver, no_recipe) +{ + comm::INotifier notifier; + std::string output_recipe = ""; + std::string input_recipe = ""; + EXPECT_THROW(rtde_interface::RTDEClient client(ROBOT_IP, notifier, output_recipe, input_recipe), UrException); +} + +TEST(UrRobotDriver, empty_recipe) +{ + comm::INotifier notifier; + std::string output_recipe = "resources/empty.txt"; + std::string input_recipe = "resources/empty.txt"; + rtde_interface::RTDEClient client(ROBOT_IP, notifier, output_recipe, input_recipe); + + EXPECT_THROW(client.init(), UrException); +} + +int main(int argc, char* argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} From 95440b5d0d2bc6838d8d422ee2cf437a30d25685 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Thu, 9 Jul 2020 15:07:28 +0200 Subject: [PATCH 14/15] Install resources directory --- ur_robot_driver/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ur_robot_driver/CMakeLists.txt b/ur_robot_driver/CMakeLists.txt index 642e10c24..48f69295f 100644 --- a/ur_robot_driver/CMakeLists.txt +++ b/ur_robot_driver/CMakeLists.txt @@ -162,7 +162,7 @@ install(TARGETS ur_robot_driver ur_robot_driver_plugin ur_robot_driver_node robo install(PROGRAMS scripts/tool_communication DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) -install(DIRECTORY config launch +install(DIRECTORY config launch resources DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}) From f6196afaea38e64cdd2467087f646bec08905171 Mon Sep 17 00:00:00 2001 From: Felix Exner Date: Fri, 10 Jul 2020 14:12:00 +0200 Subject: [PATCH 15/15] Tests: Update the name of the trajectory controller --- ur_robot_driver/test/driver.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ur_robot_driver/test/driver.test b/ur_robot_driver/test/driver.test index 0c0bc865e..2e9096abb 100644 --- a/ur_robot_driver/test/driver.test +++ b/ur_robot_driver/test/driver.test @@ -10,7 +10,7 @@ - +