windows performance counters with average value for n samples can be configured direc...
authorVictor Kirhenshtein <victor@netxms.org>
Wed, 27 Mar 2013 20:08:06 +0000 (20:08 +0000)
committerVictor Kirhenshtein <victor@netxms.org>
Wed, 27 Mar 2013 20:08:06 +0000 (20:08 +0000)
19 files changed:
ChangeLog
include/netxmsdb.h
include/nms_agent.h
include/nms_cscp.h
sql/schema.in
src/agent/subagents/winperf/collect.cpp
src/agent/subagents/winperf/tools.cpp
src/agent/subagents/winperf/winperf.cpp
src/agent/subagents/winperf/winperf.h
src/java/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/java/netxms-client/src/main/java/org/netxms/client/datacollection/DataCollectionItem.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/propertypages/General.java
src/server/core/Makefile.am
src/server/core/datacoll.cpp
src/server/core/dc_nxsl.cpp [new file with mode: 0644]
src/server/core/dcitem.cpp
src/server/core/nxcore.vcproj
src/server/include/nms_dcoll.h
src/server/tools/nxdbmgr/upgrade.cpp

index c0a5ae4..e9f5726 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,7 @@
 - Simplified configuration of DCIs based on Windows performance counters
 - Basic software inventory
 - Network discovery improved
+- Configurable default DCI retention time and polling interval
 - New NXSL functions: CreateNode
 - New NXSL operator @ - safe get object's attribute
 - New MIBs added: ASTARO-MIB, CPQHOST-MIB, CPQPOWER-MIB
index c0cd812..cf9c2bd 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   273
+#define DB_FORMAT_VERSION   274
 
 #endif
index eaea3fa..5e33c8d 100644 (file)
@@ -1,6 +1,6 @@
 /* 
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2012 Victor Kirhenshtein
+** 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 Lesser General Public License as published by
 #define COMMAND_TIMEOUT          60
 #define MAX_SUBAGENT_NAME        64
 
-
-//
-// Agent policy types
-//
-
+/**
+ * Agent policy types
+ */
 #define AGENT_POLICY_CONFIG      1
 #define AGENT_POLICY_LOG_PARSER  2
 
-
-//
-// Error codes
-//
-
+/**
+ * Error codes
+ */
 #define ERR_SUCCESS                 ((DWORD)0)
 #define ERR_UNKNOWN_COMMAND         ((DWORD)400)
 #define ERR_AUTH_REQUIRED           ((DWORD)401)
 #define ERR_MEM_ALLOC_FAILED        ((DWORD)917)
 #define ERR_FILE_DELETE_FAILED      ((DWORD)918)
 
-
-//
-// Parameter handler return codes
-//
-
+/**
+ * Parameter handler return codes
+ */
 #define SYSINFO_RC_SUCCESS       0
 #define SYSINFO_RC_UNSUPPORTED   1
 #define SYSINFO_RC_ERROR         2
 
+/**
+ * WinPerf features
+ */
+#define WINPERF_AUTOMATIC_SAMPLE_COUNT    ((DWORD)0x00000001)
+#define WINPERF_REMOTE_COUNTER_CONFIG     ((DWORD)0x00000002)
 
-//
-// Descriptions for common parameters
-//
-
+/**
+ * Descriptions for common parameters
+ */
 #define DCIDESC_FS_AVAIL                          _T("Available space on file system {instance}")
 #define DCIDESC_FS_AVAILPERC                      _T("Percentage of available space on file system {instance}")
 #define DCIDESC_FS_FREE                           _T("Free space on file system {instance}")
index 29e83f3..7aa3814 100644 (file)
@@ -906,6 +906,7 @@ typedef struct
 #define VID_INSTD_FILTER            ((DWORD)437)
 #define VID_ACCURACY                ((DWORD)438)
 #define VID_GEOLOCATION_TIMESTAMP   ((DWORD)439)
+#define VID_SAMPLE_COUNT            ((DWORD)440)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((DWORD)0x00800000)
index 5442508..9c4f7f8 100644 (file)
@@ -574,6 +574,7 @@ CREATE TABLE items
        instd_method integer not null,
        instd_data varchar(255) null,
        instd_filter SQL_TEXT null,
+       samples integer not null,
        PRIMARY KEY(item_id)
 ) TABLE_TYPE;
 
index 908b20a..b9009a8 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** Windows Performance NetXMS subagent
-** Copyright (C) 2004-2012 Victor Kirhenshtein
+** Copyright (C) 2004-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
 
 #include "winperf.h"
 
+/**
+ * Counter sets
+ */
+WinPerfCounterSet *m_cntSet[3] =
+{
+       new WinPerfCounterSet(1000, _T('A')),
+       new WinPerfCounterSet(5000, _T('B')),
+       new WinPerfCounterSet(6000, _T('C'))
+};
 
-//
-// Static data
-//
+/**
+ * Counter set constructor
+ */
+WinPerfCounterSet::WinPerfCounterSet(DWORD interval, TCHAR cls)
+{
+       m_mutex = MutexCreate();
+       m_changeCondition = ConditionCreate(TRUE);
+       m_interval = interval;
+       m_class = cls;
+       m_counters = new ObjectArray<WINPERF_COUNTER>(32, 32, true);
+}
 
-WINPERF_COUNTER_SET m_cntSet[3] =
+/**
+ * Counter set destructor
+ */
+WinPerfCounterSet::~WinPerfCounterSet()
 {
-   { 1000, 0, NULL, _T('A') },
-   { 5000, 0, NULL, _T('B') },
-   { 60000, 0, NULL, _T('C') }
-};
+       MutexDestroy(m_mutex);
+       ConditionDestroy(m_changeCondition);
+       delete m_counters;
+}
+
+/**
+ * Add counter to set
+ */
+void WinPerfCounterSet::addCounter(WINPERF_COUNTER *c)
+{
+       MutexLock(m_mutex);
+       m_counters->add(c);
+       MutexUnlock(m_mutex);
+       ConditionSet(m_changeCondition);
+}
+
+/**
+ * Collector thread
+ */
+void WinPerfCounterSet::collectorThread()
+{
+   HQUERY hQuery;
+   PDH_STATUS rc;
+   PDH_STATISTICS statData;
+   TCHAR szFName[] = _T("CollectorThread_X");
 
+   szFName[16] = m_class;
+
+       // Outer loop - rebuild query after set changes
+       while(1)
+       {
+               AgentWriteDebugLog(2, _T("WINPERF: %s waiting for set change"), szFName);
 
-//
-// Check that counter's name is valid and determine counter's type
-//
+               HANDLE handles[2];
+               handles[0] = g_hCondShutdown;
+               handles[1] = m_changeCondition;
+      DWORD waitStatus = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+               if (waitStatus == WAIT_OBJECT_0)
+         break;   // Shutdown condition
+               if (waitStatus != WAIT_OBJECT_0 + 1)
+                       continue;       // set not changed, continue waiting
 
+               ConditionReset(m_changeCondition);
+
+               AgentWriteDebugLog(2, _T("WINPERF: %s: set changed"), szFName);
+               if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
+               {
+                       ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
+                       continue;
+               }
+
+               // Add counters to query
+          DWORD dwOKCounters = 0;
+               MutexLock(m_mutex);
+               for(int i = 0; i < m_counters->size(); i++)
+               {
+                       WINPERF_COUNTER *counter = m_counters->get(i);
+                       if ((rc = PdhAddCounter(hQuery, counter->pszName, 0, &counter->handle)) != ERROR_SUCCESS)
+                       {
+                               TCHAR szBuffer[1024];
+
+                               AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("WINPERF: %s: Unable to add counter \"%s\" to query (%s)"), 
+                                             szFName, counter->pszName, GetPdhErrorText(rc, szBuffer, 1024));
+                               counter->handle = NULL;
+                       }
+                       else
+                       {
+                               dwOKCounters++;
+                       }
+               }
+               MutexUnlock(m_mutex);
+
+                       // Check if we was able to add at least one counter
+               if (dwOKCounters == 0)
+               {
+                       PdhCloseQuery(hQuery);
+                       AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("WINPERF: Failed to add any counter to query for counter set %c"), m_class);
+                       continue;
+               }
+
+               // Collection loop (until shutdown or set change)
+               AgentWriteDebugLog(2, _T("WINPERF: %s entered data collection loop"), szFName);
+               while(1)
+               {
+                       HANDLE handles[2];
+                       handles[0] = g_hCondShutdown;
+                       handles[1] = m_changeCondition;
+                       waitStatus = WaitForMultipleObjects(2, handles, FALSE, m_interval);
+                       if (waitStatus == WAIT_OBJECT_0)
+                               goto stop;      // shutdown
+                       if (waitStatus == WAIT_OBJECT_0 + 1)
+                               break;   // Set change, break collection loop
+
+                       // Collect data
+                       if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
+                               ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
+
+                       // Get raw values for each counter and compute average value
+                       for(int i = 0; i < m_counters->size(); i++)
+                       {
+                               WINPERF_COUNTER *counter = m_counters->get(i);
+                               if (counter->handle != NULL)
+                               {
+                                       // Get raw value into buffer
+                                       PdhGetRawCounterValue(counter->handle, NULL, &counter->pRawValues[counter->dwBufferPos]);
+                                       counter->dwBufferPos++;
+                                       if (counter->dwBufferPos == (DWORD)counter->wNumSamples)
+                                               counter->dwBufferPos = 0;
+
+                                       // Calculate mean value
+                                       if ((rc = PdhComputeCounterStatistics(counter->handle,
+                                                               counter->dwFormat,
+                                                               counter->dwBufferPos, 
+                                                               counter->wNumSamples,
+                                                               counter->pRawValues,
+                                                               &statData)) != ERROR_SUCCESS)
+                                       {
+                                               ReportPdhError(szFName, _T("PdhComputeCounterStatistics"), rc);
+                                       }
+
+                                       // Update mean value in counter set
+                                       switch(counter->wType)
+                                       {
+                                               case COUNTER_TYPE_INT32:
+                                                       counter->value.iLong = statData.mean.longValue;
+                                                       break;
+                                               case COUNTER_TYPE_INT64:
+                                                       counter->value.iLarge = statData.mean.largeValue;
+                                                       break;
+                                               case COUNTER_TYPE_FLOAT:
+                                                       counter->value.dFloat = statData.mean.doubleValue;
+                                                       break;
+                                               default:
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+
+               AgentWriteDebugLog(2, _T("WINPERF: %s: data collection stopped"), szFName);
+               PdhCloseQuery(hQuery);
+       }
+
+stop:
+       AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("WINPERF: Collector thread for counter set %c terminated"), m_class);
+}
+
+/**
+ * Starter for collector thread
+ */
+THREAD_RESULT THREAD_CALL WinPerfCounterSet::collectorThreadStarter(void *arg)
+{
+       ((WinPerfCounterSet *)arg)->collectorThread();
+       return THREAD_OK;
+}
+
+/**
+ * Start collector thread
+ */
+void WinPerfCounterSet::startCollectorThread()
+{
+       ThreadCreate(collectorThreadStarter, 0, this);
+}
+
+/**
+ * Check that counter's name is valid and determine counter's type
+ */
 int CheckCounter(const TCHAR *pszName, TCHAR **ppszNewName)
 {
    HQUERY hQuery;
@@ -94,11 +271,9 @@ int CheckCounter(const TCHAR *pszName, TCHAR **ppszNewName)
    return (ci.dwType & PERF_SIZE_LARGE) ? COUNTER_TYPE_INT64 : COUNTER_TYPE_INT32;
 }
 
-
-//
-// Add counter to set
-//
-
+/**
+ * Add counter to set
+ */
 WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDataType)
 {
    WINPERF_COUNTER *pCnt;
@@ -118,7 +293,7 @@ WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDa
        }
 
    // Create new counter
