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