ThingsBoard Client SDK 0.16.0
Client SDK to connect with ThingsBoard IoT Platform from IoT devices (Arduino, Espressif, etc.)
Loading...
Searching...
No Matches
Server_Side_RPC.h
Go to the documentation of this file.
1#ifndef Server_Side_RPC_h
2#define Server_Side_RPC_h
3
4// Local includes.
5#include "RPC_Callback.h"
7
8
9// server-side RPC topics.
10char constexpr RPC_SUBSCRIBE_TOPIC[] = "v1/devices/me/rpc/request/+";
11char constexpr RPC_REQUEST_TOPIC[] = "v1/devices/me/rpc/request/";
12char constexpr RPC_SEND_RESPONSE_TOPIC[] = "v1/devices/me/rpc/response/%u";
13// Log messages.
14char constexpr RPC_RESPONSE_OVERFLOWED[] = "Server-side RPC response overflowed, increase MaxRPC (%u)";
15#if !THINGSBOARD_ENABLE_DYNAMIC
16char constexpr SERVER_SIDE_RPC_SUBSCRIPTIONS[] = "server-side RPC";
17#endif // !THINGSBOARD_ENABLE_DYNAMIC
18#if THINGSBOARD_ENABLE_DEBUG
19char constexpr SERVER_RPC_METHOD_NULL[] = "Server-side RPC method name is NULL";
20char constexpr RPC_RESPONSE_NULL[] = "Response JsonDocument is NULL, skipping sending";
21char constexpr NO_RPC_PARAMS_PASSED[] = "No parameters passed with RPC, passing null JSON";
22char constexpr CALLING_RPC_CB[] = "Calling subscribed callback for rpc with methodname (%s)";
23#endif // THINGSBOARD_ENABLE_DEBUG
24
25
29#if THINGSBOARD_ENABLE_DYNAMIC
30template <typename Logger = DefaultLogger>
31#else
37template<size_t MaxSubscriptions = DEFAULT_SUBSCRIPTION_AMOUNT, size_t MaxRPC = DEFAULT_RPC_AMOUNT, typename Logger = DefaultLogger>
38#endif // THINGSBOARD_ENABLE_DYNAMIC
40 public:
42 Server_Side_RPC() = default;
43
44 ~Server_Side_RPC() override = default;
45
59 template<typename InputIterator>
60 bool RPC_Subscribe(InputIterator const & first, InputIterator const & last) {
61#if !THINGSBOARD_ENABLE_DYNAMIC
62 auto const size = Helper::distance(first, last);
63 if (m_rpc_callbacks.size() + size > m_rpc_callbacks.capacity()) {
65 return false;
66 }
67#endif // !THINGSBOARD_ENABLE_DYNAMIC
68 (void)m_subscribe_topic_callback.Call_Callback(RPC_SUBSCRIBE_TOPIC);
69 // Push back complete vector into our local m_rpc_callbacks vector.
70 m_rpc_callbacks.insert(m_rpc_callbacks.end(), first, last);
71 return true;
72 }
73
83 bool RPC_Subscribe(RPC_Callback const & callback) {
84#if !THINGSBOARD_ENABLE_DYNAMIC
85 if (m_rpc_callbacks.size() + 1 > m_rpc_callbacks.capacity()) {
87 return false;
88 }
89#endif // !THINGSBOARD_ENABLE_DYNAMIC
90 (void)m_subscribe_topic_callback.Call_Callback(RPC_SUBSCRIBE_TOPIC);
91 m_rpc_callbacks.push_back(callback);
92 return true;
93 }
94
100 m_rpc_callbacks.clear();
101 return m_unsubscribe_topic_callback.Call_Callback(RPC_SUBSCRIBE_TOPIC);
102 }
103
105 return API_Process_Type::JSON;
106 }
107
108 void Process_Response(char const * topic, uint8_t * payload, uint32_t length) override {
109 // Nothing to do
110 }
111
112 void Process_Json_Response(char const * topic, JsonDocument const & data) override {
113 if (!data.containsKey(RPC_METHOD_KEY)) {
114#if THINGSBOARD_ENABLE_DEBUG
115 Logger::printfln(SERVER_RPC_METHOD_NULL);
116#endif // THINGSBOARD_ENABLE_DEBUG
117 return;
118 }
119 char const * method_name = data[RPC_METHOD_KEY];
120
121#if THINGSBOARD_ENABLE_STL
122 auto it = std::find_if(m_rpc_callbacks.begin(), m_rpc_callbacks.end(), [&method_name](RPC_Callback const & rpc) {
123 char const * subscribedMethodName = rpc.Get_Name();
124 return (!Helper::String_IsNull_Or_Empty(subscribedMethodName) && strncmp(subscribedMethodName, method_name, strlen(subscribedMethodName)) == 0);
125 });
126 if (it != m_rpc_callbacks.end()) {
127 auto & rpc = *it;
128#else
129 for (auto const & rpc : m_rpc_callbacks) {
130 char const * subscribedMethodName = rpc.Get_Name();
131 if (Helper::String_IsNull_Or_Empty(subscribedMethodName) || strncmp(subscribedMethodName, method_name, strlen(subscribedMethodName)) != 0) {
132 continue;
133 }
134#endif // THINGSBOARD_ENABLE_STL
135#if THINGSBOARD_ENABLE_DEBUG
136 if (!data.containsKey(RPC_PARAMS_KEY)) {
137 Logger::printfln(NO_RPC_PARAMS_PASSED);
138 }
139#endif // THINGSBOARD_ENABLE_DEBUG
140
141#if THINGSBOARD_ENABLE_DEBUG
142 Logger::printfln(CALLING_RPC_CB, method_name);
143#endif // THINGSBOARD_ENABLE_DEBUG
144
145 JsonVariantConst const param = data[RPC_PARAMS_KEY];
146#if THINGSBOARD_ENABLE_DYNAMIC
147 auto const & rpc_response_size = rpc.Get_Response_Size();
148 TBJsonDocument json_buffer(rpc_response_size);
149#else
150 size_t constexpr rpc_response_size = MaxRPC;
151 StaticJsonDocument<JSON_OBJECT_SIZE(MaxRPC)> json_buffer;
152#endif // THINGSBOARD_ENABLE_DYNAMIC
153 rpc.Call_Callback(param, json_buffer);
154
155 if (json_buffer.isNull()) {
156#if THINGSBOARD_ENABLE_DEBUG
157 Logger::printfln(RPC_RESPONSE_NULL);
158#endif // THINGSBOARD_ENABLE_DEBUG
159 return;
160 }
161 else if (json_buffer.overflowed()) {
162 Logger::printfln(RPC_RESPONSE_OVERFLOWED, rpc_response_size);
163 return;
164 }
165
166 auto const request_id = Helper::Split_Topic_Into_Request_ID(topic, strlen(RPC_REQUEST_TOPIC));
167 char responseTopic[Helper::Calculate_Print_Size(RPC_SEND_RESPONSE_TOPIC, request_id)] = {};
168 (void)snprintf(responseTopic, sizeof(responseTopic), RPC_SEND_RESPONSE_TOPIC, request_id);
169 (void)m_send_json_callback.Call_Callback(responseTopic, json_buffer);
170 return;
171 }
172 }
173
174 bool Is_Response_Topic_Matching(char const * topic) const override {
175 return strncmp(RPC_REQUEST_TOPIC, topic, strlen(RPC_REQUEST_TOPIC)) == 0;
176 }
177
178 bool Unsubscribe() override {
179 return RPC_Unsubscribe();
180 }
181
183 if (!m_rpc_callbacks.empty() && !m_subscribe_topic_callback.Call_Callback(RPC_SUBSCRIBE_TOPIC)) {
185 return false;
186 }
187 return true;
188 }
189
190#if !THINGSBOARD_USE_ESP_TIMER
191 void loop() override {
192 // Nothing to do
193 }
194#endif // !THINGSBOARD_USE_ESP_TIMER
195
196 void Initialize() override {
197 // Nothing to do
198 }
199
201 m_send_json_callback.Set_Callback(send_json_callback);
202 m_subscribe_topic_callback.Set_Callback(subscribe_topic_callback);
203 m_unsubscribe_topic_callback.Set_Callback(unsubscribe_topic_callback);
204 }
205
206 private:
207#if THINGSBOARD_ENABLE_DYNAMIC
208 using Callback_Container = Container<RPC_Callback>;
209#else
210 using Callback_Container = Container<RPC_Callback, MaxSubscriptions>;
211#endif // THINGSBOARD_ENABLE_DYNAMIC
212
213 Callback<bool, char const * const, JsonDocument const &> m_send_json_callback = {}; // Send json document callback
214 Callback<bool, char const * const> m_subscribe_topic_callback = {}; // Subscribe mqtt topic client callback
215 Callback<bool, char const * const> m_unsubscribe_topic_callback = {}; // Unubscribe mqtt topic client callback
216 Callback_Container m_rpc_callbacks = {}; // server-side RPC callbacks array
217};
218
219#endif // Server_Side_RPC_h
API_Process_Type
Possible processing types an API Implementation uses to handle responses from the server.
Definition: API_Process_Type.h:19
char constexpr MAX_SUBSCRIPTIONS_TEMPLATE_NAME[]
Definition: IAPI_Implementation.h:22
char constexpr RPC_PARAMS_KEY[]
Definition: IAPI_Implementation.h:27
char constexpr SUBSCRIBE_TOPIC_FAILED[]
Definition: IAPI_Implementation.h:23
char constexpr MAX_SUBSCRIPTIONS_EXCEEDED[]
Definition: IAPI_Implementation.h:20
char constexpr RPC_METHOD_KEY[]
Definition: IAPI_Implementation.h:26
char constexpr RPC_RESPONSE_OVERFLOWED[]
Definition: Server_Side_RPC.h:14
char constexpr RPC_REQUEST_TOPIC[]
Definition: Server_Side_RPC.h:11
char constexpr SERVER_SIDE_RPC_SUBSCRIPTIONS[]
Definition: Server_Side_RPC.h:16
char constexpr RPC_SUBSCRIBE_TOPIC[]
Definition: Server_Side_RPC.h:10
char constexpr RPC_SEND_RESPONSE_TOPIC[]
Definition: Server_Side_RPC.h:12
General purpose safe callback wrapper. Expects either c-style or c++ style function pointer,...
Definition: Callback.h:30
std::function< return_type(argument_types... arguments)> function
Callback signature.
Definition: Callback.h:34
void Set_Callback(function callback)
Sets the callback method that will be called upon data arrival with the given data that was received....
Definition: Callback.h:72
return_type Call_Callback(argument_types const &... arguments) const
Calls the callback that was subscribed, when this class instance was initally created.
Definition: Callback.h:62
Custom std::array or std::vector implementation that contains a partial vector-like interface impleme...
Definition: Container.h:29
size_type size() const
Gets the current amount of elements in the underlying data container.
Definition: Container.h:147
iterator begin()
Returns an iterator to the first element of the underlying data container. If the array is empty,...
Definition: Container.h:165
void insert(iterator position, InputIterator const &first, InputIterator const &last)
Copies all elements from the given start to exclusively the given end iterator into the underlying da...
Definition: Container.h:257
size_type constexpr capacity() const
Gets the maximum amount of elements that can be stored in the underlying data container.
Definition: Container.h:157
void push_back(const_reference element)
Appends the given element at the end of the underlying data container.
Definition: Container.h:230
bool empty() const
Returns whether there are any elements in the underlying data container.
Definition: Container.h:141
void clear()
Erases all elements from the container. After this call, size() returns zero.
Definition: Container.h:330
iterator end()
Returns a iterator to one-past-the-last element of the underlying data container.
Definition: Container.h:209
static size_t Calculate_Print_Size(char const *format, Args const &... args)
Returns the total amount of bytes needed to store the formatted string with null termination,...
Definition: Helper.h:32
static size_t distance(InputIterator const &first, InputIterator const &last)
Calculates the distance between two iterators.
Definition: Helper.h:85
static bool String_IsNull_Or_Empty(char const *str)
Returns wheter the given string is either a nullptr or is an empty string, meaning it only contains a...
Definition: Helper.cpp:27
static size_t Split_Topic_Into_Request_ID(char const *received_topic, size_t const &end_position)
Splits the topic at the given position and extracts the request id parameter from the remaining strin...
Definition: Helper.cpp:31
Base functionality required by all API implementation.
Definition: IAPI_Implementation.h:37
Server-side RPC callback wrapper, contains the needed configuration settings to create the request th...
Definition: RPC_Callback.h:12
char const * Get_Name() const
Gets the name we expect to be sent with the server-side RPC request so that this method callback will...
Definition: RPC_Callback.h:45
Handles the internal implementation of the ThingsBoard server-side RPC API. See https://thingsboard....
Definition: Server_Side_RPC.h:39
void Process_Json_Response(char const *topic, JsonDocument const &data) override
Process callback that will be called upon response arrival.
Definition: Server_Side_RPC.h:112
bool Resubscribe_Permanent_Subscriptions() override
Forwards the call to let the API clear up any ongoing single-event subscriptions (Provision,...
Definition: Server_Side_RPC.h:182
Server_Side_RPC()=default
Constructor.
bool RPC_Subscribe(RPC_Callback const &callback)
Subscribe one server-side RPC callback, that will be called if a request from the server for the meth...
Definition: Server_Side_RPC.h:83
bool Is_Response_Topic_Matching(char const *topic) const override
Compares received response topic and the topic this api implementation handles responses on,...
Definition: Server_Side_RPC.h:174
API_Process_Type Get_Process_Type() const override
Returns the way the server response should be processed.
Definition: Server_Side_RPC.h:104
bool RPC_Subscribe(InputIterator const &first, InputIterator const &last)
Subscribes multiple server-side RPC callbacks, that will be called if a request from the server for t...
Definition: Server_Side_RPC.h:60
~Server_Side_RPC() override=default
void Set_Client_Callbacks(Callback< void, IAPI_Implementation & >::function subscribe_api_callback, Callback< bool, char const *const, JsonDocument const & >::function send_json_callback, Callback< bool, char const *const, char const *const >::function send_json_string_callback, Callback< bool, char const *const >::function subscribe_topic_callback, Callback< bool, char const *const >::function unsubscribe_topic_callback, Callback< uint16_t >::function get_receive_size_callback, Callback< uint16_t >::function get_send_size_callback, Callback< bool, uint16_t, uint16_t >::function set_buffer_size_callback, Callback< size_t * >::function get_request_id_callback) override
Sets the underlying callbacks that are required for the different API Implementation to communicate w...
Definition: Server_Side_RPC.h:200
void Process_Response(char const *topic, uint8_t *payload, uint32_t length) override
Process callback that will be called upon response arrival.
Definition: Server_Side_RPC.h:108
bool Unsubscribe() override
Unsubcribes all callbacks, to clear up any ongoing subscriptions and stop receiving information over ...
Definition: Server_Side_RPC.h:178
void loop() override
Internal loop method to update inernal timers for API calls that can timeout.
Definition: Server_Side_RPC.h:191
void Initialize() override
Method that allows to construct internal objects, after the required callback member methods have bee...
Definition: Server_Side_RPC.h:196
bool RPC_Unsubscribe()
Unsubcribes all server-side RPC callbacks. See https://thingsboard.io/docs/user-guide/rpc/#server-sid...
Definition: Server_Side_RPC.h:99