SensESP 3.3.0
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
signalk_put_request.cpp
Go to the documentation of this file.
2
4#include "sensesp_app.h"
5
6namespace sensesp {
7
8std::map<String, SKRequest::PendingRequest*> SKRequest::request_map_;
10SemaphoreHandle_t SKRequest::request_map_mutex_ =
11 xSemaphoreCreateMutexStatic(&SKRequest::request_map_mutex_buffer_);
12
13String SKRequest::send_request(JsonDocument& request,
14 std::function<void(JsonDocument&)> callback,
15 uint32_t timeout) {
16 // Create a new PendingRequest object to track this request...
17 auto* pending_request = new PendingRequest();
18
19 // Generate a uuid for the request...
20 pending_request->request_id = generate_uuid4();
21
22 // Save the callback for future processing...
23 pending_request->callback = callback;
24
25 // After 10 seconds, if we haven't already handled a response,
26 // assume its not coming.
27 pending_request->timeout_cleanup =
28 event_loop()->onDelay(timeout, [pending_request]() {
29 // Mark the delay event as it will be cleaned up by the ReactESP
30 // framework if this executes...
31 ESP_LOGW(__FILENAME__, "No response from server for request Id %s",
32 pending_request->request_id.c_str());
33 pending_request->timeout_cleanup = nullptr;
34 xSemaphoreTake(request_map_mutex_, portMAX_DELAY);
35 SKRequest::remove_request(pending_request->request_id);
36 xSemaphoreGive(request_map_mutex_);
37 });
38
39 xSemaphoreTake(request_map_mutex_, portMAX_DELAY);
40 request_map_[pending_request->request_id] = pending_request;
41 xSemaphoreGive(request_map_mutex_);
42
43 // Now, send the actual request to the server...
44 request["requestId"] = pending_request->request_id;
45
46 String request_txt;
47 serializeJson(request, request_txt);
48 ESP_LOGD(__FILENAME__, "Sending websocket request to server: %s",
49 request_txt.c_str());
50
51 SensESPApp::get()->get_ws_client()->sendTXT(request_txt);
52
53 return pending_request->request_id;
54}
55
57 auto iterator = request_map_.find(request_id);
58 if (iterator != request_map_.end()) {
59 return (iterator->second);
60 }
61 return nullptr;
62}
63
64void SKRequest::handle_response(JsonDocument& response) {
65 String request_id = response["requestId"];
66 xSemaphoreTake(request_map_mutex_, portMAX_DELAY);
67 PendingRequest* pending_request = get_request(request_id);
68 if (pending_request != nullptr) {
69 pending_request->callback(response);
70
71 // Now, are we done?
72 String state = response["state"];
73 if (!state.equalsIgnoreCase("PENDING")) {
74 remove_request(request_id);
75 }
76 } else {
77 ESP_LOGW(__FILENAME__,
78 "Received request response for an untracked request: %s",
79 request_id.c_str());
80 }
81 xSemaphoreGive(request_map_mutex_);
82}
83
84void SKRequest::remove_request(String request_id) {
85 // Note: caller must hold request_map_mutex_ when calling from
86 // handle_response. The timeout lambda acquires it independently.
87 PendingRequest* pending_request = SKRequest::get_request(request_id);
88 if (pending_request != nullptr) {
89 // First, stop any pending timeout handlers...
90 if (pending_request->timeout_cleanup != nullptr) {
91 // The timeout code was not called, so just
92 // remove it from the ReactESP execution queue...
93 pending_request->timeout_cleanup->remove(
94 event_loop());
95 }
96
97 // Now, remove the request from the map...
98 request_map_.erase(request_id);
99
100 // Finally, discard the request tracker...
101 delete pending_request;
102 }
103}
104
106 const String& config_path, uint32_t timeout)
108 load();
109}
110
112 JsonDocument doc;
113 JsonObject root = doc.to<JsonObject>();
114 JsonObject put_data = root["put"].to<JsonObject>();
115 put_data["path"] = sk_path;
116 set_put_value(put_data);
118 doc, [this](JsonDocument& response) { this->on_response(response); },
119 timeout);
120}
121
123 xSemaphoreTake(request_map_mutex_, portMAX_DELAY);
124 bool pending = (get_request(this->pending_request_id_) != nullptr);
125 xSemaphoreGive(request_map_mutex_);
126 return pending;
127}
128
129void SKPutRequestBase::on_response(JsonDocument& response) {
130 String request_id = response["requestId"];
131 String state = response["state"];
132 ESP_LOGD(__FILENAME__, "Response %s received for PUT request: %s",
133 state.c_str(), request_id.c_str());
134}
135
136bool SKPutRequestBase::to_json(JsonObject& root) {
137 root["sk_path"] = sk_path;
138 return true;
139}
140
141bool SKPutRequestBase::from_json(const JsonObject& config) {
142 const String expected[] = {"sk_path"};
143 for (auto str : expected) {
144 if (!config[str].is<JsonVariant>()) {
145 return false;
146 }
147 }
148 this->sk_path = config["sk_path"].as<String>();
149 return true;
150}
151
152} // namespace sensesp
virtual bool load() override
Load and populate the object from a persistent storage.
Definition saveable.cpp:8
FileSystemSaveable(const String &config_path)
Definition saveable.h:63
virtual void set_put_value(JsonObject &put_data)=0
SKPutRequestBase(const String &sk_path, const String &config_path="", uint32_t timeout=5000)
virtual bool from_json(const JsonObject &config) override
virtual bool to_json(JsonObject &root) override
virtual void on_response(JsonDocument &response)
std::function< void(JsonDocument &)> callback
static void handle_response(JsonDocument &response)
static SemaphoreHandle_t request_map_mutex_
static StaticSemaphore_t request_map_mutex_buffer_
static String send_request(JsonDocument &request, std::function< void(JsonDocument &)> callback, uint32_t timeout=5000)
static PendingRequest * get_request(String request_id)
static std::map< String, PendingRequest * > request_map_
static void remove_request(String request_id)
static std::shared_ptr< SensESPApp > get()
Get the singleton instance of the SensESPApp.
Definition sensesp_app.h:54
std::shared_ptr< reactesp::EventLoop > event_loop()
Definition sensesp.cpp:9
String generate_uuid4()
Generate a random UUIDv4 string.
Definition uuid.cpp:5