From 664e821aca79cb18fe2b07ed752c49889c88504b Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 27 Jun 2025 11:29:26 +0200 Subject: [PATCH 01/15] opt: rem redundant deserialize method --- src/decoder.h | 2 +- src/rpclite_utils.h | 5 ----- src/wrapper.h | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/decoder.h b/src/decoder.h index 3f08d76..ef45615 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -56,7 +56,7 @@ class RpcDecoder { template bool get_response(const int msg_id, RType& result, RpcError& error) { - if (!packet_incoming() || packet_type()!=RESP_MSG) return false; + if (packet_type()!=RESP_MSG) return false; MsgPack::Unpacker unpacker; diff --git a/src/rpclite_utils.h b/src/rpclite_utils.h index 3de5a5a..211a97f 100644 --- a/src/rpclite_utils.h +++ b/src/rpclite_utils.h @@ -134,11 +134,6 @@ deserialize_tuple(MsgPack::Unpacker& unpacker, std::tuple& out) { return deserialize_tuple(unpacker, out); } -template -inline bool deserialize_all(MsgPack::Unpacker& unpacker, std::tuple& values) { - return deserialize_tuple(unpacker, values); -} - // Helper to invoke a function with a tuple of arguments template inline auto invoke_with_tuple(F&& f, Tuple&& t, arx::stdx::index_sequence) diff --git a/src/wrapper.h b/src/wrapper.h index 9240615..9cda668 100644 --- a/src/wrapper.h +++ b/src/wrapper.h @@ -80,7 +80,7 @@ class RpcFunctionWrapper>: public IFunctionWrapper { handle_call(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) { //unpacker not ready if deserialization fails at this point std::tuple args; - if (!deserialize_all(unpacker, args)) return false; + if (!deserialize_tuple(unpacker, args)) return false; MsgPack::object::nil_t nil; invoke_with_tuple(_func, args, arx::stdx::make_index_sequence{}); packer.serialize(nil, nil); @@ -92,7 +92,7 @@ class RpcFunctionWrapper>: public IFunctionWrapper { handle_call(MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) { //unpacker not ready if deserialization fails at this point std::tuple args; - if (!deserialize_all(unpacker, args)) return false; + if (!deserialize_tuple(unpacker, args)) return false; MsgPack::object::nil_t nil; R out = invoke_with_tuple(_func, args, arx::stdx::make_index_sequence{}); packer.serialize(nil, out); From b66acbbbcdc07cae6a30477eeb2bdc324515883e Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 3 Jul 2025 09:37:30 +0200 Subject: [PATCH 02/15] mod: server refactoring read-process-send --- src/server.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/src/server.h b/src/server.h index d949f4c..584fe91 100644 --- a/src/server.h +++ b/src/server.h @@ -13,10 +13,9 @@ #include "SerialTransport.h" #define MAX_CALLBACKS 100 +#define RPC_BUFFER_SIZE 1024 class RPCServer { - RpcDecoder<>* decoder = nullptr; - RpcFunctionDispatcher dispatcher; public: RPCServer(ITransport& t) : decoder(&RpcDecoderManager<>::getDecoder(t)) {} @@ -33,10 +32,83 @@ class RPCServer { void run() { decoder->decode(); - decoder->process_requests(dispatcher); + read_rpc(); + process_request(); + send_response(); //delay(1); } +protected: + void read_rpc() { + _rpc_size = decoder->read_rpc(_rpc_buffer, RPC_BUFFER_SIZE); + } + + void process_request() { + if (_rpc_size == 0) return; + + MsgPack::Unpacker unpacker; + + unpacker.clear(); + if (!unpacker.feed(_rpc_buffer, _rpc_size)) { + _rpc_size = 0; // Reset size on error + return; // Error in unpacking + } + + int msg_type; + int msg_id; + MsgPack::str_t method; + MsgPack::arr_size_t req_size; + + if (!unpacker.deserialize(req_size, msg_type)) { + reset_rpc(); + return; // Header not unpackable + } + + if (msg_type == CALL_MSG && req_size.size() == REQUEST_SIZE) { + if (!unpacker.deserialize(msg_id, method)) { + reset_rpc(); + return; // Method not unpackable + } + } else if (msg_type == NOTIFY_MSG && req_size.size() == NOTIFY_SIZE) { + if (!unpacker.deserialize(method)) { + reset_rpc(); + return; // Method not unpackable + } + } else { + reset_rpc(); + return; // Invalid request size/type + } + + _rpc_type = msg_type; + + MsgPack::arr_size_t resp_size(RESPONSE_SIZE); + res_packer.clear(); + if (msg_type == CALL_MSG) res_packer.serialize(resp_size, RESP_MSG, msg_id); + + dispatcher.call(method, unpacker, res_packer); + reset_rpc(); + + } + + void send_response() { + if (res_packer.size() > 0) { + decoder->send(reinterpret_cast(res_packer.data()), res_packer.size()); + } + } + +private: + RpcDecoder<>* decoder = nullptr; + RpcFunctionDispatcher dispatcher; + uint8_t _rpc_buffer[RPC_BUFFER_SIZE]; + size_t _rpc_size = 0; + uint8_t _rpc_type = NO_MSG; + MsgPack::Packer res_packer; + + void reset_rpc() { + _rpc_size = 0; + _rpc_type = NO_MSG; + } + }; #endif //RPCLITE_SERVER_H From ef0056dfccbe3f7cfa8cc3e3b9f2a516fa33ff79 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 3 Jul 2025 14:49:20 +0200 Subject: [PATCH 03/15] feat: recode with agnostic packet parsing --- src/client.h | 38 +++++++-- src/decoder.h | 203 +++++++++++++++----------------------------- src/dispatcher.h | 2 +- src/rpclite_utils.h | 44 ++++++++++ src/server.h | 13 +-- 5 files changed, 152 insertions(+), 148 deletions(-) diff --git a/src/client.h b/src/client.h index b85630d..3275aa9 100644 --- a/src/client.h +++ b/src/client.h @@ -11,6 +11,7 @@ class RPCClient { RpcDecoder<>* decoder = nullptr; + int _waiting_msg_id; public: RpcError lastError; @@ -31,23 +32,46 @@ class RPCClient { template bool call(const MsgPack::str_t method, RType& result, Args&&... args) { - int msg_id; - if (!decoder->send_call(CALL_MSG, method, msg_id, std::forward(args)...)){ + if(!send_rpc(method, std::forward(args)...)) { + lastError.code = GENERIC_ERR; + lastError.traceback = "Failed to send RPC call"; + return false; } - RpcError error; // blocking call - while (!decoder->get_response(msg_id, result, error)){ + while (!get_response(result)){ decoder->decode(); //delay(1); } - lastError.code = error.code; - lastError.traceback = error.traceback; + return (lastError.code == NO_ERR); + + } + +protected: + template + bool send_rpc(const MsgPack::str_t method, Args&&... args) { + int msg_id; + if (decoder->send_call(CALL_MSG, method, msg_id, std::forward(args)...)) { + _waiting_msg_id = msg_id; + return true; + } + return false; + } - return (error.code == NO_ERR); + template + bool get_response(RType& result) { + RpcError tmp_error; + decoder->decode(); + if (decoder->get_response(_waiting_msg_id, result, tmp_error)) { + lastError.code = tmp_error.code; + lastError.traceback = tmp_error.traceback; + return true; + } + return false; } + }; #endif //RPCLITE_CLIENT_H diff --git a/src/decoder.h b/src/decoder.h index ef45615..abd5f17 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -8,14 +8,9 @@ using namespace RpcUtils::detail; -#define NO_MSG -1 -#define CALL_MSG 0 -#define RESP_MSG 1 -#define NOTIFY_MSG 2 -#define REQUEST_SIZE 4 -#define RESPONSE_SIZE 4 -#define NOTIFY_SIZE 3 + +#define MIN_RPC_BYTES 4 #define MAX_BUFFER_SIZE 1024 #define CHUNK_SIZE 32 @@ -56,118 +51,45 @@ class RpcDecoder { template bool get_response(const int msg_id, RType& result, RpcError& error) { - if (packet_type()!=RESP_MSG) return false; + if (!packet_incoming() || packet_type()!=RESP_MSG) return false; MsgPack::Unpacker unpacker; + unpacker.clear(); - size_t bytes_checked = 0; + if (!unpacker.feed(_raw_buffer, get_packet_size())) return false; - while (bytes_checked < _bytes_stored) { - bytes_checked++; - unpacker.clear(); - if (!unpacker.feed(_raw_buffer, bytes_checked)) continue; - MsgPack::arr_size_t resp_size; - int resp_type; - int resp_id; - if (!unpacker.deserialize(resp_size, resp_type, resp_id)) continue; - if (resp_size.size() != RESPONSE_SIZE) continue; - if (resp_type != RESP_MSG) continue; - if (resp_id != msg_id) continue; - - MsgPack::object::nil_t nil; - if (unpacker.unpackable(nil)){ // No error - if (!unpacker.deserialize(nil, result)) continue; - } else { // RPC returned an error - if (!unpacker.deserialize(error, nil)) continue; - } - consume(bytes_checked); - return true; - } - return false; - } - - template - bool send_response(const int msg_id, const RpcError& error, const RType& result) { - MsgPack::Packer packer; - MsgPack::arr_size_t resp_size(RESPONSE_SIZE); - MsgPack::object::nil_t nil; + MsgPack::arr_size_t resp_size; + int resp_type; + int resp_id; - packer.clear(); - packer.serialize(resp_size, RESP_MSG, msg_id); + if (!unpacker.deserialize(resp_size, resp_type, resp_id)) return false; + if (resp_size.size() != RESPONSE_SIZE) return false; + if (resp_type != RESP_MSG) return false; + if (resp_id != msg_id) return false; - if (error.code == NO_ERR){ - packer.serialize(nil, result); - } else { - packer.serialize(error, nil); + MsgPack::object::nil_t nil; + if (unpacker.unpackable(nil)){ // No error + if (!unpacker.deserialize(nil, result)) return false; + } else { // RPC returned an error + if (!unpacker.deserialize(error, nil)) return false; } - return send(reinterpret_cast(packer.data()), packer.size()) == packer.size(); + consume(get_packet_size()); + return true; } - template - void process_requests(RpcFunctionDispatcher& dispatcher) { - if (_packet_type!=CALL_MSG && _packet_type!=NOTIFY_MSG) return; - - MsgPack::Unpacker unpacker; - MsgPack::Packer packer; - - size_t bytes_checked = 0; - - while (bytes_checked < _bytes_stored) { - bytes_checked++; - unpacker.clear(); - if (!unpacker.feed(_raw_buffer, bytes_checked)) continue; + bool send_response(const MsgPack::Packer& packer) { + return send(reinterpret_cast(packer.data()), packer.size()) == packer.size(); + } - int msg_type; - int msg_id; - MsgPack::str_t method; - MsgPack::arr_size_t req_size; - - if (!unpacker.deserialize(req_size, msg_type)) continue; - // todo HANDLE MALFORMED CLIENT REQ ERRORS - if ((req_size.size() == REQUEST_SIZE) && (msg_type == CALL_MSG)){ - if (!unpacker.deserialize(msg_id, method)) continue; - if (unpacker.size() < REQUEST_SIZE + 1) continue; // there must be at least 5 indices - } else if ((req_size.size() == NOTIFY_SIZE) && (msg_type == NOTIFY_MSG)) { - if (!unpacker.deserialize(method)) continue; - if (unpacker.size() < NOTIFY_SIZE + 1) continue; // there must be at least 4 indices - } else if ((req_size.size() == RESPONSE_SIZE) && (msg_type == RESP_MSG)) { // this should never happen but it's addressed to a client - break; - } else { - discard_packet(); - break; - } - // Headers unpacked - - MsgPack::arr_size_t resp_size(RESPONSE_SIZE); - packer.clear(); - if (msg_type == CALL_MSG) packer.serialize(resp_size, RESP_MSG, msg_id); - size_t headers_size = packer.size(); - - if (!dispatcher.call(method, unpacker, packer)) { - if (packer.size()==headers_size) { - // Call didn't go through bc parameters are not ready yet - continue; - } else { - // something went wrong the call raised an error or the client issued a malformed request - if (msg_type == CALL_MSG) { - send(reinterpret_cast(packer.data()), packer.size()); - } // if notification client will never know something went wrong - discard_packet(); // agnostic pop - break; - } - } else { - // all is well we can respond and pop the deserialized packet - if (msg_type == CALL_MSG){ - send(reinterpret_cast(packer.data()), packer.size()); - } - consume(bytes_checked); - break; - } + size_t get_request(uint8_t* buffer, size_t buffer_size) { + if (packet_type() != CALL_MSG && packet_type() != NOTIFY_MSG) { + return 0; // No RPC } + return pop_packet(buffer, buffer_size); } void decode(){ @@ -187,30 +109,11 @@ class RpcDecoder { void parse_packet(){ - if (packet_incoming() || _bytes_stored < 2){return;} - - MsgPack::Unpacker unpacker; - unpacker.clear(); - unpacker.feed(_raw_buffer, 2); - - MsgPack::arr_size_t elem_size; - int type; - if (unpacker.deserialize(elem_size, type)){ - _packet_type = type; - } - - } - - // Check if a packet is available - inline bool packet_incoming() const { return _packet_type >= CALL_MSG; } - - int packet_type() const {return _packet_type;} - - // Get the size of the next packet in the buffer (must be array contained, no other requirements) - size_t get_packet_size() { + if (packet_incoming()){return;} size_t bytes_checked = 0; size_t container_size; + int type; MsgPack::Unpacker unpacker; while (bytes_checked < _bytes_stored){ @@ -218,21 +121,33 @@ class RpcDecoder { unpacker.clear(); if (!unpacker.feed(_raw_buffer, bytes_checked)) continue; - if (unpackArray(unpacker, container_size)) { - return bytes_checked; + if (unpackTypedArray(unpacker, container_size, type)) { + + if (type != CALL_MSG && type != RESP_MSG && type != NOTIFY_MSG) { + consume(bytes_checked); + break; // Not a valid RPC type (could be type=WRONG_MSG) + } + + if ((type == CALL_MSG && container_size != REQUEST_SIZE) || (type == RESP_MSG && container_size != RESPONSE_SIZE) || (type == NOTIFY_MSG && container_size != NOTIFY_SIZE)) { + consume(bytes_checked); + break; // Not a valid RPC format + } + + _packet_type = type; + _packet_size = bytes_checked; } else { continue; } } - return 0; } - // Discard the next (array) packet in the buffer, returns the number of bytes consumed. - size_t discard_packet() { - return consume(get_packet_size()); - } + inline bool packet_incoming() const { return _packet_size >= MIN_RPC_BYTES; } + + inline int packet_type() const { return _packet_type; } + + size_t get_packet_size() const { return _packet_size;} inline size_t size() const {return _bytes_stored;} @@ -241,6 +156,7 @@ class RpcDecoder { uint8_t _raw_buffer[BufferSize]; size_t _bytes_stored = 0; int _packet_type = NO_MSG; + size_t _packet_size = 0; int _msg_id = 0; inline bool buffer_full() const { return _bytes_stored == BufferSize; } @@ -251,7 +167,27 @@ class RpcDecoder { return _transport.write(data, size); } - // Consume the first 'size' bytes of the buffer, shifting remaining data forward + size_t pop_packet(uint8_t* buffer, size_t buffer_size) { + + if (!packet_incoming()) return 0; + + size_t packet_size = get_packet_size(); + if (packet_size > buffer_size) return 0; + + for (size_t i = 0; i < packet_size; i++) { + buffer[i] = _raw_buffer[i]; + } + + reset_packet(); + return consume(packet_size); + } + + + void reset_packet() { + _packet_type = NO_MSG; + _packet_size = 0; + } + size_t consume(size_t size) { if (size > _bytes_stored) return 0; @@ -264,7 +200,6 @@ class RpcDecoder { } _bytes_stored = remaining_bytes; - _packet_type = NO_MSG; return size; } diff --git a/src/dispatcher.h b/src/dispatcher.h index f69eaec..2a0e1d5 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -40,7 +40,7 @@ class RpcFunctionDispatcher { } } - // handle not found + // handler not found MsgPack::object::nil_t nil; packer.serialize(RpcError(FUNCTION_NOT_FOUND_ERR, name), nil); return false; diff --git a/src/rpclite_utils.h b/src/rpclite_utils.h index 211a97f..5de473c 100644 --- a/src/rpclite_utils.h +++ b/src/rpclite_utils.h @@ -8,6 +8,15 @@ namespace RpcUtils { namespace detail { +#define WRONG_MSG -2 +#define NO_MSG -1 +#define CALL_MSG 0 +#define RESP_MSG 1 +#define NOTIFY_MSG 2 + +#define REQUEST_SIZE 4 +#define RESPONSE_SIZE 4 +#define NOTIFY_SIZE 3 /////////////////////////////////////// /// --- deserialization helpers --- /// @@ -15,6 +24,41 @@ namespace detail { inline bool unpackObject(MsgPack::Unpacker& unpacker); + +inline bool unpackTypedArray(MsgPack::Unpacker& unpacker, size_t& size, int& type) { + + if (!unpacker.isArray()) { + return false; // Not an array + } + + MsgPack::arr_size_t sz; + unpacker.deserialize(sz); + int rpc_type; + + size = 0; + for (size_t i=0; idecode(); - read_rpc(); + get_rpc(); process_request(); send_response(); //delay(1); } protected: - void read_rpc() { - _rpc_size = decoder->read_rpc(_rpc_buffer, RPC_BUFFER_SIZE); + void get_rpc() { + decoder->decode(); + if (_rpc_size > 0) return; // Already have a request + _rpc_size = decoder->get_request(_rpc_buffer, RPC_BUFFER_SIZE); } void process_request() { @@ -90,9 +91,9 @@ class RPCServer { } - void send_response() { + bool send_response() { if (res_packer.size() > 0) { - decoder->send(reinterpret_cast(res_packer.data()), res_packer.size()); + return decoder->send_response(res_packer); } } From 5dea8f6760798435406f150afff743e88db6c813 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 3 Jul 2025 15:48:15 +0200 Subject: [PATCH 04/15] fix: get_response not resetting packet --- src/client.h | 1 - src/decoder.h | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client.h b/src/client.h index 3275aa9..173ada1 100644 --- a/src/client.h +++ b/src/client.h @@ -40,7 +40,6 @@ class RPCClient { // blocking call while (!get_response(result)){ - decoder->decode(); //delay(1); } diff --git a/src/decoder.h b/src/decoder.h index abd5f17..a161479 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -3,7 +3,6 @@ #include "MsgPack.h" #include "transport.h" -#include "dispatcher.h" #include "rpclite_utils.h" using namespace RpcUtils::detail; @@ -56,7 +55,8 @@ class RpcDecoder { MsgPack::Unpacker unpacker; unpacker.clear(); - if (!unpacker.feed(_raw_buffer, get_packet_size())) return false; + size_t res_size = get_packet_size(); + if (!unpacker.feed(_raw_buffer, res_size)) return false; MsgPack::arr_size_t resp_size; int resp_type; @@ -74,7 +74,8 @@ class RpcDecoder { if (!unpacker.deserialize(error, nil)) return false; } - consume(get_packet_size()); + reset_packet(); + consume(res_size); return true; } From 7c0a7d00802eb55b4eb1134588ad7494bcfd78e4 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 3 Jul 2025 18:11:56 +0200 Subject: [PATCH 05/15] fix: notifications hang the server --- src/decoder.h | 7 +++---- src/server.h | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/decoder.h b/src/decoder.h index a161479..60521ec 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -7,8 +7,6 @@ using namespace RpcUtils::detail; - - #define MIN_RPC_BYTES 4 #define MAX_BUFFER_SIZE 1024 @@ -50,7 +48,7 @@ class RpcDecoder { template bool get_response(const int msg_id, RType& result, RpcError& error) { - if (!packet_incoming() || packet_type()!=RESP_MSG) return false; + if (!packet_incoming() || _packet_type!=RESP_MSG) return false; MsgPack::Unpacker unpacker; unpacker.clear(); @@ -86,7 +84,7 @@ class RpcDecoder { size_t get_request(uint8_t* buffer, size_t buffer_size) { - if (packet_type() != CALL_MSG && packet_type() != NOTIFY_MSG) { + if (_packet_type != CALL_MSG && _packet_type != NOTIFY_MSG) { return 0; // No RPC } @@ -136,6 +134,7 @@ class RpcDecoder { _packet_type = type; _packet_size = bytes_checked; + break; } else { continue; } diff --git a/src/server.h b/src/server.h index 1fa11ab..1c893aa 100644 --- a/src/server.h +++ b/src/server.h @@ -87,14 +87,22 @@ class RPCServer { if (msg_type == CALL_MSG) res_packer.serialize(resp_size, RESP_MSG, msg_id); dispatcher.call(method, unpacker, res_packer); - reset_rpc(); } bool send_response() { - if (res_packer.size() > 0) { - return decoder->send_response(res_packer); + if (_rpc_type == NO_MSG || res_packer.size() == 0) { + return true; // No response to send + } + + if (_rpc_type == NOTIFY_MSG) { + reset_rpc(); + return true; } + + reset_rpc(); + return decoder->send_response(res_packer); + } private: @@ -102,7 +110,7 @@ class RPCServer { RpcFunctionDispatcher dispatcher; uint8_t _rpc_buffer[RPC_BUFFER_SIZE]; size_t _rpc_size = 0; - uint8_t _rpc_type = NO_MSG; + int _rpc_type = NO_MSG; MsgPack::Packer res_packer; void reset_rpc() { From 2bfb4c7a868d59c62d5b4493116293f93c7e9a9e Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 3 Jul 2025 18:14:08 +0200 Subject: [PATCH 06/15] rem: auto comments --- examples/decoder_tests/DummyTransport.h | 4 ---- src/Arduino_RPClite.h | 4 ---- src/client.h | 4 ---- src/error.h | 3 --- src/server.h | 4 ---- src/transport.h | 4 ---- 6 files changed, 23 deletions(-) diff --git a/examples/decoder_tests/DummyTransport.h b/examples/decoder_tests/DummyTransport.h index dc3b3b7..6d53b0f 100644 --- a/examples/decoder_tests/DummyTransport.h +++ b/examples/decoder_tests/DummyTransport.h @@ -1,7 +1,3 @@ -// -// Created by lucio on 4/8/25. -// - #ifndef DUMMY_TRANSPORT_H #define DUMMY_TRANSPORT_H #include "transport.h" diff --git a/src/Arduino_RPClite.h b/src/Arduino_RPClite.h index 61b7d85..faf2eed 100644 --- a/src/Arduino_RPClite.h +++ b/src/Arduino_RPClite.h @@ -1,7 +1,3 @@ -// -// Created by lucio on 4/8/25. -// - #ifndef ARDUINO_RPCLITE_H #define ARDUINO_RPCLITE_H diff --git a/src/client.h b/src/client.h index 173ada1..b4e599c 100644 --- a/src/client.h +++ b/src/client.h @@ -1,7 +1,3 @@ -// -// Created by lucio on 4/8/25. -// - #ifndef RPCLITE_CLIENT_H #define RPCLITE_CLIENT_H #include "error.h" diff --git a/src/error.h b/src/error.h index 2e801d0..ea8d4be 100644 --- a/src/error.h +++ b/src/error.h @@ -1,6 +1,3 @@ -// Created by lucio on 4/25/25. -// - #ifndef RPCLITE_ERROR_H #define RPCLITE_ERROR_H diff --git a/src/server.h b/src/server.h index 1c893aa..96e247f 100644 --- a/src/server.h +++ b/src/server.h @@ -1,7 +1,3 @@ -// -// Created by lucio on 4/8/25. -// - #ifndef RPCLITE_SERVER_H #define RPCLITE_SERVER_H diff --git a/src/transport.h b/src/transport.h index b569740..5b50638 100644 --- a/src/transport.h +++ b/src/transport.h @@ -1,7 +1,3 @@ -// -// Created by lucio on 4/8/25. -// - #ifndef RPCLITE_TRANSPORT_H #define RPCLITE_TRANSPORT_H From cb1ace8e5196259db6d254c572cd1dae26fb39d6 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Tue, 8 Jul 2025 17:29:54 +0200 Subject: [PATCH 07/15] feat: DecoderManager.size # of active decoders. RPCServer.get_rpc as bool. warn: Stream constructors create multiple decoders on the same transport --- src/client.h | 1 + src/decoder_manager.h | 9 +++++++++ src/server.h | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/client.h b/src/client.h index b4e599c..f0fb529 100644 --- a/src/client.h +++ b/src/client.h @@ -14,6 +14,7 @@ class RPCClient { RPCClient(ITransport& t) : decoder(&RpcDecoderManager<>::getDecoder(t)) {} + // TODO This is problematic becasue 'new' makes different Transport objs and different transports make different decoders RPCClient(Stream& stream) { ITransport* transport = (ITransport*) new SerialTransport(stream); decoder = &RpcDecoderManager<>::getDecoder(*transport); diff --git a/src/decoder_manager.h b/src/decoder_manager.h index 8ba11c4..57f88bb 100644 --- a/src/decoder_manager.h +++ b/src/decoder_manager.h @@ -23,6 +23,7 @@ class RpcDecoderManager { entry.transport = &transport; // In-place construct entry.decoder = new (&entry.decoder_storage.instance) RpcDecoder<>(transport); + decoders_size++; return *entry.decoder; } } @@ -31,6 +32,10 @@ class RpcDecoderManager { while (true); } + static size_t size() { + return decoders_size; + } + private: struct DecoderStorage { union { @@ -49,10 +54,14 @@ class RpcDecoderManager { }; static std::array decoders_; + static size_t decoders_size; }; // Definition of the static member template std::array::Entry, MaxTransports> RpcDecoderManager::decoders_; +template +size_t RpcDecoderManager::decoders_size = 0; + #endif //RPCLITE_DECODER_MANAGER_H \ No newline at end of file diff --git a/src/server.h b/src/server.h index 96e247f..11b073c 100644 --- a/src/server.h +++ b/src/server.h @@ -16,6 +16,7 @@ class RPCServer { public: RPCServer(ITransport& t) : decoder(&RpcDecoderManager<>::getDecoder(t)) {} + // TODO This is problematic becasue 'new' makes different Transport objs and different transports make different decoders RPCServer(Stream& stream) { ITransport* transport = (ITransport*) new SerialTransport(stream); decoder = &RpcDecoderManager<>::getDecoder(*transport); @@ -34,10 +35,11 @@ class RPCServer { } protected: - void get_rpc() { + bool get_rpc() { decoder->decode(); - if (_rpc_size > 0) return; // Already have a request + if (_rpc_size > 0) return true; // Already have a request _rpc_size = decoder->get_request(_rpc_buffer, RPC_BUFFER_SIZE); + return _rpc_size > 0; } void process_request() { From 2190912d967398a7c06cd2ea66a1509038a4feec Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 9 Jul 2025 10:33:01 +0200 Subject: [PATCH 08/15] revert: rem client/server Stream based constructors. examples: refactoring alignment --- examples/decoder_tests/decoder_tests.ino | 9 +++------ examples/rpc_lite_client/rpc_lite_client.ino | 11 ++++++----- examples/rpc_lite_server/rpc_lite_server.ino | 17 ++++++++++++++++- src/client.h | 10 +++++----- src/server.h | 10 +++++----- src/transport.h | 2 -- 6 files changed, 35 insertions(+), 24 deletions(-) diff --git a/examples/decoder_tests/decoder_tests.ino b/examples/decoder_tests/decoder_tests.ino index 3b6af6f..223564b 100644 --- a/examples/decoder_tests/decoder_tests.ino +++ b/examples/decoder_tests/decoder_tests.ino @@ -29,12 +29,9 @@ void runDecoderTest(const char* label) { delay(50); } - while (decoder.packet_incoming()) { - size_t removed = decoder.discard_packet(); - Serial.print("Removed bytes: "); - Serial.println(removed); - decoder.decode(); - } + size_t pack_size = decoder.get_packet_size(); + Serial.print("1st Packet size: "); + Serial.println(pack_size); Serial.println("-- Done --\n"); } diff --git a/examples/rpc_lite_client/rpc_lite_client.ino b/examples/rpc_lite_client/rpc_lite_client.ino index f0f0a15..1012165 100644 --- a/examples/rpc_lite_client/rpc_lite_client.ino +++ b/examples/rpc_lite_client/rpc_lite_client.ino @@ -1,14 +1,16 @@ #include -RPCClient client(Serial1); +SerialTransport transport(Serial1); +RPCClient client(transport); void setup() { Serial1.begin(115200); while(!Serial1); pinMode(LED_BUILTIN, OUTPUT); - - Serial.begin(9600); + delay(10); + + Serial.begin(115200); while(!Serial); } @@ -30,7 +32,7 @@ void blink_before(){ void loop() { float result; blink_before(); - + String str_res; bool ok = client.call("loopback", str_res, "Sending a greeting"); Serial.println(str_res); @@ -75,5 +77,4 @@ void loop() { Serial.println("Server could not handle a notification as a call"); } - delay(2000); } \ No newline at end of file diff --git a/examples/rpc_lite_server/rpc_lite_server.ino b/examples/rpc_lite_server/rpc_lite_server.ino index ccf4497..657ee66 100644 --- a/examples/rpc_lite_server/rpc_lite_server.ino +++ b/examples/rpc_lite_server/rpc_lite_server.ino @@ -1,11 +1,16 @@ #include -RPCServer server(Serial1); +SerialTransport transport(Serial1); +RPCServer server(transport); int add(int a, int b){ return a+b; } +int add2(int a, int b){ + return a+b; +} + String greet(){ return String("Hello Friend"); } @@ -24,6 +29,10 @@ public: }; +float multip(float a, float b) { + return a*b; +} + void setup() { Serial1.begin(115200); while(!Serial1); @@ -34,10 +43,16 @@ void setup() { while(!Serial); server.bind("add", add); + + if (!server.bind("add", add2)){ + Serial.println("server refused to bind same name twice"); + } + server.bind("greet", greet); server.bind("loopback", loopback); server.bind("another_greeting", [] {return MsgPack::str_t ("This is a lambda greeting");}); server.bind("object_multi", &multiplier::mult); + server.bind("multip", multip); } diff --git a/src/client.h b/src/client.h index f0fb529..c7a46ba 100644 --- a/src/client.h +++ b/src/client.h @@ -14,11 +14,11 @@ class RPCClient { RPCClient(ITransport& t) : decoder(&RpcDecoderManager<>::getDecoder(t)) {} - // TODO This is problematic becasue 'new' makes different Transport objs and different transports make different decoders - RPCClient(Stream& stream) { - ITransport* transport = (ITransport*) new SerialTransport(stream); - decoder = &RpcDecoderManager<>::getDecoder(*transport); - } + // This constructor was removed because it leads to decoder duplication + // RPCClient(Stream& stream) { + // ITransport* transport = (ITransport*) new SerialTransport(stream); + // decoder = &RpcDecoderManager<>::getDecoder(*transport); + // } template void notify(const MsgPack::str_t method, Args&&... args) { diff --git a/src/server.h b/src/server.h index 11b073c..184ee27 100644 --- a/src/server.h +++ b/src/server.h @@ -16,11 +16,11 @@ class RPCServer { public: RPCServer(ITransport& t) : decoder(&RpcDecoderManager<>::getDecoder(t)) {} - // TODO This is problematic becasue 'new' makes different Transport objs and different transports make different decoders - RPCServer(Stream& stream) { - ITransport* transport = (ITransport*) new SerialTransport(stream); - decoder = &RpcDecoderManager<>::getDecoder(*transport); - } + // This constructor was removed because it leads to decoder duplication + // RPCServer(Stream& stream) { + // ITransport* transport = (ITransport*) new SerialTransport(stream); + // decoder = &RpcDecoderManager<>::getDecoder(*transport); + // } template bool bind(const MsgPack::str_t& name, F&& func){ diff --git a/src/transport.h b/src/transport.h index 5b50638..e459da3 100644 --- a/src/transport.h +++ b/src/transport.h @@ -1,8 +1,6 @@ #ifndef RPCLITE_TRANSPORT_H #define RPCLITE_TRANSPORT_H -#include - class ITransport { // Transport abstraction interface From 77a19797430037bac6248632f375da77ada3c774 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 10 Jul 2025 17:43:17 +0200 Subject: [PATCH 09/15] mod: no protected methods feat: dispatcher with tags --- src/client.h | 1 - src/dispatcher.h | 14 ++++++++++++-- src/server.h | 14 ++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/client.h b/src/client.h index c7a46ba..35f7982 100644 --- a/src/client.h +++ b/src/client.h @@ -44,7 +44,6 @@ class RPCClient { } -protected: template bool send_rpc(const MsgPack::str_t method, Args&&... args) { int msg_id; diff --git a/src/dispatcher.h b/src/dispatcher.h index 2a0e1d5..210e7d0 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -6,6 +6,7 @@ struct DispatchEntry { MsgPack::str_t name; + MsgPack::str_t tag; IFunctionWrapper* fn; }; @@ -13,14 +14,14 @@ template class RpcFunctionDispatcher { public: template - bool bind(MsgPack::str_t name, F&& f) { + bool bind(MsgPack::str_t name, F&& f, MsgPack::str_t tag="") { if (_count >= N) return false; if (isBound(name)) return false; using WrapperT = decltype(wrap(std::forward(f))); WrapperT* instance = new WrapperT(wrap(std::forward(f))); - _entries[_count++] = {name, instance}; + _entries[_count++] = {name, tag, instance}; return true; } @@ -33,6 +34,15 @@ class RpcFunctionDispatcher { return false; } + bool hasTag(MsgPack::str_t name,MsgPack::str_t tag) const { + for (size_t i = 0; i < _count; ++i) { + if (_entries[i].name == name && _entries[i].tag == tag) { + return true; + } + } + return false; + } + bool call(MsgPack::str_t name, MsgPack::Unpacker& unpacker, MsgPack::Packer& packer) { for (size_t i = 0; i < _count; ++i) { if (_entries[i].name == name) { diff --git a/src/server.h b/src/server.h index 184ee27..341259a 100644 --- a/src/server.h +++ b/src/server.h @@ -23,8 +23,12 @@ class RPCServer { // } template - bool bind(const MsgPack::str_t& name, F&& func){ - return dispatcher.bind(name, func); + bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t& tag){ + return dispatcher.bind(name, func, tag); + } + + bool hasTag(MsgPack::str_t name, MsgPack::str_t tag){ + return dispatcher.hasTag(name, tag); } void run() { @@ -34,15 +38,15 @@ class RPCServer { //delay(1); } -protected: bool get_rpc() { decoder->decode(); if (_rpc_size > 0) return true; // Already have a request + // TODO USE A QUEUE _rpc_size = decoder->get_request(_rpc_buffer, RPC_BUFFER_SIZE); return _rpc_size > 0; } - void process_request() { + void process_request(MsgPack::str_t tag="") { if (_rpc_size == 0) return; MsgPack::Unpacker unpacker; @@ -78,6 +82,8 @@ class RPCServer { return; // Invalid request size/type } + if (!hasTag(method, tag)) return; + _rpc_type = msg_type; MsgPack::arr_size_t resp_size(RESPONSE_SIZE); From 3be72e75df7454994fd03e84f5fdfcd6214da125 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 10 Jul 2025 18:05:55 +0200 Subject: [PATCH 10/15] fix: default server.bind tag --- src/server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.h b/src/server.h index 341259a..c76292c 100644 --- a/src/server.h +++ b/src/server.h @@ -23,7 +23,7 @@ class RPCServer { // } template - bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t& tag){ + bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t& tag=""){ return dispatcher.bind(name, func, tag); } From 3a5d489206ec5d857e8969faa473248af140ccd8 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 10 Jul 2025 18:33:27 +0200 Subject: [PATCH 11/15] fix... --- src/server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.h b/src/server.h index c76292c..f235437 100644 --- a/src/server.h +++ b/src/server.h @@ -23,7 +23,7 @@ class RPCServer { // } template - bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t& tag=""){ + bool bind(const MsgPack::str_t& name, F&& func, MsgPack::str_t tag=""){ return dispatcher.bind(name, func, tag); } From 2c09c35195e9cb42fe6b965feba597c7baa972a5 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 11 Jul 2025 16:47:14 +0200 Subject: [PATCH 12/15] mod: readability RpcDecoderManager decoders_count --- src/decoder_manager.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/decoder_manager.h b/src/decoder_manager.h index 57f88bb..d2c694c 100644 --- a/src/decoder_manager.h +++ b/src/decoder_manager.h @@ -23,7 +23,7 @@ class RpcDecoderManager { entry.transport = &transport; // In-place construct entry.decoder = new (&entry.decoder_storage.instance) RpcDecoder<>(transport); - decoders_size++; + decoders_count++; return *entry.decoder; } } @@ -32,8 +32,8 @@ class RpcDecoderManager { while (true); } - static size_t size() { - return decoders_size; + static size_t getDecodersCount() const { + return decoders_count; } private: @@ -54,7 +54,7 @@ class RpcDecoderManager { }; static std::array decoders_; - static size_t decoders_size; + static size_t decoders_count; }; // Definition of the static member @@ -62,6 +62,6 @@ template std::array::Entry, MaxTransports> RpcDecoderManager::decoders_; template -size_t RpcDecoderManager::decoders_size = 0; +size_t RpcDecoderManager::decoders_count = 0; #endif //RPCLITE_DECODER_MANAGER_H \ No newline at end of file From 2dedba537fa63d8b12e8daa652e9d6d7a6eb83cd Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Mon, 14 Jul 2025 10:36:00 +0200 Subject: [PATCH 13/15] fix: getDecodersCount compile error --- src/decoder_manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoder_manager.h b/src/decoder_manager.h index d2c694c..d2c2bd7 100644 --- a/src/decoder_manager.h +++ b/src/decoder_manager.h @@ -32,7 +32,7 @@ class RpcDecoderManager { while (true); } - static size_t getDecodersCount() const { + static size_t getDecodersCount() { return decoders_count; } From 06b50fa809e7de4b389e83a87f1001702c3abc9d Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Mon, 14 Jul 2025 11:12:41 +0200 Subject: [PATCH 14/15] ver. 0.1.0 tags. README reverted examples to client/server transport constructors --- README.md | 6 ++++-- library.json | 2 +- library.properties | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f70ea97..0d52725 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ A MessagePack RPC library for Arduino allows to create a client/server architect ```cpp #include -RPCServer server(Serial1); +SerialTransport transport(Serial1); +RPCServer server(transport); int add(int a, int b){ return a+b; @@ -42,7 +43,8 @@ void loop() { ```cpp #include -RPCClient client(Serial1); +SerialTransport transport(Serial1); +RPCClient client(transport); void setup() { Serial1.begin(115200); diff --git a/library.json b/library.json index cd9a34e..cf2bffb 100644 --- a/library.json +++ b/library.json @@ -11,7 +11,7 @@ "url": "https://github.com/eigen-value", "maintainer": true }, - "version": "0.0.1", + "version": "0.1.0", "license": "MIT", "frameworks": "arduino", "platforms": "*", diff --git a/library.properties b/library.properties index 54cfdde..e62c817 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino_RPClite -version=0.0.1 +version=0.1.0 author=Lucio Rossi (eigen-value) maintainer=Lucio Rossi (eigen-value) sentence=A MessagePack RPC library for Arduino From 1970d67dd76aacd5f41fcf13790626b5c7ffcbd1 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Mon, 14 Jul 2025 16:03:36 +0200 Subject: [PATCH 15/15] mod: server sends resp before resetting rpc --- src/server.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server.h b/src/server.h index f235437..4ba5e62 100644 --- a/src/server.h +++ b/src/server.h @@ -104,8 +104,9 @@ class RPCServer { return true; } + bool send_res = decoder->send_response(res_packer); reset_rpc(); - return decoder->send_response(res_packer); + return send_res; }