diff --git a/.github/workflows/manual-dispatch.yml b/.github/workflows/manual-dispatch.yml index 3a28285..fb3eb40 100644 --- a/.github/workflows/manual-dispatch.yml +++ b/.github/workflows/manual-dispatch.yml @@ -21,7 +21,7 @@ jobs: run: | IMAGE_NAME=network-interfaces:latest docker build . \ - --build-arg CL_BRANCH=${{ inputs.cl_branch }} \ + --build-arg CONTROL_LIBRARIES_BRANCH=${{ inputs.cl_branch }} \ --tag ${IMAGE_NAME} shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 9342b07..5b44e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,17 @@ Release Versions: +- [1.1.0](#110) - [1.0.0](#100) - [0.2.0](#020) - [0.1.0](#010) +## 1.1.0 + +Version 1.1.0 sets control libraries version 6.0.0 as default and fixes the GitHub workflow. Additionally, the C++ +library now has its proper CMake configuration and can be included and linked with `find_package(network_interfaces)` +and`target_link_libraries(${MY_TARGET} ${network_interfaces_LIBRARIES}`. + ## 1.0.0 Version 1.0.0 accounts for breaking changes in control libraries and defines 5.0.0 as the required control libraries diff --git a/Dockerfile b/Dockerfile index 36fc91e..927bcd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ WORKDIR /tmp/cppzmq-${CPPZMQ_VERSION} RUN mkdir build && cd build && cmake .. -DCPPZMQ_BUILD_TESTS=OFF && make -j install WORKDIR /tmp -ARG CONTROL_LIBRARIES_BRANCH=v5.0.0 +ARG CONTROL_LIBRARIES_BRANCH=v6.0.0 RUN git clone -b ${CONTROL_LIBRARIES_BRANCH} --depth 1 https://github.com/epfl-lasa/control-libraries.git RUN cd control-libraries/source && ./install.sh --auto --no-controllers --no-dynamical-systems --no-robot-model RUN cd control-libraries/protocol && ./install.sh --auto diff --git a/build-test.sh b/build-test.sh index df11d29..2f3f336 100755 --- a/build-test.sh +++ b/build-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -CONTROL_LIBRARIES_BRANCH=v5.0.0 +CONTROL_LIBRARIES_BRANCH=v6.0.0 IMAGE_NAME=aica-technology/network-interfaces IMAGE_STAGE=build-test diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c4db9cc..5b37507 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.9) -project(network_interfaces VERSION 1.0.0) +cmake_minimum_required(VERSION 3.15) +project(network_interfaces VERSION 1.1.0) option(BUILD_TESTING "Build tests." OFF) @@ -23,17 +23,21 @@ else() find_package(GTest QUIET) endif() -find_library(clproto REQUIRED) -find_library(state_representation REQUIRED) +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) -find_package(cppzmq REQUIRED) +find_package(control_libraries 6.0.0 REQUIRED COMPONENTS state_representation) +find_package(clproto 6.0.0 REQUIRED) +find_package(cppzmq 4.7.1 REQUIRED) -include_directories( - include -) +include_directories(include) + +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories(${PROJECT_NAME} INTERFACE include) +target_link_libraries(${PROJECT_NAME} INTERFACE clproto cppzmq state_representation) install(DIRECTORY include/ - DESTINATION include + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) add_executable(zmq_loopback_state scripts/zmq_loopback_state.cpp) @@ -47,3 +51,23 @@ if(BUILD_TESTING) target_link_libraries(test_zmq_communication ${GTEST_LIBRARIES} pthread clproto cppzmq state_representation) add_test(NAME test_zmq_communication COMMAND test_zmq_communication) endif() + +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY SameMajorVersion +) + +# create config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +# install config files +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) diff --git a/cpp/Config.cmake.in b/cpp/Config.cmake.in new file mode 100644 index 0000000..3ed6789 --- /dev/null +++ b/cpp/Config.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +set(network_interfaces_LIBRARIES cppzmq clproto state_representation) + +include(CMakeFindDependencyMacro) +find_dependency(cppzmq) \ No newline at end of file diff --git a/dev-server.sh b/dev-server.sh index 8da326d..3ea97b6 100755 --- a/dev-server.sh +++ b/dev-server.sh @@ -1,5 +1,5 @@ #!/bin/bash -CONTROL_LIBRARIES_BRANCH=v5.0.0 +CONTROL_LIBRARIES_BRANCH=v6.0.0 REMOTE_SSH_PORT=4420 IMAGE_NAME=aica-technology/network-interfaces diff --git a/python/network_interfaces/zmq/network.py b/python/network_interfaces/zmq/network.py index 31bf7cf..112d6cc 100644 --- a/python/network_interfaces/zmq/network.py +++ b/python/network_interfaces/zmq/network.py @@ -16,10 +16,10 @@ class StateMessage: ee_state: sr.CartesianState joint_state: sr.JointState jacobian: sr.Jacobian - mass: sr.Parameter("mass", sr.StateType.PARAMETER_MATRIX) + mass: sr.Parameter("mass", sr.ParameterType.MATRIX) def __init__(self, ee_state=sr.CartesianState(), joint_state=sr.JointState(), jacobian=sr.Jacobian(), - mass=sr.Parameter("mass", sr.StateType.PARAMETER_MATRIX)): + mass=sr.Parameter("mass", sr.ParameterType.MATRIX)): assert isinstance(ee_state, sr.CartesianState) assert isinstance(joint_state, sr.JointState) assert isinstance(jacobian, sr.Jacobian) @@ -84,7 +84,7 @@ def encode_command(command): if command.control_type[i] >= ControlType.END.value: raise ValueError("The desired 'control_type' of the CommandMessage is unknown.") encoded_command = list() - control_type_param = sr.Parameter("control_type", command.control_type, sr.StateType.PARAMETER_INT_ARRAY) + control_type_param = sr.Parameter("control_type", command.control_type, sr.ParameterType.INT_ARRAY) encoded_command.append(clproto.encode(control_type_param, clproto.MessageType.PARAMETER_MESSAGE)) encoded_command.append(clproto.encode(command.joint_state, clproto.MessageType.JOINT_STATE_MESSAGE)) return encoded_command diff --git a/python/setup.py b/python/setup.py index 66dae6b..c354834 100644 --- a/python/setup.py +++ b/python/setup.py @@ -2,14 +2,15 @@ setuptools.setup( name="network_interfaces", - version="1.0.0", + version="1.1.0", description="This package implements network interfaces of AICA", maintainer="Dominic Reber", maintainer_email="dominic@aica.tech", url="https://github.com/aica-technology/network-interfaces", packages=setuptools.find_packages(), install_requires=[ - "control-libraries>=5.0.0", + "control-libraries>=6.0.0", + "pyzmq>=22.3" ], classifiers=[ "Programming Language :: Python :: 3", diff --git a/python/test/test_zmq_communication.py b/python/test/test_zmq_communication.py index 5d4011c..6f3ea08 100644 --- a/python/test/test_zmq_communication.py +++ b/python/test/test_zmq_communication.py @@ -1,13 +1,11 @@ -import threading -import time -import unittest - +import concurrent.futures import numpy as np import state_representation as sr +import time +import unittest import zmq -from numpy.testing import assert_array_almost_equal - from network_interfaces.zmq import network +from numpy.testing import assert_array_almost_equal class TestZMQNetworkInterface(unittest.TestCase): @@ -26,7 +24,7 @@ def setUpClass(cls): cls.robot_state = sr.CartesianState().Random("ee", "robot") cls.robot_joint_state = sr.JointState().Random("robot", 3) cls.robot_jacobian = sr.Jacobian().Random("robot", 3, "frame") - cls.robot_mass = sr.Parameter("mass", np.random.rand(3, 3), sr.StateType.PARAMETER_MATRIX) + cls.robot_mass = sr.Parameter("mass", np.random.rand(3, 3), sr.ParameterType.MATRIX) cls.control_command = sr.JointState().Random("robot", 3) cls.control_type = [1, 2, 3] cls.context = zmq.Context() @@ -41,7 +39,8 @@ def robot(self): state = network.StateMessage(self.robot_state, self.robot_joint_state, self.robot_jacobian, self.robot_mass) command = [] - for i in range(200): + start_time = time.time() + while time.time() - start_time < 2.: network.send_state(state, state_publisher) command = network.receive_command(command_subscriber) time.sleep(0.01) @@ -55,7 +54,8 @@ def control(self): command = network.CommandMessage(self.control_type, self.control_command) state = [] - for i in range(200): + start_time = time.time() + while time.time() - start_time < 2.: network.send_command(command, command_publisher) state = network.receive_state(state_subscriber) time.sleep(0.01) @@ -64,12 +64,9 @@ def control(self): self.received_state = state def test_communication(self): - robot = threading.Thread(target=self.robot) - control = threading.Thread(target=self.control) - robot.start() - control.start() - robot.join() - control.join() + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as e: + e.submit(self.robot) + e.submit(self.control) [self.assertEqual(self.received_command.control_type[i], self.control_type[i]) for i in range(len(self.control_type))]