Skip to content

Commit

Permalink
Merge pull request #10 from aica-technology/release/v0.2
Browse files Browse the repository at this point in the history
Release Version 0.2.0
  • Loading branch information
domire8 authored Jan 31, 2022
2 parents 2a76b53 + 3b2f9a0 commit 5d0345b
Show file tree
Hide file tree
Showing 14 changed files with 336 additions and 41 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/manual-dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Manual Build and Push

# Run workflow by manual dispatch
on:
workflow_dispatch:
inputs:
cl_branch:
description: 'If set, the desired branch of control libraries'
required: false
default: 'develop'

jobs:
build-publish:
runs-on: ubuntu-latest
name: Build and publish image
steps:
- name: Checkout Repository
uses: actions/checkout@v2

- name: Build image
run: |
IMAGE_NAME=network-interfaces:latest
docker build . \
--build-arg CL_BRANCH=${{ inputs.cl_branch }} \
--tag ${IMAGE_NAME}
shell: bash

- name: Login to GitHub Container Registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
shell: bash

- name: Push image
run: |
IMAGE_NAME=network-interfaces:latest
docker tag ${IMAGE_NAME} ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
docker push ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
shell: bash
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

Release Versions:

- [0.2.0](#020)
- [0.1.0](#010)

## 0.2.0

Version 0.2.0 introduces a slightly different Dockerfile and contains a GitHub workflow to build and push an image with
the pre-built network interface libraries. Additionally, two loopback scripts have been added to simplify communication
testing.

## 0.1.0

A set of drivers, protocols and libraries for communicating between software and hardware devices.
Expand Down
39 changes: 21 additions & 18 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
ARG ROS_VERSION=foxy
FROM ghcr.io/aica-technology/ros2-control-libraries:${ROS_VERSION} AS core-dependencies
FROM ghcr.io/epfl-lasa/control-libraries/development-dependencies as source-dependencies

RUN sudo apt-get update && sudo apt-get install -y \
libmbedtls-dev \
libsodium-dev \
libzmq3-dev \
&& sudo rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y libmbedtls-dev libsodium-dev libzmq3-dev \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# install cppzmq bindings
ARG CPPZMQ_VERSION=4.7.1
WORKDIR /tmp
ARG CPPZMQ_VERSION=4.7.1
RUN wget https://github.com/zeromq/cppzmq/archive/v${CPPZMQ_VERSION}.tar.gz -O cppzmq-${CPPZMQ_VERSION}.tar.gz
RUN tar -xzf cppzmq-${CPPZMQ_VERSION}.tar.gz
WORKDIR /tmp/cppzmq-${CPPZMQ_VERSION}
RUN mkdir build && cd build && cmake .. -DCPPZMQ_BUILD_TESTS=OFF && make -j install

WORKDIR /tmp
RUN rm -rf cppzmq*
ARG CONTROL_LIBRARIES_BRANCH=develop
RUN git clone -b ${CONTROL_LIBRARIES_BRANCH} --depth 1 https://github.com/epfl-lasa/control-libraries.git
RUN cd control-libraries/source && sudo ./install.sh --auto --no-controllers --no-dynamical-systems --no-robot-model
RUN cd control-libraries/protocol && sudo ./install.sh --auto
RUN pip3 install control-libraries/python

RUN rm -rf /tmp/*

# install pyzmq
RUN pip3 install pyzmq

WORKDIR ${HOME}
FROM source-dependencies as build-test

# Clean image
RUN sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/*
WORKDIR ${HOME}
COPY --chown=${USER} ./cpp ./network_interfaces/cpp
COPY --chown=${USER} ./python ./network_interfaces/python

FROM core-dependencies AS build-test
RUN cd ./network_interfaces/cpp && mkdir build && cd build && cmake -DBUILD_TESTING=ON .. \
&& make -j && CTEST_OUTPUT_ON_FAILURE=1 make test && make -j install
RUN cd ./network_interfaces/python && pip3 install ./ && python3 -m unittest discover ./test --verbose

COPY ./ ./
RUN cd cpp && mkdir build && cd build && cmake -DBUILD_TESTING=ON .. \
&& make -j all && CTEST_OUTPUT_ON_FAILURE=1 make test
RUN cd python && pip install ./ && python3 -m unittest discover ./network_interfaces/tests --verbose
# Clean image
RUN rm -rf ./network_interfaces
76 changes: 73 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,83 @@ For Python, install the library simply with
cd path/to/network-interfaces/python && pip3 install ./
```

### ZMQ loopback scripts

Both the C++ and Python directory contain *loopback* executables that subscribe to the state (or command)
and publish a command (or state) in and endless loop. They can be used to check if the robot and the user are connected
and receive each other's messages correctly. To run the scripts, make the CMake project and/or install the Python
project, and then choose one of the following:

```
bash build.sh -t
aica-docker interactive aica-technology/network-interfaces:build-test -u ros2
cd path/to/cpp/build
./zmq_loopback_state state_uri command_uri
./zmq_loopback_command state_uri command_uri
cd path/to/python/scripts
python3 zmq_loopback_state.py state_uri command_uri
python3 zmq_loopback_command.py state_uri command_uri
```

Note that the scripts are provided with a correct combination of state and command URIs. There are examples of such
combinations below. Assume the robot state is on port 1601 and the command on 1602:

#### Everything runs in one container / on the same host (network independent)

If all applications run in the same container, or on the same host, the situation is:

- The robot publishes its state on `0.0.0.0:1601` and listens to commands on `0.0.0.0:1602` with both sockets
non-binding: run `./zmq_loopback_state *:1601 *:1602` or `python3 zmq_loopback_state.py *:1601 *:1602` to receive and
print the robot's state.
- The controller sends the command on `*:1602` and receives the state on `*:1601` with both sockets binding:
run `./zmq_loopback_command 0.0.0.0:1601 0.0.0.0:1602` or
`python3 zmq_loopback_command.py 0.0.0.0:1601 0.0.0.0:1602` to receive and print the command and send back a random
state.

#### One or more containers and host, all on host network and with no hostname

Same as above.

#### One container with host (container not on host network)

The container is an SSH server or needs to be on a user-defined network, but the robot is connected directly to the host
machine. This is almost the same case as above:

- The controller sends the command on `*:1602` and receives the state on `*:1601` with both sockets binding:
run `./zmq_loopback_command 0.0.0.0:1601 0.0.0.0:1602` or
`python3 zmq_loopback_command.py 0.0.0.0:1601 0.0.0.0:1602` to receive and print the command and send back a random
state.

There is one important difference though: The container needs to bind ports 1601 and 1602 (i.e.
add `-p1601:1601 -p1602:1602` to the `docker run` command) explicitly such that the communication goes through.

#### Several containers, user-defined bridge network with hostnames

If the containers all run on a user-defined bridge network, the connecting sockets need to be provided with the hostname
of the binding sockets. For example, if the containers are running on network *aicanet* and have hostnames *robot* and
*controller*, respectively.

- The robot publishes its state on `controller.aicanet:1601` and listens to commands on `controller.aicanet:1602` with
both sockets non-binding: run `./zmq_loopback_state *:1601 *:1602` or `python3 zmq_loopback_state.py *:1601 *:1602` to
receive and print the robot's state.
- The controller sends the command on `*:1602` and receives the state on `*:1601` with both sockets binding:
run `./zmq_loopback_command controller.aicanet:1601 controller.aicanet:1602` or `
python3 zmq_loopback_command.py controller.aicanet:1601 controller.aicanet:1602` to receive and print the command and
send back a random state.

#### Note

- This list of combinations is not exhaustive.
- The binding sockets always have a URI like `*:port` whilst the connecting sockets need to provide the complete address
version (`0.0.0.0:port` if on localhost or `hostname.network:port` if on bridge network).

## Development

To build and run a Docker container as an SSH toolchain server for remote development with
Build and run a Docker container as an SSH toolchain server for remote development with:

```console
bash build.sh
aica-docker server aica-technology/network-interfaces -u ros2 -p 8010
bash build-server.sh -s
```

Note: This requires the installation of the `aica-docker` scripts
Expand Down
21 changes: 8 additions & 13 deletions build.sh → build-test.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
#!/bin/bash
ROS_VERSION=foxy
CONTROL_LIBRARIES_BRANCH=develop

IMAGE_NAME=aica-technology/network-interfaces
IMAGE_STAGE=core-dependencies
IMAGE_STAGE=build-test

BUILD_FLAGS=()

HELP_MESSAGE="Usage: ./build.sh [--rebuild] [--test] [--verbose]
Build a Docker container for remote development and/or running unittests.
HELP_MESSAGE="Usage: ./build.sh [-r] [-v]
Build a Docker container for running unittests.
Options:
-r, --rebuild Rebuild the image with no cache.
-t, --test Build and run all the unittests.
-v, --verbose Show all the output of the Docker
build process
build process.
-h, --help Show this help message."

while [ "$#" -gt 0 ]; do
Expand All @@ -30,9 +25,9 @@ while [ "$#" -gt 0 ]; do
esac
done

BUILD_FLAGS+=(--build-arg ROS_VERSION="${ROS_VERSION}")
BUILD_FLAGS+=(--build-arg CONTROL_LIBRARIES_BRANCH="${CONTROL_LIBRARIES_BRANCH}")
BUILD_FLAGS+=(-t "${IMAGE_NAME}:${IMAGE_STAGE}")
BUILD_FLAGS+=(--target "${IMAGE_STAGE}")

docker pull ghcr.io/aica-technology/ros2-control-libraries:"${ROS_VERSION}"
DOCKER_BUILDKIT=1 docker build "${BUILD_FLAGS[@]}" .
docker pull ghcr.io/epfl-lasa/control-libraries/development-dependencies || exit 1
DOCKER_BUILDKIT=1 docker build "${BUILD_FLAGS[@]}" .
15 changes: 10 additions & 5 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.9)
project(network_interfaces VERSION 0.1.0)
project(network_interfaces VERSION 0.2.0)

option(BUILD_TESTING "Build tests." OFF)

Expand Down Expand Up @@ -36,9 +36,14 @@ install(DIRECTORY include/
DESTINATION include
)

add_executable(zmq_loopback_state scripts/zmq_loopback_state.cpp)
target_link_libraries(zmq_loopback_state clproto cppzmq state_representation)

add_executable(zmq_loopback_command scripts/zmq_loopback_command.cpp)
target_link_libraries(zmq_loopback_command clproto cppzmq state_representation)

if(BUILD_TESTING)
add_executable(test_communication test/test_communication.cpp)
target_link_libraries(test_communication ${GTEST_LIBRARIES} pthread)
add_test(NAME test_communication COMMAND test_communication)
target_link_libraries(test_communication clproto cppzmq state_representation)
add_executable(test_zmq_communication test/test_zmq_communication.cpp)
target_link_libraries(test_zmq_communication ${GTEST_LIBRARIES} pthread clproto cppzmq state_representation)
add_test(NAME test_zmq_communication COMMAND test_zmq_communication)
endif()
35 changes: 35 additions & 0 deletions cpp/scripts/zmq_loopback_command.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <unistd.h>
#include <state_representation/robot/JointState.hpp>
#include <state_representation/space/cartesian/CartesianState.hpp>

#include "network_interfaces/zmq/network.h"

int main(int argc, char** argv) {
std::string state_uri, command_uri;
if (argc != 3) {
std::cerr << "Provide two arguments: state_uri command_uri" << std::endl;
return 1;
} else {
state_uri = argv[1];
command_uri = argv[2];
}
::zmq::context_t context(1);
::zmq::socket_t subscriber, publisher;
network_interfaces::zmq::configure_subscriber(context, subscriber, command_uri, false);
network_interfaces::zmq::configure_publisher(context, publisher, state_uri, false);

network_interfaces::zmq::StateMessage state;
state.joint_state = state_representation::JointState::Random("loopback", 7);
state.ee_state = state_representation::CartesianState::Random("loopback_ee");
network_interfaces::zmq::CommandMessage command;

while (true) {
network_interfaces::zmq::send(state, publisher);
if (network_interfaces::zmq::receive(command, subscriber)) {
std::cout << command << std::endl;
}
usleep(100000);
}

return 0;
}
35 changes: 35 additions & 0 deletions cpp/scripts/zmq_loopback_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <unistd.h>
#include <state_representation/robot/JointState.hpp>

#include "network_interfaces/zmq/network.h"

int main(int argc, char** argv) {
std::string state_uri, command_uri;
if (argc != 3) {
std::cerr << "Provide two arguments: state_uri command_uri" << std::endl;
return 1;
} else {
state_uri = argv[1];
command_uri = argv[2];
}
::zmq::context_t context(1);
::zmq::socket_t subscriber, publisher;
network_interfaces::zmq::configure_subscriber(context, subscriber, state_uri, true);
network_interfaces::zmq::configure_publisher(context, publisher, command_uri, true);

network_interfaces::zmq::StateMessage state;
network_interfaces::zmq::CommandMessage command;

while (true) {
if (network_interfaces::zmq::receive(state, subscriber)) {
std::cout << state << std::endl;
command.joint_state = state_representation::JointState::Zero(state.joint_state.get_name(), state.joint_state.get_names());
command.joint_state.set_positions(state.joint_state.get_positions());
command.control_type = std::vector<int>{3};
network_interfaces::zmq::send(command, publisher);
}
usleep(100000);
}

return 0;
}
File renamed without changes.
35 changes: 35 additions & 0 deletions dev-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash
CONTROL_LIBRARIES_BRANCH=develop
REMOTE_SSH_PORT=4420

IMAGE_NAME=aica-technology/network-interfaces
IMAGE_STAGE=source-dependencies

BUILD_FLAGS=()

HELP_MESSAGE="Usage: ./dev-server.sh [-r] [-v]
Build a Docker container for remote development.
Options:
-r, --rebuild Rebuild the image with no cache.
-v, --verbose Show all the output of the Docker
build process.
-h, --help Show this help message."

while [ "$#" -gt 0 ]; do
case "$1" in
-r|--rebuild) BUILD_FLAGS+=(--no-cache); shift 1;;
-v|--verbose) BUILD_FLAGS+=(--progress=plain); shift 1;;
-h|--help) echo "${HELP_MESSAGE}"; exit 0;;
*) echo "Unknown option: $1" >&2; echo "${HELP_MESSAGE}"; exit 1;;
esac
done

BUILD_FLAGS+=(--build-arg CONTROL_LIBRARIES_BRANCH="${CONTROL_LIBRARIES_BRANCH}")
BUILD_FLAGS+=(-t "${IMAGE_NAME}:${IMAGE_STAGE}")
BUILD_FLAGS+=(--target "${IMAGE_STAGE}")

docker pull ghcr.io/epfl-lasa/control-libraries/development-dependencies || exit 1
DOCKER_BUILDKIT=1 docker build "${BUILD_FLAGS[@]}" . || exit 1

aica-docker server "${IMAGE_NAME}:${IMAGE_STAGE}" -u developer -p "${REMOTE_SSH_PORT}"
Loading

0 comments on commit 5d0345b

Please sign in to comment.