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 ¶meter) 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 ¶m) 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