Implemented Sensor class; Added LoRaWAN subagent
authorVictor Kirhenshtein <victor@netxms.org>
Thu, 3 Aug 2017 07:29:50 +0000 (10:29 +0300)
committerEriks Jenkevics <eriks@netxms.org>
Tue, 15 Aug 2017 13:57:38 +0000 (16:57 +0300)
172 files changed:
configure.ac
include/netxmsdb.h
include/nms_agent.h
include/nms_cscp.h
include/nms_util.h
include/nxcldefs.h
include/nxcpapi.h
include/nxdbapi.h
sql/schema.in
src/agent/core/dbupgrade.cpp
src/agent/core/hddinfo.cpp
src/agent/core/localdb.cpp
src/agent/core/localdb.h
src/agent/nxsagent/png.cpp
src/agent/subagents/lorawan/.cproject [new file with mode: 0644]
src/agent/subagents/lorawan/.settings/language.settings.xml [new file with mode: 0644]
src/agent/subagents/lorawan/Makefile.am [new file with mode: 0644]
src/agent/subagents/lorawan/communication_processor.cpp [new file with mode: 0644]
src/agent/subagents/lorawan/lorawan.h [new file with mode: 0644]
src/agent/subagents/lorawan/lorawan_server_link.cpp [new file with mode: 0644]
src/agent/subagents/lorawan/main.cpp [new file with mode: 0644]
src/agent/subagents/lorawan/mqtt_client.cpp [new file with mode: 0644]
src/agent/subagents/nas/.cproject [new file with mode: 0644]
src/agent/subagents/nas/.settings/language.settings.xml [new file with mode: 0644]
src/agent/subagents/nas/.settings/org.eclipse.cdt.codan.core.prefs [new file with mode: 0644]
src/agent/subagents/nas/Makefile.am [new file with mode: 0644]
src/agent/subagents/nas/decoder.cpp [new file with mode: 0644]
src/agent/subagents/nas/main.cpp [new file with mode: 0644]
src/agent/subagents/nas/nas.h [new file with mode: 0644]
src/db/libnxdb/session.cpp
src/java/client/META-INF/MANIFEST.MF
src/java/client/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/java/client/netxms-base/src/main/java/org/netxms/base/NXCPMessage.java
src/java/client/netxms-client/src/main/java/org/netxms/client/MacAddress.java
src/java/client/netxms-client/src/main/java/org/netxms/client/NXCObjectCreationData.java
src/java/client/netxms-client/src/main/java/org/netxms/client/NXCObjectModificationData.java
src/java/client/netxms-client/src/main/java/org/netxms/client/NXCSession.java
src/java/client/netxms-client/src/main/java/org/netxms/client/objects/AbstractObject.java
src/java/client/netxms-client/src/main/java/org/netxms/client/objects/Sensor.java [new file with mode: 0644]
src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanConfig.java [new file with mode: 0644]
src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanRegConfig.java [new file with mode: 0644]
src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorConfig.java [new file with mode: 0644]
src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorRegistrationConfig.java [new file with mode: 0644]
src/java/netxms-eclipse/Core/src/org/netxms/ui/eclipse/console/messages_ar.properties
src/java/netxms-eclipse/Dashboard/plugin.xml
src/java/netxms-eclipse/Dashboard/src/org/netxms/ui/eclipse/dashboard/DashboardsDynamicMenu.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/dialogs/SelectDciDialog.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/views/DataCollectionEditor.java
src/java/netxms-eclipse/NXSL/plugin.xml
src/java/netxms-eclipse/OSM/plugin.xml
src/java/netxms-eclipse/OSM/src/org/netxms/ui/eclipse/osm/GeoLocationCache.java
src/java/netxms-eclipse/ObjectBrowser/icons/sensor.gif [new file with mode: 0644]
src/java/netxms-eclipse/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/api/ObjectAdapter.java
src/java/netxms-eclipse/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/api/ObjectSelectionFilterFactory.java
src/java/netxms-eclipse/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/views/ObjectBrowser.java
src/java/netxms-eclipse/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/views/ObjectFinder.java
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_ar.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_cs.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_de.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_es.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_fr.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_pt.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_ru.properties
src/java/netxms-eclipse/ObjectManager/OSGI-INF/l10n/bundle_zh_CN.properties
src/java/netxms-eclipse/ObjectManager/plugin.xml
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/Messages.java
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/BindObjectTo.java
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/CreateSensor.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_ar.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_cs.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_de.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_es.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_fr.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_pt.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_ru.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_zh_CN.properties
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/propertypages/SensorProperties.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/LoraWanWizard.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/SensorCommon.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/CreateSensorWizard.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorGeneralPage.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorLoraWan.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/Messages.java
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_ar.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_cs.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_de.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_es.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_fr.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_pt.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_ru.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_zh_CN.properties
src/java/netxms-eclipse/ObjectView/src/org/netxms/ui/eclipse/objectview/objecttabs/elements/GeneralInfo.java
src/java/netxms-eclipse/PerfView/plugin.xml
src/java/netxms-eclipse/PerfView/src/org/netxms/ui/eclipse/perfview/objecttabs/PerformanceTab.java
src/java/netxms-eclipse/PerfView/src/org/netxms/ui/eclipse/perfview/views/HistoricalDataView.java
src/java/netxms-eclipse/PerfView/src/org/netxms/ui/eclipse/perfview/views/TableLastValuesView.java
src/libnetxms/Makefile.am
src/libnetxms/inetaddr.cpp
src/libnetxms/macaddr.cpp [new file with mode: 0644]
src/libnetxms/message.cpp
src/libnxmb/.settings/language.settings.xml [new file with mode: 0644]
src/server/core/Makefile.am
src/server/core/condition.cpp
src/server/core/datacoll.cpp
src/server/core/dcst.cpp
src/server/core/hk.cpp
src/server/core/netobj.cpp
src/server/core/objects.cpp
src/server/core/poll.cpp
src/server/core/sensor.cpp [new file with mode: 0644]
src/server/core/session.cpp
src/server/core/template.cpp
src/server/include/nms_objects.h
src/server/nxapisrv/java/src/main/java/org/netxms/websvc/handlers/GrafanaAlarms.java
src/server/tools/nxdbmgr/upgrade.cpp
webui/webapp/Dashboard/plugin.xml
webui/webapp/Dashboard/src/org/netxms/ui/eclipse/dashboard/DashboardsDynamicMenu.java
webui/webapp/DataCollection/src/org/netxms/ui/eclipse/datacollection/dialogs/SelectDciDialog.java
webui/webapp/DataCollection/src/org/netxms/ui/eclipse/datacollection/views/DataCollectionEditor.java
webui/webapp/NXSL/plugin.xml
webui/webapp/OSM/plugin.xml
webui/webapp/OSM/src/org/netxms/ui/eclipse/osm/GeoLocationCache.java
webui/webapp/ObjectBrowser/icons/sensor.gif [new file with mode: 0644]
webui/webapp/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/api/ObjectAdapter.java
webui/webapp/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/api/ObjectSelectionFilterFactory.java
webui/webapp/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/views/ObjectBrowser.java
webui/webapp/ObjectBrowser/src/org/netxms/ui/eclipse/objectbrowser/views/ObjectFinder.java
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_ar.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_cs.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_de.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_es.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_fr.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_pt.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_ru.properties
webui/webapp/ObjectManager/OSGI-INF/l10n/bundle_zh_CN.properties
webui/webapp/ObjectManager/plugin.xml
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/Messages.java
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/BindObjectTo.java
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/CreateSensor.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_ar.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_cs.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_de.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_es.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_fr.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_pt.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_ru.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/messages_zh_CN.properties
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/propertypages/SensorProperties.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/LoraWanWizard.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/SensorCommon.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/CreateSensorWizard.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorGeneralPage.java [new file with mode: 0644]
webui/webapp/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorLoraWan.java [new file with mode: 0644]
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/Messages.java
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_ar.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_cs.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_de.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_es.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_fr.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_pt.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_ru.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/messages_zh_CN.properties
webui/webapp/ObjectView/src/org/netxms/ui/eclipse/objectview/objecttabs/elements/GeneralInfo.java
webui/webapp/PerfView/plugin.xml
webui/webapp/PerfView/src/org/netxms/ui/eclipse/perfview/views/HistoricalDataView.java
webui/webapp/PerfView/src/org/netxms/ui/eclipse/perfview/views/TableLastValuesView.java

index 47650f6..1ce5823 100644 (file)
@@ -695,7 +695,7 @@ AC_ARG_WITH(dist,
 [AS_HELP_STRING(--with-dist,for maintainers only)],
        DB_DRIVERS="mysql mariadb pgsql odbc mssql sqlite oracle db2 informix"
        MODULES="appagent jansson libexpat libstrophe libtre zlib libnetxms libnxjava install sqlite snmp libnxsl libnxmb libnxlp libnxcc db server smsdrv agent client nxscript nxcproxy nxlptest tools"
-       SUBAGENT_DIRS="linux ds18x20 freebsd openbsd minix mqtt mysql netbsd sunos aix ipso hpux odbcquery informix oracle lmsensors darwin rpi java jmx bind9 ubntlw netsvc db2 tuxedo mongodb ssh vmgr xen"
+       SUBAGENT_DIRS="linux ds18x20 freebsd openbsd minix mqtt mysql netbsd sunos aix ipso hpux odbcquery informix oracle lmsensors darwin rpi java jmx bind9 ubntlw netsvc db2 tuxedo mongodb ssh vmgr xen lorawan nas"
        AGENT_DIRS="libnxtux"
        SMSDRV_DIRS="anysms kannel mymobile nexmo nxagent slack smseagle text2reach websms"
    HDLINK_DIRS="jira redmine"
@@ -3590,6 +3590,7 @@ AC_CONFIG_FILES([
        src/agent/subagents/linux/Makefile
        src/agent/subagents/lmsensors/Makefile
        src/agent/subagents/logwatch/Makefile
+       src/agent/subagents/lorawan/Makefile
        src/agent/subagents/minix/Makefile
        src/agent/subagents/mqtt/Makefile
        src/agent/subagents/mysql/Makefile
@@ -3606,6 +3607,7 @@ AC_CONFIG_FILES([
        src/agent/subagents/sunos/Makefile
        src/agent/subagents/tuxedo/Makefile
        src/agent/subagents/mongodb/Makefile
+       src/agent/subagents/nas/Makefile
        src/agent/subagents/ubntlw/Makefile
        src/agent/subagents/ups/Makefile
        src/agent/subagents/vmgr/Makefile
index 259b07d..2fe7b15 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   457
+#define DB_FORMAT_VERSION   458
 
 #endif
index c210cd7..e343c76 100644 (file)
 #define ERR_OUT_OF_STATE_REQUEST    ((UINT32)922)
 #define ERR_ENCRYPTION_ERROR        ((UINT32)923)
 #define ERR_MALFORMED_RESPONSE      ((UINT32)924)
+#define ERR_INVALID_OBJECT          ((UINT32)925)
 
 /**
  * Bulk data reconciliation DCI processing status codes
@@ -805,4 +806,68 @@ bool LIBNXAGENT_EXPORTABLE WriteRegistry(const TCHAR *attr, INT32 value);
 bool LIBNXAGENT_EXPORTABLE WriteRegistry(const TCHAR *attr, INT64 value);
 bool LIBNXAGENT_EXPORTABLE DeleteRegistryEntry(const TCHAR *attr);
 
+/**
+ * LoraWAN device payload
+ */
+typedef BYTE lorawan_payload_t[36];
+
+class LIBNXAGENT_EXPORTABLE LoraDeviceData
+{
+private:
+   uuid m_guid;
+   MacAddress m_devAddr;
+   MacAddress m_devEui;
+   lorawan_payload_t m_payload;
+   INT32 m_decoder;
+   char m_dataRate[24];
+   INT32 m_rssi;
+   double m_snr;
+   double m_freq;
+   UINT32 m_fcnt;
+   UINT32 m_port;
+   time_t m_lastContact;
+
+public:
+   LoraDeviceData(NXCPMessage *request);
+   LoraDeviceData(DB_RESULT result, int row);
+
+   UINT32 saveDeviceData();
+   UINT32 updateDeviceData();
+   UINT32 deleteDeviceData();
+
+   bool isOtaa() { return (m_devEui.length() > 0) ? true : false; }
+
+   uuid getGuid() { return m_guid; }
+
+   MacAddress getDevAddr() { return m_devAddr; }
+   void setDevAddr(MacAddress devAddr) { m_devAddr = devAddr; updateDeviceData(); }
+   MacAddress getDevEui() { return m_devEui; }
+
+   const BYTE *getPayload() { return m_payload; }
+   void setPayload(const char *payload) {  StrToBinA(payload, m_payload, 36); }
+
+   UINT32 getDecoder() { return m_decoder; }
+
+   const char *getDataRate() { return m_dataRate; }
+   void setDataRate(const char *dataRate) { strncpy(m_dataRate, dataRate, 24); }
+
+   INT32 getRssi() { return m_rssi; }
+   void setRssi(INT32 rssi) { m_rssi = rssi; }
+
+   double getSnr() { return m_snr; }
+   void setSnr(double snr) { m_snr = snr; }
+
+   double getFreq() { return m_freq; }
+   void setFreq(double freq) { m_freq = freq; }
+
+   UINT32 getFcnt() { return m_fcnt; }
+   void setFcnt(UINT32 fcnt) { m_fcnt = fcnt; }
+
+   UINT32 getPort() { return m_port; }
+   void setPort(UINT32 port) { m_port = port; }
+
+   const INT32 getLastContact() { return (INT32)m_lastContact; }
+   void updateLastContact() { m_lastContact = time(NULL); }
+};
+
 #endif   /* _nms_agent_h_ */
index 4e8dd6f..e4bcc1a 100644 (file)
@@ -587,6 +587,8 @@ typedef struct
 #define CMD_CREATE_OBJECT_ACCESS_SNAPSHOT 0x0160
 #define CMD_UNBIND_AGENT_TUNNEL           0x0161
 #define CMD_RESTART                       0x0162
+#define CMD_REGISTER_LORAWAN_SENSOR       0x0163
+#define CMD_UNREGISTER_LORAWAN_SENSOR     0x0164
 
 #define CMD_RS_LIST_REPORTS            0x1100
 #define CMD_RS_GET_REPORT              0x1101
@@ -1182,6 +1184,25 @@ typedef struct
 #define VID_ORGANIZATION            ((UINT32)578)
 #define VID_TUNNEL_ID               ((UINT32)579)
 #define VID_PARENT_INTERFACE        ((UINT32)580)
+#define VID_SENSOR_FLAGS            ((UINT32)581)
+#define VID_DEVICE_CLASS            ((UINT32)582)
+#define VID_COMM_PROTOCOL           ((UINT32)583)
+#define VID_XML_CONFIG              ((UINT32)584)
+#define VID_DEVICE_ADDRESS          ((UINT32)585)
+#define VID_META_TYPE               ((UINT32)586)
+#define VID_LAST_CONN_TIME          ((UINT32)587)
+#define VID_FRAME_COUNT             ((UINT32)588)
+#define VID_SIGNAL_STRENGHT         ((UINT32)589)
+#define VID_SIGNAL_NOISE            ((UINT32)590)
+#define VID_FREQUENCY               ((UINT32)591)
+#define VID_SENSOR_PROXY            ((UINT32)592)
+#define VID_XML_REG_CONFIG          ((UINT32)593)
+#define VID_REG_TYPE                ((UINT32)594)
+#define VID_DECODER                 ((UINT32)595)
+#define VID_LORA_APP_EUI            ((UINT32)596)
+#define VID_LORA_APP_KEY            ((UINT32)597)
+#define VID_LORA_APP_S_KEY          ((UINT32)598)
+#define VID_LORA_NWK_S_KWY          ((UINT32)599)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
index e493246..749d645 100644 (file)
@@ -1546,6 +1546,46 @@ inline bool SocketAddressEquals(struct sockaddr *a1, struct sockaddr *a2)
    return false;
 }
 
+enum MacAddressNotation
+{
+   MAC_ADDR_FLAT_STRING = 0,
+   MAC_ADDR_COLON_SEPARATED = 1,
+   MAC_ADDR_BYTEPAIR_COLON_SEPARATED = 2,
+   MAC_ADDR_HYPHEN_SEPARATED = 3,
+   MAC_ADDR_DOT_SEPARATED = 4,
+   MAC_ADDR_BYTEPAIR_DOT_SEPARATED = 5
+};
+
+/**
+ * MAC address
+ */
+class LIBNETXMS_EXPORTABLE MacAddress
+{
+private:
+   BYTE m_value[16];
+   size_t m_length;
+
+   TCHAR *toStringInternal(TCHAR *buffer, const TCHAR separator, bool bytePair = false) const;
+
+public:
+   MacAddress() { m_length = 0; memset(m_value, 0, 16); }
+   MacAddress(const BYTE *value, size_t length) { m_length = min(length, 16); memcpy(m_value, value, m_length); }
+   MacAddress(const MacAddress& src) { memcpy(m_value, src.m_value, src.m_length); m_length = src.m_length; }
+
+   static MacAddress parse(const char *str);
+   static MacAddress parse(const TCHAR *str);
+
+   const BYTE *value() const { return m_value; }
+   size_t length() const { return m_length; }
+
+   bool isMulticast() const;
+   bool isBroadcast() const;
+   bool equals(const MacAddress &a) const;
+
+   TCHAR *toString(TCHAR *buffer, MacAddressNotation notation = MAC_ADDR_COLON_SEPARATED) const;
+   String toString(MacAddressNotation notation = MAC_ADDR_COLON_SEPARATED) const;
+};
+
 /**
  * IP address
  */
index e2bdc91..eb56bd8 100644 (file)
@@ -55,6 +55,7 @@
 #define CURRENT_USER             ((UINT32)0xFFFFFFFF)
 #define MAX_DCI_DATA_RECORDS     200000
 #define MAX_POLICY_CONFIG_NAME   64
+#define MAX_INT32                0x7FFFFFFF
 
 /**
  * NetXMS agent authentication methods
 #define OBJECT_AGENTPOLICY_LOGPARSER 34
 #define OBJECT_CHASSIS               35
 #define OBJECT_DASHBOARDGROUP        36
+#define OBJECT_SENSOR                37
 
 /** Base value for custom object classes */
 #define OBJECT_CUSTOM                10000
index 6db943d..061b3f3 100644 (file)
@@ -114,6 +114,7 @@ public:
    void setField(UINT32 fieldId, const BYTE *value, size_t size) { set(fieldId, NXCP_DT_BINARY, value, false, size); }
    void setField(UINT32 fieldId, const InetAddress& value) { set(fieldId, NXCP_DT_INETADDR, &value); }
    void setField(UINT32 fieldId, const uuid& value) { set(fieldId, NXCP_DT_BINARY, value.getValue(), false, UUID_LENGTH); }
+   void setField(UINT32 fieldId, const MacAddress& value) { set(fieldId, NXCP_DT_BINARY, value.value(), false, value.length()); }
 #ifdef UNICODE
    void setFieldFromMBString(UINT32 fieldId, const char *value);
 #else
@@ -141,6 +142,7 @@ public:
        char *getFieldAsUtf8String(UINT32 fieldId, char *buffer = NULL, size_t bufferSize = 0) const;
    UINT32 getFieldAsBinary(UINT32 fieldId, BYTE *buffer, size_t bufferSize) const;
    InetAddress getFieldAsInetAddress(UINT32 fieldId) const;
+   MacAddress getFieldAsMacAddress(UINT32 fieldId) const;
    uuid getFieldAsGUID(UINT32 fieldId) const;
 
    void deleteAllFields();
index 1bbf28e..610d318 100644 (file)
@@ -143,6 +143,7 @@ void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, INT64 v
 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, UINT64 value);
 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, double value);
 void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const uuid& value);
+void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const MacAddress& value);
 bool LIBNXDB_EXPORTABLE DBExecute(DB_STATEMENT hStmt);
 bool LIBNXDB_EXPORTABLE DBExecuteEx(DB_STATEMENT hStmt, TCHAR *errorText);
 DB_RESULT LIBNXDB_EXPORTABLE DBSelectPrepared(DB_STATEMENT hStmt);
@@ -171,6 +172,7 @@ UINT64 LIBNXDB_EXPORTABLE DBGetFieldUInt64(DB_RESULT hResult, int iRow, int iCol
 double LIBNXDB_EXPORTABLE DBGetFieldDouble(DB_RESULT hResult, int iRow, int iColumn);
 UINT32 LIBNXDB_EXPORTABLE DBGetFieldIPAddr(DB_RESULT hResult, int iRow, int iColumn);
 InetAddress LIBNXDB_EXPORTABLE DBGetFieldInetAddr(DB_RESULT hResult, int iRow, int iColumn);
+MacAddress LIBNXDB_EXPORTABLE DBGetFieldMacAddr(DB_RESULT hResult, int iRow, int iColumn);
 bool LIBNXDB_EXPORTABLE DBGetFieldByteArray(DB_RESULT hResult, int iRow, int iColumn,
                                             int *pnArray, int nSize, int nDefault);
 bool LIBNXDB_EXPORTABLE DBGetFieldByteArray2(DB_RESULT hResult, int iRow, int iColumn,
index a65e150..2a45ef5 100644 (file)
@@ -1723,3 +1723,30 @@ CREATE TABLE object_access_snapshot
    access_rights integer not null,
    PRIMARY KEY(user_id,object_id)
 ) TABLE_TYPE;
+
+/**
+ * Sensor specific information table
+ */
+CREATE TABLE sensors
+(
+   id integer not null,
+   proxy_node integer not null,
+   flags integer not null,
+   mac_address varchar(16) null,
+   device_class integer not null,
+   vendor varchar(128) null,
+   communication_protocol integer not null,
+   xml_reg_config varchar(4000) null,
+   xml_config varchar(4000) null,
+   serial_number varchar(256) null,
+   device_address varchar(256) null,
+   meta_type varchar(256) null,
+   description varchar(512) null,
+   last_connection_time integer not null,
+   frame_count integer not null,
+   signal_strenght integer not null,
+   signal_noise integer not null,
+   frequency integer not null,
+   runtime_flags integer null,
+   PRIMARY KEY(id)
+) TABLE_TYPE;
index 8730730..99ad757 100644 (file)
@@ -38,6 +38,24 @@ bool g_ignoreAgentDbErrors = FALSE;
 static DB_HANDLE s_db = NULL;
 
 /**
+ * Upgrade from V5 to V6
+ */
+static BOOL H_UpgradeFromV5(int currVersion, int newVersion)
+{
+   TCHAR upgradeQueries[] =
+            _T("CREATE TABLE device_decoder_map (")
+            _T("  guid varchar(36) not null,")
+            _T("  devAddr varchar(10) null,")
+            _T("  devEui varchar(10) null,")
+            _T("  decoder integer not null,")
+            _T("  last_contact integer null,")
+            _T("  PRIMARY KEY(guid))");
+   CHK_EXEC(Query(upgradeQueries));
+   CHK_EXEC(WriteMetadata(_T("SchemaVersion"), 6));
+   return TRUE;
+}
+
+/**
  * Upgrade from V4 to V5
  */
 static BOOL H_UpgradeFromV4(int currVersion, int newVersion)
@@ -291,6 +309,7 @@ static struct
    { 2, 3, H_UpgradeFromV2 },
    { 3, 4, H_UpgradeFromV3 },
    { 4, 5, H_UpgradeFromV4 },
+   { 5, 6, H_UpgradeFromV5 },
    { 0, 0, NULL }
 };
 
index 170fa8b..e23902a 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS multiplatform core agent
 ** Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Victor Kirhenshtein
 **
@@ -37,7 +37,7 @@
 
 //
 // Get value of specific attribute from SMART_ATA_VALUES structure
-// 
+//
 
 static BOOL GetAttributeValue(ATA_SMART_VALUES *pSmartValues, BYTE bAttr,
                               TCHAR *pValue, int nType)
