"Fossies" - the Fresh Open Source Software Archive

Member "netxms-3.8.166/src/server/core/sensor.cpp" (23 Feb 2021, 36700 Bytes) of package /linux/misc/netxms-3.8.166.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sensor.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.7.145_vs_3.8.120.

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