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);
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_t *) old_value);
490 nyx_variant_t new_val = internal_string_to_variant(format, (nyx_string_t *) new_value);
491
492 switch(new_val.type)
493 {
494 case NYX_VARIANT_TYPE_INT:
495 if((success = object2->in_callback._int == NULL || object2->in_callback._int(vector, (nyx_dict_t *) object2, new_val.value._int, old_val.value._int))) {
496 modified = nyx_dict_set((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val));
497 }
498 break;
499 case NYX_VARIANT_TYPE_UINT:
500 if((success = object2->in_callback._uint == NULL || object2->in_callback._uint(vector, (nyx_dict_t *) object2, new_val.value._uint, old_val.value._uint))) {
501 modified = nyx_dict_set((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val));
502 }
503 break;
504 case NYX_VARIANT_TYPE_LONG:
505 if((success = object2->in_callback._long == NULL || object2->in_callback._long(vector, (nyx_dict_t *) object2, new_val.value._long, old_val.value._long))) {
506 modified = nyx_dict_set((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val));
507 }
508 break;
509 case NYX_VARIANT_TYPE_ULONG:
510 if((success = object2->in_callback._ulong == NULL || object2->in_callback._ulong(vector, (nyx_dict_t *) object2, new_val.value._ulong, old_val.value._ulong))) {
511 modified = nyx_dict_set((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val));
512 }
513 break;
514 case NYX_VARIANT_TYPE_DOUBLE:
515 if((success = object2->in_callback._double == NULL || object2->in_callback._double(vector, (nyx_dict_t *) object2, new_val.value._double, old_val.value._double))) {
516 modified = nyx_dict_set((nyx_dict_t *) object2, "$", internal_variant_to_string(format, new_val));
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->in_callback._str == NULL || object2->in_callback._str(vector, (nyx_dict_t *) object2, new_val, old_val)))
533 {
534 modified = nyx_dict_set((nyx_dict_t *) object2, "$", nyx_string_from_dup(new_val));
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->in_callback._int == NULL || object2->in_callback._int(vector, (nyx_dict_t *) object2, (int) new_val, (int) old_val)))
548 {
549 modified = nyx_dict_set((nyx_dict_t *) object2, "$", nyx_string_from_unmanaged(nyx_state_to_str(new_val)));
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->in_callback._int == NULL || object2->in_callback._int(vector, (nyx_dict_t *) object2, (int) new_val, (int) old_val)))
563 {
564 modified = nyx_dict_set((nyx_dict_t *) object2, "$", nyx_string_from_unmanaged(nyx_onoff_to_str(new_val)));
565 }
566 }
567
568 break;
569
570 /*----------------------------------------------------------------*/
571
572 case 0x29BFE4D7: // defBLOBVector
573 {
574 /*--------------------------------------------------------*/
575
576 size_t src_size;
577 buff_t src_buff;
578
579 nyx_string_get_buff((nyx_string_t *) new_value, &src_size, &src_buff);
580
581 /*--------------------------------------------------------*/
582
583 size_t dst_size;
584 buff_t dst_buff;
585
586 if(internal_blob_is_compressed((nyx_dict_t *) object2)) {
587 dst_buff = nyx_zlib_base64_inflate(&dst_size, src_size, src_buff);
588 }
589 else {
590 dst_buff = nyx_base64_decode(&dst_size, src_size, src_buff);
591 }
592
593 /*--------------------------------------------------------*/
594
595 if((success = object2->in_callback._buffer == NULL || object2->in_callback._buffer(vector, (nyx_dict_t *) object2, dst_size, dst_buff)))
596 {
597 modified = nyx_dict_set((nyx_dict_t *) object2, "$", nyx_string_from_buff_managed(dst_size, dst_buff));
598 }
599 else
600 {
601 nyx_memory_free(dst_buff);
602 }
603
604 /*--------------------------------------------------------*/
605 }
606
607 break;
608
609 /*----------------------------------------------------------------*/
610
611 default:
612 NYX_LOG_ERROR("Invalid INDI / Nyx object");
613 continue;
614
615 /*----------------------------------------------------------------*/
616 }
617
618 /*--------------------------------------------------------------------*/
619
620 if(success)
621 {
622 str_t str = nyx_object_to_string(object2);
623 NYX_LOG_DEBUG("Updating (modified: %s) `%s::%s` with %s", modified ? "true" : "false", device1, name1, str);
624 nyx_memory_free(str);
625 }
626
627 /*--------------------------------------------------------------------*/
628
629 vector_modified = vector_modified || modified;
630
631 /*--------------------------------------------------------------------*/
632 }
633
634 /*------------------------------------------------------------------------*/
635 }
636 }
637 }
638
639 /*------------------------------------------------------------------------------------*/
640 }
641 }
642 }
643
644 /*------------------------------------------------------------------------------------------------*/
645
646 if(vector->base.in_callback._vector != NULL) vector->base.in_callback._vector(vector, vector_modified);
647
648 nyx_node_notify(&vector->base);
649
650 break; /* property found */
651
652 /*------------------------------------------------------------------------------------------------*/
653 }
654
655 /*----------------------------------------------------------------------------------------------------*/
656 }
657
658 /*--------------------------------------------------------------------------------------------------------*/
659 }
660
661 /*------------------------------------------------------------------------------------------------------------*/
662 }
663
664 /*----------------------------------------------------------------------------------------------------------------*/
665}
666
667/*--------------------------------------------------------------------------------------------------------------------*/
668
669static void _process_message(nyx_node_t *node, nyx_object_t *object)
670{
671 if(object->type == NYX_TYPE_DICT)
672 {
673 STR_t tag = nyx_dict_get_string((nyx_dict_t *) object, "<>");
674
675 if(tag != NULL)
676 {
677 if(strcmp(tag, "getProperties") == 0)
678 {
679 _get_properties(node, (nyx_dict_t *) object);
680 }
681 else if(strcmp(tag, "enableBLOB") == 0)
682 {
683 _enable_blob(node, (nyx_dict_t *) object);
684 }
685 else if(strcmp(tag, "enableStream") == 0)
686 {
687 _enable_stream(node, (nyx_dict_t *) object);
688 }
689 else if(strcmp(tag, "newNumberVector") == 0
690 ||
691 strcmp(tag, "newTextVector") == 0
692 ||
693 strcmp(tag, "newLightVector") == 0
694 ||
695 strcmp(tag, "newSwitchVector") == 0
696 ||
697 strcmp(tag, "newBLOBVector") == 0
698 ) {
699 _set_properties(node, (nyx_dict_t *) object);
700 }
701 }
702 }
703}
704
705/*--------------------------------------------------------------------------------------------------------------------*/
706#if !defined(ARDUINO)
707/*--------------------------------------------------------------------------------------------------------------------*/
708
709static size_t _tcp_handler(nyx_node_t *node, nyx_event_type_t event_type, const nyx_str_t payload)
710{
711 /*----------------------------------------------------------------------------------------------------------------*/
712 /* NYX_NODE_EVENT_MSG */
713 /*----------------------------------------------------------------------------------------------------------------*/
714
715 if(event_type == NYX_NODE_EVENT_MSG)
716 {
717 nyx_xml_stream_t xml_stream = NYX_XML_STREAM();
718
719 if(nyx_xml_stream_detect_opening_tag(&xml_stream, payload.len, payload.buf))
720 {
721 if(nyx_xml_stream_detect_closing_tag(&xml_stream, payload.len, payload.buf))
722 {
723 /*----------------------------------------------------------------------------------------------------*/
724
725 nyx_xmldoc_t *xmldoc = nyx_xmldoc_parse_buff(xml_stream.len, xml_stream.s_ptr);
726
727 if(xmldoc != NULL)
728 {
729 nyx_object_t *object = nyx_xmldoc_to_object(xmldoc);
730
731 if(object != NULL)
732 {
733 _process_message(node, object);
734
735 nyx_object_free(object);
736 }
737
738 nyx_xmldoc_free(xmldoc);
739 }
740
741 /*----------------------------------------------------------------------------------------------------*/
742
743 return xml_stream.pos + xml_stream.len;
744
745 /*----------------------------------------------------------------------------------------------------*/
746 }
747 }
748 }
749
750 /*----------------------------------------------------------------------------------------------------------------*/
751
752 return 0;
753}
754
755/*--------------------------------------------------------------------------------------------------------------------*/
756#endif
757/*--------------------------------------------------------------------------------------------------------------------*/
758
759static 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)
760{
761 /*----------------------------------------------------------------------------------------------------------------*/
762 /* NYX_EVENT_OPEN */
763 /*----------------------------------------------------------------------------------------------------------------*/
764
765 if(event_type == NYX_NODE_EVENT_OPEN)
766 {
767 for(size_t i = 0; i < sizeof(SPECIAL_TOPICS) / sizeof(nyx_str_t); i++)
768 {
769 str_t topic = nyx_memory_alloc(SPECIAL_TOPICS[i].len + node->node_id.len + 2);
770
771 if(sprintf(topic, "%s/%s", SPECIAL_TOPICS[i].buf, node->node_id.buf) > 0)
772 {
773 NYX_LOG_INFO("Subscribing to `%s` and `%s` topics",
774 SPECIAL_TOPICS[i].buf,
775 /*---*/ topic /*---*/
776 );
777
778 internal_mqtt_sub(node, SPECIAL_TOPICS[i], 2);
779
780 internal_mqtt_sub(node, nyx_str_s(topic), 2);
781 }
782
783 nyx_memory_free(topic);
784 }
785
786 /*------------------------------------------------------------------------------------------------------------*/
787
788 if(node->user_mqtt_handler != NULL)
789 {
790 node->user_mqtt_handler(
791 node,
792 NYX_NODE_EVENT_OPEN,
793 0x00, NULL,
794 0x00, NULL
795 );
796 }
797
798 /*------------------------------------------------------------------------------------------------------------*/
799
800 _get_properties(node, NULL);
801
802 /*------------------------------------------------------------------------------------------------------------*/
803 }
804
805 /*----------------------------------------------------------------------------------------------------------------*/
806 /* NYX_NODE_EVENT_MSG */
807 /*----------------------------------------------------------------------------------------------------------------*/
808
809 else if(event_type == NYX_NODE_EVENT_MSG)
810 {
811 if(event_topic.len > 0 && event_topic.buf != NULL)
812 {
813 /*--------------------------------------------------------------------------------------------------------*/
814 /* SPECIAL MESSAGES */
815 /*--------------------------------------------------------------------------------------------------------*/
816
817 if(_starts_with(event_topic, SPECIAL_TOPICS[0]))
818 {
819 /*----------------------------------------------------------------------------------------------------*/
820 /* TRIGGER PING */
821 /*----------------------------------------------------------------------------------------------------*/
822
823 nyx_node_ping(node);
824
825 /*----------------------------------------------------------------------------------------------------*/
826 }
827 else
828 {
829 if(event_payload.len > 0 && event_payload.buf != NULL)
830 {
831 if(_starts_with(event_topic, SPECIAL_TOPICS[1]))
832 {
833 /*--------------------------------------------------------------------------------------------*/
834 /* SET_MASTER_CLIENT */
835 /*--------------------------------------------------------------------------------------------*/
836
837 nyx_memory_free(node->master_client_message.buf);
838
839 /*--------------------------------------------------------------------------------------------*/
840
841 node->master_client_message.buf = nyx_string_ndup(event_payload.buf, node->master_client_message.len = event_payload.len);
842
843 /*--------------------------------------------------------------------------------------------*/
844 }
845 else if(_starts_with(event_topic, SPECIAL_TOPICS[2]))
846 {
847 /*--------------------------------------------------------------------------------------------*/
848 /* JSON NEW XXX VECTOR */
849 /*--------------------------------------------------------------------------------------------*/
850
851 nyx_object_t *object = nyx_object_parse_buff(event_payload.len, event_payload.buf);
852
853 if(object != NULL)
854 {
855 _process_message(node, object);
856
857 nyx_object_free(object);
858 }
859
860 /*--------------------------------------------------------------------------------------------*/
861 }
862 else if(_starts_with(event_topic, SPECIAL_TOPICS[3]))
863 {
864 /*--------------------------------------------------------------------------------------------*/
865 /* XML NEW XXX VECTOR */
866 /*--------------------------------------------------------------------------------------------*/
867 #if !defined(ARDUINO)
868 /*--------------------------------------------------------------------------------------------*/
869
870 nyx_xmldoc_t *xmldoc = nyx_xmldoc_parse_buff(event_payload.len, event_payload.buf);
871
872 if(xmldoc != NULL)
873 {
874 nyx_object_t *object = nyx_xmldoc_to_object(xmldoc);
875
876 if(object != NULL)
877 {
878 _process_message(node, object);
879
880 nyx_object_free(object);
881 }
882
883 nyx_xmldoc_free(xmldoc);
884 }
885
886 /*--------------------------------------------------------------------------------------------*/
887 #endif
888 /*--------------------------------------------------------------------------------------------*/
889 }
890 }
891 }
892
893 /*--------------------------------------------------------------------------------------------------------*/
894 /* USER MESSAGE */
895 /*--------------------------------------------------------------------------------------------------------*/
896
897 if(node->user_mqtt_handler != NULL)
898 {
899 node->user_mqtt_handler(
900 node,
901 NYX_NODE_EVENT_MSG,
902 event_topic.len,
903 event_topic.buf,
904 event_payload.len,
905 event_payload.buf
906 );
907 }
908
909 /*--------------------------------------------------------------------------------------------------------*/
910 }
911 }
912
913 /*----------------------------------------------------------------------------------------------------------------*/
914}
915
916/*--------------------------------------------------------------------------------------------------------------------*/
917
918nyx_node_t *nyx_node_initialize(
919 STR_t node_id,
920 nyx_dict_t *vectors[],
921
922 STR_t indi_url,
923 STR_t mqtt_url,
924 STR_t nss_url,
925
926 STR_t mqtt_username,
927 STR_t mqtt_password,
928
929 nyx_mqtt_handler_t mqtt_handler,
930
931 uint32_t retry_ms,
932 bool enable_xml
933) {
934 /*----------------------------------------------------------------------------------------------------------------*/
935 /* ALLOCATE NODE */
936 /*----------------------------------------------------------------------------------------------------------------*/
937
938 nyx_node_t *node = nyx_memory_alloc(sizeof(nyx_node_t));
939
940 memset(node, 0x00, sizeof(nyx_node_t));
941
942 /*----------------------------------------------------------------------------------------------------------------*/
943 /* PATCH VECTORS */
944 /*----------------------------------------------------------------------------------------------------------------*/
945
946 for(nyx_dict_t **vector_ptr = vectors; *vector_ptr != NULL; vector_ptr++)
947 {
948 nyx_dict_t *vector = *vector_ptr;
949
950 /*------------------------------------------------------------------------------------------------------------*/
951
952 nyx_dict_set(vector, "@client", nyx_string_from_dup(node_id));
953
954 /*------------------------------------------------------------------------------------------------------------*/
955
956 nyx_object_t *children = nyx_dict_get(vector, "children");
957
958 if(children != NULL && children->type == NYX_TYPE_LIST)
959 {
960 size_t idx;
961
962 nyx_object_t *vector_def;
963
964 for(nyx_list_iter_t iter = NYX_LIST_ITER((nyx_list_t *) children); nyx_list_iterate(&iter, &idx, &vector_def);)
965 {
966 vector_def->node = node;
967 }
968 }
969
970 vector->base.node = node;
971
972 /*------------------------------------------------------------------------------------------------------------*/
973 }
974
975 /*----------------------------------------------------------------------------------------------------------------*/
976 /* SET NODE OPTIONS */
977 /*----------------------------------------------------------------------------------------------------------------*/
978
979 node->node_id = nyx_str_s(nyx_string_dup(node_id));
980
981 /*----------------------------------------------------------------------------------------------------------------*/
982
983 str_t master_client_topic = nyx_memory_alloc(sizeof("nyx/master_client/") + node->node_id.len + 1);
984
985 if(sprintf(master_client_topic, "nyx/master_client/%s", node->node_id.buf) > 0)
986 {
987 node->master_client_message = nyx_str_s(nyx_string_dup(NYX_ALL));
988
989 node->master_client_topic = nyx_str_s(master_client_topic);
990 }
991
992 /*----------------------------------------------------------------------------------------------------------------*/
993
994 node->indi_url = _safe_dup(indi_url);
995 node->mqtt_url = _safe_dup(mqtt_url);
996 node->nss_url = _safe_dup(nss_url);
997
998 node->mqtt_username = _safe_dup(mqtt_username);
999 node->mqtt_password = _safe_dup(mqtt_password);
1000
1001 /*----------------------------------------------------------------------------------------------------------------*/
1002
1003 node->enable_xml = enable_xml;
1004
1005 /*----------------------------------------------------------------------------------------------------------------*/
1006
1007 node->vectors = vectors;
1008
1009 /*----------------------------------------------------------------------------------------------------------------*/
1010
1011 #if !defined(ARDUINO)
1012 node->tcp_handler = _tcp_handler;
1013 #endif
1014 node->mqtt_handler = _mqtt_handler;
1015
1016 node->user_mqtt_handler = mqtt_handler;
1017
1018 /*----------------------------------------------------------------------------------------------------------------*/
1019 /* INITIALIZE UNDERLYING STACK */
1020 /*----------------------------------------------------------------------------------------------------------------*/
1021
1022 internal_stack_initialize(node, retry_ms);
1023
1024 /*----------------------------------------------------------------------------------------------------------------*/
1025
1026 return node;
1027}
1028
1029/*--------------------------------------------------------------------------------------------------------------------*/
1030
1031void nyx_node_finalize(nyx_node_t *node, bool free_vectors)
1032{
1033 if(node != NULL)
1034 {
1035 /*------------------------------------------------------------------------------------------------------------*/
1036 /* FINALIZE UNDERLYING STACK */
1037 /*------------------------------------------------------------------------------------------------------------*/
1038
1039 internal_stack_finalize(node);
1040
1041 /*------------------------------------------------------------------------------------------------------------*/
1042 /* FREE DEF VECTORS */
1043 /*------------------------------------------------------------------------------------------------------------*/
1044
1045 if(free_vectors)
1046 {
1047 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
1048 {
1049 nyx_dict_free(*vector_ptr);
1050 }
1051 }
1052
1053 /*------------------------------------------------------------------------------------------------------------*/
1054 /* FREE NODE */
1055 /*------------------------------------------------------------------------------------------------------------*/
1056
1057 nyx_memory_free(node->node_id.buf);
1058
1059 nyx_memory_free(node->master_client_topic.buf);
1060
1061 nyx_memory_free(node->master_client_message.buf);
1062
1063 /*------------------------------------------------------------------------------------------------------------*/
1064
1065 nyx_memory_free(node->indi_url);
1066 nyx_memory_free(node->mqtt_url);
1067 nyx_memory_free(node->nss_url);
1068
1069 nyx_memory_free(node->mqtt_username);
1070 nyx_memory_free(node->mqtt_password);
1071
1072 /*------------------------------------------------------------------------------------------------------------*/
1073
1074 nyx_memory_free(node);
1075
1076 /*------------------------------------------------------------------------------------------------------------*/
1077 }
1078}
1079
1080/*--------------------------------------------------------------------------------------------------------------------*/
1081
1082void nyx_node_ping(const nyx_node_t *node)
1083{
1084 internal_mqtt_pub(node, nyx_str_s("nyx/ping/node"), node->node_id, 0);
1085
1086 internal_mqtt_pub(node, node->master_client_topic, node->master_client_message, 0);
1087}
1088
1089/*--------------------------------------------------------------------------------------------------------------------*/
1090
1091static bool _notify(nyx_object_t *object)
1092{
1093 if(object->type == NYX_TYPE_DICT && (object->flags & NYX_FLAGS_DISABLED) == 0)
1094 {
1095 const nyx_dict_t *vector = (nyx_dict_t *) object;
1096
1097 STR_t tag = nyx_dict_get_string(vector, "<>");
1098
1099 if(tag != NULL)
1100 {
1101 /*--------------------------------------------------------------------------------------------------------*/
1102
1103 nyx_dict_t *set_vector;
1104
1105 if(strcmp("defNumberVector", tag) == 0) {
1106 set_vector = nyx_number_set_vector_new(vector);
1107 }
1108 else if(strcmp("defTextVector", tag) == 0) {
1109 set_vector = nyx_text_set_vector_new(vector);
1110 }
1111 else if(strcmp("defLightVector", tag) == 0) {
1112 set_vector = nyx_light_set_vector_new(vector);
1113 }
1114 else if(strcmp("defSwitchVector", tag) == 0) {
1115 set_vector = nyx_switch_set_vector_new(vector);
1116 }
1117 else if(strcmp("defStreamVector", tag) == 0) {
1118 set_vector = nyx_stream_set_vector_new(vector);
1119 }
1120 else if(strcmp("defBLOBVector", tag) == 0) {
1121
1122 if((vector->base.flags & NYX_FLAGS_BLOB_MASK) != 0) {
1123
1124 set_vector = nyx_blob_set_vector_new(vector);
1125 }
1126 else {
1127 return false;
1128 }
1129 }
1130 else {
1131 return false;
1132 }
1133
1134 /*--------------------------------------------------------------------------------------------------------*/
1135
1136 STR_t perm = nyx_dict_get_string(vector, "@perm");
1137
1138 bool is_not_wo = perm == NULL || strcmp(perm, "wo") != 0;
1139
1140 if(is_not_wo) _sub_object(object->node, (nyx_object_t *) set_vector);
1141
1142 /*--------------------------------------------------------------------------------------------------------*/
1143
1144 nyx_dict_free(set_vector);
1145
1146 /*--------------------------------------------------------------------------------------------------------*/
1147
1148 return true;
1149 }
1150 }
1151
1152 return false;
1153}
1154
1155/*--------------------------------------------------------------------------------------------------------------------*/
1156
1157bool nyx_node_notify(nyx_object_t *object)
1158{
1159 for(; object != NULL; object = object->parent)
1160 {
1161 if(_notify(object))
1162 {
1163 return true;
1164 }
1165 }
1166
1167 return false;
1168}
1169
1170/*--------------------------------------------------------------------------------------------------------------------*/
1171
1172static void _device_onoff(const nyx_node_t *node, STR_t device, STR_t name, STR_t message, nyx_onoff_t onoff)
1173{
1174 /*----------------------------------------------------------------------------------------------------------------*/
1175
1176 if(node != NULL && device != NULL)
1177 {
1178 /*------------------------------------------------------------------------------------------------------------*/
1179
1180 for(nyx_dict_t **vector_ptr = node->vectors; *vector_ptr != NULL; vector_ptr++)
1181 {
1182 nyx_dict_t *vector = *vector_ptr;
1183
1184 /*--------------------------------------------------------------------------------------------------------*/
1185
1186 STR_t device2 = nyx_dict_get_string(vector, "@device");
1187 STR_t name2 = nyx_dict_get_string(vector, "@name");
1188
1189 /*--------------------------------------------------------------------------------------------------------*/
1190
1191 if(device2 == NULL || strcmp(device, device2) != 0)
1192 {
1193 continue;
1194 }
1195
1196 if(name != NULL)
1197 {
1198 if(name2 == NULL || strcmp(name, name2) != 0)
1199 {
1200 continue;
1201 }
1202 }
1203
1204 /*--------------------------------------------------------------------------------------------------------*/
1205
1206 switch(onoff)
1207 {
1208 case NYX_ONOFF_OFF:
1209 vector->base.flags |= NYX_FLAGS_DISABLED;
1210 break;
1211
1212 case NYX_ONOFF_ON:
1213 vector->base.flags &= ~NYX_FLAGS_DISABLED;
1214
1215 _sub_object(node, (nyx_object_t *) vector);
1216 break;
1217 }
1218
1219 /*--------------------------------------------------------------------------------------------------------*/
1220 }
1221
1222 /*------------------------------------------------------------------------------------------------------------*/
1223
1224 if(onoff == NYX_ONOFF_OFF)
1225 {
1226 nyx_dict_t *del_property_new = nyx_del_property_new(device, name, message);
1227
1228 _sub_object(node, (nyx_object_t *) del_property_new);
1229
1230 nyx_dict_free(del_property_new);
1231 }
1232
1233 /*------------------------------------------------------------------------------------------------------------*/
1234 }
1235}
1236
1237/*--------------------------------------------------------------------------------------------------------------------*/
1238
1239void nyx_node_enable(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1240{
1241 _device_onoff(node, device, name, message, NYX_ONOFF_ON);
1242}
1243
1244/*--------------------------------------------------------------------------------------------------------------------*/
1245
1246void nyx_node_disable(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1247{
1248 _device_onoff(node, device, name, message, NYX_ONOFF_OFF);
1249}
1250
1251/*--------------------------------------------------------------------------------------------------------------------*/
1252
1253void nyx_node_send_message(const nyx_node_t *node, STR_t device, STR_t message)
1254{
1255 nyx_dict_t *dict = nyx_message_new(device, message);
1256
1257 _sub_object(node, (nyx_object_t *) dict);
1258
1259 nyx_dict_free(dict);
1260}
1261
1262/*--------------------------------------------------------------------------------------------------------------------*/
1263
1264void nyx_node_send_del_property(const nyx_node_t *node, STR_t device, STR_t name, STR_t message)
1265{
1266 nyx_dict_t *dict = nyx_del_property_new(device, name, message);
1267
1268 _sub_object(node, (nyx_object_t *) dict);
1269
1270 nyx_dict_free(dict);
1271}
1272
1273/*--------------------------------------------------------------------------------------------------------------------*/
#define NYX_LIST_ITER(list)
Initializes a JSON list iterator.
Definition nyx_node.h:1416
#define NYX_LOG_FATAL(fmt,...)
Logs a fatal message.
Definition nyx_node.h:207
#define NYX_LOG_DEBUG(fmt,...)
Logs a debug message.
Definition nyx_node.h:240
#define NYX_LOG_INFO(fmt,...)
Logs an info message.
Definition nyx_node.h:229
#define NYX_LOG_ERROR(fmt,...)
Logs an error message.
Definition nyx_node.h:218
#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
nyx_state_t
Vector state hint.
Definition nyx_node.h:1781
nyx_onoff_t
Switch state.
Definition nyx_node.h:1884
@ NYX_ONOFF_ON
Switch is ON.
Definition nyx_node.h:1885
@ NYX_ONOFF_OFF
Switch is OFF.
Definition nyx_node.h:1886
#define NYX_OBJECT_MAGIC
Magic number for identifying JSON objects.
Definition nyx_node.h:401
@ NYX_TYPE_DICT
Dict object.
Definition nyx_node.h:426
@ NYX_TYPE_LIST
List object.
Definition nyx_node.h:427
@ NYX_TYPE_STRING
String object.
Definition nyx_node.h:425
__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 a JSON dict object.
Struct describing a JSON list iterator.
Definition nyx_node.h:1402
Struct describing a JSON list object.
Opaque struct describing a Nyx node.
Struct describing a JSON object.
Struct describing a JSON string object.
Definition nyx_node.h:900
nyx_object_t base
Common object header for JSON objects.
Definition nyx_node.h:901
Struct describing an XML document.