@@ -72,13 +72,13 @@ LONG H_PhysicalDiskInfo(const TCHAR *pszParam, const TCHAR *pszArg, TCHAR *pValu
 {
    LONG nRet = SYSINFO_RC_ERROR, nDisk, nCmd;
    TCHAR szBuffer[128], *eptr;                     //
-   BYTE pbValue[40];                               // 
+   BYTE pbValue[40];                               //
    HANDLE hDevice;
    SENDCMDINPARAMS rq;
    SENDCMDOUTPARAMS *pResult;
    DWORD dwBytes;
    BOOL bSwapWords = FALSE;
-   //memset(pbValue, 0, sizeof(BYTE) * 40);
+   //memset(pbValue, 0, 40);
    if (!AgentGetParameterArg(pszParam, 1, szBuffer, 128))
       return SYSINFO_RC_UNSUPPORTED;
 
@@ -89,7 +89,7 @@ LONG H_PhysicalDiskInfo(const TCHAR *pszParam, const TCHAR *pszArg, TCHAR *pValu
 
    // Open device
    _sntprintf(szBuffer, 128, _T("\\\\.\\PHYSICALDRIVE%d"), nDisk);      //
-   hDevice = CreateFile(szBuffer, GENERIC_READ | GENERIC_WRITE, 
+   hDevice = CreateFile(szBuffer, GENERIC_READ | GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (hDevice != INVALID_HANDLE_VALUE)
    {
@@ -209,7 +209,7 @@ LONG H_PhysicalDiskInfo(const TCHAR *pszParam, const TCHAR *pszArg, TCHAR *pValu
                           memcpy(pbValue, ((ATA_IDENTIFY_DEVICE_DATA *)pResult->bBuffer)->serial_no, 20); // has bug
                           pbValue[21] = 0;
                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)pbValue, -1, pValue, 21);
-               StrStrip(pValue); 
+               StrStrip(pValue);
 #else
                           memcpy(pValue, ((ATA_IDENTIFY_DEVICE_DATA *)pResult->bBuffer)->serial_no, 20); //
                pValue[21] = 0;
@@ -222,7 +222,7 @@ LONG H_PhysicalDiskInfo(const TCHAR *pszParam, const TCHAR *pszArg, TCHAR *pValu
                                memcpy(pbValue, ((ATA_IDENTIFY_DEVICE_DATA *)pResult->bBuffer)->fw_rev, 8); // has bug
                           pbValue[8] = 0;
                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)pbValue, -1, pValue, 8);
-               StrStrip(pValue); 
+               StrStrip(pValue);
 #else
                                memcpy(pValue, ((ATA_IDENTIFY_DEVICE_DATA *)pResult->bBuffer)->fw_rev, 8);  //
                pValue[8] = 0;
index 2802881..df8ace6 100644 (file)
@@ -99,6 +99,14 @@ static const TCHAR *s_dbInitQueries[] =
    _T("  version integer not null,")
    _T("  PRIMARY KEY(guid))"),
 
+   _T("CREATE TABLE device_decoder_map (")
+   _T("  guid varchar(36) not null,")
+   _T("  devAddr varchar(10) null,")
+   _T("  devEui varchar(10) null,")
+   _T("  decoder integer not null,")
+   _T("  last_contact integer null,")
+   _T("  PRIMARY KEY(guid))"),
+
    _T("CREATE TABLE dc_config (")
    _T("  server_id number(20) not null,")
    _T("  dci_id integer not null,")
index 45c18e2..c3e5be8 100644 (file)
@@ -27,7 +27,7 @@
 /**
  * Database schema version
  */
-#define DB_SCHEMA_VERSION     5
+#define DB_SCHEMA_VERSION     6
 
 bool OpenLocalDatabase();
 void CloseLocalDatabase();
index 21f20db..59cd152 100644 (file)
@@ -30,7 +30,7 @@ bool SaveBitmapToPng(HBITMAP hBitmap, const TCHAR *fileName)
 {
    BITMAP bitmap;
    GetObject(hBitmap, sizeof(bitmap), (LPSTR)&bitmap);
-   
+
    DWORD scanlineSize = ((bitmap.bmWidth * 4) + (4 - 1)) & ~(4 - 1);
    DWORD bufferSize = scanlineSize * bitmap.bmHeight;
    BYTE *buffer = (BYTE *)malloc(bufferSize);
@@ -59,7 +59,7 @@ bool SaveBitmapToPng(HBITMAP hBitmap, const TCHAR *fileName)
    const int width = bitmap.bmWidth;
    const int height = bitmap.bmHeight;
    const int depth = 8;
-   const int bytesPerPixel = sizeof(BYTE) * 4;
+   const int bytesPerPixel = 4;
 
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
@@ -95,7 +95,7 @@ bool SaveBitmapToPng(HBITMAP hBitmap, const TCHAR *fileName)
    {
       png_byte *row = (png_byte *)&buffer[y * bitmap.bmWidth * 4];
       row_pointers[height - y - 1] = row;
-      
+
       // Convert RGBA to BGR
       for(int i = 0, j = 0; i < width * 4; i++)
       {
diff --git a/src/agent/subagents/lorawan/.cproject b/src/agent/subagents/lorawan/.cproject
new file mode 100644 (file)
index 0000000..685c8f6
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+       <storageModule moduleId="org.eclipse.cdt.core.settings">
+               <cconfiguration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806">
+                       <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806" moduleId="org.eclipse.cdt.core.settings" name="Build (GNU)">
+                               <externalSettings/>
+                               <extensions>
+                                       <extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                               </extensions>
+                       </storageModule>
+                       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                               <configuration buildProperties="" description="" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806" name="Build (GNU)" parent="org.eclipse.cdt.build.core.emptycfg">
+                                       <folderInfo id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806.743147890" name="/" resourcePath="">
+                                               <toolChain id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.303519718" name="GNU Autotools Toolchain" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolChain">
+                                                       <targetPlatform id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.93119795" isAbstract="false" name="GNU Autotools Target Platform" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform"/>
+                                                       <builder id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.1531050121" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Autotools Makefile Generator" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder"/>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.161990452" name="configure" superClass="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure">
+                                                               <option id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name.1895764635" name="Name" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name" value="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806" valueType="string"/>
+                                                       </tool>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.759678249" name="autogen.sh" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen"/>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.2025150998" name="GCC C Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc">
+                                                               <option id="gnu.c.compiler.option.include.paths.1163866843" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/src/agent&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.231945917" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1292841045" name="GCC C++ Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp">
+                                                               <option id="gnu.cpp.compiler.option.include.paths.1378673278" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/src/agent&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.522096378" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                                       </tool>
+                                               </toolChain>
+                                       </folderInfo>
+                               </configuration>
+                       </storageModule>
+                       <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+               </cconfiguration>
+       </storageModule>
+       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+               <project id="lorawanSub.null.1960622763" name="lorawanSub"/>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+       <storageModule moduleId="refreshScope"/>
+       <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+       <storageModule moduleId="scannerConfiguration">
+               <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806;org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806.743147890;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.2025150998;cdt.managedbuild.tool.gnu.c.compiler.input.231945917">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806;org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806.743147890;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1292841045;cdt.managedbuild.tool.gnu.cpp.compiler.input.522096378">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+       </storageModule>
+</cproject>
diff --git a/src/agent/subagents/lorawan/.settings/language.settings.xml b/src/agent/subagents/lorawan/.settings/language.settings.xml
new file mode 100644 (file)
index 0000000..d988fe4
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+       <configuration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.819950806" name="Build (GNU)">
+               <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+                       <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+                       <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+                       <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.autotools.core.LibtoolGCCBuildCommandParser" keep-relative-paths="false" name="CDT Libtool GCC Build Output Parser" parameter="(libtool:\s+compile:\s+)?((g?cc)|([gc]\+\+)|(clang))" prefer-non-shared="true"/>
+                       <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="1424788779437872117" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+                               <language-scope id="org.eclipse.cdt.core.gcc"/>
+                               <language-scope id="org.eclipse.cdt.core.g++"/>
+                       </provider>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+               </extension>
+       </configuration>
+</project>
diff --git a/src/agent/subagents/lorawan/Makefile.am b/src/agent/subagents/lorawan/Makefile.am
new file mode 100644 (file)
index 0000000..f2a6214
--- /dev/null
@@ -0,0 +1,17 @@
+SUBAGENT = lorawan
+
+pkglib_LTLIBRARIES = lorawan.la
+lorawan_la_SOURCES = main.cpp mqtt_client.cpp lorawan_server_link.cpp communication_processor.cpp
+lorawan_la_CPPFLAGS=-I@top_srcdir@/include @MQTT_CPPFLAGS@
+lorawan_la_LDFLAGS = -module -avoid-version -export-symbols ../subagent.sym @MQTT_LDFLAGS@
+lorawan_la_LIBADD = ../../libnxagent/libnxagent.la ../../../libnetxms/libnetxms.la ../../../libnxmb/libnxmb.la -lcurl @MQTT_LIBS@
+
+EXTRA_DIST = lorawan.h
+
+if !STATIC_BUILD
+install-exec-hook:
+       mv -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ $(DESTDIR)$(pkglibdir)/$(SUBAGENT).nsm
+       rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).la
+       rm -f $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+       ln -s netxms/$(SUBAGENT).nsm $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+endif
\ No newline at end of file
diff --git a/src/agent/subagents/lorawan/communication_processor.cpp b/src/agent/subagents/lorawan/communication_processor.cpp
new file mode 100644 (file)
index 0000000..2dbe590
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ ** LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "lorawan.h"
+
+/**
+ * Create new Lora Device Data object from NXCPMessage
+ */
+LoraDeviceData::LoraDeviceData(NXCPMessage *request)
+{
+   m_guid = request->getFieldAsGUID(VID_GUID);
+   if (request->getFieldAsUInt32(VID_REG_TYPE) == 0) // OTAA
+      m_devEui = request->getFieldAsMacAddress(VID_MAC_ADDR);
+   else
+   {
+      char devAddr[12];
+      request->getFieldAsMBString(VID_DEVICE_ADDRESS, devAddr, 12);
+      m_devAddr = MacAddress::parse(devAddr);
+   }
+
+   memset(m_payload, 0, 36);
+   m_decoder = request->getFieldAsInt32(VID_DECODER);
+   m_dataRate[0] = 0;
+   m_rssi = 1;
+   m_snr = -100;
+   m_freq = 0;
+   m_fcnt = 0;
+   m_port = 0;
+   m_lastContact = 0;
+}
+
+/**
+ * Create Lora Device Data object from DB record
+ */
+LoraDeviceData::LoraDeviceData(DB_RESULT result, int row)
+{
+   m_guid = DBGetFieldGUID(result, row, 0);
+   m_devAddr = DBGetFieldMacAddr(result, row, 1);
+   m_devEui = DBGetFieldMacAddr(result, row, 2);
+   m_decoder = DBGetFieldULong(result, row, 3);
+   m_lastContact = DBGetFieldULong(result, row, 4);
+
+   memset(m_payload, 0, 36);
+   m_dataRate[0] = 0;
+   m_rssi = 0;
+   m_snr = -100;
+   m_freq = 0;
+   m_fcnt = 0;
+   m_port = 0;
+}
+
+/**
+ * Update Lora device data object in DB
+ */
+UINT32 LoraDeviceData::updateDeviceData()
+{
+   UINT32 rcc = ERR_IO_FAILURE;
+
+      DB_HANDLE hdb = AgentGetLocalDatabaseHandle();
+      DB_STATEMENT hStmt;
+      if (FindDevice(m_guid) == NULL)
+         hStmt = DBPrepare(hdb, _T("INSERT INTO device_decoder_map(devAddr,devEui,decoder,last_contact,guid) VALUES (?,?,?,?,?)"));
+      else
+         hStmt = DBPrepare(hdb, _T("UPDATE device_decoder_map SET devAddr=?,devEui=?,decoder=?,last_contact=? WHERE guid=?"));
+
+      if (hStmt != NULL)
+      {
+         DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_devAddr.length() > 0 ? (const TCHAR*)m_devAddr.toString(MAC_ADDR_FLAT_STRING) : _T(""), DB_BIND_STATIC);
+         DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, m_devEui.length() > 0 ? (const TCHAR*)m_devEui.toString(MAC_ADDR_FLAT_STRING) : _T(""), DB_BIND_STATIC);
+         DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, m_decoder);
+         DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (UINT32)m_lastContact);
+         DBBind(hStmt, 5, DB_SQLTYPE_VARCHAR, m_guid);
+         if (DBExecute(hStmt))
+            rcc = ERR_SUCCESS;
+         else
+            rcc = ERR_EXEC_FAILED;
+
+         DBFreeStatement(hStmt);
+      }
+      DBConnectionPoolReleaseConnection(hdb);
+
+      return rcc;
+}
+
+/**
+ * Save new device in the database and local map
+ */
+UINT32 LoraDeviceData::saveDeviceData()
+{
+   UINT32 rcc = updateDeviceData();
+
+   if (rcc == ERR_SUCCESS)
+   {
+      MutexLock(g_deviceMapMutex);
+      g_deviceMap.set(m_guid, this);
+      MutexUnlock(g_deviceMapMutex);
+   }
+
+   return rcc;
+}
+
+/**
+ * Remove device from local map and DB
+ */
+UINT32 LoraDeviceData::deleteDeviceData()
+{
+   UINT32 rcc = ERR_IO_FAILURE;
+
+   DB_HANDLE hdb = AgentGetLocalDatabaseHandle();
+   DB_STATEMENT hStmt = DBPrepare(hdb, _T("DELETE FROM device_decoder_map WHERE guid=?"));
+
+   if (hStmt != NULL)
+   {
+      DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_guid);
+      if (DBExecute(hStmt))
+      {
+         MutexLock(g_deviceMapMutex);
+         g_deviceMap.remove(m_guid);
+         MutexUnlock(g_deviceMapMutex);
+
+         rcc = ERR_SUCCESS;
+      }
+      else
+         rcc = ERR_EXEC_FAILED;
+
+      DBFreeStatement(hStmt);
+   }
+   DBConnectionPoolReleaseConnection(hdb);
+
+   return rcc;
+}
+
+/**
+ * MQTT Mesage handler
+ */
+void MqttMessageHandler(const char *payload, char *topic)
+{
+   json_error_t error;
+   json_t *root = json_loads(payload, 0, &error);
+
+   json_t *tmp = json_object_get(root, "appargs");
+   if (json_is_string(tmp))
+   {
+      TCHAR buffer[64];
+#ifdef UNICODE
+      MultiByteToWideChar(CP_UTF8, 0, json_string_value(tmp), -1, buffer, 64);
+#else
+      nx_strncpy(buffer, json_string_value(tmp), 64);
+#endif
+      LoraDeviceData *data = FindDevice(uuid::parse(buffer));
+
+      if (data != NULL)
+      {
+         MacAddress rxDevAddr;
+         MacAddress rxDevEui;
+
+         tmp = json_object_get(root, "deveui");
+         if (json_is_string(tmp))
+            rxDevEui = MacAddress::parse(json_string_value(tmp));
+         tmp = json_object_get(root, "devaddr");
+         if (json_is_string(tmp))
+            rxDevAddr = MacAddress::parse(json_string_value(tmp));
+
+         if ((data->getDevAddr().length() != 0 && data->getDevAddr().equals(rxDevAddr))
+             || (data->getDevEui().length() != 0 && data->getDevEui().equals(rxDevEui)))
+         {
+            if (data->getDevAddr().length() == 0 && rxDevAddr.length() != 0)
+               data->setDevAddr(rxDevAddr);
+
+            tmp = json_object_get(root, "data");
+            if (json_is_string(tmp))
+               data->setPayload(json_string_value(tmp));
+
+            tmp = json_object_get(root, "fcnt");
+            if (json_is_integer(tmp))
+               data->setFcnt(json_integer_value(tmp));
+
+            tmp = json_object_get(root, "port");
+            if (json_is_integer(tmp))
+               data->setPort(json_integer_value(tmp));
+
+            tmp = json_object_get(root, "rxq");
+            if (json_is_object(tmp))
+            {
+               json_t *tmp2 = json_object_get(tmp, "datr");
+               if (json_is_string(tmp2))
+                  data->setDataRate(json_string_value(tmp2));
+
+               tmp2 = json_object_get(tmp, "freq");
+               if (json_is_real(tmp2))
+                  data->setFreq(json_real_value(tmp2));
+
+               tmp2 = json_object_get(tmp, "lsnr");
+               if (json_is_real(tmp2))
+                  data->setSnr(json_real_value(tmp2));
+
+               tmp2 = json_object_get(tmp, "rssi");
+               if (json_is_integer(tmp2))
+                  data->setRssi(json_integer_value(tmp2));
+
+               free(tmp2);
+            }
+            else
+               nxlog_debug(6, _T("LoraWAN Module:[MqttMessageHandler] No RX quality data received..."));
+
+            NXMBDispatcher *dispatcher = NXMBDispatcher::getInstance();
+            if (!dispatcher->call(_T("NOTIFY_DECODERS"), data, NULL))
+               nxlog_debug(6, _T("LoraWAN Module:[MqttMessageHandler] Call to NXMBDispacher failed..."));
+
+            data->updateLastContact();
+         }
+         else
+            nxlog_debug(6, _T("LoraWAN Module:[MqttMessageHandler] Neither the devAddr nor the devEUI returned a match..."));
+      }
+   }
+   else
+      nxlog_debug(6, _T("LoraWAN Module:[MqttMessageHandler] No GUID found due to missing \"appargs\" field in received JSON"));
+
+   free(root);
+   free(tmp);
+}
+
+/**
+ * Handler for communication parameters
+ */
+LONG H_Communication(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   TCHAR guid[38];
+   if (!AgentGetParameterArg(param, 1, guid, 38))
+      return SYSINFO_RC_ERROR;
+
+   LoraDeviceData *data = FindDevice(uuid::parse(guid));
+   if (data == NULL)
+      return SYSINFO_RC_ERROR;
+
+   switch(*arg)
+   {
+      case 'A':
+         ret_string(value, data->getDevAddr().toString(MAC_ADDR_FLAT_STRING));
+         break;
+      case 'C':
+         ret_int(value, data->getLastContact());
+         break;
+      case 'D':
+         ret_mbstring(value, data->getDataRate());
+         break;
+      case 'F':
+         ret_double(value, data->getFreq(), 1);
+         break;
+      case 'M':
+         ret_uint(value, data->getFcnt());
+         break;
+      case 'R':
+         ret_int(value, data->getRssi());
+         break;
+      case 'S':
+         ret_double(value, data->getSnr(), 1);
+         break;
+      default:
+         return SYSINFO_RC_UNSUPPORTED;
+   }
+
+   return SYSINFO_RC_SUCCESS;
+}
diff --git a/src/agent/subagents/lorawan/lorawan.h b/src/agent/subagents/lorawan/lorawan.h
new file mode 100644 (file)
index 0000000..7d95544
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ ** LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include <nms_agent.h>
+#include <nms_util.h>
+#include <mosquitto.h>
+#include <curl/curl.h>
+#include <nxmbapi.h>
+
+#ifdef _WIN32
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif   /* _WIN32 */
+
+/**
+ * Constants
+ */
+#define MAX_AUTH_LENGTH     1024
+
+/**
+ * LoraWAN device map
+ */
+extern HashMap<uuid, LoraDeviceData> g_deviceMap;
+
+/**
+ * Device map mutex
+ */
+extern MUTEX g_deviceMapMutex;
+
+/**
+ * Simple MQTT client definition
+ */
+class MqttClient
+{
+private:
+   char *m_hostname;
+   UINT16 m_port;
+   char *m_pattern;
+   THREAD m_loopThread;
+   struct mosquitto *m_handle;
+   void (*m_messageHandler)(const char *, char *);
+
+   void networkLoop();
+   static THREAD_RESULT THREAD_CALL networkLoopStarter(void *arg);
+   static void messageCallback(struct mosquitto *mosq, void *userData, const struct mosquitto_message *msg);
+   void executeMessageHandler(const char *payload, char *topic) { m_messageHandler(payload, topic); }
+
+public:
+   MqttClient(const ConfigEntry *config);
+   ~MqttClient();
+
+   void setMessageHandler(void (*messageHandler)(const char *, char *));
+   void startNetworkLoop();
+   void stopNetworkLoop();
+};
+
+/**
+ * LoraWAN server link definition
+ */
+class LoraWanServerLink
+{
+private:
+   char *m_url;
+   char *m_app;
+   char *m_appId;
+   char *m_region;
+   bool m_adr;
+   UINT32 m_fcntCheck;
+   char m_auth[MAX_AUTH_LENGTH];
+   char m_errorBuffer[CURL_ERROR_SIZE];
+   CURL *m_curl;
+   long m_response;
+   MUTEX m_curlHandleMutex;
+
+   UINT32 sendRequest(const char *method, const char *url, const char *responseData = NULL, const curl_slist *headers = NULL, char *postFields = NULL);
+
+public:
+   LoraWanServerLink(const ConfigEntry *config);
+   ~LoraWanServerLink();
+
+   UINT32 registerDevice(NXCPMessage *request);
+   UINT32 deleteDevice(uuid guid);
+
+   bool connect();
+   void disconnect();
+};
+
+/**
+ * Communication processor functions
+ */
+void MqttMessageHandler(const char *payload, char *topic);
+
+LONG H_Communication(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
+
+/**
+ * Device map functions
+ */
+UINT32 AddDevice(LoraDeviceData *data);
+UINT32 RemoveDevice(uuid guid);
+LoraDeviceData *FindDevice(uuid guid);
diff --git a/src/agent/subagents/lorawan/lorawan_server_link.cpp b/src/agent/subagents/lorawan/lorawan_server_link.cpp
new file mode 100644 (file)
index 0000000..b4e7d8d
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ ** LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "lorawan.h"
+
+/**
+ * LoraWAN server link constructor
+ */
+LoraWanServerLink::LoraWanServerLink(const ConfigEntry *config)
+{
+   char *m_user;
+   char *m_pass;
+#ifdef UNICODE
+   m_user = UTF8StringFromWideString(config->getSubEntryValue(L"User", 0, L"admin"));
+   m_pass = UTF8StringFromWideString(config->getSubEntryValue(L"Password", 0, L"admin"));
+   m_url = UTF8StringFromWideString(config->getSubEntryValue(L"URL", 0, L"http://localhost"));
+   m_app = UTF8StringFromWideString(config->getSubEntryValue(L"Application", 0, L"backend"));
+   m_appId = UTF8StringFromWideString(config->getSubEntryValue(L"ApplicationId", 0, L"LoraWAN Devices"));
+   m_region = UTF8StringFromWideString(config->getSubEntryValue(L"Region", 0, L"EU863-870"));
+#else
+   m_user = strdup(config->getSubEntryValue("User", 0, "admin"));
+   m_pass = strdup(config->getSubEntryValue("Password", 0, "admin"));
+   m_url =  strdup(config->getSubEntryValue("URL", 0, "http://localhost"));
+   m_app = strdup(config->getSubEntryValue("Application", 0, "backend"));
+   m_appId = strdup(config->getSubEntryValue("ApplicationId", 0, L"LoraWAN Devices"));
+   m_region = strdup(config->getSubEntryValue("Region", 0, L"EU863-870"));
+#endif
+   m_adr = config->getSubEntryValueAsBoolean(_T("ADR"), 0, true);
+   m_fcntCheck = config->getSubEntryValueAsUInt(_T("FcntCheck"), 0, 3);
+   m_response = 0;
+
+   snprintf(m_auth, MAX_AUTH_LENGTH, "%s:%s", m_user, m_pass);
+   m_curl = NULL;
+   m_curlHandleMutex = MutexCreate();
+
+   free(m_user);
+   free(m_pass);
+}
+
+/*
+ * LoraWAN server link destructor
+ */
+LoraWanServerLink::~LoraWanServerLink()
+{
+   disconnect();
+   curl_global_cleanup();
+   MutexDestroy(m_curlHandleMutex);
+   free(m_url);
+   free(m_app);
+   free(m_appId);
+   free(m_region);
+}
+
+/**
+ * Send cURL request
+ */
+UINT32 LoraWanServerLink::sendRequest(const char *method, const char *url, const char *responseData, const curl_slist *headers, char *postFields)
+{
+   MutexLock(m_curlHandleMutex);
+   curl_easy_setopt(m_curl, CURLOPT_URL, url);
+   curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, method);
+   curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
+   curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, responseData);
+   curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, postFields);
+
+   UINT32 rcc = curl_easy_perform(m_curl);
+   if (rcc == CURLE_OK)
+   {
+      curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &m_response);
+      nxlog_debug(7, _T("LoraWAN Module: LoraWAN server request - URL: %hs, Method: %hs, Response: %03d"), url, method, m_response);
+   }
+   else
+      nxlog_debug(7, _T("LoraWAN Module: call to curl_easy_perform() failed: %hs"), m_errorBuffer);
+
+   MutexUnlock(m_curlHandleMutex);
+   return rcc;
+}
+
+/**
+ * Connect to LoraWAN server
+ */
+bool LoraWanServerLink::connect()
+{
+   disconnect();
+
+   bool rcc = false;
+   curl_global_init(CURL_GLOBAL_ALL);
+   m_curl = curl_easy_init();
+   if (m_curl == NULL)
+   {
+      nxlog_debug(4, _T("LoraWAN Module: call to curl_easy_init() failed"));
+      return rcc;
+   }
+
+   curl_easy_setopt(m_curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+   curl_easy_setopt(m_curl, CURLOPT_USERPWD, m_auth);
+   curl_easy_setopt(m_curl, CURLOPT_URL, m_url);
+   curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_errorBuffer);
+
+   if (sendRequest("OPTIONS", m_url) == CURLE_OK)
+   {
+      if (m_response == 200)
+      {
+         nxlog_debug(4, _T("LoraWAN Module: LoraWAN server login successful"));
+         rcc = true;
+      }
+      else
+         nxlog_debug(4, _T("LoraWAN Module: LoraWAN server login failed, HTTP response code %03d"), m_response);
+   }
+
+   return rcc;
+}
+
+/**
+ * Disconnect from LoraWAN server
+ */
+void LoraWanServerLink::disconnect()
+{
+   if (m_curl == NULL)
+      return;
+
+   curl_easy_cleanup(m_curl);
+   m_curl = NULL;
+}
+
+/**
+ * Register new LoraWAN device
+ */
+UINT32 LoraWanServerLink::registerDevice(NXCPMessage *request)
+{
+   LoraDeviceData *data = new LoraDeviceData(request);
+
+   json_t *root = json_object();
+   json_object_set_new(root, "adr_flag_set", json_integer(m_adr ? 1 : 0));
+   json_object_set_new(root, "app", json_string(m_app));
+   json_object_set_new(root, "appid", json_string(m_appId));
+   json_object_set_new(root, "can_join", json_true());
+   json_object_set_new(root, "fcnt_check", json_integer(m_fcntCheck));
+   json_object_set_new(root, "region", json_string(m_region));
+   json_object_set_new(root, "appargs", data->getGuid().toJson());
+   json_object_set_new(root, "txwin", 0);
+
+   char url[MAX_PATH];
+   strcpy(url, m_url);
+   if (data->isOtaa()) // OTAA
+   {
+      TCHAR appEui[17];
+      TCHAR appKey[33];
+      request->getFieldAsString(VID_LORA_APP_EUI, appEui, 17);
+      request->getFieldAsString(VID_LORA_APP_KEY, appKey, 33);
+      nxlog_debug(4, _T("LoraWAN Module: Config appEui %s"), appEui);
+      nxlog_debug(4, _T("LoraWAN Module: Config appKey %s"), appKey);
+      json_object_set_new(root, "deveui", json_string_t((const TCHAR*)data->getDevEui().toString(MAC_ADDR_FLAT_STRING)));
+      json_object_set_new(root, "appeui", json_string_t(appEui));
+      json_object_set_new(root, "appkey", json_string_t(appKey));
+      strcat(url, "/devices");
+   }
+   else  // ABP
+   {
+      TCHAR appSKey[33];
+      TCHAR nwkSKey[33];
+      request->getFieldAsString(VID_LORA_APP_S_KEY, appSKey, 33);
+      request->getFieldAsString(VID_LORA_NWK_S_KWY, nwkSKey, 33);
+      json_object_set_new(root, "devaddr", json_string_t((const TCHAR*)data->getDevAddr().toString(MAC_ADDR_FLAT_STRING)));
+      json_object_set_new(root, "appskey", json_string_t(appSKey));
+      json_object_set_new(root, "nwkskey", json_string_t(nwkSKey));
+      strcat(url, "/nodes");
+   }
+
+   char *jsonData = json_dumps(root, 0);
+   struct curl_slist *headers = NULL;
+   headers = curl_slist_append(headers, "Content-Type: application/json;charset=UTF-8");
+
+   UINT32 rcc;
+   if (sendRequest("POST", url, NULL, headers, jsonData) == CURLE_OK)
+   {
+      if (m_response == 204)
+      {
+         nxlog_debug(4, _T("LoraWAN Module: New LoraWAN device successfully registered"));
+         rcc = data->saveDeviceData();
+      }
+      else
+      {
+         nxlog_debug(4, _T("LoraWAN Module: LoraWAN device registration failed, HTTP response code %03d"), m_response);
+         rcc = ERR_BAD_RESPONSE;
+      }
+   }
+   else
+      rcc = ERR_EXEC_FAILED;
+
+   free(root);
+   free(jsonData);
+
+   return rcc;
+}
+
+/**
+ * Delete LoraWAN device
+ */
+UINT32 LoraWanServerLink::deleteDevice(uuid guid)
+{
+   UINT32 rcc = ERR_INVALID_OBJECT;
+   LoraDeviceData *data = FindDevice(guid);
+   if (data == NULL)
+      return rcc;
+
+   char url[MAX_PATH];
+   char addr[24];
+   if (data->isOtaa())  // if OTAA
+   {
+      WideCharToMultiByte(CP_UTF8, 0, (const TCHAR*)data->getDevEui().toString(MAC_ADDR_FLAT_STRING), -1, addr, 24, NULL, NULL);
+      snprintf(url, MAX_PATH, "%s/devices/%s", m_url, addr);
+      if (sendRequest("DELETE", url) == CURLE_OK)
+      {
+         curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &m_response);
+         if (m_response == 204)
+         {
+            nxlog_debug(4, _T("LoraWAN Module: New LoraWAN device successfully deleted"));
+         }
+         else
+         {
+            nxlog_debug(4, _T("LoraWAN Module: LoraWAN device deletion failed, HTTP response code %03d"), m_response);
+            rcc = ERR_BAD_RESPONSE;
+         }
+      }
+   }
+   if (data->getDevAddr().length() != 0)
+   {
+      WideCharToMultiByte(CP_UTF8, 0, (const TCHAR *)data->getDevAddr().toString(MAC_ADDR_FLAT_STRING), -1, addr, 24, NULL, NULL);
+      snprintf(url, MAX_PATH, "%s/nodes/%s", m_url, addr);
+      struct curl_slist *headers = NULL;
+      headers = curl_slist_append(headers, "Accept: application/json");
+      char *responseData;
+      if (sendRequest("GET", url, responseData, headers) == CURLE_OK)
+      {
+         if (m_response == 200)
+         {
+            if (sendRequest("DELETE", url) == CURLE_OK)
+            {
+               if (m_response == 204)
+                  nxlog_debug(4, _T("LoraWAN Module: New LoraWAN node successfully deleted"));
+               else
+               {
+                  nxlog_debug(4, _T("LoraWAN Module: LoraWAN node deletion failed, HTTP response code %03d"), m_response);
+                  rcc = ERR_BAD_RESPONSE;
+               }
+            }
+         }
+         else
+            nxlog_debug(4, _T("LoraWAN Module: LoraWAN node deletion failed, HTTP response code %03d"), m_response);
+      }
+      else
+         rcc = ERR_EXEC_FAILED;
+
+      free(responseData);
+   }
+   if (m_response == 204)
+      rcc = data->deleteDeviceData();
+
+   return rcc;
+}
diff --git a/src/agent/subagents/lorawan/main.cpp b/src/agent/subagents/lorawan/main.cpp
new file mode 100644 (file)
index 0000000..12aff96
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ ** LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "lorawan.h"
+
+/**
+ * MQTT connection settings
+ */
+static MqttClient *s_mqtt;
+
+/**
+ * LoraWAN server link
+ */
+static LoraWanServerLink *s_link = NULL;
+
+/**
+ * LoraWAN device map
+ */
+HashMap<uuid, LoraDeviceData> g_deviceMap(true);
+
+/**
+ * Device map mutex
+ */
+MUTEX g_deviceMapMutex = INVALID_MUTEX_HANDLE;
+
+/**
+ * Parameters
+ */
+static NETXMS_SUBAGENT_PARAM m_parameters[] =
+{
+   { _T("LoraWAN.RSSI(*)"), H_Communication, _T("R"), DCI_DT_INT, _T("RSSI") },
+   { _T("LoraWAN.SNR(*)"), H_Communication, _T("S"), DCI_DT_INT, _T("SNR") },
+   { _T("LoraWAN.Frequency(*)"), H_Communication, _T("F"), DCI_DT_UINT, _T("Frequency") },
+   { _T("LoraWAN.MessageCount(*)"), H_Communication, _T("M"), DCI_DT_UINT, _T("Message count") },
+   { _T("LoraWAN.DataRate(*)"), H_Communication, _T("D"), DCI_DT_STRING, _T("Data rate") },
+   { _T("LoraWAN.LastContact(*)"), H_Communication, _T("C"), DCI_DT_STRING, _T("Last contact") },
+   { _T("LoraWAN.DevAddr(*)"), H_Communication, _T("A"), DCI_DT_STRING, _T("DevAddr") }
+};
+
+/**
+ * Find device in local map
+ */
+LoraDeviceData *FindDevice(uuid guid)
+{
+   LoraDeviceData *data;
+
+   MutexLock(g_deviceMapMutex);
+   data = g_deviceMap.get(guid);
+   MutexUnlock(g_deviceMapMutex);
+
+   return data;
+}
+
+/**
+ * Load LoraWAN devices from DB
+ */
+static void LoadDevices()
+{
+   DB_HANDLE hdb = AgentGetLocalDatabaseHandle();
+   DB_RESULT hResult = DBSelect(hdb, _T("SELECT guid,devAddr,devEui,decoder,last_contact FROM device_decoder_map"));
+
+   if (hResult != NULL)
+   {
+      UINT32 nRows = DBGetNumRows(hResult);
+      MutexLock(g_deviceMapMutex);
+      for(int i = 0; i < nRows; i++)
+      {
+         LoraDeviceData *data = new LoraDeviceData(hResult, i);
+         g_deviceMap.set(data->getGuid(), data);
+      }
+      MutexUnlock(g_deviceMapMutex);
+      DBFreeResult(hResult);
+   }
+   else
+      nxlog_debug(4, _T("LoraWAN Subagent: Unable to load device map table"));
+
+   DBConnectionPoolReleaseConnection(hdb);
+}
+
+/**
+ * Process commands regarding LoraWAN devices
+ */
+static BOOL ProcessCommands(UINT32 command, NXCPMessage *request, NXCPMessage *response, AbstractCommSession *session)
+{
+   switch(command)
+   {
+      case CMD_REGISTER_LORAWAN_SENSOR:
+         response->setField(VID_RCC, s_link->registerDevice(request));
+         return TRUE;
+         break;
+      case CMD_UNREGISTER_LORAWAN_SENSOR:
+         response->setField(VID_RCC, s_link->deleteDevice(request->getFieldAsGUID(VID_GUID)));
+         return TRUE;
+         break;
+      default:
+         return FALSE;
+   }
+}
+
+/**
+ * Startup handler
+ */
+static BOOL SubagentInit(Config *config)
+{
+   g_deviceMapMutex = MutexCreate();
+
+   LoadDevices();
+
+   s_mqtt = new MqttClient(config->getEntry(_T("/LORAWAN")));
+   s_mqtt->setMessageHandler(MqttMessageHandler);
+   s_mqtt->startNetworkLoop();
+   s_link = new LoraWanServerLink(config->getEntry(_T("/LORAWAN")));
+   s_link->connect();
+
+   return TRUE;
+}
+
+/**
+ * Shutdown handler
+ */
+static void SubagentShutdown()
+{
+   s_mqtt->stopNetworkLoop();
+
+   MutexDestroy(g_deviceMapMutex);
+   delete(s_mqtt);
+   delete(s_link);
+}
+
+/**
+ * Subagent information
+ */
+static NETXMS_SUBAGENT_INFO m_info =
+{
+       NETXMS_SUBAGENT_INFO_MAGIC,
+       _T("LORAWAN"), NETXMS_VERSION_STRING,
+       SubagentInit,
+       SubagentShutdown,
+       ProcessCommands, // command handler
+       sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
+       m_parameters,
+       0, NULL,                // lists
+       0, NULL,                // tables
+   0, NULL,    // actions
+       0, NULL         // push parameters
+};
+
+/**
+ * Entry point for NetXMS agent
+ */
+DECLARE_SUBAGENT_ENTRY_POINT(LORAWAN)
+{
+       *ppInfo = &m_info;
+       return TRUE;
+}
diff --git a/src/agent/subagents/lorawan/mqtt_client.cpp b/src/agent/subagents/lorawan/mqtt_client.cpp
new file mode 100644 (file)
index 0000000..9395429
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ ** LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "lorawan.h"
+
+/**
+ * Simple MQTT client constructor
+ */
+MqttClient::MqttClient(const ConfigEntry *config)
+{
+   mosquitto_lib_init();
+#ifdef UNICODE
+   m_hostname = UTF8StringFromWideString(config->getSubEntryValue(L"Hostname", 0, L"127.0.0.1"));
+   m_pattern = UTF8StringFromWideString(config->getSubEntryValue(L"Topic", 0, L"#"));
+#else
+   m_hostname = strdup(config->getSubEntryValue("Hostname", 0, "127.0.0.1"));
+   m_pattern = strdup(config->getSubEntryValue("Topic", 0, "#"));
+#endif
+   m_port = (UINT16)config->getSubEntryValueAsUInt(_T("Port"), 0, 1883);;
+   m_loopThread = INVALID_THREAD_HANDLE;
+
+   char clientId[128];
+   strcpy(clientId, "nxlora/");
+   char *guid = uuid::generate().toString().getUTF8String();
+   strcat(clientId, guid);
+   free(guid);
+   m_handle = mosquitto_new(clientId, true, this);
+   m_messageHandler = NULL;
+}
+
+/**
+ * Simple MQTT client destructor
+ */
+MqttClient::~MqttClient()
+{
+   if (m_loopThread != INVALID_THREAD_HANDLE)
+      ThreadJoin(m_loopThread);
+   if (m_handle != NULL)
+      mosquitto_destroy(m_handle);
+   free(m_hostname);
+   free(m_pattern);
+}
+
+/**
+ * MQTT Network loop
+ */
+void MqttClient::networkLoop()
+{
+   while(mosquitto_connect(m_handle, m_hostname, m_port, 120) != MOSQ_ERR_SUCCESS)
+   {
+      nxlog_debug(4, _T("LoraWAN Module: MQTT unable to connect to broker at %hs:%d, will retry in 60 seconds"), m_hostname, m_port);
+      if (AgentSleepAndCheckForShutdown(60000))
+         return; // Server shutdown
+   }
+   nxlog_debug(3, _T("LoraWAN Module: MQTT connected to broker %hs:%d"), m_hostname, m_port);
+
+   if (mosquitto_subscribe(m_handle, NULL, m_pattern, 0) == MOSQ_ERR_SUCCESS)
+   {
+      nxlog_debug(4, _T("LoraWAN Module: MQTT subscribed to topic %hs on broker %hs:%d"), m_pattern, m_hostname, m_port);
+      mosquitto_message_callback_set(m_handle, MqttClient::messageCallback);
+      mosquitto_loop_forever(m_handle, -1, 1);
+   }
+   else
+      nxlog_debug(4, _T("LoraWAN Module: MQTT cannot subscribe to topic %hs on broker %hs:%d"), m_pattern, m_hostname, m_port);
+}
+
+/**
+ * MQTT Network loop starter
+ */
+THREAD_RESULT THREAD_CALL MqttClient::networkLoopStarter(void *arg)
+{
+   ((MqttClient *)arg)->networkLoop();
+   return THREAD_OK;
+}
+
+/**
+ * Start MQTT network loop
+ */
+void MqttClient::startNetworkLoop()
+{
+   m_loopThread = ThreadCreateEx(MqttClient::networkLoopStarter, 0, this);
+}
+
+/**
+ * Stop MQTT network loop
+ */
+void MqttClient::stopNetworkLoop()
+{
+   mosquitto_disconnect(m_handle);
+   ThreadJoin(m_loopThread);
+   m_loopThread = INVALID_THREAD_HANDLE;
+   mosquitto_lib_cleanup();
+}
+
+/**
+ * Set MQTT message handler
+ */
+void MqttClient::setMessageHandler(void (*messageHandler)(const char *, char *))
+{
+   m_messageHandler = messageHandler;
+}
+
+/**
+ * MQTT message callback
+ */
+void MqttClient::messageCallback(struct mosquitto *mosq, void *userData, const struct mosquitto_message *msg)
+{
+   if (msg->payloadlen <= 0)
+      return;
+
+   nxlog_debug(6, _T("LoraWAN Module:[MQTTMessageCallback] message received: %hs=\"%hs\""), msg->topic, (const char *)msg->payload);
+      ((MqttClient *)userData)->executeMessageHandler((const char *)msg->payload, msg->topic);
+}
diff --git a/src/agent/subagents/nas/.cproject b/src/agent/subagents/nas/.cproject
new file mode 100644 (file)
index 0000000..547329f
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+       <storageModule moduleId="org.eclipse.cdt.core.settings">
+               <cconfiguration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611">
+                       <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611" moduleId="org.eclipse.cdt.core.settings" name="Build (GNU)">
+                               <externalSettings/>
+                               <extensions>
+                                       <extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                               </extensions>
+                       </storageModule>
+                       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                               <configuration buildProperties="" description="" id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611" name="Build (GNU)" parent="org.eclipse.cdt.build.core.emptycfg">
+                                       <folderInfo id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611.1903778165" name="/" resourcePath="">
+                                               <toolChain id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.988512789" name="GNU Autotools Toolchain" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolChain">
+                                                       <targetPlatform id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.1901799413" isAbstract="false" name="GNU Autotools Target Platform" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform"/>
+                                                       <builder id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.1327956954" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Autotools Makefile Generator" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder"/>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.1450797606" name="configure" superClass="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure">
+                                                               <option id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name.1075684483" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name" value="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611" valueType="string"/>
+                                                       </tool>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.163275505" name="autogen.sh" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen"/>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.943604297" name="GCC C Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc">
+                                                               <option id="gnu.c.compiler.option.include.paths.2045040845" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/src/agent&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.758145295" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1918719895" name="GCC C++ Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp">
+                                                               <option id="gnu.cpp.compiler.option.include.paths.7996259" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/src/agent&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.112877841" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                                       </tool>
+                                               </toolChain>
+                                       </folderInfo>
+                               </configuration>
+                       </storageModule>
+                       <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+               </cconfiguration>
+       </storageModule>
+       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+               <project id="nas.null.660617191" name="nas"/>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+       <storageModule moduleId="refreshScope"/>
+       <storageModule moduleId="scannerConfiguration">
+               <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.1470884232;org.eclipse.linuxtools.cdt.autotools.core.toolChain.1470884232.1364879027;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1187539074;cdt.managedbuild.tool.gnu.cpp.compiler.input.591641913">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.1470884232;org.eclipse.linuxtools.cdt.autotools.core.toolChain.1470884232.1364879027;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.1167159969;cdt.managedbuild.tool.gnu.c.compiler.input.1326545146">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611;org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611.1903778165;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.943604297;cdt.managedbuild.tool.gnu.c.compiler.input.758145295">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611;org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611.1903778165;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1918719895;cdt.managedbuild.tool.gnu.cpp.compiler.input.112877841">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+       </storageModule>
+</cproject>
diff --git a/src/agent/subagents/nas/.settings/language.settings.xml b/src/agent/subagents/nas/.settings/language.settings.xml
new file mode 100644 (file)
index 0000000..5882171
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+       <configuration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.497658611" name="Build (GNU)">
+               <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+                       <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+                       <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+                       <provider copy-of="extension" id="org.eclipse.cdt.autotools.core.LibtoolGCCBuildCommandParser"/>
+                       <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="1424788779437872117" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+                               <language-scope id="org.eclipse.cdt.core.gcc"/>
+                               <language-scope id="org.eclipse.cdt.core.g++"/>
+                       </provider>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+               </extension>
+       </configuration>
+</project>
diff --git a/src/agent/subagents/nas/.settings/org.eclipse.cdt.codan.core.prefs b/src/agent/subagents/nas/.settings/org.eclipse.cdt.codan.core.prefs
new file mode 100644 (file)
index 0000000..812e407
--- /dev/null
@@ -0,0 +1,71 @@
+eclipse.preferences.version=1
+org.eclipse.cdt.codan.checkers.errnoreturn=Warning
+org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false}
+org.eclipse.cdt.codan.checkers.errreturnvalue=Error
+org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"}
+org.eclipse.cdt.codan.checkers.nocommentinside=-Error
+org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"}
+org.eclipse.cdt.codan.checkers.nolinecomment=-Error
+org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"}
+org.eclipse.cdt.codan.checkers.noreturn=Error
+org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false}
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"}
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"}
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false,enable_fallthrough_quickfix_param\=>false}
+org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
+org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"}
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true}
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"}
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"}
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"}
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"}
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"}
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false}
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false}
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")}
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"}
diff --git a/src/agent/subagents/nas/Makefile.am b/src/agent/subagents/nas/Makefile.am
new file mode 100644 (file)
index 0000000..2bd9b10
--- /dev/null
@@ -0,0 +1,18 @@
+SUBAGENT = nas
+
+pkglib_LTLIBRARIES = nas.la
+nas_la_SOURCES = main.cpp decoder.cpp
+nas_la_CPPFLAGS=-I@top_srcdir@/include
+nas_la_LDFLAGS = -module -avoid-version -export-symbols ../subagent.sym
+nas_la_LIBADD = ../../libnxagent/libnxagent.la ../../../libnetxms/libnetxms.la ../../../libnxmb/libnxmb.la
+
+EXTRA_DIST = nas.h
+
+if !STATIC_BUILD
+install-exec-hook:
+       if test "x`uname -s`" = "xAIX" ; then OBJECT_MODE=@OBJECT_MODE@ $(AR) x $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ ; rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a ; fi
+       mv -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ $(DESTDIR)$(pkglibdir)/$(SUBAGENT).nsm
+       rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).la
+       rm -f $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+       ln -s netxms/$(SUBAGENT).nsm $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+endif
diff --git a/src/agent/subagents/nas/decoder.cpp b/src/agent/subagents/nas/decoder.cpp
new file mode 100644 (file)
index 0000000..62d4f00
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ ** NAS Smart water meter retrofit decoder for LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "nas.h"
+
+/**
+ * Calculates the battery voltage from the given offset
+ */
+static double CalculateBatteryVoltage(int offset)
+{
+   return 3.6 - max(0, (254 - offset - max(0, 16 - offset) - max(0, 245 - offset - max(0, 16 - offset)))) * 0.05 - max(0, 245 - offset - max(0, 16 - offset)) * 0.004 - max(0, 16 - offset) * 0.05;
+}
+
+/**
+ * Parses the received payload
+ */
+static void ParsePayload(SensorData *sData, const lorawan_payload_t payload, UINT32 fieldId)
+{
+   switch(fieldId)
+   {
+      case SENSOR_COUNTER32:
+      {
+         UINT32 valueInt32;
+         memcpy(&valueInt32, payload, 4);
+#if WORDS_BIGENDIAN
+         valueInt32 = bswap_32(valueInt32);
+#endif
+         UINT32 msw = (UINT32)(sData->counter >> 32);
+         sData->counter = (((UINT64)msw << 32) | valueInt32);
+      }
+         break;
+      case SENSOR_COUNTER64:
+         memcpy(&sData->counter, payload, 8);
+#if WORDS_BIGENDIAN
+         valueInt64 = bswap_64(sData->counter);
+#endif
+         break;
+      case SENSOR_BATTERY:
+      {
+         int offset = (int)payload[4];
+         sData->batt = CalculateBatteryVoltage(offset);
+         nxlog_debug(7, _T("LoraWAN Module: Batt %f"), sData->batt);
+      }
+         break;
+      case SENSOR_TEMP:
+         sData->temp = (int)payload[5];
+         nxlog_debug(7, _T("LoraWAN Module: Temp %d"), sData->temp);
+         break;
+      case SENSOR_RSSI:
+         sData->rssi = (INT32)(char)payload[6];
+         nxlog_debug(7, _T("LoraWAN Module: rssi %d"), sData->rssi);
+         break;
+      case SENSOR_WATCH_MODE:
+         sData->watchMode =(payload[7] & 0x01) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: watch m %d"), sData->watchMode);
+         break;
+      case SENSOR_WATCH_STATUS:
+         sData->watchStatus =(payload[8] & 0x01) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: watchStatus m %d"), sData->watchStatus);
+         break;
+      case SENSOR_LEAK_MODE:
+         sData->leakMode =(payload[7] & 0x02) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: leakMode m %d"), sData->leakMode);
+         break;
+      case SENSOR_LEAK_STATUS:
+         sData->leakStatus =(payload[8] & 0x02) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: leakStatus m %d"), sData->leakStatus);
+         break;
+      case SENSOR_REV_FLOW_MODE:
+         sData->revFlowMode =(payload[7] & 0x04) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: revFlowMode m %d"), sData->revFlowMode);
+         break;
+      case SENSOR_REV_FLOW_STATUS:
+         sData->revFlowStatus =(payload[8] & 0x04) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: revFlowStatus m %d"), sData->revFlowStatus);
+         break;
+      case SENSOR_TAMPER_MODE:
+         sData->tamperMode =(payload[7] & 0x08) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: tamperMode m %d"), sData->tamperMode);
+         break;
+      case SENSOR_TAMPER_STATUS:
+         sData->tamperStatus =(payload[8] & 0x08) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: tamperStatus m %d"), sData->tamperStatus);
+         break;
+      case SENSOR_MODE:
+         sData->sensorMode =(payload[7] & 0x10) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: sensorMode m %d"), sData->sensorMode);
+         break;
+      case SENSOR_REPORTING_MODE:
+         sData->reportingMode =(payload[7] & 0x20) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: reportingMode m %d"), sData->reportingMode);
+         break;
+      case SENSOR_TEMP_DETECT_MODE:
+         sData->tempDetectMode =(payload[7] & 0x40) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: tempDetectMode m %d"), sData->tempDetectMode);
+         break;
+      case SENSOR_TEMP_DETECT_STATUS:
+         sData->tempDetectStatus =(payload[8] & 0x40) ? 1 : 0;
+         nxlog_debug(7, _T("LoraWAN Module: tempDetectStatus m %d"), sData->tempDetectStatus);
+         break;
+   }
+}
+
+/**
+ * Decodes LoraWAN device payload
+ */
+bool Decode(const TCHAR *name, const void *data, void *result)
+{
+   LoraDeviceData *dData = (LoraDeviceData *)data;
+   if (dData == NULL  || dData->getDecoder() != NAS)
+      return false;
+
+   SensorData *sData = FindSensor(dData->getGuid());
+   if (sData == NULL) // Create new entry
+   {
+      sData = new struct SensorData();
+      sData->guid = dData->getGuid();
+      AddSensor(sData);
+   }
+
+   switch(dData->getPort())
+   {
+      case 24:
+         ParsePayload(sData, dData->getPayload(), SENSOR_COUNTER32);
+         ParsePayload(sData, dData->getPayload(), SENSOR_BATTERY);
+         ParsePayload(sData, dData->getPayload(), SENSOR_TEMP);
+         ParsePayload(sData, dData->getPayload(), SENSOR_RSSI);
+         ParsePayload(sData, dData->getPayload(), SENSOR_WATCH_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_WATCH_STATUS);
+         ParsePayload(sData, dData->getPayload(), SENSOR_LEAK_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_LEAK_STATUS);
+         ParsePayload(sData, dData->getPayload(), SENSOR_REV_FLOW_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_REV_FLOW_STATUS);
+         ParsePayload(sData, dData->getPayload(), SENSOR_TAMPER_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_TAMPER_STATUS);
+         ParsePayload(sData, dData->getPayload(), SENSOR_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_REPORTING_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_TEMP_DETECT_MODE);
+         ParsePayload(sData, dData->getPayload(), SENSOR_TEMP_DETECT_STATUS);
+         break;
+      case 6:
+         if (sData->reportingMode)
+         {
+            sData->counter = 0;
+            sData->reportingMode = 0;
+         }
+         ParsePayload(sData, dData->getPayload(), SENSOR_COUNTER64);
+         break;
+      case 14:
+         if (!sData->reportingMode)
+         {
+            sData->counter = 0;
+            sData->reportingMode = 1;
+         }
+         ParsePayload(sData, dData->getPayload(), SENSOR_COUNTER64);
+         break;
+      case 99: // Boot message
+         return true;
+      default:
+         return false;
+   }
+   nxlog_debug(7, _T("LoraWAN Module: Sensor[%s] updated"), (const TCHAR*)sData->guid.toString());
+
+   return true;
+}
+
+/**
+ * Handler for sensor data
+ */
+LONG H_Sensor(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   TCHAR guid[38];
+   if (!AgentGetParameterArg(param, 1, guid, 38))
+      return SYSINFO_RC_ERROR;
+
+   SensorData *data = FindSensor(uuid::parse(guid));
+   if (data == NULL)
+      return SYSINFO_RC_ERROR;
+
+   switch(*arg)
+   {
+      case 'B':
+         ret_double(value, data->batt, 3);
+         break;
+      case 'R':
+         ret_int(value, data->rssi);
+         break;
+      case 'U':
+         ret_uint64(value, data->counter);
+         break;
+      default:
+         return SYSINFO_RC_UNSUPPORTED;
+   }
+
+   return SYSINFO_RC_SUCCESS;
+}
+
+/**
+ * Handler for sensor modes
+ */
+LONG H_Mode(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   TCHAR guid[38];
+   if (!AgentGetParameterArg(param, 1, guid, 38))
+      return SYSINFO_RC_ERROR;
+
+   SensorData *data = FindSensor(uuid::parse(guid));
+   if (data == NULL)
+      return SYSINFO_RC_ERROR;
+
+   switch(*arg)
+   {
+      case 'A':
+         ret_uint(value, data->tamperMode);
+         break;
+      case 'F':
+         ret_uint(value, data->revFlowMode);
+         break;
+      case 'L':
+         ret_uint(value, data->leakMode);
+         break;
+      case 'R':
+         ret_uint(value, data->reportingMode);
+         break;
+      case 'S':
+         ret_uint(value, data->sensorMode);
+         break;
+      case 'T':
+         ret_uint(value, data->temp);
+         break;
+      case 'W':
+         ret_uint(value, data->watchMode);
+         break;
+      default:
+         return SYSINFO_RC_UNSUPPORTED;
+   }
+
+   return SYSINFO_RC_SUCCESS;
+}
+
+/**
+ * Handler for sensor status
+ */
+LONG H_Status(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   TCHAR guid[38];
+   if (!AgentGetParameterArg(param, 1, guid, 38))
+      return SYSINFO_RC_ERROR;
+
+   SensorData *data = FindSensor(uuid::parse(guid));
+   if (data == NULL)
+      return SYSINFO_RC_ERROR;
+
+   switch(*arg)
+   {
+      case 'A':
+         ret_uint(value, data->tamperStatus);
+         break;
+      case 'D':
+         ret_uint(value, data->tempDetectStatus);
+         break;
+      case 'L':
+         ret_uint(value, data->leakStatus);
+         break;
+      case 'R':
+         ret_uint(value, data->revFlowStatus);
+         break;
+      case 'T':
+         ret_uint(value, data->temp);
+         break;
+      case 'W':
+         ret_uint(value, data->watchStatus);
+         break;
+      default:
+         return SYSINFO_RC_UNSUPPORTED;
+   }
+
+   return SYSINFO_RC_SUCCESS;
+}
diff --git a/src/agent/subagents/nas/main.cpp b/src/agent/subagents/nas/main.cpp
new file mode 100644 (file)
index 0000000..e328884
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ ** NAS Smart water meter retrofit decoder for LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include "nas.h"
+
+/**
+ * Sensor data map
+ */
+HashMap<uuid, SensorData> g_sensorMap(true);
+
+/**
+ * Find sensor data in local cache
+ */
+ SensorData *FindSensor(uuid guid)
+{
+   SensorData *data;
+   MutexLock(s_sensorMapMutex);
+   data = g_sensorMap.get(guid);
+   MutexUnlock(s_sensorMapMutex);
+
+   return data;
+}
+
+/**
+ * Add sensor data to local cache
+ */
+void AddSensor(SensorData *data)
+{
+   MutexLock(s_sensorMapMutex);
+   g_sensorMap.set(data->guid, data);
+   MutexUnlock(s_sensorMapMutex);
+}
+
+/**
+ * Startup handler
+ */
+static BOOL SubagentInit(Config *config)
+{
+   s_sensorMapMutex = MutexCreate();
+
+   NXMBDispatcher *dispatcher = NXMBDispatcher::getInstance();
+   dispatcher->addCallHandler(_T("NOTIFY_DECODERS"), Decode);
+
+   return TRUE;
+}
+
+/**
+ * Shutdown handler
+ */
+static void SubagentShutdown()
+{
+   NXMBDispatcher *dispatcher = NXMBDispatcher::getInstance();
+   dispatcher->removeCallHandler(_T("NOTIFY_DECODERS"));
+   g_sensorMap.clear();
+   MutexDestroy(s_sensorMapMutex);
+}
+
+/**
+ * Parameters
+ */
+static NETXMS_SUBAGENT_PARAM m_parameters[] =
+{
+       { _T("WaterMeter.Usage(*)"), H_Sensor, _T("U"), DCI_DT_UINT64, _T("Usage") },
+       { _T("WaterMeter.Battery(*)"), H_Sensor, _T("B"), DCI_DT_FLOAT, _T("Battery voltage") },
+   { _T("WaterMeter.RSSI(*)"), H_Sensor, _T("R"), DCI_DT_INT, _T("RSSI") },
+       { _T("WaterMeter.Temperature(*)"), H_Status, _T("T"), DCI_DT_UINT, _T("Temperature") },
+   { _T("WaterMeter.Watch.Mode(*)"), H_Mode, _T("W"), DCI_DT_UINT, _T("Watch mode") },
+   { _T("WaterMeter.Watch.Status(*)"), H_Status, _T("W"), DCI_DT_UINT, _T("Watch status") },
+   { _T("WaterMeter.Leak.Mode(*)"), H_Mode, _T("L"), DCI_DT_UINT, _T("Leak mode") },
+   { _T("WaterMeter.Leak.Status(*)"), H_Status, _T("L"), DCI_DT_UINT, _T("Leak status") },
+   { _T("WaterMeter.ReverseFlow.Mode(*)"), H_Mode, _T("F"), DCI_DT_UINT, _T("Reverse flow mode") },
+   { _T("WaterMeter.ReverseFlow.Status(*)"), H_Status, _T("R"), DCI_DT_UINT, _T("Reverse flow status") },
+   { _T("WaterMeter.Tamper.Mode(*)"), H_Mode, _T("A"), DCI_DT_UINT, _T("Tamper mode") },
+   { _T("WaterMeter.Tamper.Status(*)"), H_Status, _T("A"), DCI_DT_UINT, _T("Tamper status") },
+   { _T("WaterMeter.Sensor.Mode(*)"), H_Mode, _T("S"), DCI_DT_UINT, _T("Sensor mode") },
+   { _T("WaterMeter.Reporting.Mode(*)"), H_Mode, _T("R"), DCI_DT_UINT, _T("Reporting mode") },
+   { _T("WaterMeter.TemperatureDetection.Mode(*)"), H_Mode, _T("T"), DCI_DT_UINT, _T("Temperature detection mode") },
+   { _T("WaterMeter.TemperatureDetection.Status(*)"), H_Status, _T("D"), DCI_DT_UINT, _T("Temperature detection status") }
+};
+
+/**
+ * Subagent information
+ */
+static NETXMS_SUBAGENT_INFO m_info =
+{
+       NETXMS_SUBAGENT_INFO_MAGIC,
+       _T("NAS"), NETXMS_VERSION_STRING,
+       SubagentInit,
+       SubagentShutdown,
+       NULL, // command handler
+       sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
+       m_parameters,
+       0, NULL,                // lists
+       0, NULL,                // tables
+       0, NULL,    // actions
+       0, NULL         // push parameters
+};
+
+/**
+ * Entry point for NetXMS agent
+ */
+DECLARE_SUBAGENT_ENTRY_POINT(NAS)
+{
+       *ppInfo = &m_info;
+       return TRUE;
+}
diff --git a/src/agent/subagents/nas/nas.h b/src/agent/subagents/nas/nas.h
new file mode 100644 (file)
index 0000000..b1fafef
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ ** NAS Smart water meter retrofit decoder for LoraWAN subagent
+ ** Copyright (C) 2009 - 2017 Raden Solutions
+ **
+ ** This program is free software; you can redistribute it and/or modify
+ ** it under the terms of the GNU General Public License as published by
+ ** the Free Software Foundation; either version 2 of the License, or
+ ** (at your option) any later version.
+ **
+ ** This program is distributed in the hope that it will be useful,
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ** GNU General Public License for more details.
+ **
+ ** You should have received a copy of the GNU General Public License
+ ** along with this program; if not, write to the Free Software
+ ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **
+ **/
+
+#include <nms_agent.h>
+#include <nms_util.h>
+#include <nxmbapi.h>
+
+/**
+ * Decoder ID
+ */
+#define NAS 0
+
+/**
+ * Sensor data field defines
+ */
+#define SENSOR_COUNTER32            0
+#define SENSOR_COUNTER64            1
+#define SENSOR_BATTERY              2
+#define SENSOR_TEMP                 3
+#define SENSOR_RSSI                 4
+#define SENSOR_WATCH_MODE           5
+#define SENSOR_WATCH_STATUS         6
+#define SENSOR_LEAK_MODE            7
+#define SENSOR_LEAK_STATUS          8
+#define SENSOR_REV_FLOW_MODE        9
+#define SENSOR_REV_FLOW_STATUS      10
+#define SENSOR_TAMPER_MODE          11
+#define SENSOR_TAMPER_STATUS        12
+#define SENSOR_MODE                 13
+#define SENSOR_REPORTING_MODE       14
+#define SENSOR_TEMP_DETECT_MODE     15
+#define SENSOR_TEMP_DETECT_STATUS   16
+
+/**
+ * Sensor data struct
+ */
+struct SensorData
+{
+   uuid guid;
+   UINT64 counter;
+   double batt;
+   INT32 rssi;
+   INT32 temp;
+   UINT32 watchMode;
+   UINT32 watchStatus;
+   UINT32 leakMode;
+   UINT32 leakStatus;
+   UINT32 revFlowMode;
+   UINT32 revFlowStatus;
+   UINT32 tamperMode;
+   UINT32 tamperStatus;
+   UINT32 sensorMode;
+   UINT32 reportingMode;
+   UINT32 tempDetectMode;
+   UINT32 tempDetectStatus;
+
+   ~SensorData() {}
+};
+
+/**
+ * Sensor data map
+ */
+extern HashMap<uuid, SensorData> g_sensorMap;
+
+/**
+ * Sensor map mutex
+ */
+static MUTEX s_sensorMapMutex = INVALID_MUTEX_HANDLE;
+
+/**
+ * Sensor data handlers
+ */
+LONG H_Sensor(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
+LONG H_Mode(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
+LONG H_Status(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
+
+/**
+ * Sensor map functions
+ */
+SensorData *FindSensor(uuid guid);
+void AddSensor(SensorData *data);
+
+/**
+ * NXMBDispatcher handler
+ */
+bool Decode(const TCHAR *name, const void *data, void *result);
index e9f4668..6233e94 100644 (file)
@@ -689,6 +689,17 @@ InetAddress LIBNXDB_EXPORTABLE DBGetFieldInetAddr(DB_RESULT hResult, int iRow, i
 }
 
 /**
+ * Get field`s value as MAC address
+ */
+MacAddress LIBNXDB_EXPORTABLE DBGetFieldMacAddr(DB_RESULT hResult, int iRow, int iColumn)
+{
+   TCHAR *pszVal, buffer[36];
+   pszVal = DBGetField(hResult, iRow, iColumn, buffer, 36);
+
+   return pszVal == NULL ? MacAddress() : MacAddress::parse(pszVal);
+}
+
+/**
  * Get field's value as integer array from byte array encoded in hex
  */
 bool LIBNXDB_EXPORTABLE DBGetFieldByteArray(DB_RESULT hResult, int iRow, int iColumn,
@@ -1384,6 +1395,15 @@ void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const u
 }
 
 /**
+ * Bind Mac Address
+ */
+void LIBNXDB_EXPORTABLE DBBind(DB_STATEMENT hStmt, int pos, int sqlType, const MacAddress& value)
+{
+   TCHAR buffer[36];
+   DBBind(hStmt, pos, sqlType, DB_CTYPE_STRING, value.toString(buffer, MAC_ADDR_FLAT_STRING), DB_BIND_TRANSIENT);
+}
+
+/**
  * Execute prepared statement (non-SELECT)
  */
 bool LIBNXDB_EXPORTABLE DBExecuteEx(DB_STATEMENT hStmt, TCHAR *errorText)
index 316c771..7eebf61 100644 (file)
@@ -31,6 +31,7 @@ Export-Package: org.netxms.base,
  org.netxms.client.packages,
  org.netxms.client.plugin,
  org.netxms.client.reporting,
+ org.netxms.client.sensor.configs,
  org.netxms.client.server,
  org.netxms.client.services,
  org.netxms.client.snmp,
index d908950..052de0c 100644 (file)
@@ -966,7 +966,20 @@ public class NXCPCodes
    public static final long VID_TUNNEL_GUID = 577;
    public static final long VID_ORGANIZATION = 578;
    public static final long VID_TUNNEL_ID = 579;
-   public static final long VID_PARENT_INTERFACE = 580;
+   public static final long VID_PARENT_INTERFACE = 580; 
+   public static final long VID_SENSOR_FLAGS = 581; 
+   public static final long VID_DEVICE_CLASS = 582; 
+   public static final long VID_COMM_PROTOCOL = 583; 
+   public static final long VID_XML_CONFIG = 584; 
+   public static final long VID_DEVICE_ADDRESS = 585; 
+   public static final long VID_META_TYPE = 586; 
+   public static final long VID_LAST_CONN_TIME = 587;
+   public static final long VID_FRAME_COUNT = 588;
+   public static final long VID_SIGNAL_STRENGHT = 589;
+   public static final long VID_SIGNAL_NOISE = 590;
+   public static final long VID_FREQUENCY = 591;
+   public static final long VID_SENSOR_PROXY = 592;
+   public static final long VID_XML_REG_CONFIG = 593;
 
        public static final long VID_ACL_USER_BASE = 0x00001000L;
        public static final long VID_ACL_USER_LAST = 0x00001FFFL;
index 9697cbf..c016466 100644 (file)
@@ -30,6 +30,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 import java.util.zip.CRC32;
+import org.netxms.client.MacAddress;
 import com.jcraft.jzlib.Deflater;
 import com.jcraft.jzlib.DeflaterOutputStream;
 import com.jcraft.jzlib.InflaterInputStream;
@@ -454,6 +455,19 @@ public class NXCPMessage
       setField(new NXCPMessageField(fieldId, NXCPMessageField.TYPE_INT64, (value != null) ? value.getTime() / 1000L : 0L));
    }
    
+   /**
+    * Set byte field from MacAddress object value. If value is null, field will not be set.
+    * 
+    * @param fieldId field ID
+    * @param value MacAddress object (can be null)
+    */
+   public void setField(long fieldId, MacAddress macAddress)
+   {
+      if(macAddress == null)
+         return;
+      setField(fieldId, macAddress.getValue());      
+   }
+   
        /**
         * Get field as byte array
         * 
@@ -538,6 +552,18 @@ public class NXCPMessage
                return (var != null) ? var.getAsInetAddress() : null;
        }
        
+       /**
+        *  Get field as MacAddress
+        *  
+        * @param fieldId field id
+        * @return Mac Address
+        */
+       public MacAddress getFieldAsMacAddress(final long fieldId)
+       {
+          final NXCPMessageField var = findField(fieldId);
+          return (var != null) ? new MacAddress(var.getAsBinary()) : null;
+       }
+       
    /**
     * Get field as InetAddressEx
     * 
index 0a112b0..f34c293 100644 (file)
@@ -48,7 +48,7 @@ public class MacAddress
        {
                if (src != null)
                {
-                       value = Arrays.copyOf(src, 6);
+                       value = Arrays.copyOf(src, src.length);
                }
                else
                {
@@ -59,7 +59,6 @@ public class MacAddress
        
        /**
         * Check if MAC address is all zeroes
-        * 
         * @return true if MAC address is all zeroes
         */
        public boolean isNull()
@@ -115,27 +114,30 @@ public class MacAddress
        /**
         * Parse MAC address string representation. Supported representations are 6 groups of
         * two hex digits, separated by spaces, minuses, or colons; or 4 groups of three hex digits 
-        * separated by dots; or 12 non-separated digits. Examples of valid MAC address strings:
+        * separated by dots; or 12 non-separated digits; or 16 non-separated hex digits. 
+        * Examples of valid MAC address strings:
         * 00:10:FA:23:11:7A
         * 01 02 fa c4 10 dc
         * 00-90-0b-11-01-29
         * 009.00b.110.129
         * 0203fcd456c1
+        * 0203FCD465C1DF56
         * 
         * @param str MAC address string
         * @return MAC address object
         * @throws MacAddressFormatException if MAC address sting is invalid
         */
        public static MacAddress parseMacAddress(String str) throws MacAddressFormatException
-       {
-               Pattern pattern = Pattern.compile("^([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})$");
+       {          
+               Pattern pattern = Pattern.compile("^([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})[ :\\-]?([0-9a-fA-F]{2})?[ :\\-]?([0-9a-fA-F]{2})?$");
                Matcher matcher = pattern.matcher(str.trim());
                if (matcher.matches())
                {
-                       byte[] bytes = new byte[6];
+                  int count = matcher.groupCount();
+                       byte[] bytes = new byte[count];
                        try
                        {
-                               for(int i = 0; i < 6; i++)
+                               for(int i = 0; i < count; i++)
                                        bytes[i] = (byte)Integer.parseInt(matcher.group(i + 1), 16);
                        }
                        catch(NumberFormatException e)
@@ -182,7 +184,7 @@ public class MacAddress
         */
        public byte[] getValue()
        {
-               return Arrays.copyOf(value, 6);
+               return Arrays.copyOf(value, value.length);
        }
        
        /**
index 1f586c5..902dbe4 100644 (file)
@@ -21,6 +21,7 @@ package org.netxms.client;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import org.netxms.base.InetAddressEx;
 import org.netxms.client.objects.AbstractObject;
@@ -74,6 +75,17 @@ public class NXCObjectCreationData
        private long chassisId;
        private String sshLogin;
        private String sshPassword;
+   private int deviceClass;
+   private String vendor;
+   private int  commProtocol;
+   private String xmlConfig;
+   private String xmlRegConfig;
+   private String serialNumber;
+   private String deviceAddress;
+   private String metaType;
+   private String description;
+   private Date lastConnectionTime;
+   private long sensorProxy;
        
        /**
         * Constructor.
@@ -125,6 +137,15 @@ public class NXCObjectCreationData
                createStatusDci = false;
                sshLogin = "";
                sshPassword = "";
+          deviceClass = 0;
+          vendor = "";
+          commProtocol = 0;
+          xmlConfig ="";
+          serialNumber = "";
+          deviceAddress = "";
+          metaType = "";
+          description = "";
+          sensorProxy = 0;
        }
 
        /**
@@ -727,4 +748,179 @@ public class NXCObjectCreationData
    {
       this.sshPassword = sshPassword;
    }
+
+   /**
+    * @return the deviceClass
+    */
+   public int getDeviceClass()
+   {
+      return deviceClass;
+   }
+
+   /**
+    * @param deviceClass the deviceClass to set
+    */
+   public void setDeviceClass(int deviceClass)
+   {
+      this.deviceClass = deviceClass;
+   }
+
+   /**
+    * @return the commProtocol
+    */
+   public int getCommProtocol()
+   {
+      return commProtocol;
+   }
+
+   /**
+    * @param commProtocol the commProtocol to set
+    */
+   public void setCommProtocol(int commProtocol)
+   {
+      this.commProtocol = commProtocol;
+   }
+
+   /**
+    * @return the xmlConfig
+    */
+   public String getXmlConfig()
+   {
+      return xmlConfig;
+   }
+
+   /**
+    * @param xmlConfig the xmlConfig to set
+    */
+   public void setXmlConfig(String xmlConfig)
+   {
+      this.xmlConfig = xmlConfig;
+   }
+
+   /**
+    * @return the xmlRegConfig
+    */
+   public String getXmlRegConfig()
+   {
+      return xmlRegConfig;
+   }
+
+   /**
+    * @param xmlRegConfig the xmlRegConfig to set
+    */
+   public void setXmlRegConfig(String xmlRegConfig)
+   {
+      this.xmlRegConfig = xmlRegConfig;
+   }
+
+   /**
+    * @return the deviceAddress
+    */
+   public String getDeviceAddress()
+   {
+      return deviceAddress;
+   }
+
+   /**
+    * @param deviceAddress the deviceAddress to set
+    */
+   public void setDeviceAddress(String deviceAddress)
+   {
+      this.deviceAddress = deviceAddress;
+   }
+
+   /**
+    * @return the metaType
+    */
+   public String getMetaType()
+   {
+      return metaType;
+   }
+
+   /**
+    * @param metaType the metaType to set
+    */
+   public void setMetaType(String metaType)
+   {
+      this.metaType = metaType;
+   }
+
+   /**
+    * @return the description
+    */
+   public String getDescription()
+   {
+      return description;
+   }
+
+   /**
+    * @param description the description to set
+    */
+   public void setDescription(String description)
+   {
+      this.description = description;
+   }
+
+   /**
+    * @return the lastConnectionTime
+    */
+   public Date getLastConnectionTime()
+   {
+      return lastConnectionTime;
+   }
+
+   /**
+    * @param lastConnectionTime the lastConnectionTime to set
+    */
+   public void setLastConnectionTime(Date lastConnectionTime)
+   {
+      this.lastConnectionTime = lastConnectionTime;
+   }
+
+   /**
+    * @param vendor the vendor to set
+    */
+   public void setVendor(String vendor)
+   {
+      this.vendor = vendor;
+   }
+
+   /**
+    * @param serialNumber the serialNumber to set
+    */
+   public void setSerialNumber(String serialNumber)
+   {
+      this.serialNumber = serialNumber;
+   }
+   
+   /**
+    * @return the vendor
+    */
+   public final String getVendor()
+   {
+      return vendor;
+   }
+   /**
+    * @return the serialNumber
+    */
+   public final String getSerialNumber()
+   {
+      return serialNumber;
+   }
+
+   /**
+    * @return the sensorProxy
+    */
+   public long getSensorProxy()
+   {
+      return sensorProxy;
+   }
+
+   /**
+    * @param sensorProxy the sensorProxy to set
+    */
+   public void setSensorProxy(long sensorProxy)
+   {
+      this.sensorProxy = sensorProxy;
+   }
 }
index 04ed34e..0e74397 100644 (file)
@@ -119,6 +119,14 @@ public class NXCObjectModificationData
    public static final int AGENT_COMPRESSION_MODE = 68;
    public static final int URL_LIST               = 69;
    public static final int SEED_OBJECTS           = 70;
+   public static final int MAC_ADDRESS            = 71;
+   public static final int DEVICE_CLASS           = 72;
+   public static final int VENDOR                 = 73;
+   public static final int SERIAL_NUMBER          = 74;
+   public static final int DEVICE_ADDRESS         = 75;
+   public static final int META_TYPE              = 76;
+   public static final int SENSOR_PROXY           = 77;
+   public static final int XML_CONFIG             = 78;
        
        private Set<Integer> fieldSet;
        private long objectId;
@@ -213,6 +221,14 @@ public class NXCObjectModificationData
        private long zoneProxy;
        private List<ObjectUrl> urls;
    private List<Long> seedObjectIds;
+   private MacAddress macAddress;
+   private int deviceClass;
+   private String vendor;
+   private String serialNumber;
+   private String deviceAddress;
+   private String metaType;
+   private long sensorProxy;
+   private String xmlConfig;
        
        /**
         * Constructor for creating modification data for given object
@@ -1785,4 +1801,134 @@ public class NXCObjectModificationData
       this.seedObjectIds = seedObjectIds;
       fieldSet.add(SEED_OBJECTS);
    }
+
+   /**
+    * @return the macAddress
+    */
+   public MacAddress getMacAddress()
+   {
+      return macAddress;
+   }
+
+   /**
+    * @param macAddress the macAddress to set
+    */
+   public void setMacAddress(MacAddress macAddress)
+   {
+      this.macAddress = macAddress;
+      fieldSet.add(MAC_ADDRESS);
+   }
+
+   /**
+    * @return the deviceClass
+    */
+   public int getDeviceClass()
+   {
+      return deviceClass;
+   }
+
+   /**
+    * @param deviceClass the deviceClass to set
+    */
+   public void setDeviceClass(int deviceClass)
+   {
+      this.deviceClass = deviceClass;
+      fieldSet.add(DEVICE_CLASS);
+   }
+
+   /**
+    * @return the vendor
+    */
+   public String getVendor()
+   {
+      return vendor;
+   }
+
+   /**
+    * @param vendor the vendor to set
+    */
+   public void setVendor(String vendor)
+   {
+      this.vendor = vendor;
+      fieldSet.add(VENDOR);
+   }
+
+   /**
+    * @return the serialNumber
+    */
+   public String getSerialNumber()
+   {
+      return serialNumber;
+   }
+
+   /**
+    * @param serialNumber the serialNumber to set
+    */
+   public void setSerialNumber(String serialNumber)
+   {
+      this.serialNumber = serialNumber;
+      fieldSet.add(SERIAL_NUMBER);
+   }
+
+   /**
+    * @return the deviceAddress
+    */
+   public String getDeviceAddress()
+   {
+      return deviceAddress;
+   }
+
+   /**
+    * @param deviceAddress the deviceAddress to set
+    */
+   public void setDeviceAddress(String deviceAddress)
+   {
+      this.deviceAddress = deviceAddress;
+      fieldSet.add(DEVICE_ADDRESS);
+   }
+
+   /**
+    * @return the metaType
+    */
+   public String getMetaType()
+   {
+      return metaType;
+   }
+
+   /**
+    * @param metaType the metaType to set
+    */
+   public void setMetaType(String metaType)
+   {
+      this.metaType = metaType;
+      fieldSet.add(META_TYPE);
+   }
+
+   public void setSensorProxy(long proxyNode)
+   {
+      this.sensorProxy = proxyNode;
+      fieldSet.add(SENSOR_PROXY);
+   }
+   
+   public long getSensorProxy()
+   {
+      return sensorProxy;
+   }
+
+   /**
+    * @return the xmlConfig
+    */
+   public String getXmlConfig()
+   {
+      return xmlConfig;
+   }
+
+   /**
+    * @param xmlConfig the xmlConfig to set
+    */
+   public void setXmlConfig(String xmlConfig)
+   {
+      this.xmlConfig = xmlConfig;
+      fieldSet.add(XML_CONFIG);
+   }
 }
index dbc89d1..28ea0be 100644 (file)
@@ -142,6 +142,7 @@ import org.netxms.client.objects.NodeLink;
 import org.netxms.client.objects.PolicyGroup;
 import org.netxms.client.objects.PolicyRoot;
 import org.netxms.client.objects.Rack;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.ServiceCheck;
 import org.netxms.client.objects.ServiceRoot;
 import org.netxms.client.objects.Subnet;
@@ -1189,6 +1190,9 @@ public class NXCSession
          case AbstractObject.OBJECT_RACK:
             object = new Rack(msg, this);
             break;
+         case AbstractObject.OBJECT_SENSOR:
+            object = new Sensor(msg, this);
+            break;
          case AbstractObject.OBJECT_SERVICEROOT:
             object = new ServiceRoot(msg, this);
             break;
@@ -4482,6 +4486,20 @@ public class NXCSession
          case AbstractObject.OBJECT_SLMCHECK:
             msg.setFieldInt16(NXCPCodes.VID_IS_TEMPLATE, data.isTemplate() ? 1 : 0);
             break;
+         case AbstractObject.OBJECT_SENSOR:            
+            msg.setFieldInt32(NXCPCodes.VID_SENSOR_FLAGS, data.getFlags());
+            msg.setField(NXCPCodes.VID_MAC_ADDR, data.getMacAddress());
+            msg.setFieldInt32(NXCPCodes.VID_DEVICE_CLASS, data.getDeviceClass());
+            msg.setField(NXCPCodes.VID_VENDOR, data.getVendor());
+            msg.setFieldInt32(NXCPCodes.VID_COMM_PROTOCOL, data.getCommProtocol());
+            msg.setField(NXCPCodes.VID_XML_CONFIG, data.getXmlConfig());
+            msg.setField(NXCPCodes.VID_XML_REG_CONFIG, data.getXmlRegConfig());
+            msg.setField(NXCPCodes.VID_SERIAL_NUMBER, data.getSerialNumber());
+            msg.setField(NXCPCodes.VID_DEVICE_ADDRESS, data.getDeviceAddress());
+            msg.setField(NXCPCodes.VID_META_TYPE, data.getMetaType());
+            msg.setField(NXCPCodes.VID_DESCRIPTION, data.getDescription());
+            msg.setFieldInt32(NXCPCodes.VID_SENSOR_PROXY, (int)data.getSensorProxy());
+            break;
       }
 
       if (userData != null) createCustomObject(data, userData, msg);
@@ -5057,6 +5075,46 @@ public class NXCSession
       {
          msg.setField(NXCPCodes.VID_SEED_OBJECTS, data.getSeedObjectIds());
       }
+      
+      if (data.isFieldSet(NXCObjectModificationData.MAC_ADDRESS))
+      {
+         msg.setField(NXCPCodes.VID_MAC_ADDR, data.getMacAddress().getValue());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.DEVICE_CLASS))
+      {
+         msg.setFieldInt32(NXCPCodes.VID_DEVICE_CLASS, data.getDeviceClass());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.VENDOR))
+      {
+         msg.setField(NXCPCodes.VID_VENDOR, data.getVendor());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.SERIAL_NUMBER))
+      {
+         msg.setField(NXCPCodes.VID_SERIAL_NUMBER, data.getSerialNumber());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.DEVICE_ADDRESS))
+      {
+         msg.setField(NXCPCodes.VID_DEVICE_ADDRESS, data.getDeviceAddress());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.META_TYPE))
+      {
+         msg.setField(NXCPCodes.VID_META_TYPE, data.getMetaType());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.SENSOR_PROXY))
+      {
+         msg.setFieldInt32(NXCPCodes.VID_SENSOR_PROXY, (int)data.getSensorProxy());
+      }
+      
+      if (data.isFieldSet(NXCObjectModificationData.XML_CONFIG))
+      {
+         msg.setField(NXCPCodes.VID_XML_CONFIG, data.getXmlConfig());
+      }
             
       modifyCustomObject(data, userData, msg);
 
