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

Add HTTP client #55

Open
wants to merge 5 commits into
base: noetic-devel
Choose a base branch
from
Open
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
190 changes: 190 additions & 0 deletions smacc/include/smacc/client_bases/smacc_http_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#pragma once

#include <smacc/impl/smacc_state_impl.h>
#include <smacc/smacc_client.h>

#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/optional/optional_io.hpp>
#include <functional>
#include <thread>

namespace smacc {
namespace client_bases {

class session : public std::enable_shared_from_this<session> {
boost::asio::ip::tcp::resolver resolver_;
boost::beast::tcp_stream stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
boost::beast::http::request<boost::beast::http::empty_body> req_;
boost::beast::http::response<boost::beast::http::string_body> res_;

public:
// Objects are constructed with a strand to
// ensure that handlers do not execute concurrently.
session(boost::asio::io_context &ioc,
const std::function<void(const boost::beast::http::response<
boost::beast::http::string_body> &)>
response)
: resolver_(boost::asio::make_strand(ioc)),
stream_(boost::asio::make_strand(ioc)),
onResponse{response} {}

// Start the asynchronous operation
void run(const std::string &host, const std::string &port,
const std::string &target, const int &version) {
// Set up an HTTP GET request message
req_.version(version);
req_.method(boost::beast::http::verb::get);
req_.target(target);
req_.set(boost::beast::http::field::host, host);
req_.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);

// Look up the domain name
resolver_.async_resolve(host.c_str(), port.c_str(),
boost::beast::bind_front_handler(
&session::on_resolve, shared_from_this()));
}

void on_resolve(boost::beast::error_code ec,
boost::asio::ip::tcp::resolver::results_type results) {
if (ec) return fail(ec, "resolve");

// Set a timeout on the operation
stream_.expires_after(std::chrono::seconds(30));

// Make the connection on the IP address we get from a lookup
stream_.async_connect(results,
boost::beast::bind_front_handler(&session::on_connect,
shared_from_this()));
}

private:
void fail(boost::beast::error_code ec, char const *what) {
std::cerr << what << ": " << ec.message() << "\n";
res_.result(boost::beast::http::status::bad_request);
res_.reason() = ec.message();
onResponse(res_);
}

void on_connect(boost::beast::error_code ec,
boost::asio::ip::tcp::resolver::results_type::endpoint_type) {
if (ec) return fail(ec, "connect");

// Set a timeout on the operation
stream_.expires_after(std::chrono::seconds(30));

// Send the HTTP request to the remote host
boost::beast::http::async_write(
stream_, req_,
boost::beast::bind_front_handler(&session::on_write,
shared_from_this()));
}

void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);

if (ec) return fail(ec, "write");

// Receive the HTTP response
boost::beast::http::async_read(stream_, buffer_, res_,
boost::beast::bind_front_handler(
&session::on_read, shared_from_this()));
}

void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);

if (ec) return fail(ec, "read");

// Gracefully close the socket
stream_.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);

// not_connected happens sometimes so don't bother reporting it.
if (ec && ec != boost::beast::errc::not_connected)
return fail(ec, "shutdown");

// If we get here then the connection is closed gracefully
onResponse(res_);
}

std::function<void(const boost::beast::http::response<
boost::beast::http::string_body> &response)>
onResponse;
};

class SmaccHttpClient : public smacc::ISmaccClient {
public:
boost::optional<std::string> serverName;
boost::optional<int> timeout;

SmaccHttpClient()
: worker_guard_{boost::asio::make_work_guard(io_context_)},
initialized_{false} {}

SmaccHttpClient(const std::string &serverName, const int &timeout)
: worker_guard_{boost::asio::make_work_guard(io_context_)},
initialized_{false} {
this->serverName = serverName;
this->timeout = timeout;
}

~SmaccHttpClient() {
worker_guard_.reset();
tcp_connection_runner_.join();
}

template <typename T>
boost::signals2::connection onResponseReceived(
void (T::*callback)(const boost::beast::http::response<
boost::beast::http::string_body> &),
T *object) {
return this->getStateMachine()->createSignalConnection(onResponseReceived_,
callback, object);
}

template <typename TOrthogonal, typename TSourceObject>
void onOrthogonalAllocation() {}

virtual void initialize() {
if (!initialized_) {
if (!timeout) timeout = 2000; // 2s timeout default
if (!serverName) {
ROS_ERROR("Server URL not set, skipping initialisation");
} else {
ROS_INFO_STREAM("[" << this->getName()
<< "] Initialising HTTP client for " << serverName);
tcp_connection_runner_ = std::thread{[&]() { io_context_.run(); }};
initialized_ = true;
}
}
}

void makeRequest(const std::string &path) {
std::make_shared<session>(io_context_, runSignal)
->run(serverName.get(), "80", path, 11);
}

private:
smacc::SmaccSignal<void(
const boost::beast::http::response<boost::beast::http::string_body> &)>
onResponseReceived_;

boost::asio::io_context io_context_;
boost::asio::executor_work_guard<decltype(io_context_)::executor_type>
worker_guard_;
std::thread tcp_connection_runner_;

bool initialized_;

std::function<void(const boost::beast::http::response<
boost::beast::http::string_body> &response)>
runSignal{[&](const boost::beast::http::response<
boost::beast::http::string_body> &response) {
onResponseReceived_(response);
}};
};
} // namespace client_bases
} // namespace smacc
114 changes: 114 additions & 0 deletions smacc_sm_reference_library/sm_atomic_http/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Changelog for package sm_atomic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Forthcoming
-----------

