Instrument Neutral Distributed Interface INDI  2.0.0
indiweatherinterface.cpp
Go to the documentation of this file.
1 /*
2  Weather Interface
3  Copyright (C) 2018 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "indiweatherinterface.h"
22 
23 #include "indilogger.h"
24 
25 #include <cstring>
26 
27 namespace INDI
28 {
29 
30 WeatherInterface::WeatherInterface(DefaultDevice *defaultDevice) : m_defaultDevice(defaultDevice)
31 {
32  m_UpdateTimer.callOnTimeout(std::bind(&WeatherInterface::checkWeatherUpdate, this));
33  m_UpdateTimer.setSingleShot(true);
34  m_UpdateTimer.setInterval(60000);
35 }
36 
38 {
39  for (int i = 0; i < ParametersNP.nnp; i++)
40  {
41  free(ParametersN[i].aux0);
42  free(ParametersN[i].aux1);
43  free(ParametersRangeNP[i].np);
44  }
45 
46  free(ParametersN);
47  free(ParametersRangeNP);
48  free(critialParametersL);
49 }
50 
51 void WeatherInterface::initProperties(const char *statusGroup, const char *paramsGroup)
52 {
53  m_ParametersGroup = paramsGroup;
54 
55  // Update Period
56  UpdatePeriodNP[0].fill("PERIOD", "Period (s)", "%.f", 0, 3600, 60, 60);
57  UpdatePeriodNP.fill(m_defaultDevice->getDeviceName(), "WEATHER_UPDATE", "Update", statusGroup,
58  IP_RW, 60, IPS_IDLE);
59 
60  // Refresh
61  RefreshSP[0].fill("REFRESH", "Refresh", ISS_OFF);
62  RefreshSP.fill(m_defaultDevice->getDeviceName(), "WEATHER_REFRESH", "Weather", statusGroup, IP_RW, ISR_ATMOST1, 0,
63  IPS_IDLE);
64 
65  // Override
66  OverrideSP[0].fill("OVERRIDE", "Override Status", ISS_OFF);
67  OverrideSP.fill(m_defaultDevice->getDeviceName(), "WEATHER_OVERRIDE", "Safety", statusGroup, IP_RW,
68  ISR_NOFMANY, 0, IPS_IDLE);
69 
70  // Parameters
71  IUFillNumberVector(&ParametersNP, nullptr, 0, m_defaultDevice->getDeviceName(), "WEATHER_PARAMETERS", "Parameters",
72  paramsGroup, IP_RO, 60, IPS_OK);
73 
74  // Weather Status
75  IUFillLightVector(&critialParametersLP, nullptr, 0, m_defaultDevice->getDeviceName(), "WEATHER_STATUS", "Status",
76  statusGroup, IPS_IDLE);
77 }
78 
80 {
81  if (m_defaultDevice->isConnected())
82  {
83  m_defaultDevice->defineProperty(UpdatePeriodNP);
84  m_defaultDevice->defineProperty(RefreshSP);
85  m_defaultDevice->defineProperty(OverrideSP);
86 
88  m_defaultDevice->defineProperty(&critialParametersLP);
89 
90  if (ParametersN)
91  m_defaultDevice->defineProperty(&ParametersNP);
92 
94  {
95  for (int i = 0; i < nRanges; i++)
96  m_defaultDevice->defineProperty(&ParametersRangeNP[i]);
97  }
98 
100  }
101  else
102  {
103  m_defaultDevice->deleteProperty(UpdatePeriodNP);
104  m_defaultDevice->deleteProperty(RefreshSP);
105  m_defaultDevice->deleteProperty(OverrideSP);
106 
107  if (critialParametersL)
108  m_defaultDevice->deleteProperty(critialParametersLP.name);
109 
110  if (ParametersN)
111  m_defaultDevice->deleteProperty(ParametersNP.name);
112 
113  if (ParametersRangeNP)
114  {
115  for (int i = 0; i < nRanges; i++)
116  m_defaultDevice->deleteProperty(ParametersRangeNP[i].name);
117  }
118 
119  }
120 
121  return true;
122 }
123 
125 {
126  if (!m_defaultDevice->isConnected())
127  return;
128 
129  IPState state = updateWeather();
130 
131  switch (state)
132  {
133  // Ok
134  case IPS_OK:
135 
137  {
138  // Override weather state if required
139  if (OverrideSP[0].getState() == ISS_ON)
141 
142  IDSetLight(&critialParametersLP, nullptr);
143  }
144 
145  ParametersNP.s = state;
146  IDSetNumber(&ParametersNP, nullptr);
147 
148  // If update period is set, then set up the timer
149  if (UpdatePeriodNP[0].getValue() > 0)
150  m_UpdateTimer.start(UpdatePeriodNP[0].getValue() * 1000);
151 
152  return;
153 
154  // Alert
155  // We retry every 5000 ms until we get OK
156  case IPS_ALERT:
157  ParametersNP.s = state;
158  IDSetNumber(&ParametersNP, nullptr);
159  break;
160 
161  // Weather update is in progress
162  default:
163  break;
164  }
165 
166  m_UpdateTimer.start(5000);
167 }
168 
169 bool WeatherInterface::processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
170 {
171  INDI_UNUSED(dev);
172 
173  // Refresh
174  if (RefreshSP.isNameMatch(name))
175  {
178  RefreshSP.apply();
179 
181  return true;
182  }
183 
184  // Override
185  if (OverrideSP.isNameMatch(name))
186  {
187  OverrideSP.update(states, names, n);
188  if (OverrideSP[0].getState() == ISS_ON)
189  {
190  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
191  "Weather override is enabled. Observatory is not safe. Turn off override as soon as possible.");
194  IDSetLight(&critialParametersLP, nullptr);
195  }
196  else
197  {
198  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Weather override is disabled");
200 
202  IDSetLight(&critialParametersLP, nullptr);
203  }
204 
205  OverrideSP.apply();
206  return true;
207  }
208 
209  return false;
210 }
211 
212 bool WeatherInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
213 {
214  INDI_UNUSED(dev);
215 
216  // Update period
217  if (UpdatePeriodNP.isNameMatch(name))
218  {
219  UpdatePeriodNP.update(values, names, n);
222 
223  if (UpdatePeriodNP[0].getValue() == 0)
224  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_SESSION, "Periodic updates are disabled.");
225  else
226  {
227  m_UpdateTimer.setInterval(UpdatePeriodNP[0].getValue() * 1000);
228  m_UpdateTimer.start();
229  }
230  return true;
231  }
232  else
233  {
234  // Parameter ranges
235  for (int i = 0; i < nRanges; i++)
236  {
237  if (!strcmp(name, ParametersRangeNP[i].name))
238  {
239  IUUpdateNumber(&ParametersRangeNP[i], values, names, n);
240 
241  ParametersN[i].min = ParametersRangeNP[i].np[0].value;
242  ParametersN[i].max = ParametersRangeNP[i].np[1].value;
243  *(static_cast<double *>(ParametersN[i].aux0)) = ParametersRangeNP[i].np[2].value;
244 
246  IDSetLight(&critialParametersLP, nullptr);
247 
249  IDSetNumber(&ParametersRangeNP[i], nullptr);
250 
251  return true;
252  }
253  }
254  }
255 
256  return false;
257 }
258 
260 {
261  DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR,
262  "updateWeather() must be implemented in Weather device child class to update GEOGRAPHIC_COORD properties.");
263  return IPS_ALERT;
264 }
265 
266 void WeatherInterface::addParameter(std::string name, std::string label, double numMinOk, double numMaxOk,
267  double percWarning)
268 {
269  DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_DEBUG, "Parameter %s is added. Ok (%g,%g,%g) ", name.c_str(),
270  numMinOk,
271  numMaxOk, percWarning);
272 
273  ParametersN = (ParametersN == nullptr) ? static_cast<INumber *>(malloc(sizeof(INumber))) :
274  static_cast<INumber *>(realloc(ParametersN, (ParametersNP.nnp + 1) * sizeof(INumber)));
275 
276  double *warn = static_cast<double *>(malloc(sizeof(double)));
277 
278  *warn = percWarning;
279 
280  IUFillNumber(&ParametersN[ParametersNP.nnp], name.c_str(), label.c_str(), "%4.2f", numMinOk, numMaxOk, 0, 0);
281 
282  ParametersN[ParametersNP.nnp].aux0 = warn;
283 
285  ParametersNP.nnp++;
286 
287  createParameterRange(name, label);
288 }
289 
290 void WeatherInterface::setParameterValue(std::string name, double value)
291 {
292  for (int i = 0; i < ParametersNP.nnp; i++)
293  {
294  if (!strcmp(ParametersN[i].name, name.c_str()))
295  {
296  ParametersN[i].value = value;
297  return;
298  }
299  }
300 }
301 
303 {
304  for (int i = 0; i < ParametersNP.nnp; i++)
305  {
306  if (!strcmp(ParametersN[i].name, param.c_str()))
307  {
309  (critialParametersL == nullptr) ?
310  static_cast<ILight *>(malloc(sizeof(ILight))) :
311  static_cast<ILight *>(realloc(critialParametersL, (critialParametersLP.nlp + 1) * sizeof(ILight)));
312 
314 
316 
318 
319  return true;
320  }
321  }
322 
323  DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
324  "Unable to find parameter %s in list of existing parameters!", param.c_str());
325  return false;
326 }
327 
329 {
330  double warn = *(static_cast<double *>(parameter.aux0));
331  double rangeWarn = (parameter.max - parameter.min) * (warn / 100);
332 
333  if ((parameter.value < parameter.min) || (parameter.value > parameter.max))
334  {
335  return IPS_ALERT;
336  }
337  //else if (parameter.value < (parameter.min + rangeWarn) || parameter.value > (parameter.max - rangeWarn))
338  // FIXME This is a hack to prevent warnings parameters which minimum values are zero (e.g. Wind)
339  else if ( ((parameter.value < (parameter.min + rangeWarn)) && parameter.min != 0)
340  || ((parameter.value > (parameter.max - rangeWarn)) && parameter.max != 0))
341  {
342  return IPS_BUSY;
343  }
344  else
345  {
346  return IPS_OK;
347  }
348 
349  return IPS_IDLE;
350 }
351 
352 IPState WeatherInterface::checkParameterState(const std::string &param) const
353 {
354  for (int i = 0; i < ParametersNP.nnp; i++)
355  {
356  if (!strcmp(ParametersN[i].name, param.c_str()))
357  {
358  return checkParameterState(ParametersN[i]);
359  }
360  }
361 
362  return IPS_IDLE;
363 }
364 
366 {
367  if (critialParametersL == nullptr)
368  return false;
369 
370  std::vector<IPState> preStates(critialParametersLP.nlp);
371  for (int i = 0; i < critialParametersLP.nlp; i++)
372  preStates[i] = critialParametersL[i].s;
373 
375 
376  for (int i = 0; i < critialParametersLP.nlp; i++)
377  {
378  for (int j = 0; j < ParametersNP.nnp; j++)
379  {
380  if (!strcmp(critialParametersL[i].name, ParametersN[j].name))
381  {
383  switch (state)
384  {
385  case IPS_BUSY:
387  DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
388  "Warning: Parameter %s value (%g) is in the warning zone!",
389  ParametersN[j].label, ParametersN[j].value);
390  break;
391 
392  case IPS_ALERT:
394  DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
395  "Caution: Parameter %s value (%g) is in the danger zone!",
396  ParametersN[j].label, ParametersN[j].value);
397  break;
398 
399  case IPS_IDLE:
400  case IPS_OK:
401  critialParametersL[i].s = IPS_OK;
402  }
403  }
404  }
405 
406  // The overall state is the worst individual state.
409  }
410 
411  // if Any state changed, return true.
412  for (int i = 0; i < critialParametersLP.nlp; i++)
413  {
414  if (preStates[i] != critialParametersL[i].s)
415  return true;
416  }
417 
418  return false;
419 }
420 
421 void WeatherInterface::createParameterRange(std::string name, std::string label)
422 {
424  (ParametersRangeNP == nullptr) ?
425  static_cast<INumberVectorProperty *>(malloc(sizeof(INumberVectorProperty))) :
426  static_cast<INumberVectorProperty *>(realloc(ParametersRangeNP, (nRanges + 1) * sizeof(INumberVectorProperty)));
427 
428  INumber *rangesN = static_cast<INumber *>(malloc(sizeof(INumber) * 3));
429 
430  IUFillNumber(&rangesN[0], "MIN_OK", "OK range min", "%4.2f", -1e6, 1e6, 0, ParametersN[nRanges].min);
431  IUFillNumber(&rangesN[1], "MAX_OK", "OK range max", "%4.2f", -1e6, 1e6, 0, ParametersN[nRanges].max);
432  IUFillNumber(&rangesN[2], "PERC_WARN", "% for Warning", "%g", 0, 100, 1,
433  *(static_cast<double *>(ParametersN[nRanges].aux0)));
434 
435  char propName[MAXINDINAME];
436  char propLabel[MAXINDILABEL];
437  snprintf(propName, MAXINDINAME, "%s", name.c_str());
438  snprintf(propLabel, MAXINDILABEL, "%s", label.c_str());
439 
440  IUFillNumberVector(&ParametersRangeNP[nRanges], rangesN, 3, m_defaultDevice->getDeviceName(), propName, propLabel,
441  m_ParametersGroup.c_str(),
442  IP_RW, 60, IPS_IDLE);
443 
444  nRanges++;
445 }
446 
448 {
449  UpdatePeriodNP.save(fp);
450  for (int i = 0; i < nRanges; i++)
452  return true;
453 }
454 
455 
456 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void setSingleShot(bool singleShot)
Set whether the timer is a single-shot timer.
Definition: inditimer.cpp:109
void callOnTimeout(const std::function< void()> &callback)
Definition: inditimer.cpp:76
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition: inditimer.cpp:82
void setInterval(int msec)
Set the timeout interval in milliseconds.
Definition: inditimer.cpp:103
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process weather switch properties.
bool syncCriticalParameters()
updateWeatherState Send update weather state to client
virtual IPState updateWeather()
updateWeather Update weather conditions from device or service. The function should not change the st...
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool setCriticalParameter(std::string param)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process weather number properties.
WeatherInterface(DefaultDevice *defaultDevice)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
ILightVectorProperty critialParametersLP
INDI::PropertySwitch OverrideSP
INumberVectorProperty ParametersNP
IPState checkParameterState(const std::string &param) const
checkParameterState Checks the given parameter against the defined bounds
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning)
addParameter Add a physical weather measurable parameter to the weather driver. The weather value has...
void checkWeatherUpdate()
checkWeatherUpdate Calls updateWeather and update critical parameters accordingly.
void initProperties(const char *statusGroup, const char *paramsGroup)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
INDI::PropertySwitch RefreshSP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber UpdatePeriodNP
INumberVectorProperty * ParametersRangeNP
double max(void)
double min(void)
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
#define MAXINDILABEL
Definition: indiapi.h:192
@ ISR_NOFMANY
Definition: indiapi.h:175
@ ISR_ATMOST1
Definition: indiapi.h:174
#define MAXINDINAME
Definition: indiapi.h:191
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
Definition: indidevapi.c:169
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:255
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
Definition: indidriver.c:1251
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
Namespace to encapsulate INDI client, drivers, and mediator classes.
One light descriptor.
One number descriptor.
char name[MAXINDINAME]
Definition: indiapi.h:421
Number vector property descriptor.
Definition: indiapi.h:319
char name[MAXINDINAME]
Definition: indiapi.h:323