Skip to content

Commit

Permalink
arduino messaging and responding to client requests
Browse files Browse the repository at this point in the history
  • Loading branch information
catink123 committed Jan 12, 2024
1 parent ef5ba7b commit a85b7c5
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 18 deletions.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ add_executable(
websocket_session.cpp
json_message.hpp
json_message.cpp
arduino_messenger.hpp
arduino_messenger.cpp
)

set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
Expand Down
93 changes: 93 additions & 0 deletions src/arduino_messenger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "arduino_messenger.hpp"

arduino_messenger::arduino_messenger(
net::io_context& io,
std::string_view device_name,
unsigned int baud_rate
) : com(io)
{
com.open(std::string(device_name));
com.set_option(net::serial_port_base::baud_rate(baud_rate));
}

void arduino_messenger::run() {
// read from COM-port indefinitely
do_read();

// try to empty outgoing message queue
do_write();
}

void arduino_messenger::do_read() {
net::async_read_until(com, buffer, '%',
std::bind(
&arduino_messenger::on_read,
shared_from_this(),
net::placeholders::error,
net::placeholders::bytes_transferred
)
);
}

void arduino_messenger::on_read(
const boost::system::error_code& ec,
std::size_t bytes_transferred
) {
if (ec || bytes_transferred == 0) {
return;
}

auto buffer_data = buffer.data();

std::string message(reinterpret_cast<const char*>(buffer_data.data()), buffer_data.size());

incoming_message_queue.push(json_message::parse_message(message));
}

void arduino_messenger::do_write() {
if (!outgoing_message_queue.empty()) {
outgoing_message_buffer = outgoing_message_queue.front().dump_message() + '%';
outgoing_message_queue.pop();

std::cout << "Sending message: " << outgoing_message_buffer << std::endl;

net::async_write(
com,
net::buffer(outgoing_message_buffer),
beast::bind_front_handler(
&arduino_messenger::on_write,
shared_from_this()
)
);
}
else {
net::post(
com.get_executor(),
std::bind(
&arduino_messenger::do_write,
shared_from_this()
)
);
}
}

void arduino_messenger::on_write(
const boost::system::error_code& ec,
std::size_t bytes_transferred
) {
boost::ignore_unused(bytes_transferred);

outgoing_message_buffer.clear();

if (ec) {
std::cerr << "Couldn't write to COM-port: " << ec.message() << std::endl;
return;
}

do_write();
}

void arduino_messenger::send_message(json_message message) {
std::lock_guard lock(omq_mutex);
outgoing_message_queue.push(message);
}
44 changes: 44 additions & 0 deletions src/arduino_messenger.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef ARDUINO_MESSENGER_HPP
#define ARDUINO_MESSENGER_HPP

#include "common.hpp"
#include <boost/asio/serial_port.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/read_until.hpp>
#include "json_message.hpp"
#include <queue>
#include <thread>

class arduino_messenger : public std::enable_shared_from_this<arduino_messenger> {
net::serial_port com;
net::streambuf buffer;

std::queue<json_message> outgoing_message_queue;
std::mutex omq_mutex;

std::string outgoing_message_buffer;

public:
std::queue<json_message> incoming_message_queue;
std::mutex imq_mutex;

arduino_messenger(
net::io_context& io,
std::string_view device_name,
unsigned int baud_rate = 9600
);

void send_message(json_message message);

void run();

private:
void do_read();
void do_write();
void on_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
void on_write(const boost::system::error_code& ec, std::size_t bytes_transferred);
};