-   pCnt = (WINPERF_COUNTER *)malloc(sizeof(WINPERF_COUNTER));
+   pCnt = new WINPERF_COUNTER;
    memset(pCnt, 0, sizeof(WINPERF_COUNTER));
        if (pszNewName != NULL)
        {
@@ -146,22 +321,16 @@ WINPERF_COUNTER *AddCounter(TCHAR *pszName, int iClass, int iNumSamples, int iDa
    }
 
    // Add counter to set
-   m_cntSet[iClass].dwNumCounters++;
-   m_cntSet[iClass].ppCounterList = 
-      (WINPERF_COUNTER **)realloc(m_cntSet[iClass].ppCounterList, sizeof(WINPERF_COUNTER *) * m_cntSet[iClass].dwNumCounters);
-   m_cntSet[iClass].ppCounterList[m_cntSet[iClass].dwNumCounters - 1] = pCnt;
-
+       m_cntSet[iClass]->addCounter(pCnt);
    return pCnt;
 }
 
-
-//
-// Add custom counter from configuration file
-// Should be in form 
-// <parameter name>:<counter path>:<number of samples>:<class>:<data type>:<description>
-// Class can be A (poll every second), B (poll every 5 seconds) or C (poll every 30 seconds)
-//
-
+/**
+ * Add custom counter from configuration file
+ * Should be in form 
+ * <parameter name>:<counter path>:<number of samples>:<class>:<data type>:<description>
+ * Class can be A (poll every second), B (poll every 5 seconds) or C (poll every 30 seconds)
+ */
 BOOL AddCounterFromConfig(TCHAR *pszStr)
 {
    TCHAR *ptr, *eptr, *pszCurrField;
@@ -275,125 +444,11 @@ BOOL AddCounterFromConfig(TCHAR *pszStr)
    return ((iState == -1) && (iField >= 4) && (iField <= 6));
 }
 
-
-//
-// Collector thread
-//
-
-static THREAD_RESULT THREAD_CALL CollectorThread(WINPERF_COUNTER_SET *pSet)
+/**
+ * Start collector threads
+ */
+void StartCollectorThreads()
 {
-   HQUERY hQuery;
-   PDH_STATUS rc;
-   PDH_STATISTICS statData;
-   DWORD i, dwOKCounters = 0;
-   TCHAR szFName[] = _T("CollectorThread_X");
-
-   szFName[16] = pSet->cClass;
-   if (pSet->dwNumCounters == 0)
-   {
-      AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Counter set %c is empty, collector thread for that set will not start"),
-                      pSet->cClass);
-      return 0;
-   }
-
-   if ((rc = PdhOpenQuery(NULL, 0, &hQuery)) != ERROR_SUCCESS)
-   {
-      ReportPdhError(szFName, _T("PdhOpenQuery"), rc);
-      return -1;
-   }
-
-   // Add counters to query
-   for(i = 0; i < pSet->dwNumCounters; i++)
-      if ((rc = PdhAddCounter(hQuery, pSet->ppCounterList[i]->pszName, 
-                              0, &pSet->ppCounterList[i]->handle)) != ERROR_SUCCESS)
-      {
-         TCHAR szBuffer[1024];
-
-         AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("%s: Unable to add counter \"%s\" to query (%s)"), 
-                         szFName, pSet->ppCounterList[i]->pszName, GetPdhErrorText(rc, szBuffer, 1024));
-         pSet->ppCounterList[i]->handle = NULL;
-      }
-      else
-      {
-         dwOKCounters++;
-      }
-
-      // Check if we was able to add at least one counter
-   if (dwOKCounters == 0)
-   {
-      PdhCloseQuery(hQuery);
-      AgentWriteLog(EVENTLOG_WARNING_TYPE, _T("Failed to add any counter to query for counter set %c, ")
-                                           _T("collector thread for that set will not start"),
-                      pSet->cClass);
-      return 0;
-   }
-
-   // Main collection loop
-   while(1)
-   {
-      if (WaitForSingleObject(g_hCondShutdown, pSet->dwInterval) != WAIT_TIMEOUT)
-         break;   // We got a signal
-
-      // Collect data
-      if ((rc = PdhCollectQueryData(hQuery)) != ERROR_SUCCESS)
-         ReportPdhError(szFName, _T("PdhCollectQueryData"), rc);
-
-      // Get raw values for each counter and compute average value
-      for(i = 0; i < pSet->dwNumCounters; i++)
-         if (pSet->ppCounterList[i]->handle != NULL)
-         {
-            // Get raw value into buffer
-            PdhGetRawCounterValue(pSet->ppCounterList[i]->handle, NULL, 
-               &pSet->ppCounterList[i]->pRawValues[pSet->ppCounterList[i]->dwBufferPos]);
-            pSet->ppCounterList[i]->dwBufferPos++;
-            if (pSet->ppCounterList[i]->dwBufferPos == (DWORD)pSet->ppCounterList[i]->wNumSamples)
-               pSet->ppCounterList[i]->dwBufferPos = 0;
-
-            // Calculate mean value
-            if ((rc = PdhComputeCounterStatistics(pSet->ppCounterList[i]->handle,
-                     pSet->ppCounterList[i]->dwFormat,
-                     pSet->ppCounterList[i]->dwBufferPos, 
-                     pSet->ppCounterList[i]->wNumSamples,
-                     pSet->ppCounterList[i]->pRawValues,
-                     &statData)) != ERROR_SUCCESS)
-            {
-               ReportPdhError(szFName, _T("PdhComputeCounterStatistics"), rc);
-            }
-
-            // Update mean value in counter set
-            switch(pSet->ppCounterList[i]->wType)
-            {
-               case COUNTER_TYPE_INT32:
-                  pSet->ppCounterList[i]->value.iLong = statData.mean.longValue;
-                  break;
-               case COUNTER_TYPE_INT64:
-                  pSet->ppCounterList[i]->value.iLarge = statData.mean.largeValue;
-                  break;
-               case COUNTER_TYPE_FLOAT:
-                  pSet->ppCounterList[i]->value.dFloat = statData.mean.doubleValue;
-                  break;
-               default:
-                  break;
-            }
-         }
-   }
-
-   // Cleanup
-   PdhCloseQuery(hQuery);
-   AgentWriteLog(EVENTLOG_INFORMATION_TYPE, _T("Collector thread for counter set %c terminated"), pSet->cClass);
-
-   return 0;
-}
-
-
-//
-// Start collector threads
-//
-
-void StartCollectorThreads(void)
-{
-   int i;
-
-   for(i = 0; i < 3; i++)
-      ThreadCreate((THREAD_RESULT (THREAD_CALL *)(void *))CollectorThread, 0, &m_cntSet[i]);
+   for(int i = 0; i < 3; i++)
+               m_cntSet[i]->startCollectorThread();
 }
index 3304450..523f06c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** Windows Performance NetXMS subagent
-** Copyright (C) 2004-2012 Victor Kirhenshtein
+** Copyright (C) 2004-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
 
 #include "winperf.h"
 
