Instrument Neutral Distributed Interface INDI 2.2.5
basedevice.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2011 Jasem Mutlaq. All rights reserved.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*******************************************************************************/
18
19#include "basedevice.h"
20#include "basedevice_p.h"
21
22#include "base64.h"
23#include "config.h"
24#include "indicom.h"
25#include "sharedblob.h"
27#include "locale_compat.h"
28
29#include "indipropertytext.h"
30#include "indipropertynumber.h"
31#include "indipropertyswitch.h"
32#include "indipropertylight.h"
33#include "indipropertyblob.h"
34
35#ifdef ENABLE_INDI_SHARED_MEMORY
36# include "sharedblob_parse.h"
37#endif
38
39#include <cerrno>
40#include <cassert>
41#include <cstdlib>
42#include <cstring>
43#include <zlib.h>
44#include <sys/stat.h>
45#include <thread>
46#include <chrono>
47#include <algorithm>
48
49#if defined(_MSC_VER)
50#define snprintf _snprintf
51#pragma warning(push)
53#pragma warning(disable : 4996)
54#endif
55
56namespace INDI
57{
58
60{
61 static char indidev[] = "INDIDEV=";
62
63 if (getenv("INDIDEV") != nullptr)
64 {
65 deviceName = getenv("INDIDEV");
66 putenv(indidev);
67 }
68}
69
74
76 : d_ptr(BaseDevicePrivate::invalid())
77{ }
78
81
83 : d_ptr(&dd)
84{ }
85
86BaseDevice::BaseDevice(const std::shared_ptr<BaseDevicePrivate> &dd)
87 : d_ptr(dd)
88{ }
89
91{
92 return getProperty(name, INDI_NUMBER);
93}
94
96{
97 return getProperty(name, INDI_TEXT);
98}
99
101{
102 return getProperty(name, INDI_SWITCH);
103}
104
106{
107 return getProperty(name, INDI_LIGHT);
108}
109
111{
112 return getProperty(name, INDI_BLOB);
113}
114
116{
117 for (const auto &oneProp : getProperties())
118 if (oneProp.isNameMatch(name))
119 return oneProp.getState();
120
121 return IPS_IDLE;
122}
123
125{
126 for (const auto &oneProp : getProperties())
127 if (oneProp.isNameMatch(name))
128 return oneProp.getPermission();
129
130 return IP_RO;
131}
132
134{
135 INDI::Property prop = getProperty(name, type);
136 return prop ? prop.getProperty() : nullptr;
137}
138
140{
141 D_PTR(const BaseDevice);
142 std::lock_guard<std::mutex> lock(d->m_Lock);
143
144 for (const auto &oneProp : getProperties())
145 {
146 if (type != oneProp.getType() && type != INDI_UNKNOWN)
147 continue;
148
149 if (!oneProp.getRegistered())
150 continue;
151
152 if (oneProp.isNameMatch(name))
153 return oneProp;
154 }
155
156 return INDI::Property();
157}
158
160{
161 D_PTR(BaseDevice);
162 return d->pAll;
163}
164
166{
167 D_PTR(const BaseDevice);
168 return d->pAll;
169}
170
171int BaseDevice::removeProperty(const char *name, char *errmsg)
172{
173 D_PTR(BaseDevice);
174 int result = INDI_PROPERTY_INVALID;
175
176 std::lock_guard<std::mutex> lock(d->m_Lock);
177
178 d->pAll.erase_if([&name, &result](INDI::Property & prop) -> bool
179 {
180#if 0
181 if (prop.isNameMatch(name))
182 {
183 // JM 2021-04-28: delete later. We perform the actual delete after 100ms to give clients a chance to remove the object.
184 // This is necessary when rapid define-delete-define sequences are made.
185 // This HACK is not ideal. We need to start using std::shared_ptr for this purpose soon, but this will be a major change to the
186 // interface. Perhaps for INDI 2.0
187 std::thread([prop]
188 {
189 std::this_thread::sleep_for(std::chrono::milliseconds(100));
190 }).detach();
191 result = 0;
192 return true;
193 }
194 else
195 {
196 return false;
197 }
198#endif
199 if (prop.isNameMatch(name))
200 {
201 result = 0;
202 return true;
203 }
204 else
205 return false;
206 });
207
208 if (result != 0)
209 snprintf(errmsg, MAXRBUF, "Error: Property %s not found in device %s.", name, getDeviceName());
210
211 return result;
212}
213
214std::string BaseDevice::getSharedFilePath(std::string fileName)
215{
216 std::string pathName;
217
218 struct stat st;
219
220 // absolute file path
221 if (stat(fileName.c_str(), &st) == 0)
222 {
223 pathName = fileName;
224 return pathName;
225 }
226
227 // get base name of file
228 const size_t lastSlashIdx = fileName.find_last_of("\\/");
229 if (std::string::npos != lastSlashIdx)
230 {
231 fileName.erase(0, lastSlashIdx + 1);
232 }
233
234 const char * indiprefix = getenv("INDIPREFIX");
235 if (indiprefix)
236 {
237#if defined(OSX_EMBEDED_MODE)
238 pathName = std::string(indiprefix) + "/Contents/Resources/" + fileName;
239#elif defined(__APPLE__)
240 pathName = std::string(indiprefix) + "/Contents/Resources/DriverSupport/" + fileName;
241#else
242 pathName = std::string(indiprefix) + "/share/indi/" + fileName;
243#endif
244 }
245 else
246 {
247 pathName = std::string(DATA_INSTALL_DIR) + "/" + fileName;
248 }
249 return pathName;
250}
251
252
253static std::string sGetSheletonFilePath(std::string fileName)
254{
255 std::string pathName;
256
257 struct stat st;
258
259 // ENV file path
260 const char *indiskel = getenv("INDISKEL");
261 if (indiskel)
262 {
263 pathName = indiskel;
264
265 IDLog("Using INDISKEL %s\n", pathName.c_str());
266 return pathName;
267 }
268
269 // absolute file path
270 if (stat(fileName.c_str(), &st) == 0)
271 {
272 pathName = fileName;
273 IDLog("Using %s\n", pathName.c_str());
274 return pathName;
275 }
276
277 // get base name of file
278 const size_t lastSlashIdx = fileName.find_last_of("\\/");
279 if (std::string::npos != lastSlashIdx)
280 {
281 fileName.erase(0, lastSlashIdx + 1);
282 }
283
284 const char * indiprefix = getenv("INDIPREFIX");
285 if (indiprefix)
286 {
287#if defined(OSX_EMBEDED_MODE)
288 pathName = std::string(indiprefix) + "/Contents/Resources/" + fileName;
289#elif defined(__APPLE__)
290 pathName = std::string(indiprefix) + "/Contents/Resources/DriverSupport/" + fileName;
291#else
292 pathName = std::string(indiprefix) + "/share/indi/" + fileName;
293#endif
294 }
295 else
296 {
297 pathName = std::string(DATA_INSTALL_DIR) + "/" + fileName;
298 }
299 IDLog("Using prefix %s\n", pathName.c_str());
300 return pathName;
301}
302
303bool BaseDevice::buildSkeleton(const char *filename)
304{
305 D_PTR(BaseDevice);
306
307 LilXmlDocument document = d->xmlParser.readFromFile(sGetSheletonFilePath(filename));
308
309 if(!document.isValid())
310 {
311 IDLog("Unable to parse skeleton XML: %s", d->xmlParser.errorMessage());
312 return false;
313 }
314
315 char errmsg[MAXRBUF];
316
317 for (const auto &element : document.root().getElements())
318 {
319 buildProp(element, errmsg, true);
320 }
321
322 return true;
323}
324
325int BaseDevice::buildProp(const INDI::LilXmlElement &root, char *errmsg, bool isDynamic)
326{
327 D_PTR(BaseDevice);
328
329 // only for check, #PS: remove
330 {
331 char *rname, *rdev;
332 if (crackDN(root.handle(), &rdev, &rname, errmsg) < 0)
333 return -1;
334 }
335
336 // find type of tag
337 static const std::map<INDI_PROPERTY_TYPE, std::string> tagTypeName =
338 {
339 {INDI_NUMBER, "defNumberVector"},
340 {INDI_SWITCH, "defSwitchVector"},
341 {INDI_TEXT, "defTextVector"},
342 {INDI_LIGHT, "defLightVector"},
343 {INDI_BLOB, "defBLOBVector"}
344 };
345
346 const auto rootTagName = root.tagName();
347 const auto rootTagType = std::find_if(tagTypeName.begin(), tagTypeName.end(), [&rootTagName](const auto & it)
348 {
349 return rootTagName == it.second;
350 });
351
352 if (rootTagType == tagTypeName.end())
353 {
354 snprintf(errmsg, MAXRBUF, "INDI: <%s> Unable to process tag", rootTagName.c_str());
355 return -1;
356 }
357
358 //
359 const char * propertyName = root.getAttribute("name").toCString();
360
361 if (getProperty(propertyName).isValid())
362 {
364 }
365
366 if (d->deviceName.empty())
367 d->deviceName = root.getAttribute("device").toString();
368
369 INDI::Property property;
370 switch (rootTagType->first)
371 {
372 case INDI_NUMBER:
373 {
374 INDI::PropertyNumber typedProperty {0};
375 for (const auto &element : root.getElementsByTagName("defNumber"))
376 {
378
379 widget.setParent(typedProperty.getNumber());
380
381 widget.setName (element.getAttribute("name"));
382 widget.setLabel (element.getAttribute("label"));
383
384 widget.setFormat (element.getAttribute("format"));
385 widget.setMin (element.getAttribute("min"));
386 widget.setMax (element.getAttribute("max"));
387 widget.setStep (element.getAttribute("step"));
388
389 widget.setValue (element.context().toDoubleSexa());
390
391 if (!widget.isNameMatch(""))
392 typedProperty.push(std::move(widget));
393 }
394 property = typedProperty;
395 break;
396 }
397 case INDI_SWITCH:
398 {
399 INDI::PropertySwitch typedProperty {0};
400 typedProperty.setRule(root.getAttribute("rule"));
401 for (const auto &element : root.getElementsByTagName("defSwitch"))
402 {
404
405 widget.setParent(typedProperty.getSwitch());
406
407 widget.setName (element.getAttribute("name"));
408 widget.setLabel (element.getAttribute("label"));
409
410 widget.setState (element.context());
411
412 if (!widget.isNameMatch(""))
413 typedProperty.push(std::move(widget));
414 }
415 property = typedProperty;
416 break;
417 }
418
419 case INDI_TEXT:
420 {
421 INDI::PropertyText typedProperty {0};
422 for (const auto &element : root.getElementsByTagName("defText"))
423 {
425
426 widget.setParent(typedProperty.getText());
427
428 widget.setName (element.getAttribute("name"));
429 widget.setLabel (element.getAttribute("label"));
430
431 widget.setText (element.context());
432
433 if (!widget.isNameMatch(""))
434 typedProperty.push(std::move(widget));
435 }
436 property = typedProperty;
437 break;
438 }
439
440 case INDI_LIGHT:
441 {
442 INDI::PropertyLight typedProperty {0};
443 for (const auto &element : root.getElementsByTagName("defLight"))
444 {
446
447 widget.setParent(typedProperty.getLight());
448
449 widget.setName (element.getAttribute("name"));
450 widget.setLabel (element.getAttribute("label"));
451
452 widget.setState (element.context());
453
454 if (!widget.isNameMatch(""))
455 typedProperty.push(std::move(widget));
456 }
457 property = typedProperty;
458 break;
459 }
460
461 case INDI_BLOB:
462 {
463 INDI::PropertyBlob typedProperty {0};
464 typedProperty.setBlobDeleter([](void * &blob)
465 {
466#ifdef ENABLE_INDI_SHARED_MEMORY
467 IDSharedBlobFree(blob);
468#else
469 free(blob);
470#endif
471 blob = nullptr;
472 });
473 for (const auto &element : root.getElementsByTagName("defBLOB"))
474 {
476
477 widget.setParent(typedProperty.getBLOB());
478
479 widget.setName (element.getAttribute("name"));
480 widget.setLabel (element.getAttribute("label"));
481
482 widget.setFormat (element.getAttribute("format"));
483
484 if (!widget.isNameMatch(""))
485 typedProperty.push(std::move(widget));
486 }
487 property = typedProperty;
488 break;
489 }
490
491 case INDI_UNKNOWN: // it will never happen
492 return -1;
493 }
494
495 if (!property.isValid())
496 {
497 IDLog("%s: invalid name '%s'\n", propertyName, rootTagName.c_str());
498 return 0;
499 }
500
501 if (property.isEmpty())
502 {
503 IDLog("%s: %s with no valid members\n", propertyName, rootTagName.c_str());
504 return 0;
505 }
506
507 property.setBaseDevice (*this);
508 property.setName (propertyName);
509 property.setDynamic (isDynamic);
510 property.setDeviceName (getDeviceName());
511
512 property.setLabel (root.getAttribute("label"));
513 property.setGroupName (root.getAttribute("group"));
514 property.setState (root.getAttribute("state"));
515
516 if (rootTagType->first != INDI_LIGHT)
517 {
518 property.setTimeout(root.getAttribute("timeout"));
519 property.setPermission(root.getAttribute("perm").toIPerm());
520 }
521
522 d->addProperty(property);
523
524 // IDLog("Adding number property %s to list.\n", property.getName());
525 d->mediateNewProperty(property);
526
527 return (0);
528}
529
530bool BaseDevice::isConnected() const
531{
532 auto svp = getSwitch(INDI::SP::CONNECTION);
533 if (!svp)
534 return false;
535
536 auto sp = svp.findWidgetByName("CONNECT");
537
538 return sp && sp->getState() == ISS_ON && svp.getState() == IPS_OK;
539}
540
541void BaseDevice::attach()
542{
543 D_PTR(BaseDevice);
544 d->mediateNewDevice(*this);
545}
546
547void BaseDevice::detach()
548{
549 D_PTR(BaseDevice);
550 d->mediateRemoveDevice(*this);
551}
552
553// helper for BaseDevice::setValue
554template <typename TypedProperty>
555static void for_property(
556 //XMLEle *root,
557 const LilXmlElement &root,
558 INDI::Property &property,
559 const std::function<void(const LilXmlElement &, INDI::WidgetView<typename TypedProperty::ViewType> *)> &function
560)
561{
562 TypedProperty typedProperty = property;
563
564 for (const auto &element : root.getElements())
565 {
566 auto * item = typedProperty.findWidgetByName(element.getAttribute("name"));
567 if (item)
568 function(element, item);
569 }
570
571 typedProperty.emitUpdate();
572}
573
574/*
575 * return 0 if ok else -1 with reason in errmsg
576 */
577int BaseDevice::setValue(const INDI::LilXmlElement &root, char *errmsg)
578{
579 D_PTR(BaseDevice);
580
581 if (!root.getAttribute("name").isValid())
582 {
583 snprintf(errmsg, MAXRBUF, "INDI: <%s> unable to find name attribute", root.tagName().c_str());
584 return -1;
585 }
586
587 // check message
588 checkMessage(root.handle());
589
590 // find type of tag
591 static const std::map<INDI_PROPERTY_TYPE, std::string> tagTypeName =
592 {
593 {INDI_NUMBER, "setNumberVector"},
594 {INDI_SWITCH, "setSwitchVector"},
595 {INDI_TEXT, "setTextVector"},
596 {INDI_LIGHT, "setLightVector"},
597 {INDI_BLOB, "setBLOBVector"}
598 };
599
600 const auto rootTagName = root.tagName();
601 const auto rootTagType = std::find_if(tagTypeName.begin(), tagTypeName.end(), [&rootTagName](const auto & it)
602 {
603 return rootTagName == it.second;
604 });
605
606 if (rootTagType == tagTypeName.end())
607 {
608 snprintf(errmsg, MAXRBUF, "INDI: <%s> Unable to process tag", rootTagName.c_str());
609 return -1;
610 }
611
612 // update generic values
613 const char * propertyName = root.getAttribute("name").toCString();
614
615 INDI::Property property = getProperty(propertyName, rootTagType->first);
616
617 if (!property.isValid())
618 {
619 snprintf(errmsg, MAXRBUF, "INDI: Could not find property %s in %s", propertyName, getDeviceName());
620 return -1;
621 }
622
623 // 1. set overall property state, if any
624 {
625 bool ok = false;
626 property.setState(root.getAttribute("state").toIPState(&ok));
627
628 if (!ok)
629 {
630 snprintf(errmsg, MAXRBUF, "INDI: <%s> bogus state %s for %s", rootTagName.c_str(), root.getAttribute("state").toCString(),
631 propertyName);
632 return -1;
633 }
634 }
635
636 // 2. allow changing the timeout
637 if (rootTagType->first != INDI_LIGHT)
638 {
639 AutoCNumeric locale;
640 bool ok = false;
641 auto timeoutValue = root.getAttribute("timeout").toDouble(&ok);
642 if (ok)
643 property.setTimeout(timeoutValue);
644 }
645
646 // update specific values
647 switch (rootTagType->first)
648 {
649 case INDI_NUMBER:
650 {
651 AutoCNumeric locale;
652 for_property<INDI::PropertyNumber>(root, property, [](const LilXmlElement & element, auto * item)
653 {
654 item->setValue(element.context());
655
656 // Permit changing of min/max
657 if (auto min = element.getAttribute("min")) item->setMin(min);
658 if (auto max = element.getAttribute("max")) item->setMax(max);
659 });
660 locale.Restore();
661 break;
662 }
663
664 case INDI_SWITCH:
665 {
666 for_property<INDI::PropertySwitch>(root, property, [](const LilXmlElement & element, auto * item)
667 {
668 item->setState(element.context());
669 });
670 break;
671 }
672
673 case INDI_TEXT:
674 {
675 for_property<INDI::PropertyText>(root, property, [](const LilXmlElement & element, auto * item)
676 {
677 item->setText(element.context());
678 });
679 break;
680 }
681
682 case INDI_LIGHT:
683 {
684 for_property<INDI::PropertyLight>(root, property, [](const LilXmlElement & element, auto * item)
685 {
686 item->setState(element.context());
687 });
688 break;
689 }
690
691 case INDI_BLOB:
692 {
693 if (d->setBLOB(PropertyBlob(property), root, errmsg) < 0)
694 return -1;
695 break;
696 }
697
698 case INDI_UNKNOWN: // it will never happen
699 return -1;
700 }
701
702 d->mediateUpdateProperty(property);
703
704 return 0;
705}
706
707#ifdef ENABLE_INDI_SHARED_MEMORY
708static bool sSharedToBlob(const INDI::LilXmlElement &element, INDI::WidgetViewBlob &widget)
709{
710 auto attachementId = element.getAttribute("attached-data-id");
711
712 if (!attachementId.isValid())
713 {
714 return false;
715 }
716
717 auto size = element.getAttribute("size");
718 // Client mark blob that can be attached directly
719
720 // FIXME: Where is the blob data buffer freed at the end ?
721 // FIXME: blobSize is not buffer size here. Must pass it all the way through
722 // (while compressing shared buffer is useless)
723 if (auto directAttachment = element.getAttribute("attachment-direct"))
724 {
725 if (widget.getBlob())
726 {
727 IDSharedBlobFree(widget.getBlob());
728 widget.setBlobLen(0);
729 }
730 widget.setBlob(attachBlobByUid(attachementId.toString(), size));
731 }
732 else
733 {
734 // For compatibility, copy to a modifiable memory area
735 widget.setBlob(realloc(widget.getBlob(), size));
736 void *tmp = attachBlobByUid(attachementId.toString(), size);
737 memcpy(widget.getBlob(), tmp, size);
738 IDSharedBlobFree(tmp);
739 }
740 widget.setBlobLen(size);
741
742 return true;
743}
744#endif
745
746/* Set BLOB vector. Process incoming data stream
747 * Return 0 if okay, -1 if error
748*/
749int BaseDevicePrivate::setBLOB(INDI::PropertyBlob property, const LilXmlElement &root, char *errmsg)
750{
751 for (const auto &element : root.getElementsByTagName("oneBLOB"))
752 {
753 auto name = element.getAttribute("name");
754 auto format = element.getAttribute("format");
755 auto size = element.getAttribute("size");
756
757 auto widget = property.findWidgetByName(name);
758
759 if (!name || !format || !size)
760 {
761 snprintf(errmsg, MAXRBUF, "INDI: %s.%s.%s No valid members.",
762 property.getDeviceName(), property.getName(), name.toCString()
763 );
764 return -1;
765 }
766
767 if (size.toInt() == 0)
768 {
769 continue;
770 }
771
772 widget->setSize(size);
773#ifdef ENABLE_INDI_SHARED_MEMORY
774 if (sSharedToBlob(element, *widget) == false)
775#endif
776 {
777 size_t base64_encoded_size = element.context().size();
778 size_t base64_decoded_size = 3 * base64_encoded_size / 4;
779 widget->setBlob(realloc(widget->getBlob(), base64_decoded_size));
780 int blobLen = from64tobits_fast(static_cast<char *>(widget->getBlob()), element.context(), base64_encoded_size);
781 widget->setBlobLen(blobLen);
782 }
783
784 if (format.endsWith(".z"))
785 {
786 widget->setFormat(format.toString().substr(0, format.lastIndexOf(".z")));
787
788 uLongf dataSize = widget->getSize() * sizeof(uint8_t);
789 Bytef *dataBuffer = static_cast<Bytef *>(malloc(dataSize));
790
791 if (dataBuffer == nullptr)
792 {
793 strncpy(errmsg, "Unable to allocate memory for data buffer", MAXRBUF);
794 return -1;
795 }
796 int r = uncompress(dataBuffer, &dataSize, static_cast<unsigned char *>(widget->getBlob()),
797 static_cast<uLong>(widget->getBlobLen()));
798 if (r != Z_OK)
799 {
800 snprintf(errmsg, MAXRBUF, "INDI: %s.%s.%s compression error: %d",
801 property.getDeviceName(), property.getName(), widget->getName(), r);
802 free(dataBuffer);
803 return -1;
804 }
805 widget->setSize(dataSize);
806#ifdef ENABLE_INDI_SHARED_MEMORY
807 IDSharedBlobFree(widget->getBlob());
808#else
809 free(widget->getBlob());
810#endif
811 widget->setBlob(dataBuffer);
812
813 }
814 else
815 {
816 widget->setFormat(format);
817 }
818
819 property.emitUpdate();
820 }
821
822 return 0;
823}
824
825void BaseDevice::setDeviceName(const char *dev)
826{
827 D_PTR(BaseDevice);
828 d->deviceName = dev;
829}
830
831const char *BaseDevice::getDeviceName() const
832{
833 D_PTR(const BaseDevice);
834 return d->deviceName.data();
835}
836
837bool BaseDevice::isDeviceNameMatch(const char *otherName) const
838{
839 D_PTR(const BaseDevice);
840 return d->deviceName == otherName;
841}
842
843bool BaseDevice::isDeviceNameMatch(const std::string &otherName) const
844{
845 D_PTR(const BaseDevice);
846 return d->deviceName == otherName;
847}
848
849/* add message to queue
850 * N.B. don't put carriage control in msg, we take care of that.
851 */
852void BaseDevice::checkMessage(XMLEle *root)
853{
854 XMLAtt *ap;
855 ap = findXMLAtt(root, "message");
856
857 if (ap)
858 doMessage(root);
859}
860
861/* Store msg in queue */
862void BaseDevice::doMessage(XMLEle *msg)
863{
864 XMLAtt *message;
865 XMLAtt *time_stamp;
866
867 char msgBuffer[MAXRBUF];
868
869 /* prefix our timestamp if not with msg */
870 time_stamp = findXMLAtt(msg, "timestamp");
871
872 /* finally! the msg */
873 message = findXMLAtt(msg, "message");
874 if (!message)
875 return;
876
877 if (time_stamp)
878 snprintf(msgBuffer, MAXRBUF, "%s: %s ", valuXMLAtt(time_stamp), valuXMLAtt(message));
879 else
880 snprintf(msgBuffer, MAXRBUF, "%s: %s ", indi_timestamp(), valuXMLAtt(message));
881
882 std::string finalMsg = msgBuffer;
883
884 // Prepend to the log
885 addMessage(finalMsg);
886}
887
888void BaseDevice::addMessage(const std::string &msg)
889{
890 D_PTR(BaseDevice);
891 std::unique_lock<std::mutex> guard(d->m_Lock);
892 d->messageLog.push_back(msg);
893 guard.unlock();
894
895 d->mediateNewMessage(*this, int(d->messageLog.size() - 1));
896}
897
898const std::string &BaseDevice::messageQueue(size_t index) const
899{
900 D_PTR(const BaseDevice);
901 std::lock_guard<std::mutex> lock(d->m_Lock);
902 // Callers may be notified about a message index that no longer belongs to this
903 // device's log (e.g. the device was torn down and replaced between notification
904 // and delivery). Let at() throw std::out_of_range so callers can handle it instead
905 // of aborting via assert().
906 return d->messageLog.at(index);
907}
908
909const std::string &BaseDevice::lastMessage() const
910{
911 D_PTR(const BaseDevice);
912 std::lock_guard<std::mutex> lock(d->m_Lock);
913 assert(d->messageLog.size() != 0);
914 return d->messageLog.back();
915}
916
917bool BaseDevice::isValid() const
918{
919 D_PTR(const BaseDevice);
920 return d->valid;
921}
922
923bool BaseDevice::isSameDevice(const BaseDevice &other) const
924{
925 return d_ptr == other.d_ptr;
926}
927
928void BaseDevice::watchProperty(const char *name, const std::function<void(INDI::Property)> &callback, WATCH watch)
929{
930 D_PTR(BaseDevice);
931 d->watchPropertyMap[name].callback = callback;
932 d->watchPropertyMap[name].watch = watch;
933
934 // call callback function if property already exists
935 INDI::Property property = getProperty(name);
936 if (property.isValid())
937 {
938 callback(property);
939 }
940}
941
942void BaseDevice::registerProperty(const INDI::Property &property)
943{
944 D_PTR(BaseDevice);
945
946 if (property.getType() == INDI_UNKNOWN)
947 return;
948
949 auto pContainer = getProperty(property.getName(), property.getType());
950
951 if (pContainer.isValid())
952 pContainer.setRegistered(true);
953 else
954 d->addProperty(property);
955}
956
957// #PS: TODO remove
958void BaseDevice::registerProperty(const INDI::Property &property, INDI_PROPERTY_TYPE)
959{
960 registerProperty(property);
961}
962
963const char *BaseDevice::getDriverName() const
964{
965 auto driverName = getText("DRIVER_INFO").findWidgetByName("DRIVER_NAME");
966
967 return driverName ? driverName->getText() : nullptr;
968}
969
970const char *BaseDevice::getDriverExec() const
971{
972 auto driverExec = getText("DRIVER_INFO").findWidgetByName("DRIVER_EXEC");
973 return driverExec ? driverExec->getText() : nullptr;
974}
975
976const char *BaseDevice::getDriverVersion() const
977{
978 auto driverVersion = getText("DRIVER_INFO").findWidgetByName("DRIVER_VERSION");
979 return driverVersion ? driverVersion->getText() : nullptr;
980}
981
982uint16_t BaseDevice::getDriverInterface() const
983{
984 auto driverInterface = getText("DRIVER_INFO").findWidgetByName("DRIVER_INTERFACE");
985 return driverInterface ? atoi(driverInterface->getText()) : 0;
986}
987
988void BaseDevice::setMediator(INDI::BaseMediator *mediator)
989{
990 D_PTR(BaseDevice);
991 d->mediator = mediator;
992}
993
994INDI::BaseMediator *BaseDevice::getMediator() const
995{
996 D_PTR(const BaseDevice);
997 return d->mediator;
998}
999
1000BaseDevice *BaseDevice::operator->()
1001{
1002 D_PTR(BaseDevice);
1003 return &d->self;
1004}
1005
1006BaseDevice::operator BaseDevice*()
1007{
1008 D_PTR(BaseDevice);
1009 return isValid() ? &d->self : nullptr;
1010}
1011
1012}
1013
1014#if defined(_MSC_VER)
1015#undef snprintf
1016#pragma warning(pop)
1017#endif
int from64tobits_fast(char *out, const char *in, int inlen)
Definition base64.c:122
BaseDevice::Properties pAll
Class to provide basic INDI device functionality.
Definition basedevice.h:52
INDI::PropertyNumber getNumber(const char *name) const
void * getRawProperty(const char *name, INDI_PROPERTY_TYPE type=INDI_UNKNOWN) const
Return a property and its type given its name.
Property getProperty(const char *name, INDI_PROPERTY_TYPE type=INDI_UNKNOWN) const
Return a property and its type given its name.
INDI::PropertySwitch getSwitch(const char *name) const
virtual ~BaseDevice()
int removeProperty(const char *name, char *errmsg)
Remove a property.
INDI::PropertyBlob getBLOB(const char *name) const
INDI::PropertyLight getLight(const char *name) const
std::shared_ptr< BaseDevicePrivate > d_ptr
Definition basedevice.h:319
INDI::PropertyText getText(const char *name) const
IPerm getPropertyPermission(const char *name) const
IPState getPropertyState(const char *name) const
Properties getProperties()
Return a list of all properties in the device.
Meditates event notification as generated by driver and passed to clients.
Definition indibase.h:98
bool isValid() const
Definition indililxml.h:350
bool isValid() const
Definition indililxml.h:445
LilXmlElement root() const
Definition indililxml.h:450
Elements getElementsByTagName(const char *tagName) const
Definition indililxml.h:388
Elements getElements() const
Definition indililxml.h:377
LilXmlValue context() const
Definition indililxml.h:420
XMLEle * handle() const
Definition indililxml.h:367
std::string tagName() const
Definition indililxml.h:372
LilXmlAttribute getAttribute(const char *name) const
Definition indililxml.h:405
IPState toIPState(safe_ptr< bool > ok=nullptr) const
Definition indililxml.h:284
std::string toString() const
Definition indililxml.h:265
double toDouble(safe_ptr< bool > ok=nullptr) const
Definition indililxml.h:248
IPerm toIPerm(safe_ptr< bool > ok=nullptr) const
Definition indililxml.h:291
size_t size() const
Definition indililxml.h:224
const char * toCString() const
Definition indililxml.h:260
const char * getDeviceName() const
const char * getName() const
void setBlobDeleter(const std::function< void(void *&)> &deleter)
Set the Blob Deleter function You can define a function to release the memory of the elements....
Provides generic container for INDI properties.
bool isValid() const
bool isEmpty() const
const char * getName() const
void * getProperty() const
bool isNameMatch(const char *otherName) const
INDI_PROPERTY_TYPE getType() const
void setState(IPState state)
double max(void)
#define min(a, b)
@ ISS_ON
Definition indiapi.h:150
IPerm
Permission hint, with respect to client.
Definition indiapi.h:181
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ INDI_PROPERTY_DUPLICATED
INDI_PROPERTY_TYPE
void IDLog(const char *fmt,...)
Definition indicom.c:305
const char * indi_timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition indicom.c:337
Implementations for common driver routines.
int crackDN(XMLEle *root, char **dev, char **name, char msg[])
Extract dev and name attributes from an XML element.
Definition indidevapi.c:770
#define MAXRBUF
Definition indidriver.c:52
@ INDI_LIGHT
Definition indidriver.c:60
@ INDI_TEXT
Definition indidriver.c:59
@ INDI_UNKNOWN
Definition indidriver.c:62
@ INDI_NUMBER
Definition indidriver.c:57
@ INDI_SWITCH
Definition indidriver.c:58
@ INDI_BLOB
Definition indidriver.c:61
#define getDeviceName
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition lilxml.cpp:524
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition lilxml.cpp:624
const char * CONNECTION
Connect to and disconnect from device.
Namespace to encapsulate INDI client, drivers, and mediator classes.
void * attachBlobByUid(const std::string &identifier, size_t size)
uint16_t type
Definition pwc-ioctl.h:0
void IDSharedBlobFree(void *ptr)
Free a buffer allocated using IDSharedBlobAlloc. Fall back to free for buffer that are not shared blo...
Definition sharedblob.c:139
const char * getName() const
bool isNameMatch(const char *otherName) const
void setFormat(const char *format)
void setParent(IBLOBVectorProperty *parent)
void setLabel(const char *label)
void setName(const char *name)
bool isNameMatch(const char *otherName) const
void setName(const char *name)
void setParent(ILightVectorProperty *parent)
void setState(const IPState &state)
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
void setParent(INumberVectorProperty *parent)
void setName(const char *name)
void setFormat(const char *format)
void setLabel(const char *label)
void setParent(ISwitchVectorProperty *parent)
bool isNameMatch(const char *otherName) const
void setLabel(const char *label)
void setName(const char *name)
void setState(const ISState &state)
void setName(const char *name)
void setText(const char *text, size_t size)
void setParent(ITextVectorProperty *parent)
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const