#endif
9 changes: 6 additions & 3 deletions src/http_listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
http_listener::http_listener(
net::io_context& ioc,
tcp::endpoint endpoint,
const std::shared_ptr<const std::string>& doc_root
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<arduino_messenger> arduino_connection
) : ioc(ioc),
acceptor(net::make_strand(ioc)),
doc_root(doc_root)
doc_root(doc_root),
arduino_connection(arduino_connection)
{
beast::error_code ec;

Expand Down Expand Up @@ -59,7 +61,8 @@ void http_listener::on_accept(beast::error_code ec, tcp::socket socket) {

std::make_shared<http_session>(
std::move(socket),
doc_root
doc_root,
arduino_connection
)->run();

do_accept();
Expand Down
5 changes: 4 additions & 1 deletion src/http_listener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@
#include <string>
#include "common.hpp"
#include "http_session.hpp"
#include "arduino_messenger.hpp"

class http_listener : public std::enable_shared_from_this<http_listener> {
net::io_context& ioc;
tcp::acceptor acceptor;
std::shared_ptr<const std::string> doc_root;
std::shared_ptr<arduino_messenger> arduino_connection;

public:
http_listener(
net::io_context& ioc,
tcp::endpoint endpoint,
const std::shared_ptr<const std::string>& doc_root
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<arduino_messenger> arduino_connection
);

void run();
Expand Down
9 changes: 6 additions & 3 deletions src/http_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

http_session::http_session(
tcp::socket&& socket,
const std::shared_ptr<const std::string>& doc_root
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<arduino_messenger> arduino_connection
) : stream(std::move(socket)),
doc_root(doc_root)
doc_root(doc_root),
arduino_connection(arduino_connection)
{
static_assert(queue_limit > 0, "queue limit must be non-zero and positive");
response_queue.reserve(queue_limit);
Expand Down Expand Up @@ -57,7 +59,8 @@ void http_session::on_read(
if (websocket::is_upgrade(parser->get())) {
// create a new websocket session, moving the socket and request into it
std::make_shared<websocket_session>(
stream.release_socket()
stream.release_socket(),
arduino_connection
)->do_accept(parser->release());
return;
}
Expand Down
6 changes: 5 additions & 1 deletion src/http_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <string>
#include <chrono>
#include "websocket_session.hpp"
#include "arduino_messenger.hpp"

beast::string_view mime_type(
beast::string_view path
Expand All @@ -29,6 +30,8 @@ class http_session : public std::enable_shared_from_this<http_session> {
beast::flat_buffer buffer;
std::shared_ptr<const std::string> doc_root;

std::shared_ptr<arduino_messenger> arduino_connection;

// a queue to prevent overload
static constexpr std::size_t queue_limit = 16;
std::vector<http::message_generator> response_queue;
Expand All @@ -38,7 +41,8 @@ class http_session : public std::enable_shared_from_this<http_session> {
public:
http_session(
tcp::socket&& socket,
const std::shared_ptr<const std::string>& doc_root
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<arduino_messenger> arduino_connection
);

void run();
Expand Down
9 changes: 9 additions & 0 deletions src/json_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,13 @@ json_message json_message::parse_message(
json_message::str_to_type(parsed_json["type"].get<std::string>()),
parsed_json["payload"]
);
}

std::string json_message::dump_message() const {
nlohmann::json json = {
{ "type", type_to_str(type) },
{ "payload", payload }
};

return json.dump();
}
5 changes: 3 additions & 2 deletions src/json_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ class json_message {
};

MessageType type;
nlohmann::json::value_t payload;
nlohmann::json payload;

json_message(
const MessageType type,
nlohmann::json::value_t payload
nlohmann::json payload
) : type(type), payload(payload) {}

static std::string type_to_str(const MessageType& type);
Expand All @@ -40,6 +40,7 @@ class json_message {
);

static json_message parse_message(const std::string_view str);
std::string dump_message() const;
};

#endif
12 changes: 11 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@ const auto THREAD_COUNT = 8;
int main() {
net::io_context ioc{THREAD_COUNT};

auto arduino_connection =
std::make_shared<arduino_messenger>(
ioc,
"COM7",
115200
);

arduino_connection->run();

std::make_shared<http_listener>(
ioc,
tcp::endpoint{ADDRESS, PORT},
DOC_ROOT
DOC_ROOT,
arduino_connection
)->run();

std::cout << "Server started at " << ADDRESS << ":" << PORT << "." << std::endl;
Expand Down
27 changes: 21 additions & 6 deletions src/websocket_session.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "websocket_session.hpp"

websocket_session::websocket_session(
tcp::socket&& socket
) : ws(std::move(socket)) {}
tcp::socket&& socket,
std::shared_ptr<arduino_messenger> arduino_connection
) : ws(std::move(socket)),
arduino_connection(arduino_connection)
{}

void websocket_session::on_accept(beast::error_code ec) {
if (ec) {
Expand All @@ -29,6 +32,20 @@ void websocket_session::do_read() {
}

void websocket_session::do_write() {
// empty arduino messages
{
std::lock_guard lock(arduino_connection->imq_mutex);
auto& queue = arduino_connection->incoming_message_queue;
while (!queue.empty()) {
auto message = queue.front();
queue.pop();

if (message.type == json_message::QueryStateResult) {
queue_message(message.dump_message());
}
}
}

// if there is something to send, do it
if (write_queue.size() > 0) {
write_buffer = write_queue.front();
Expand Down Expand Up @@ -105,10 +122,8 @@ void websocket_session::handle_message(std::string_view message) {
try {
auto parsed_msg = json_message::parse_message(message);

if (parsed_msg.type == json_message::QueryState)
queue_message(json_message::create_message(json_message::QueryStateResult));
if (parsed_msg.type == json_message::ChangeState)
queue_message(json_message::create_message(json_message::Text, "changing of state not implemented!"));
if (parsed_msg.type == json_message::QueryState || parsed_msg.type == json_message::ChangeState)
arduino_connection->send_message(parsed_msg);
}
catch (...) {}
}
7 changes: 6 additions & 1 deletion src/websocket_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
#include <queue>
#include <thread>
#include "json_message.hpp"
#include "arduino_messenger.hpp"

class websocket_session : public std::enable_shared_from_this<websocket_session> {
websocket::stream<beast::tcp_stream> ws;
beast::flat_buffer buffer;
std::string write_buffer;
std::queue<std::string> write_queue;
std::shared_ptr<arduino_messenger> arduino_connection;

public:
explicit websocket_session(tcp::socket&& socket);
explicit websocket_session(
tcp::socket&& socket,
std::shared_ptr<arduino_messenger> arduino_connection
);

template<class Body, class Allocator>
void do_accept(
Expand Down

0 comments on commit a85b7c5

Please sign in to comment.