index edfa514..b27a035 100644 (file)
@@ -100,6 +100,7 @@ public abstract class AbstractObject
        public static final int OBJECT_AGENTPOLICY_LOGPARSER = 34;
    public static final int OBJECT_CHASSIS = 35;
    public static final int OBJECT_DASHBOARDGROUP = 36;
+   public static final int OBJECT_SENSOR = 37;
        public static final int OBJECT_CUSTOM = 10000;
        
        // Status calculation methods
diff --git a/src/java/client/netxms-client/src/main/java/org/netxms/client/objects/Sensor.java b/src/java/client/netxms-client/src/main/java/org/netxms/client/objects/Sensor.java
new file mode 100644 (file)
index 0000000..dd2f3c2
--- /dev/null
@@ -0,0 +1,338 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2003-2012 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.client.objects;
+
+import java.util.Date;
+import java.util.Set;
+import org.netxms.base.Logger;
+import org.netxms.base.NXCPCodes;
+import org.netxms.base.NXCPMessage;
+import org.netxms.client.MacAddress;
+import org.netxms.client.NXCSession;
+import org.netxms.client.sensor.configs.LoraWanConfig;
+import org.netxms.client.sensor.configs.LoraWanRegConfig;
+import org.netxms.client.sensor.configs.SensorConfig;
+import org.netxms.client.sensor.configs.SensorRegistrationConfig;
+
+/**
+ * Mobile device object
+ */
+public class Sensor extends DataCollectionTarget
+{
+   /**
+    * Sensor flags
+    */
+   public final static int SENSOR_PROVISIONED         = 0x00000001;
+   public final static int SENSOR_REGISTERED          = 0x00000002;
+   public final static int SENSOR_ACTIVE              = 0x00000004;
+   public final static int SENSOR_CONF_UPDATE_PENDING = 0x00000008;
+   
+   /**
+    * Sensor communication protocol type
+    */
+   public final static int  SENSOR_PROTO_UNKNOWN   = 0;
+   public final static int  COMM_LORAWAN           = 1;   
+   public final static String[] COMM_METHOD = { "Unknown", "LoRaWAN" };
+   
+   /**
+    * Sensor device class
+    */
+   public final static int  SENSOR_CLASS_UNKNOWN   = 0;
+   public final static int  SENSOR_UPS             = 1;
+   public final static int  SENSOR_WATER_METER     = 2;
+   public final static int  SENSOR_ELECTR_METER    = 3;   
+   public final static String[] DEV_CLASS_NAMES = { "Unknown", "UPS", "Water meter", "Electricity meter" };
+   
+   
+   private int flags;
+   protected MacAddress macAddress;
+       private int deviceClass;
+       private String vendor;
+       private int  commProtocol;
+       private SensorRegistrationConfig regConfig;
+   private SensorConfig config;
+       private String serialNumber;
+       private String deviceAddress;
+       private String metaType;
+       private String description;
+       private Date lastConnectionTime;
+       private int frameCount; //zero when no info
+   private int signalStrenght; //+1 when no information(cannot be +)
+   private int signalNoise; //*10 from origin number
+   private int frequency; //*10 from origin number
+   private long proxyId;
+       
+       /**
+        * Object constructor from message
+        * 
+        * @param msg
+        * @param session
+        */
+       public Sensor(NXCPMessage msg, NXCSession session)
+       {
+               super(msg, session);
+               flags = msg.getFieldAsInt32(NXCPCodes.VID_SENSOR_FLAGS);
+               macAddress = new MacAddress(msg.getFieldAsBinary(NXCPCodes.VID_MAC_ADDR));
+          deviceClass  = msg.getFieldAsInt32(NXCPCodes.VID_DEVICE_CLASS);
+          vendor = msg.getFieldAsString(NXCPCodes.VID_VENDOR);
+          commProtocol = msg.getFieldAsInt32(NXCPCodes.VID_COMM_PROTOCOL);
+          setXmlRegConfig(msg.getFieldAsString(NXCPCodes.VID_XML_REG_CONFIG));
+      setXmlConfig(msg.getFieldAsString(NXCPCodes.VID_XML_CONFIG));
+          serialNumber = msg.getFieldAsString(NXCPCodes.VID_SERIAL_NUMBER);
+          deviceAddress = msg.getFieldAsString(NXCPCodes.VID_DEVICE_ADDRESS);
+          metaType = msg.getFieldAsString(NXCPCodes.VID_META_TYPE);
+          description = msg.getFieldAsString(NXCPCodes.VID_DESCRIPTION);
+          lastConnectionTime  = msg.getFieldAsDate(NXCPCodes.VID_LAST_CONN_TIME);
+          frameCount = msg.getFieldAsInt32(NXCPCodes.VID_FRAME_COUNT); 
+          signalStrenght = msg.getFieldAsInt32(NXCPCodes.VID_SIGNAL_STRENGHT); 
+          signalNoise = msg.getFieldAsInt32(NXCPCodes.VID_SIGNAL_NOISE); 
+          frequency = msg.getFieldAsInt32(NXCPCodes.VID_FREQUENCY);
+          proxyId = msg.getFieldAsInt32(NXCPCodes.VID_SENSOR_PROXY);
+       }
+
+       /* (non-Javadoc)
+        * @see org.netxms.client.objects.AbstractObject#isAllowedOnMap()
+        */
+       @Override
+       public boolean isAllowedOnMap()
+       {
+               return true;
+       }
+
+   /* (non-Javadoc)
+    * @see org.netxms.client.objects.AbstractObject#isAlarmsVisible()
+    */
+   @Override
+   public boolean isAlarmsVisible()
+   {
+      return true;
+   }
+
+   /* (non-Javadoc)
+    * @see org.netxms.client.objects.GenericObject#getObjectClassName()
+    */
+   @Override
+   public String getObjectClassName()
+   {
+      return "Sensor";
+   }
+
+   /* (non-Javadoc)
+    * @see org.netxms.client.objects.AbstractObject#getStrings()
+    */
+   @Override
+   public Set<String> getStrings()
+   {      
+      Set<String> strings = super.getStrings();
+      addString(strings, macAddress.toString());
+      //addString(strings, DEV_CLASS_NAMES[deviceClass]);
+      addString(strings, vendor);
+      addString(strings, serialNumber);
+      addString(strings, deviceAddress);
+      addString(strings, metaType);
+      addString(strings, description);
+      return strings;
+   }
+   
+   /**
+    *  Config class creator from XML 
+    *  
+    * @param xml xml for object creation
+    */
+   private void setXmlRegConfig(String xml)
+   {
+      switch(commProtocol)
+      {
+         case COMM_LORAWAN:
+            try
+            {
+               regConfig = SensorRegistrationConfig.createFromXml(LoraWanRegConfig.class, xml);
+            }
+            catch(Exception e)
+            {
+               regConfig = new LoraWanRegConfig();
+               Logger.debug("Sensor.Sensor", "Cannot parse LoraWanConfig XML", e);
+            }
+            break;
+         default:
+            config = null;
+      }
+   }
+   
+   /**
+    *  Config class creator from XML 
+    *  
+    * @param xml xml for object creation
+    */
+   private void setXmlConfig(String xml)
+   {
+      switch(commProtocol)
+      {
+         case COMM_LORAWAN:
+            try
+            {
+               config = SensorConfig.createFromXml(LoraWanConfig.class, xml);
+            }
+            catch(Exception e)
+            {
+               config = new LoraWanConfig();
+               Logger.debug("Sensor.Sensor", "Cannot parse LoraWanConfig XML", e);
+            }
+            break;
+         default:
+            config = null;
+      }
+   }
+
+       /**
+        * @return the vendor
+        */
+       public final String getVendor()
+       {
+               return vendor;
+       }
+       /**
+        * @return the serialNumber
+        */
+       public final String getSerialNumber()
+       {
+               return serialNumber;
+       }
+
+   /**
+    * @return the flags
+    */
+   public int getFlags()
+   {
+      return flags;
+   }
+
+   /**
+    * @return the macAddress
+    */
+   public MacAddress getMacAddress()
+   {
+      return macAddress;
+   }
+
+   /**
+    * @return the frameCount
+    */
+   public int getFrameCount()
+   {
+      return frameCount;
+   }
+
+   /**
+    * @return the signalStrenght
+    */
+   public int getSignalStrenght()
+   {
+      return signalStrenght;
+   }
+
+   /**
+    * @return the frequency
+    */
+   public int getFrequency()
+   {
+      return frequency;
+   }
+
+   /**
+    * @return the deviceClass
+    */
+   public int getDeviceClass()
+   {
+      return deviceClass;
+   }
+
+   /**
+    * @return the commProtocol
+    */
+   public int getCommProtocol()
+   {
+      return commProtocol;
+   }
+
+   /**
+    * @return the deviceAddress
+    */
+   public String getDeviceAddress()
+   {
+      return deviceAddress;
+   }
+
+   /**
+    * @return the metaType
+    */
+   public String getMetaType()
+   {
+      return metaType;
+   }
+
+   /**
+    * @return the description
+    */
+   public String getDescription()
+   {
+      return description;
+   }
+
+   /**
+    * @return the lastConnectionTime
+    */
+   public Date getLastConnectionTime()
+   {
+      return lastConnectionTime;
+   }
+
+   /**
+    * @return the proxyId
+    */
+   public long getProxyId()
+   {
+      return proxyId;
+   }
+
+   /**
+    * @return the signalNoise
+    */
+   public int getSignalNoise()
+   {
+      return signalNoise;
+   }
+   
+   /**
+    * @return the regConfig
+    */
+   public SensorRegistrationConfig getRegConfig()
+   {
+      return regConfig;
+   }
+
+   /**
+    * @return the config
+    */
+   public SensorConfig getConfig()
+   {
+      return config;
+   }
+
+}
diff --git a/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanConfig.java b/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanConfig.java
new file mode 100644 (file)
index 0000000..bdd4de6
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.client.sensor.configs;
+
+import org.simpleframework.xml.Element;
+import org.simpleframework.xml.Root;
+
+/**
+ * LoRaWAN specific configuration parameters
+ */
+@Root(name="config")
+public class LoraWanConfig extends SensorConfig
+{
+   public static final int NAS = 0;
+   public static final String[] DECODER_NAMES = {"NAS"};
+   
+   @Element(required=true)
+   public int decoder;   
+}
diff --git a/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanRegConfig.java b/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/LoraWanRegConfig.java
new file mode 100644 (file)
index 0000000..6687437
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.client.sensor.configs;
+
+import org.simpleframework.xml.Element;
+import org.simpleframework.xml.Root;
+
+/**
+ * LoRaWAN specific configuration parameters
+ */
+@Root(name="config")
+public class LoraWanRegConfig extends SensorRegistrationConfig
+{
+   public static final int OTAA = 0;
+   public static final int APB = 1;
+   public static final String[] REG_OPTION_NAMES = {"OTAA","APB"};
+
+   @Element(required=true)
+   public int registrationType;
+
+   @Element(required=false)
+   public String appEUI;
+
+   @Element(required=false)
+   public String appKey;
+
+   @Element(required=false)
+   public String nwkSKey;
+
+   @Element(required=false)
+   public String appSKey;  
+   
+}
diff --git a/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorConfig.java b/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorConfig.java
new file mode 100644 (file)
index 0000000..8cbb963
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.client.sensor.configs;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import org.simpleframework.xml.Root;
+import org.simpleframework.xml.Serializer;
+import org.simpleframework.xml.core.Persister;
+
+/**
+ * Base for all sensor configurations
+ * This is class should be extended by any sensor config class
+ */
+@Root(name="config")
+public class SensorConfig
+{
+   /**
+    * Create DCI list object from XML document
+    * 
+    * @param xml XML document
+    * @return deserialized object
+    * @throws Exception if the object cannot be fully deserialized
+    */
+   public static SensorConfig createFromXml(Class<? extends SensorConfig> retClass, final String xml) throws Exception
+   {
+      Serializer serializer = new Persister();
+      return serializer.read(retClass, xml);
+   }
+   
+   /**
+    * Create XML from configuration.
+    * 
+    * @return XML document
+    * @throws Exception if the schema for the object is not valid
+    */
+   public String createXml() throws Exception
+   {
+      Serializer serializer = new Persister();
+      Writer writer = new StringWriter();
+      serializer.write(this, writer);
+      return writer.toString();
+   }
+}
diff --git a/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorRegistrationConfig.java b/src/java/client/netxms-client/src/main/java/org/netxms/client/sensor/configs/SensorRegistrationConfig.java
new file mode 100644 (file)
index 0000000..b879d05
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.client.sensor.configs;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import org.simpleframework.xml.Root;
+import org.simpleframework.xml.Serializer;
+import org.simpleframework.xml.core.Persister;
+
+/**
+ * Base for all sensor configurations
+ * This is class should be extended by any sensor config class
+ */
+@Root(name="config")
+public class SensorRegistrationConfig
+{
+   /**
+    * Create DCI list object from XML document
+    * 
+    * @param xml XML document
+    * @return deserialized object
+    * @throws Exception if the object cannot be fully deserialized
+    */
+   public static SensorRegistrationConfig createFromXml(Class<? extends SensorRegistrationConfig> retClass, final String xml) throws Exception
+   {
+      Serializer serializer = new Persister();
+      return serializer.read(retClass, xml);
+   }
+   
+   /**
+    * Create XML from configuration.
+    * 
+    * @return XML document
+    * @throws Exception if the schema for the object is not valid
+    */
+   public String createXml() throws Exception
+   {
+      Serializer serializer = new Persister();
+      Writer writer = new StringWriter();
+      serializer.write(this, writer);
+      return writer.toString();
+   }
+}
index a9e6564..a216f9c 100644 (file)
@@ -144,7 +144,7 @@ SendSMS_DialogTextPrefix=SMS to number
 SendSMS_DialogTextSuffix=\ sent successfully
 SendSMS_DialogTitle=Send SMS
 SendSMS_JobTitle=Send SMS to 
