53a190e178325c956e7abe091cc24e93747154a6
[public/netxms.git] / src / server / core / sensor.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 Raden Solutions
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program 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
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: sensor.cpp
20 **/
21
22 #include "nxcore.h"
23
24 /**
25 * Default empty Sensor class constructior
26 */
27 Sensor::Sensor() : DataCollectionTarget()
28 {
29 m_proxyNodeId = 0;
30 m_flags = 0;
31 m_deviceClass = SENSOR_CLASS_UNKNOWN;
32 m_vendor = NULL;
33 m_commProtocol = SENSOR_PROTO_UNKNOWN;
34 m_xmlRegConfig = NULL;
35 m_xmlConfig = NULL;
36 m_serialNumber = NULL;
37 m_deviceAddress = NULL;
38 m_metaType = NULL;
39 m_description = NULL;
40 m_lastConnectionTime = 0;
41 m_frameCount = 0; //zero when no info
42 m_signalStrenght = 1; //+1 when no information(cannot be +)
43 m_signalNoise = MAX_INT32; //*10 from origin number
44 m_frequency = 0; //*10 from origin number
45 m_lastStatusPoll = 0;
46 m_lastConfigurationPoll = 0;
47 m_status = STATUS_UNKNOWN;
48 }
49
50 /**
51 * Constructor with all fields for Sensor class
52 */
53 Sensor::Sensor(TCHAR *name, UINT32 flags, MacAddress macAddress, UINT32 deviceClass, TCHAR *vendor,
54 UINT32 commProtocol, TCHAR *xmlRegConfig, TCHAR *xmlConfig, TCHAR *serialNumber, TCHAR *deviceAddress,
55 TCHAR *metaType, TCHAR *description, UINT32 proxyNode) : DataCollectionTarget(name)
56 {
57 m_flags = flags;
58 m_macAddress = macAddress;
59 m_deviceClass = deviceClass;
60 m_vendor = vendor;
61 m_commProtocol = commProtocol;
62 m_xmlRegConfig = xmlRegConfig;
63 m_xmlConfig = xmlConfig;
64 m_serialNumber = serialNumber;
65 m_deviceAddress = deviceAddress;
66 m_metaType = metaType;
67 m_description = description;
68 m_proxyNodeId = proxyNode;
69 m_lastConnectionTime = 0;
70 m_frameCount = 0; //zero when no info
71 m_signalStrenght = 1; //+1 when no information(cannot be +)
72 m_signalNoise = MAX_INT32; //*10 from origin number
73 m_frequency = 0; //*10 from origin number
74 m_lastStatusPoll = 0;
75 m_lastConfigurationPoll = 0;
76 m_status = STATUS_UNKNOWN;
77 }
78
79 Sensor *Sensor::createSensor(TCHAR *name, NXCPMessage *request)
80 {
81 Sensor *sensor = new Sensor(name,
82 request->getFieldAsUInt32(VID_SENSOR_FLAGS),
83 request->getFieldAsMacAddress(VID_MAC_ADDR),
84 request->getFieldAsUInt32(VID_DEVICE_CLASS),
85 request->getFieldAsString(VID_VENDOR),
86 request->getFieldAsUInt32(VID_COMM_PROTOCOL),
87 request->getFieldAsString(VID_XML_REG_CONFIG),
88 request->getFieldAsString(VID_XML_CONFIG),
89 request->getFieldAsString(VID_SERIAL_NUMBER),
90 request->getFieldAsString(VID_DEVICE_ADDRESS),
91 request->getFieldAsString(VID_META_TYPE),
92 request->getFieldAsString(VID_DESCRIPTION),
93 request->getFieldAsUInt32(VID_SENSOR_PROXY));
94
95 switch(request->getFieldAsUInt32(VID_COMM_PROTOCOL))
96 {
97 case COMM_LORAWAN:
98 sensor->generateGuid();
99 if (registerLoraDevice(sensor) == NULL)
100 {
101 delete sensor;
102 return NULL;
103 }
104 break;
105 case COMM_DLMS:
106 sensor->m_state = SSF_PROVISIONED | SSF_REGISTERED | SSF_ACTIVE;
107 break;
108 }
109 return sensor;
110 }
111
112 /**
113 * Create agent connection
114 */
115 AgentConnectionEx *Sensor::getAgentConnection()
116 {
117 UINT32 rcc = ERR_CONNECT_FAILED;
118 if (IsShutdownInProgress())
119 return NULL;
120
121 NetObj *proxy = FindObjectById(m_proxyNodeId, OBJECT_NODE);
122 if(proxy == NULL)
123 return NULL;
124
125 return ((Node *)proxy)->acquireProxyConnection(SENSOR_PROXY);
126 }
127
128 /**
129 * Register LoRa WAN device
130 */
131 Sensor *Sensor::registerLoraDevice(Sensor *sensor)
132 {
133 NetObj *proxy = FindObjectById(sensor->m_proxyNodeId, OBJECT_NODE);
134 if(proxy == NULL)
135 return NULL;
136
137 AgentConnectionEx *conn = sensor->getAgentConnection();
138 if (conn == NULL)
139 {
140 return sensor; //Unprovisoned sensor - will try to provison it on next connect
141 }
142
143 Config regConfig;
144 Config config;
145 #ifdef UNICODE
146 char *regXml = UTF8StringFromWideString(sensor->getXmlRegConfig());
147 regConfig.loadXmlConfigFromMemory(regXml, (UINT32)strlen(regXml), NULL, "config", false);
148 free(regXml);
149
150 char *xml = UTF8StringFromWideString(sensor->getXmlConfig());
151 config.loadXmlConfigFromMemory(xml, (UINT32)strlen(xml), NULL, "config", false);
152 free(xml);
153 #else
154 regConfig.loadXmlConfigFromMemory(sensor->getXmlRegConfig(), (UINT32)strlen(sensor->getXmlRegConfig()), NULL, "config", false);
155 config.loadXmlConfigFromMemory(sensor->getXmlConfig(), (UINT32)strlen(sensor->getXmlConfig()), NULL, "config", false);
156 #endif
157
158
159
160 NXCPMessage msg(conn->getProtocolVersion());
161 msg.setCode(CMD_REGISTER_LORAWAN_SENSOR);
162 msg.setId(conn->generateRequestId());
163 msg.setField(VID_DEVICE_ADDRESS, sensor->getDeviceAddress());
164 msg.setField(VID_MAC_ADDR, sensor->getMacAddress());
165 msg.setField(VID_GUID, sensor->getGuid());
166 msg.setField(VID_DECODER, config.getValueAsInt(_T("/decoder"), 0));
167 msg.setField(VID_REG_TYPE, regConfig.getValueAsInt(_T("/registrationType"), 0));
168 if(regConfig.getValueAsInt(_T("/registrationType"), 0) == 0)
169 {
170 msg.setField(VID_LORA_APP_EUI, regConfig.getValue(_T("/appEUI")));
171 msg.setField(VID_LORA_APP_KEY, regConfig.getValue(_T("/appKey")));
172 }
173 else
174 {
175 msg.setField(VID_LORA_APP_S_KEY, regConfig.getValue(_T("/appSKey")));
176 msg.setField(VID_LORA_NWK_S_KWY, regConfig.getValue(_T("/nwkSKey")));
177 }
178 NXCPMessage *response = conn->customRequest(&msg);
179 if (response != NULL)
180 {
181 if(response->getFieldAsUInt32(VID_RCC) == RCC_SUCCESS)
182 {
183 sensor->lockProperties();
184 sensor->setProvisoned();
185 sensor->unlockProperties();
186 }
187 delete response;
188 }
189
190 return sensor;
191 }
192
193 //set correct status calculation function
194 //set correct configuration poll - provision if possible, for lora get device name, get all possible DCI's, try to do provisionning
195 //set status poll - check if connection is on if not generate alarm, check, that proxy is up and running
196
197 /**
198 * Sensor class destructor
199 */
200 Sensor::~Sensor()
201 {
202 free(m_vendor);
203 free(m_xmlRegConfig);
204 free(m_xmlConfig);
205 free(m_serialNumber);
206 free(m_deviceAddress);
207 free(m_metaType);
208 free(m_description);
209 }
210
211 /**
212 * Load from database SensorDevice class
213 */
214 bool Sensor::loadFromDatabase(DB_HANDLE hdb, UINT32 id)
215 {
216 m_id = id;
217
218 if (!loadCommonProperties(hdb))
219 {
220 nxlog_debug(2, _T("Cannot load common properties for sensor object %d"), id);
221 return false;
222 }
223
224 TCHAR query[512];
225 _sntprintf(query, 512, _T("SELECT mac_address,device_class,vendor,communication_protocol,xml_config,serial_number,device_address,")
226 _T("meta_type,description,last_connection_time,frame_count,signal_strenght,signal_noise,frequency,proxy_node,xml_reg_config FROM sensors WHERE id=%d"), m_id);
227 DB_RESULT hResult = DBSelect(hdb, query);
228 if (hResult == NULL)
229 return false;
230
231 m_macAddress = DBGetFieldMacAddr(hResult, 0, 0);
232 m_deviceClass = DBGetFieldULong(hResult, 0, 1);
233 m_vendor = DBGetField(hResult, 0, 2, NULL, 0);
234 m_commProtocol = DBGetFieldULong(hResult, 0, 3);
235 m_xmlConfig = DBGetField(hResult, 0, 4, NULL, 0);
236 m_serialNumber = DBGetField(hResult, 0, 5, NULL, 0);
237 m_deviceAddress = DBGetField(hResult, 0, 6, NULL, 0);
238 m_metaType = DBGetField(hResult, 0, 7, NULL, 0);
239 m_description = DBGetField(hResult, 0, 8, NULL, 0);
240 m_lastConnectionTime = DBGetFieldULong(hResult, 0, 9);
241 m_frameCount = DBGetFieldULong(hResult, 0, 10);
242 m_signalStrenght = DBGetFieldLong(hResult, 0, 11);
243 m_signalNoise = DBGetFieldLong(hResult, 0, 12);
244 m_frequency = DBGetFieldLong(hResult, 0, 13);
245 m_proxyNodeId = DBGetFieldLong(hResult, 0, 14);
246 m_xmlRegConfig = DBGetField(hResult, 0, 15, NULL, 0);
247 DBFreeResult(hResult);
248
249 // Load DCI and access list
250 loadACLFromDB(hdb);
251 loadItemsFromDB(hdb);
252 for(int i = 0; i < m_dcObjects->size(); i++)
253 if (!m_dcObjects->get(i)->loadThresholdsFromDB(hdb))
254 return false;
255
256 return true;
257 }
258
259 /**
260 * Save to database Sensor class
261 */
262 bool Sensor::saveToDatabase(DB_HANDLE hdb)
263 {
264 lockProperties();
265
266 bool success = saveCommonProperties(hdb);
267
268 if (success && (m_modified & MODIFY_SENSOR_PROPERTIES))
269 {
270 DB_STATEMENT hStmt;
271 bool isNew = !(IsDatabaseRecordExist(hdb, _T("sensors"), _T("id"), m_id));
272 if (isNew)
273 hStmt = DBPrepare(hdb, _T("INSERT INTO sensors (mac_address,device_class,vendor,communication_protocol,xml_config,serial_number,device_address,meta_type,description,last_connection_time,frame_count,signal_strenght,signal_noise,frequency,proxy_node,id,xml_reg_config) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
274 else
275 hStmt = DBPrepare(hdb, _T("UPDATE sensors SET mac_address=?,device_class=?,vendor=?,communication_protocol=?,xml_config=?,serial_number=?,device_address=?,meta_type=?,description=?,last_connection_time=?,frame_count=?,signal_strenght=?,signal_noise=?,frequency=?,proxy_node=? WHERE id=?"));
276 if (hStmt != NULL)
277 {
278 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_macAddress);
279 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)m_deviceClass);
280 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, m_vendor, DB_BIND_STATIC);
281 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (INT32)m_commProtocol);
282 DBBind(hStmt, 5, DB_SQLTYPE_VARCHAR, m_xmlConfig, DB_BIND_STATIC);
283 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, m_serialNumber, DB_BIND_STATIC);
284 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, m_deviceAddress, DB_BIND_STATIC);
285 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, m_metaType, DB_BIND_STATIC);
286 DBBind(hStmt, 9, DB_SQLTYPE_VARCHAR, m_description, DB_BIND_STATIC);
287 DBBind(hStmt, 10, DB_SQLTYPE_INTEGER, (UINT32)m_lastConnectionTime);
288 DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, m_frameCount);
289 DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, m_signalStrenght);
290 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, m_signalNoise);
291 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, m_frequency);
292 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_proxyNodeId);
293 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_id);
294 if (isNew)
295 DBBind(hStmt, 17, DB_SQLTYPE_VARCHAR, m_xmlRegConfig, DB_BIND_STATIC);
296
297 success = DBExecute(hStmt);
298
299 DBFreeStatement(hStmt);
300 }
301 else
302 {
303 success = false;
304 }
305 }
306
307 // Save data collection items
308 if (success && (m_modified & MODIFY_DATA_COLLECTION))
309 {
310 lockDciAccess(false);
311 for(int i = 0; i < m_dcObjects->size(); i++)
312 m_dcObjects->get(i)->saveToDatabase(hdb);
313 unlockDciAccess();
314 }
315
316 // Save access list
317 if (success)
318 saveACLToDB(hdb);
319
320 // Clear modifications flag and unlock object
321 if (success)
322 m_modified = 0;
323 unlockProperties();
324
325 return success;
326 }
327
328 /**
329 * Delete from database
330 */
331 bool Sensor::deleteFromDatabase(DB_HANDLE hdb)
332 {
333 bool success = DataCollectionTarget::deleteFromDatabase(hdb);
334 if (success)
335 success = executeQueryOnObject(hdb, _T("DELETE FROM sensors WHERE id=?"));
336 return success;
337 }
338
339 /**
340 * Create NXSL object for this object
341 */
342 NXSL_Value *Sensor::createNXSLObject()
343 {
344 return new NXSL_Value(new NXSL_Object(&g_nxslSensorClass, this));
345 }
346
347 /**
348 * Sensor class serialization to json
349 */
350 json_t *Sensor::toJson()
351 {
352 json_t *root = DataCollectionTarget::toJson();
353 json_object_set_new(root, "flags", json_integer(m_flags));
354 json_object_set_new(root, "macAddr", json_string_t(m_macAddress.toString(MAC_ADDR_FLAT_STRING)));
355 json_object_set_new(root, "deviceClass", json_integer(m_deviceClass));
356 json_object_set_new(root, "vendor", json_string_t(m_vendor));
357 json_object_set_new(root, "commProtocol", json_integer(m_commProtocol));
358 json_object_set_new(root, "xmlConfig", json_string_t(m_xmlConfig));
359 json_object_set_new(root, "serialNumber", json_string_t(m_serialNumber));
360 json_object_set_new(root, "deviceAddress", json_string_t(m_deviceAddress));
361 json_object_set_new(root, "metaType", json_string_t(m_metaType));
362 json_object_set_new(root, "description", json_string_t(m_description));
363 json_object_set_new(root, "proxyNode", json_integer(m_proxyNodeId));
364 return root;
365 }
366
367 void Sensor::fillMessageInternal(NXCPMessage *msg)
368 {
369 DataCollectionTarget::fillMessageInternal(msg);
370 msg->setField(VID_SENSOR_FLAGS, m_flags);
371 msg->setField(VID_MAC_ADDR, m_macAddress);
372 msg->setField(VID_DEVICE_CLASS, m_deviceClass);
373 msg->setField(VID_VENDOR, CHECK_NULL_EX(m_vendor));
374 msg->setField(VID_COMM_PROTOCOL, m_commProtocol);
375 msg->setField(VID_XML_CONFIG, CHECK_NULL_EX(m_xmlConfig));
376 msg->setField(VID_XML_REG_CONFIG, CHECK_NULL_EX(m_xmlRegConfig));
377 msg->setField(VID_SERIAL_NUMBER, CHECK_NULL_EX(m_serialNumber));
378 msg->setField(VID_DEVICE_ADDRESS, CHECK_NULL_EX(m_deviceAddress));
379 msg->setField(VID_META_TYPE, CHECK_NULL_EX(m_metaType));
380 msg->setField(VID_DESCRIPTION, CHECK_NULL_EX(m_description));
381 msg->setFieldFromTime(VID_LAST_CONN_TIME, m_lastConnectionTime);
382 msg->setField(VID_FRAME_COUNT, m_frameCount);
383 msg->setField(VID_SIGNAL_STRENGHT, m_signalStrenght);
384 msg->setField(VID_SIGNAL_NOISE, m_signalNoise);
385 msg->setField(VID_FREQUENCY, m_frequency);
386 msg->setField(VID_SENSOR_PROXY, m_proxyNodeId);
387 }
388
389 UINT32 Sensor::modifyFromMessageInternal(NXCPMessage *request)
390 {
391 if (request->isFieldExist(VID_FLAGS))
392 m_flags = request->getFieldAsUInt32(VID_FLAGS);
393
394 if (request->isFieldExist(VID_MAC_ADDR))
395 m_macAddress = request->getFieldAsMacAddress(VID_MAC_ADDR);
396 if (request->isFieldExist(VID_VENDOR))
397 {
398 free(m_vendor);
399 m_vendor = request->getFieldAsString(VID_VENDOR);
400 }
401 if (request->isFieldExist(VID_DEVICE_CLASS))
402 m_deviceClass = request->getFieldAsUInt32(VID_DEVICE_CLASS);
403 if (request->isFieldExist(VID_SERIAL_NUMBER))
404 {
405 free(m_serialNumber);
406 m_serialNumber = request->getFieldAsString(VID_SERIAL_NUMBER);
407 }
408 if (request->isFieldExist(VID_DEVICE_ADDRESS))
409 {
410 free(m_deviceAddress);
411 m_deviceAddress = request->getFieldAsString(VID_DEVICE_ADDRESS);
412 }
413 if (request->isFieldExist(VID_META_TYPE))
414 {
415 free(m_metaType);
416 m_metaType = request->getFieldAsString(VID_META_TYPE);
417 }
418 if (request->isFieldExist(VID_DESCRIPTION))
419 {
420 free(m_description);
421 m_description = request->getFieldAsString(VID_DESCRIPTION);
422 }
423 if (request->isFieldExist(VID_SENSOR_PROXY))
424 m_proxyNodeId = request->getFieldAsUInt32(VID_SENSOR_PROXY);
425 if (request->isFieldExist(VID_XML_CONFIG))
426 {
427 free(m_xmlConfig);
428 m_xmlConfig = request->getFieldAsString(VID_XML_CONFIG);
429 }
430
431 return DataCollectionTarget::modifyFromMessageInternal(request);
432 }
433
434 /**
435 * Calculate sensor status
436 */
437 void Sensor::calculateCompoundStatus(BOOL bForcedRecalc)
438 {
439 UINT32 oldStatus = m_status;
440 calculateStatus(bForcedRecalc);
441 lockProperties();
442 if (oldStatus != m_status)
443 setModified(MODIFY_RUNTIME);
444 unlockProperties();
445 }
446
447 void Sensor::calculateStatus(BOOL bForcedRecalc)
448 {
449 AgentConnectionEx *conn = getAgentConnection();
450 if (conn == NULL)
451 {
452 m_status = STATUS_UNKNOWN;
453 return;
454 }
455 DataCollectionTarget::calculateCompoundStatus(bForcedRecalc);
456 lockProperties();
457 int status = 0;
458 if (m_state == 0 || m_state == SSF_PROVISIONED)
459 status = STATUS_UNKNOWN;
460 else if (m_state & SSF_ACTIVE)
461 status = STATUS_NORMAL;
462 else
463 status = STATUS_CRITICAL;
464
465 m_status = m_status != STATUS_UNKNOWN ? std::max(m_status, status) : status;
466 unlockProperties();
467 }
468
469 /**
470 * Get instances for instance discovery DCO
471 */
472 StringMap *Sensor::getInstanceList(DCObject *dco)
473 {
474 if (dco->getInstanceDiscoveryData() == NULL)
475 return NULL;
476
477 DataCollectionTarget *obj;
478 if (dco->getSourceNode() != 0)
479 {
480 obj = (DataCollectionTarget *)FindObjectById(dco->getSourceNode(), OBJECT_NODE);
481 if (obj == NULL)
482 {
483 DbgPrintf(6, _T("Sensor::getInstanceList(%s [%d]): source node [%d] not found"), dco->getName(), dco->getId(), dco->getSourceNode());
484 return NULL;
485 }
486 if (!obj->isTrustedNode(m_id))
487 {
488 DbgPrintf(6, _T("Sensor::getInstanceList(%s [%d]): this node (%s [%d]) is not trusted by source sensor %s [%d]"),
489 dco->getName(), dco->getId(), m_name, m_id, obj->getName(), obj->getId());
490 return NULL;
491 }
492 }
493 else
494 {
495 obj = this;
496 }
497
498 StringList *instances = NULL;
499 StringMap *instanceMap = NULL;
500 switch(dco->getInstanceDiscoveryMethod())
501 {
502 case IDM_AGENT_LIST:
503 if (obj->getObjectClass() == OBJECT_NODE)
504 ((Node *)obj)->getListFromAgent(dco->getInstanceDiscoveryData(), &instances);
505 else if (obj->getObjectClass() == OBJECT_SENSOR)
506 ((Sensor *)obj)->getListFromAgent(dco->getInstanceDiscoveryData(), &instances);
507 break;
508 case IDM_SCRIPT:
509 obj->getStringMapFromScript(dco->getInstanceDiscoveryData(), &instanceMap, this);
510 break;
511 default:
512 instances = NULL;
513 break;
514 }
515 if ((instances == NULL) && (instanceMap == NULL))
516 return NULL;
517
518 if (instanceMap == NULL)
519 {
520 instanceMap = new StringMap;
521 for(int i = 0; i < instances->size(); i++)
522 instanceMap->set(instances->get(i), instances->get(i));
523 }
524 delete instances;
525 return instanceMap;
526 }
527
528 /**
529 * Perform configuration poll on node
530 */
531 void Sensor::configurationPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
532 {
533 if (m_runtimeFlags & DCDF_DELETE_IN_PROGRESS)
534 {
535 if (rqId == 0)
536 m_runtimeFlags &= ~DCDF_QUEUED_FOR_CONFIGURATION_POLL;
537 return;
538 }
539
540 poller->setStatus(_T("wait for lock"));
541 pollerLock();
542
543 if (IsShutdownInProgress())
544 {
545 pollerUnlock();
546 return;
547 }
548
549 m_pollRequestor = session;
550 nxlog_debug(5, _T("Starting configuration poll for sensor %s (ID: %d), m_flags: %d"), m_name, m_id, m_flags);
551
552 bool hasChanges = false;
553
554 if (m_commProtocol == COMM_LORAWAN)
555 {
556 if (!(m_state & SSF_PROVISIONED))
557 {
558 if ((registerLoraDevice(this) != NULL) && (m_state & SSF_PROVISIONED))
559 {
560 nxlog_debug(6, _T("ConfPoll(%s [%d}): sensor successfully registered"), m_name, m_id);
561 hasChanges = true;
562 }
563 }
564 if ((m_state & SSF_PROVISIONED) && (m_deviceAddress == NULL))
565 {
566 getItemFromAgent(_T("LoraWAN.DevAddr(*)"), 0, m_deviceAddress);
567 if (m_deviceAddress != NULL)
568 {
569 nxlog_debug(6, _T("ConfPoll(%s [%d}): sensor DevAddr[%s] successfully obtained"), m_name, m_id, m_deviceAddress);
570 hasChanges = true;
571 }
572 }
573 }
574
575 applyUserTemplates();
576 updateContainerMembership();
577
578 // Execute hook script
579 poller->setStatus(_T("hook"));
580 executeHookScript(_T("ConfigurationPoll"));
581
582 sendPollerMsg(rqId, _T("Finished configuration poll for sensor %s\r\n"), m_name);
583 sendPollerMsg(rqId, _T("Sensor configuration was%schanged after poll\r\n"), hasChanges ? _T(" ") : _T(" not "));
584
585 if (rqId == 0)
586 m_runtimeFlags &= ~DCDF_QUEUED_FOR_CONFIGURATION_POLL;
587 m_lastConfigurationPoll = time(NULL);
588
589 nxlog_debug(5, _T("Finished configuration poll for sensor %s (ID: %d)"), m_name, m_id);
590 pollerUnlock();
591
592 if (hasChanges)
593 {
594 lockProperties();
595 setModified(MODIFY_SENSOR_PROPERTIES);
596 unlockProperties();
597 }
598
599 m_runtimeFlags |= DCDF_CONFIGURATION_POLL_PASSED;
600 }
601
602 /**
603 * Check if DLMS converter used for sensor access is accessible
604 */
605 void Sensor::checkDlmsConverterAccessibility()
606 {
607 //Create conectivity test DCI and try to get it's result
608 }
609
610 /**
611 * Perform status poll on sensor
612 */
613 void Sensor::statusPoll(PollerInfo *poller, ClientSession *session, UINT32 rqId)
614 {
615 if (m_runtimeFlags & DCDF_DELETE_IN_PROGRESS)
616 {
617 if (rqId == 0)
618 m_runtimeFlags &= ~DCDF_QUEUED_FOR_STATUS_POLL;
619 return;
620 }
621
622 Queue *pQueue = new Queue; // Delayed event queue
623
624 poller->setStatus(_T("wait for lock"));
625 pollerLock();
626
627 if (IsShutdownInProgress())
628 {
629 delete pQueue;
630 pollerUnlock();
631 return;
632 }
633
634 m_pollRequestor = session;
635 sendPollerMsg(rqId, _T("Starting status poll for sensor %s\r\n"), m_name);
636 nxlog_debug(5, _T("Starting status poll for sensor %s (ID: %d)"), m_name, m_id);
637
638 UINT32 prevState = m_state;
639
640 nxlog_debug(6, _T("StatusPoll(%s): checking agent"), m_name);
641 poller->setStatus(_T("check agent"));
642 sendPollerMsg(rqId, _T("Checking NetXMS agent connectivity\r\n"));
643
644 AgentConnectionEx *conn = getAgentConnection();
645 lockProperties();
646 if (conn != NULL)
647 {
648 nxlog_debug(6, _T("StatusPoll(%s): connected to agent"), m_name);
649 if (m_state & DCSF_UNREACHABLE)
650 {
651 m_state &= ~DCSF_UNREACHABLE;
652 sendPollerMsg(rqId, POLLER_INFO _T("Connectivity with NetXMS agent restored\r\n"));
653 }
654 }
655 else
656 {
657 nxlog_debug(6, _T("StatusPoll(%s): agent unreachable"), m_name);
658 sendPollerMsg(rqId, POLLER_ERROR _T("NetXMS agent unreachable\r\n"));
659 if (!(m_state & DCSF_UNREACHABLE))
660 m_state |= DCSF_UNREACHABLE;
661 }
662 unlockProperties();
663 nxlog_debug(6, _T("StatusPoll(%s): agent check finished"), m_name);
664
665 switch(m_commProtocol)
666 {
667 case COMM_LORAWAN:
668 if (m_runtimeFlags & SSF_PROVISIONED)
669 {
670 lockProperties();
671 TCHAR lastValue[MAX_DCI_STRING_VALUE] = { 0 };
672 time_t now;
673 getItemFromAgent(_T("LoraWAN.LastContact(*)"), MAX_DCI_STRING_VALUE, lastValue);
674 time_t lastConnectionTime = _tcstol(lastValue, NULL, 0);
675 if (lastConnectionTime != 0)
676 {
677 m_lastConnectionTime = lastConnectionTime;
678 nxlog_debug(6, _T("StatusPoll(%s [%d}): Last connection time updated - %d"), m_name, m_id, m_lastConnectionTime);
679 }
680
681 now = time(NULL);
682
683 if (!(m_state & SSF_REGISTERED))
684 {
685 if (m_lastConnectionTime > 0)
686 {
687 m_state |= SSF_REGISTERED;
688 nxlog_debug(6, _T("StatusPoll(%s [%d}): Status set to REGISTERED"), m_name, m_id);
689 }
690 }
691 if (m_state & SSF_REGISTERED)
692 {
693 if (now - m_lastConnectionTime > 3600) // Last contact > 1h
694 {
695 m_state &= ~SSF_ACTIVE;
696 nxlog_debug(6, _T("StatusPoll(%s [%d}): Inactive for over 1h, status set to INACTIVE"), m_name, m_id);
697 }
698 else
699 {
700 // FIXME: modify runtime if needed
701 m_state |= SSF_ACTIVE;
702 nxlog_debug(6, _T("StatusPoll(%s [%d]): Status set to ACTIVE"), m_name, m_id);
703 getItemFromAgent(_T("LoraWAN.RSSI(*)"), MAX_DCI_STRING_VALUE, lastValue);
704 m_signalStrenght = _tcstol(lastValue, NULL, 10);
705 getItemFromAgent(_T("LoraWAN.SNR(*)"), MAX_DCI_STRING_VALUE, lastValue);
706 m_signalNoise = static_cast<INT32>(_tcstod(lastValue, NULL) * 10);
707 getItemFromAgent(_T("LoraWAN.Frequency(*)"), MAX_DCI_STRING_VALUE, lastValue);
708 m_frequency = static_cast<UINT32>(_tcstod(lastValue, NULL) * 10);
709 }
710 }
711
712 unlockProperties();
713 }
714 break;
715 case COMM_DLMS:
716 checkDlmsConverterAccessibility();
717 break;
718 default:
719 break;
720 }
721 calculateStatus(TRUE);
722
723 // Send delayed events and destroy delayed event queue
724 if (pQueue != NULL)
725 {
726 ResendEvents(pQueue);
727 delete pQueue;
728 }
729 poller->setStatus(_T("hook"));
730 executeHookScript(_T("StatusPoll"));
731
732 if (rqId == 0)
733 m_runtimeFlags &= ~DCDF_QUEUED_FOR_STATUS_POLL;
734
735 if (prevState != m_state)
736 setModified(MODIFY_SENSOR_PROPERTIES);
737
738 sendPollerMsg(rqId, _T("Finished status poll for sensor %s\r\n"), m_name);
739 sendPollerMsg(rqId, _T("Sensor status after poll is %s\r\n"), GetStatusAsText(m_status, true));
740
741 pollerUnlock();
742 m_lastStatusPoll = time(NULL);
743 nxlog_debug(5, _T("Finished status poll for sensor %s (ID: %d)"), m_name, m_id);
744 }
745
746 void Sensor::prepareLoraDciParameters(String &parameter)
747 {
748 int place = parameter.find(_T(")"));
749 if(place > 0)
750 {
751 parameter.replace(_T(")"), m_guid.toString());
752 parameter.append(_T(")"));
753 }
754 else
755 {
756 parameter.append(_T("("));
757 parameter.append(m_guid.toString());
758 parameter.append(_T(")"));
759 }
760 }
761
762 /**
763 * Set all required parameters for DLMS request
764 */
765 void Sensor::prepareDlmsDciParameters(String &parameter)
766 {
767 Config config;
768 #ifdef UNICODE
769 char *xml = UTF8StringFromWideString(m_xmlConfig);
770 config.loadXmlConfigFromMemory(xml, (UINT32)strlen(xml), NULL, "config", false);
771 free(xml);
772 #else
773 config.loadXmlConfigFromMemory(m_xmlConfig, (UINT32)strlen(m_xmlConfig), NULL, "config", false);
774 #endif
775 ConfigEntry *configRoot = config.getEntry(_T("/connections"));
776 if (configRoot != NULL)
777 {
778 int place = parameter.find(_T(")"));
779 if(place > 0)
780 {
781 parameter.replace(_T(")"), _T(""));
782 }
783 else
784 {
785 parameter.append(_T("("));
786 }
787 ObjectArray<ConfigEntry> *credentials = configRoot->getSubEntries(_T("/cred"));
788 for(int i = 0; i < credentials->size(); i++)
789 {
790 ConfigEntry *cred = credentials->get(i);
791 parameter.append(_T(","));
792 parameter.append(cred->getSubEntryValueAsInt(_T("/lineType")));
793 parameter.append(_T(","));
794 parameter.append(cred->getSubEntryValueAsInt(_T("/port")));
795 parameter.append(_T(","));
796 parameter.append(cred->getSubEntryValueAsInt(_T("/password")));
797 parameter.append(_T(","));
798 parameter.append(cred->getSubEntryValue(_T("/inetAddress")));
799 parameter.append(_T(","));
800 parameter.append(cred->getSubEntryValueAsInt(_T("/linkNumber")));
801 parameter.append(_T(","));
802 parameter.append(cred->getSubEntryValueAsInt(_T("/lineNumber")));
803 parameter.append(_T(","));
804 parameter.append(cred->getSubEntryValueAsInt(_T("/linkParams")));
805 }
806 parameter.append(_T(")"));
807 delete credentials;
808 }
809
810 /*
811 config.
812 //set number of configurations
813 //set all parameters
814 <config>
815 <connections class="java.util.ArrayList">
816 <cred>
817 <lineType>32</lineType>
818 <port>3001</port>
819 <password>ABCD</password>
820 <inetAddress>127.0.0.1</inetAddress>
821 <linkNumber>54</linkNumber>
822 <lineNumber>31</lineNumber>
823 <linkParams>1231</linkParams>
824 </cred>
825 </connections>
826 </config>
827 */
828 }
829
830 /**
831 * Get item's value via native agent
832 */
833 UINT32 Sensor::getItemFromAgent(const TCHAR *szParam, UINT32 dwBufSize, TCHAR *szBuffer)
834 {
835 if (m_state & DCSF_UNREACHABLE)
836 return DCE_COMM_ERROR;
837
838 UINT32 dwError = ERR_NOT_CONNECTED, dwResult = DCE_COMM_ERROR;
839 int retry = 3;
840
841 nxlog_debug(7, _T("Sensor(%s)->GetItemFromAgent(%s)"), m_name, szParam);
842 // Establish connection if needed
843 AgentConnectionEx *conn = getAgentConnection();
844 if (conn == NULL)
845 {
846 return dwResult;
847 }
848
849 String parameter(szParam);
850 switch(m_commProtocol)
851 {
852 case COMM_LORAWAN:
853 prepareLoraDciParameters(parameter);
854 break;
855 case COMM_DLMS:
856 if(parameter.find(_T("Sensor")) != -1)
857 prepareDlmsDciParameters(parameter);
858 break;
859 }
860 nxlog_debug(3, _T("Sensor(%s)->GetItemFromAgent(%s)"), m_name, parameter.getBuffer());
861
862 // Get parameter from agent
863 while(retry-- > 0)
864 {
865 dwError = conn->getParameter(parameter, dwBufSize, szBuffer);
866 switch(dwError)
867 {
868 case ERR_SUCCESS:
869 dwResult = DCE_SUCCESS;
870 break;
871 case ERR_UNKNOWN_PARAMETER:
872 dwResult = DCE_NOT_SUPPORTED;
873 break;
874 case ERR_NO_SUCH_INSTANCE:
875 dwResult = DCE_NO_SUCH_INSTANCE;
876 break;
877 case ERR_NOT_CONNECTED:
878 case ERR_CONNECTION_BROKEN:
879 break;
880 case ERR_REQUEST_TIMEOUT:
881 // Reset connection to agent after timeout
882 nxlog_debug(7, _T("Sensor(%s)->GetItemFromAgent(%s): timeout; resetting connection to agent..."), m_name, szParam);
883 if (getAgentConnection() == NULL)
884 break;
885 nxlog_debug(7, _T("Sensor(%s)->GetItemFromAgent(%s): connection to agent restored successfully"), m_name, szParam);
886 break;
887 case ERR_INTERNAL_ERROR:
888 dwResult = DCE_COLLECTION_ERROR;
889 break;
890 }
891 }
892
893 nxlog_debug(7, _T("Sensor(%s)->GetItemFromAgent(%s): dwError=%d dwResult=%d"), m_name, szParam, dwError, dwResult);
894 return dwResult;
895 }
896
897 /**
898 * Get list from agent
899 */
900 UINT32 Sensor::getListFromAgent(const TCHAR *name, StringList **list)
901 {
902 UINT32 dwError = ERR_NOT_CONNECTED, dwResult = DCE_COMM_ERROR;
903 UINT32 i, dwTries = 3;
904
905 *list = NULL;
906
907 if (m_state & DCSF_UNREACHABLE) //removed disable agent usage for all polls
908 return DCE_COMM_ERROR;
909
910 nxlog_debug(7, _T("Sensor(%s)->GetItemFromAgent(%s)"), m_name, name);
911 AgentConnectionEx *conn = getAgentConnection();
912 if (conn == NULL)
913 {
914 return dwResult;
915 }
916
917 String parameter(name);
918 switch(m_commProtocol)
919 {
920 case COMM_LORAWAN:
921 prepareLoraDciParameters(parameter);
922 break;
923 case COMM_DLMS:
924 if(parameter.find(_T("Sensor")) != -1)
925 prepareDlmsDciParameters(parameter);
926 break;
927 }
928 nxlog_debug(3, _T("Sensor(%s)->GetItemFromAgent(%s)"), m_name, parameter.getBuffer());
929
930 // Get parameter from agent
931 while(dwTries-- > 0)
932 {
933 dwError = conn->getList(parameter);
934 switch(dwError)
935 {
936 case ERR_SUCCESS:
937 dwResult = DCE_SUCCESS;
938 *list = new StringList;
939 for(i = 0; i < conn->getNumDataLines(); i++)
940 (*list)->add(conn->getDataLine(i));
941 break;
942 case ERR_UNKNOWN_PARAMETER:
943 dwResult = DCE_NOT_SUPPORTED;
944 break;
945 case ERR_NO_SUCH_INSTANCE:
946 dwResult = DCE_NO_SUCH_INSTANCE;
947 break;
948 case ERR_NOT_CONNECTED:
949 case ERR_CONNECTION_BROKEN:
950 case ERR_REQUEST_TIMEOUT:
951 // Reset connection to agent after timeout
952 DbgPrintf(7, _T("Sensor(%s)->getListFromAgent(%s): timeout; resetting connection to agent..."), m_name, name);
953 if (getAgentConnection() == NULL)
954 break;
955 break;
956 DbgPrintf(7, _T("Sensor(%s)->getListFromAgent(%s): connection to agent restored successfully"), m_name, name);
957 break;
958 case ERR_INTERNAL_ERROR:
959 dwResult = DCE_COLLECTION_ERROR;
960 break;
961 }
962 }
963
964 DbgPrintf(7, _T("Sensor(%s)->getListFromAgent(%s): dwError=%d dwResult=%d"), m_name, name, dwError, dwResult);
965 return dwResult;
966 }
967
968 /**
969 * Prepare sensor object for deletion
970 */
971 void Sensor::prepareForDeletion()
972 {
973 // Prevent sensor from being queued for polling
974 lockProperties();
975 m_runtimeFlags |= DCDF_DELETE_IN_PROGRESS;
976 unlockProperties();
977
978 // Wait for all pending polls
979 nxlog_debug(4, _T("Sensor::PrepareForDeletion(%s [%d]): waiting for outstanding polls to finish"), m_name, m_id);
980 while(1)
981 {
982 lockProperties();
983 if ((m_runtimeFlags &
984 (DCDF_QUEUED_FOR_STATUS_POLL | DCDF_QUEUED_FOR_CONFIGURATION_POLL)) == 0)
985 {
986 unlockProperties();
987 break;
988 }
989 unlockProperties();
990 ThreadSleepMs(100);
991 }
992 nxlog_debug(4, _T("Sensor::PrepareForDeletion(%s [%d]): no outstanding polls left"), m_name, m_id);
993
994 AgentConnectionEx *conn = getAgentConnection();
995 if(m_commProtocol == COMM_LORAWAN && conn != NULL)
996 {
997 NXCPMessage msg(conn->getProtocolVersion());
998 msg.setCode(CMD_UNREGISTER_LORAWAN_SENSOR);
999 msg.setId(conn->generateRequestId());
1000 msg.setField(VID_GUID, m_guid);
1001 NXCPMessage *response = conn->customRequest(&msg);
1002 if (response != NULL)
1003 {
1004 if(response->getFieldAsUInt32(VID_RCC) == RCC_SUCCESS)
1005 nxlog_debug(4, _T("Sensor::PrepareForDeletion(%s [%d]): successfully unregistered from LoRaWAN server"), m_name, m_id);
1006
1007 delete response;
1008 }
1009 }
1010
1011 DataCollectionTarget::prepareForDeletion();
1012 }