Skip to content

Commit

Permalink
release: version 8.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
domire8 committed May 1, 2024
1 parent a0b5ae4 commit bd147af
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 6 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Release Versions

- [8.1.0](#810)
- [8.0.0](#800)
- [7.4.0](#740)
- [7.3.0](#730)
Expand All @@ -13,6 +14,16 @@ Release Versions
- [6.3.0](#630)
- [6.2.0](#620)

## 8.1.0

Version 8.1.0 adds a new module called `communication_interfaces` to the control libraries. This is a library for
simple socket communication and was previously developed in a different place. It currently implements sockets for UPD,
TCP, and ZMQ communication.

### Full changelog

- feat: migrate communication interfaces (#190)

## 8.0.0

Version 8.0.0 is a major update that adds new state types and collision detection features to control-libraries.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8.0.0
8.1.0
2 changes: 1 addition & 1 deletion demos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(control_libraries 8.0.0 CONFIG REQUIRED)
find_package(control_libraries 8.1.0 CONFIG REQUIRED)

set(DEMOS_SCRIPTS
task_space_control_loop
Expand Down
2 changes: 1 addition & 1 deletion doxygen/doxygen.conf
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = "Control Libraries"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 8.0.0
PROJECT_NUMBER = 8.1.0

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion protocol/clproto_cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15)

project(clproto VERSION 8.0.0)
project(clproto VERSION 8.1.0)

# Default to C99
if(NOT CMAKE_C_STANDARD)
Expand Down
31 changes: 31 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,34 @@ encoded_msg = clproto.encode(B, clproto.MessageType.JOINT_STATE_MESSAGE)

decoded_object = clproto.decode(encoded_msg)
```

### Note on the communication interfaces

The Python bindings require an additional step of sanitizing the data when sending and receiving bytes. To illustrate
this, an example is provided here.

```python
# First a server and a client is connected
context = ZMQContext()
server = ZMQPublisher(ZMQSocketConfiguration(context, "127.0.0.1", "5001", True))
client = ZMQSubscriber(ZMQSocketConfiguration(context, "127.0.0.1", "5001", False))
server.open()
client.open()
```

```python
# Then a string is sent through
str_msg = "Hello!"
server.send_bytes(str_msg)
received_str_msg = client.receive_bytes()
if received_str_msg is not None:
print(received_str_msg)
```

Here we expect the printed value to be `Hello!`, however due to the way strings and bytes are processed, the string
message is left as a byte literal and `b'Hello!'` is printed instead. We can correct this as follows:

```python
# Instead, decode the value
print(received_str_msg.decode("utf-8")) # will print Hello! as expected
```
2 changes: 1 addition & 1 deletion python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# names of the environment variables that define osqp and openrobots include directories
osqp_path_var = 'OSQP_INCLUDE_DIR'

__version__ = "8.0.0"
__version__ = "8.1.0"
__libraries__ = ['state_representation', 'clproto', 'controllers', 'dynamical_systems', 'robot_model', 'communication_interfaces']
__include_dirs__ = ['include']

Expand Down
2 changes: 1 addition & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15)

project(control_libraries VERSION 8.0.0)
project(control_libraries VERSION 8.1.0)

# Build options
option(BUILD_TESTING "Build all tests." OFF)
Expand Down
115 changes: 115 additions & 0 deletions source/communication_interfaces/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Communication interfaces

A library for simple socket communication. It currently implements sockets for UPD, TCP, and ZMQ communication.

## 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 `on_open()`, `on_receive_bytes(std::string&)`, and `on_send_bytes(const std::string&)`.

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

The `on_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 `on_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;

private:
void on_open() override;

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

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

void on_close() override;
}
```
```c++
// DerivedSocket.cpp
DerivedSocket::DerivedSocket(DerivedSocketConfig configuraiton) {
// save configuration parameters for later use
}
DerivedSocket::~DerivedSocket() {
DerivedSocket::on_close();
}
void DerivedSocket::on_open() {
// Configure and open the socket
}
bool DerivedSocket::on_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::on_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::on_close() {
// Perform clean-up steps here
}
```

## Notes on ZMQ communication

It can be difficult to set up a working configuration for ZMQ communication. The examples below assume that there are
two ZMQ sockets, one that has 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.
- The controller sends the command on `*:1602` and receives the state on `*:1601` with both sockets binding.

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

Same as above.

#### 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.
- The controller sends the command on `*:1602` and receives the state on `*:1601` with both sockets binding.

#### 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).

0 comments on commit bd147af

Please sign in to comment.