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 
WeatherInterface(DefaultDevice * defaultDevice)30 WeatherInterface::WeatherInterface(DefaultDevice *defaultDevice) : m_defaultDevice(defaultDevice)
31 {
32 }
33 
~WeatherInterface()34 WeatherInterface::~WeatherInterface()
35 {
36     for (int i = 0; i < ParametersNP.nnp; i++)
37     {
38         free(ParametersN[i].aux0);
39         free(ParametersN[i].aux1);
40         free(ParametersRangeNP[i].np);
41     }
42 
43     free(ParametersN);
44     free(ParametersRangeNP);
45     free(critialParametersL);
46 }
47 
initProperties(const char * statusGroup,const char * paramsGroup)48 void WeatherInterface::initProperties(const char *statusGroup, const char *paramsGroup)
49 {
50     m_ParametersGroup = paramsGroup;
51 
52     // Parameters
53     IUFillNumberVector(&ParametersNP, nullptr, 0, m_defaultDevice->getDeviceName(), "WEATHER_PARAMETERS", "Parameters",
54                        paramsGroup, IP_RO, 60, IPS_OK);
55 
56     // Weather Status
57     IUFillLightVector(&critialParametersLP, nullptr, 0, m_defaultDevice->getDeviceName(), "WEATHER_STATUS", "Status",
58                       statusGroup, IPS_IDLE);
59 }
60 
updateProperties()61 bool WeatherInterface::updateProperties()
62 {
63     if (m_defaultDevice->isConnected())
64     {
65         if (critialParametersL)
66             m_defaultDevice->defineProperty(&critialParametersLP);
67 
68         if (ParametersN)
69             m_defaultDevice->defineProperty(&ParametersNP);
70 
71         if (ParametersRangeNP)
72         {
73             for (int i = 0; i < nRanges; i++)
74                 m_defaultDevice->defineProperty(&ParametersRangeNP[i]);
75         }
76     }
77     else
78     {
79         if (critialParametersL)
80             m_defaultDevice->deleteProperty(critialParametersLP.name);
81 
82         if (ParametersN)
83             m_defaultDevice->deleteProperty(ParametersNP.name);
84 
85         if (ParametersRangeNP)
86         {
87             for (int i = 0; i < nRanges; i++)
88                 m_defaultDevice->deleteProperty(ParametersRangeNP[i].name);
89         }
90 
91     }
92 
93     return true;
94 }
95 
processNumber(const char * dev,const char * name,double values[],char * names[],int n)96 bool WeatherInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
97 {
98     INDI_UNUSED(dev);
99 
100     // Parameter ranges
101     for (int i = 0; i < nRanges; i++)
102     {
103         if (!strcmp(name, ParametersRangeNP[i].name))
104         {
105             IUUpdateNumber(&ParametersRangeNP[i], values, names, n);
106 
107             ParametersN[i].min               = ParametersRangeNP[i].np[0].value;
108             ParametersN[i].max               = ParametersRangeNP[i].np[1].value;
109             *(static_cast<double *>(ParametersN[i].aux0)) = ParametersRangeNP[i].np[2].value;
110 
111             if (syncCriticalParameters())
112                 IDSetLight(&critialParametersLP, nullptr);
113 
114             ParametersRangeNP[i].s = IPS_OK;
115             IDSetNumber(&ParametersRangeNP[i], nullptr);
116 
117             return true;
118         }
119     }
120 
121     return false;
122 }
123 
updateWeather()124 IPState WeatherInterface::updateWeather()
125 {
126     DEBUGDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_ERROR,
127                 "updateWeather() must be implemented in Weather device child class to update GEOGRAPHIC_COORD properties.");
128     return IPS_ALERT;
129 }
130 
addParameter(std::string name,std::string label,double numMinOk,double numMaxOk,double percWarning)131 void WeatherInterface::addParameter(std::string name, std::string label, double numMinOk, double numMaxOk,
132                                     double percWarning)
133 {
134     DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_DEBUG, "Parameter %s is added. Ok (%g,%g,%g) ", name.c_str(),
135                  numMinOk,
136                  numMaxOk, percWarning);
137 
138     ParametersN = (ParametersN == nullptr) ? static_cast<INumber *>(malloc(sizeof(INumber))) :
139                   static_cast<INumber *>(realloc(ParametersN, (ParametersNP.nnp + 1) * sizeof(INumber)));
140 
141     double *warn = static_cast<double *>(malloc(sizeof(double)));
142 
143     *warn = percWarning;
144 
145     IUFillNumber(&ParametersN[ParametersNP.nnp], name.c_str(), label.c_str(), "%4.2f", numMinOk, numMaxOk, 0, 0);
146 
147     ParametersN[ParametersNP.nnp].aux0 = warn;
148 
149     ParametersNP.np = ParametersN;
150     ParametersNP.nnp++;
151 
152     createParameterRange(name, label);
153 }
154 
setParameterValue(std::string name,double value)155 void WeatherInterface::setParameterValue(std::string name, double value)
156 {
157     for (int i = 0; i < ParametersNP.nnp; i++)
158     {
159         if (!strcmp(ParametersN[i].name, name.c_str()))
160         {
161             ParametersN[i].value = value;
162             return;
163         }
164     }
165 }
166 
setCriticalParameter(std::string param)167 bool WeatherInterface::setCriticalParameter(std::string param)
168 {
169     for (int i = 0; i < ParametersNP.nnp; i++)
170     {
171         if (!strcmp(ParametersN[i].name, param.c_str()))
172         {
173             critialParametersL =
174                 (critialParametersL == nullptr) ?
175                 static_cast<ILight *>(malloc(sizeof(ILight))) :
176                 static_cast<ILight *>(realloc(critialParametersL, (critialParametersLP.nlp + 1) * sizeof(ILight)));
177 
178             IUFillLight(&critialParametersL[critialParametersLP.nlp], param.c_str(), ParametersN[i].label, IPS_IDLE);
179 
180             critialParametersLP.lp = critialParametersL;
181 
182             critialParametersLP.nlp++;
183 
184             return true;
185         }
186     }
187 
188     DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
189                  "Unable to find parameter %s in list of existing parameters!", param.c_str());
190     return false;
191 }
192 
checkParameterState(const INumber & parameter) const193 IPState WeatherInterface::checkParameterState(const INumber &parameter) const
194 {
195     double warn = *(static_cast<double *>(parameter.aux0));
196     double rangeWarn = (parameter.max - parameter.min) * (warn / 100);
197 
198     if ((parameter.value < parameter.min) || (parameter.value > parameter.max))
199     {
200         return IPS_ALERT;
201     }
202     //else if (parameter.value < (parameter.min + rangeWarn) || parameter.value > (parameter.max - rangeWarn))
203     // FIXME This is a hack to prevent warnings parameters which minimum values are zero (e.g. Wind)
204     else if (   ((parameter.value < (parameter.min + rangeWarn)) && parameter.min != 0)
205                 || ((parameter.value > (parameter.max - rangeWarn)) && parameter.max != 0))
206     {
207         return IPS_BUSY;
208     }
209     else
210     {
211         return IPS_OK;
212     }
213 
214     return IPS_IDLE;
215 }
216 
checkParameterState(const std::string & param) const217 IPState WeatherInterface::checkParameterState(const std::string &param) const
218 {
219     for (int i = 0; i < ParametersNP.nnp; i++)
220     {
221         if (!strcmp(ParametersN[i].name, param.c_str()))
222         {
223             return checkParameterState(ParametersN[i]);
224         }
225     }
226 
227     return IPS_IDLE;
228 }
229 
syncCriticalParameters()230 bool WeatherInterface::syncCriticalParameters()
231 {
232     if (critialParametersL == nullptr)
233         return false;
234 
235     std::vector<IPState> preStates(critialParametersLP.nlp);
236     for (int i = 0; i < critialParametersLP.nlp; i++)
237         preStates[i] = critialParametersL[i].s;
238 
239     critialParametersLP.s = IPS_IDLE;
240 
241     for (int i = 0; i < critialParametersLP.nlp; i++)
242     {
243         for (int j = 0; j < ParametersNP.nnp; j++)
244         {
245             if (!strcmp(critialParametersL[i].name, ParametersN[j].name))
246             {
247                 IPState state = checkParameterState(ParametersN[j]);
248                 switch (state)
249                 {
250                     case IPS_BUSY:
251                         critialParametersL[i].s = IPS_BUSY;
252                         DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
253                                      "Warning: Parameter %s value (%g) is in the warning zone!",
254                                      ParametersN[j].label, ParametersN[j].value);
255                         break;
256 
257                     case IPS_ALERT:
258                         critialParametersL[i].s = IPS_ALERT;
259                         DEBUGFDEVICE(m_defaultDevice->getDeviceName(), Logger::DBG_WARNING,
260                                      "Caution: Parameter %s value (%g) is in the danger zone!",
261                                      ParametersN[j].label, ParametersN[j].value);
262                         break;
263 
264                     case IPS_IDLE:
265                     case IPS_OK:
266                         critialParametersL[i].s = IPS_OK;
267                 }
268             }
269         }
270 
271         // The overall state is the worst individual state.
272         if (critialParametersL[i].s > critialParametersLP.s)
273             critialParametersLP.s = critialParametersL[i].s;
274     }
275 
276     // if Any state changed, return true.
277     for (int i = 0; i < critialParametersLP.nlp; i++)
278     {
279         if (preStates[i] != critialParametersL[i].s)
280             return true;
281     }
282 
283     return false;
284 }
285 
createParameterRange(std::string name,std::string label)286 void WeatherInterface::createParameterRange(std::string name, std::string label)
287 {
288     ParametersRangeNP =
289         (ParametersRangeNP == nullptr) ?
290         static_cast<INumberVectorProperty *>(malloc(sizeof(INumberVectorProperty))) :
291         static_cast<INumberVectorProperty *>(realloc(ParametersRangeNP, (nRanges + 1) * sizeof(INumberVectorProperty)));
292 
293     INumber *rangesN = static_cast<INumber *>(malloc(sizeof(INumber) * 3));
294 
295     IUFillNumber(&rangesN[0], "MIN_OK", "OK range min", "%4.2f", -1e6, 1e6, 0, ParametersN[nRanges].min);
296     IUFillNumber(&rangesN[1], "MAX_OK", "OK range max", "%4.2f", -1e6, 1e6, 0, ParametersN[nRanges].max);
297     IUFillNumber(&rangesN[2], "PERC_WARN", "% for Warning", "%g", 0, 100, 1,
298                  *(static_cast<double *>(ParametersN[nRanges].aux0)));
299 
300     char propName[MAXINDINAME];
301     char propLabel[MAXINDILABEL];
302     snprintf(propName, MAXINDINAME, "%s", name.c_str());
303     snprintf(propLabel, MAXINDILABEL, "%s", label.c_str());
304 
305     IUFillNumberVector(&ParametersRangeNP[nRanges], rangesN, 3, m_defaultDevice->getDeviceName(), propName, propLabel,
306                        m_ParametersGroup.c_str(),
307                        IP_RW, 60, IPS_IDLE);
308 
309     nRanges++;
310 }
311 
saveConfigItems(FILE * fp)312 bool WeatherInterface::saveConfigItems(FILE *fp)
313 {
314     for (int i = 0; i < nRanges; i++)
315         IUSaveConfigNumber(fp, &ParametersRangeNP[i]);
316     return true;
317 }
318 
319 
320 }
321