Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add is_opened flag to socket base class #68

Merged
merged 8 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "network interfaces",
"remoteUser": "ros2",
"build": {
"dockerfile": "../Dockerfile",
"context": "..",
"target": "development",
"args": {
"ROS2_VERSION": "iron"
}
},
"workspaceMount": "source=${localWorkspaceFolder},target=/home/ros2/.devcontainer,type=bind,consistency=cached",
"workspaceFolder": "/home/ros2/.devcontainer",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools-extension-pack",
"eamodio.gitlens"
]
}
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.idea
*cmake-build-debug*
build/
15 changes: 15 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "AMD",
"includePath": [
"/home/ros2/.devcontainer/source/communication_interfaces/include/**"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cmake.sourceDirectory": "/home/ros2/.devcontainer/source/communication_interfaces",
"cmake.configureArgs": [ "-DBUILD_TESTING=ON" ],
"C_Cpp.clang_format_style": "file:/home/ros2/.clang-format"
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Release Versions:
- [0.2.0](#020)
- [0.1.0](#010)

## Upcoming changes (in development)

- feat: add is_opened flag to socket base class (#68)

## 2.0.1

Version 2.0.1 contains a hotfix that enables socket communiction with any serialized message in Python, which was not
Expand Down
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ 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&)`.
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 `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
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.
Expand All @@ -45,13 +45,14 @@ public:

~DerivedSocket() override;

void open() override;
private:
void on_open() override;

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

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

void close() override;
void on_close() override;
}
```
Expand All @@ -62,24 +63,24 @@ DerivedSocket::DerivedSocket(DerivedSocketConfig configuraiton) {
}
DerivedSocket::~DerivedSocket() {
DerivedSocket::close();
DerivedSocket::on_close();
}
void DerivedSocket::open() {
void DerivedSocket::on_open() {
// Configure and open the socket
}
bool DerivedSocket::receive_bytes(std::string& buffer) {
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::send_bytes(const std::string& buffer) {
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::close() {
void DerivedSocket::on_close() {
// Perform clean-up steps here
}
```
Expand Down
16 changes: 8 additions & 8 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ ROS2_VERSION=humble

HELP_MESSAGE="Usage: build.sh [options]
Options:
--test Target the test layer to run the tests.
--test Target the test layer to run the tests.

--ros2-version <VERSION> Specify the version of ROS 2 to use.
(default: $ROS2_VERSION)
--ros2-version <VERSION> Specify the version of ROS 2 to use.
(default: $ROS2_VERSION)

-v|--verbose Set the build output to verbose.
-v|--verbose Set the build output to verbose.

--cache-id <id> Invalidate the mount cache (e.g. CMake build folder)
by providing a new value.
--cache-id <id> Invalidate the mount cache (e.g. CMake build folder)
by providing a new value.

-r|--no-cache Invalidate all cache (layer + mount).
-r|--no-cache Invalidate all cache (layer + mount).

-h|--help Show this help message.
-h|--help Show this help message.
"

TEST=0
Expand Down
15 changes: 1 addition & 14 deletions python/src/communication_interfaces_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,6 @@

using namespace communication_interfaces;

class PySocket : public sockets::ISocket, public std::enable_shared_from_this<PySocket> {
public:
using sockets::ISocket::ISocket;

void open() override { PYBIND11_OVERRIDE_PURE(void, ISocket, open, ); }

bool receive_bytes(std::string& buffer) override { PYBIND11_OVERRIDE_PURE(bool, ISocket, receive_bytes, buffer); }

bool send_bytes(const std::string& buffer) override { PYBIND11_OVERRIDE_PURE(bool, ISocket, send_bytes, buffer); }

void close() override { PYBIND11_OVERRIDE(void, ISocket, close, ); }
};

PYBIND11_MODULE(communication_interfaces, m) {
m.doc() = "Python bindings for communication interfaces";

Expand All @@ -36,7 +23,7 @@ PYBIND11_MODULE(communication_interfaces, m) {

auto m_sub_sock = m.def_submodule("sockets", "Submodule for communication interfaces sockets");

py::class_<sockets::ISocket, std::shared_ptr<sockets::ISocket>, PySocket>(m_sub_sock, "ISocket")
py::class_<sockets::ISocket, std::shared_ptr<sockets::ISocket>>(m_sub_sock, "ISocket")
.def("open", &sockets::ISocket::open, "Perform configuration steps to open the socket for communication")
.def(
"receive_bytes",
Expand Down
25 changes: 25 additions & 0 deletions python/test/test_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import pytest
import time

from communication_interfaces.exceptions import SocketConfigurationError
from communication_interfaces.sockets import TCPClientConfiguration, TCPClient, TCPServerConfiguration, TCPServer


Expand Down Expand Up @@ -29,3 +30,27 @@ def test_comm(self):
response = self.client.receive_bytes()
assert response
assert response.decode("utf-8").rstrip("\x00") == self.server_message

buffer = ""
self.server.close()
with pytest.raises(SocketConfigurationError):
self.server.receive_bytes()
with pytest.raises(SocketConfigurationError):
self.server.send_bytes(buffer)
self.client.close()
with pytest.raises(SocketConfigurationError):
self.client.receive_bytes()
with pytest.raises(SocketConfigurationError):
self.client.send_bytes(buffer)

def test_not_open(self):
buffer = ""
with pytest.raises(SocketConfigurationError):
self.server.receive_bytes()
with pytest.raises(SocketConfigurationError):
self.server.send_bytes(buffer)

with pytest.raises(SocketConfigurationError):
self.client.receive_bytes()
with pytest.raises(SocketConfigurationError):
self.client.send_bytes(buffer)
33 changes: 30 additions & 3 deletions python/test/test_udp.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,35 @@ def test_port_reuse(udp_config, server):
server2.open()


def test_open_close(server):
def test_open_close(udp_config):
buffer = ""
udp_config.timeout_duration_sec = 0.5
server = UDPServer(udp_config)
with pytest.raises(SocketConfigurationError):
server.send_bytes(buffer)
with pytest.raises(SocketConfigurationError):
server.receive_bytes()
server.open()
server.close()
assert server.send_bytes("test")
assert not server.receive_bytes()

assert not server.send_bytes("")
server.close()
with pytest.raises(SocketConfigurationError):
server.send_bytes(buffer)
with pytest.raises(SocketConfigurationError):
server.receive_bytes()

client = UDPClient(udp_config)
with pytest.raises(SocketConfigurationError):
client.send_bytes(buffer)
with pytest.raises(SocketConfigurationError):
client.receive_bytes()
client.open()
assert client.send_bytes("test")
assert not client.receive_bytes()

client.close()
with pytest.raises(SocketConfigurationError):
client.send_bytes(buffer)
with pytest.raises(SocketConfigurationError):
client.receive_bytes()
16 changes: 16 additions & 0 deletions python/test/test_zmq.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import time

from communication_interfaces.exceptions import SocketConfigurationError
from communication_interfaces.sockets import ZMQContext, ZMQSocketConfiguration, ZMQPublisher, ZMQSubscriber, \
ZMQCombinedSocketsConfiguration, ZMQPublisherSubscriber

Expand Down Expand Up @@ -71,3 +72,18 @@ def test_send_receive_combined(zmq_context):

server.close()
client.close()


def test_open_close(zmq_config):
buffer = ""
socket = ZMQPublisher(zmq_config)
with pytest.raises(SocketConfigurationError):
socket.send_bytes(buffer)
with pytest.raises(SocketConfigurationError):
socket.receive_bytes()

socket.open()
assert socket.send_bytes("test")
socket.close()
with pytest.raises(SocketConfigurationError):
socket.send_bytes(buffer)
1 change: 1 addition & 0 deletions source/communication_interfaces/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ if (NOT cppzmq_POPULATED)
endif()

add_library(${PROJECT_NAME} SHARED
${PROJECT_SOURCE_DIR}/src/sockets/ISocket.cpp
${PROJECT_SOURCE_DIR}/src/sockets/UDPSocket.cpp
${PROJECT_SOURCE_DIR}/src/sockets/UDPClient.cpp
${PROJECT_SOURCE_DIR}/src/sockets/UDPServer.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <string>

#include "communication_interfaces/exceptions/SocketConfigurationException.hpp"

namespace communication_interfaces::sockets {

/**
Expand All @@ -23,25 +25,56 @@ class ISocket {
* @brief Perform configuration steps to open the socket for communication
* @throws SocketConfigurationException if opening fails
*/
virtual void open() = 0;
void open();

/**
* @brief Receive bytes from the socket
* @param buffer The buffer to fill with the received bytes
* @return True if bytes were received, false otherwise
* @throws SocketConfigurationException if socket has not been opened yet
*/
virtual bool receive_bytes(std::string& buffer) = 0;
bool receive_bytes(std::string& buffer);

/**
* @brief Send bytes to the socket
* @param buffer The buffer with the bytes to send
* @return True if bytes were sent, false otherwise
* @throws SocketConfigurationException if socket has not been opened yet
*/
virtual bool send_bytes(const std::string& buffer) = 0;
bool send_bytes(const std::string& buffer);

/**
* @brief Perform steps to disconnect and close the socket communication
*/
virtual void close() {}
void close();

protected:
/**
* @brief Perform configuration steps to open the socket for communication
* @throws SocketConfigurationException if opening fails
*/
virtual void on_open() = 0;

/**
* @brief Receive bytes from the socket
* @param buffer The buffer to fill with the received bytes
* @return True if bytes were received, false otherwise
*/
virtual bool on_receive_bytes(std::string& buffer) = 0;

/**
* @brief Send bytes to the socket
* @param buffer The buffer with the bytes to send
* @return True if bytes were sent, false otherwise
*/
virtual bool on_send_bytes(const std::string& buffer) = 0;

/**
* @brief Perform steps to disconnect and close the socket communication
*/
virtual void on_close() {};

private:
bool opened_ = false;
};
}// namespace communication_interfaces::sockets
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ class TCPClient : public TCPSocket {
*/
explicit TCPClient(TCPClientConfiguration configuration);

private:
/**
* @copydoc ISocket::open()
* @copydoc ISocket::on_open()
* @details Connect the client socket to the server
*/
void open() override;
void on_open() override;

private:
TCPClientConfiguration config_; ///< Socket configuration struct
};
} // namespace communication_interfaces::sockets
Loading