* creating feature smacc_runtime test
* Merge branch 'master' into melodic-devel
* adding xterm dependency to examples
* Final CbTimer Push
* Merge branch 'master' into melodic-devel
* fixed Doxygen Client Namespaces
* refactoring client namespaces names
* Merge branch 'master' into melodic-devel
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* sm_atomic diagrams
* Update README.md
* Update README.md
* Update README.md
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Improved sm_atomic
* more formatting & commenting
* renaming runtimeConfiguration to runtimeConfigure
* pushing renaming of runtimeConfigure
* more on smacc behaviors
* Changed static_configure to configure_orthogonal
* unpushed code
* Replaced all smacc::transition's with Transition
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* Update README.md
* Adding sm images
* Update README.md
* Update README.md
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* adding launch file to sm_atomic
* Renaming transition
* sm dance bot and refactoring namings
* namespaces iteration 3, orthogonal recurrent pattern, more tsts on sm_dance_bot_2
* more refactoring and renaming examples and namespaces
* progressing in the API and testing timers and callbacks
* ros timer client behaviors, refactoring sm_atomic and logic unit hierarchy. other improvements on rosout
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Create README.md
* more naming refactoring in move_base_z_client
* renaming move_base client to ClMoveBaseZ (zorro)
* renaming orthogonals
* more on refactoring renaming
* adding object tagging to clients, specifically action clients
* renaming consistently SmAtomic
* testing countdown logic unit more refactoring and analyzing propagating issue
* Contributors: Brett Aldrich, Pablo Iñigo Blasco, Pabo Iñigo Blasco, Unknown, Víctor Ferrer García, [email protected], brettpac, pablo.inigo.blasco, reelrbtx

* creating feature smacc_runtime test
* Merge branch 'master' into melodic-devel
* adding xterm dependency to examples
* Final CbTimer Push
* Merge branch 'master' into melodic-devel
* fixed Doxygen Client Namespaces
* refactoring client namespaces names
* Merge branch 'master' into melodic-devel
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* sm_atomic diagrams
* Update README.md
* Update README.md
* Update README.md
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Improved sm_atomic
* more formatting & commenting
* renaming runtimeConfiguration to runtimeConfigure
* pushing renaming of runtimeConfigure
* more on smacc behaviors
* Changed static_configure to configure_orthogonal
* unpushed code
* Replaced all smacc::transition's with Transition
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* Update README.md
* Adding sm images
* Update README.md
* Update README.md
* Merge branch 'master' of https://github.com/reelrbtx/SMACC
* Update README.md
* adding launch file to sm_atomic
* Renaming transition
* sm dance bot and refactoring namings
* namespaces iteration 3, orthogonal recurrent pattern, more tsts on sm_dance_bot_2
* more refactoring and renaming examples and namespaces
* progressing in the API and testing timers and callbacks
* ros timer client behaviors, refactoring sm_atomic and logic unit hierarchy. other improvements on rosout
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Update README.md
* Create README.md
* more naming refactoring in move_base_z_client
* renaming move_base client to ClMoveBaseZ (zorro)
* renaming orthogonals
* more on refactoring renaming
* adding object tagging to clients, specifically action clients
* renaming consistently SmAtomic
* testing countdown logic unit more refactoring and analyzing propagating issue
* Contributors: Brett Aldrich, Pablo Iñigo Blasco, Pabo Iñigo Blasco, Unknown, Víctor Ferrer García, [email protected], brettpac, pablo.inigo.blasco, reelrbtx
51 changes: 51 additions & 0 deletions smacc_sm_reference_library/sm_atomic_http/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.11.0)
project(sm_atomic_http)

## Find catkin macros and libraries
find_package(catkin REQUIRED smacc ros_timer_client)

###################################
## catkin specific configuration ##
###################################
catkin_package()

###########
## Build ##
###########
set(CMAKE_CXX_STANDARD 14)
add_compile_options(-std=c++11) #workaround for ubuntu 16.04, to extinguish

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
include
${catkin_INCLUDE_DIRS}
)

## Declare a C++ executable
add_executable(${PROJECT_NAME}_node src/sm_atomic_http_node.cpp)

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
)

#############
## Install ##
#############

## Mark executables for installation
install(TARGETS ${PROJECT_NAME}_node
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

## Mark other files for installation (e.g. launch and config files, etc.)
install(FILES
launch/sm_atomic.launch
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
)

install(DIRECTORY
config/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config
)
39 changes: 39 additions & 0 deletions smacc_sm_reference_library/sm_atomic_http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<h2>State Machine Diagram</h2>
<img src="https://github.com/reelrbtx/SMACC/blob/master/smacc_sm_reference_library/sm_atomic/docs/smacc_state_machine_20200207-004740.dot.svg" width="950" align="center" border="10"/>

<h2>Description</h2> A completely minimal state machine example.<br></br>
<a href="https://reelrbtx.github.io/SMACC_Documentation/master/html/namespacesm__atomic.html">Doxygen Namespace & Class Reference</a>

<h2>Build Instructions</h2>
Before you build, make sure you've installed all the dependencies...

```
rosdep install --from-paths src --ignore-src -r -y
```

Then you build with either catkin build or catkin make...

```
catkin build
```
<h2>Operating Instructions</h2>
After you build, remember to source the proper devel folder...

```
source ~/catkin_ws/devel/setup.bash
```

And then run the launch file...

```
roslaunch sm_atomic sm_atomic.launch
```

<h2>Viewer Instructions</h2>
If you have the SMACC Viewer installed then type...

```
rosrun smacc_viewer smacc_viewer_node.py
```

If you don't have the SMACC Viewer installed, click <a href="http://smacc.ninja/smacc-viewer/">here</a> for instructions.
Loading