Skip to content

Commit

Permalink
Merge pull request #41 from aica-technology/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisBrunner authored May 30, 2023
2 parents f4bea82 + 97ec448 commit eb7794a
Show file tree
Hide file tree
Showing 39 changed files with 1,607 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.github
/*.sh
/*.md
Dockerfile
LICENSE
53 changes: 53 additions & 0 deletions .github/workflows/build-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Build and Push Multi-Arch Images

on:
push:
branches:
- develop
- main
tags:
- "v*.*.*"

jobs:
get-tag:
runs-on: ubuntu-latest
name: Get tag
outputs:
tag: ${{ steps.parse-tag.outputs.tag }}
steps:
- uses: actions/checkout@v3
- uses: aica-technology/.github/.github/actions/[email protected]
id: parse-tag

build:
needs: [get-tag]
strategy:
matrix:
arch: [amd64, arm64]
include:
- image: ubuntu-latest
- image: buildjet-2vcpu-ubuntu-2204-arm
arch: arm64

runs-on: ${{ matrix.image }}
name: Build and publish (${{ matrix.arch }})
steps:
- uses: actions/checkout@v3
- uses: aica-technology/.github/.github/actions/[email protected]
with:
image_name: aica-technology/network-interfaces
image_tags: ${{ needs.get-tag.outputs.tag }}-${{ matrix.arch }}
dockerfile_path: Dockerfile.ci
token: ${{ secrets.GITHUB_TOKEN }}

multi-arch:
runs-on: ubuntu-latest
name: Merge into a multi-arch image
needs: [get-tag, build]
steps:
- uses: aica-technology/.github/.github/actions/[email protected]
with:
image_name: aica-technology/network-interfaces
image_tags: ${{ needs.get-tag.outputs.tag }}
archs: amd64,arm64
token: ${{ secrets.GITHUB_TOKEN }}
13 changes: 8 additions & 5 deletions .github/workflows/manual-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ on:
workflow_dispatch:
inputs:
cl_branch:
description: 'If set, the desired branch of control libraries'
description: "If set, the desired branch of control libraries"
required: false
default: 'develop'
default: "develop"
tag:
description: "The tag to use"
required: true

jobs:
build-publish:
Expand All @@ -19,7 +22,7 @@ jobs:

- name: Build image
run: |
IMAGE_NAME=network-interfaces:latest
IMAGE_NAME=network-interfaces:${{ inputs.tag }}
docker build . \
--build-arg CONTROL_LIBRARIES_BRANCH=${{ inputs.cl_branch }} \
--tag ${IMAGE_NAME}
Expand All @@ -31,7 +34,7 @@ jobs:

- name: Push image
run: |
IMAGE_NAME=network-interfaces:latest
IMAGE_NAME=network-interfaces:${{ inputs.tag }}
docker tag ${IMAGE_NAME} ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
docker push ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}
shell: bash
shell: bash
19 changes: 19 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.15)
project(all_network_interfaces)

include(FetchContent)
option(CPPZMQ_BUILD_TESTS OFF)
FetchContent_Declare(
cppzmq
GIT_REPOSITORY https://github.com/zeromq/cppzmq/
GIT_TAG v4.7.1
)
FetchContent_MakeAvailable(cppzmq)

add_subdirectory(cpp)
add_subdirectory(source/communication_interfaces)

if(BUILD_TESTING)
# reexport the test target defined in the subdirectories
enable_testing()
endif()
95 changes: 95 additions & 0 deletions Dockerfile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
FROM ghcr.io/aica-technology/ros2-modulo-control:humble as base

FROM base as apt-dependencies
RUN <<HEREDOC
PACKAGES="libzmq3-dev"
mkdir -p /tmp/apt

sudo apt-get update
# We then do a dry-run and parse the output of apt to gather the list of packages to be installed
# Example output:
# ```
# #########
# NOTE: This is only a simulation!
# apt-get needs root privileges for real execution.
# Keep also in mind that locking is deactivated,
# so don't depend on the relevance to the real current situation!
# Reading package lists...
# Building dependency tree...
# Reading state information...
# The following additional packages will be installed:
# libavutil56 libblosc1
# The following NEW packages will be installed:
# libavutil56 libblosc1
# 0 upgraded, 5 newly installed, 0 to remove and 28 not upgraded.
# Inst libavutil56 (7:4.4.2-0ubuntu0.22.04.1 Ubuntu:22.04/jammy-updates, Ubuntu:22.04/jammy-security [arm64])
# Inst libblosc1 (1.21.1+ds2-2 Ubuntu:22.04/jammy [arm64])
# Conf libavutil56 (7:4.4.2-0ubuntu0.22.04.1 Ubuntu:22.04/jammy-updates, Ubuntu:22.04/jammy-security [arm64])
# Conf libblosc1 (1.21.1+ds2-2 Ubuntu:22.04/jammy [arm64])
# ```
# Transformed into:
# ```
# libavutil56
# libblosc1
# ```
apt-get install ${PACKAGES} --dry-run \
| grep -e '^Inst ' \
| sed -E 's/^Inst (\S+) .*$/\1/' > /tmp/new-packages.txt
# Then we install apt packages like normal
sudo apt-get install -y ${PACKAGES}
# Finally we use dpkg to get all files installed by those packages and copy them to a new root
# - get list of files installed by all the packages
# - remove empty lines
# - sort
# - remove duplicates
# - copy files while keeping file hierarchy and preserving links as-is
# - remove "omitting directory" messages (we don't do recursive copy as we only want specific files) for cleaner output
xargs -a /tmp/new-packages.txt dpkg-query -L \
| sed '/^$/d' | sort | uniq \
| xargs -d "\n" cp --parents -dp -t /tmp/apt 2>&1 \
| grep -v 'omitting directory'
# this root can then be copied to / to install everything globally or use LD_LIBRARY_PATH to use it locally
HEREDOC

FROM base as python
COPY --chown=${USER} ./python /python
RUN \
--mount=type=cache,target=${HOME}/.cache,id=pip-${TARGETPLATFORM},uid=1000 \
--mount=type=ssh,uid=1000 \
python3 -m pip install --prefix=/tmp/python /python

FROM base as code
WORKDIR /src
COPY --from=apt-dependencies /tmp/apt /
COPY --chown=${USER} . /src

FROM code as development
COPY --from=python /tmp/python/local/lib/python3.10/dist-packages/ ${HOME}/.local/lib/python3.10/site-packages/

FROM code as build
RUN \
--mount=type=cache,target=./build,id=cmake-${TARGETPLATFORM},uid=1000 \
--mount=type=ssh,uid=1000 \
cmake -B build \
&& cmake --build build

FROM build as test
RUN \
--mount=type=cache,target=./build,id=cmake-${TARGETPLATFORM},uid=1000 \
--mount=type=ssh,uid=1000 \
cmake -B build -DBUILD_TESTING=ON \
&& CTEST_OUTPUT_ON_FAILURE=1 cmake --build build --target all test
COPY --from=python /tmp/python/local/lib/python3.10/dist-packages/ ${HOME}/.local/lib/python3.10/site-packages/
RUN python3 -m unittest discover python/test --verbose

FROM build as install
RUN \
--mount=type=cache,target=./build,id=cmake-${TARGETPLATFORM},uid=1000 \
--mount=type=ssh,uid=1000 \
cmake -B build -DCMAKE_INSTALL_PREFIX=/tmp/net-ifaces \
&& cmake --build build --target all install

FROM scratch as production
COPY --from=apt-dependencies /tmp/apt /
COPY --from=install /tmp/net-ifaces /usr/local
COPY --from=python /tmp/python/local/lib/python3.10/dist-packages/ /home/ros2/.local/lib/python3.10/site-packages/
6 changes: 4 additions & 2 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ include(FindPkgConfig)

add_project_dependency(control_libraries 7.0.0 REQUIRED COMPONENTS state_representation)
add_project_dependency(clproto 7.0.0 REQUIRED)
add_project_dependency(cppzmq 4.7.1 REQUIRED)
if (NOT ${cppzmq_FOUND}) # provided by parent CMakeLists.txt (if used)
add_project_dependency(cppzmq 4.7.1 REQUIRED)
endif()

include_directories(include)

Expand Down Expand Up @@ -93,4 +95,4 @@ if(${PKG_CONFIG_FOUND})
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig/ COMPONENT pkgconfig)
else()
message(WARNING "Could not find pkg-config executable, skipping generation of pkg-config files.")
endif()
endif()
15 changes: 15 additions & 0 deletions source/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM ghcr.io/aica-technology/control-libraries/development-dependencies as source-dependencies

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

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

RUN rm -rf /tmp/*
77 changes: 77 additions & 0 deletions source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
## Socket Interface

The `ISocket` class is an interface for simple socket communication, defining functions for opening a socket,
sending and receiving bytes, and closing the socket connection.

The `ISocket` class defines an `open()` method to perform configuration steps to open the socket for communication.
If opening the socket fails, an exception is thrown. The `close()` method is also provided to perform steps to disconnect
and close the socket communication.

The functions `receive_bytes(std::string&)` and `send_bytes(const std::string&)` perform the read and write logic of the socket
respectively.

### Implementing a derived socket class

To use this class, create a subclass that inherits from it and implement its pure virtual functions. The pure virtual
functions are `open()`, `receive_bytes(std::string&)`, and `send_bytes(const std::string&)`.

Configuration parameters should be passed with a configuration struct, resulting in a single argument constructor.

The `close()` function can optionally be overridden to perform steps to disconnect and close the socket communication.
If a derived class defines any cleanup behavior in `close()`, it should also be invoked statically and explicitly
in the destructor of the derived class.

An example is given below.

```c++
// DerivedSocket.hpp

struct DerivedSocketConfig {
int param1;
double param2;
};

class DerivedSocket : ISocket {
public:
DerivedSocket(DerivedSocketConfig configuration);

~DerivedSocket() override;

void open() override;

bool receive_bytes(std::string& buffer) override;

bool send_bytes(const std::string& buffer) override;

void close() override;
}
```
```c++
// DerivedSocket.cpp
DerivedSocket::DerivedSocket(DerivedSocketConfig configuraiton) {
// save configuration parameters for later use
}
DerivedSocket::~DerivedSocket() {
DerivedSocket::close();
}
void DerivedSocket::open() {
// Configure and open the socket
}
bool DerivedSocket::receive_bytes(std::string& buffer) {
// Read the contents of the socket into the buffer and return true on success. Otherwise, return false.
return true;
}
bool DerivedSocket::send_bytes(const std::string& buffer) {
// Write the contents of the buffer onto the socket and return true on success. Otherwise, return false.
return true;
}
void DerivedSocket::close() {
// Perform clean-up steps here
}
```
Loading

0 comments on commit eb7794a

Please sign in to comment.