ThingsBoard Client SDK 0.16.0
Client SDK to connect with ThingsBoard IoT Platform from IoT devices (Arduino, Espressif, etc.)
Loading...
Searching...
No Matches
Attribute_Request.h
Go to the documentation of this file.
1#ifndef Attribute_Request_h
2#define Attribute_Request_h
3
4// Local includes.
8
9
10// Attribute request API topics.
11char constexpr ATTRIBUTE_REQUEST_TOPIC[] = "v1/devices/me/attributes/request/%u";
12char constexpr ATTRIBUTE_RESPONSE_SUBSCRIBE_TOPIC[] = "v1/devices/me/attributes/response/+";
13char constexpr ATTRIBUTE_RESPONSE_TOPIC[] = "v1/devices/me/attributes/response/";
14// Client side attribute request keys.
15char constexpr CLIENT_REQUEST_KEYS[] = "clientKeys";
16char constexpr CLIENT_RESPONSE_KEY[] = "client";
17// Shared attribute request keys.
18char constexpr SHARED_REQUEST_KEY[] = "sharedKeys";
19// Log messages.
20#if THINGSBOARD_ENABLE_DEBUG
21char constexpr NO_KEYS_TO_REQUEST[] = "No keys to request were given";
22char constexpr ATT_KEY_NOT_FOUND[] = "Attribute key in Attribute_Request_Callback is NULL";
23char constexpr ATT_KEY_IS_NULL[] = "Requested attribute key is NULL";
24#endif // THINGSBOARD_ENABLE_DEBUG
25#if !THINGSBOARD_ENABLE_DYNAMIC
26char constexpr CLIENT_SHARED_ATTRIBUTE_SUBSCRIPTIONS[] = "client or shared attribute request";
27#endif // THINGSBOARD_ENABLE_DYNAMIC
28
29
33#if THINGSBOARD_ENABLE_DYNAMIC
34template <typename Logger = DefaultLogger>
35#else
41template<size_t MaxSubscriptions = DEFAULT_SUBSCRIPTION_AMOUNT, size_t MaxAttributes = DEFAULT_ATTRIBUTES_AMOUNT, typename Logger = DefaultLogger>
42#endif // THINGSBOARD_ENABLE_DYNAMIC
44#if THINGSBOARD_ENABLE_DYNAMIC
47#else
50#endif // THINGSBOARD_ENABLE_DYNAMIC
51
52 public:
54 Attribute_Request() = default;
55
56 ~Attribute_Request() override = default;
57
68 return Attributes_Request(callback, CLIENT_REQUEST_KEYS, CLIENT_RESPONSE_KEY);
69 }
70
81 return Attributes_Request(callback, SHARED_REQUEST_KEY, SHARED_RESPONSE_KEY);
82 }
83
85 return API_Process_Type::JSON;
86 }
87
88 void Process_Response(char const * topic, uint8_t * payload, uint32_t length) override {
89 // Nothing to do
90 }
91
92 void Process_Json_Response(char const * topic, JsonDocument const & data) override {
93 auto const request_id = Helper::Split_Topic_Into_Request_ID(topic, strlen(ATTRIBUTE_RESPONSE_TOPIC));
94 JsonObjectConst object = data.template as<JsonObjectConst>();
95
96 Timeoutable_Request * request_callback = nullptr;
97#if THINGSBOARD_ENABLE_STL
98 auto it = std::find_if(m_attribute_request_callbacks.begin(), m_attribute_request_callbacks.end(), [&request_id](Callback_Value & attribute_request) {
99 return attribute_request.Get_Request_ID() == request_id;
100 });
101 if (it != m_attribute_request_callbacks.end()) {
102 auto & attribute_request = *it;
103#else
104 for (auto it = m_attribute_request_callbacks.begin(); it != m_attribute_request_callbacks.end(); ++it) {
105 auto & attribute_request = *it;
106
107 if (attribute_request.Get_Request_ID() != request_id) {
108 continue;
109 }
110#endif // THINGSBOARD_ENABLE_STL
111 char const * attribute_response_key = attribute_request.Get_Attribute_Key();
112 if (attribute_response_key == nullptr) {
113#if THINGSBOARD_ENABLE_DEBUG
114 Logger::printfln(ATT_KEY_NOT_FOUND);
115#endif // THINGSBOARD_ENABLE_DEBUG
116 goto delete_callback;
117 }
118
119 if (object.containsKey(attribute_response_key)) {
120 object = object[attribute_response_key];
121 }
122
123 request_callback = &attribute_request.Get_Request_Timeout();
124 request_callback->Stop_Timeout_Timer();
125 attribute_request.Call_Callback(object);
126
127 delete_callback:
128 // Delete callback because the changes have been requested and the callback is no longer needed
129 m_attribute_request_callbacks.erase(it);
130#if !THINGSBOARD_ENABLE_STL
131 break;
132#endif // !THINGSBOARD_ENABLE_STL
133 }
134
135 // Unsubscribe from the shared attribute request topic,
136 // if we are not waiting for any further responses with shared attributes from the server.
137 // Will be resubscribed if another request is sent anyway
138 if (m_attribute_request_callbacks.empty()) {
139 (void)Attributes_Request_Unsubscribe();
140 }
141 }
142
143 bool Is_Response_Topic_Matching(char const * topic) const override {
144 return strncmp(ATTRIBUTE_RESPONSE_TOPIC, topic, strlen(ATTRIBUTE_RESPONSE_TOPIC)) == 0;
145 }
146
147 bool Unsubscribe() override {
148 return Attributes_Request_Unsubscribe();
149 }
150
152 m_attribute_request_callbacks.clear();
153 return true;
154 }
155
156#if !THINGSBOARD_USE_ESP_TIMER
157 void loop() override {
158 for (auto & attribute_request : m_attribute_request_callbacks) {
159 auto & request_callback = attribute_request.Get_Request_Timeout();
160 request_callback.Update_Timeout_Timer();
161 }
162 }
163#endif // !THINGSBOARD_USE_ESP_TIMER
164
165 void Initialize() override {
166 // Nothing to do
167 }
168
170 m_send_json_callback.Set_Callback(send_json_callback);
171 m_subscribe_topic_callback.Set_Callback(subscribe_topic_callback);
172 m_unsubscribe_topic_callback.Set_Callback(unsubscribe_topic_callback);
173 m_get_request_id_callback.Set_Callback(get_request_id_callback);
174 }
175
176 private:
177 bool Attributes_Request(Callback_Value const & callback, char const * attribute_request_key, char const * attribute_response_key) {
178 auto const & attributes = callback.Get_Attributes();
179
180 // Check if any sharedKeys were requested
181 if (attributes.empty()) {
182#if THINGSBOARD_ENABLE_DEBUG
183 Logger::printfln(NO_KEYS_TO_REQUEST);
184#endif // THINGSBOARD_ENABLE_DEBUG
185 return false;
186 }
187 else if (attribute_request_key == nullptr || attribute_response_key == nullptr) {
188#if THINGSBOARD_ENABLE_DEBUG
189 Logger::printfln(ATT_KEY_NOT_FOUND);
190#endif // THINGSBOARD_ENABLE_DEBUG
191 return false;
192 }
193
194 Callback_Value * registered_callback = nullptr;
195 if (!Attributes_Request_Subscribe(callback, registered_callback)) {
196 return false;
197 }
198 else if (registered_callback == nullptr) {
199 return false;
200 }
201
202 // String are const char* and therefore stored as a pointer --> zero copy, meaning the size for the strings is 0 bytes,
203 // Data structure size depends on the amount of key value pairs passed + the default clientKeys or sharedKeys
204 // See https://arduinojson.org/v6/assistant/ for more information on the needed size for the JsonDocument
205 StaticJsonDocument<JSON_OBJECT_SIZE(1)> request_buffer;
206
207 // Calculate the size required for the char buffer containing all the attributes seperated by a comma,
208 // before initalizing it so it is possible to allocate it on the stack
209 size_t size = 0U;
210 for (auto const & att : attributes) {
212 continue;
213 }
214
215 size += strlen(att);
216 size += strlen(",");
217 }
218
219 // Add space for null termination at the end of the char array, has to be done,
220 // because we later cast it to const char *, meaning the original size information is lost
221 // and is instead handled by null termination at the end of the string
222 size += 1;
223
224 // Initalizes complete array to 0, required because strncat needs both destination and source to contain proper null terminated strings
225 char request[size] = {};
226 for (auto const & att : attributes) {
228#if THINGSBOARD_ENABLE_DEBUG
229 Logger::printfln(ATT_KEY_IS_NULL);
230#endif // THINGSBOARD_ENABLE_DEBUG
231 continue;
232 }
233
234 strncat(request, att, size);
235 size -= strlen(att);
236 strncat(request, ",", size);
237 size -= strlen(",");
238 }
239
240 // Ensure to cast to const, this is done so that ArduinoJson does not copy the value but instead simply store the pointer, which does not require any more memory,
241 // besides the base size needed to allocate one key-value pair. Because if we don't the char array would be copied
242 // and because there is not enough space the value would simply be "undefined" instead. Which would cause the request to not be sent correctly
243 request_buffer[attribute_request_key] = static_cast<const char*>(request);
244
245 size_t * p_request_id = m_get_request_id_callback.Call_Callback();
246 if (p_request_id == nullptr) {
247 Logger::printfln(REQUEST_ID_NULL);
248 return false;
249 }
250 auto & request_id = *p_request_id;
251
252 registered_callback->Set_Request_ID(++request_id);
253 registered_callback->Set_Attribute_Key(attribute_response_key);
254 auto & request_callback = registered_callback->Get_Request_Timeout();
255 request_callback.Start_Timeout_Timer();
256
257 char topic[Helper::Calculate_Print_Size(ATTRIBUTE_REQUEST_TOPIC, request_id)] = {};
258 (void)snprintf(topic, sizeof(topic), ATTRIBUTE_REQUEST_TOPIC, request_id);
259 return m_send_json_callback.Call_Callback(topic, request_buffer);
260 }
261
266 bool Attributes_Request_Subscribe(Callback_Value const & callback, Callback_Value * & registered_callback) {
267#if !THINGSBOARD_ENABLE_DYNAMIC
268 if (m_attribute_request_callbacks.size() + 1 > m_attribute_request_callbacks.capacity()) {
270 return false;
271 }
272#endif // !THINGSBOARD_ENABLE_DYNAMIC
273 if (!m_subscribe_topic_callback.Call_Callback(ATTRIBUTE_RESPONSE_SUBSCRIBE_TOPIC)) {
275 return false;
276 }
277 m_attribute_request_callbacks.push_back(callback);
278 registered_callback = &m_attribute_request_callbacks.back();
279 return true;
280 }
281
284 bool Attributes_Request_Unsubscribe() {
286 return m_unsubscribe_topic_callback.Call_Callback(ATTRIBUTE_RESPONSE_SUBSCRIBE_TOPIC);
287 }
288
289 Callback<bool, char const * const, JsonDocument const &> m_send_json_callback = {}; // Send json document callback
290 Callback<bool, char const * const> m_subscribe_topic_callback = {}; // Subscribe mqtt topic client callback
291 Callback<bool, char const * const> m_unsubscribe_topic_callback = {}; // Unubscribe mqtt topic client callback
292 Callback<size_t *> m_get_request_id_callback = {}; // Get internal request id callback
293 Callback_Container m_attribute_request_callbacks = {}; // Client-side or shared attribute request callback vector
294};
295
296#endif // Attribute_Request_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 ATTRIBUTE_RESPONSE_SUBSCRIBE_TOPIC[]
Definition: Attribute_Request.h:12
char constexpr CLIENT_SHARED_ATTRIBUTE_SUBSCRIPTIONS[]
Definition: Attribute_Request.h:26
char constexpr CLIENT_RESPONSE_KEY[]
Definition: Attribute_Request.h:16
char constexpr ATTRIBUTE_RESPONSE_TOPIC[]
Definition: Attribute_Request.h:13
char constexpr SHARED_REQUEST_KEY[]
Definition: Attribute_Request.h:18
char constexpr ATTRIBUTE_REQUEST_TOPIC[]
Definition: Attribute_Request.h:11
char constexpr CLIENT_REQUEST_KEYS[]
Definition: Attribute_Request.h:15
char constexpr MAX_SUBSCRIPTIONS_TEMPLATE_NAME[]
Definition: IAPI_Implementation.h:22
char constexpr SHARED_RESPONSE_KEY[]
Definition: IAPI_Implementation.h:31
char constexpr SUBSCRIBE_TOPIC_FAILED[]
Definition: IAPI_Implementation.h:23
char constexpr MAX_SUBSCRIPTIONS_EXCEEDED[]
Definition: IAPI_Implementation.h:20
char constexpr REQUEST_ID_NULL[]
Definition: IAPI_Implementation.h:24
Client-side or shared attributes request callback wrapper, contains the needed configuration settings...
Definition: Attribute_Request_Callback.h:25
Timeoutable_Request & Get_Request_Timeout()
Gets the request timeout callback.
Definition: Attribute_Request_Callback.h:132
size_t const & Get_Request_ID() const
Gets the unique request identifier that is connected to the original request.
Definition: Attribute_Request_Callback.h:71
char const * Get_Attribute_Key() const
Gets the response key of the json array key-value pair, that we expect the client-side or shared attr...
Definition: Attribute_Request_Callback.h:87
Handles the internal implementation of the ThingsBoard client and shared Attribute Request API....
Definition: Attribute_Request.h:43
void Initialize() override
Method that allows to construct internal objects, after the required callback member methods have bee...
Definition: Attribute_Request.h:165
~Attribute_Request() 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: Attribute_Request.h:169
bool Unsubscribe() override
Unsubcribes all callbacks, to clear up any ongoing subscriptions and stop receiving information over ...
Definition: Attribute_Request.h:147
bool Resubscribe_Permanent_Subscriptions() override
Forwards the call to let the API clear up any ongoing single-event subscriptions (Provision,...
Definition: Attribute_Request.h:151
bool Client_Attributes_Request(Callback_Value const &callback)
Requests one client-side attribute, which will call the passed callback. If the key-value pair from t...
Definition: Attribute_Request.h:67
void loop() override
Internal loop method to update inernal timers for API calls that can timeout.
Definition: Attribute_Request.h:157
void Process_Json_Response(char const *topic, JsonDocument const &data) override
Process callback that will be called upon response arrival.
Definition: Attribute_Request.h:92
bool Is_Response_Topic_Matching(char const *topic) const override
Compares received response topic and the topic this api implementation handles responses on,...
Definition: Attribute_Request.h:143
void Process_Response(char const *topic, uint8_t *payload, uint32_t length) override
Process callback that will be called upon response arrival.
Definition: Attribute_Request.h:88
API_Process_Type Get_Process_Type() const override
Returns the way the server response should be processed.
Definition: Attribute_Request.h:84
Attribute_Request()=default
Constructor.
bool Shared_Attributes_Request(Callback_Value const &callback)
Requests one shared attribute, which will call the passed callback. If the key-value pair from the se...
Definition: Attribute_Request.h:80
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
size_type size() const
Gets the current amount of elements in the underlying data container.
Definition: Container.h:147
reference back()
Returns a reference to the last element of the array. If the array is empty this method will assert a...
Definition: Container.h:196
iterator begin()
Returns an iterator to the first element of the underlying data container. If the array is empty,...
Definition: Container.h:165
void erase(const_iterator position)
Removes the element at the given position, has to move all element one to the left if the iterator do...
Definition: Container.h:284
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 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
General purpose request callback that can timeout if the response to the request is not received in t...
Definition: Timeoutable_Request.h:10
void Update_Timeout_Timer()
Updates the internal timeout timer.
Definition: Timeoutable_Request.cpp:20
void Start_Timeout_Timer()
Starts the internal timeout timer if we actually received a configured valid timeout time and a valid...
Definition: Timeoutable_Request.cpp:25
void Stop_Timeout_Timer()
Stops the internal timeout timer, is called as soon as an answer is received from the cloud....
Definition: Timeoutable_Request.cpp:32