Nyx Node
Loading...
Searching...
No Matches
node.c
1/* NyxNode
2 * Author: Jérôme ODIER <jerome.odier@lpsc.in2p3.fr>
3 * SPDX-License-Identifier: GPL-2.0-only (Mongoose backend) or GPL-3.0+
4 */
5
6/*--------------------------------------------------------------------------------------------------------------------*/
7
8#include <stdio.h>
9#include <string.h>
10
11#include "nyx_node_internal.h"
12
13/*--------------------------------------------------------------------------------------------------------------------*/
14/* HELPERS */
15/*--------------------------------------------------------------------------------------------------------------------*/
16
17static str_t _safe_dup(STR_t s)
18{
19 return s != NULL && s[0] != '\0' ? nyx_string_dup(s) : NULL;
20}
21
22/*--------------------------------------------------------------------------------------------------------------------*/
23
24static bool _starts_with(const nyx_str_t topic, const nyx_str_t prefix)
25{
26 return topic.len >= prefix.len && memcmp(topic.buf, prefix.buf, prefix.len) == 0;
27}
28
29/*--------------------------------------------------------------------------------------------------------------------*/
30/* NODE */
31/*--------------------------------------------------------------------------------------------------------------------*/
32
33#define NYX_C_STR(a) {(str_t) (a), sizeof(a) - 1}
34
35static const nyx_str_t SPECIAL_TOPICS[] = {
36 NYX_C_STR("nyx/cmd/trigger_ping"),
37 NYX_C_STR("nyx/cmd/set_master_client"),
38 NYX_C_STR("nyx/cmd/json"),
39 NYX_C_STR("nyx/cmd/xml"),
40};
41
42/*--------------------------------------------------------------------------------------------------------------------*/
43
44static void _sub_object(const nyx_node_t *node, const nyx_object_t *object)
45{
46 /*----------------------------------------------------------------------------------------------------------------*/
47 #if !defined(ARDUINO)
48 /*----------------------------------------------------------------------------------------------------------------*/
49
50 if(node->enable_xml)
51 {
52 nyx_xmldoc_t *xmldoc = nyx_object_to_xmldoc(object);
53
54 if(xmldoc != NULL)
55 {
56 /*--------------------------------------------------------------------------------------------------------*/
57
58 str_t xml = nyx_xmldoc_to_string(xmldoc);
59 internal_mqtt_pub(node, nyx_str_s("nyx/xml"), nyx_str_s(xml), 2);
60 internal_indi_pub(node, nyx_str_s(xml));
61 nyx_memory_free(xml);
62
63 /*--------------------------------------------------------------------------------------------------------*/
64
65 nyx_xmldoc_free(xmldoc);
66
67 /*--------------------------------------------------------------------------------------------------------*/
68 }
69 }
70
71 /*----------------------------------------------------------------------------------------------------------------*/
72 #endif
73 /*----------------------------------------------------------------------------------------------------------------*/
74
75 str_t json = nyx_object_to_string(object);
76 internal_mqtt_pub(node, nyx_str_s("nyx/json"), nyx_str_s(json), 2);
77 ////////_indi_pub(node, nyx_str_s(json));
78 nyx_memory_free(json);
79
80 /*----------------------------------------------------------------------------------------------------------------*/
81}
82
83/*--------------------------------------------------------------------------------------------------------------------*/
84
85static void _get_properties(const nyx_node_t *node, const nyx_dict_t *dict)
86{
87 /*----------------------------------------------------------------------------------------------------------------*/
88 /* GET PROPERTIES */
89 /*----------------------------------------------------------------------------------------------------------------*/
90
91 STR_t device1;
92 STR_t name1;
93
94 if(dict != NULL)
95 {
96 device1 = nyx_dict_get_string(dict, "@device");
97 name1 = nyx_dict_get_string(dict, "@name");
98 }
99 else
100 {
101 device1 = NULL;
102 name1 = NULL;
103 }
104
105 /*----------------------------------------------------------------------------------------------------------------*/
106
107 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
108 {
109 nyx_dict_t *vector = *vector_ptr;
110
111 if((vector->base.flags & NYX_FLAGS_DISABLED) == 0)
112 {
113 /*--------------------------------------------------------------------------------------------------------*/
114
115 STR_t device2 = nyx_dict_get_string(vector, "@device");
116 STR_t name2 = nyx_dict_get_string(vector, "@name");
117
118 /*--------------------------------------------------------------------------------------------------------*/
119
120 if(device2 != NULL && name2 != NULL)
121 {
122 /*----------------------------------------------------------------------------------------------------*/
123
124 if(device1 != NULL)
125 {
126 if(strcmp(device1, device2) != 0)
127 {
128 continue;
129 }
130
131 if(name1 != NULL)
132 {
133 if(strcmp(name1, name2) != 0)
134 {
135 continue;
136 }
137 }
138 }
139
140 /*----------------------------------------------------------------------------------------------------*/
141
142 _sub_object(node, (nyx_object_t *) vector);
143
144 /*----------------------------------------------------------------------------------------------------*/
145 }
146
147 /*--------------------------------------------------------------------------------------------------------*/
148 }
149 }
150
151 /*----------------------------------------------------------------------------------------------------------------*/
152}
153
154/*--------------------------------------------------------------------------------------------------------------------*/
155
156static int _get_client_index(nyx_node_t *node, STR_t client)
157{
158 if(client == NULL)
159 {
160 client = "@INDI";
161 }
162
163 /*----------------------------------------------------------------------------------------------------------------*/
164
165 uint32_t hash = nyx_hash(strlen(client), client, NYX_OBJECT_MAGIC);
166
167 /*----------------------------------------------------------------------------------------------------------------*/
168
169 for(size_t i = 0; i < sizeof(node->client_hashes) / sizeof(uint32_t); i++)
170 {
171 if(node->client_hashes[i] == 0x00
172 ||
173 node->client_hashes[i] == hash
174 ) {
175 node->client_hashes[i] = hash;
176
177 return (int) i;
178 }
179 }
180
181 return -1;
182
183 /*----------------------------------------------------------------------------------------------------------------*/
184}
185
186/*--------------------------------------------------------------------------------------------------------------------*/
187
188static void _enable_xxx(nyx_node_t *node, const nyx_dict_t *dict, STR_t tag, int (* str_to_xxx)(STR_t), uint64_t mask)
189{
190 /*----------------------------------------------------------------------------------------------------------------*/
191
192 STR_t client = nyx_dict_get_string(dict, "@client");
193
194 int index = _get_client_index(node, client);
195
196 if(index < 0)
197 {
198 NYX_LOG_ERROR("Too many connected clients");
199
200 return;
201 }
202
203 /*----------------------------------------------------------------------------------------------------------------*/
204
205 STR_t device1 = nyx_dict_get_string(dict, "@device");
206 STR_t name1 = nyx_dict_get_string(dict, "@name");
207 STR_t value1 = nyx_dict_get_string(dict, "$");
208
209 /*----------------------------------------------------------------------------------------------------------------*/
210
211 int value = str_to_xxx(value1);
212
213 /*----------------------------------------------------------------------------------------------------------------*/
214
215 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
216 {
217 nyx_dict_t *vector = *vector_ptr;
218
219 /*------------------------------------------------------------------------------------------------------------*/
220
221 STR_t device2 = nyx_dict_get_string(vector, "@device");
222 STR_t name2 = nyx_dict_get_string(vector, "@name");
223 STR_t tag2 = nyx_dict_get_string(vector, "<>");
224
225 /*------------------------------------------------------------------------------------------------------------*/
226
227 if(device1 != NULL)
228 {
229 if(device2 == NULL || strcmp(device1, device2) != 0)
230 {
231 continue;
232 }
233
234 if(name1 != NULL)
235 {
236 if(name2 == NULL || strcmp(name1, name2) != 0)
237 {
238 continue;
239 }
240 }
241 }
242
243 /*------------------------------------------------------------------------------------------------------------*/
244
245 if(tag2 != NULL && strcmp(tag, tag2) == 0)
246 {
247 /*--------------------------------------------------------------------------------------------------------*/
248
249 switch(value)
250 {
251 /*----------------------------------------------------------------------------------------------------*/
252 /* BLOB */
253 /*----------------------------------------------------------------------------------------------------*/
254
255 case NYX_BLOB_STATE_ENABLED:
256 vector->base.flags |= UINT64_C(1) << (2 + 0 * 31 + index);
257 break;
258
259 case NYX_BLOB_STATE_DISABLED:
260 vector->base.flags &= ~(UINT64_C(1) << (2 + 0 * 31 + index));
261 break;
262
263 /*----------------------------------------------------------------------------------------------------*/
264 /* STREAM */
265 /*----------------------------------------------------------------------------------------------------*/
266
267 case NYX_STREAM_STATE_ENABLED:
268 vector->base.flags |= UINT64_C(1) << (2 + 1 * 31 + index);
269 break;
270
271 case NYX_STREAM_STATE_DISABLED:
272 vector->base.flags &= ~(UINT64_C(1) << (2 + 1 * 31 + index));
273 break;
274
275 /*----------------------------------------------------------------------------------------------------*/
276 /* INTERNAL ERROR */
277 /*----------------------------------------------------------------------------------------------------*/
278
279 default:
280 NYX_LOG_FATAL("Internal error");
281
282 /*----------------------------------------------------------------------------------------------------*/
283 }
284
285 /*--------------------------------------------------------------------------------------------------------*/
286
287 NYX_LOG_DEBUG("%s:%s %s", device2, name2, (vector->base.flags & mask) == 0 ? "disabled" : "enabled");
288
289 /*--------------------------------------------------------------------------------------------------------*/
290 }
291
292 /*------------------------------------------------------------------------------------------------------------*/
293 }
294
295 /*----------------------------------------------------------------------------------------------------------------*/
296}
297
298/*--------------------------------------------------------------------------------------------------------------------*/
299
300__NYX_INLINE__ void _enable_blob(nyx_node_t *node, const nyx_dict_t *dict)
301{
302 _enable_xxx(node, dict, "defBLOBVector", (int (*)(STR_t)) &nyx_str_to_blob_state, NYX_FLAGS_BLOB_MASK);
303}
304
305/*--------------------------------------------------------------------------------------------------------------------*/
306
307__NYX_INLINE__ void _enable_stream(nyx_node_t *node, const nyx_dict_t *dict)
308{
309 _enable_xxx(node, dict, "defStreamVector", (int (*)(STR_t)) &nyx_str_to_stream_state, NYX_FLAGS_STREAM_MASK);
310}
311
312/*--------------------------------------------------------------------------------------------------------------------*/
313
314static bool _is_allowed(const nyx_node_t *node, const nyx_dict_t *dict)
315{
316 /*----------------------------------------------------------------------------------------------------------------*/
317
318 STR_t client1 = node->master_client_message.buf;
319
320 if(client1 != NULL && strcmp(NYX_ALL, client1) == 0)
321 {
322 return true;
323 }
324
325 /*----------------------------------------------------------------------------------------------------------------*/
326
327 STR_t client2 = nyx_dict_get_string(dict, "@client");
328
329 if(client1 != NULL && client2 != NULL && strcmp(client1, client2) == 0)
330 {
331 return true;
332 }
333
334 /*----------------------------------------------------------------------------------------------------------------*/
335
336 return false;
337}
338
339/*--------------------------------------------------------------------------------------------------------------------*/
340
341static nyx_string_t OFF = {
342 .base = NYX_OBJECT(NYX_TYPE_STRING),
343 .managed = false,
344 .length = 0x000003,
345 .value = (str_t) /* NOSONAR */ "Off",
346};
347
348/*--------------------------------------------------------------------------------------------------------------------*/
349
350static void _set_properties(const nyx_node_t *node, const nyx_dict_t *dict)
351{
352 if(!_is_allowed(node, dict))
353 {
354 return;
355 }
356
357 /*----------------------------------------------------------------------------------------------------------------*/
358
359 nyx_object_t *tag1_string = nyx_dict_get(dict, "<>");
360 nyx_object_t *device1_string = nyx_dict_get(dict, "@device");
361 nyx_object_t *name1_string = nyx_dict_get(dict, "@name");
362 nyx_object_t *children1_list = nyx_dict_get(dict, "children");
363
364 /*----------------------------------------------------------------------------------------------------------------*/
365
366 if(tag1_string != NULL && tag1_string->type == NYX_TYPE_STRING
367 &&
368 device1_string != NULL && device1_string->type == NYX_TYPE_STRING
369 &&
370 name1_string != NULL && name1_string->type == NYX_TYPE_STRING
371 &&
372 children1_list != NULL && children1_list->type == NYX_TYPE_LIST
373 ) {
374 /*------------------------------------------------------------------------------------------------------------*/
375
376 STR_t tag1 = nyx_string_get((nyx_string_t *) tag1_string);
377 STR_t device1 = nyx_string_get((nyx_string_t *) device1_string);
378 STR_t name1 = nyx_string_get((nyx_string_t *) name1_string);
379
380 /*------------------------------------------------------------------------------------------------------------*/
381
382 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
383 {
384 nyx_dict_t *vector = *vector_ptr;
385
386 /*--------------------------------------------------------------------------------------------------------*/
387
388 nyx_object_t *tag2_string = nyx_dict_get(vector, "<>");
389 nyx_object_t *device2_string = nyx_dict_get(vector, "@device");
390 nyx_object_t *name2_string = nyx_dict_get(vector, "@name");
391 nyx_object_t *children2_list = nyx_dict_get(vector, "children");
392
393 /*--------------------------------------------------------------------------------------------------------*/
394
395 if(tag2_string != NULL && tag2_string->type == NYX_TYPE_STRING
396 &&
397 device2_string != NULL && device2_string->type == NYX_TYPE_STRING
398 &&
399 name2_string != NULL && name2_string->type == NYX_TYPE_STRING
400 &&
401 children2_list != NULL && children2_list->type == NYX_TYPE_LIST
402 ) {
403 /*----------------------------------------------------------------------------------------------------*/
404
405 STR_t tag2 = nyx_string_get((nyx_string_t *) tag2_string);
406 STR_t device2 = nyx_string_get((nyx_string_t *) device2_string);
407 STR_t name2 = nyx_string_get((nyx_string_t *) name2_string);
408
409 /*----------------------------------------------------------------------------------------------------*/
410
411 if(strlen(tag1) > 3 && strlen(tag2) > 3 // skip "def" and "new" suffixes
412 &&
413 strcmp(tag1 + 3, tag2 + 3) == 0
414 &&
415 strcmp(device1, device2) == 0
416 &&
417 strcmp(name1, name2) == 0
418 ) {
419 size_t idx1;
420 size_t idx2;
421
422 nyx_object_t *object1;
423 nyx_object_t *object2;
424
425 bool vector_modified = false;
426
427 uint32_t hash = nyx_hash(strlen(tag2), tag2, 0);
428
429 /*------------------------------------------------------------------------------------------------*/
430
431 STR_t rule = nyx_dict_get_string(vector, "@rule");
432
433 bool is_one_of_many = rule != NULL && strcmp(rule, "OneOfMany") == 0;
434
435 /*------------------------------------------------------------------------------------------------*/
436
437 for(nyx_list_iter_t iter1 = NYX_LIST_ITER(children1_list); nyx_list_iterate(&iter1, &idx1, &object1);)
438 {
439 if(object1->type == NYX_TYPE_DICT)
440 {
441 nyx_object_t *prop1_string = nyx_dict_get((nyx_dict_t *) object1, "@name");
442
443 if(prop1_string != NULL && prop1_string->type == NYX_TYPE_STRING)
444 {
445 STR_t prop1 = nyx_string_get((nyx_string_t *) prop1_string);
446
447 /*------------------------------------------------------------------------------------*/
448
449 for(nyx_list_iter_t iter2 = NYX_LIST_ITER(children2_list); nyx_list_iterate(&iter2, &idx2, &object2);)
450 {
451 if(object2->type == NYX_TYPE_DICT)
452 {
453 nyx_object_t *prop2_string = nyx_dict_get((nyx_dict_t *) object2, "@name");
454
455 if(prop2_string != NULL && prop2_string->type == NYX_TYPE_STRING)
456 {
457 STR_t prop2 = nyx_string_get((nyx_string_t *) prop2_string);
458
459 /*------------------------------------------------------------------------*/
460
461 bool is_current = strcmp(prop1, prop2) == 0;
462
463 if(is_current || is_one_of_many)
464 {
465 /*--------------------------------------------------------------------*/
466
467 nyx_object_t *old_value = /*--------*/ nyx_dict_get((nyx_dict_t *) object2, "$");
468 nyx_object_t *new_value = is_current ? nyx_dict_get((nyx_dict_t *) object1, "$")
469 : (nyx_object_t *) &OFF
470 ;
471
472 /*--------------------------------------------------------------------*/
473
474 bool success = false;
475 bool modified = false;
476
477 switch(hash)
478 {
479 /*----------------------------------------------------------------*/
480
481 case 0x56BE29BD: // defNumberVector
482 {
483 nyx_object_t *format_string = nyx_dict_get((nyx_dict_t *) object2, "@format");
484
485 if(format_string != NULL && format_string->type == NYX_TYPE_STRING)
486 {
487 STR_t format = nyx_string_get((nyx_string_t *) format_string);
488
489 nyx_variant_t old_val = internal_string_to_variant(format, nyx_string_get((nyx_string_t *) old_value));
490 nyx_variant_t new_val = internal_string_to_variant(format, nyx_string_get((nyx_string_t *) new_value));
491
492 switch(new_val.type)
493 {
494 case NYX_VARIANT_TYPE_INT:
495 if((success = object2->callback._int == NULL || object2->callback._int(vector, (nyx_dict_t *) object2, new_val.value._int, old_val.value._int))) {
496 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val), true);
497 }
498 break;
499 case NYX_VARIANT_TYPE_UINT:
500 if((success = object2->callback._uint == NULL || object2->callback._uint(vector, (nyx_dict_t *) object2, new_val.value._uint, old_val.value._uint))) {
501 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val), true);
502 }
503 break;
504 case NYX_VARIANT_TYPE_LONG:
505 if((success = object2->callback._long == NULL || object2->callback._long(vector, (nyx_dict_t *) object2, new_val.value._long, old_val.value._long))) {
506 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val), true);
507 }
508 break;
509 case NYX_VARIANT_TYPE_ULONG:
510 if((success = object2->callback._ulong == NULL || object2->callback._ulong(vector, (nyx_dict_t *) object2, new_val.value._ulong, old_val.value._ulong))) {
511 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val), true);
512 }
513 break;
514 case NYX_VARIANT_TYPE_DOUBLE:
515 if((success = object2->callback._double == NULL || object2->callback._double(vector, (nyx_dict_t *) object2, new_val.value._double, old_val.value._double))) {
516 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val), true);
517 }
518 break;
519 }
520 }
521 }
522
523 break;
524
525 /*----------------------------------------------------------------*/
526
527 case 0x1FD73301: // defTextVector
528 {
529 STR_t old_val = nyx_string_get((nyx_string_t *) old_value);
530 STR_t new_val = nyx_string_get((nyx_string_t *) new_value);
531
532 if((success = object2->callback._str == NULL || object2->callback._str(vector, (nyx_dict_t *) object2, new_val, old_val)))
533 {
534 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", nyx_string_dup(new_val), true);
535 }
536 }
537
538 break;
539
540 /*----------------------------------------------------------------*/
541
542 case 0xFEC07AA7: // defLightVector
543 {
544 nyx_state_t old_val = nyx_str_to_state(nyx_string_get((nyx_string_t *) old_value));
545 nyx_state_t new_val = nyx_str_to_state(nyx_string_get((nyx_string_t *) new_value));
546
547 if((success = object2->callback._int == NULL || object2->callback._int(vector, (nyx_dict_t *) object2, (int) new_val, (int) old_val)))
548 {
549 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", nyx_state_to_str(new_val), false);
550 }
551 }
552
553 break;
554
555 /*----------------------------------------------------------------*/
556
557 case 0x17C598B1: // defSwitchVector
558 {
559 nyx_onoff_t old_val = nyx_str_to_onoff(nyx_string_get((nyx_string_t *) old_value));
560 nyx_onoff_t new_val = nyx_str_to_onoff(nyx_string_get((nyx_string_t *) new_value));
561
562 if((success = object2->callback._int == NULL || object2->callback._int(vector, (nyx_dict_t *) object2, (int) new_val, (int) old_val)))
563 {
564 modified = nyx_dict_set_string((nyx_dict_t *) object2, "$", nyx_onoff_to_str(new_val), false);
565 }
566 }
567 break;
568
569 /*----------------------------------------------------------------*/
570
571 case 0x29BFE4D7: // defBLOBVector
572 {
573 /*--------------------------------------------------------*/
574
575 size_t src_size;
576 buff_t src_buff;
577
578 nyx_string_get_buff((nyx_string_t *) new_value, &src_size, &src_buff);
579
580 /*--------------------------------------------------------*/
581
582 size_t dst_size;
583 buff_t dst_buff;
584
585 if(internal_blob_is_compressed((nyx_dict_t *) object2)) {
586 dst_buff = nyx_zlib_base64_inflate(&dst_size, src_size, src_buff);
587 }
588 else {
589 dst_buff = nyx_base64_decode(&dst_size, src_size, src_buff);
590 }
591
592 /*--------------------------------------------------------*/
593
594 if((success = object2->callback._buffer == NULL || object2->callback._buffer(vector, (nyx_dict_t *) object2, dst_size, dst_buff)))
595 {
596 modified = nyx_dict_set_buff((nyx_dict_t *) object2, "$", dst_size, dst_buff, true);
597 }
598 else
599 {
600 nyx_memory_free(dst_buff);
601 }
602
603 /*--------------------------------------------------------*/
604 }
605
606 break;
607
608 /*----------------------------------------------------------------*/
609
610 default:
611 NYX_LOG_ERROR("Invalid INDI / Nyx object");
612 continue;
613
614 /*----------------------------------------------------------------*/
615 }
616
617 /*--------------------------------------------------------------------*/
618
619 if(success)
620 {
621 str_t str = nyx_object_to_string(object2);
622 NYX_LOG_DEBUG("Updating (modified: %s) `%s::%s` with %s", modified ? "true" : "false", device1, name1, str);
623 nyx_memory_free(str);
624 }
625
626 /*--------------------------------------------------------------------*/
627
628 vector_modified = vector_modified || modified;
629
630 /*--------------------------------------------------------------------*/
631 }
632
633 /*------------------------------------------------------------------------*/
634 }
635 }
636 }
637
638 /*------------------------------------------------------------------------------------*/
639 }
640 }
641 }
642
643 /*------------------------------------------------------------------------------------------------*/
644
645 if(vector->base.callback._vector != NULL) vector->base.callback._vector(vector, vector_modified);
646
647 nyx_object_notify(&vector->base);
648
649 break; /* property found */
650
651 /*------------------------------------------------------------------------------------------------*/
652 }
653
654 /*----------------------------------------------------------------------------------------------------*/
655 }
656
657 /*--------------------------------------------------------------------------------------------------------*/
658 }
659
660 /*------------------------------------------------------------------------------------------------------------*/
661 }
662
663 /*----------------------------------------------------------------------------------------------------------------*/
664}
665
666/*--------------------------------------------------------------------------------------------------------------------*/
667
668static void _process_message(nyx_node_t *node, nyx_object_t *object)
669{
670 if(object->type == NYX_TYPE_DICT)
671 {
672 STR_t tag = nyx_dict_get_string((nyx_dict_t *) object, "<>");
673
674 if(tag != NULL)
675 {
676 /**/ if(strcmp(tag, "getProperties") == 0)
677 {
678 _get_properties(node, (nyx_dict_t *) object);
679 }
680 else if(strcmp(tag, "enableBLOB") == 0)
681 {
682 _enable_blob(node, (nyx_dict_t *) object);
683 }
684 else if(strcmp(tag, "enableStream") == 0)
685 {
686 _enable_stream(node, (nyx_dict_t *) object);
687 }
688 else if(strcmp(tag, "newNumberVector") == 0
689 ||
690 strcmp(tag, "newTextVector") == 0
691 ||
692 strcmp(tag, "newLightVector") == 0
693 ||
694 strcmp(tag, "newSwitchVector") == 0
695 ||
696 strcmp(tag, "newBLOBVector") == 0
697 ) {
698 _set_properties(node, (nyx_dict_t *) object);
699 }
700 }
701 }
702}
703
704/*--------------------------------------------------------------------------------------------------------------------*/
705#if !defined(ARDUINO)
706/*--------------------------------------------------------------------------------------------------------------------*/
707
708static size_t _tcp_handler(nyx_node_t *node, nyx_event_type_t event_type, const nyx_str_t payload)
709{
710 /*----------------------------------------------------------------------------------------------------------------*/
711 /* NYX_NODE_EVENT_MSG */
712 /*----------------------------------------------------------------------------------------------------------------*/
713
714 if(event_type == NYX_NODE_EVENT_MSG)
715 {
716 nyx_xml_stream_t xml_stream = NYX_XML_STREAM();
717
718 if(nyx_xml_stream_detect_opening_tag(&xml_stream, payload.len, payload.buf))
719 {
720 if(nyx_xml_stream_detect_closing_tag(&xml_stream, payload.len, payload.buf))
721 {
722 /*----------------------------------------------------------------------------------------------------*/
723
724 nyx_xmldoc_t *xmldoc = nyx_xmldoc_parse_buff(xml_stream.len, xml_stream.s_ptr);
725
726 if(xmldoc != NULL)
727 {
728 nyx_object_t *object = nyx_xmldoc_to_object(xmldoc);
729
730 if(object != NULL)
731 {
732 _process_message(node, object);
733
734 nyx_object_unref(object);
735 }
736
737 nyx_xmldoc_free(xmldoc);
738 }
739
740 /*----------------------------------------------------------------------------------------------------*/
741
742 return xml_stream.pos + xml_stream.len;
743
744 /*----------------------------------------------------------------------------------------------------*/
745 }
746 }
747 }
748
749 /*----------------------------------------------------------------------------------------------------------------*/
750
751 return 0;
752}
753
754/*--------------------------------------------------------------------------------------------------------------------*/
755#endif
756/*--------------------------------------------------------------------------------------------------------------------*/
757
758static void _mqtt_handler(nyx_node_t *node, nyx_event_type_t event_type, const nyx_str_t event_topic, const nyx_str_t event_payload)
759{
760 /*----------------------------------------------------------------------------------------------------------------*/
761 /* NYX_EVENT_OPEN */
762 /*----------------------------------------------------------------------------------------------------------------*/
763
764 if(event_type == NYX_NODE_EVENT_OPEN)
765 {
766 for(size_t i = 0; i < sizeof(SPECIAL_TOPICS) / sizeof(nyx_str_t); i++)
767 {
768 str_t topic = nyx_memory_alloc(SPECIAL_TOPICS[i].len + node->node_id.len + 2);
769
770 if(sprintf(topic, "%s/%s", SPECIAL_TOPICS[i].buf, node->node_id.buf) > 0)
771 {
772 NYX_LOG_INFO("Subscribing to `%s` and `%s` topics",
773 SPECIAL_TOPICS[i].buf,
774 /*---*/ topic /*---*/
775 );
776
777 internal_mqtt_sub(node, SPECIAL_TOPICS[i], 2);
778
779 internal_mqtt_sub(node, nyx_str_s(topic), 2);
780 }
781
782 nyx_memory_free(topic);
783 }
784
785 /*------------------------------------------------------------------------------------------------------------*/
786
787 if(node->user_mqtt_handler != NULL)
788 {
789 node->user_mqtt_handler(
790 node,
791 NYX_NODE_EVENT_OPEN,
792 0x00, NULL,
793 0x00, NULL
794 );
795 }
796
797 /*------------------------------------------------------------------------------------------------------------*/
798
799 _get_properties(node, NULL);
800
801 /*------------------------------------------------------------------------------------------------------------*/
802 }
803
804 /*----------------------------------------------------------------------------------------------------------------*/
805 /* NYX_NODE_EVENT_MSG */
806 /*----------------------------------------------------------------------------------------------------------------*/
807
808 else if(event_type == NYX_NODE_EVENT_MSG)
809 {
810 if(event_topic.len > 0 && event_topic.buf != NULL)
811 {
812 /*--------------------------------------------------------------------------------------------------------*/
813 /* SPECIAL MESSAGES */
814 /*--------------------------------------------------------------------------------------------------------*/
815
816 if(_starts_with(event_topic, SPECIAL_TOPICS[0]))
817 {
818 /*----------------------------------------------------------------------------------------------------*/
819 /* TRIGGER PING */
820 /*----------------------------------------------------------------------------------------------------*/
821
822 nyx_node_ping(node);
823
824 /*----------------------------------------------------------------------------------------------------*/
825 }
826 else
827 {
828 if(event_payload.len > 0 && event_payload.buf != NULL)
829 {
830 /**/ if(_starts_with(event_topic, SPECIAL_TOPICS[1]))
831 {
832 /*--------------------------------------------------------------------------------------------*/
833 /* SET_MASTER_CLIENT */
834 /*--------------------------------------------------------------------------------------------*/
835
836 nyx_memory_free(node->master_client_message.buf);
837
838 /*--------------------------------------------------------------------------------------------*/
839
840 node->master_client_message.buf = nyx_string_ndup(event_payload.buf, node->master_client_message.len = event_payload.len);
841
842 /*--------------------------------------------------------------------------------------------*/
843 }
844 else if(_starts_with(event_topic, SPECIAL_TOPICS[2]))
845 {
846 /*--------------------------------------------------------------------------------------------*/
847 /* JSON NEW XXX VECTOR */
848 /*--------------------------------------------------------------------------------------------*/
849
850 nyx_object_t *object = nyx_object_parse_buff(event_payload.len, event_payload.buf);
851
852 if(object != NULL)
853 {
854 _process_message(node, object);
855
856 nyx_object_unref(object);
857 }
858
859 /*--------------------------------------------------------------------------------------------*/
860 }
861 else if(_starts_with(event_topic, SPECIAL_TOPICS[3]))
862 {
863 /*--------------------------------------------------------------------------------------------*/
864 /* XML NEW XXX VECTOR */
865 /*--------------------------------------------------------------------------------------------*/
866 #if !defined(ARDUINO)
867 /*--------------------------------------------------------------------------------------------*/
868
869 nyx_xmldoc_t *xmldoc = nyx_xmldoc_parse_buff(event_payload.len, event_payload.buf);
870
871 if(xmldoc != NULL)
872 {
873 nyx_object_t *object = nyx_xmldoc_to_object(xmldoc);
874
875 if(object != NULL)
876 {
877 _process_message(node, object);
878
879 nyx_object_unref(object);
880 }
881
882 nyx_xmldoc_free(xmldoc);
883 }
884
885 /*--------------------------------------------------------------------------------------------*/
886 #endif
887 /*--------------------------------------------------------------------------------------------*/
888 }
889 }
890 }
891
892 /*--------------------------------------------------------------------------------------------------------*/
893 /* USER MESSAGE */
894 /*--------------------------------------------------------------------------------------------------------*/
895
896 if(node->user_mqtt_handler != NULL)
897 {
898 node->user_mqtt_handler(
899 node,
900 NYX_NODE_EVENT_MSG,
901 event_topic.len,
902 event_topic.buf,
903 event_payload.len,
904 event_payload.buf
905 );
906 }
907
908 /*--------------------------------------------------------------------------------------------------------*/
909 }
910 }
911
912 /*----------------------------------------------------------------------------------------------------------------*/
913}
914
915/*--------------------------------------------------------------------------------------------------------------------*/
916
917nyx_node_t *nyx_node_initialize(
918 STR_t node_id,
919 nyx_dict_t *vectors[],
920 /**/
921 STR_t indi_url,
922 STR_t mqtt_url,
923 STR_t nss_url,
924 /**/
925 STR_t mqtt_username,
926 STR_t mqtt_password,
927 /**/
928 nyx_mqtt_handler_t mqtt_handler,
929 /**/
930 uint32_t retry_ms,
931 bool enable_xml
932) {
933 /*----------------------------------------------------------------------------------------------------------------*/
934 /* ALLOCATE NODE */
935 /*----------------------------------------------------------------------------------------------------------------*/
936
937 nyx_node_t *node = nyx_memory_alloc(sizeof(nyx_node_t));
938
939 memset(node, 0x00, sizeof(nyx_node_t));
940
941 /*----------------------------------------------------------------------------------------------------------------*/
942 /* PATCH VECTORS */
943 /*----------------------------------------------------------------------------------------------------------------*/
944
945 for(nyx_dict_t **vector_ptr = vectors; *vector_ptr != NULL; vector_ptr++)
946 {
947 nyx_dict_t *vector = *vector_ptr;
948
949 /*------------------------------------------------------------------------------------------------------------*/
950
951 nyx_dict_set_string(vector, "@client", nyx_string_dup(node_id), true);
952
953 /*------------------------------------------------------------------------------------------------------------*/
954
955 nyx_object_t *children = nyx_dict_get(vector, "children");
956
957 if(children != NULL && children->type == NYX_TYPE_LIST)
958 {
959 size_t idx;
960
961 nyx_object_t *vector_def;
962
963 for(nyx_list_iter_t iter = NYX_LIST_ITER((nyx_list_t *) children); nyx_list_iterate(&iter, &idx, &vector_def);)
964 {
965 vector_def->node = node;
966 }
967 }
968
969 vector->base.node = node;
970
971 /*------------------------------------------------------------------------------------------------------------*/
972 }
973
974 /*----------------------------------------------------------------------------------------------------------------*/
975 /* SET NODE OPTIONS */
976 /*----------------------------------------------------------------------------------------------------------------*/
977
978 node->node_id = nyx_str_s(nyx_string_dup(node_id));
979
980 /*----------------------------------------------------------------------------------------------------------------*/
981
982 str_t master_client_topic = nyx_memory_alloc(sizeof("nyx/master_client/") + node->node_id.len + 1);
983
984 if(sprintf(master_client_topic, "nyx/master_client/%s", node->node_id.buf) > 0)
985 {
986 node->master_client_message = nyx_str_s(nyx_string_dup(NYX_ALL));
987
988 node->master_client_topic = nyx_str_s(master_client_topic);
989 }
990
991 /*----------------------------------------------------------------------------------------------------------------*/
992
993 node->indi_url = _safe_dup(indi_url);
994 node->mqtt_url = _safe_dup(mqtt_url);
995 node->nss_url = _safe_dup(nss_url);
996
997 node->mqtt_username = _safe_dup(mqtt_username);
998 node->mqtt_password = _safe_dup(mqtt_password);
999
1000 /*----------------------------------------------------------------------------------------------------------------*/
1001
1002 node->enable_xml = enable_xml;
1003
1004 /*----------------------------------------------------------------------------------------------------------------*/
1005
1006 node->vectors = vectors;
1007
1008 /*----------------------------------------------------------------------------------------------------------------*/
1009
1010 #if !defined(ARDUINO)
1011 node->tcp_handler = _tcp_handler;
1012 #endif
1013 node->mqtt_handler = _mqtt_handler;
1014
1015 node->user_mqtt_handler = mqtt_handler;
1016
1017 /*----------------------------------------------------------------------------------------------------------------*/
1018 /* INITIALIZE UNDERLYING STACK */
1019 /*----------------------------------------------------------------------------------------------------------------*/
1020
1021 internal_stack_initialize(node, retry_ms);
1022
1023 /*----------------------------------------------------------------------------------------------------------------*/
1024
1025 return node;
1026}
1027
1028/*--------------------------------------------------------------------------------------------------------------------*/
1029
1030void nyx_node_finalize(nyx_node_t *node, bool free_vectors)
1031{
1032 if(node != NULL)
1033 {
1034 /*------------------------------------------------------------------------------------------------------------*/
1035 /* FINALIZE UNDERLYING STACK */
1036 /*------------------------------------------------------------------------------------------------------------*/
1037
1038 internal_stack_finalize(node);
1039
1040 /*------------------------------------------------------------------------------------------------------------*/
1041 /* FREE DEF VECTORS */
1042 /*------------------------------------------------------------------------------------------------------------*/
1043
1044 if(free_vectors)
1045 {
1046 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
1047 {
1048 nyx_object_unref(*vector_ptr);
1049 }
1050 }
1051
1052 /*------------------------------------------------------------------------------------------------------------*/
1053 /* FREE NODE */
1054 /*------------------------------------------------------------------------------------------------------------*/
1055
1056 nyx_memory_free(node->node_id.buf);
1057
1058 nyx_memory_free(node->master_client_topic.buf);
1059
1060 nyx_memory_free(node->master_client_message.buf);
1061
1062 /*------------------------------------------------------------------------------------------------------------*/
1063
1064 nyx_memory_free(node->indi_url);
1065 nyx_memory_free(node->mqtt_url);
1066 nyx_memory_free(node->nss_url);
1067
1068 nyx_memory_free(node->mqtt_username);
1069 nyx_memory_free(node->mqtt_password);
1070
1071 /*------------------------------------------------------------------------------------------------------------*/
1072
1073 nyx_memory_free(node);
1074
1075 /*------------------------------------------------------------------------------------------------------------*/
1076 }
1077}
1078
1079/*--------------------------------------------------------------------------------------------------------------------*/
1080
1081void nyx_node_ping(const nyx_node_t *node)
1082{
1083 internal_mqtt_pub(node, nyx_str_s("nyx/ping/node"), node->node_id, 0);
1084
1085 internal_mqtt_pub(node, node->master_client_topic, node->master_client_message, 0);
1086}
1087
1088/*--------------------------------------------------------------------------------------------------------------------*/
1089
1090bool internal_notify(const nyx_object_t *object)
1091{
1092 if(object->node != NULL && object->type == NYX_TYPE_DICT && (object->flags & NYX_FLAGS_DISABLED) == 0)
1093 {
1094 const nyx_dict_t *vector = (nyx_dict_t *) object;
1095
1096 STR_t tag = nyx_dict_get_string(vector, "<>");
1097
1098 if(tag != NULL)
1099 {
1100 /*--------------------------------------------------------------------------------------------------------*/
1101
1102 nyx_dict_t *set_vector;
1103
1104 /**/ if(strcmp("defNumberVector", tag) == 0) {
1105 set_vector = nyx_number_set_vector_new(vector);
1106 }
1107 else if(strcmp("defTextVector", tag) == 0) {
1108 set_vector = nyx_text_set_vector_new(vector);
1109 }
1110 else if(strcmp("defLightVector", tag) == 0) {
1111 set_vector = nyx_light_set_vector_new(vector);
1112 }
1113 else if(strcmp("defSwitchVector", tag) == 0) {
1114 set_vector = nyx_switch_set_vector_new(vector);
1115 }
1116 else if(strcmp("defStreamVector", tag) == 0) {
1117 set_vector = nyx_stream_set_vector_new(vector);
1118 }
1119 else if(strcmp("defBLOBVector", tag) == 0) {
1120
1121 if((vector->base.flags & NYX_FLAGS_BLOB_MASK) != 0) {
1122
1123 set_vector = nyx_blob_set_vector_new(vector);
1124 }
1125 else {
1126 return false;
1127 }
1128 }
1129 else {
1130 return false;
1131 }
1132
1133 /*--------------------------------------------------------------------------------------------------------*/
1134
1135 STR_t perm = nyx_dict_get_string(vector, "@perm");
1136
1137 bool is_not_wo = perm == NULL || strcmp(perm, "wo") != 0;
1138
1139 if(is_not_wo) _sub_object(object->node, (nyx_object_t *) set_vector);
1140
1141 /*--------------------------------------------------------------------------------------------------------*/
1142
1143 nyx_object_unref(&set_vector->base);
1144
1145 /*--------------------------------------------------------------------------------------------------------*/
1146
1147 return true;
1148 }
1149 }
1150
1151 return false;
1152}
1153
1154/*--------------------------------------------------------------------------------------------------------------------*/
1155
1156static void _device_onoff(const nyx_node_t *node, STR_t device, STR_t name, STR_t message, nyx_onoff_t onoff)
1157{
1158 /*----------------------------------------------------------------------------------------------------------------*/
1159
1160 if(node != NULL && device != NULL)
1161 {
1162 /*------------------------------------------------------------------------------------------------------------*/
1163
1164 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
1165 {
1166 nyx_dict_t *vector = *vector_ptr;
1167
1168 /*--------------------------------------------------------------------------------------------------------*/
1169
1170 STR_t device2 = nyx_dict_get_string(vector, "@device");
1171 STR_t name2 = nyx_dict_get_string(vector, "@name");
1172
1173 /*--------------------------------------------------------------------------------------------------------*/
1174
1175 if(device2 == NULL || strcmp(device, device2) != 0)
1176 {
1177 continue;
1178 }
1179
1180 if(name != NULL)
1181 {
1182 if(name2 == NULL || strcmp(name, name2) != 0)
1183 {
1184 continue;
1185 }
1186 }
1187
1188 /*--------------------------------------------------------------------------------------------------------*/
1189
1190 switch(onoff)
1191 {
1192 case NYX_ONOFF_OFF:
1193 vector->base.flags |= NYX_FLAGS_DISABLED;
1194 break;
1195
1196 case NYX_ONOFF_ON:
1197 vector->base.flags &= ~NYX_FLAGS_DISABLED;
1198
1199 _sub_object(node, (nyx_object_t *) vector);
1200 break;
1201 }
1202
1203 /*--------------------------------------------------------------------------------------------------------*/
1204 }
1205
1206 /*------------------------------------------------------------------------------------------------------------*/
1207
1208 if(onoff == NYX_ONOFF_OFF)
1209 {
1210 nyx_dict_t *del_property_new = nyx_del_property_new(device, name, message);
1211
1212 _sub_object(node, (nyx_object_t *) del_property_new);
1213
1214 nyx_dict_free(del_property_new);
1215 }
1216
1217 /*------------------------------------------------------------------------------------------------------------*/
1218 }
1219}
1220
1221/*--------------------------------------------------------------------------------------------------------------------*/
1222
1223void nyx_node_enable(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1224{
1225 _device_onoff(node, device, name, message, NYX_ONOFF_ON);
1226}
1227
1228/*--------------------------------------------------------------------------------------------------------------------*/
1229
1230void nyx_node_disable(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1231{
1232 _device_onoff(node, device, name, message, NYX_ONOFF_OFF);
1233}
1234
1235/*--------------------------------------------------------------------------------------------------------------------*/
1236
1237void nyx_node_send_message(const nyx_node_t *node, STR_t device, STR_t message)
1238{
1239 nyx_dict_t *dict = nyx_message_new(device, message);
1240
1241 _sub_object(node, (nyx_object_t *) dict);
1242
1243 nyx_dict_free(dict);
1244}
1245
1246/*--------------------------------------------------------------------------------------------------------------------*/
1247
1248void nyx_node_send_del_property(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1249{
1250 nyx_dict_t *dict = nyx_del_property_new(device, name, message);
1251
1252 _sub_object(node, (nyx_object_t *) dict);
1253
1254 nyx_dict_free(dict);
1255}
1256
1257/*--------------------------------------------------------------------------------------------------------------------*/
Struct describing a JSON dict object.
#define NYX_LIST_ITER(list)
Initializes a JSON list iterator.
Definition nyx_node.h:1457
Struct describing a JSON list iterator.
Definition nyx_node.h:1443
Struct describing a JSON list object.
#define NYX_LOG_FATAL(fmt,...)
Logs a fatal message.
Definition nyx_node.h:208
#define NYX_LOG_DEBUG(fmt,...)
Logs a debug message.
Definition nyx_node.h:252
#define NYX_LOG_INFO(fmt,...)
Logs an info message.
Definition nyx_node.h:241
#define NYX_LOG_ERROR(fmt,...)
Logs an error message.
Definition nyx_node.h:219
#define STR_t
Alias for const char *.
Definition nyx_node.h:71
__NYX_NULLABLE__ buff_t nyx_memory_alloc(__NYX_ZEROABLE__ size_t size)
Similar to libc malloc except that a memory overflow causes the node to stop.
__NYX_ZEROABLE__ size_t nyx_memory_free(__NYX_NULLABLE__ buff_t buff)
Similar to libc free except that it returns the amount of memory freed.
#define buff_t
Alias for void *.
Definition nyx_node.h:67
__NYX_NULLABLE__ str_t nyx_string_dup(__NYX_NULLABLE__ STR_t s)
Similar to libc strdup.
__NYX_NULLABLE__ str_t nyx_string_ndup(__NYX_NULLABLE__ STR_t s, __NYX_ZEROABLE__ size_t n)
Similar to libc strndup.
#define str_t
Alias for char *.
Definition nyx_node.h:70
void nyx_node_finalize(nyx_node_t *node, bool free_vectors)
Finalizes a Nyx node.
Definition node.c:1030
Opaque struct describing a Nyx node.
nyx_state_t
Vector state hint.
Definition nyx_node.h:1913
nyx_onoff_t
Switch state.
Definition nyx_node.h:2016
@ NYX_ONOFF_ON
Switch is ON.
Definition nyx_node.h:2017
@ NYX_ONOFF_OFF
Switch is OFF.
Definition nyx_node.h:2018
#define NYX_OBJECT_MAGIC
Magic number for identifying JSON objects.
Definition nyx_node.h:413
@ NYX_TYPE_DICT
Dict object.
Definition nyx_node.h:438
@ NYX_TYPE_LIST
List object.
Definition nyx_node.h:439
@ NYX_TYPE_STRING
String object.
Definition nyx_node.h:437
Struct describing a JSON object.
Struct describing a JSON string object.
Definition nyx_node.h:914
__NYX_NULLABLE__ nyx_object_t * nyx_xmldoc_to_object(__NYX_NULLABLE__ const nyx_xmldoc_t *xmldoc)
Converts an XML Nyx / INDI command to the JSON one.
__NYX_NULLABLE__ nyx_xmldoc_t * nyx_object_to_xmldoc(__NYX_NULLABLE__ const nyx_object_t *object)
Converts a JSON Nyx / INDI command to the XML one.
uint32_t nyx_hash(__NYX_ZEROABLE__ size_t size, __NYX_NULLABLE__ BUFF_t buff, uint32_t seed)
Hashes a buffer using the MurmurHash2 algorithm.
__NYX_NULLABLE__ buff_t nyx_zlib_base64_inflate(__NYX_NOTNULL__ size_t *result_size, __NYX_ZEROABLE__ size_t len, __NYX_NULLABLE__ STR_t str)
Decompresses a string using the ZLib+Base64 algorithm.
__NYX_NULLABLE__ buff_t nyx_base64_decode(__NYX_NULLABLE__ size_t *result_size, __NYX_ZEROABLE__ size_t len, __NYX_NULLABLE__ STR_t str)
Decodes a string using the Base64 algorithm.
Struct describing an XML document.