Loading...
Searching...
No Matches
BitfocusContext.hpp
1#pragma once
2#include <boost/asio/io_context.hpp>
3#include <boost/asio/ip/udp.hpp>
4
5#include <QColor>
6#include <QCoreApplication>
7#include <QElapsedTimer>
8#include <QJsonArray>
9#include <QJsonDocument>
10#include <QJsonObject>
11#include <QMap>
12#include <QProcess>
13#include <QRegularExpression>
14#include <QSocketNotifier>
15#include <QUuid>
16
17#include <array>
18#include <functional>
19#include <optional>
20#include <set>
21
22#include <verdigris>
23#if !defined(_WIN32)
24#include <sys/socket.h>
25#include <sys/un.h>
26
27#include <fcntl.h>
28#include <stdlib.h>
29#include <unistd.h>
30#endif
31namespace bitfocus
32{
33
34using module_configuration = std::map<QString, QVariant>;
35
37QString nodeExecutable(const QString& nodeVersion);
38
40{
42 {
43 struct choice
44 {
45 QString id;
46 QString label;
48 QJsonValue value;
49 };
50
51 QString id;
52 QString label;
53 // "static-text", "textinput", "secret-text", "number", "checkbox", "dropdown",
54 // "multidropdown", "colorpicker", "bonjour-device", "custom-variable"
55 QString type;
56 QVariant value;
57 QString tooltip;
58 QString regex;
59 // ex. : "e=>!![\"TF\",\"DM3\",\"DM7\"].includes(e.model)&&(e.kaInterval=e.kaIntervalH,!0)",
60 QString isVisibleFn;
61 QVariant min;
62 QVariant max;
63 QVariant step;
64 std::vector<choice> choices;
65 QVariant default_value{}; // true, a number, a string etc
66 QJsonValue default_json{QJsonValue::Undefined};
67 QString returnType;
68 double width{};
69 bool allowCustom{};
70
71 bool hasDefault() const noexcept { return !default_json.isUndefined(); }
73 bool isInteger() const noexcept;
74 };
75
77 {
78 bool hasLearn{};
79 QString name;
80 std::vector<config_field> options;
81 };
83 {
84 QString name;
85 QVariant value;
86 };
88 {
89 bool hasLearn{};
90 bool isInverted{false};
91 bool disabled{false};
92 int upgradeIndex{0};
93 QString name;
94 std::vector<config_field> options;
95 QString type;
96 struct
97 {
98 QColor bgcolor;
99 QColor color;
100 } defaultStyle;
101 };
103 {
104 QString name;
105 QString category;
106 QString text;
107 QString type;
108 std::vector<QVariantMap> feedbacks;
109 std::vector<QVariantMap> steps;
110 };
111
113 {
114 QString id;
115 QString controlId;
116 QString definitionId;
117 QVariantMap options;
118 int imageWidth{72};
119 int imageHeight{58};
120 int upgradeIndex{-1};
121 bool disabled{false};
122 bool isInverted{false};
123 };
124
125 std::map<QString, action_definition> actions;
126 std::map<QString, variable_definition> variables;
127 std::map<QString, feedback_definition> feedbacks;
128 std::map<QString, preset_definition> presets;
129 std::vector<config_field> config_fields;
130 module_configuration config;
132 std::optional<int> upgradeIndex;
133};
134
136QJsonValue defaultOptionValue(const module_data::config_field& f);
137
139QJsonValue toModuleValue(const module_data::config_field& f, const QVariant& v);
140
142double floatToDouble(float f);
143
145QVariant widenFloat(const QVariant& v);
146
148QJsonValue ejsonDecode(const QJsonValue& v);
149
150// note: callback id shared between both ends so every message has to be processed in order
151#if defined(_WIN32)
152struct win32_handles;
153struct module_handler_base : public QObject
154{
155 std::unique_ptr<win32_handles> handles{};
156 explicit module_handler_base(
157 QString node_path, QString module_path, QString entrypoint,
158 QString connection_id);
159 virtual ~module_handler_base();
160 void do_write(std::string_view res);
162 bool restart_process();
163 virtual void processMessage(std::string_view) = 0;
164 virtual void on_process_exited() { }
165
167 int release_grace_ms{};
168};
169#else
170struct module_handler_base : public QObject
171{
174 std::size_t max_message_size = 256 * 1024 * 1024;
175
176 char buf[16 * 4096]{};
177 std::vector<char> queue;
179 std::size_t scanned{};
181 std::string pending_write;
182 std::unique_ptr<QProcess> process;
183 QSocketNotifier* socket{};
184 QSocketNotifier* write_socket{};
185 int pfd[2]{-1, -1};
187 bool discarding{};
188
189 explicit module_handler_base(
190 QString node_path, QString module_path, QString entrypoint,
191 QString connection_id);
192 virtual ~module_handler_base();
193
194 void on_read(QSocketDescriptor, QSocketNotifier::Type);
195 void do_write(std::string_view res);
197 bool restart_process();
198
199 virtual void processMessage(std::string_view) = 0;
200 virtual void on_process_exited() { }
201
203 int release_grace_ms{};
204
205private:
206 void start_process();
207 void release_process(int grace_ms);
208 void process_queue();
209 void flush_writes();
210 void on_disconnected();
211
212 QString m_nodePath, m_modulePath, m_entrypoint, m_connectionId;
213};
214#endif
215
216struct shared_udp_port;
218{
219 W_OBJECT(module_handler)
220public:
221 explicit module_handler(
222 QString path, QString entrypoint, QString nodeVersion, QString apiversion,
223 module_configuration config, QString label = {}, bool firstInit = false,
224 std::optional<int> upgradeIndex = {},
225 std::optional<std::set<QString>> secretKeys = {});
226 virtual ~module_handler();
227
228 using module_handler_base::do_write;
229 void do_write(QString res);
230 QString jsonToString(QJsonObject obj);
231
232 void afterRegistration(std::function<void()>);
233
234 void processMessage(std::string_view v) override;
235 void on_process_exited() override;
236
237 int writeRequest(QString name, QString p);
238 void writeNotification(QString name, QString p);
239 void writeReply(QJsonValue id, QString p);
240 void writeReply(QJsonValue id, QJsonObject p);
241 void writeReply(QJsonValue id, QString p, bool success);
242 void writeReply(QJsonValue id, QJsonObject p, bool success);
243
244 // Module -> app (requests handling)
245 void on_register(QJsonValue id);
246 void on_setActionDefinitions(QJsonArray obj);
247 void on_setVariableDefinitions(QJsonArray obj, QJsonArray values);
248 void on_setFeedbackDefinitions(QJsonArray obj);
249 void on_setPresetDefinitions(QJsonArray obj);
250 void on_setVariableValues(QJsonArray obj);
251 void on_set_status(QJsonObject obj);
252 void on_log_message(QJsonObject obj);
253 void on_saveConfig(QJsonObject obj);
254 void on_parseVariablesInString(QJsonValue id, QJsonObject obj);
255 void on_updateFeedbackValues(QJsonObject obj);
256 void on_recordAction(QJsonObject obj);
257 void on_setCustomVariable(QJsonObject obj);
258 void on_sharedUdpSocketJoin(QJsonValue id, QJsonObject obj);
259 void on_sharedUdpSocketLeave(QJsonObject obj);
260 void on_sharedUdpSocketSend(QJsonObject obj);
261 void on_send_osc(QJsonObject obj);
262
263 module_data::config_field parseConfigField(const QJsonObject& f);
264
265 // Module -> app (replies handling)
266 void on_response_configFields(QJsonArray fields);
267
268 // App -> module
269
270 int init(QString label);
271 void send_success(QJsonValue id);
272 void updateConfigAndLabel(QString label, module_configuration conf);
273 int requestConfigFields();
274 void updateFeedbacks(const std::map<QString, module_data::feedback_instance>& feedbacks);
275 void feedbackLearnValues();
276 void feedbackDelete();
277 void variablesChanged();
278 void actionUpdate();
279 void actionDelete();
280 void actionLearnValues();
281 void actionRun(std::string_view act, QVariantMap options);
282 void destroy();
283 void executeHttpRequest(
284 const QString& method, const QString& path, const QString& body,
285 const QMap<QString, QString>& headers, const QMap<QString, QString>& query,
286 std::function<void(int status, QMap<QString, QString> respHeaders, QString respBody)>
287 callback);
288 void startStopRecordingActions();
289 void sharedUdpSocketMessage(
290 const QString& handleId, int port, const QByteArray& data, const QString& address,
291 int sourcePort, bool ipv6);
292 void sharedUdpSocketError(const QString& handleId, int port, const QString& message);
293 const bitfocus::module_data& model();
295 std::optional<std::set<QString>> secretKeys() const;
296
297 enum DefinitionCategory
298 {
299 Actions = 1,
300 Feedbacks = 2,
301 Variables = 4,
302 All = 7
303 };
305 int takeChangedDefinitions() noexcept { return std::exchange(m_changedDefinitions, 0); }
306
307 void configurationParsed() W_SIGNAL(configurationParsed);
309 void reregistered() W_SIGNAL(reregistered);
311 void definitionsChanged() W_SIGNAL(definitionsChanged);
313 void configurationSaved() W_SIGNAL(configurationSaved);
314 void variableChanged(QString var, QVariant val) W_SIGNAL(variableChanged, var, val);
315 void feedbackValueChanged(QString id, QString controlId, QVariant value)
316 W_SIGNAL(feedbackValueChanged, id, controlId, value);
317
318private:
319 QJsonObject configObject(bool secrets) const;
320 void notifyDefinitionsChanged(DefinitionCategory c);
321 void completeRegistration();
322 void on_init_response(const QJsonObject& payload);
323
324 bitfocus::module_data m_model;
325
326 boost::asio::io_context m_send_service;
327 boost::asio::ip::udp::socket m_socket{m_send_service};
328
329 std::map<QString, std::shared_ptr<shared_udp_port>> m_shared_udp_handles;
330 std::map<int, std::function<void(int, QMap<QString, QString>, QString)>> m_httpCallbacks;
331 std::map<int, QString> m_pendingActions;
332
333 std::vector<std::function<void()>> m_afterRegistrationQueue;
334 std::set<QString> m_secretFields;
335 bool m_secretsKnown{};
336 QString m_label;
337 int m_cbid{1};
338 int m_actionId{};
339
340 int m_init_msg_id{-1};
341 int m_req_cfg_id{-1};
342
343 bool m_expects_label_updates{true};
344 bool m_hasHttpHandler{false};
345 bool m_registered{false};
346 bool m_firstInit{false};
347 bool m_destroyed{false};
348 bool m_definitionsPending{false};
349 int m_changedDefinitions{};
350 QJsonArray m_lastActions, m_lastFeedbacks, m_lastVariables;
351 bool m_initDone{false};
352 bool m_everRegistered{false};
353 int m_restarts{0};
354 QElapsedTimer m_uptime;
355 bool m_fieldsDone{false};
356};
357
358} // namespace bitfocus
Definition ClipMode.hpp:10
Definition BitfocusContext.hpp:77
Definition BitfocusContext.hpp:44
QJsonValue value
The id with the type the module declared.
Definition BitfocusContext.hpp:48
Definition BitfocusContext.hpp:42
bool isInteger() const noexcept
Integral numbers: all of default, min, max and step are integers.
Definition BitfocusContext.cpp:36
Definition BitfocusContext.hpp:88
Definition BitfocusContext.hpp:113
Definition BitfocusContext.hpp:103
Definition BitfocusContext.hpp:83
Definition BitfocusContext.hpp:40
std::optional< int > upgradeIndex
Upgrade scripts the configuration went through, as reported by init.
Definition BitfocusContext.hpp:132
Definition BitfocusContext.hpp:171
std::string pending_write
Bytes the module has not read yet: the socket is non-blocking.
Definition BitfocusContext.hpp:181
Definition BitfocusContext.hpp:218
int takeChangedDefinitions() noexcept
The categories changed since the last call.
Definition BitfocusContext.hpp:305
Definition BitfocusContext.win32.cpp:17