REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
protocol.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3//
4// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
5
6#include <net/auth.h>
7#include <net/settings.h>
8#include <protocol/protocol.h>
9#include <protocol/protocol_oob.h>
10#include <protocol/registry.h>
11#include <run/run_manager.h>
12#include <utils/StringPrint.h>
13#include <utils/logging.h>
14#include <utils/serial_lines.h>
15
16#include <algorithm>
17#include <cctype>
18#include <locale>
19#include <utils/streaming_json.h>
20
21FLASHMEM void trim(char *str) {
22 unsigned int start = 0, end = strlen(str) - 1;
23
24 // Remove leading whitespace
25 while (isspace(str[start])) {
26 start++;
27 }
28
29 // Remove trailing whitespace
30 while (end > start && isspace(str[end])) {
31 end--;
32 }
33
34 // If the string was trimmed, adjust the null terminator
35 if (start > 0 || end < (strlen(str) - 1)) {
36 memmove(str, str + start, end - start + 1);
37 str[end - start + 1] = '\0';
38 }
39}
40
41FLASHMEM void msg::JsonLinesProtocol::init(size_t envelope_size) {
42 envelope_in = new DynamicJsonDocument(envelope_size);
43 envelope_out = new DynamicJsonDocument(envelope_size);
44}
45
46FLASHMEM void msg::JsonLinesProtocol::handleMessage(net::auth::AuthentificationContext &user_context,
47 Print &output) {
48 auto envelope_out = this->envelope_out->to<JsonObject>();
49 auto envelope_in = this->envelope_in->as<JsonObjectConst>();
50
51 // Unpack metadata from envelope
52 std::string msg_id = envelope_in["id"];
53 std::string msg_type = envelope_in["type"];
54 bool perf_trace = envelope_in["perf_trace"] | false;
55 elapsedMicros handle_message_time_us;
56
57 // Create return envelope
58 envelope_out.clear();
59 envelope_out["id"] = msg_id;
60 envelope_out["type"] = msg_type;
61 auto msg_out = envelope_out.createNestedObject("msg");
62
63 // Select message handler
64 auto msg_handler = msg::handlers::Registry::get().lookup(msg_type);
65 auto requiredClearance = msg::handlers::Registry::get().requiredClearance(msg_type);
66 int return_code = 0;
67 if (!msg_handler) {
68 return_code = -10; // No handler for message known
69 msg_out["error"] = "Unknown message type. Try this message: {'type':'help'}.";
70 } else if (!user_context.can_do(requiredClearance)) {
71 return_code = -20;
72 msg_out["error"] = "User is not authorized for action";
73 } else {
74 auto msg_in = envelope_in["msg"].as<JsonObjectConst>();
75 return_code = msg_handler->handle(msg_in, msg_out);
76 if (return_code == msg::handlers::MessageHandler::not_implemented) {
77 return_code = msg_handler->handle(msg_in, msg_out, user_context);
78 if (return_code == msg::handlers::MessageHandler::not_implemented) {
79 // we don't use envelope_out but stream instead
80 auto envelope_and_msg_out = utils::StreamingJson(output);
81 envelope_and_msg_out.begin_dict();
82 envelope_and_msg_out.kv("id", msg_id);
83 envelope_and_msg_out.kv("type", msg_type);
84 envelope_and_msg_out.key("msg");
85 // it is now the job of the handler to wrap his content into
86 // begin_dict() / end_dict() or begin_list() / end_list()
87 return_code = msg_handler->handle(msg_in, envelope_and_msg_out);
88 // Attention: If the msg_handler does not return anything here,
89 // we will get malformatted message of the kind
90 // ..."type":"foo","msg":"code":123}
91 if (return_code == msg::handlers::MessageHandler::not_implemented) {
92 // *assume* that no message has been given: Provide an empty one.
93 envelope_and_msg_out.begin_dict();
94 envelope_and_msg_out.end_dict();
95 }
96 if (return_code != 0) {
97 LOG_ALWAYS("Error while handling streaming message.")
98 }
99 envelope_and_msg_out.kv("code", return_code);
100 if (perf_trace)
101 envelope_and_msg_out.kv("perf_handle_message_time_us", handle_message_time_us);
102 envelope_and_msg_out.end_dict(); // envelope
103 return;
104 }
105 }
106 }
107
108 if (return_code != 0) {
109 // Message could not be handled, mark envelope as unsuccessful
110 envelope_out["error"] = msg_out["error"];
111 envelope_out.remove("msg");
112 LOG_ALWAYS("Error while handling message.");
113 }
114
115 envelope_out["code"] = return_code;
116 if (perf_trace)
117 envelope_out["perf_handle_message_time_us"] = (unsigned long)handle_message_time_us;
118
119 serializeJson(envelope_out, output);
120 // notice we don't send a NL here, has to be done by the callee!
121}
122
123utils::SerialLineReader serial_line_reader;
124
125FLASHMEM void msg::JsonLinesProtocol::process_serial_input(net::auth::AuthentificationContext &user_context) {
126 char *line = serial_line_reader.line_available();
127 if (!line)
128 return;
129
130 auto error = deserializeJson(*envelope_in, line);
131 if (error == DeserializationError::Code::EmptyInput) {
132 // do nothing, just ignore empty input.
133 } else if (error) {
134 trim(line); // for not-destroying the output
135 LOG4("Malformed serial line input. Expecting JSON-Lines. Error: ", error.c_str(), ". Input was: ", line);
136 } else {
137 handleMessage(user_context, Serial);
138 Serial.println();
139 }
140}
141
142FLASHMEM bool msg::JsonLinesProtocol::process_tcp_input(net::EthernetClient &connection,
143 net::auth::AuthentificationContext &user_context) {
144 auto error = deserializeJson(*envelope_in, connection);
145 if (error == DeserializationError::Code::EmptyInput) {
146 // Serial.print(".");
147 } else if (error) {
148 LOG2("Malformed TCP/IP input. Expecting JSON Lines. Error: ", error.c_str());
149 } else {
150 handleMessage(user_context, connection);
151 // serializeJson(envelope_out->as<JsonObject>(), Serial);
152 // serializeJson(envelope_out->as<JsonObject>(), connection);
153 if (!connection.writeFully("\n"))
154 return true; // break;
155 }
156 return false;
157}
158
159FLASHMEM void msg::JsonLinesProtocol::process_string_input(const std::string &envelope_in_str,
160 std::string &envelope_out_str,
161 net::auth::AuthentificationContext &user_context) {
162 auto error = deserializeJson(*envelope_in, envelope_in_str);
163 if (error == DeserializationError::Code::EmptyInput) {
164 // Serial.print(".");
165 } else if (error) {
166 envelope_out_str = "{'error':'Error while parsing JSON, error message: ";
167 envelope_out_str += error.c_str();
168 envelope_out_str += "'}\n";
169 } else {
170 utils::StringPrint s;
171 handleMessage(user_context, s);
172 envelope_out_str = s.str();
173 // serializeJson(envelope_out->as<JsonObject>(), Serial);
174 // serializeJson(envelope_out->as<JsonObject>(), envelope_out_str);
175 }
176}
177
178FLASHMEM void msg::JsonLinesProtocol::process_out_of_band_handlers(carrier::Carrier &carrier_) {
179 if (!run::RunManager::get().queue.empty()) {
180 // Currently, the following prints to all connected clients.
181 client::RunStateChangeNotificationHandler run_state_change_handler{broadcast, *envelope_out};
182 client::RunDataNotificationHandler run_data_handler{carrier_, broadcast};
183 client::StreamingRunDataNotificationHandler alternative_run_data_handler{carrier_, broadcast};
184
185 // TODO: Remove after debugging
186 // LOGMEV("Protocol OOB RunManager now broadcasting to %d targets\n", broadcast.size());
187 // broadcast.println("{'TEST':'TEST'}");
188 run::RunManager::get().run_next(carrier_, &run_state_change_handler, &run_data_handler,
189 &alternative_run_data_handler);
190 }
191}
auto & carrier_
FLASHMEM void trim(char *str)
Definition protocol.cpp:21
utils::SerialLineReader serial_line_reader
Definition protocol.cpp:123