-SendSMS_SendError=Cannot send SMS to 
+SendSMS_SendError=Cannot send SMS to
 ServerClock_OptionShowText=Show &text
 ServerClock_OptionShowTimeZone=Show time &zone
 ServerClock_ServerTime=Server time is
index 1df5abe..9ac9d55 100644 (file)
                            <instanceof
                                  value="org.netxms.client.objects.MobileDevice">
                            </instanceof>
+                           <instanceof
+                                 value="org.netxms.client.objects.Sensor">
+                           </instanceof>
                         </or>
                         <test
                               forcePluginActivation="true"
index f22db98..8499387 100644 (file)
@@ -43,6 +43,7 @@ import org.netxms.client.objects.Container;
 import org.netxms.client.objects.EntireNetwork;
 import org.netxms.client.objects.MobileDevice;
 import org.netxms.client.objects.Node;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.ServiceRoot;
 import org.netxms.client.objects.Subnet;
 import org.netxms.client.objects.Zone;
@@ -102,7 +103,8 @@ public class DashboardsDynamicMenu extends ContributionItem implements IWorkbenc
                    !(object instanceof Subnet) &&
                    !(object instanceof Zone) &&
           !(object instanceof Condition) &&
-                   !(object instanceof EntireNetwork))
+                   !(object instanceof EntireNetwork) &&
+                   !(object instanceof Sensor))
                        return;
                
                List<AbstractObject> dashboards = object.getDashboards(true);
