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