-
-//
-// Constants
-//
-
+/**
+ * Max length of counter element
+ */
 #define MAX_ELEMENT_LENGTH             1024
 
-
-//
-// Static data
-//
-
+/**
+ * List of configured counters
+ */
 static COUNTER_INDEX *m_pCounterList = NULL;
 static DWORD m_dwNumCounters = 0;
 
-
-//
-// Get error text for PDH functions
-//
-
+/**
+ * Get error text for PDH functions
+ */
 TCHAR *GetPdhErrorText(DWORD dwError, TCHAR *pszBuffer, int iBufferSize)
 {
    TCHAR *pszMsg;
@@ -64,11 +58,9 @@ TCHAR *GetPdhErrorText(DWORD dwError, TCHAR *pszBuffer, int iBufferSize)
    return pszBuffer;
 }
 
-
-//
-// Report PDH error to master agent's log
-//
-
+/**
+ * Report PDH error to master agent's log
+ */
 void ReportPdhError(TCHAR *pszFunction, TCHAR *pszPdhCall, PDH_STATUS dwError)
 {
    TCHAR szBuffer[1024];
index 1730f47..02f4164 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** Windows Performance NetXMS subagent
-** Copyright (C) 2004-2012 Victor Kirhenshtein
+** Copyright (C) 2004-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
 
 #define WPF_ENABLE_DEFAULT_COUNTERS    0x0001
 
-
-//
-// Global variables
-//
-
+/**
+ * Shutdown condition
+ */
 HANDLE g_hCondShutdown = NULL;
 
-
-//
-// Static variables
-//
-
+/**
+ * Static variables
+ */
 static DWORD m_dwFlags = WPF_ENABLE_DEFAULT_COUNTERS;
 static DWORD m_dwNumCPU = 1;
 static WINPERF_COUNTER *m_pProcessorCounters[MAX_CPU_COUNT];
 static WINPERF_COUNTER *m_pProcessorCounters5[MAX_CPU_COUNT];
 static WINPERF_COUNTER *m_pProcessorCounters15[MAX_CPU_COUNT];
+static MUTEX s_autoCountersLock = MutexCreate();
+static StringObjectMap<WINPERF_COUNTER> *s_autoCounters = new StringObjectMap<WINPERF_COUNTER>(false);
 
-
-//
-// List of predefined performance counters
-//
+/**
+ * List of predefined performance counters
+ */
 static struct
 {
    TCHAR *pszParamName;
@@ -83,11 +80,9 @@ static struct
    { NULL, NULL, 0, 0, 0, NULL, 0 }
 };
 
-
-//
-// Value of given performance counter
-//
-
+/**
+ * Handler for PDH.Version parameter
+ */
 static LONG H_PdhVersion(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
 {
    DWORD dwVersion;
@@ -98,11 +93,18 @@ static LONG H_PdhVersion(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue
    return SYSINFO_RC_SUCCESS;
 }
 
+/**
+ * Handler for WinPerf.Features parameter
+ */
+static LONG H_WinPerfFeatures(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
+{
+   ret_uint(pValue, WINPERF_AUTOMATIC_SAMPLE_COUNT | WINPERF_REMOTE_COUNTER_CONFIG);
+   return SYSINFO_RC_SUCCESS;
+}
 
-//
-// Value of CPU utilization counter
-//
-
+/**
+ * Value of CPU utilization counter
+ */
 static LONG H_CPUUsage(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
 {
    LONG nProcessor, nRet = SYSINFO_RC_SUCCESS;
@@ -182,6 +184,31 @@ static LONG H_PdhCounterValue(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *p
        {
                if (!AgentGetParameterArg(pszParam, 1, szCounter, MAX_PATH))
                        return SYSINFO_RC_UNSUPPORTED;
+
+               // required number of samples
+               TCHAR buffer[MAX_PATH] = _T("");
+               AgentGetParameterArg(pszParam, 2, buffer, MAX_PATH);
+               if (buffer[0] != 0)   // sample count given
+               {
+                       int samples = _tcstol(buffer, NULL, 0);
+                       if (samples > 1)
+                       {
+                               _sntprintf(buffer, MAX_PATH, _T("%d#%s"), samples, szCounter);
+                               MutexLock(s_autoCountersLock);
+                               WINPERF_COUNTER *counter = s_autoCounters->get(buffer);
+                               if (counter == NULL)
+                               {
+                                       counter = AddCounter(szCounter, 0, samples, COUNTER_TYPE_AUTO);
+                                       if (counter != NULL)
+                                       {
+                                               AgentWriteDebugLog(5, _T("WINPERF: new automatic counter created: \"%s\""), buffer);
+                                               s_autoCounters->set(buffer, counter);
+                                       }
+                               }
+                               MutexUnlock(s_autoCountersLock);
+                               return (counter != NULL) ? H_CollectedCounterData(pszParam, (const TCHAR *)counter, pValue) : SYSINFO_RC_UNSUPPORTED;
+                       }
+               }
        }
        else    // Call from H_CounterAlias
        {
@@ -341,22 +368,18 @@ static LONG H_PdhObjectItems(const TCHAR *pszParam, const TCHAR *pArg, StringLis
    return iResult;
 }
 
-
-//
-// Value of specific performance parameter, which is mapped one-to-one to
-// performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
-//
-
+/**
+ * Value of specific performance parameter, which is mapped one-to-one to
+ * performance counter. Actually, it's an alias for PDH.CounterValue(xxx) parameter.
+ */
 static LONG H_CounterAlias(const TCHAR *pszParam, const TCHAR *pArg, TCHAR *pValue)
 {
    return H_PdhCounterValue(NULL, pArg, pValue);
 }
 
-
-//
-// Initialize subagent
-//
-
+/**
+ * Initialize subagent
+ */
 static BOOL SubAgentInit(Config *config)
 {
    // Create shutdown condition object
@@ -386,7 +409,8 @@ static NETXMS_SUBAGENT_PARAM m_parameters[] =
    { _T("System.CPU.Usage5(*)"), H_CPUUsage, _T("2"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGE5_EX },
    { _T("System.CPU.Usage15(*)"), H_CPUUsage, _T("3"), DCI_DT_INT, DCIDESC_SYSTEM_CPU_USAGE15_EX },
        { _T("System.ThreadCount"), H_CounterAlias, _T("\\System\\Threads"), DCI_DT_UINT, DCIDESC_SYSTEM_THREADCOUNT },
-       { _T("System.Uptime"), H_CounterAlias, _T("\\System\\System Up Time"), DCI_DT_UINT, DCIDESC_SYSTEM_UPTIME }
+       { _T("System.Uptime"), H_CounterAlias, _T("\\System\\System Up Time"), DCI_DT_UINT, DCIDESC_SYSTEM_UPTIME },
+   { _T("WinPerf.Features"), H_WinPerfFeatures, NULL, DCI_DT_UINT, _T("Features supported by this WinPerf version") },
 };
 static NETXMS_SUBAGENT_LIST m_enums[] =
 {
@@ -408,11 +432,9 @@ static NETXMS_SUBAGENT_INFO m_info =
        0, NULL // push parameters
 };
 
-
-//
-// Add new parameter to list
-//
-
+/**
+ * Add new parameter to list
+ */
 BOOL AddParameter(TCHAR *pszName, LONG (* fpHandler)(const TCHAR *, const TCHAR *, TCHAR *),
                   TCHAR *pArg, int iDataType, TCHAR *pszDescription)
 {
index e848e4b..3f12c68 100644 (file)
@@ -67,35 +67,42 @@ struct WINPERF_COUNTER
    HCOUNTER handle;
 };
 
+/**
+ * Counter set
+ */
+class WinPerfCounterSet
+{
+private:
+   DWORD m_interval;    // Interval beetween samples in milliseconds
+       ObjectArray<WINPERF_COUNTER> *m_counters;
+   TCHAR m_class;
+       MUTEX m_mutex;
+       CONDITION m_changeCondition;
 
-//
-// Counter set structure
-//
+       void collectorThread();
+       static THREAD_RESULT THREAD_CALL collectorThreadStarter(void *);
 
-struct WINPERF_COUNTER_SET
-{
-   DWORD dwInterval;    // Interval beetween samples in milliseconds
-   DWORD dwNumCounters;
-   WINPERF_COUNTER **ppCounterList;
-   TCHAR cClass;
-};
+public:
+       WinPerfCounterSet(DWORD interval, TCHAR cls);
+       ~WinPerfCounterSet();
 
+       void addCounter(WINPERF_COUNTER *c);
 
-//
-// Counter index structure
-//
+       void startCollectorThread();
+};
 
+/**
+ * Counter index structure
+ */
 struct COUNTER_INDEX
 {
        DWORD dwIndex;
        TCHAR *pszName;
 };
 
-
-//
-// Functions
-//
-
+/**
+ * Functions
+ */
 void CreateCounterIndex(TCHAR *pData);
 BOOL TranslateCounterName(const TCHAR *pszName, TCHAR *pszOut);
 int CheckCounter(const TCHAR *pszName, TCHAR **ppszNewName);
index 2080eaf..f9104a1 100644 (file)
@@ -740,6 +740,7 @@ public class NXCPCodes
        public static final long VID_INSTD_FILTER            = 437;\r
        public static final long VID_ACCURACY                = 438;\r
        public static final long VID_GEOLOCATION_TIMESTAMP   = 439;\r
+       public static final long VID_SAMPLE_COUNT            = 440;\r
 \r
        public static final long VID_ACL_USER_BASE            = 0x00001000L;\r
        public static final long VID_ACL_USER_LAST            = 0x00001FFFL;\r
index e44de6b..dde6dbd 100644 (file)
@@ -57,6 +57,7 @@ public class DataCollectionItem extends DataCollectionObject
        \r
        private int dataType;\r
        private int deltaCalculation;\r
+       private int sampleCount;\r
        private String transformationScript;\r
        private String instance;\r
        private int baseUnits;\r
@@ -80,6 +81,7 @@ public class DataCollectionItem extends DataCollectionObject
                \r
                dataType = msg.getVariableAsInteger(NXCPCodes.VID_DCI_DATA_TYPE);\r
                deltaCalculation = msg.getVariableAsInteger(NXCPCodes.VID_DCI_DELTA_CALCULATION);\r
+               sampleCount = msg.getVariableAsInteger(NXCPCodes.VID_SAMPLE_COUNT);\r
                transformationScript = msg.getVariableAsString(NXCPCodes.VID_TRANSFORMATION_SCRIPT);\r
                instance = msg.getVariableAsString(NXCPCodes.VID_INSTANCE);\r
                baseUnits = msg.getVariableAsInteger(NXCPCodes.VID_BASE_UNITS);\r
@@ -111,6 +113,7 @@ public class DataCollectionItem extends DataCollectionObject
                \r
                dataType = DT_INT;\r
                deltaCalculation = DELTA_NONE;\r
+               sampleCount = 0;\r
                transformationScript = null;\r
                instance = "";\r
                baseUnits = 0;\r
@@ -132,6 +135,7 @@ public class DataCollectionItem extends DataCollectionObject
                msg.setVariableInt16(NXCPCodes.VID_DCOBJECT_TYPE, DCO_TYPE_ITEM);\r
                msg.setVariableInt16(NXCPCodes.VID_DCI_DATA_TYPE, dataType);\r
                msg.setVariableInt16(NXCPCodes.VID_DCI_DELTA_CALCULATION, deltaCalculation);\r
+               msg.setVariableInt16(NXCPCodes.VID_SAMPLE_COUNT, sampleCount);\r
                msg.setVariable(NXCPCodes.VID_INSTANCE, instance);\r
                msg.setVariable(NXCPCodes.VID_TRANSFORMATION_SCRIPT, transformationScript);\r
                msg.setVariableInt16(NXCPCodes.VID_SNMP_RAW_VALUE_TYPE, snmpRawValueType);\r
@@ -377,4 +381,20 @@ public class DataCollectionItem extends DataCollectionObject
        {\r
                this.instanceDiscoveryFilter = instanceDiscoveryFilter;\r
        }\r
+\r
+       /**\r
+        * @return the sampleCount\r
+        */\r
+       public int getSampleCount()\r
+       {\r
+               return sampleCount;\r
+       }\r
+\r
+       /**\r
+        * @param sampleCount the sampleCount to set\r
+        */\r
+       public void setSampleCount(int sampleCount)\r
+       {\r
+               this.sampleCount = sampleCount;\r
+       }\r
 }\r
index d1b7afc..88f38c5 100644 (file)
@@ -20,7 +20,6 @@ package org.netxms.ui.eclipse.datacollection.propertypages;
 \r
 import java.util.HashMap;\r
 import java.util.Map;\r
-\r
 import org.eclipse.core.runtime.IProgressMonitor;\r
 import org.eclipse.jface.dialogs.Dialog;\r
 import org.eclipse.jface.window.Window;\r
@@ -39,6 +38,7 @@ import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;\r
 import org.eclipse.swt.widgets.Control;\r
 import org.eclipse.swt.widgets.Group;\r
+import org.eclipse.swt.widgets.Spinner;\r
 import org.eclipse.swt.widgets.Text;\r
 import org.eclipse.ui.dialogs.PropertyPage;\r
 import org.netxms.client.NXCSession;\r
@@ -59,7 +59,6 @@ import org.netxms.ui.eclipse.datacollection.dialogs.WinPerfCounterSelectionDialo
 import org.netxms.ui.eclipse.jobs.ConsoleJob;\r
 import org.netxms.ui.eclipse.objectbrowser.widgets.ObjectSelector;\r
 import org.netxms.ui.eclipse.shared.ConsoleSharedData;\r
-import org.netxms.ui.eclipse.tools.NumericTextFieldValidator;\r
 import org.netxms.ui.eclipse.tools.WidgetHelper;\r
 import org.netxms.ui.eclipse.widgets.LabeledText;\r
 \r
@@ -92,11 +91,12 @@ public class General extends PropertyPage
        private Button checkInterpretRawSnmpValue;\r
        private Combo snmpRawType;\r
        private Button checkUseCustomSnmpPort;\r
-       private Text customSnmpPort;\r
+       private Spinner customSnmpPort;\r
        private ObjectSelector proxyNode;\r
        private Combo schedulingMode;\r
-       private LabeledText pollingInterval;\r
-       private LabeledText retentionTime;\r
+       private Spinner pollingInterval;\r
+       private Spinner retentionTime;\r
+       private Spinner sampleCount;\r
        private Combo clusterResource;\r
        private Button statusActive;\r
        private Button statusDisabled;\r
@@ -294,11 +294,13 @@ public class General extends PropertyPage
       checkUseCustomSnmpPort.setLayoutData(fd);\r
       checkUseCustomSnmpPort.setEnabled(dci.getOrigin() == DataCollectionItem.SNMP);\r
 \r
-      customSnmpPort = new Text(groupData, SWT.BORDER);\r
+      customSnmpPort = new Spinner(groupData, SWT.BORDER);\r
+      customSnmpPort.setMinimum(1);\r
+      customSnmpPort.setMaximum(65535);\r
       if ((dci.getOrigin() == DataCollectionItem.SNMP) && (dci.getSnmpPort() != 0))\r
       {\r
        customSnmpPort.setEnabled(true);\r
-       customSnmpPort.setText(Integer.toString(dci.getSnmpPort()));\r
+       customSnmpPort.setSelection(dci.getSnmpPort());\r
       }\r
       else\r
       {\r
@@ -310,11 +312,18 @@ public class General extends PropertyPage
       fd.top = new FormAttachment(checkUseCustomSnmpPort, WidgetHelper.OUTER_SPACING, SWT.BOTTOM);\r
       customSnmpPort.setLayoutData(fd);\r
       \r
+      fd = new FormData();\r
+      fd.left = new FormAttachment(0, 0);\r
+      fd.top = new FormAttachment(snmpRawType, WidgetHelper.OUTER_SPACING, SWT.BOTTOM);\r
+      fd.right = new FormAttachment(100, 0);\r
+      sampleCount = WidgetHelper.createLabeledSpinner(groupData, SWT.BORDER, "Sample count for average value calculation (0 to disable)", 0, 65535, fd);\r
+               sampleCount.setEnabled(dci.getOrigin() == DataCollectionItem.WINPERF);\r
+      \r
       proxyNode = new ObjectSelector(groupData, SWT.NONE, true);\r
       proxyNode.setLabel(Messages.General_ProxyNode);\r
       fd = new FormData();\r
       fd.left = new FormAttachment(0, 0);\r
-      fd.top = new FormAttachment(snmpRawType, WidgetHelper.OUTER_SPACING, SWT.BOTTOM);\r
+      fd.top = new FormAttachment(sampleCount.getParent(), WidgetHelper.OUTER_SPACING, SWT.BOTTOM);\r
       fd.right = new FormAttachment(100, 0);\r
       proxyNode.setLayoutData(fd);\r
       proxyNode.setObjectClass(Node.class);\r
@@ -353,20 +362,17 @@ public class General extends PropertyPage
                        @Override\r
                        public void widgetSelected(SelectionEvent e)\r
                        {\r
-                               pollingInterval.getTextControl().setEnabled(schedulingMode.getSelectionIndex() == 0);\r
+                               pollingInterval.setEnabled(schedulingMode.getSelectionIndex() == 0);\r
                        }\r
       });\r
       \r
-      pollingInterval = new LabeledText(groupPolling, SWT.NONE);\r
-      pollingInterval.getTextControl().setTextLimit(5);\r
-      pollingInterval.setLabel(Messages.General_PollingInterval);\r
-      pollingInterval.setText(Integer.toString(dci.getPollingInterval()));\r
-      pollingInterval.setEnabled(!dci.isUseAdvancedSchedule() && (dci.getOrigin() != DataCollectionItem.PUSH));\r
       fd = new FormData();\r
       fd.left = new FormAttachment(50, WidgetHelper.OUTER_SPACING / 2);\r
       fd.right = new FormAttachment(100, 0);\r
       fd.top = new FormAttachment(0, 0);\r
-      pollingInterval.setLayoutData(fd);\r
+      pollingInterval = WidgetHelper.createLabeledSpinner(groupPolling, SWT.BORDER, Messages.General_PollingInterval, 1, 99999, fd);\r
+      pollingInterval.setSelection(dci.getPollingInterval());\r
+      pollingInterval.setEnabled(!dci.isUseAdvancedSchedule() && (dci.getOrigin() != DataCollectionItem.PUSH));\r
       \r
       fd = new FormData();\r
       fd.left = new FormAttachment(0, 0);\r
@@ -435,10 +441,8 @@ public class General extends PropertyPage
       storageLayout.marginHeight = WidgetHelper.OUTER_SPACING;\r
       groupStorage.setLayout(storageLayout);\r
       \r
-      retentionTime = new LabeledText(groupStorage, SWT.NONE);\r
-      retentionTime.setLabel(Messages.General_RetentionTime);\r
-      retentionTime.getTextControl().setTextLimit(5);\r
-      retentionTime.setText(Integer.toString(dci.getRetentionTime()));\r
+      retentionTime = WidgetHelper.createLabeledSpinner(groupStorage, SWT.BORDER, Messages.General_RetentionTime, 1, 99999, null);\r
+      retentionTime.setSelection(dci.getRetentionTime());\r
       \r
       return dialogArea;\r
        }\r
@@ -451,11 +455,12 @@ public class General extends PropertyPage
                int index = origin.getSelectionIndex();\r
                proxyNode.setEnabled(index != DataCollectionItem.PUSH);\r
                schedulingMode.setEnabled(index != DataCollectionItem.PUSH);\r
-               pollingInterval.getTextControl().setEnabled((index != DataCollectionItem.PUSH) && (schedulingMode.getSelectionIndex() == 0));\r
+               pollingInterval.setEnabled((index != DataCollectionItem.PUSH) && (schedulingMode.getSelectionIndex() == 0));\r
                checkInterpretRawSnmpValue.setEnabled(index == DataCollectionItem.SNMP);\r
                snmpRawType.setEnabled((index == DataCollectionItem.SNMP) && checkInterpretRawSnmpValue.getSelection());\r
                checkUseCustomSnmpPort.setEnabled(index == DataCollectionItem.SNMP);\r
                customSnmpPort.setEnabled((index == DataCollectionItem.SNMP) && checkUseCustomSnmpPort.getSelection());\r
+               sampleCount.setEnabled(index == DataCollectionItem.WINPERF);\r
        }\r
        \r
        /**\r
@@ -509,11 +514,6 @@ public class General extends PropertyPage
         */\r
        protected boolean applyChanges(final boolean isApply)\r
        {\r
-               if (!WidgetHelper.validateTextInput(customSnmpPort, Messages.General_CustomPort, new NumericTextFieldValidator(1, 65535), this) ||\r
-                   !WidgetHelper.validateTextInput(pollingInterval, new NumericTextFieldValidator(1, 1000000), this) ||\r
-                   !WidgetHelper.validateTextInput(retentionTime, new NumericTextFieldValidator(1, 65535), this))\r
-                       return false;\r
-               \r
                if (isApply)\r
                        setValid(false);\r
                \r
@@ -521,6 +521,7 @@ public class General extends PropertyPage
                dci.setName(parameter.getText().trim());\r
                dci.setOrigin(origin.getSelectionIndex());\r
                dci.setDataType(dataType.getSelectionIndex());\r
+               dci.setSampleCount(sampleCount.getSelection());\r
                dci.setProxyNode(proxyNode.getObjectId());\r
                dci.setUseAdvancedSchedule(schedulingMode.getSelectionIndex() == 1);\r
                dci.setPollingInterval(Integer.parseInt(pollingInterval.getText()));\r
@@ -596,13 +597,17 @@ public class General extends PropertyPage
        protected void performDefaults()\r
        {\r
                super.performDefaults();\r
+               \r
+               NXCSession session = (NXCSession)ConsoleSharedData.getSession();\r
+               \r
                schedulingMode.select(0);\r
-               pollingInterval.setText("60"); //$NON-NLS-1$\r
+               pollingInterval.setSelection(session.getDefaultDciPollingInterval());\r
                statusActive.setSelection(true);\r
                statusDisabled.setSelection(false);\r
                statusUnsupported.setSelection(false);\r
-               retentionTime.setText("30"); //$NON-NLS-1$\r
+               retentionTime.setSelection(session.getDefaultDciRetentionTime());\r
                checkInterpretRawSnmpValue.setSelection(false);\r
                checkUseCustomSnmpPort.setSelection(false);\r
+               customSnmpPort.setSelection(161);\r
        }\r
 }\r
index 7e44bcc..2882bba 100644 (file)
@@ -7,14 +7,15 @@ libnxcore_la_SOURCES =  acl.cpp agent.cpp agent_policy.cpp actions.cpp \
                        bridge.cpp cdp.cpp cert.cpp client.cpp cluster.cpp \
                        columnfilter.cpp components.cpp condition.cpp \
                        config.cpp container.cpp correlate.cpp dashboard.cpp \
-                       datacoll.cpp dbwrite.cpp dcitem.cpp dcithreshold.cpp \
-                       dcivalue.cpp dcobject.cpp dctable.cpp dctarget.cpp \
-                       dctcolumn.cpp debug.cpp download_job.cpp ef.cpp \
-                       email.cpp entirenet.cpp epp.cpp events.cpp evproc.cpp \
-                       fdb.cpp hk.cpp id.cpp import.cpp index.cpp \
-                       interface.cpp isc.cpp job.cpp jobmgr.cpp jobqueue.cpp \
-                       layer2.cpp lln.cpp lldp.cpp locks.cpp logfilter.cpp \
-                       loghandle.cpp logs.cpp main.cpp mdconn.cpp mdsession.cpp \
+                       datacoll.cpp dbwrite.cpp dc_nxsl.cpp dcitem.cpp \
+                       dcithreshold.cpp dcivalue.cpp dcobject.cpp \
+                       dctable.cpp dctarget.cpp dctcolumn.cpp debug.cpp \
+                       download_job.cpp ef.cpp email.cpp entirenet.cpp \
+                       epp.cpp events.cpp evproc.cpp fdb.cpp hk.cpp id.cpp \
+                       import.cpp index.cpp interface.cpp isc.cpp job.cpp \
+                       jobmgr.cpp jobqueue.cpp layer2.cpp lln.cpp lldp.cpp \
+                       locks.cpp logfilter.cpp loghandle.cpp logs.cpp \
+                       main.cpp mdconn.cpp mdsession.cpp \
                        mobile.cpp modules.cpp mt.cpp ndd.cpp ndp.cpp \
                        netinfo.cpp netmap.cpp netobj.cpp netsrv.cpp node.cpp \
                        nodelink.cpp np.cpp nxsl_classes.cpp nxslext.cpp \
index f0a623a..9b43deb 100644 (file)
@@ -78,7 +78,7 @@ static void *GetItemData(DataCollectionTarget *dcTarget, DCItem *pItem, TCHAR *p
                        if (dcTarget->Type() == OBJECT_NODE)
                        {
                                TCHAR name[MAX_PARAM_NAME];
-                               _sntprintf(name, MAX_PARAM_NAME, _T("PDH.CounterValue(\"%s\")"), pItem->getName());
+                               _sntprintf(name, MAX_PARAM_NAME, _T("PDH.CounterValue(\"%s\",%d)"), pItem->getName(), pItem->getSampleCount());
                 *error = ((Node *)dcTarget)->getItemFromAgent(name, MAX_LINE_SIZE, pBuffer);
                        }
                        else
diff --git a/src/server/core/dc_nxsl.cpp b/src/server/core/dc_nxsl.cpp
new file mode 100644 (file)
index 0000000..cb19032
--- /dev/null
@@ -0,0 +1,371 @@
+/* 
+** NetXMS - 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.
+**
+** File: dc_nxsl.cpp
+**
+**/
+
+#include "nxcore.h"
+
+/**
+ * NXSL function: Get DCI object
+ * First argument is a node object (usually passed to script via $node variable),
+ * and second is DCI ID
+ */
+static int F_GetDCIObject(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isInteger())
+               return NXSL_ERR_NOT_INTEGER;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+
+       Node *node = (Node *)object->getData();
+       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
+       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
+       {
+               *ppResult = new NXSL_Value(new NXSL_Object(&g_nxslDciClass, dci));
+       }
+       else
+       {
+               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
+       }
+
+       return 0;
+}
+
+/**
+ * NXSL function: Get DCI value from within transformation script
+ * First argument is a node object (passed to script via $node variable),
+ * and second is DCI ID
+ */
+static int F_GetDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isInteger())
+               return NXSL_ERR_NOT_INTEGER;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+
+       Node *node = (Node *)object->getData();
+       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
+       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
+       {
+               *ppResult = ((DCItem *)dci)->getValueForNXSL(F_LAST, 1);
+       }
+       else
+       {
+               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
+       }
+
+       return 0;
+}
+
+/**
+ * Internal implementation of GetDCIValueByName and GetDCIValueByDescription
+ */
+static int GetDciValueExImpl(bool byName, int argc, NXSL_Value **argv, NXSL_Value **ppResult)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isString())
+               return NXSL_ERR_NOT_STRING;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+
+       Node *node = (Node *)object->getData();
+       DCObject *dci = byName ? node->getDCObjectByName(argv[1]->getValueAsCString()) : node->getDCObjectByDescription(argv[1]->getValueAsCString());
+       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
+       {
+               *ppResult = ((DCItem *)dci)->getValueForNXSL(F_LAST, 1);
+       }
+       else
+       {
+               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
+       }
+
+       return 0;
+}
+
+/**
+ * NXSL function: Get DCI value from within transformation script
+ * First argument is a node object (passed to script via $node variable),
+ * and second is DCI name
+ */
+static int F_GetDCIValueByName(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       return GetDciValueExImpl(true, argc, argv, ppResult);
+}
+
+/**
+ * NXSL function: Get DCI value from within transformation script
+ * First argument is a node object (passed to script via $node variable),
+ * and second is DCI description
+ */
+static int F_GetDCIValueByDescription(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       return GetDciValueExImpl(false, argc, argv, ppResult);
+}
+
+/**
+ * NXSL function: Find DCI by name
+ */
+static int F_FindDCIByName(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isString())
+               return NXSL_ERR_NOT_STRING;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+
+       Node *node = (Node *)object->getData();
+       DCObject *dci = node->getDCObjectByName(argv[1]->getValueAsCString());
+       *ppResult = ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM)) ? new NXSL_Value(dci->getId()) : new NXSL_Value((DWORD)0);
+       return 0;
+}
+
+/**
+ * NXSL function: Find DCI by description
+ */
+static int F_FindDCIByDescription(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isString())
+               return NXSL_ERR_NOT_STRING;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+
+       Node *node = (Node *)object->getData();
+       DCObject *dci = node->getDCObjectByDescription(argv[1]->getValueAsCString());
+       *ppResult = ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM)) ? new NXSL_Value(dci->getId()) : new NXSL_Value((DWORD)0);
+       return 0;
+}
+
+/**
+ * Get min, max or average of DCI values for a period
+ */
+typedef enum { DCI_MIN, DCI_MAX, DCI_AVG } DciSqlFunc_t;
+
+static int F_GetDCIValueStat(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program, DciSqlFunc_t sqlFunc)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isInteger() || !argv[2]->isInteger() || !argv[3]->isInteger())
+               return NXSL_ERR_NOT_INTEGER;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+       
+       Node *node = (Node *)object->getData();
+       DWORD nodeId = node->Id();
+       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
+       if (dci == NULL || dci->getType() != DCO_TYPE_ITEM)
+       {
+               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
+       }
+       else
+       {
+               *ppResult = NULL;
+
+               double result = 0.;
+               DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
+               TCHAR query[1024];
+
+               if (g_nDBSyntax == DB_SYNTAX_ORACLE)
+               {
+                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(to_number(idata_value),0)) FROM idata_%u ")
+                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
+                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")), node->Id());
+               }
+               else if (g_nDBSyntax == DB_SYNTAX_PGSQL)
+               {
+                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value::double precision,0)) FROM idata_%u ")
+                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
+                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")),   node->Id());
+               }
+               else
+               {
+                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value,0)) FROM idata_%u ")
+                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
+                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")),   node->Id());
+               }
+
+               DB_STATEMENT hStmt = DBPrepare(hdb, query);
+               if (hStmt != NULL)
+               {
+                       DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, argv[1]->getValueAsUInt32());
+                       DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, argv[2]->getValueAsInt32());
+                       DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, argv[3]->getValueAsInt32());
+                       DB_RESULT hResult = DBSelectPrepared(hStmt);
+                       if (hResult != NULL)
+                       {
+                               if (DBGetNumRows(hResult) == 1)
+                               {
+                                       result = DBGetFieldDouble(hResult, 0, 0);
+                               }
+                               *ppResult = new NXSL_Value(result);
+                               DBFreeResult(hResult);
+                       }
+                       else
+                       {
+                               *ppResult = new NXSL_Value;     // Return NULL if prepared select failed
+                       }
+                       DBFreeStatement(hStmt);
+               }
+               else
+               {
+                       *ppResult = new NXSL_Value;     // Return NULL if prepare failed
+               }
+
+               DBConnectionPoolReleaseConnection(hdb);
+       }
+
+       return 0;
+}
+
+/**
+ * Get min of DCI values for a period
+ */
+static int F_GetMinDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_MIN);
+}
+
+/**
+ * Get max of DCI values for a period
+ */
+static int F_GetMaxDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_MAX);
+}
+
+/**
+ * Get average of DCI values for a period
+ */
+static int F_GetAvgDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_AVG);
+}
+
+/**
+ * NXSL function: create new DCI
+ * Format: CreateDCI(node, origin, name, description, dataType, pollingInterval, retentionTime)
+ * Possible origin values: "agent", "snmp", "internal", "push"
+ * Possible dataType values: "int32", "uint32", "int64", "uint64", "float", "string"
+ * Returns DCI object on success and NULL of failure
+ */
+static int F_CreateDCI(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
+{
+       if (!argv[0]->isObject())
+               return NXSL_ERR_NOT_OBJECT;
+
+       if (!argv[1]->isString() || !argv[2]->isString() || !argv[3]->isString() || !argv[4]->isString())
+               return NXSL_ERR_NOT_STRING;
+
+       if (!argv[5]->isInteger() || !argv[6]->isInteger())
+               return NXSL_ERR_NOT_INTEGER;
+
+       NXSL_Object *object = argv[0]->getValueAsObject();
+       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
+               return NXSL_ERR_BAD_CLASS;
+       Node *node = (Node *)object->getData();
+
+       // Origin
+       static const TCHAR *originNames[] = { _T("internal"), _T("agent"), _T("snmp"), _T("cpsnmp"), _T("push"), NULL };
+       int origin = -1;
+       const TCHAR *name = argv[1]->getValueAsCString();
+       for(int i = 0; originNames[i] != NULL; i++)
+               if (!_tcsicmp(originNames[i], name))
+               {
+                       origin = i;
+                       break;
+               }
+
+       // Data types
+       static const TCHAR *dtNames[] = { _T("int32"), _T("uint32"), _T("int64"), _T("uint64"), _T("string"), _T("float"), NULL };
+       int dataType = -1;
+       name = argv[4]->getValueAsCString();
+       for(int i = 0; dtNames[i] != NULL; i++)
+               if (!_tcsicmp(dtNames[i], name))
+               {
+                       dataType = i;
+                       break;
+               }
+
+       int pollingInterval = argv[5]->getValueAsInt32();
+       int retentionTime = argv[6]->getValueAsInt32();
+
+       if ((origin != -1) && (dataType != -1) && (pollingInterval > 0) && (retentionTime > 0))
+       {
+               DCItem *dci = new DCItem(CreateUniqueId(IDG_ITEM), argv[2]->getValueAsCString(), 
+                       origin, dataType, pollingInterval, retentionTime, node, argv[3]->getValueAsCString());
+               node->addDCObject(dci);
+               *ppResult = new NXSL_Value(new NXSL_Object(&g_nxslDciClass, dci));
+       }
+       else
+       {
+               *ppResult = new NXSL_Value;
+       }
+       return 0;
+}
+
+/**
+ * Additional NXSL functions for DCI manipulation
+ */
+static NXSL_ExtFunction m_nxslDCIFunctions[] =
+{
+   { _T("CreateDCI"), F_CreateDCI, 7 },
+   { _T("FindDCIByName"), F_FindDCIByName, 2 },
+   { _T("FindDCIByDescription"), F_FindDCIByDescription, 2 },
+   { _T("GetDCIObject"), F_GetDCIObject, 2 },
+   { _T("GetDCIValue"), F_GetDCIValue, 2 },
+   { _T("GetDCIValueByName"), F_GetDCIValueByName, 2 },
+   { _T("GetDCIValueByDescription"), F_GetDCIValueByDescription, 2 },
+       { _T("GetMaxDCIValue"), F_GetMaxDCIValue, 4 },
+       { _T("GetMinDCIValue"), F_GetMinDCIValue, 4 },
+       { _T("GetAvgDCIValue"), F_GetAvgDCIValue, 4 }
+};
+
+/**
+ * Register DCI-related functions in NXSL environment
+ */
+void RegisterDCIFunctions(NXSL_Environment *pEnv)
+{
+       pEnv->registerFunctionSet(sizeof(m_nxslDCIFunctions) / sizeof(NXSL_ExtFunction), m_nxslDCIFunctions);
+}
index cefd060..f4770c9 100644 (file)
 
 #include "nxcore.h"
 
-/**
- * NXSL function: Get DCI object
- * First argument is a node object (usually passed to script via $node variable),
- * and second is DCI ID
- */
-static int F_GetDCIObject(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isInteger())
-               return NXSL_ERR_NOT_INTEGER;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-
-       Node *node = (Node *)object->getData();
-       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
-       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
-       {
-               *ppResult = new NXSL_Value(new NXSL_Object(&g_nxslDciClass, dci));
-       }
-       else
-       {
-               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
-       }
-
-       return 0;
-}
-
-/**
- * NXSL function: Get DCI value from within transformation script
- * First argument is a node object (passed to script via $node variable),
- * and second is DCI ID
- */
-static int F_GetDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isInteger())
-               return NXSL_ERR_NOT_INTEGER;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-
-       Node *node = (Node *)object->getData();
-       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
-       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
-       {
-               *ppResult = ((DCItem *)dci)->getValueForNXSL(F_LAST, 1);
-       }
-       else
-       {
-               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
-       }
-
-       return 0;
-}
-
-/**
- * Internal implementation of GetDCIValueByName and GetDCIValueByDescription
- */
-static int GetDciValueExImpl(bool byName, int argc, NXSL_Value **argv, NXSL_Value **ppResult)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isString())
-               return NXSL_ERR_NOT_STRING;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-
-       Node *node = (Node *)object->getData();
-       DCObject *dci = byName ? node->getDCObjectByName(argv[1]->getValueAsCString()) : node->getDCObjectByDescription(argv[1]->getValueAsCString());
-       if ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM))
-       {
-               *ppResult = ((DCItem *)dci)->getValueForNXSL(F_LAST, 1);
-       }
-       else
-       {
-               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
-       }
-
-       return 0;
-}
-
-/**
- * NXSL function: Get DCI value from within transformation script
- * First argument is a node object (passed to script via $node variable),
- * and second is DCI name
- */
-static int F_GetDCIValueByName(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       return GetDciValueExImpl(true, argc, argv, ppResult);
-}
-
-/**
- * NXSL function: Get DCI value from within transformation script
- * First argument is a node object (passed to script via $node variable),
- * and second is DCI description
- */
-static int F_GetDCIValueByDescription(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       return GetDciValueExImpl(false, argc, argv, ppResult);
-}
-
-/**
- * NXSL function: Find DCI by name
- */
-static int F_FindDCIByName(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isString())
-               return NXSL_ERR_NOT_STRING;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-
-       Node *node = (Node *)object->getData();
-       DCObject *dci = node->getDCObjectByName(argv[1]->getValueAsCString());
-       *ppResult = ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM)) ? new NXSL_Value(dci->getId()) : new NXSL_Value((DWORD)0);
-       return 0;
-}
-
-/**
- * NXSL function: Find DCI by description
- */
-static int F_FindDCIByDescription(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isString())
-               return NXSL_ERR_NOT_STRING;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-
-       Node *node = (Node *)object->getData();
-       DCObject *dci = node->getDCObjectByDescription(argv[1]->getValueAsCString());
-       *ppResult = ((dci != NULL) && (dci->getType() == DCO_TYPE_ITEM)) ? new NXSL_Value(dci->getId()) : new NXSL_Value((DWORD)0);
-       return 0;
-}
-
-/**
- * Get min, max or average of DCI values for a period
- */
-typedef enum { DCI_MIN, DCI_MAX, DCI_AVG } DciSqlFunc_t;
-
-static int F_GetDCIValueStat(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program, DciSqlFunc_t sqlFunc)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isInteger() || !argv[2]->isInteger() || !argv[3]->isInteger())
-               return NXSL_ERR_NOT_INTEGER;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-       
-       Node *node = (Node *)object->getData();
-       DWORD nodeId = node->Id();
-       DCObject *dci = node->getDCObjectById(argv[1]->getValueAsUInt32());
-       if (dci == NULL || dci->getType() != DCO_TYPE_ITEM)
-       {
-               *ppResult = new NXSL_Value;     // Return NULL if DCI not found
-       }
-       else
-       {
-               *ppResult = NULL;
-
-               double result = 0.;
-               DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
-               TCHAR query[1024];
-
-               if (g_nDBSyntax == DB_SYNTAX_ORACLE)
-               {
-                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(to_number(idata_value),0)) FROM idata_%u ")
-                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
-                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")), node->Id());
-               }
-               else if (g_nDBSyntax == DB_SYNTAX_PGSQL)
-               {
-                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value::double precision,0)) FROM idata_%u ")
-                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
-                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")),   node->Id());
-               }
-               else
-               {
-                       _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value,0)) FROM idata_%u ")
-                               _T("WHERE item_id=? and idata_timestamp between ? and ?"), 
-                               sqlFunc == DCI_MAX ? _T("max"): (sqlFunc == DCI_MIN ? _T("min") : _T("avg")),   node->Id());
-               }
-
-               DB_STATEMENT hStmt = DBPrepare(hdb, query);
-               if (hStmt != NULL)
-               {
-                       DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, argv[1]->getValueAsUInt32());
-                       DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, argv[2]->getValueAsInt32());
-                       DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, argv[3]->getValueAsInt32());
-                       DB_RESULT hResult = DBSelectPrepared(hStmt);
-                       if (hResult != NULL)
-                       {
-                               if (DBGetNumRows(hResult) == 1)
-                               {
-                                       result = DBGetFieldDouble(hResult, 0, 0);
-                               }
-                               *ppResult = new NXSL_Value(result);
-                               DBFreeResult(hResult);
-                       }
-                       else
-                       {
-                               *ppResult = new NXSL_Value;     // Return NULL if prepared select failed
-                       }
-                       DBFreeStatement(hStmt);
-               }
-               else
-               {
-                       *ppResult = new NXSL_Value;     // Return NULL if prepare failed
-               }
-
-               DBConnectionPoolReleaseConnection(hdb);
-       }
-
-       return 0;
-}
-
-/**
- * Get min of DCI values for a period
- */
-static int F_GetMinDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_MIN);
-}
-
-/**
- * Get max of DCI values for a period
- */
-static int F_GetMaxDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_MAX);
-}
-
-/**
- * Get average of DCI values for a period
- */
-static int F_GetAvgDCIValue(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       return F_GetDCIValueStat(argc, argv, ppResult, program, DCI_AVG);
-}
-
-/**
- * NXSL function: create new DCI
- * Format: CreateDCI(node, origin, name, description, dataType, pollingInterval, retentionTime)
- * Possible origin values: "agent", "snmp", "internal", "push"
- * Possible dataType values: "int32", "uint32", "int64", "uint64", "float", "string"
- * Returns DCI object on success and NULL of failure
- */
-static int F_CreateDCI(int argc, NXSL_Value **argv, NXSL_Value **ppResult, NXSL_Program *program)
-{
-       if (!argv[0]->isObject())
-               return NXSL_ERR_NOT_OBJECT;
-
-       if (!argv[1]->isString() || !argv[2]->isString() || !argv[3]->isString() || !argv[4]->isString())
-               return NXSL_ERR_NOT_STRING;
-
-       if (!argv[5]->isInteger() || !argv[6]->isInteger())
-               return NXSL_ERR_NOT_INTEGER;
-
-       NXSL_Object *object = argv[0]->getValueAsObject();
-       if (_tcscmp(object->getClass()->getName(), g_nxslNodeClass.getName()))
-               return NXSL_ERR_BAD_CLASS;
-       Node *node = (Node *)object->getData();
-
-       // Origin
-       static const TCHAR *originNames[] = { _T("internal"), _T("agent"), _T("snmp"), _T("cpsnmp"), _T("push"), NULL };
-       int origin = -1;
-       const TCHAR *name = argv[1]->getValueAsCString();
-       for(int i = 0; originNames[i] != NULL; i++)
-               if (!_tcsicmp(originNames[i], name))
-               {
-                       origin = i;
-                       break;
-               }
-
-       // Data types
-       static const TCHAR *dtNames[] = { _T("int32"), _T("uint32"), _T("int64"), _T("uint64"), _T("string"), _T("float"), NULL };
-       int dataType = -1;
-       name = argv[4]->getValueAsCString();
-       for(int i = 0; dtNames[i] != NULL; i++)
-               if (!_tcsicmp(dtNames[i], name))
-               {
-                       dataType = i;
-                       break;
-               }
-
-       int pollingInterval = argv[5]->getValueAsInt32();
-       int retentionTime = argv[6]->getValueAsInt32();
-
-       if ((origin != -1) && (dataType != -1) && (pollingInterval > 0) && (retentionTime > 0))
-       {
-               DCItem *dci = new DCItem(CreateUniqueId(IDG_ITEM), argv[2]->getValueAsCString(), 
-                       origin, dataType, pollingInterval, retentionTime, node, argv[3]->getValueAsCString());
-               node->addDCObject(dci);
-               *ppResult = new NXSL_Value(new NXSL_Object(&g_nxslDciClass, dci));
-       }
-       else
-       {
-               *ppResult = new NXSL_Value;
-       }
-       return 0;
-}
-
-/**
- * Additional NXSL functions for DCI manipulation
- */
-static NXSL_ExtFunction m_nxslDCIFunctions[] =
-{
-   { _T("CreateDCI"), F_CreateDCI, 7 },
-   { _T("FindDCIByName"), F_FindDCIByName, 2 },
-   { _T("FindDCIByDescription"), F_FindDCIByDescription, 2 },
-   { _T("GetDCIObject"), F_GetDCIObject, 2 },
-   { _T("GetDCIValue"), F_GetDCIValue, 2 },
-   { _T("GetDCIValueByName"), F_GetDCIValueByName, 2 },
-   { _T("GetDCIValueByDescription"), F_GetDCIValueByDescription, 2 },
-       { _T("GetMaxDCIValue"), F_GetMaxDCIValue, 4 },
-       { _T("GetMinDCIValue"), F_GetMinDCIValue, 4 },
-       { _T("GetAvgDCIValue"), F_GetAvgDCIValue, 4 }
-};
-
-/**
- * Register DCI-related functions in NXSL environment
- */
-void RegisterDCIFunctions(NXSL_Environment *pEnv)
-{
-       pEnv->registerFunctionSet(sizeof(m_nxslDCIFunctions) / sizeof(NXSL_ExtFunction), m_nxslDCIFunctions);
-}
-
 /**
  * Default constructor for DCItem
  */
