Skip to content

Commit 90faa8c

Browse files
committed
mod: BridgeClass rem unsafe get error methods
mod: tcp_client and monitor use RpcResult async calls to get errors examples: Bridge test.ino with python code doc: impr Bridge call examples new API
1 parent 5dd0758 commit 90faa8c

File tree

7 files changed

+121
-69
lines changed

7 files changed

+121
-69
lines changed

README.md

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ In this repo it will be implemented an Arduino library wrapper for RPClite to be
55
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.
66

77
- The Bridge object is pre-defined on Serial1 and automatically initialized inside the main setup()
8-
- The Bridge.call method is blocking and works the same as in RPClite
8+
- The Bridge.call method is non-blocking and returns a RpcResult async object
9+
- RpcResult class implements a blocking .result method that waits for the RPC response and returns true if the RPC returned with no errors
910
- The Bridge can provide callbacks to incoming RPC requests both in a thread-unsafe and thread-safe fashion (provide & provide_safe)
1011
- Thread-safe methods execution is granted in the main loop thread where update_safe is called. By design users cannot access .update_safe() freely
1112
- 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
@@ -27,7 +28,7 @@ void setup() {
2728

2829
Bridge.begin();
2930
Monitor.begin();
30-
31+
3132
pinMode(LED_BUILTIN, OUTPUT);
3233

3334
if (!Bridge.provide("set_led", set_led)) {
@@ -41,27 +42,33 @@ void setup() {
4142
}
4243

4344
void loop() {
44-
float res;
45-
if (!Bridge.call("multiply", 1.0, 2.0).result(res)) {
46-
Monitor.println("Error calling method: multiply");
47-
Monitor.println(Bridge.get_error_code());
48-
Monitor.println(Bridge.get_error_message());
45+
float sum;
46+
47+
// CALL EXAMPLES
48+
49+
// Standard chained call: Bridge.call("method", params...).result(res)
50+
if (!Bridge.call("add", 1.0, 2.0).result(sum)) {
51+
Monitor.println("Error calling method: add");
52+
};
53+
54+
// Async call
55+
RpcResult async_rpc = Bridge.call("add", 3.0, 4.5);
56+
if (!async_rpc.result(sum)) {
57+
Monitor.println("Error calling method: add");
58+
Monitor.print("Error code: ");
59+
Monitor.println(async_rpc.error.code);
60+
Monitor.print("Error message: ");
61+
Monitor.println(async_rpc.error.traceback);
62+
}
63+
64+
// Implicit boolean cast. Use with caution as in this case the call is indeed
65+
// executed expecting a fallback nil result (MsgPack::object::nil_t)
66+
if (!Bridge.call("send_greeting", "Hello Friend")) {
67+
Monitor.println("Error calling method: send_greeting");
4968
};
5069

70+
// Please use notify when no reult (None, null, void, nil etc.) is expected from the opposite side
71+
// the following is executed immediately
5172
Bridge.notify("signal", 200);
5273
}
5374
```
54-
55-
## Best practices ##
56-
Avoid catching Bridge call RpcResult without invoking its .result right away
57-
```cpp
58-
// OK
59-
float out;
60-
RpcResult res = Bridge.call("multiply", 1.0, 2.0);
61-
res.result(out);
62-
Monitor.println("TEST");
63-
64-
// NOT OK
65-
//RpcResult res = Bridge.call("multiply", 1.0, 2.0);
66-
//Monitor.println("TEST");
67-
```

examples/test/python/main.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import time
2+
from arduino.app_utils import *
3+
4+
5+
def no_args_no_result():
6+
print("0 args no result")
7+
return
8+
9+
def no_args_bool_result():
10+
print("0 args bool result")
11+
return True
12+
13+
def one_args_float_result(a):
14+
print("1 args float result")
15+
return a^2
16+
17+
def two_args_float_result(a, b):
18+
print("2 args float result")
19+
return a/b
20+
21+
def one_args_no_result(msg: str):
22+
print("1 args no result")
23+
print(f"Message received: {msg}")
24+
25+
def two_args_no_result(msg: str, x: int):
26+
print("2 args no result")
27+
print(f"Message received: {msg} {str(x)}")
28+
29+
30+
if __name__ == "__main__":
31+
32+
Bridge.provide("0_args_no_result", no_args_no_result)
33+
Bridge.provide("0_args_bool_result", no_args_bool_result)
34+
Bridge.provide("1_args_float_result", one_args_float_result)
35+
Bridge.provide("2_args_float_result", two_args_float_result)
36+
Bridge.provide("1_args_no_result", one_args_no_result)
37+
Bridge.provide("2_args_no_result", two_args_no_result)
38+
39+
App.run()

examples/test/server_test

-3.97 MB
Binary file not shown.

examples/test/test.ino

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@
99
1010
*/
1111

12-
// to run this test example, launch server_test in a shell and then this sketch
13-
14-
15-
/* available registers
16-
register("ping")
17-
register("0_args_no_result")
18-
register("1_args_no_result")
19-
register("2_args_no_result")
20-
register("0_args_bool_result")
21-
register("1_args_bool_result")
22-
register("2_args_bool_result")
23-
*/
2412

2513
#include "Arduino_RouterBridge.h"
2614

@@ -42,7 +30,7 @@ void loop() {
4230
Bridge.call("0_args_no_result");
4331

4432
if (Bridge.call("0_args_no_result")){
45-
Monitor.println("0_args_no_result TRUE without result"); // return true because no result
33+
Monitor.println("0_args_no_result TRUE without result"); // returns true because there's no result
4634
}
4735
else{
4836
Monitor.println("0_args_no_result FALSE without result");
@@ -52,34 +40,63 @@ void loop() {
5240
Monitor.println("0_args_bool_result TRUE without result");
5341
}
5442
else{
55-
Monitor.println("0_args_bool_result FALSE without result"); // return false because you need check the result
43+
Monitor.println("0_args_bool_result FALSE without result"); // returns false -> check the result
5644
}
5745

58-
x=false;
46+
x = false;
5947
if (Bridge.call("0_args_bool_result").result(x)){
60-
Monitor.println("0_args_bool_result TRUE with result: "+String(x)); // return true - the perfect call
48+
Monitor.println("0_args_bool_result TRUE with result: "+String(x)); // returns true - the perfect call
6149
}
6250
else{
6351
Monitor.println("0_args_bool_result FALSE witt result: "+String(x));
6452
}
6553

6654
int y = -1;
6755
if (Bridge.call("0_args_bool_result").result(y)){
68-
Monitor.println("0_args_bool_result TRUE with result: "+String(y)+" (wrong result type)"); // return true - the perfect call
56+
Monitor.println("0_args_bool_result TRUE with result: "+String(y)+" (wrong result type)");
6957
}
70-
else{
71-
Monitor.println("0_args_bool_result FALSE with result: "+String(y)+" (wrong result type)");
58+
else {
59+
Monitor.println("0_args_bool_result FALSE with result: "+String(y)+" (wrong result type)"); // returns false - wrong type
7260
}
7361

62+
float pow;
63+
RpcResult async_res = Bridge.call("1_args_float_result", 2.0, 3.0); // passing 2 args to a function expecting 1
64+
if (async_res.result(pow)) {
65+
Monitor.println("Result of assignment and then result: "+String(pow)); // returns true, so the right result
66+
} else {
67+
Monitor.println("Error code: "+String(async_res.error.code));
68+
Monitor.println("Error message: "+async_res.error.traceback);
69+
}
7470

75-
// avoid to do followings
71+
float div = 0;
72+
RpcResult async_res1 = Bridge.call("2_args_float_result", 2.0); // passing 1 arg when 2 are expected
73+
if (async_res1.result(div)) {
74+
Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result
75+
} else {
76+
Monitor.println("Error code: "+String(async_res1.error.code));
77+
Monitor.println("Error message: "+async_res1.error.traceback);
78+
}
79+
80+
div = 0;
81+
RpcResult async_res2 = Bridge.call("2_args_float_result", 2.0, "invalid"); // passing a wrong type arg
82+
if (async_res2.result(div)) {
83+
Monitor.println("Result of assignment and then result: "+String(div)); // returns true, so the right result
84+
} else {
85+
Monitor.println("Error code: "+String(async_res2.error.code));
86+
Monitor.println("Error message: "+async_res2.error.traceback);
87+
}
7688

77-
RpcResult result = Bridge.call("0_args_bool_result"); // the call happens but you won't get the result
89+
x = false;
90+
RpcResult async_res3 = Bridge.call("0_args_bool_result");
91+
if (async_res3.result(x)) {
92+
Monitor.println("Result of assignment and then result: "+String(x)); // returns true, so the right result
93+
} else {
94+
Monitor.println("Error expecting bool result: "+String(async_res3.error.code));
95+
}
7896

79-
bool x = false;
80-
RpcResult result2 = Bridge.call("0_args_bool_result");
81-
result2.result(x);
82-
Monitor.println("Result of assignment and then result: "+String(x)); // return true, so the right result
97+
// Avoid the following:
98+
// the call happens in the destructor falling back to the "no_result" case (a type mismatch here)
99+
RpcResult async_res4 = Bridge.call("0_args_bool_result");
83100

84101
delay(1000);
85102
}

src/bridge.h

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ void updateEntryPoint(void *, void *, void *);
3232
template<typename... Args>
3333
class RpcResult {
3434

35-
private:
36-
std::tuple<Args...> callback_params;
37-
MsgPack::str_t method;
38-
3935
public:
4036
RpcError error;
4137

@@ -92,10 +88,13 @@ class RpcResult {
9288

9389
private:
9490
uint32_t msg_id_wait{};
95-
RPCClient* client;
9691
bool _executed = false;
92+
93+
MsgPack::str_t method;
94+
RPCClient* client;
9795
struct k_mutex* read_mutex;
9896
struct k_mutex* write_mutex;
97+
std::tuple<Args...> callback_params;
9998
};
10099

101100
class BridgeClass {
@@ -206,8 +205,6 @@ class BridgeClass {
206205
return RpcResult<Args...>(method, client, &read_mutex, &write_mutex, std::forward<Args>(args)...);
207206
}
208207

209-
210-
211208
template<typename... Args>
212209
void notify(const MsgPack::str_t method, Args&&... args) {
213210
while (true) {
@@ -220,18 +217,6 @@ class BridgeClass {
220217
}
221218
}
222219

223-
String get_error_message() const {
224-
return static_cast<String>(client->lastError.traceback);
225-
}
226-
227-
uint8_t get_error_code() const {
228-
return static_cast<uint8_t>(client->lastError.code);
229-
}
230-
231-
RpcError& get_last_client_error() const {
232-
return client->lastError;
233-
}
234-
235220
private:
236221

237222
void update_safe() {

src/monitor.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ class BridgeMonitor: public Stream {
122122
if (size == 0) return;
123123

124124
MsgPack::arr_t<uint8_t> message;
125-
bool ret = bridge->call(MON_READ_METHOD, size).result(message);
125+
RpcResult async_rpc = bridge->call(MON_READ_METHOD, size);
126+
127+
const bool ret = async_rpc.result(message);
126128

127129
if (ret) {
128130
k_mutex_lock(&monitor_mutex, K_FOREVER);
@@ -132,7 +134,7 @@ class BridgeMonitor: public Stream {
132134
k_mutex_unlock(&monitor_mutex);
133135
}
134136

135-
// if (bridge.lastError.code > NO_ERR) {
137+
// if (async_rpc.error.code > NO_ERR) {
136138
// is_connected = false;
137139
// }
138140
}

src/tcp_client.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,17 @@ class BridgeTCPClient : public Client {
198198
k_mutex_lock(&client_mutex, K_FOREVER);
199199

200200
MsgPack::arr_t<uint8_t> message;
201-
const bool ret = bridge->call(TCP_READ_METHOD, connection_id, size).result(message);
201+
RpcResult async_rpc = bridge->call(TCP_READ_METHOD, connection_id, size);
202+
203+
const bool ret = async_rpc.result(message);
202204

203205
if (ret) {
204206
for (size_t i = 0; i < message.size(); ++i) {
205207
temp_buffer.store_char(static_cast<char>(message[i]));
206208
}
207209
}
208210

209-
if (bridge->get_last_client_error().code > NO_ERR) {
211+
if (async_rpc.error.code > NO_ERR) {
210212
_connected = false;
211213
}
212214

0 commit comments

Comments
 (0)