index 2003f32..36a9fa1 100644 (file)
@@ -41,6 +41,7 @@ import org.netxms.client.objects.AbstractNode;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
 import org.netxms.client.objects.MobileDevice;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.Template;
 import org.netxms.ui.eclipse.datacollection.Activator;
 import org.netxms.ui.eclipse.datacollection.Messages;
@@ -178,7 +179,7 @@ public class SelectDciDialog extends Dialog
                                        AbstractObject object = objectTree.getFirstSelectedObject2();
                                        if ((object != null) && 
                                            ((object instanceof AbstractNode) || (object instanceof MobileDevice) || (object instanceof Cluster) ||
-                                            (allowTemplateItems && (object instanceof Template))))
+                                            (allowTemplateItems && (object instanceof Template) || (object instanceof Sensor))))
                                        {
                                                dciList.setNode(object);
                                        }
index df5e326..8ab8bc5 100644 (file)
@@ -66,6 +66,7 @@ import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
 import org.netxms.client.objects.DataCollectionTarget;
 import org.netxms.client.objects.MobileDevice;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.Template;
 import org.netxms.ui.eclipse.actions.RefreshAction;
 import org.netxms.ui.eclipse.console.resources.GroupMarkers;
@@ -802,6 +803,8 @@ public class DataCollectionEditor extends ViewPart
                                        dciConfig.copyObjects(o.getObjectId(), dciList);
                                for(AbstractObject o : dlg.getSelectedObjects(MobileDevice.class))
                                        dciConfig.copyObjects(o.getObjectId(), dciList);
+            for(AbstractObject o : dlg.getSelectedObjects(Sensor.class))
+               dciConfig.copyObjects(o.getObjectId(), dciList);
                                if (doMove)
                                {
                                        for(long id : dciList)
index 7f460b4..6f14e3f 100644 (file)
                      <instanceof
                            value="org.netxms.client.objects.Subnet">
                      </instanceof>
+                     <instanceof
+                           value="org.netxms.client.objects.Sensor">
+                     </instanceof>
                   </or>
                </iterate>
             </visibleWhen>
index 91e19ab..023baab 100644 (file)
          </action>
       </objectContribution>
       <objectContribution
+            id="org.netxms.ui.eclipse.osm.actions.popup.object.Sensor"
+            objectClass="org.netxms.client.objects.Sensor">
+         <action
+               class="org.netxms.ui.eclipse.osm.actions.OpenLocationMap"
+               enablesFor="1"
+               icon="icons/map.png"
+               id="org.netxms.ui.eclipse.osm.popupActions.OpenLocationMap#Sensor"
+               label="%action.label.Geolocation"
+               menubarPath="additions">
+         </action>
+      </objectContribution>
+      <objectContribution
             id="org.netxms.ui.eclipse.osm.actions.popup.object.Rack"
             objectClass="org.netxms.client.objects.Rack">
          <action
index 6c63749..3455f71 100644 (file)
@@ -90,7 +90,8 @@ public class GeoLocationCache implements SessionListener
                                         (object.getObjectClass() == AbstractObject.OBJECT_MOBILEDEVICE) ||
                                         (object.getObjectClass() == AbstractObject.OBJECT_CLUSTER) ||
                                         (object.getObjectClass() == AbstractObject.OBJECT_CONTAINER) ||
-                                        (object.getObjectClass() == AbstractObject.OBJECT_RACK))
+                                        (object.getObjectClass() == AbstractObject.OBJECT_RACK) ||
+                                        (object.getObjectClass() == AbstractObject.OBJECT_SENSOR))
                                {
                                        GeoLocation gl = object.getGeolocation();
                                        if (gl.getType() != GeoLocation.UNSET)
@@ -114,7 +115,8 @@ public class GeoLocationCache implements SessionListener
                         (object.getObjectClass() != AbstractObject.OBJECT_MOBILEDEVICE) &&
                    (object.getObjectClass() != AbstractObject.OBJECT_CLUSTER) &&
                    (object.getObjectClass() != AbstractObject.OBJECT_CONTAINER) &&
-          (object.getObjectClass() != AbstractObject.OBJECT_RACK))
+          (object.getObjectClass() != AbstractObject.OBJECT_RACK) &&
+          (object.getObjectClass() != AbstractObject.OBJECT_SENSOR))
                        return;
                
                GeoLocation prevLocation = null;
diff --git a/src/java/netxms-eclipse/ObjectBrowser/icons/sensor.gif b/src/java/netxms-eclipse/ObjectBrowser/icons/sensor.gif
new file mode 100644 (file)
index 0000000..1132786
Binary files /dev/null and b/src/java/netxms-eclipse/ObjectBrowser/icons/sensor.gif differ
index 2432b9b..d5fe76f 100644 (file)
@@ -101,6 +101,8 @@ public class ObjectAdapter implements IWorkbenchAdapter
             return Activator.getImageDescriptor("icons/policy_root.gif"); //$NON-NLS-1$
          case AbstractObject.OBJECT_RACK:
             return Activator.getImageDescriptor("icons/rack.gif"); //$NON-NLS-1$
+         case AbstractObject.OBJECT_SENSOR:
+            return Activator.getImageDescriptor("icons/sensor.gif"); //$NON-NLS-1$
                        case AbstractObject.OBJECT_SERVICEROOT:
                                return Activator.getImageDescriptor("icons/service_root.png"); //$NON-NLS-1$
          case AbstractObject.OBJECT_SLMCHECK:
index aa17d84..d974a04 100644 (file)
@@ -79,7 +79,10 @@ public class ObjectSelectionFilterFactory
                classFilter.add(AbstractObject.OBJECT_RACK);
                classFilter.add(AbstractObject.OBJECT_NODE);
                if (allowMobileDevices)
+               {
                        classFilter.add(AbstractObject.OBJECT_MOBILEDEVICE);
+             classFilter.add(AbstractObject.OBJECT_SENSOR);
+               }
                return classFilter;
        }
 
@@ -226,7 +229,10 @@ public class ObjectSelectionFilterFactory
                classFilter.add(AbstractObject.OBJECT_TEMPLATEGROUP);
                classFilter.add(AbstractObject.OBJECT_TEMPLATE);
                if (allowMobileDevices)
+               {
                        classFilter.add(AbstractObject.OBJECT_MOBILEDEVICE);
+             classFilter.add(AbstractObject.OBJECT_SENSOR);
+               }
                return classFilter;
        }
 
index f770757..f00c2b5 100644 (file)
@@ -89,6 +89,7 @@ import org.netxms.client.objects.Node;
 import org.netxms.client.objects.PolicyGroup;
 import org.netxms.client.objects.PolicyRoot;
 import org.netxms.client.objects.Rack;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.ServiceRoot;
 import org.netxms.client.objects.Subnet;
 import org.netxms.client.objects.Template;
@@ -896,7 +897,8 @@ public class ObjectBrowser extends ViewPart
                                              (currentObject instanceof Condition) ||
                                              (currentObject instanceof Rack) ||
                           (currentObject instanceof MobileDevice) ||
-                                                  (currentObject instanceof Container)) &&
+                                                  (currentObject instanceof Container) || 
+                                                  (currentObject instanceof Sensor)) &&
                                                  ((parentObject instanceof Container) ||
                                                   (parentObject instanceof ServiceRoot)) ? APPROVE : REJECT;
                                        case TEMPLATES:
index 51ad1ac..dfffdc5 100644 (file)
@@ -127,6 +127,7 @@ public class ObjectFinder extends ViewPart
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_POLICYGROUP, "Policy Group"));
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_POLICYROOT, "Policy Root"));
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_RACK, "Rack"));
+      OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_SENSOR, "Sensor"));
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_SERVICEROOT, "Service Root"));
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_SLMCHECK, "Service Check"));
       OBJECT_CLASSES.add(new ObjectClass(AbstractObject.OBJECT_SUBNET, "Subnet"));
index 35b7a7b..b9f61d9 100644 (file)
@@ -52,9 +52,10 @@ page.name.NetSrv=Network Service
 page.name.Polling=Polling
 page.name.Rack=Rack
 page.name.Script=Script
+page.name.Sensor=Sensor
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.StatusCalc=Status Calculation
 page.name.Subnets=Subnets
 page.name.TrustedNodes=Trusted Nodes
-view.name.NodePoll=Node Poll
+view.name.NodePoll=Node Poll
\ No newline at end of file
index d48ba9f..7648aef 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=Script
+page.name.Sensor=Sensor
 page.name.StatusCalc=Status Calculation
 page.name.Subnets=Subnets
 page.name.TrustedNodes=Trusted Nodes
index 8a96104..cf4ac41 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=Skript
+page.name.Sensor=Sensor
 page.name.StatusCalc=V\u00fdpo\u010det stavu
 page.name.Subnets=Subnets
 page.name.TrustedNodes=D\u016fv\u011bryhodn\u00e9 prvky
index 4818be3..1efc6cb 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script= Skript
+page.name.Sensor=Sensor
 page.name.StatusCalc= Status Berechnung
 page.name.Subnets=Subnetze
 page.name.TrustedNodes= Vertra\u00fcnsw\u00fcrdige Knoten
index 3e4921d..23b03fa 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=Script
+page.name.Sensor=Sensor
 page.name.StatusCalc=Status Calculation
 page.name.Subnets=Subnets
 page.name.TrustedNodes=Trusted Nodes
index 17a9f10..55b374c 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script= Script
+page.name.Sensor=Sensor
 page.name.StatusCalc= Status Calculation
 page.name.Subnets=Subnets
 page.name.TrustedNodes= Trusted Nodes
index 9fa44f9..7e20eb8 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=Script
+page.name.Sensor=Sensor
 page.name.StatusCalc=C\u00e1lculo de status
 page.name.Subnets=sub-redes
 page.name.TrustedNodes=Equipamentos confi\u00e1veis
index 045c527..c3d1bfd 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=\u0421\u0442\u043e\u0439\u043a\u0430
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439
+page.name.Sensor=Sensor
 page.name.StatusCalc=\u0412\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f
 page.name.Subnets=\u041f\u043e\u0434\u0441\u0435\u0442\u0438
 page.name.TrustedNodes=\u0414\u043e\u0432\u0435\u0440\u0435\u043d\u043d\u044b\u0435 \u0443\u0437\u043b\u044b
index 311d893..12403b9 100644 (file)
@@ -53,6 +53,7 @@ page.name.Rack=Rack
 page.name.SNMP=SNMP
 page.name.SSH=SSH
 page.name.Script=Script
+page.name.Sensor=Sensor
 page.name.StatusCalc=Status Calculation
 page.name.Subnets=Subnets
 page.name.TrustedNodes=Trusted Nodes
index 03a3ef4..1ac7807 100644 (file)
                 <instanceof
                       value="org.netxms.client.objects.MobileDevice">
                 </instanceof>
+                <instanceof
+                      value="org.netxms.client.objects.Sensor">
+                </instanceof>
              </or>
           </enabledWhen>
         </page>
                  <instanceof
                        value="org.netxms.client.objects.MobileDevice">
                  </instanceof>
+                <instanceof
+                      value="org.netxms.client.objects.Sensor">
+                </instanceof>
                  <instanceof
                        value="org.netxms.client.objects.Zone">
                  </instanceof>
               </instanceof>
            </enabledWhen>
         </page>
+        <page
+              class="org.netxms.ui.eclipse.objectmanager.propertypages.SensorProperties"
+              id="org.netxms.ui.eclipse.objectmanager.propertypages.SensorProperties"
+              name="%page.name.Sensor">
+           <enabledWhen>
+              <instanceof
+                    value="org.netxms.client.objects.Sensor">
+              </instanceof>
+           </enabledWhen>
+        </page>
   </extension>
 
    <extension
                menubarPath="CreateObjectSubmenu/additions">
          </action>
          <action
+               class="org.netxms.ui.eclipse.objectmanager.actions.CreateSensor"
+               enablesFor="1"
+               id="org.netxms.ui.eclipse.objectmanager.popupActions.createSensor_Container"
+               label="Create Sensor"
+               menubarPath="CreateObjectSubmenu/additions">
+         </action>
+         <action
                class="org.netxms.ui.eclipse.objectmanager.actions.CreateContainer"
                enablesFor="1"
                id="org.netxms.ui.eclipse.objectmanager.popupActions.createContainer_Container"
                menubarPath="CreateObjectSubmenu/additions"
                state="false">
          </action>
+         <action
+               class="org.netxms.ui.eclipse.objectmanager.actions.CreateSensor"
+               enablesFor="1"
+               id="org.netxms.ui.eclipse.objectmanager.popupActions.createSensor_Container"
+               label="&amp;Sensor..."
+               menubarPath="CreateObjectSubmenu/additions">
+         </action>
       </objectContribution>
 
       <objectContribution
                menubarPath="CreateObjectSubmenu/additions">
          </action>
          <action
+               class="org.netxms.ui.eclipse.objectmanager.actions.CreateSensor"
+               enablesFor="1"
+               id="org.netxms.ui.eclipse.objectmanager.popupActions.createSensor_ServiceRoot"
+               label="Create Sensor"
+               menubarPath="CreateObjectSubmenu/additions">
+         </action>
+         <action
                class="org.netxms.ui.eclipse.objectmanager.actions.CreateContainer"
                enablesFor="1"
                id="org.netxms.ui.eclipse.objectmanager.popupActions.createContainer_ServiceRoot"
                label="%action.label.CreateChassis"
                menubarPath="CreateObjectSubmenu/additions">
          </action>
+         <action
+               class="org.netxms.ui.eclipse.objectmanager.actions.CreateSensor"
+               enablesFor="1"
+               id="org.netxms.ui.eclipse.objectmanager.popupActions.createSensor_Container"
+               label="&amp;Sensor..."
+               menubarPath="CreateObjectSubmenu/additions">
+         </action>
       </objectContribution>
 
 
       </objectContribution>
       <objectContribution
             adaptable="false"
+            id="org.netxms.ui.eclipse.objectmanager.actions.popup.object.Sensor"
+            objectClass="org.netxms.client.objects.Sensor">
+         <action
+               class="org.netxms.ui.eclipse.objectmanager.actions.BindObjectTo"
+               enablesFor="+"
+               id="org.netxms.ui.eclipse.objectmanager.popupActions.BindObjectTo_Sensor"
+               label="%action.label.BindTo"
+               menubarPath="objectbinding">
+         </action>
+      </objectContribution>
+      <objectContribution
+            adaptable="false"
             id="org.netxms.ui.eclipse.objectmanager.actions.popup.object.Rack"
             objectClass="org.netxms.client.objects.Rack">
          <action
index e6b4145..fd53957 100644 (file)
@@ -359,6 +359,27 @@ public class Messages extends NLS
    public static String RackProperties_UpdatingRackProperties;
    public static String RemoveClusterNode_JobError;
    public static String RemoveClusterNode_JobTitle;
+   public static String SensorPolling_JobError;
+   public static String SensorPolling_JobName;
+   public static String SensorWizard_General_CommMethod;
+   public static String SensorWizard_General_Description;
+   public static String SensorWizard_General_DescrLabel;
+   public static String SensorWizard_General_DeviceAddr;
+   public static String SensorWizard_General_DeviceClass;
+   public static String SensorWizard_General_MacAddr;
+   public static String SensorWizard_General_MetaType;
+   public static String SensorWizard_General_Proxy;
+   public static String SensorWizard_General_Serial;
+   public static String SensorWizard_General_Title;
+   public static String SensorWizard_General_Vendor;
+   public static String SensorWizard_LoRaWAN_Title;
+   public static String SensorWizard_LoRaWAN_Description;
+   public static String SensorWizard_LoRaWAN_Decoder;
+   public static String SensorWizard_LoRaWAN_RegType;
+   public static String SensorWizard_LoRaWAN_AppEUI;
+   public static String SensorWizard_LoRaWAN_AppKey;
+   public static String SensorWizard_LoRaWAN_AppSKey;
+   public static String SensorWizard_LoRaWAN_NwkSkey;
    public static String SetInterfaceExpStateDlg_Label;
    public static String SetInterfaceExpStateDlg_StateDown;
    public static String SetInterfaceExpStateDlg_StateIgnore;
index ba41ac4..58d2d31 100644 (file)
@@ -36,6 +36,7 @@ import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
 import org.netxms.client.objects.MobileDevice;
 import org.netxms.client.objects.Rack;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.Subnet;
 import org.netxms.ui.eclipse.jobs.ConsoleJob;
 import org.netxms.ui.eclipse.objectbrowser.dialogs.ObjectSelectionDialog;
