Skip to content

Commit

Permalink
multi-client support
Browse files Browse the repository at this point in the history
  • Loading branch information
catink123 committed Jan 16, 2024
1 parent b5a233c commit e7d9dc3
Show file tree
Hide file tree
Showing 14 changed files with 181 additions and 48 deletions.
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ add_executable(
arduino_messenger.hpp
arduino_messenger.cpp
auth.hpp
)
common_state.hpp
common_state.cpp
)

set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)

Expand Down
17 changes: 12 additions & 5 deletions src/arduino_messenger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ void arduino_messenger::on_read(
const boost::system::error_code& ec,
std::size_t bytes_transferred
) {
// this doesn't work
if (ec && !com.is_open()) {
std::cout << "COM-port closed." << std::endl;
}

if (ec || bytes_transferred == 0) {
return;
}
Expand All @@ -64,8 +69,6 @@ void arduino_messenger::on_read(

buffer.consume(bytes_transferred);

std::cout << "Read message: " << message << std::endl;

std::lock_guard lock(imq_mutex);
incoming_message_queue.push(json_message::parse_message(message));

Expand All @@ -77,8 +80,6 @@ void arduino_messenger::do_write() {
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),
Expand Down Expand Up @@ -108,7 +109,13 @@ void arduino_messenger::on_write(
outgoing_message_buffer.clear();

if (ec) {
std::cerr << "Couldn't write to COM-port: " << ec.message() << std::endl;
// this doesn't work, else-clause called
if (!com.is_open()) {
std::cerr << "COM-port closed." << std::endl;
}
else {
std::cerr << "Couldn't write to COM-port: " << ec.message() << std::endl;
}
return;
}

Expand Down
21 changes: 13 additions & 8 deletions src/arduino_program/gate_control/gate_control.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,21 @@ const int capacity = 100;
StaticJsonDocument<capacity> doc;
bool state = false;

void send_state() {
DynamicJsonDocument dyn_doc(100);
dyn_doc[TYPE] = QUERY_STATE_RESULT;
dyn_doc[PAYLOAD] = state;

String msg;
serializeJson(dyn_doc, msg);

Serial.write(msg.c_str());
}

void change_state(bool new_state, bool persist_change = true) {
if (persist_change) {
state = new_state;
send_state();
}

if (new_state) {
Expand Down Expand Up @@ -42,14 +54,7 @@ void loop() {
change_state(false);
}
} else if (doc[TYPE] == QUERY_STATE) {
DynamicJsonDocument dyn_doc(100);
dyn_doc[TYPE] = QUERY_STATE_RESULT;
dyn_doc[PAYLOAD] = state;

String msg;
serializeJson(dyn_doc, msg);

Serial.write(msg.c_str());
send_state();
} else {
Serial.write("{\"type\": \"error\", \"payload\": \"unknown_command\"}");
}
Expand Down
42 changes: 35 additions & 7 deletions src/client/control/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,54 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gate Control</title>
<style>
.state {
border-radius: 50px;
padding: 2px 5px;
}

.state.unknown {
background: yellow;
}

.state.raised {
background: lime;
}

.state.lowered {
background: red;
color: white;
}
</style>
</head>
<body>
<h1>Control Page</h1>

<button disabled id="send-query">Query State</button>
<button disabled id="send-up">Send Raise</button>
<button disabled id="send-down">Send Lower</button>

<p>Current state: <span class="state unknown">Unknown</span></p>

<script>
let ws = new WebSocket("ws://" + location.host);
const stateSpan = document.querySelector("span.state");

ws.addEventListener('message', e => {
let msg = JSON.parse(e.data);
if (msg.type == "text") alert(`Text from Server: ${msg.payload}`);
if (msg.type == "query_state_result") alert(`Current Gate State: ${msg.payload}`);
if (msg.type == "availability") alert(`Availability: ${msg.payload}`);
let msg = JSON.parse(e.data);
if (msg.type == "text") alert(`Text from Server: ${msg.payload}`);
if (msg.type == "query_state_result") {
if (msg.payload == true) {
stateSpan.className = "state raised";
stateSpan.innerText = "Raised";
} else if (msg.payload == false) {
stateSpan.className = "state lowered";
stateSpan.innerText = "Lowered";
}
}
if (msg.type == "availability") alert(`Availability: ${msg.payload}`);
});

function setListener(selector, type, listener) {
function setListener(selector, type, listener) {
document.querySelector(selector).addEventListener(type, listener);
}

Expand All @@ -32,7 +61,6 @@ <h1>Control Page</h1>

ws.addEventListener('open', () => {
document.querySelectorAll('button').forEach(btn => btn.disabled = false);
bindMessage("#send-query", { type: "query_state", payload: null });
bindMessage("#send-up", { type: "change_state", payload: true });
bindMessage("#send-down", { type: "change_state", payload: false });
});
Expand Down
54 changes: 54 additions & 0 deletions src/common_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "common_state.hpp"

common_state::common_state(
net::io_context& io,
std::shared_ptr<arduino_messenger> messenger
) : io(io.get_executor()), messenger(messenger) {}

void common_state::add_session(
std::shared_ptr<websocket_session> session
) {
sessions.push_back(session);
messenger->send_message(json_message(json_message::QueryState));
}

void common_state::run() {
update();
}

void common_state::update() {
if (sessions.size() > 0) {
// remove all dead sessions
sessions.erase(
std::remove_if(
sessions.begin(),
sessions.end(),
[](std::weak_ptr<websocket_session>& s) { return s.expired(); }
),
sessions.end()
);

// update all sessions with new state
if (!messenger->incoming_message_queue.empty()) {
json_message message = messenger->incoming_message_queue.front();
if (message.type == json_message::QueryStateResult) {
for (auto& session : sessions) {
if (std::shared_ptr<websocket_session> sp = session.lock()) {
sp->queue_message(
messenger->incoming_message_queue.front().dump_message()
);
}
}
messenger->incoming_message_queue.pop();
}
}
}

net::post(
io,
std::bind(
&common_state::update,
shared_from_this()
)
);
}
27 changes: 27 additions & 0 deletions src/common_state.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef COMMON_STATE_HPP
#define COMMON_STATE_HPP

#include <memory>
#include <vector>
#include "websocket_session.hpp"
#include <boost/asio/any_io_executor.hpp>
#include "arduino_messenger.hpp"

class common_state : public std::enable_shared_from_this<common_state> {
net::any_io_executor io;
std::vector<std::weak_ptr<websocket_session>> sessions;
std::shared_ptr<arduino_messenger> messenger;

public:
common_state(
net::io_context& io,
std::shared_ptr<arduino_messenger> messenger
);

void add_session(std::shared_ptr<websocket_session> session);

void run();
void update();
};

#endif
5 changes: 4 additions & 1 deletion src/http_listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ http_listener::http_listener(
net::io_context& ioc,
tcp::endpoint endpoint,
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<arduino_messenger> arduino_connection
std::shared_ptr<common_state> comstate,
std::shared_ptr<arduino_messenger> arduino_connection
) : ioc(ioc),
acceptor(net::make_strand(ioc)),
doc_root(doc_root),
comstate(comstate),
arduino_connection(arduino_connection)
{
beast::error_code ec;
Expand Down Expand Up @@ -62,6 +64,7 @@ void http_listener::on_accept(beast::error_code ec, tcp::socket socket) {
std::make_shared<http_session>(
std::move(socket),
doc_root,
comstate,
arduino_connection
)->run();

Expand Down
6 changes: 4 additions & 2 deletions src/http_listener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@
#include <string>
#include "common.hpp"
#include "http_session.hpp"
#include "arduino_messenger.hpp"
#include "common_state.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<common_state> comstate;
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,
std::shared_ptr<arduino_messenger> arduino_connection
std::shared_ptr<common_state> comstate,
std::shared_ptr<arduino_messenger> arduino_connection
);

void run();
Expand Down
15 changes: 11 additions & 4 deletions src/http_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
http_session::http_session(
tcp::socket&& socket,
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<common_state> comstate,
std::shared_ptr<arduino_messenger> arduino_connection
) : stream(std::move(socket)),
doc_root(doc_root),
comstate(comstate),
arduino_connection(arduino_connection)
{
static_assert(queue_limit > 0, "queue limit must be non-zero and positive");
Expand Down Expand Up @@ -58,10 +60,15 @@ void http_session::on_read(
// if the request is a WebSocket Upgrade
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(),
arduino_connection
)->do_accept(parser->release());
auto session =
std::make_shared<websocket_session>(
stream.release_socket(),
arduino_connection
);

session->do_accept(parser->release());
comstate->add_session(session);

return;
}

Expand Down
3 changes: 3 additions & 0 deletions src/http_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <chrono>
#include "websocket_session.hpp"
#include "arduino_messenger.hpp"
#include "common_state.hpp"
#include "auth.hpp"

namespace base64 = beast::detail::base64;
Expand Down Expand Up @@ -40,6 +41,7 @@ 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<common_state> comstate;
std::shared_ptr<arduino_messenger> arduino_connection;

// a queue to prevent overload
Expand All @@ -52,6 +54,7 @@ class http_session : public std::enable_shared_from_this<http_session> {
http_session(
tcp::socket&& socket,
const std::shared_ptr<const std::string>& doc_root,
std::shared_ptr<common_state> comstate,
std::shared_ptr<arduino_messenger> arduino_connection
);

Expand Down
2 changes: 1 addition & 1 deletion src/json_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class json_message {

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

static std::string type_to_str(const MessageType& type);
Expand Down
10 changes: 10 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <thread>
#include "common.hpp"
#include "http_listener.hpp"
#include "common_state.hpp"

const auto ADDRESS = net::ip::make_address_v4("0.0.0.0");
const auto PORT = static_cast<unsigned short>(80);
Expand All @@ -30,10 +31,19 @@ int main(int argc, char* argv[]) {

arduino_connection->run();

auto comstate =
std::make_shared<common_state>(
ioc,
arduino_connection
);

comstate->run();

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

Expand Down

0 comments on commit e7d9dc3

Please sign in to comment.