@@ -378,6 +30,7 @@ DCItem::DCItem() : DCObject()
    m_thresholds = NULL;
    m_dataType = DCI_DT_INT;
    m_deltaCalculation = DCM_ORIGINAL_VALUE;
+       m_sampleCount = 0;
    m_instance[0] = 0;
    m_transformerSource = NULL;
    m_transformer = NULL;
@@ -402,6 +55,7 @@ DCItem::DCItem(const DCItem *pSrc) : DCObject(pSrc)
 {
    m_dataType = pSrc->m_dataType;
    m_deltaCalculation = pSrc->m_deltaCalculation;
+       m_sampleCount = pSrc->m_sampleCount;
        _tcscpy(m_instance, pSrc->m_instance);
    m_transformerSource = NULL;
    m_transformer = NULL;
@@ -444,7 +98,7 @@ DCItem::DCItem(const DCItem *pSrc) : DCObject(pSrc)
  *    delta_calculation,transformation,template_id,description,instance,
  *    template_item_id,flags,resource_id,proxy_node,base_units,unit_multiplier,
  *    custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,
- *    instd_method,instd_data,instd_filter
+ *    instd_method,instd_data,instd_filter,samples
  */
 DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
 {
@@ -488,6 +142,7 @@ DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
    pszTmp = DBGetField(hResult, iRow, 25, NULL, 0);
        setInstanceFilter(pszTmp);
    free(pszTmp);
+       m_sampleCount = DBGetFieldLong(hResult, iRow, 25);
 
    // Load last raw value from database
        TCHAR szQuery[256];
@@ -519,6 +174,7 @@ DCItem::DCItem(DWORD dwId, const TCHAR *szName, int iSource, int iDataType,
    m_instance[0] = 0;
    m_dataType = iDataType;
    m_deltaCalculation = DCM_ORIGINAL_VALUE;
+       m_sampleCount = 0;
    m_transformerSource = NULL;
    m_transformer = NULL;
    m_thresholds = NULL;
@@ -546,6 +202,7 @@ DCItem::DCItem(ConfigEntry *config, Template *owner) : DCObject(config, owner)
    nx_strncpy(m_instance, config->getSubEntryValue(_T("instance"), 0, _T("")), MAX_DB_STRING);
    m_dataType = (BYTE)config->getSubEntryValueInt(_T("dataType"));
    m_deltaCalculation = (BYTE)config->getSubEntryValueInt(_T("delta"));
+   m_sampleCount = (BYTE)config->getSubEntryValueInt(_T("samples"));
    m_dwCacheSize = 0;
    m_ppValueCache = NULL;
    m_tPrevValueTimeStamp = 0;
@@ -678,7 +335,7 @@ BOOL DCItem::saveToDB(DB_HANDLE hdb)
                  _T("resource_id=?,proxy_node=?,base_units=?,")
                           _T("unit_multiplier=?,custom_units_name=?,perftab_settings=?,")
                      _T("system_tag=?,snmp_port=?,snmp_raw_value_type=?,")
-                                         _T("instd_method=?,instd_data=?,instd_filter=? WHERE item_id=?"));
+                                         _T("instd_method=?,instd_data=?,instd_filter=?,samples=? WHERE item_id=?"));
        }
    else
        {
@@ -688,8 +345,8 @@ BOOL DCItem::saveToDB(DB_HANDLE hdb)
                  _T("transformation,description,instance,template_item_id,flags,")
                  _T("resource_id,proxy_node,base_units,unit_multiplier,")
                           _T("custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,")
-                                         _T("instd_method,instd_data,instd_filter,item_id) VALUES ")
-                          _T("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
+                                         _T("instd_method,instd_data,instd_filter,samples,item_id) VALUES ")
+                          _T("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
        }
        if (hStmt == NULL)
                return FALSE;
@@ -722,7 +379,8 @@ BOOL DCItem::saveToDB(DB_HANDLE hdb)
        DBBind(hStmt, 24, DB_SQLTYPE_INTEGER, (LONG)m_instanceDiscoveryMethod);
        DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_instanceDiscoveryData, DB_BIND_STATIC);
        DBBind(hStmt, 26, DB_SQLTYPE_VARCHAR, m_instanceFilterSource, DB_BIND_STATIC);
-       DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, m_dwId);
+       DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, (LONG)m_sampleCount);
+       DBBind(hStmt, 28, DB_SQLTYPE_INTEGER, m_dwId);
 
    BOOL bResult = DBExecute(hStmt);
        DBFreeStatement(hStmt);