@@ -106,7 +107,7 @@ public class BindObjectTo implements IObjectActionDelegate
                   objects = new HashSet<Long>();
                   for(Object o : ((IStructuredSelection)selection).toList())
                   {
-                     if ((o instanceof AbstractNode) || (o instanceof Subnet) || (o instanceof MobileDevice) || (o instanceof Rack) || (o instanceof Cluster))
+                     if ((o instanceof AbstractNode) || (o instanceof Subnet) || (o instanceof MobileDevice) || (o instanceof Rack) || (o instanceof Cluster) || (o instanceof Sensor))
                         objects.add(((AbstractObject)o).getObjectId());
                   }
                }
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/CreateSensor.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/actions/CreateSensor.java
new file mode 100644 (file)
index 0000000..9d45cf8
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2003-2013 Victor Kirhenshtein
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.actions;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.client.objects.Container;
+import org.netxms.client.objects.ServiceRoot;
+import org.netxms.ui.eclipse.jobs.ConsoleJob;
+import org.netxms.ui.eclipse.objectmanager.Activator;
+import org.netxms.ui.eclipse.objectmanager.wizards.CreateSensorWizard;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
+
+/**
+ * Create sensor object
+ */
+public class CreateSensor implements IObjectActionDelegate
+{
+       private IWorkbenchWindow window;
+       private IWorkbenchPart part;
+       private long parentId = -1;
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, org.eclipse.ui.IWorkbenchPart)
+        */
+       @Override
+       public void setActivePart(IAction action, IWorkbenchPart targetPart)
+       {
+               part = targetPart;
+               window = targetPart.getSite().getWorkbenchWindow();
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+        */
+       @Override
+       public void run(IAction action)
+       {
+          //Create wizard - first page with overall information and communication type, second page with communication details
+          final CreateSensorWizard creationWizard =  new CreateSensorWizard(parentId);
+          WizardDialog dlg = new WizardDialog(window.getShell(), creationWizard);
+          if (dlg.open() != Window.OK)
+         return;
+               
+               final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+               new ConsoleJob("Create Sensor", part, Activator.PLUGIN_ID, null) {
+                       @Override
+                       protected void runInternal(IProgressMonitor monitor) throws Exception
+                       {
+                               session.createObject(creationWizard.getCreationData());
+                       }
+
+                       @Override
+                       protected String getErrorMessage()
+                       {
+                               return String.format("Create sensor %s", creationWizard.getCreationData().getName());
+                       }
+               }.start();
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+        */
+       @Override
+       public void selectionChanged(IAction action, ISelection selection)
+       {
+               if ((selection instanceof IStructuredSelection) && (((IStructuredSelection)selection).size() == 1))
+               {
+                       final Object object = ((IStructuredSelection)selection).getFirstElement();
+                       if ((object instanceof Container) || (object instanceof ServiceRoot))
+                       {
+                               parentId = ((AbstractObject)object).getObjectId();
+                       }
+                       else
+                       {
+                               parentId = -1;
+                       }
+               }
+               else
+               {
+                       parentId = -1;
+               }
+
+               action.setEnabled(parentId != -1);
+       }
+}
index a5371ee..179d0fc 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Cannot remove node from cluster
 RemoveClusterNode_JobTitle=Remove cluster node
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=New expected state for selected interface(s):
 SetInterfaceExpStateDlg_StateDown=DOWN
 SetInterfaceExpStateDlg_StateIgnore=IGNORE
index a6d3083..bae1a8c 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Cannot remove node from cluster
 RemoveClusterNode_JobTitle=Remove cluster node
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=New expected state for selected interface(s):
 SetInterfaceExpStateDlg_StateDown=DOWN
 SetInterfaceExpStateDlg_StateIgnore=IGNORE
index f9f2fab..e23deb9 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Nelze odstranit prvek z clusteru
 RemoveClusterNode_JobTitle=Odstranit cluster prvek
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=Nov\u00fd po\u017eadovan\u00fd stav pro vybran\u00e9 rozhran\u00ed\:
 SetInterfaceExpStateDlg_StateDown=DOL\u016e
 SetInterfaceExpStateDlg_StateIgnore=IGNOROVAT
index f2ba6a7..975f779 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Oben nach unten
 RackProperties_UpdatingRackProperties=Rack-Eigenschaften aktualisieren %s 
 RemoveClusterNode_JobError=Knoten kann nicht von dem Kluster entfernt werden
 RemoveClusterNode_JobTitle=Klusterknoten entfernen
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=r erwarteter Zustand f\u00fcr das ausgew\u00e4hlte Interface:
 SetInterfaceExpStateDlg_StateDown=RUNTER
 SetInterfaceExpStateDlg_StateIgnore=IGNORIEREN
index c6da65e..2219357 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Cannot remove node from cluster
 RemoveClusterNode_JobTitle=Remove cluster node
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=New expected state for selected interface(s):
 SetInterfaceExpStateDlg_StateDown=DOWN
 SetInterfaceExpStateDlg_StateIgnore=IGNORE
index a6d3083..bae1a8c 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Cannot remove node from cluster
 RemoveClusterNode_JobTitle=Remove cluster node
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=New expected state for selected interface(s):
 SetInterfaceExpStateDlg_StateDown=DOWN
 SetInterfaceExpStateDlg_StateIgnore=IGNORE
index 0f9ba9f..ee8ccaa 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=De cima pra baixo
 RackProperties_UpdatingRackProperties=Atualizando propriedades do rack %s
 RemoveClusterNode_JobError=N\u00e3o \u00e9 poss\u00edvel remover o equipamento do cluster
 RemoveClusterNode_JobTitle=Remover equipamento do cluster
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=Novo estado esperado pelas interface(s) selecionadas:
 SetInterfaceExpStateDlg_StateDown=INDISPON\u00cdVEL
 SetInterfaceExpStateDlg_StateIgnore=IGNORAR
index ad43a95..bd7b147 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=\u0421\u0432\u0435\u0440\u0445\u0443 \u0432\u043d\u0438
 RackProperties_UpdatingRackProperties=\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u0438\u043d\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0441\u0442\u043e\u0439\u043a\u0438 %s
 RemoveClusterNode_JobError=\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0443\u0437\u0435\u043b \u0438\u0437 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430
 RemoveClusterNode_JobTitle=\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0443\u0437\u043b\u0430 \u0438\u0437 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0430
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=\u041d\u043e\u0432\u043e\u0435 \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 (\u043e\u0432):
 SetInterfaceExpStateDlg_StateDown=\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d
 SetInterfaceExpStateDlg_StateIgnore=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c
index c6da65e..2219357 100644 (file)
@@ -352,6 +352,27 @@ RackProperties_TopBottom=Top to bottom
 RackProperties_UpdatingRackProperties=Updating rack %s properties
 RemoveClusterNode_JobError=Cannot remove node from cluster
 RemoveClusterNode_JobTitle=Remove cluster node
+SensorPolling_JobError=Cannot update sensor polling settings
+SensorPolling_JobName=Update sensor polling settings
+SensorWizard_General_CommMethod=Communication method
+SensorWizard_General_Description=Set correct common sensor data
+SensorWizard_General_DescrLabel=Description
+SensorWizard_General_DeviceAddr=Device address
+SensorWizard_General_DeviceClass=Device class
+SensorWizard_General_MacAddr=MAC address
+SensorWizard_General_MetaType=Meta type
+SensorWizard_General_Proxy=Proxy node
+SensorWizard_General_Serial=Serial number
+SensorWizard_General_Title=General
+SensorWizard_General_Vendor=Vendor
+SensorWizard_LoRaWAN_Title=LoRaWAN
+SensorWizard_LoRaWAN_Description=Set correct data for new LoRaWAN sensor connection
+SensorWizard_LoRaWAN_Decoder=Decoder
+SensorWizard_LoRaWAN_RegType=Registration type
+SensorWizard_LoRaWAN_AppEUI=AppEUI
+SensorWizard_LoRaWAN_AppKey=AppKey
+SensorWizard_LoRaWAN_AppSKey=AppSkey
+SensorWizard_LoRaWAN_NwkSkey=NwkSkey
 SetInterfaceExpStateDlg_Label=New expected state for selected interface(s):
 SetInterfaceExpStateDlg_StateDown=DOWN
 SetInterfaceExpStateDlg_StateIgnore=IGNORE
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/propertypages/SensorProperties.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/propertypages/SensorProperties.java
new file mode 100644 (file)
index 0000000..8cf916d
--- /dev/null
@@ -0,0 +1,155 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.propertypages;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.netxms.client.NXCObjectModificationData;
+import org.netxms.client.NXCSession;
+import org.netxms.client.objects.Sensor;
+import org.netxms.ui.eclipse.jobs.ConsoleJob;
+import org.netxms.ui.eclipse.objectmanager.Activator;
+import org.netxms.ui.eclipse.objectmanager.Messages;
+import org.netxms.ui.eclipse.objectmanager.widgets.SensorCommon;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
+import org.netxms.ui.eclipse.tools.WidgetHelper;
+
+public class SensorProperties extends PropertyPage
+{
+   private Sensor sensor;
+   private SensorCommon commonData;
+   
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
+    */
+   @Override
+   protected Control createContents(Composite parent)
+   {
+      Composite dialogArea = new Composite(parent, SWT.NONE);      
+      sensor = (Sensor)getElement().getAdapter(Sensor.class);
+      
+      GridLayout layout = new GridLayout();
+      layout.verticalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.horizontalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+      dialogArea.setLayout(layout);
+      
+      commonData = new SensorCommon(dialogArea, SWT.NONE, null, sensor.getMacAddress().toString(), sensor.getDeviceClass(), sensor.getVendor(), 
+                                    sensor.getSerialNumber(), sensor.getDeviceAddress(), sensor.getMetaType(), sensor.getDescription(), 
+                                    sensor.getProxyId(), sensor.getCommProtocol());
+      
+      GridData gd = new GridData();
+      gd.verticalAlignment = SWT.TOP;
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      commonData.setLayoutData(gd);
+      
+      return dialogArea;
+   }
+   
+   /**
+    * Apply changes
+    * 
+    * @param isApply true if update operation caused by "Apply" button
+    */
+   protected boolean applyChanges(final boolean isApply)
+   {
+      if (!commonData.validate())
+         return false;
+      
+      final NXCObjectModificationData md = new NXCObjectModificationData(sensor.getObjectId());
+      md.setMacAddress(commonData.getMacAddress());
+      md.setDeviceClass(commonData.getDeviceClass());
+      md.setVendor(commonData.getVendor());
+      md.setSerialNumber(commonData.getSerial());
+      md.setDeviceAddress(commonData.getDeviceAddress());         
+      md.setMetaType(commonData.getMetaType());         
+      md.setDescription(commonData.getDescription());
+      md.setSensorProxy(commonData.getProxyNode());
+      
+      if (isApply)
+         setValid(false);
+      
+      final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+      new ConsoleJob(Messages.get().SensorPolling_JobName, null, Activator.PLUGIN_ID, null) {
+         @Override
+         protected void runInternal(IProgressMonitor monitor) throws Exception
+         {
+            session.modifyObject(md);
+         }
+
+         @Override
+         protected String getErrorMessage()
+         {
+            return Messages.get().SensorPolling_JobError;
+         }
+
+         @Override
+         protected void jobFinalize()
+         {
+            if (isApply)
+            {
+               runInUIThread(new Runnable() {
+                  @Override
+                  public void run()
+                  {
+                     SensorProperties.this.setValid(true);
+                  }
+               });
+            }
+         }
+      }.start();
+      return true;
+   }
+   
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.preference.PreferencePage#performApply()
+    */
+   @Override
+   protected void performApply()
+   {
+      applyChanges(true);
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.preference.PreferencePage#performOk()
+    */
+   @Override
+   public boolean performOk()
+   {
+      return applyChanges(false);
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
+    */
+   @Override
+   protected void performDefaults()
+   {
+      super.performDefaults();
+      commonData.updateFields("", 0, "", "", "", "", "");
+   }
+
+}
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/LoraWanWizard.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/LoraWanWizard.java
new file mode 100644 (file)
index 0000000..5428357
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.widgets;
+
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.netxms.client.sensor.configs.LoraWanConfig;
+import org.netxms.client.sensor.configs.LoraWanRegConfig;
+import org.netxms.ui.eclipse.objectmanager.Messages;
+import org.netxms.ui.eclipse.tools.WidgetHelper;
+import org.netxms.ui.eclipse.widgets.LabeledText;
+/**
+ * LoRaWAN configuration
+ */
+public class LoraWanWizard extends Composite
+{
+   private Combo comboDecoder;
+   private Combo comboRegistrationType;
+   private LabeledText field2;
+   private LabeledText field3;
+   
+   /**
+    * Common sensor changeable field constructor
+    *  
+    * @param parent
+    * @param style
+    */
+   public LoraWanWizard(Composite parent, int style, LoraWanRegConfig conf, final IWizard wizard)
+   {
+      super(parent, style); 
+      
+      GridLayout layout = new GridLayout();
+      layout.marginHeight = 0;
+      layout.marginWidth = 0;
+      setLayout(layout);
+      
+      comboDecoder = WidgetHelper.createLabeledCombo(parent, SWT.BORDER | SWT.READ_ONLY, Messages.get().SensorWizard_LoRaWAN_Decoder, 
+            WidgetHelper.DEFAULT_LAYOUT_DATA);
+      comboDecoder.setItems(LoraWanConfig.DECODER_NAMES);
+      comboDecoder.select(0);
+      
+      comboRegistrationType = WidgetHelper.createLabeledCombo(parent, SWT.BORDER | SWT.READ_ONLY, Messages.get().SensorWizard_LoRaWAN_RegType, 
+            WidgetHelper.DEFAULT_LAYOUT_DATA);
+      comboRegistrationType.setItems(LoraWanRegConfig.REG_OPTION_NAMES);
+      comboRegistrationType.select(0);
+      comboRegistrationType.addModifyListener(new ModifyListener() {
+         
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            if(comboRegistrationType.getSelectionIndex() == 0)
+            {
+               field2.setLabel(Messages.get().SensorWizard_LoRaWAN_AppEUI);
+               field3.setLabel(Messages.get().SensorWizard_LoRaWAN_AppKey);
+            }
+            else
+            {
+               field2.setLabel(Messages.get().SensorWizard_LoRaWAN_NwkSkey);
+               field3.setLabel(Messages.get().SensorWizard_LoRaWAN_AppSKey);
+            }
+            if(wizard != null)
+               wizard.getContainer().updateButtons();
+         }
+      });
+      comboRegistrationType.setEnabled(wizard != null);
+      
+      field2 = new LabeledText(parent, SWT.NONE);
+      field2.setLabel(Messages.get().SensorWizard_LoRaWAN_AppEUI);
+      field2.setText("");
+      field2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      field2.getTextControl().addModifyListener(new ModifyListener() {
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            if(wizard != null)
+               wizard.getContainer().updateButtons();
+         }
+      });
+      field2.setEditable(wizard != null);
+      
+      field3 = new LabeledText(parent, SWT.NONE);
+      field3.setLabel(Messages.get().SensorWizard_LoRaWAN_AppKey);
+      field3.setText("");
+      field3.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      field3.getTextControl().addModifyListener(new ModifyListener() {
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            if(wizard != null)
+               wizard.getContainer().updateButtons();
+         }
+      });
+      field3.setEditable(wizard != null);
+   }
+   
+   public boolean validate()
+   {
+      if(comboRegistrationType.getSelectionIndex() == 0)
+      {
+         return field2.getText().length() == 16 && field3.getText().length() == 32;
+      }
+      return field2.getText().length() == 32 && field3.getText().length() == 32;         
+   }
+   
+   public String getRegConfig()
+   {
+      LoraWanRegConfig regConf = new LoraWanRegConfig();
+      regConf.registrationType = comboRegistrationType.getSelectionIndex();
+      if(comboRegistrationType.getSelectionIndex() == 0)
+      {
+         regConf.appEUI = field2.getText();
+         regConf.appKey = field3.getText();
+      }
+      else
+      {
+         regConf.nwkSKey = field2.getText();
+         regConf.appSKey = field3.getText();
+      }
+      try
+      {
+         return regConf.createXml();
+      }
+      catch(Exception e)
+      {
+         e.printStackTrace();
+         return null;
+      }
+   }
+   
+   public String getConfig()
+   {
+      LoraWanConfig conf = new LoraWanConfig();
+      conf.decoder = comboDecoder.getSelectionIndex();
+      try
+      {
+         return conf.createXml();
+      }
+      catch(Exception e)
+      {
+         e.printStackTrace();
+         return null;
+      }
+   }
+}
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/SensorCommon.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/widgets/SensorCommon.java
new file mode 100644 (file)
index 0000000..a5b149b
--- /dev/null
@@ -0,0 +1,225 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.widgets;
+
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.netxms.client.MacAddress;
+import org.netxms.client.MacAddressFormatException;
+import org.netxms.client.objects.Node;
+import org.netxms.client.objects.Sensor;
+import org.netxms.ui.eclipse.objectbrowser.dialogs.ObjectSelectionDialog;
+import org.netxms.ui.eclipse.objectbrowser.widgets.ObjectSelector;
+import org.netxms.ui.eclipse.objectmanager.Messages;
+import org.netxms.ui.eclipse.tools.WidgetHelper;
+import org.netxms.ui.eclipse.widgets.LabeledText;
+/**
+ * Common sensor changeable fields
+ */
+public class SensorCommon extends Composite
+{
+   private LabeledText textMacAddress;
+   private Combo comboDeviceClass;
+   private LabeledText textVendor;
+   private LabeledText textSerial;
+   private LabeledText textDeviceAddress;
+   private LabeledText textMetaType;
+   private LabeledText textDescription;
+   private ObjectSelector selectorProxyNode;
+   private ModifyListener modifyListener = null;
+   
+   public SensorCommon(Composite parent, int style, IWizard wizard)
+   {
+      this(parent, style, wizard, "", 0, "", "", "", "", "",0,0);
+   }
+   
+   /**
+    * Common sensor changeable field constructor
+    *  
+    * @param parent
+    * @param style
+    */
+   public SensorCommon(Composite parent, int style, final IWizard wizard, String mac, int devClass, String vendor, String serial, String devAddress, String metaType, String desc, long proxyNodeId, int commProto)
+   {
+      super(parent, style);
+      
+      GridLayout layout = new GridLayout();
+      layout.marginHeight = 0;
+      layout.marginWidth = 0;
+      layout.numColumns = 2;
+      setLayout(layout);
+      
+      selectorProxyNode = new ObjectSelector(this, SWT.NONE, true);
+      selectorProxyNode.setLabel(Messages.get().SensorWizard_General_Proxy);
+      selectorProxyNode.setObjectClass(Node.class);
+      selectorProxyNode.setClassFilter(ObjectSelectionDialog.createNodeSelectionFilter(false));
+      selectorProxyNode.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+      if(proxyNodeId != 0)
+         selectorProxyNode.setObjectId(proxyNodeId);
+      
+      textMacAddress = new LabeledText(this, SWT.NONE);
+      textMacAddress.setLabel(Messages.get().SensorWizard_General_MacAddr);
+      textMacAddress.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      textMacAddress.setEditable(commProto != Sensor.COMM_LORAWAN);
+      
+      comboDeviceClass = WidgetHelper.createLabeledCombo(this, SWT.BORDER | SWT.READ_ONLY, Messages.get().SensorWizard_General_DeviceClass, 
+            WidgetHelper.DEFAULT_LAYOUT_DATA);
+      comboDeviceClass.setItems(Sensor.DEV_CLASS_NAMES);
+      comboDeviceClass.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      
+      textVendor = new LabeledText(this, SWT.NONE);
+      textVendor.setLabel(Messages.get().SensorWizard_General_Vendor);
+      textVendor.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      
+      textSerial = new LabeledText(this, SWT.NONE);
+      textSerial.setLabel(Messages.get().SensorWizard_General_Serial);
+      textSerial.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      
+      textDeviceAddress = new LabeledText(this, SWT.NONE);
+      textDeviceAddress.setLabel(Messages.get().SensorWizard_General_DeviceAddr);
+      textDeviceAddress.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      textDeviceAddress.setEnabled(commProto != Sensor.COMM_LORAWAN);
+      
+      textMetaType = new LabeledText(this, SWT.NONE);
+      textMetaType.setLabel(Messages.get().SensorWizard_General_MetaType);
+      textMetaType.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+      
+      textDescription = new LabeledText(this, SWT.NONE);
+      textDescription.setLabel(Messages.get().SensorWizard_General_DescrLabel);
+      textDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+      updateFields(mac, devClass, vendor, serial, devAddress, metaType, desc);
+      
+      if (wizard != null)
+      {
+         modifyListener = new ModifyListener() {         
+            @Override
+            public void modifyText(ModifyEvent e)
+            {
+               if (wizard != null)
+                  wizard.getContainer().updateButtons();
+            }
+         };
+         selectorProxyNode.addModifyListener(modifyListener);
+         textMacAddress.getTextControl().addModifyListener(modifyListener);
+         textDeviceAddress.getTextControl().addModifyListener(modifyListener);
+      }
+      
+      
+   }
+   
+   public void updateFields(String mac, int devClass, String vendor, String serial, String devAddress, String metaType, String desc)
+   {
+      textMacAddress.setText(mac);
+      comboDeviceClass.select(devClass);
+      textVendor.setText(vendor);
+      textSerial.setText(serial);
+      textDeviceAddress.setText(devAddress);
+      textMetaType.setText(metaType);
+      textDescription.setText(desc);
+      
+   }
+   
+   /**
+    * Check if all required fields are filled
+    * @return true if page is valid
+    */
+   public boolean validate()
+   {
+      return (selectorProxyNode.getObjectId() != 0)
+            && ((textMacAddress.getText().length() >= 12
+            && textMacAddress.getText().length() <= 23)
+            || (textDeviceAddress.getText().length() > 0));
+   }
+
+   /**
+    * @return the textMacAddress value as MacAddress object
+    */
+   public MacAddress getMacAddress()
+   {
+      if(textMacAddress.getText().isEmpty())
+         return null;
+      try
+      {
+         return MacAddress.parseMacAddress(textMacAddress.getText());
+      }
+      catch(MacAddressFormatException e)
+      {
+         return null;
+      }
+   }
+
+   /**
+    * @return the comboDeviceClass selection index
+    */
+   public int getDeviceClass()
+   {
+      return comboDeviceClass.getSelectionIndex();
+   }
+
+   /**
+    * @return the textVendor input text
+    */
+   public String getVendor()
+   {
+      return textVendor.getText();
+   }
+
+   /**
+    * @return the textSerial input text
+    */
+   public String getSerial()
+   {
+      return textSerial.getText();
+   }
+
+   /**
+    * @return the textDeviceAddress input text
+    */
+   public String getDeviceAddress()
+   {
+      return textDeviceAddress.getText();
+   }
+
+   /**
+    * @return the textMetaType input text
+    */
+   public String getMetaType()
+   {
+      return textMetaType.getText();
+   }
+
+   /**
+    * @return the textDescription input text
+    */
+   public String getDescription()
+   {
+      return textDescription.getText();
+   }
+   
+   public long getProxyNode()
+   {
+      return selectorProxyNode.getObjectId();
+   }
+}
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/CreateSensorWizard.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/CreateSensorWizard.java
new file mode 100644 (file)
index 0000000..31dcdc9
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.wizards;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.netxms.client.NXCObjectCreationData;
+import org.netxms.client.objects.AbstractObject;
+import org.netxms.client.objects.Sensor;
+import org.netxms.ui.eclipse.objectmanager.widgets.SensorCommon;
+import org.netxms.ui.eclipse.objectmanager.wizards.pages.SensorGeneralPage;
+import org.netxms.ui.eclipse.objectmanager.wizards.pages.SensorLoraWan;
+
+/**
+ * Sensor creation wizard
+ */
+public class CreateSensorWizard extends Wizard
+{
+   private NXCObjectCreationData cd;
+   private SensorGeneralPage first;
+   private SensorLoraWan lora;
+   
+   /**
+    * Wizard constructor. Also creates NXCObjectCreationData for new object. 
+    * 
+    * @param parentId parent object ID in object tree
+    */
+   public CreateSensorWizard(long parentId)
+   {
+      super();
+      cd = new NXCObjectCreationData(AbstractObject.OBJECT_SENSOR,  "", parentId);
+   }
+   
+   @Override
+   public String getWindowTitle() {
+       return "Create sensor";
+   }
+   
+   @Override
+   public void addPages() {
+       first = new SensorGeneralPage();
+       lora = new SensorLoraWan();
+       
+       
+       addPage(first);
+       addPage(lora);
+   }
+   
+   @Override
+   public IWizardPage getNextPage(IWizardPage currentPage) {
+      if(currentPage == first)
+      {
+         if (first.getCommMethod() == Sensor.COMM_LORAWAN) {
+            return lora;
+         }
+      }
+      return null;
+   }
+   
+   @Override
+   public boolean performFinish()
+   {
+      cd.setName(first.getObjectName());
+      cd.setCommProtocol(first.getCommMethod());
+      SensorCommon commonData =  first.getCommonData();
+      cd.setMacAddress(commonData.getMacAddress());
+      cd.setDeviceClass(commonData.getDeviceClass());
+      cd.setVendor(commonData.getVendor());
+      cd.setSerialNumber(commonData.getSerial());
+      cd.setMetaType(commonData.getMetaType());
+      cd.setDeviceAddress(commonData.getDeviceAddress());
+      cd.setDescription(commonData.getDescription());
+      cd.setSensorProxy(commonData.getProxyNode());
+      if(cd.getCommProtocol() == Sensor.COMM_LORAWAN)
+         cd.setXmlRegConfig(lora.getRegConfig());
+      if(cd.getCommProtocol() == Sensor.COMM_LORAWAN)
+         cd.setXmlConfig(lora.getConfig());
+      return true;
+   }
+
+   /**
+    * Returns filled object creation object for Sensor object
+    * @return
+    */
+   public NXCObjectCreationData getCreationData()
+   {
+      return cd;
+   }
+
+}
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorGeneralPage.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorGeneralPage.java
new file mode 100644 (file)
index 0000000..d865803
--- /dev/null
@@ -0,0 +1,164 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.wizards.pages;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.netxms.client.objects.Sensor;
+import org.netxms.ui.eclipse.objectmanager.Messages;
+import org.netxms.ui.eclipse.objectmanager.widgets.SensorCommon;
+import org.netxms.ui.eclipse.tools.WidgetHelper;
+import org.netxms.ui.eclipse.widgets.LabeledText;
+
+/**
+ * General sensor configuration wizard page
+ */
+public class SensorGeneralPage extends WizardPage 
+{
+   private Composite container;
+   private LabeledText textObjectName;
+   private Combo comboCommMethod;
+   private SensorCommon commonData;
+   
+   /**
+    * Constructor for Sensor general property page
+    */
+   public SensorGeneralPage()
+   {
+      super(Messages.get().SensorWizard_General_Title);
+      setTitle(Messages.get().SensorWizard_General_Title);
+      setDescription(Messages.get().SensorWizard_General_Description);
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+    */
+   @Override
+   public void createControl(Composite parent)
+   {
+      container = new Composite(parent, SWT.NONE);
+      
+      GridLayout layout = new GridLayout();
+      layout.verticalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.horizontalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+      container.setLayout(layout);
+      
+      textObjectName = new LabeledText(container, SWT.NONE);
+      textObjectName.setLabel(Messages.get().General_ObjectName);
+      textObjectName.setText("");
+      GridData gd = new GridData();
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      textObjectName.setLayoutData(gd);
+      textObjectName.getTextControl().addModifyListener(new ModifyListener() {
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            getWizard().getContainer().updateButtons();
+         }
+      });
+      
+      comboCommMethod = (Combo)WidgetHelper.createLabeledCombo(container, SWT.BORDER | SWT.READ_ONLY, Messages.get().SensorWizard_General_CommMethod, 
+            WidgetHelper.DEFAULT_LAYOUT_DATA);
+      comboCommMethod.setItems(Sensor.COMM_METHOD);
+      comboCommMethod.select(0);
+      gd = new GridData();
+      gd.grabExcessHorizontalSpace = false;
+      comboCommMethod.setLayoutData(gd);
+      comboCommMethod.addModifyListener(new ModifyListener() {         
+         @Override
+         public void modifyText(ModifyEvent e)
+         {
+            getWizard().getContainer().updateButtons();
+         }
+      });
+      
+      commonData = new SensorCommon(container, SWT.NONE, getWizard());
+      
+      gd = new GridData();
+      gd.verticalAlignment = SWT.TOP;
+      gd.horizontalAlignment = SWT.FILL;
+      gd.grabExcessHorizontalSpace = true;
+      commonData.setLayoutData(gd);
+
+      setControl(container); 
+      
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.wizard.WizardPage#canFlipToNextPage()
+    */
+   @Override
+   public boolean canFlipToNextPage()
+   {
+      if(!isCurrentPage())
+         return true;
+      if(textObjectName.getText().isEmpty())
+         return false;
+      
+      return commonData.validate();
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+    */
+   @Override
+   public boolean isPageComplete()
+   {
+      if(!isCurrentPage())
+         return true;
+      if(textObjectName.getText().isEmpty())
+         return false;
+      if(comboCommMethod.getSelectionIndex() != Sensor.COMM_LORAWAN)
+         return true;
+      return false;
+   }
+
+   /**
+    * @return the comboCommMethod
+    */
+   public int getCommMethod()
+   {
+      return comboCommMethod.getSelectionIndex();
+   }
+
+   /**
+    * @return the textObjectName
+    */
+   public String getObjectName()
+   {
+      return textObjectName.getText();
+   }
+
+   /**
+    * @return the commonData
+    */
+   public SensorCommon getCommonData()
+   {
+      return commonData;
+   }
+}
diff --git a/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorLoraWan.java b/src/java/netxms-eclipse/ObjectManager/src/org/netxms/ui/eclipse/objectmanager/wizards/pages/SensorLoraWan.java
new file mode 100644 (file)
index 0000000..b00a69b
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * NetXMS - open source network management system
+ * Copyright (C) 2017 Raden Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.netxms.ui.eclipse.objectmanager.wizards.pages;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.netxms.client.sensor.configs.LoraWanRegConfig;
+import org.netxms.ui.eclipse.objectmanager.Messages;
+import org.netxms.ui.eclipse.objectmanager.widgets.LoraWanWizard;
+import org.netxms.ui.eclipse.tools.WidgetHelper;
+
+/**
+ * LoRaWAN wizard page
+ */
+public class SensorLoraWan extends WizardPage 
+{
+   private Composite container;
+   private LoraWanRegConfig conf;
+   private LoraWanWizard loraWidget;
+
+   public SensorLoraWan()
+   {
+      super(Messages.get().SensorWizard_LoRaWAN_Title);
+      setTitle(Messages.get().SensorWizard_LoRaWAN_Title);
+      setDescription(Messages.get().SensorWizard_LoRaWAN_Description);
+   } 
+
+   @Override
+   public void createControl(Composite parent)
+   {
+      container = new Composite(parent, SWT.NONE); 
+      conf = new LoraWanRegConfig();
+      
+      GridLayout layout = new GridLayout();
+      layout.verticalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.horizontalSpacing = WidgetHelper.DIALOG_SPACING;
+      layout.marginWidth = 0;
+      layout.marginHeight = 0;
+      container.setLayout(layout);        
+     
+      loraWidget = new LoraWanWizard(container, SWT.NONE, conf, getWizard());
+      
+      setControl(container);   
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.wizard.WizardPage#canFlipToNextPage()
+    */
+   @Override
+   public boolean canFlipToNextPage()
+   {
+      if(!isCurrentPage())
+         return true;
+      return false; // no next page exist
+   }
+
+   /* (non-Javadoc)
+    * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+    */
+   @Override
+   public boolean isPageComplete()
+   {
+      if(!isCurrentPage())
+         return true;
+      return loraWidget.validate();
+   }
+
+   public String getRegConfig()
+   {
+      return loraWidget.getRegConfig();
+   }
+
+   public String getConfig()
+   {
+      return loraWidget.getConfig();
+   }
+}
index 9fb9128..ebe5db3 100644 (file)
@@ -195,6 +195,18 @@ public class Messages extends NLS
    public static String ObjectStatusMapView_ActionGroupNodes;
    public static String ObjectStatusMapView_ActionShowFilter;
    public static String ObjectStatusMapView_PartName;
+   public static String SensorStatus_DeviceAddress;
+   public static String SensorStatus_MacAddress;
+   public static String SensorStatus_Vendor;
+   public static String SensorStatus_DeviceClass;
+   public static String SensorStatus_CommProtocol;
+   public static String SensorStatus_SerialNumber;
+   public static String SensorStatus_MetaType;
+   public static String SensorStatus_Description;
+   public static String SensorStatus_FrameCount;
+   public static String SensorStatus_RSSI;
+   public static String SensorStatus_SNR;
+   public static String SensorStatus_Frequency;
    public static String ShowObjectDetailsView_Error;
    public static String ShowObjectDetailsView_ErrorOpeningView;
    public static String ShowSoftwareInventory_Error;
index d857df2..52f65e7 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Group nodes
 ObjectStatusMapView_ActionShowFilter=Show &filter
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Error
 ShowObjectDetailsView_ErrorOpeningView=Error opening view: %s
 ShowSoftwareInventory_Error=Error
index 46f428a..f2f7d3a 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Group nodes
 ObjectStatusMapView_ActionShowFilter=Show &filter
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Error
 ShowObjectDetailsView_ErrorOpeningView=Error opening view: %s
 ShowSoftwareInventory_Error=Error
index c35fc21..f648a8a 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Skupina prvk\u016f
 ObjectStatusMapView_ActionShowFilter=Zobrazit &filtr
 ObjectStatusMapView_PartName=Stavov\u00e1 mapa -
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Chyba
 ShowObjectDetailsView_ErrorOpeningView=Chyba p\u0159i otev\u00edr\u00e1n\u00ed\: %s
 ShowSoftwareInventory_Error=Chyba
index 447a3ef..5710e0b 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Beschreibung
 ObjectStatusMapView_ActionGroupNodes=&Gruppenknoten
 ObjectStatusMapView_ActionShowFilter=&Filter anzeigen
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Fehler
 ShowObjectDetailsView_ErrorOpeningView=Fehleransicht: %s
 ShowSoftwareInventory_Error=Fehler
index 793536c..ac6c1a1 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Group nodes
 ObjectStatusMapView_ActionShowFilter=Mostrar &filtro
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Error
 ShowObjectDetailsView_ErrorOpeningView=Error opening view: %s
 ShowSoftwareInventory_Error=Error
index 46f428a..f2f7d3a 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Group nodes
 ObjectStatusMapView_ActionShowFilter=Show &filter
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Error
 ShowObjectDetailsView_ErrorOpeningView=Error opening view: %s
 ShowSoftwareInventory_Error=Error
index 9addca8..e971e3b 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Descri\u00e7\u00e3o de Sys
 ObjectStatusMapView_ActionGroupNodes=&Grupo de equipamentos
 ObjectStatusMapView_ActionShowFilter=Exibir &filtro
 ObjectStatusMapView_PartName=Mapa de status - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Erro
 ShowObjectDetailsView_ErrorOpeningView=Erro ao abrir visualiza\u00e7\u00e3o: %s
 ShowSoftwareInventory_Error=Erro
index 5651a85..f39c0b6 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435
 ObjectStatusMapView_ActionGroupNodes=&\u0413\u0440\u0443\u043f\u043f\u0430 \u0443\u0437\u043b\u043e\u0432
 ObjectStatusMapView_ActionShowFilter=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c &\u0444\u0438\u043b\u044c\u0442\u0440
 ObjectStatusMapView_PartName=\u041a\u0430\u0440\u0442\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=\u041e\u0448\u0438\u0431\u043a\u0430
 ShowObjectDetailsView_ErrorOpeningView=\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f: %s
 ShowSoftwareInventory_Error=\u041e\u0448\u0438\u0431\u043a\u0430
index 46f428a..f2f7d3a 100644 (file)
@@ -188,6 +188,18 @@ NodesTab_ColSysDescr=Sys Description
 ObjectStatusMapView_ActionGroupNodes=&Group nodes
 ObjectStatusMapView_ActionShowFilter=Show &filter
 ObjectStatusMapView_PartName=Status Map - %s
+SensorStatus_DeviceAddress=Device address
+SensorStatus_MacAddress=MAC address
+SensorStatus_Vendor=Vendor
+SensorStatus_DeviceClass=Device class
+SensorStatus_CommProtocol=Communication protocol
+SensorStatus_SerialNumber=Serial number
+SensorStatus_MetaType=Meta type
+SensorStatus_Description=Description
+SensorStatus_FrameCount=Frame count
+SensorStatus_RSSI=Received signal strength indicator
+SensorStatus_SNR=Signal-to-noise ratio
+SensorStatus_Frequency=Frequency
 ShowObjectDetailsView_Error=Error
 ShowObjectDetailsView_ErrorOpeningView=Error opening view: %s
 ShowSoftwareInventory_Error=Error
index cbf1fba..67f2f3a 100644 (file)
@@ -31,6 +31,7 @@ import org.netxms.client.objects.MobileDevice;
 import org.netxms.client.objects.Node;
 import org.netxms.client.objects.NodeLink;
 import org.netxms.client.objects.Rack;
+import org.netxms.client.objects.Sensor;
 import org.netxms.client.objects.ServiceCheck;
 import org.netxms.client.objects.ServiceContainer;
 import org.netxms.client.objects.Subnet;
@@ -187,6 +188,26 @@ public class GeneralInfo extends TableElement
                                if (md.getBatteryLevel() >= 0)
                                        addPair(Messages.get().GeneralInfo_BatteryLevel, Integer.toString(md.getBatteryLevel()) + "%"); //$NON-NLS-1$
                                break;
+         case AbstractObject.OBJECT_SENSOR:
+            Sensor sensor = (Sensor)object;
+            addPair(Messages.get().SensorStatus_DeviceAddress, sensor.getDeviceAddress(), false);
+            if(sensor.getMacAddress() != null && !sensor.getMacAddress().isNull())
+               addPair(Messages.get().SensorStatus_MacAddress, sensor.getMacAddress().toString(), true);
+            addPair(Messages.get().SensorStatus_Vendor, sensor.getVendor(), true);            
+            addPair(Messages.get().SensorStatus_DeviceClass, Sensor.DEV_CLASS_NAMES[sensor.getDeviceClass()]);
+            addPair(Messages.get().SensorStatus_CommProtocol, Sensor.COMM_METHOD[sensor.getCommProtocol()]);
+            addPair(Messages.get().SensorStatus_SerialNumber, sensor.getSerialNumber(), true);
+            addPair(Messages.get().SensorStatus_MetaType, sensor.getMetaType(), true);
+            addPair(Messages.get().SensorStatus_Description, sensor.getDescription(), true);
+            if(sensor.getFrameCount() != 0)
+               addPair(Messages.get().SensorStatus_FrameCount, Integer.toString(sensor.getFrameCount()));
+            if(sensor.getSignalStrenght() != 1)
+               addPair(Messages.get().SensorStatus_RSSI, Integer.toString(sensor.getSignalStrenght()));
+            if(sensor.getSignalNoise() != Integer.MAX_VALUE)
+               addPair(Messages.get().SensorStatus_SNR, Double.toString((double)sensor.getSignalNoise()/10));
+            if(sensor.getFrequency() != 0)
+               addPair(Messages.get().SensorStatus_Frequency, Double.toString((double)sensor.getFrequency()/10));
+            break;
                        case AbstractObject.OBJECT_ACCESSPOINT:
                                AccessPoint ap = (AccessPoint)object;
             addPair(Messages.get().GeneralInfo_State, ap.getState().toString());
index 686d541..52ed994 100644 (file)
                      <instanceof
                            value="org.netxms.client.objects.MobileDevice">
                      </instanceof>
+                     <instanceof
+                           value="org.netxms.client.objects.Sensor">
+                     </instanceof>
                   </or>
                </iterate>
             </visibleWhen>
index 47a6703..307038c 100644 (file)
@@ -45,6 +45,8 @@ import org.netxms.client.datacollection.PerfTabDci;
 import org.netxms.client.objects.AbstractNode;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
+import org.netxms.client.objects.MobileDevice;
+import org.netxms.client.objects.Sensor;
 import org.netxms.ui.eclipse.console.resources.SharedColors;
 import org.netxms.ui.eclipse.objectview.objecttabs.ObjectTab;
 import org.netxms.ui.eclipse.perfview.Activator;
@@ -261,7 +263,7 @@ public class PerformanceTab extends ObjectTab
        @Override
        public boolean showForObject(AbstractObject object)
        {
-               return (object instanceof AbstractNode || object instanceof Cluster);
+               return (object instanceof AbstractNode || object instanceof Cluster || object instanceof MobileDevice || object instanceof Sensor);
        }
 
    /* (non-Javadoc)
index a2cccbe..ee12db4 100644 (file)
@@ -45,6 +45,7 @@ import org.netxms.client.objects.AbstractNode;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
 import org.netxms.client.objects.MobileDevice;
+import org.netxms.client.objects.Sensor;
 import org.netxms.ui.eclipse.actions.ExportToCsvAction;
 import org.netxms.ui.eclipse.actions.RefreshAction;
 import org.netxms.ui.eclipse.jobs.ConsoleJob;
@@ -95,7 +96,7 @@ public class HistoricalDataView extends ViewPart
                
                nodeId = Long.parseLong(parts[0]);
                AbstractObject object = session.findObjectById(nodeId);
-               if ((object == null) || (!(object instanceof AbstractNode) && !(object instanceof MobileDevice) && !(object instanceof Cluster)))
+               if ((object == null) || (!(object instanceof AbstractNode) && !(object instanceof MobileDevice) && !(object instanceof Cluster) && !(object instanceof Sensor)))
                        throw new PartInitException(Messages.get().HistoricalDataView_InvalidObjectID);
                nodeName = object.getObjectName();
                
index e021858..eda8d85 100644 (file)
@@ -34,6 +34,7 @@ import org.netxms.client.objects.AbstractNode;
 import org.netxms.client.objects.AbstractObject;
 import org.netxms.client.objects.Cluster;
 import org.netxms.client.objects.MobileDevice;
+import org.netxms.client.objects.Sensor;
 import org.netxms.ui.eclipse.actions.ExportToCsvAction;
 import org.netxms.ui.eclipse.actions.RefreshAction;
 import org.netxms.ui.eclipse.perfview.Messages;
@@ -70,7 +71,7 @@ public class TableLastValuesView extends ViewPart
                
                objectId = Long.parseLong(parts[0]);
                AbstractObject object = session.findObjectById(objectId);
-               if ((object == null) || (!(object instanceof AbstractNode) && !(object instanceof Cluster) && !(object instanceof MobileDevice)))
+               if ((object == null) || (!(object instanceof AbstractNode) && !(object instanceof Cluster) && !(object instanceof MobileDevice)  && !(object instanceof Sensor)))
                        throw new PartInitException(Messages.get().TableLastValuesView_InvalidObjectID);
                
                dciId = Long.parseLong(parts[1]);
index f058d6f..de94863 100644 (file)
@@ -2,7 +2,7 @@ SOURCES = array.cpp base64.cpp bytestream.cpp cc_mb.cpp cc_ucs2.cpp \
           cc_ucs4.cpp cc_utf8.cpp cch.cpp config.cpp crypto.cpp diff.cpp \
          dirw_unix.c geolocation.cpp getopt.c dload.cpp hash.cpp \
          hashmapbase.cpp ice.c icmp.cpp icmp6.cpp iconv.cpp inet_pton.c \
-         inetaddr.cpp log.cpp lz4.c main.cpp md5.cpp message.cpp \
+         inetaddr.cpp log.cpp lz4.c main.cpp macaddr.cpp md5.cpp message.cpp \
          msgrecv.cpp msgwq.cpp net.cpp nxcp.cpp pa.cpp parisc_atomic.cpp \
           qsort.c queue.cpp rbuffer.cpp rwlock.cpp scandir.c serial.cpp \
          sha1.cpp sha2.cpp solaris9_atomic.c spoll.cpp streamcomp.cpp \
index 9336952..60f7082 100644 (file)
@@ -318,7 +318,7 @@ bool InetAddress::sameSubnet(const InetAddress &a) const
 
 /**
  * Check if two inet addresses are equal
- * This method ignores mask bits and only compares addresses. 
+ * This method ignores mask bits and only compares addresses.
  * To compare two address/mask pairs use InetAddress::compareTo.
  */
 bool InetAddress::equals(const InetAddress &a) const
@@ -667,4 +667,3 @@ void InetAddressList::fillMessage(NXCPMessage *msg, UINT32 sizeFieldId, UINT32 b
       msg->setField(fieldId++, *m_list->get(i));
    }
 }
-
diff --git a/src/libnetxms/macaddr.cpp b/src/libnetxms/macaddr.cpp
new file mode 100644 (file)
index 0000000..6b5280f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+** NetXMS - Network Management System
+** Utility Library
+** Copyright (C) 2003-2017 Raden Solutions
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU Lesser General Public License as published
+** by the Free Software Foundation; either version 3 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: macaddr.cpp
+**
+**/
+
+#include "libnetxms.h"
+#include <nxcpapi.h>
+
+/**
+ * Returns true if it is the multicast address
+ */
+bool MacAddress::isMulticast() const
+{
+   return (m_length == 6) ? !(m_value[0] & 0x01) > 0 : false;
+}
+
+/**
+ * Returns true if it is the broadcast address
+ */
+bool MacAddress::isBroadcast() const
+{
+   return (m_length == 6) ? !memcmp(m_value, "\xFF\xFF\xFF\xFF\xFF\xFF", 6) : false;
+}
+
+/**
+ * Returns true if addrese are equals
+ */
+bool MacAddress::equals(const MacAddress &a) const
+{
+   if(a.length() == m_length)
+   {
+      return !memcmp(m_value, a.value(), m_length);
+   }
+   return false;
+}
+
+/**
+ * Returns string representaiton of mac address
+ */
+TCHAR *MacAddress::toString(TCHAR *buffer, MacAddressNotation notation) const
+{
+   switch(notation)
+   {
+      case MAC_ADDR_FLAT_STRING:
+         BinToStr(m_value, m_length, buffer);
+         break;
+      case MAC_ADDR_COLON_SEPARATED:
+         toStringInternal(buffer, _T(':'));
+         break;
+      case MAC_ADDR_BYTEPAIR_COLON_SEPARATED:
+         toStringInternal(buffer, _T(':'), true);
+         break;
+      case MAC_ADDR_HYPHEN_SEPARATED:
+         toStringInternal(buffer, _T('-'));
+         break;
+      case MAC_ADDR_DOT_SEPARATED:
+         toStringInternal(buffer, _T('.'));
+         break;
+      case MAC_ADDR_BYTEPAIR_DOT_SEPARATED:
+         toStringInternal(buffer, _T('.'), true);
+         break;
+   }
+   return buffer;
+}
+
+/**
+ * Internal method to string
+ */
+TCHAR *MacAddress::toStringInternal(TCHAR *buffer, const TCHAR separator, bool bytePair) const
+{
+   TCHAR *curr = buffer;
+
+   for(int i = 0; i < m_length; i++)
+   {
+      *curr++ = bin2hex(m_value[i] >> 4);
+      *curr++ = bin2hex(m_value[i] & 15);
+      if(!bytePair || (i % 2 == 0))
+         *curr++ = separator;
+   }
+   *(curr - 1) = 0;
+   return buffer;
+}
+
+/**
+ * Returns string representaiton of mac address
+ */
+String MacAddress::toString(MacAddressNotation notation) const
+{
+   if (m_length == 0)
+      return String();
+
+   int stringSize;
+   switch(notation)
+   {
+      case MAC_ADDR_FLAT_STRING:
+         stringSize = m_length * 2;
+         break;
+      case MAC_ADDR_COLON_SEPARATED:
+      case MAC_ADDR_HYPHEN_SEPARATED:
+      case MAC_ADDR_DOT_SEPARATED:
+         stringSize = m_length * 2 + m_length/2; //-1 separator +1 for
+         break;
+      case MAC_ADDR_BYTEPAIR_DOT_SEPARATED:
+      case MAC_ADDR_BYTEPAIR_COLON_SEPARATED:
+         stringSize = m_length * 2 + m_length/2; //-1 separator +1 for
+         break;
+   }
+   TCHAR *buf = (TCHAR *)malloc(stringSize * sizeof(TCHAR));
+   String str(toString(buf, notation));
+   free(buf);
+   return str;
+}
+
+/**
+ * Parse string as MAC address
+ */
+MacAddress MacAddress::parse(const char *str)
+{
+   if (str == NULL || strlen(str) > 16)
+      return MacAddress();
+
+   BYTE buffer[16];
+   size_t size = StrToBinA(str, buffer, strlen(str));
+
+   return MacAddress(buffer, size);
+}
+
+/**
+ * Parse string as MAC address (WCHAR version)
+ */
+MacAddress MacAddress::parse(const WCHAR *str)
+{
+   char mb[256];
+   WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, str, -1, mb, 256, NULL, NULL);
+   return parse(mb);
+}
index 37eab2b..7fdf0fc 100644 (file)
@@ -592,6 +592,19 @@ InetAddress NXCPMessage::getFieldAsInetAddress(UINT32 fieldId) const
    return InetAddress();
 }
 
