Skip to content
Draft
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ In this repo it will be implemented an Arduino library wrapper for RPClite to be
Including Arduino_RouterBridge.h gives the user access to a Bridge object that can be used both as a RPC client and/or server to execute and serve RPCs to/from the CPU Host running a GOLANG router.

- The Bridge object is pre-defined on Serial1 and automatically initialized inside the main setup()
- The Bridge.call method is non-blocking and returns a RpcResult async object
- RpcResult class implements a blocking .result method that waits for the RPC response and returns true if the RPC returned with no errors
- The Bridge.call method is non-blocking and returns a RpcCall async object
- RpcCall class implements a blocking .result method that waits for the RPC response and returns true if the RPC returned with no errors
- The RpcCall.result will return - by reference - the result value of that call *exactly once*. Subsequent calls to .result will return an error condition
- The Bridge can provide callbacks to incoming RPC requests both in a thread-unsafe and thread-safe fashion (provide & provide_safe)
- Thread-safe methods execution is granted in the main loop thread where update_safe is called. By design users cannot access .update_safe() freely
- Thread-unsafe methods are served in an update callback, whose execution is granted in a separate thread. Nonetheless users can access .update() freely with caution
Expand Down Expand Up @@ -52,7 +53,7 @@ void loop() {
};

// Async call
RpcResult async_rpc = Bridge.call("add", 3.0, 4.5);
RpcCall async_rpc = Bridge.call("add", 3.0, 4.5);
if (!async_rpc.result(sum)) {
Monitor.println("Error calling method: add");
Monitor.print("Error code: ");
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_bridge/simple_bridge.ino
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void loop() {
};

// Call with deferred response check
RpcResult outcome = Bridge.call("multiply", 5.0, 7.0);
RpcCall outcome = Bridge.call("multiply", 5.0, 7.0);
Serial.println("RPC called");
delay(10);
if (outcome.result(res)) {
Expand Down
10 changes: 5 additions & 5 deletions examples/test/test.ino
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void loop() {
}

float pow;
RpcResult async_res = Bridge.call("1_args_float_result", 2.0, 3.0); // passing 2 args to a function expecting 1
RpcCall async_res = Bridge.call("1_args_float_result", 2.0, 3.0); // passing 2 args to a function expecting 1
if (async_res.result(pow)) {
Monitor.println("Result of assignment and then result: "+String(pow)); // returns true, so the right result
} else {
Expand All @@ -69,7 +69,7 @@ void loop() {
}

float div = 0;
RpcResult async_res1 = Bridge.call("2_args_float_result", 2.0); // passing 1 arg when 2 are expected
RpcCall async_res1 = Bridge.call("2_args_float_result", 2.0); // passing 1 arg when 2 are expected
if (async_res1.result(div)) {
Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result
} else {
Expand All @@ -78,7 +78,7 @@ void loop() {
}

div = 0;
RpcResult async_res2 = Bridge.call("2_args_float_result", 2.0, "invalid"); // passing a wrong type arg
RpcCall async_res2 = Bridge.call("2_args_float_result", 2.0, "invalid"); // passing a wrong type arg
if (async_res2.result(div)) {
Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result
} else {
Expand All @@ -87,7 +87,7 @@ void loop() {
}

x = false;
RpcResult async_res3 = Bridge.call("0_args_bool_result");
RpcCall async_res3 = Bridge.call("0_args_bool_result");
if (async_res3.result(x)) {
Monitor.println("Result of assignment and then result: "+String(x)); // returns true, so the right result
} else {
Expand All @@ -96,7 +96,7 @@ void loop() {

// Avoid the following:
// the call happens in the destructor falling back to the "no_result" case (a type mismatch here)
RpcResult async_res4 = Bridge.call("0_args_bool_result");
RpcCall async_res4 = Bridge.call("0_args_bool_result");

delay(1000);
}
26 changes: 16 additions & 10 deletions src/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,27 @@
#define DEFAULT_SERIAL_BAUD 115200

#include <zephyr/kernel.h>
#include <zephyr/sys/atomic.h>
#include <Arduino_RPClite.h>


void updateEntryPoint(void *, void *, void *);

template<typename... Args>
class RpcResult {

class RpcCall {
public:
RpcError error;
RpcError error{GENERIC_ERR, "This call is not executed yet"};

RpcResult(const MsgPack::str_t& m, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, Args&&... args): method(m), client(c), read_mutex(rm), write_mutex(wm), callback_params(std::forward_as_tuple(std::forward<Args>(args)...)) {}
RpcCall(const MsgPack::str_t& m, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, Args&&... args): method(m), client(c), read_mutex(rm), write_mutex(wm), callback_params(std::forward_as_tuple(std::forward<Args>(args)...)) {}

template<typename RType> bool result(RType& result) {
if (_executed) return error.code == NO_ERR;

if (!atomic_cas(&_executed, 0, 1)){
// this thread lost the race
error.code = GENERIC_ERR;
error.traceback = "This call result is no longer available";
return false;
}

while (true) {
if (k_mutex_lock(write_mutex, K_MSEC(10)) == 0) {
Expand Down Expand Up @@ -69,7 +75,7 @@ class RpcResult {
k_yield();
}
}
_executed = true;

return error.code == NO_ERR;
}

Expand All @@ -78,7 +84,7 @@ class RpcResult {
return result(nil);
}

~RpcResult(){
~RpcCall(){
result();
}

Expand All @@ -88,7 +94,7 @@ class RpcResult {

private:
uint32_t msg_id_wait{};
bool _executed = false;
atomic_t _executed = ATOMIC_INIT(0);;

MsgPack::str_t method;
RPCClient* client;
Expand Down Expand Up @@ -210,8 +216,8 @@ class BridgeClass {
}

template<typename... Args>
RpcResult<Args...> call(const MsgPack::str_t& method, Args&&... args) {
return RpcResult<Args...>(method, client, &read_mutex, &write_mutex, std::forward<Args>(args)...);
RpcCall<Args...> call(const MsgPack::str_t& method, Args&&... args) {
return RpcCall<Args...>(method, client, &read_mutex, &write_mutex, std::forward<Args>(args)...);
}

template<typename... Args>
Expand Down
2 changes: 1 addition & 1 deletion src/monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class BridgeMonitor: public Stream {
k_mutex_lock(&monitor_mutex, K_FOREVER);

MsgPack::arr_t<uint8_t> message;
RpcResult async_rpc = bridge->call(MON_READ_METHOD, size);
RpcCall async_rpc = bridge->call(MON_READ_METHOD, size);
const bool ret = _connected && async_rpc.result(message);

if (ret) {
Expand Down
2 changes: 1 addition & 1 deletion src/tcp_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class BridgeTCPClient : public Client {
k_mutex_lock(&client_mutex, K_FOREVER);

MsgPack::arr_t<uint8_t> message;
RpcResult async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size);
RpcCall async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size);
const bool ret = _connected && async_rpc.result(message);

if (ret) {
Expand Down