@@ -851,6 +509,7 @@ void DCItem::createMessage(CSCPMessage *pMsg)
    pMsg->SetVariable(VID_INSTANCE, m_instance);
    pMsg->SetVariable(VID_DCI_DATA_TYPE, (WORD)m_dataType);
    pMsg->SetVariable(VID_DCI_DELTA_CALCULATION, (WORD)m_deltaCalculation);
+   pMsg->SetVariable(VID_SAMPLE_COUNT, (WORD)m_sampleCount);
    pMsg->SetVariable(VID_TRANSFORMATION_SCRIPT, CHECK_NULL_EX(m_transformerSource));
        pMsg->SetVariable(VID_BASE_UNITS, (WORD)m_nBaseUnits);
        pMsg->SetVariable(VID_MULTIPLIER, (DWORD)m_nMultiplier);
@@ -905,6 +564,7 @@ void DCItem::updateFromMessage(CSCPMessage *pMsg, DWORD *pdwNumMaps, DWORD **ppd
    pMsg->GetVariableStr(VID_INSTANCE, m_instance, MAX_DB_STRING);
    m_dataType = (BYTE)pMsg->GetVariableShort(VID_DCI_DATA_TYPE);
    m_deltaCalculation = (BYTE)pMsg->GetVariableShort(VID_DCI_DELTA_CALCULATION);
+       m_sampleCount = (int)pMsg->GetVariableShort(VID_SAMPLE_COUNT);
    TCHAR *pszStr = pMsg->GetVariableStr(VID_TRANSFORMATION_SCRIPT);
    setTransformationScript(pszStr);
    safe_free(pszStr);
@@ -1703,6 +1363,7 @@ void DCItem::createNXMPRecord(String &str)
                           _T("\t\t\t\t\t<name>%s</name>\n")
                           _T("\t\t\t\t\t<description>%s</description>\n")
                           _T("\t\t\t\t\t<dataType>%d</dataType>\n")
+                          _T("\t\t\t\t\t<samples>%d</samples>\n")
                           _T("\t\t\t\t\t<origin>%d</origin>\n")
                           _T("\t\t\t\t\t<interval>%d</interval>\n")
                           _T("\t\t\t\t\t<retention>%d</retention>\n")
@@ -1716,7 +1377,7 @@ void DCItem::createNXMPRecord(String &str)
                           _T("\t\t\t\t\t<snmpPort>%d</snmpPort>\n"),
                                                                  (int)m_dwId, (const TCHAR *)EscapeStringForXML2(m_szName),
                           (const TCHAR *)EscapeStringForXML2(m_szDescription),
-                          m_dataType, (int)m_source, m_iPollingInterval, m_iRetentionTime,
+                          m_dataType, m_sampleCount, (int)m_source, m_iPollingInterval, m_iRetentionTime,
                           (const TCHAR *)EscapeStringForXML2(m_instance),
                           (const TCHAR *)EscapeStringForXML2(m_systemTag),
                                                                  (int)m_deltaCalculation, (m_flags & DCF_ADVANCED_SCHEDULE) ? 1 : 0,
index 9b45510..47c14bd 100644 (file)
                                RelativePath=".\dbwrite.cpp"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath=".\dc_nxsl.cpp"\r
+                               >\r
+                       </File>\r
                        <File\r
                                RelativePath=".\dcitem.cpp"\r
                                >\r
index e316251..4781b25 100644 (file)
@@ -285,6 +285,7 @@ protected:
    TCHAR m_instance[MAX_DB_STRING];
    BYTE m_deltaCalculation;      // Delta calculation method
    BYTE m_dataType;
+       int m_sampleCount;            // Number of samples required to calculate value
        ObjectArray<Threshold> *m_thresholds;
    TCHAR *m_transformerSource;   // Transformation script (source code)
    NXSL_Program *m_transformer;  // Compiled transformation script
@@ -336,6 +337,7 @@ public:
        const TCHAR *getInstanceDiscoveryData() { return m_instanceDiscoveryData; }
        NXSL_Program *getInstanceFilter() { return m_instanceFilter; }
        const TCHAR *getInstance() { return m_instance; }
+       int getSampleCount() { return m_sampleCount; }
 
        void filterInstanceList(StringList *instances);
        void expandInstance();
index 111f982..eab70b4 100644 (file)
@@ -309,6 +309,21 @@ static BOOL CreateEventTemplate(int code, const TCHAR *name, int severity, int f
        return SQLQuery(query);
 }
 
+/**
+ * Upgrade from V273 to V274
+ */
+static BOOL H_UpgradeFromV273(int currVersion, int newVersion)
+{
+   static TCHAR batch[] =
+      _T("ALTER TABLE items ADD samples integer\n")
+      _T("UPDATE items SET samples=0\n")
+      _T("<END>");
+   CHK_EXEC(SQLBatch(batch));
+
+       CHK_EXEC(SQLQuery(_T("UPDATE metadata SET var_value='274' WHERE var_name='SchemaVersion'")));
+   return TRUE;
+}
+
 /**
  * Upgrade from V272 to V273
  */
@@ -6743,6 +6758,7 @@ static struct
    { 270, 271, H_UpgradeFromV270 },
    { 271, 272, H_UpgradeFromV271 },
    { 272, 273, H_UpgradeFromV272 },
+   { 273, 274, H_UpgradeFromV273 },
    { 0, 0, NULL }
 };