+/*
+ * Get field as MAC address
+ */
+MacAddress NXCPMessage::getFieldAsMacAddress(UINT32 fieldId) const
+{
+   NXCP_MESSAGE_FIELD *f = find(fieldId);
+
+   if (f == NULL || !((f->type == NXCP_DT_BINARY) && (f->df_binary.length <= 8)))
+      return MacAddress();
+
+   return MacAddress(f->df_binary.value, f->df_binary.length);
+}
+
 /**
  * Get string field
  * If buffer is NULL, memory block of required size will be allocated
diff --git a/src/libnxmb/.settings/language.settings.xml b/src/libnxmb/.settings/language.settings.xml
new file mode 100644 (file)
index 0000000..a57c21b
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+       <configuration id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.245750645" name="Build (GNU)">
+               <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+                       <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+                       <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+                       <provider copy-of="extension" id="org.eclipse.cdt.autotools.core.LibtoolGCCBuildCommandParser"/>
+                       <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="1424788779437872117" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+                               <language-scope id="org.eclipse.cdt.core.gcc"/>
+                               <language-scope id="org.eclipse.cdt.core.g++"/>
+                       </provider>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+               </extension>
+       </configuration>
+</project>
index ab40183..32721d6 100644 (file)
@@ -29,7 +29,7 @@ libnxcore_la_SOURCES =  accesspoint.cpp acl.cpp actions.cpp addrlist.cpp \
                        nxslext.cpp objects.cpp objtools.cpp package.cpp \
                        pds.cpp poll.cpp ps.cpp rack.cpp radius.cpp \
                        reporting.cpp rootobj.cpp schedule.cpp script.cpp \
-                       session.cpp slmcheck.cpp smclp.cpp \
+                       sensor.cpp session.cpp slmcheck.cpp smclp.cpp \
                        sms.cpp snmp.cpp snmptrap.cpp stp.cpp subnet.cpp summary_email.cpp \
                        svccontainer.cpp swpkg.cpp syncer.cpp syslogd.cpp \
                        template.cpp tools.cpp tracert.cpp tunnel.cpp \
index a0a28c6..f614de4 100644 (file)
@@ -341,7 +341,8 @@ UINT32 ConditionObject::modifyFromMessageInternal(NXCPMessage *pRequest)
             {
                if ((pObject->getObjectClass() == OBJECT_NODE) ||
                    (pObject->getObjectClass() == OBJECT_CLUSTER) ||
-                   (pObject->getObjectClass() == OBJECT_MOBILEDEVICE))
+                   (pObject->getObjectClass() == OBJECT_MOBILEDEVICE) ||
+                   (pObject->getObjectClass() == OBJECT_SENSOR))
                {
                   ((DataCollectionTarget *)pObject)->updateDCItemCacheSize(m_dciList[i].id, m_id);
                }
index 8558fe2..36e1fd5 100644 (file)
@@ -86,6 +86,8 @@ static void *GetItemData(DataCollectionTarget *dcTarget, DCItem *pItem, TCHAR *p
          case DS_NATIVE_AGENT:
                           if (dcTarget->getObjectClass() == OBJECT_NODE)
                    *error = ((Node *)dcTarget)->getItemFromAgent(pItem->getName(), MAX_LINE_SIZE, pBuffer);
+                          else if (dcTarget->getObjectClass() == OBJECT_SENSOR)
+               *error = ((Sensor *)dcTarget)->getItemFromAgent(pItem->getName(), MAX_LINE_SIZE, pBuffer);
                           else
                                   *error = DCE_NOT_SUPPORTED;
             break;
@@ -403,6 +405,7 @@ static THREAD_RESULT THREAD_CALL ItemPoller(void *pArg)
                g_idxClusterById.forEach(QueueItems, &watchdogId);
                g_idxMobileDeviceById.forEach(QueueItems, &watchdogId);
       g_idxChassisById.forEach(QueueItems, &watchdogId);
+               g_idxSensorById.forEach(QueueItems, &watchdogId);
 
       // Save last poll time
       dwTimingHistory[currPos] = (UINT32)(GetCurrentTimeMs() - qwStart);
index 61cd467..571ffda 100644 (file)
@@ -403,8 +403,8 @@ Table *QuerySummaryTable(LONG tableId, SummaryTable *adHocDefinition, UINT32 bas
    for(int i = 0; i < childObjects->size(); i++)
    {
       NetObj *obj = childObjects->get(i);
-      if (((obj->getObjectClass() != OBJECT_NODE) && (obj->getObjectClass() != OBJECT_MOBILEDEVICE)) ||
-          !obj->checkAccessRights(userId, OBJECT_ACCESS_READ))
+      if (((obj->getObjectClass() != OBJECT_NODE) && (obj->getObjectClass() != OBJECT_MOBILEDEVICE) &&
+          (obj->getObjectClass() != OBJECT_SENSOR)) || !obj->checkAccessRights(userId, OBJECT_ACCESS_READ))
       {
          obj->decRefCount();
          continue;
index e8953df..5d52a9c 100644 (file)
@@ -253,6 +253,7 @@ static THREAD_RESULT THREAD_CALL HouseKeeper(void *pArg)
                g_idxNodeById.forEach(CleanDciData, hdb);
                g_idxClusterById.forEach(CleanDciData, hdb);
                g_idxMobileDeviceById.forEach(CleanDciData, hdb);
+               g_idxSensorById.forEach(CleanDciData, hdb);
 
                DBConnectionPoolReleaseConnection(hdb);
 
index 323280d..5447fca 100644 (file)
@@ -827,7 +827,7 @@ void NetObj::calculateCompoundStatus(BOOL bForcedRecalc)
 
    int mostCriticalAlarm = GetMostCriticalStatusForObject(m_id);
    int mostCriticalDCI =
-      (getObjectClass() == OBJECT_NODE || getObjectClass() == OBJECT_MOBILEDEVICE || getObjectClass() == OBJECT_CLUSTER || getObjectClass() == OBJECT_ACCESSPOINT) ?
+      (getObjectClass() == OBJECT_NODE || getObjectClass() == OBJECT_MOBILEDEVICE || getObjectClass() == OBJECT_CLUSTER || getObjectClass() == OBJECT_ACCESSPOINT || getObjectClass() == OBJECT_SENSOR) ?
          ((DataCollectionTarget *)this)->getMostCriticalDCIStatus() : STATUS_UNKNOWN;
 
    int oldStatus = m_status;
index 1c24858..4507871 100644 (file)
@@ -52,6 +52,7 @@ ObjectIndex g_idxConditionById;
 ObjectIndex g_idxServiceCheckById;
 ObjectIndex g_idxNetMapById;
 ObjectIndex g_idxChassisById;
+ObjectIndex g_idxSensorById;
 
 /**
  * Static data
@@ -161,6 +162,7 @@ static THREAD_RESULT THREAD_CALL CacheLoadingThread(void *pArg)
        UpdateDataCollectionCache(&g_idxMobileDeviceById);
        UpdateDataCollectionCache(&g_idxAccessPointById);
    UpdateDataCollectionCache(&g_idxChassisById);
+   UpdateDataCollectionCache(&g_idxSensorById);
 
    DbgPrintf(1, _T("Finished caching of DCI values"));
    return THREAD_OK;
@@ -250,7 +252,7 @@ void NetObjInsert(NetObj *pObject, bool newObject, bool importedObject)
    {
       // Assign unique ID to new object
       pObject->setId(CreateUniqueId(IDG_NETWORK_OBJECT));
-      if (!importedObject) // imported objects already have valid GUID
+      if (!importedObject && pObject->getGuid().isNull()) // imported objects already have valid GUID
          pObject->generateGuid();
 
       // Create tables for storing data collection values
@@ -429,6 +431,9 @@ void NetObjInsert(NetObj *pObject, bool newObject, bool importedObject)
                        case OBJECT_NETWORKMAP:
                                g_idxNetMapById.put(pObject->getId(), pObject);
             break;
+                       case OBJECT_SENSOR:
+                               g_idxSensorById.put(pObject->getId(), pObject);
+            break;
          default:
                                {
                                        bool processed = false;
@@ -594,6 +599,9 @@ void NetObjDeleteFromIndexes(NetObj *pObject)
                case OBJECT_NETWORKMAP:
                        g_idxNetMapById.remove(pObject->getId());
          break;
+               case OBJECT_SENSOR:
+                       g_idxSensorById.remove(pObject->getId());
+         break;
       default:
                        {
                                bool processed = false;
@@ -1021,6 +1029,9 @@ NetObj NXCORE_EXPORTABLE *FindObject(bool (* comparator)(NetObj *, void *), void
       case OBJECT_ZONE:
          index = &g_idxZoneByGUID;
          break;
+      case OBJECT_SENSOR:
+         index = &g_idxSensorById;
+         break;
       default:
          index = &g_idxObjectById;
          break;
@@ -1385,6 +1396,29 @@ BOOL LoadObjects()
       DBFreeResult(hResult);
    }
 
+   // Load mobile devices
+   DbgPrintf(2, _T("Loading sensors..."));
+   hResult = DBSelect(hdb, _T("SELECT id FROM sensors"));
+   if (hResult != NULL)
+   {
+      int count = DBGetNumRows(hResult);
+      for(int i = 0; i < count; i++)
+      {
+         UINT32 id = DBGetFieldULong(hResult, i, 0);
+         Sensor *sensor = new Sensor;
+         if (sensor->loadFromDatabase(hdb, id))
+         {
+            NetObjInsert(sensor, false, false);  // Insert into indexes
+         }
+         else     // Object load failed
+         {
+            delete sensor;
+            nxlog_write(MSG_MOBILEDEVICE_LOAD_FAILED, NXLOG_ERROR, "d", id);
+         }
+      }
+      DBFreeResult(hResult);
+   }
+
    // Load nodes
    DbgPrintf(2, _T("Loading nodes..."));
    hResult = DBSelect(hdb, _T("SELECT id FROM nodes"));
@@ -1992,7 +2026,8 @@ bool IsValidParentClass(int childClass, int parentClass)
              (childClass == OBJECT_MOBILEDEVICE) ||
              (childClass == OBJECT_NODE) ||
              (childClass == OBJECT_RACK) ||
-             (childClass == OBJECT_SUBNET))
+             (childClass == OBJECT_SUBNET) ||
+             (childClass == OBJECT_SENSOR))
             return true;
          break;
       case OBJECT_CHASSIS:
@@ -2009,7 +2044,8 @@ bool IsValidParentClass(int childClass, int parentClass)
       case OBJECT_TEMPLATE:
          if ((childClass == OBJECT_NODE) ||
              (childClass == OBJECT_CLUSTER) ||
-             (childClass == OBJECT_MOBILEDEVICE))
+             (childClass == OBJECT_MOBILEDEVICE) ||
+             (childClass == OBJECT_SENSOR))
             return true;
          break;
       case OBJECT_NETWORKMAPROOT:
@@ -2208,7 +2244,8 @@ bool IsEventSource(int objectClass)
        return (objectClass == OBJECT_NODE) ||
               (objectClass == OBJECT_CONTAINER) ||
               (objectClass == OBJECT_CLUSTER) ||
-                        (objectClass == OBJECT_MOBILEDEVICE);
+                        (objectClass == OBJECT_MOBILEDEVICE ||
+          (objectClass == OBJECT_SENSOR));
 }
 
 /**
@@ -2268,6 +2305,9 @@ bool NXCORE_EXPORTABLE CreateObjectAccessSnapshot(UINT32 userId, int objClass)
       case OBJECT_ZONE:
          index = &g_idxZoneByGUID;
          break;
+      case OBJECT_SENSOR:
+         index = &g_idxSensorById;
+         break;
       default:
          index = &g_idxObjectById;
          break;
index 929ae30..9ef6e37 100644 (file)
@@ -558,6 +558,23 @@ static void QueueForPolling(NetObj *object, void *data)
 
        switch(object->getObjectClass())
        {
+          case OBJECT_SENSOR:
+         {
+            Sensor *sensor = (Sensor *)object;
+            if (sensor->isReadyForStatusPoll())
+            {
+               sensor->lockForStatusPoll();
+               DbgPrintf(2, _T("Sensor %d \"%s\" queued for status poll"), (int)sensor->getId(), sensor->getName());
+               ThreadPoolExecute(g_pollerThreadPool, sensor, &Sensor::statusPoll, RegisterPoller(POLLER_TYPE_STATUS, sensor));
+            }
+            if (sensor->isReadyForConfigurationPoll())
+            {
+               sensor->lockForConfigurationPoll();
+               DbgPrintf(2, _T("Sensor %d \"%s\" queued for configuration poll"), (int)sensor->getId(), sensor->getName());
+               ThreadPoolExecute(g_pollerThreadPool, sensor, &Sensor::configurationPoll, RegisterPoller(POLLER_TYPE_CONFIGURATION, sensor));
+            }
+            break;
+         }
                case OBJECT_NODE:
                        {
                                Node *node = (Node *)object;
diff --git a/src/server/core/sensor.cpp b/src/server/core/sensor.cpp
new file mode 100644 (file)
index 0000000..72c5f4e
--- /dev/null
@@ -0,0 +1,795 @@
+/*
+** NetXMS - Network Management System
+** Copyright (C) 2003-2016 Victor Kirhenshtein
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: sensor.cpp
+**/
+
+#include "nxcore.h"
+
+/**
+ * Default empty Sensor class constructior
+ */
+Sensor::Sensor() : DataCollectionTarget()
+{
+   m_proxyNodeId = 0;
+       m_flags = 0;
+       m_deviceClass = SENSOR_CLASS_UNKNOWN;
+       m_vendor = NULL;
+       m_commProtocol = SENSOR_PROTO_UNKNOWN;
+       m_xmlRegConfig = NULL;
+       m_xmlConfig = NULL;
+       m_serialNumber = NULL;
+       m_deviceAddress = NULL;
+       m_metaType = NULL;
+       m_description = NULL;
+       m_lastConnectionTime = 0;
+       m_frameCount = 0; //zero when no info
+   m_signalStrenght = 1; //+1 when no information(cannot be +)
+   m_signalNoise = MAX_INT32; //*10 from origin number
+   m_frequency = 0; //*10 from origin number
+   m_lastStatusPoll = 0;
+   m_lastConfigurationPoll = 0;
+   m_dwDynamicFlags = 0;
+   m_hPollerMutex = MutexCreate();
+   m_hAgentAccessMutex = MutexCreate();
+   m_status = STATUS_UNKNOWN;
+   m_proxyAgentConn = NULL;
+}
+
+/**
+ * Constructor with all fields for Sensor class
+ */
+Sensor::Sensor(TCHAR *name, UINT32 flags, MacAddress macAddress, UINT32 deviceClass, TCHAR *vendor,
+               UINT32 commProtocol, TCHAR *xmlRegConfig, TCHAR *xmlConfig, TCHAR *serialNumber, TCHAR *deviceAddress,
+               TCHAR *metaType, TCHAR *description, UINT32 proxyNode) : DataCollectionTarget(name)
+{
+   m_flags = flags;
+   m_macAddress = macAddress;
+       m_deviceClass = deviceClass;
+       m_vendor = vendor;
+       m_commProtocol = commProtocol;
+       m_xmlRegConfig = xmlRegConfig;
+       m_xmlConfig = xmlConfig;
+       m_serialNumber = serialNumber;
+       m_deviceAddress = deviceAddress;
+       m_metaType = metaType;
+       m_description = description;
+       m_proxyNodeId = proxyNode;
+   m_lastConnectionTime = 0;
+       m_frameCount = 0; //zero when no info
+   m_signalStrenght = 1; //+1 when no information(cannot be +)
+   m_signalNoise = MAX_INT32; //*10 from origin number
+   m_frequency = 0; //*10 from origin number
+   m_lastStatusPoll = 0;
+   m_lastConfigurationPoll = 0;
+   m_dwDynamicFlags = 0;
+   m_hPollerMutex = MutexCreate();
+   m_hAgentAccessMutex = MutexCreate();
+   m_status = STATUS_UNKNOWN;
+   m_proxyAgentConn = NULL;
+}
+
+Sensor *Sensor::createSensor(TCHAR *name, NXCPMessage *request)
+{
+   Sensor *sensor = new Sensor(name,
+                          request->getFieldAsUInt32(VID_SENSOR_FLAGS),
+                          request->getFieldAsMacAddress(VID_MAC_ADDR),
+                          request->getFieldAsUInt32(VID_DEVICE_CLASS),
+                          request->getFieldAsString(VID_VENDOR),
+                          request->getFieldAsUInt32(VID_COMM_PROTOCOL),
+                          request->getFieldAsString(VID_XML_REG_CONFIG),
+                          request->getFieldAsString(VID_XML_CONFIG),
+                          request->getFieldAsString(VID_SERIAL_NUMBER),
+                          request->getFieldAsString(VID_DEVICE_ADDRESS),
+                          request->getFieldAsString(VID_META_TYPE),
+                          request->getFieldAsString(VID_DESCRIPTION),
+                          request->getFieldAsUInt32(VID_SENSOR_PROXY));
+
+   switch(request->getFieldAsUInt32(VID_COMM_PROTOCOL))
+   {
+      case COMM_LORAWAN:
+         sensor->generateGuid();
+         if (registerLoraDevice(sensor) == NULL)
+         {
+            delete sensor;
+            return NULL;
+         }
+         break;
+   }
+   return sensor;
+}
+
+/**
+ * Create agent connection
+ */
+UINT32 Sensor::connectToAgent()
+{
+   UINT32 rcc = ERR_CONNECT_FAILED;
+   if (IsShutdownInProgress())
+      return rcc;
+
+   NetObj *proxy = FindObjectById(m_proxyNodeId, OBJECT_NODE);
+   if(proxy == NULL)
+      return ERR_INVALID_OBJECT;
+
+   // Create new agent connection object if needed
+   if (m_proxyAgentConn == NULL)
+   {
+      m_proxyAgentConn = ((Node *)proxy)->createAgentConnection();
+      if (m_proxyAgentConn != NULL)
+      {
+         nxlog_debug(7, _T("Sensor::connectToAgent(%s [%d]): new agent connection created"), m_name, m_id);
+         rcc = ERR_SUCCESS;
+      }
+   }
+   else if (m_proxyAgentConn->nop() == ERR_SUCCESS)
+   {
+      nxlog_debug(7, _T("Sensor::connectToAgent(%s [%d]): already connected"), m_name, m_id);
+      rcc = ERR_SUCCESS;
+   }
+   else
+   {
+      deleteAgentConnection();
+      connectToAgent(); // retry to connect
+   }
+
+   return rcc;
+}
+
+/**
+ * Delete agent connection
+ */
+void Sensor::deleteAgentConnection()
+{
+   if (m_proxyAgentConn != NULL)
+   {
+      m_proxyAgentConn->decRefCount();
+      m_proxyAgentConn = NULL;
+   }
+}
+
+/**
+ * Register LoRa WAN device
+ */
+Sensor *Sensor::registerLoraDevice(Sensor *sensor)
+{
+   sensor->agentLock();
+   UINT32 rcc = sensor->connectToAgent();
+   if(rcc == ERR_INVALID_OBJECT)
+   {
+      sensor->agentUnlock();
+      return NULL;
+   }
+   else if (rcc == ERR_CONNECT_FAILED)
+   {
+      sensor->agentUnlock();
+      return sensor; //Unprovisoned sensor - will try to provison it on next connect
+   }
+
+   Config regConfig;
+   char regXml[MAX_CONFIG_VALUE];
+   WideCharToMultiByte(CP_UTF8, 0, sensor->getXmlRegConfig(), -1, regXml, MAX_CONFIG_VALUE, NULL, NULL);
+   regConfig.loadXmlConfigFromMemory(regXml, MAX_CONFIG_VALUE, NULL, "config", false);
+
+   Config config;
+   char xml[MAX_CONFIG_VALUE];
+   WideCharToMultiByte(CP_UTF8, 0, sensor->getXmlConfig(), -1, xml, MAX_CONFIG_VALUE, NULL, NULL);
+   config.loadXmlConfigFromMemory(xml, MAX_CONFIG_VALUE, NULL, "config", false);
+
+   NXCPMessage msg(sensor->getAgentConnection()->getProtocolVersion());
+   msg.setCode(CMD_REGISTER_LORAWAN_SENSOR);
+   msg.setId(sensor->getAgentConnection()->generateRequestId());
+   msg.setField(VID_DEVICE_ADDRESS, sensor->getDeviceAddress());
+   msg.setField(VID_MAC_ADDR, sensor->getMacAddress());
+   msg.setField(VID_GUID, sensor->getGuid());
+   msg.setField(VID_DECODER, config.getValueAsInt(_T("/decoder"), 0));
+   msg.setField(VID_REG_TYPE, regConfig.getValueAsInt(_T("/registrationType"), 0));
+   if(regConfig.getValueAsInt(_T("/registrationType"), 0) == 0)
+   {
+      msg.setField(VID_LORA_APP_EUI, regConfig.getValue(_T("/appEUI")));
+      msg.setField(VID_LORA_APP_KEY, regConfig.getValue(_T("/appKey")));
+   }
+   else
+   {
+      msg.setField(VID_LORA_APP_S_KEY, regConfig.getValue(_T("/appSKey")));
+      msg.setField(VID_LORA_NWK_S_KWY, regConfig.getValue(_T("/nwkSKey")));
+   }
+   NXCPMessage *response = sensor->getAgentConnection()->customRequest(&msg);
+   if (response != NULL)
+   {
+      if(response->getFieldAsUInt32(VID_RCC) == RCC_SUCCESS)
+         sensor->setProvisoned();
+
+      delete response;
+   }
+   sensor->agentUnlock();
+
+   return sensor;
+}
+
+//set correct status calculation function
+//set correct configuration poll - provision if possible, for lora get device name, get all possible DCI's, try to do provisionning
+//set status poll - check if connection is on if not generate alarm, check, that proxy is up and running
+
+/**
+ * Sensor class destructor
+ */
+Sensor::~Sensor()
+{
+   free(m_vendor);
+   free(m_xmlRegConfig);
+   free(m_xmlConfig);
+   free(m_serialNumber);
+   free(m_deviceAddress);
+   free(m_metaType);
+   free(m_description);
+   MutexDestroy(m_hPollerMutex);
+   MutexDestroy(m_hAgentAccessMutex);
+   if (m_proxyAgentConn != NULL)
+      m_proxyAgentConn->decRefCount();
+}
+
+/**
+ * Load from database SensorDevice class
+ */
+bool Sensor::loadFromDatabase(DB_HANDLE hdb, UINT32 id)
+{
+   m_id = id;
+
+   if (!loadCommonProperties(hdb))
+   {
+      nxlog_debug(2, _T("Cannot load common properties for sensor object %d"), id);
+      return false;
+   }
+
+       TCHAR query[512];
+       _sntprintf(query, 512, _T("SELECT flags,mac_address,device_class,vendor,communication_protocol,xml_config,serial_number,device_address,")
+                          _T("meta_type,description,last_connection_time,frame_count,signal_strenght,signal_noise,frequency,proxy_node,xml_reg_config,runtime_flags FROM sensors WHERE id=%d"), m_id);
+       DB_RESULT hResult = DBSelect(hdb, query);
+       if (hResult == NULL)
+               return false;
+
+   m_flags = DBGetFieldULong(hResult, 0, 0);
+   m_macAddress = DBGetFieldMacAddr(hResult, 0, 1);
+       m_deviceClass = DBGetFieldULong(hResult, 0, 2);
+       m_vendor = DBGetField(hResult, 0, 3, NULL, 0);
+       m_commProtocol = DBGetFieldULong(hResult, 0, 4);
+       m_xmlConfig = DBGetField(hResult, 0, 5, NULL, 0);
+       m_serialNumber = DBGetField(hResult, 0, 6, NULL, 0);
+       m_deviceAddress = DBGetField(hResult, 0, 7, NULL, 0);
+       m_metaType = DBGetField(hResult, 0, 8, NULL, 0);
+       m_description = DBGetField(hResult, 0, 9, NULL, 0);
+   m_lastConnectionTime = DBGetFieldULong(hResult, 0, 10);
+   m_frameCount = DBGetFieldULong(hResult, 0, 11);
+   m_signalStrenght = DBGetFieldLong(hResult, 0, 12);
+   m_signalNoise = DBGetFieldLong(hResult, 0, 13);
+   m_frequency = DBGetFieldLong(hResult, 0, 14);
+   m_proxyNodeId = DBGetFieldLong(hResult, 0, 15);
+   m_xmlRegConfig = DBGetField(hResult, 0, 16, NULL, 0);
+   m_dwDynamicFlags = DBGetFieldULong(hResult, 0, 17);
+   m_dwDynamicFlags &= NDF_PERSISTENT; // Clear out all non-persistent runtime flags
+
+   // Load DCI and access list
+   loadACLFromDB(hdb);
+   loadItemsFromDB(hdb);
+   for(int i = 0; i < m_dcObjects->size(); i++)
+      if (!m_dcObjects->get(i)->loadThresholdsFromDB(hdb))
+         return false;
+
+   return true;
+}
+
+/**
+ * Save to database Sensor class
+ */
+BOOL Sensor::saveToDatabase(DB_HANDLE hdb)
+{
+   lockProperties();
+
+   BOOL success = saveCommonProperties(hdb);
+
+   if(success)
+   {
+      DB_STATEMENT hStmt;
+      bool isNew = !(IsDatabaseRecordExist(hdb, _T("sensors"), _T("id"), m_id));
+      if (isNew)
+         hStmt = DBPrepare(hdb, _T("INSERT INTO sensors (flags,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,runtime_flags,id,xml_reg_config) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
+      else
+         hStmt = DBPrepare(hdb, _T("UPDATE sensors SET flags=?,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=?,runtime_flags=? WHERE id=?"));
+      if (hStmt != NULL)
+      {
+         DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_flags);
+         DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, m_macAddress);
+         DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)m_deviceClass);
+         DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, m_vendor, DB_BIND_STATIC);
+         DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)m_commProtocol);
+         DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, m_xmlConfig, DB_BIND_STATIC);
+         DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, m_serialNumber, DB_BIND_STATIC);
+         DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, m_deviceAddress, DB_BIND_STATIC);
+         DBBind(hStmt, 9, DB_SQLTYPE_VARCHAR, m_metaType, DB_BIND_STATIC);
+         DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, m_description, DB_BIND_STATIC);
+         DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, (UINT32)m_lastConnectionTime);
+         DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, m_frameCount);
+         DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, m_signalStrenght);
+         DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, m_signalNoise);
+         DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_frequency);
+         DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_proxyNodeId);
+         DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, m_dwDynamicFlags);
+         DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, m_id);
+         if(isNew)
+            DBBind(hStmt, 19, DB_SQLTYPE_VARCHAR, m_xmlRegConfig, DB_BIND_STATIC);
+
+         success = DBExecute(hStmt);
+
+         DBFreeStatement(hStmt);
+      }
+      else
+      {
+         success = FALSE;
+      }
+       }
+
+   // Save data collection items
+   if (success)
+   {
+               lockDciAccess(false);
+      for(int i = 0; i < m_dcObjects->size(); i++)
+         m_dcObjects->get(i)->saveToDatabase(hdb);
+               unlockDciAccess();
+   }
+
+   // Save access list
+   if(success)
+      saveACLToDB(hdb);
+
+   // Clear modifications flag and unlock object
+       if (success)
+               m_isModified = FALSE;
+   unlockProperties();
+
+   return success;
+}
+
+/**
+ * Delete from database
+ */
+bool Sensor::deleteFromDatabase(DB_HANDLE hdb)
+{
+   bool success = DataCollectionTarget::deleteFromDatabase(hdb);
+   if (success)
+      success = executeQueryOnObject(hdb, _T("DELETE FROM sensors WHERE id=?"));
+   return success;
+}
+
+
+NXSL_Value *Sensor::createNXSLObject()
+{
+   return new NXSL_Value(new NXSL_Object(&g_nxslMobileDeviceClass, this));
+}
+
+/**
+ * Sensor class serialization to json
+ */
+json_t *Sensor::toJson()
+{
+   json_t *root = DataCollectionTarget::toJson();
+   json_object_set_new(root, "flags", json_integer(m_flags));
+   json_object_set_new(root, "macAddr", json_string_t(m_macAddress.toString(MAC_ADDR_FLAT_STRING)));
+   json_object_set_new(root, "deviceClass", json_integer(m_deviceClass));
+   json_object_set_new(root, "vendor", json_string_t(m_vendor));
+   json_object_set_new(root, "commProtocol", json_integer(m_commProtocol));
+   json_object_set_new(root, "xmlConfig", json_string_t(m_xmlConfig));
+   json_object_set_new(root, "serialNumber", json_string_t(m_serialNumber));
+   json_object_set_new(root, "deviceAddress", json_string_t(m_deviceAddress));
+   json_object_set_new(root, "metaType", json_string_t(m_metaType));
+   json_object_set_new(root, "description", json_string_t(m_description));
+   json_object_set_new(root, "proxyNode", json_integer(m_proxyNodeId));
+   return root;
+}
+
+void Sensor::fillMessageInternal(NXCPMessage *msg)
+{
+   DataCollectionTarget::fillMessageInternal(msg);
+   msg->setField(VID_SENSOR_FLAGS, m_flags);
+       msg->setField(VID_MAC_ADDR, m_macAddress);
+   msg->setField(VID_DEVICE_CLASS, m_deviceClass);
+       msg->setField(VID_VENDOR, CHECK_NULL_EX(m_vendor));
+   msg->setField(VID_COMM_PROTOCOL, m_commProtocol);
+       msg->setField(VID_XML_CONFIG, CHECK_NULL_EX(m_xmlConfig));
+       msg->setField(VID_XML_REG_CONFIG, CHECK_NULL_EX(m_xmlRegConfig));
+       msg->setField(VID_SERIAL_NUMBER, CHECK_NULL_EX(m_serialNumber));
+       msg->setField(VID_DEVICE_ADDRESS, CHECK_NULL_EX(m_deviceAddress));
+       msg->setField(VID_META_TYPE, CHECK_NULL_EX(m_metaType));
+       msg->setField(VID_DESCRIPTION, CHECK_NULL_EX(m_description));
+       msg->setFieldFromTime(VID_LAST_CONN_TIME, m_lastConnectionTime);
+       msg->setField(VID_FRAME_COUNT, m_frameCount);
+       msg->setField(VID_SIGNAL_STRENGHT, m_signalStrenght);
+       msg->setField(VID_SIGNAL_NOISE, m_signalNoise);
+       msg->setField(VID_FREQUENCY, m_frequency);
+       msg->setField(VID_SENSOR_PROXY, m_proxyNodeId);
+}
+
+UINT32 Sensor::modifyFromMessageInternal(NXCPMessage *request)
+{
+   if (request->isFieldExist(VID_MAC_ADDR))
+      m_macAddress = request->getFieldAsMacAddress(VID_MAC_ADDR);
+   if (request->isFieldExist(VID_VENDOR))
+   {
+      free(m_vendor);
+      m_vendor = request->getFieldAsString(VID_VENDOR);
+   }
+   if (request->isFieldExist(VID_DEVICE_CLASS))
+      m_deviceClass = request->getFieldAsUInt32(VID_DEVICE_CLASS);
+   if (request->isFieldExist(VID_SERIAL_NUMBER))
+   {
+      free(m_serialNumber);
+      m_serialNumber = request->getFieldAsString(VID_SERIAL_NUMBER);
+   }
+   if (request->isFieldExist(VID_DEVICE_ADDRESS))
+   {
+      free(m_deviceAddress);
+      m_deviceAddress = request->getFieldAsString(VID_DEVICE_ADDRESS);
+   }
+   if (request->isFieldExist(VID_META_TYPE))
+   {
+      free(m_metaType);
+      m_metaType = request->getFieldAsString(VID_META_TYPE);
+   }
+   if (request->isFieldExist(VID_DESCRIPTION))
+   {
+      free(m_description);
+      m_description = request->getFieldAsString(VID_DESCRIPTION);
+   }
+   if (request->isFieldExist(VID_SENSOR_PROXY))
+      m_proxyNodeId = request->getFieldAsUInt32(VID_SENSOR_PROXY);
+   if (request->isFieldExist(VID_XML_CONFIG))
+   {
+      free(m_xmlConfig);
+      m_xmlConfig = request->getFieldAsString(VID_XML_CONFIG);
+   }
+
+   return DataCollectionTarget::modifyFromMessageInternal(request);
+}
+
+/**
+ * Calculate sensor status
+ */
+void Sensor::calculateStatus()
+{
+   if (m_flags == 0 || m_flags == SENSOR_PROVISIONED)
+      m_status = STATUS_UNKNOWN;
+   else if (m_flags & SENSOR_ACTIVE)
+      m_status = STATUS_NORMAL;
+   else if (m_flags & ~SENSOR_ACTIVE)
+      m_status = STATUS_CRITICAL;
+}
+
+/**
+ * Entry point for configuration poller
+ */
+void Sensor::configurationPoll(PollerInfo *poller)
+{
+   poller->startExecution();
+   ObjectTransactionStart();
+   configurationPoll(NULL, 0, poller, 0);
+   ObjectTransactionEnd();
+   delete poller;
+}
+
+/**
+ * Perform configuration poll on node
+ */
+void Sensor::configurationPoll(ClientSession *pSession, UINT32 dwRqId, PollerInfo *poller, int maskBits)
+{
+   if (m_dwDynamicFlags & NDF_DELETE_IN_PROGRESS)
+   {
+      if (dwRqId == 0)
+         m_dwDynamicFlags &= ~NDF_QUEUED_FOR_CONFIG_POLL;
+      return;
+   }
+
+   poller->setStatus(_T("wait for lock"));
+   pollerLock();
+
+   if (IsShutdownInProgress())
+   {
+      pollerUnlock();
+      return;
+   }
+
+   nxlog_debug(5, _T("Starting configuration poll for sensor %s (ID: %d), m_flags: %d"), m_name, m_id, m_flags);
+
+   bool hasChanges = false;
+
+   if (!(m_flags & SENSOR_PROVISIONED))
+   {
+      if ((registerLoraDevice(this) != NULL) && (m_flags & SENSOR_PROVISIONED))
+      {
+         nxlog_debug(6, _T("ConfPoll(%s [%d}): sensor successfully registered"), m_name, m_id);
+         hasChanges = true;
+      }
+   }
+   if ((m_flags & SENSOR_PROVISIONED) && (m_deviceAddress == NULL))
+   {
+      getItemFromAgent(_T("LoraWAN.DevAddr(*)"), 0, m_deviceAddress);
+      if (m_deviceAddress != NULL)
+      {
+         nxlog_debug(6, _T("ConfPoll(%s [%d}): sensor DevAddr[%s] successfully obtained"), m_name, m_id, m_deviceAddress);
+         hasChanges = true;
+      }
+   }
+
+   poller->setStatus(_T("cleanup"));
+   if (dwRqId == 0)
+      m_dwDynamicFlags &= ~NDF_QUEUED_FOR_CONFIG_POLL;
+   m_dwDynamicFlags &= ~NDF_RECHECK_CAPABILITIES;
+   m_lastConfigurationPoll = time(NULL);
+
+   nxlog_debug(5, _T("Finished configuration poll for sensor %s (ID: %d)"), m_name, m_id);
+   pollerUnlock();
+
+   if (hasChanges)
+   {
+      lockProperties();
+      setModified();
+      unlockProperties();
+   }
+}
+
+/**
+ * Status poller entry point
+ */
+void Sensor::statusPoll(PollerInfo *poller)
+{
+   poller->startExecution();
+   statusPoll(NULL, 0, poller);
+
+   delete poller;
+}
+
+/**
+ * Perform status poll on sensor
+ */
+void Sensor::statusPoll(ClientSession *pSession, UINT32 dwRqId, PollerInfo *poller)
+{
+   if (m_dwDynamicFlags & NDF_DELETE_IN_PROGRESS)
+   {
+      if (dwRqId == 0)
+         m_dwDynamicFlags &= ~NDF_QUEUED_FOR_STATUS_POLL;
+      return;
+   }
+
+   Queue *pQueue = new Queue;     // Delayed event queue
+
+   poller->setStatus(_T("wait for lock"));
+   pollerLock();
+
+   if (IsShutdownInProgress())
+   {
+      delete pQueue;
+      pollerUnlock();
+      return;
+   }
+
+   sendPollerMsg(dwRqId, _T("Starting status poll for sensor %s\r\n"), m_name);
+   nxlog_debug(5, _T("Starting status poll for sensor %s (ID: %d)"), m_name, m_id);
+
+   nxlog_debug(6, _T("StatusPoll(%s): checking agent"), m_name);
+   poller->setStatus(_T("check agent"));
+   sendPollerMsg(dwRqId, _T("Checking NetXMS agent connectivity\r\n"));
+
+   agentLock();
+   UINT32 rcc = connectToAgent();
+   if (rcc == ERR_SUCCESS)
+   {
+      nxlog_debug(6, _T("StatusPoll(%s): connected to agent"), m_name);
+      if (m_dwDynamicFlags & NDF_AGENT_UNREACHABLE)
+      {
+         m_dwDynamicFlags &= ~NDF_AGENT_UNREACHABLE;
+         sendPollerMsg(dwRqId, POLLER_INFO _T("Connectivity with NetXMS agent restored\r\n"));
+      }
+   }
+   else
+   {
+      nxlog_debug(6, _T("StatusPoll(%s): agent unreachable, rcc: %d"), m_name, rcc);
+      sendPollerMsg(dwRqId, POLLER_ERROR _T("NetXMS agent unreachable\r\n"));
+      if (!(m_dwDynamicFlags & NDF_AGENT_UNREACHABLE))
+         m_dwDynamicFlags |= NDF_AGENT_UNREACHABLE;
+   }
+   agentUnlock();
+   nxlog_debug(6, _T("StatusPoll(%s): agent check finished"), m_name);
+
+   switch(m_commProtocol)
+   {
+      case COMM_LORAWAN:
+         if (m_flags & SENSOR_PROVISIONED)
+         {
+            TCHAR lastValue[MAX_DCI_STRING_VALUE] = { 0 };
+            time_t now;
+            getItemFromAgent(_T("LoraWAN.LastContact(*)"), MAX_DCI_STRING_VALUE, lastValue);
+            time_t lastConnectionTime = _tcstol(lastValue, NULL, 0);
+            if (lastConnectionTime != 0)
+            {
+               m_lastConnectionTime = lastConnectionTime;
+               nxlog_debug(6, _T("StatusPoll(%s [%d}): Last connection time updated - %d"), m_name, m_id, m_lastConnectionTime);
+            }
+
+            now = time(NULL);
+
+            if (!(m_flags & SENSOR_REGISTERED))
+            {
+               if (m_lastConnectionTime > 0)
+               {
+                  m_flags |= SENSOR_REGISTERED;
+                  nxlog_debug(6, _T("StatusPoll(%s [%d}): Status set to REGISTERED"), m_name, m_id);
+               }
+            }
+            if (m_flags & SENSOR_REGISTERED)
+            {
+               if (now - m_lastConnectionTime > 3600) // Last contact > 1h
+               {
+                  m_flags &= ~SENSOR_ACTIVE;
+                  nxlog_debug(6, _T("StatusPoll(%s [%d}): Inactive for over 1h, status set to INACTIVE"), m_name, m_id);
+               }
+               else
+               {
+                  m_flags |= SENSOR_ACTIVE;
+                  nxlog_debug(6, _T("StatusPoll(%s [%d}): Status set to ACTIVE"), m_name, m_id);
+                  getItemFromAgent(_T("LoraWAN.RSSI(*)"), MAX_DCI_STRING_VALUE, lastValue);
+                  m_signalStrenght = _tcstod(lastValue, NULL);
+                  getItemFromAgent(_T("LoraWAN.SNR(*)"), MAX_DCI_STRING_VALUE, lastValue);
+                  m_signalNoise = (_tcstod(lastValue, NULL)*10);
+                  getItemFromAgent(_T("LoraWAN.Frequency(*)"), MAX_DCI_STRING_VALUE, lastValue);
+                  m_frequency = (_tcstod(lastValue, NULL)*10);
+               }
+            }
+
+            lockProperties();
+            calculateStatus();
+            setModified();
+            unlockProperties();
+         }