number of samples casn be configured for table thresholds
authorVictor Kirhenshtein <victor@netxms.org>
Mon, 8 May 2017 13:43:32 +0000 (16:43 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Mon, 8 May 2017 13:43:32 +0000 (16:43 +0300)
17 files changed:
ChangeLog
include/netxms-version.h
include/netxmsdb.h
sql/schema.in
src/java/client/netxms-client/src/main/java/org/netxms/client/ProtocolVersion.java
src/java/client/netxms-client/src/main/java/org/netxms/client/datacollection/DataCollectionTable.java
src/java/client/netxms-client/src/main/java/org/netxms/client/datacollection/TableThreshold.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/dialogs/EditTableThresholdDialog.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/propertypages/TableThresholds.java
src/java/netxms-eclipse/DataCollection/src/org/netxms/ui/eclipse/datacollection/propertypages/helpers/TableThresholdLabelProvider.java
src/server/core/dctable.cpp
src/server/core/dctthreshold.cpp
src/server/include/nms_dcoll.h
src/server/tools/nxdbmgr/upgrade.cpp
webui/webapp/DataCollection/src/org/netxms/ui/eclipse/datacollection/dialogs/EditTableThresholdDialog.java
webui/webapp/DataCollection/src/org/netxms/ui/eclipse/datacollection/propertypages/TableThresholds.java
webui/webapp/DataCollection/src/org/netxms/ui/eclipse/datacollection/propertypages/helpers/TableThresholdLabelProvider.java

index 04cb751..33535b3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,7 @@
 - Network device database (allows some device specific information to be provided without drivers)
 - Server certificate's key can be stored in separate file
 - Multiple CA certificates can be specified in server configuration file
+- Number of samples can be configured for table thresholds
 - Management console:
        - Node's zone name shown in alarm browser and object overview when zoning is enabled
        - Sorting option in table based charts on dashboards (to implement "Top N" style charts)
index 81f20d0..0a1500b 100644 (file)
@@ -48,7 +48,7 @@
 #define CLIENT_PROTOCOL_VERSION_PUSH      1     /* Data push API */
 #define CLIENT_PROTOCOL_VERSION_TRAP      1     /* Event (trap) sending API */
 #define CLIENT_PROTOCOL_VERSION_MOBILE    1     /* All functionality relevant for mobile client */
-#define CLIENT_PROTOCOL_VERSION_FULL      8     /* All functionality */
+#define CLIENT_PROTOCOL_VERSION_FULL      9     /* All functionality */
 
 /**
  * Protocol version positions
index d33c9ee..c0aa872 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   449
+#define DB_FORMAT_VERSION   450
 
 #endif
index 553e127..3d3f5a0 100644 (file)
@@ -698,6 +698,7 @@ CREATE TABLE dct_thresholds
   sequence_number integer not null,
   activation_event integer not null,
   deactivation_event integer not null,
+  sample_count integer not null,
   PRIMARY KEY(id)
 ) TABLE_TYPE;
 
@@ -716,6 +717,18 @@ CREATE TABLE dct_threshold_conditions
 ) TABLE_TYPE;
 
 /*
+** Table threshold instances
+*/
+CREATE TABLE dct_threshold_instances
+(
+  threshold_id integer not null,
+  instance varchar(1024) not null,
+  match_count integer not null,
+  is_active char(1) not null,
+  PRIMARY KEY(threshold_id,instance)
+) TABLE_TYPE;
+
+/*
 ** Schedules for DCIs
 */
 CREATE TABLE dci_schedules
index 9727867..3dc4df6 100644 (file)
@@ -33,7 +33,7 @@ public final class ProtocolVersion
    public static final int PUSH = 1;
    public static final int TRAP = 1;
    public static final int MOBILE = 1;
-   public static final int FULL = 8;
+   public static final int FULL = 9;
    
    // Indexes
    public static final int INDEX_BASE = 0;
index c0bf43e..43337f1 100644 (file)
@@ -53,21 +53,21 @@ public class DataCollectionTable extends DataCollectionObject
                
                int count = msg.getFieldAsInt32(NXCPCodes.VID_NUM_COLUMNS);
                columns = new ArrayList<ColumnDefinition>(count);
-               long varId = NXCPCodes.VID_DCI_COLUMN_BASE;
+               long fieldId = NXCPCodes.VID_DCI_COLUMN_BASE;
                for(int i = 0; i < count; i++)
                {
-                       columns.add(new ColumnDefinition(msg, varId));
-                       varId += 10;
+                       columns.add(new ColumnDefinition(msg, fieldId));
+                       fieldId += 10;
                }
 
                count = msg.getFieldAsInt32(NXCPCodes.VID_NUM_THRESHOLDS);
                thresholds = new ArrayList<TableThreshold>(count);
-               varId = NXCPCodes.VID_DCI_THRESHOLD_BASE;
+               fieldId = NXCPCodes.VID_DCI_THRESHOLD_BASE;
                for(int i = 0; i < count; i++)
                {
-                       final TableThreshold t = new TableThreshold(msg, varId);
+                       final TableThreshold t = new TableThreshold(msg, fieldId);
                        thresholds.add(t);
-                       varId = t.getNextVarId();
+                       fieldId = t.getNextVarId();
                }
        }
 
index 73499cf..876156a 100644 (file)
@@ -32,6 +32,7 @@ public class TableThreshold
        private long id;
        private int activationEvent;
        private int deactivationEvent;
+       private int sampleCount;
        private List<List<TableCondition>> conditions;
        private long nextVarId;
        
@@ -43,6 +44,7 @@ public class TableThreshold
                id = 0;
                activationEvent = 69;
                deactivationEvent = 70;
+               sampleCount = 1;
                conditions = new ArrayList<List<TableCondition>>(0);
        }
        
@@ -56,6 +58,7 @@ public class TableThreshold
                id = src.id;
                activationEvent = src.activationEvent;
                deactivationEvent = src.deactivationEvent;
+               sampleCount = src.sampleCount;
                conditions = new ArrayList<List<TableCondition>>(src.conditions.size());
                for(List<TableCondition> sl : src.conditions)
                {
@@ -74,25 +77,26 @@ public class TableThreshold
         */
        protected TableThreshold(NXCPMessage msg, long baseId)
        {
-               long varId = baseId;
-               id = msg.getFieldAsInt64(varId++);
-               activationEvent = msg.getFieldAsInt32(varId++);
-               deactivationEvent = msg.getFieldAsInt32(varId++);
+               long fieldId = baseId;
+               id = msg.getFieldAsInt64(fieldId++);
+               activationEvent = msg.getFieldAsInt32(fieldId++);
+               deactivationEvent = msg.getFieldAsInt32(fieldId++);
+      sampleCount = msg.getFieldAsInt32(fieldId++);
                
-               int groupCount = msg.getFieldAsInt32(varId++);
+               int groupCount = msg.getFieldAsInt32(fieldId++);
                conditions = new ArrayList<List<TableCondition>>(groupCount);
                for(int i = 0; i < groupCount; i++)
                {
-                       int condCount = msg.getFieldAsInt32(varId++);
+                       int condCount = msg.getFieldAsInt32(fieldId++);
                        List<TableCondition> list = new ArrayList<TableCondition>(condCount);
                        for(int j = 0; j < condCount; j++)
                        {
-                               list.add(new TableCondition(msg, varId));
-                               varId += 3;
+                               list.add(new TableCondition(msg, fieldId));
+                               fieldId += 3;
                        }
                        conditions.add(list);
                }
-               nextVarId = varId;
+               nextVarId = fieldId;
        }
        
        /**
@@ -104,24 +108,25 @@ public class TableThreshold
         */
        protected long fillMessage(NXCPMessage msg, long baseId)
        {
-               long varId = baseId;
+               long fieldId = baseId;
                
-               msg.setFieldInt32(varId++, (int)id);
-               msg.setFieldInt32(varId++, activationEvent);
-               msg.setFieldInt32(varId++, deactivationEvent);
-               msg.setFieldInt32(varId++, conditions.size());
+               msg.setFieldInt32(fieldId++, (int)id);
+               msg.setFieldInt32(fieldId++, activationEvent);
+               msg.setFieldInt32(fieldId++, deactivationEvent);
+      msg.setFieldInt32(fieldId++, sampleCount);
+               msg.setFieldInt32(fieldId++, conditions.size());
                for(List<TableCondition> l : conditions)
                {
-                       msg.setFieldInt32(varId++, l.size());
+                       msg.setFieldInt32(fieldId++, l.size());
                        for(TableCondition c : l)
                        {
-                               msg.setField(varId++, c.getColumn());
-                               msg.setFieldInt16(varId++, c.getOperation());
-                               msg.setField(varId++, c.getValue());
+                               msg.setField(fieldId++, c.getColumn());
+                               msg.setFieldInt16(fieldId++, c.getOperation());
+                               msg.setField(fieldId++, c.getValue());
                        }
                }
                
-               return varId;
+               return fieldId;
        }
        
        /**
@@ -202,6 +207,22 @@ public class TableThreshold
        }
 
        /**
+    * @return the sampleCount
+    */
+   public int getSampleCount()
+   {
+      return sampleCount;
+   }
+
+   /**
+    * @param sampleCount the sampleCount to set
+    */
+   public void setSampleCount(int sampleCount)
+   {
+      this.sampleCount = sampleCount;
+   }
+
+   /**
         * @return the conditions
         */
        public List<List<TableCondition>> getConditions()
index a5e1e6f..15b7ac8 100644 (file)
@@ -32,6 +32,7 @@ import org.netxms.ui.eclipse.datacollection.propertypages.TableColumns.TableColu
 import org.netxms.ui.eclipse.datacollection.widgets.TableConditionsEditor;
 import org.netxms.ui.eclipse.eventmanager.widgets.EventSelector;
 import org.netxms.ui.eclipse.tools.WidgetHelper;
+import org.netxms.ui.eclipse.widgets.LabeledSpinner;
 
 /**
  * Table threshold editing dialog
@@ -41,6 +42,7 @@ public class EditTableThresholdDialog extends Dialog
        private TableThreshold threshold;
        private EventSelector activationEvent;
        private EventSelector deactivationEvent;
+       private LabeledSpinner sampleCount;
        private TableConditionsEditor conditionsEditor;
        private TableColumnDataProvider columnCallback;
        
@@ -87,6 +89,11 @@ public class EditTableThresholdDialog extends Dialog
                deactivationEvent.setEventCode(threshold.getDeactivationEvent());
                deactivationEvent.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
                
+               sampleCount = new LabeledSpinner(dialogArea, SWT.NONE);
+               sampleCount.setLabel("Sample count");
+               sampleCount.setRange(1, 100000);
+               sampleCount.setSelection(threshold.getSampleCount());
+               
                new Label(dialogArea, SWT.NONE).setText(Messages.get().EditTableThresholdDialog_Conditions);
                
                conditionsEditor = new TableConditionsEditor(dialogArea, SWT.BORDER, columnCallback);
@@ -107,6 +114,7 @@ public class EditTableThresholdDialog extends Dialog
        {
                threshold.setActivationEvent((int)activationEvent.getEventCode());
                threshold.setDeactivationEvent((int)deactivationEvent.getEventCode());
+               threshold.setSampleCount(sampleCount.getSelection());
                threshold.setConditions(conditionsEditor.getConditions());
                super.okPressed();
        }       
index 7b3a8ab..768b42e 100644 (file)
@@ -292,6 +292,10 @@ public class TableThresholds extends DCIPropertyPageDialog
                column.setText(Messages.get().TableThresholds_Condition);
                column.setWidth(200);
                
+      column = new TableColumn(table, SWT.LEFT);
+      column.setText("Samples");
+      column.setWidth(90);
+      
                column = new TableColumn(table, SWT.LEFT);
                column.setText(Messages.get().TableThresholds_ActivationEvent);
                column.setWidth(140);
@@ -336,7 +340,6 @@ public class TableThresholds extends DCIPropertyPageDialog
                        if (dlg.open() == Window.OK)
                        {
                                thresholdList.update(t, null);
-                               editor.modify();
                        }
                }
        }
@@ -353,7 +356,6 @@ public class TableThresholds extends DCIPropertyPageDialog
                        thresholds.add(t);
              thresholdList.setInput(thresholds.toArray());
              thresholdList.setSelection(new StructuredSelection(t));
-                       editor.modify();
                }
        }
        
index dc3454e..06b3e32 100644 (file)
@@ -49,12 +49,12 @@ public class TableThresholdLabelProvider extends LabelProvider implements ITable
                {
                        case 0:
                                return thresholdIcon;
-                       case 1:
+                       case 2:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getActivationEvent());
                                return StatusDisplayInfo.getStatusImage((event != null) ? event.getSeverity() : Severity.UNKNOWN);
                        }
-                       case 2:
+                       case 3:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getDeactivationEvent());
                                return StatusDisplayInfo.getStatusImage((event != null) ? event.getSeverity() : Severity.UNKNOWN);
@@ -74,11 +74,13 @@ public class TableThresholdLabelProvider extends LabelProvider implements ITable
                        case 0:
                                return ((TableThreshold)element).getConditionAsText();
                        case 1:
+                          return Integer.toString(((TableThreshold)element).getSampleCount());
+                       case 2:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getActivationEvent());
                                return eventLabelProvider.getText(event);
                        }
-                       case 2:
+                       case 3:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getDeactivationEvent());
                                return eventLabelProvider.getText(event);
index cacf5db..090a282 100644 (file)
@@ -605,7 +605,7 @@ bool DCTable::saveToDatabase(DB_HANDLE hdb)
  */
 bool DCTable::loadThresholds(DB_HANDLE hdb)
 {
-   DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT id,activation_event,deactivation_event FROM dct_thresholds WHERE table_id=? ORDER BY sequence_number"));
+   DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT id,activation_event,deactivation_event,sample_count FROM dct_thresholds WHERE table_id=? ORDER BY sequence_number"));
    if (hStmt == NULL)
       return false;
 
@@ -641,6 +641,17 @@ bool DCTable::saveThresholds(DB_HANDLE hdb)
    }
    DBFreeStatement(hStmt);
 
+   hStmt = DBPrepare(hdb, _T("DELETE FROM dct_threshold_instances WHERE threshold_id=?"));
+   if (hStmt == NULL)
+      return false;
+
+   for(int i = 0; i < m_thresholds->size(); i++)
+   {
+      DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_thresholds->get(i)->getId());
+      DBExecute(hStmt);
+   }
+   DBFreeStatement(hStmt);
+
    hStmt = DBPrepare(hdb, _T("DELETE FROM dct_thresholds WHERE table_id=?"));
    if (hStmt == NULL)
       return false;
index 0504aef..91b1a41 100644 (file)
@@ -1,6 +1,6 @@
 /* 
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2015 Victor Kirhenshtein
+** Copyright (C) 2003-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
 #include "nxcore.h"
 
 /**
+ * Create new threshold instance
+ */
+DCTableThresholdInstance::DCTableThresholdInstance(const TCHAR *name, int matchCount, bool active)
+{
+   m_name = _tcsdup(name);
+   m_matchCount = matchCount;
+   m_active = active;
+}
+
+/**
+ * Copy constructor threshold instance
+ */
+DCTableThresholdInstance::DCTableThresholdInstance(const DCTableThresholdInstance *src)
+{
+   m_name = _tcsdup(src->m_name);
+   m_matchCount = src->m_matchCount;
+   m_active = src->m_active;
+}
+
+/**
+ * Threshold instance destructor
+ */
+DCTableThresholdInstance::~DCTableThresholdInstance()
+{
+   free(m_name);
+}
+
+/**
  * Create new condition
  */
 DCTableCondition::DCTableCondition(const TCHAR *column, int operation, const TCHAR *value)
@@ -309,7 +337,8 @@ DCTableThreshold::DCTableThreshold()
    m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
    m_activationEvent = EVENT_TABLE_THRESHOLD_ACTIVATED;
    m_deactivationEvent = EVENT_TABLE_THRESHOLD_DEACTIVATED;
-   m_activeKeys = new StringSet;
+   m_sampleCount = 1;
+   m_instances = new StringObjectMap<DCTableThresholdInstance>(true);
 }
 
 /**
@@ -323,12 +352,13 @@ DCTableThreshold::DCTableThreshold(DCTableThreshold *src)
       m_groups->add(new DCTableConditionGroup(src->m_groups->get(i)));
    m_activationEvent = src->m_activationEvent;
    m_deactivationEvent = src->m_deactivationEvent;
-   m_activeKeys = new StringSet;
+   m_sampleCount = src->m_sampleCount;
+   m_instances = new StringObjectMap<DCTableThresholdInstance>(true);
 }
 
 /**
  * Create table threshold from database
- * Expected column order: id,activation_event,deactivation_event
+ * Expected column order: id,activation_event,deactivation_event,sample_count
  */
 DCTableThreshold::DCTableThreshold(DB_HANDLE hdb, DB_RESULT hResult, int row)
 {
@@ -336,8 +366,10 @@ DCTableThreshold::DCTableThreshold(DB_HANDLE hdb, DB_RESULT hResult, int row)
    m_activationEvent = DBGetFieldULong(hResult, row, 1);
    m_deactivationEvent = DBGetFieldULong(hResult, row, 2);
    m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
+   m_sampleCount = DBGetFieldLong(hResult, row, 3);
+   m_instances = new StringObjectMap<DCTableThresholdInstance>(true);
    loadConditions(hdb);
-   m_activeKeys = new StringSet;
+   loadInstances(hdb);
 }
 
 /**
@@ -345,18 +377,19 @@ DCTableThreshold::DCTableThreshold(DB_HANDLE hdb, DB_RESULT hResult, int row)
  */
 DCTableThreshold::DCTableThreshold(NXCPMessage *msg, UINT32 *baseId)
 {
-   UINT32 varId = *baseId;
-   m_id = msg->getFieldAsUInt32(varId++);
+   UINT32 fieldId = *baseId;
+   m_id = msg->getFieldAsUInt32(fieldId++);
    if (m_id == 0)
       m_id = CreateUniqueId(IDG_THRESHOLD);
-   m_activationEvent = msg->getFieldAsUInt32(varId++);
-   m_deactivationEvent = msg->getFieldAsUInt32(varId++);
-   int count = (int)msg->getFieldAsUInt32(varId++);
+   m_activationEvent = msg->getFieldAsUInt32(fieldId++);
+   m_deactivationEvent = msg->getFieldAsUInt32(fieldId++);
+   m_sampleCount = msg->getFieldAsUInt32(fieldId++);
+   int count = (int)msg->getFieldAsUInt32(fieldId++);
    m_groups = new ObjectArray<DCTableConditionGroup>(count, 4, true);
-   *baseId = varId;
+   *baseId = fieldId;
    for(int i = 0; i < count; i++)
       m_groups->add(new DCTableConditionGroup(msg, baseId));
-   m_activeKeys = new StringSet;
+   m_instances = new StringObjectMap<DCTableThresholdInstance>(true);
 }
 
 /**
@@ -367,6 +400,7 @@ DCTableThreshold::DCTableThreshold(ConfigEntry *e)
    m_id = CreateUniqueId(IDG_THRESHOLD);
    m_activationEvent = EventCodeFromName(e->getSubEntryValue(_T("activationEvent"), 0, _T("SYS_TABLE_THRESHOLD_ACTIVATED")));
    m_deactivationEvent = EventCodeFromName(e->getSubEntryValue(_T("deactivationEvent"), 0, _T("SYS_TABLE_THRESHOLD_DEACTIVATED")));
+   m_sampleCount = e->getSubEntryValueAsInt(_T("sampleCount"), 0, 1);
 
        ConfigEntry *groupsRoot = e->findEntry(_T("groups"));
        if (groupsRoot != NULL)
@@ -383,7 +417,7 @@ DCTableThreshold::DCTableThreshold(ConfigEntry *e)
        {
        m_groups = new ObjectArray<DCTableConditionGroup>(4, 4, true);
        }
-   m_activeKeys = new StringSet;
+   m_instances = new StringObjectMap<DCTableThresholdInstance>(true);
 }
 
 /**
@@ -426,12 +460,51 @@ void DCTableThreshold::loadConditions(DB_HANDLE hdb)
 }
 
 /**
+ * Load instances from database
+ */
+void DCTableThreshold::loadInstances(DB_HANDLE hdb)
+{
+   DB_STATEMENT hStmt = DBPrepare(hdb, _T("SELECT instance,match_count,is_active FROM dct_threshold_instances WHERE threshold_id=?"));
+   if (hStmt == NULL)
+      return;
+
+   DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
+   DB_RESULT hResult = DBSelectPrepared(hStmt);
+   if (hResult != NULL)
+   {
+      int count = DBGetNumRows(hResult);
+      for(int i = 0; i < count; i++)
+      {
+         TCHAR name[1024];
+         DBGetField(hResult, i, 0, name, 1024);
+         m_instances->set(name, new DCTableThresholdInstance(name, DBGetFieldLong(hResult, i, 1), DBGetFieldLong(hResult, i, 2) ? true : false));
+      }
+      DBFreeResult(hResult);
+   }
+   DBFreeStatement(hStmt);
+}
+
+/**
  * Table threshold destructor
  */
 DCTableThreshold::~DCTableThreshold()
 {
    delete m_groups;
-   delete m_activeKeys;
+   delete m_instances;
+}
+
+/**
+ * Enumeration callback for saving threshold instances
+ */
+static EnumerationCallbackResult SaveThresholdInstancesCallback(const TCHAR *key, const void *value, void *arg)
+{
+   DB_STATEMENT hStmt = (DB_STATEMENT)arg;
+   const DCTableThresholdInstance *i = (const DCTableThresholdInstance *)value;
+   DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, i->getName(), DB_BIND_STATIC);
+   DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, i->getMatchCount());
+   DBBind(hStmt, 4, DB_SQLTYPE_VARCHAR, i->isActive() ? _T("1") : _T("0"), DB_BIND_STATIC);
+   DBExecute(hStmt);
+   return _CONTINUE;
 }
 
 /**
@@ -439,7 +512,7 @@ DCTableThreshold::~DCTableThreshold()
  */
 bool DCTableThreshold::saveToDatabase(DB_HANDLE hdb, UINT32 tableId, int seq)
 {
-   DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO dct_thresholds (id,table_id,sequence_number,activation_event,deactivation_event) VALUES (?,?,?,?,?)"));
+   DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO dct_thresholds (id,table_id,sequence_number,activation_event,deactivation_event,sample_count) VALUES (?,?,?,?,?,?)"));
    if (hStmt == NULL)
       return false;
 
@@ -448,6 +521,7 @@ bool DCTableThreshold::saveToDatabase(DB_HANDLE hdb, UINT32 tableId, int seq)
    DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)seq);
    DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, m_activationEvent);
    DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, m_deactivationEvent);
+   DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, m_sampleCount);
    DBExecute(hStmt);
    DBFreeStatement(hStmt);
 
@@ -474,6 +548,17 @@ bool DCTableThreshold::saveToDatabase(DB_HANDLE hdb, UINT32 tableId, int seq)
       }
       DBFreeStatement(hStmt);
    }
+
+   if (m_instances->size() > 0)
+   {
+      hStmt = DBPrepare(hdb, _T("INSERT INTO dct_threshold_instances (threshold_id,instance,match_count,is_active) VALUES (?,?,?,?)"));
+      if (hStmt == NULL)
+         return false;
+
+      DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
+      m_instances->forEach(SaveThresholdInstancesCallback, hStmt);
+      DBFreeStatement(hStmt);
+   }
    return true;
 }
 
@@ -482,24 +567,26 @@ bool DCTableThreshold::saveToDatabase(DB_HANDLE hdb, UINT32 tableId, int seq)
  */
 UINT32 DCTableThreshold::fillMessage(NXCPMessage *msg, UINT32 baseId)
 {
-   UINT32 varId = baseId;
-   msg->setField(varId++, m_id);
-   msg->setField(varId++, m_activationEvent);
-   msg->setField(varId++, m_deactivationEvent);
-   msg->setField(varId++, (UINT32)m_groups->size());
+   UINT32 fieldId = baseId;
+   msg->setField(fieldId++, m_id);
+   msg->setField(fieldId++, m_activationEvent);
+   msg->setField(fieldId++, m_deactivationEvent);
+   msg->setField(fieldId++, m_sampleCount);
+   msg->setField(fieldId++, (UINT32)m_groups->size());
    for(int i = 0; i < m_groups->size(); i++)
    {
-      varId = m_groups->get(i)->fillMessage(msg, varId);
+      fieldId = m_groups->get(i)->fillMessage(msg, fieldId);
    }
-   return varId;
+   return fieldId;
 }
 
 /**
  * Check threshold
  * Method will return the following codes:
- *    THRESHOLD_REACHED - when value match the threshold condition while previous check doesn't
- *    THRESHOLD_REARMED - when value doesn't match the threshold condition while previous check do
- *    NO_ACTION - when there are no changes in value match to threshold's condition
+ *    ACTIVATED - when value match the threshold condition while previous check doesn't
+ *    DEACTIVATED - when value doesn't match the threshold condition while previous check do
+ *    ALREADY_ACTIVE - when value match the threshold condition and threshold is already active
+ *    ALREADY_INACTIVE - when value doesn't match the threshold condition and threshold is already inactive
  */
 ThresholdCheckResult DCTableThreshold::check(Table *value, int row, const TCHAR *instance)
 {
@@ -507,20 +594,34 @@ ThresholdCheckResult DCTableThreshold::check(Table *value, int row, const TCHAR
    {
       if (m_groups->get(i)->check(value, row))
       {
-         if (m_activeKeys->contains(instance))
+         DCTableThresholdInstance *i = m_instances->get(instance);
+         if (i != NULL)
+         {
+            i->incMatchCount();
+            if (i->isActive())
+               return ALREADY_ACTIVE;
+         }
+         else
          {
-            return ALREADY_ACTIVE;
+            i = new DCTableThresholdInstance(instance, 1, false);
+            m_instances->set(instance, i);
          }
-         m_activeKeys->add(instance);
-         return ACTIVATED;
+         if (i->getMatchCount() >= m_sampleCount)
+         {
+            i->setActive();
+            return ACTIVATED;
+         }
+         return ALREADY_INACTIVE;
       }
    }
 
    // no match
-   if (m_activeKeys->contains(instance))
+   DCTableThresholdInstance *i = m_instances->get(instance);
+   if (i != NULL)
    {
-      m_activeKeys->remove(instance);
-      return DEACTIVATED;
+      bool deactivated = i->isActive();
+      m_instances->remove(instance);
+      return deactivated ? DEACTIVATED : ALREADY_INACTIVE;
    }
    return ALREADY_INACTIVE;
 }
@@ -537,9 +638,11 @@ void DCTableThreshold::createNXMPRecord(String &str, int id)
    str.appendFormattedString(_T("\t\t\t\t\t\t<threshold id=\"%d\">\n")
                           _T("\t\t\t\t\t\t\t<activationEvent>%s</activationEvent>\n")
                           _T("\t\t\t\t\t\t\t<deactivationEvent>%s</deactivationEvent>\n")
+                          _T("\t\t\t\t\t\t\t<sampleCount>%s</sampleCount>\n")
                           _T("\t\t\t\t\t\t\t<groups>\n"),
                                                                  id, (const TCHAR *)EscapeStringForXML2(activationEvent),
-                                                                 (const TCHAR *)EscapeStringForXML2(deactivationEvent));
+                                                                 (const TCHAR *)EscapeStringForXML2(deactivationEvent),
+                                                                 m_sampleCount);
    for(int i = 0; i < m_groups->size(); i++)
    {
       str.appendFormattedString(_T("\t\t\t\t\t\t\t\t<group id=\"%d\">\n\t\t\t\t\t\t\t\t\t<conditions>\n"), i + 1);
@@ -561,10 +664,19 @@ void DCTableThreshold::createNXMPRecord(String &str, int id)
 }
 
 /**
+ * Callback for cloning threshld instances
+ */
+static EnumerationCallbackResult CloneThresholdInstances(const TCHAR *key, const void *value, void *data)
+{
+   ((StringObjectMap<DCTableThresholdInstance> *)data)->set(key, new DCTableThresholdInstance((const DCTableThresholdInstance *)value));
+   return _CONTINUE;
+}
+
+/**
  * Copy threshold state
  */
 void DCTableThreshold::copyState(DCTableThreshold *src)
 {
-   m_activeKeys->clear();
-   m_activeKeys->addAll(src->m_activeKeys);
+   m_instances->clear();
+   src->m_instances->forEach(CloneThresholdInstances, m_instances);
 }
index cbb706b..7f2fc21 100644 (file)
@@ -515,6 +515,29 @@ public:
 };
 
 /**
+ * Threshold instance
+ */
+class NXCORE_EXPORTABLE DCTableThresholdInstance
+{
+private:
+   TCHAR *m_name;
+   int m_matchCount;
+   bool m_active;
+
+public:
+   DCTableThresholdInstance(const TCHAR *name, int matchCount, bool active);
+   DCTableThresholdInstance(const DCTableThresholdInstance *src);
+   ~DCTableThresholdInstance();
+
+   const TCHAR *getName() const { return m_name; }
+   int getMatchCount() const { return m_matchCount; }
+   bool isActive() const { return m_active; }
+
+   void incMatchCount() { m_matchCount++; }
+   void setActive() { m_active = true; }
+};
+
+/**
  * Threshold definition for tabe DCI
  */
 class NXCORE_EXPORTABLE DCTableThreshold
@@ -524,9 +547,11 @@ private:
    ObjectArray<DCTableConditionGroup> *m_groups;
    UINT32 m_activationEvent;
    UINT32 m_deactivationEvent;
-   StringSet *m_activeKeys;
+   int m_sampleCount;
+   StringObjectMap<DCTableThresholdInstance> *m_instances;
 
    void loadConditions(DB_HANDLE hdb);
+   void loadInstances(DB_HANDLE hdb);
 
 public:
    DCTableThreshold();
@@ -547,6 +572,7 @@ public:
    UINT32 getId() const { return m_id; }
    UINT32 getActivationEvent() const { return m_activationEvent; }
    UINT32 getDeactivationEvent() const { return m_deactivationEvent; }
+   int getSampleCount() const { return m_sampleCount; }
 };
 
 /**
index caef0db..b78e681 100644 (file)
@@ -747,6 +747,30 @@ static bool SetSchemaVersion(int version)
 }
 
 /**
+ * Upgrade from V449 to V450
+ */
+static BOOL H_UpgradeFromV449(int currVersion, int newVersion)
+{
+   static const TCHAR *batch =
+            _T("ALTER TABLE dct_thresholds ADD sample_count integer\n")
+            _T("UPDATE dct_thresholds SET sample_count=1\n")
+            _T("<END>");
+   CHK_EXEC(SQLBatch(batch));
+   SetNotNullConstraint(_T("dct_thresholds"), _T("sample_count"));
+
+   CHK_EXEC(CreateTable(
+      _T("CREATE TABLE dct_threshold_instances (")
+      _T("  threshold_id integer not null,")
+      _T("  instance varchar(1024) not null,")
+      _T("  match_count integer not null,")
+      _T("  is_active char(1) not null,")
+      _T("PRIMARY KEY(threshold_id,instance))")));
+
+   CHK_EXEC(SetSchemaVersion(450));
+   return TRUE;
+}
+
+/**
  * Upgrade from V448 to V449
  */
 static BOOL H_UpgradeFromV448(int currVersion, int newVersion)
@@ -11699,6 +11723,7 @@ static struct
    { 446, 447, H_UpgradeFromV446 },
    { 447, 448, H_UpgradeFromV447 },
    { 448, 449, H_UpgradeFromV448 },
+   { 449, 450, H_UpgradeFromV449 },
    { 0, 0, NULL }
 };
 
index a5e1e6f..15b7ac8 100644 (file)
@@ -32,6 +32,7 @@ import org.netxms.ui.eclipse.datacollection.propertypages.TableColumns.TableColu
 import org.netxms.ui.eclipse.datacollection.widgets.TableConditionsEditor;
 import org.netxms.ui.eclipse.eventmanager.widgets.EventSelector;
 import org.netxms.ui.eclipse.tools.WidgetHelper;
+import org.netxms.ui.eclipse.widgets.LabeledSpinner;
 
 /**
  * Table threshold editing dialog
@@ -41,6 +42,7 @@ public class EditTableThresholdDialog extends Dialog
        private TableThreshold threshold;
        private EventSelector activationEvent;
        private EventSelector deactivationEvent;
+       private LabeledSpinner sampleCount;
        private TableConditionsEditor conditionsEditor;
        private TableColumnDataProvider columnCallback;
        
@@ -87,6 +89,11 @@ public class EditTableThresholdDialog extends Dialog
                deactivationEvent.setEventCode(threshold.getDeactivationEvent());
                deactivationEvent.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
                
+               sampleCount = new LabeledSpinner(dialogArea, SWT.NONE);
+               sampleCount.setLabel("Sample count");
+               sampleCount.setRange(1, 100000);
+               sampleCount.setSelection(threshold.getSampleCount());
+               
                new Label(dialogArea, SWT.NONE).setText(Messages.get().EditTableThresholdDialog_Conditions);
                
                conditionsEditor = new TableConditionsEditor(dialogArea, SWT.BORDER, columnCallback);
@@ -107,6 +114,7 @@ public class EditTableThresholdDialog extends Dialog
        {
                threshold.setActivationEvent((int)activationEvent.getEventCode());
                threshold.setDeactivationEvent((int)deactivationEvent.getEventCode());
+               threshold.setSampleCount(sampleCount.getSelection());
                threshold.setConditions(conditionsEditor.getConditions());
                super.okPressed();
        }       
index 7b3a8ab..768b42e 100644 (file)
@@ -292,6 +292,10 @@ public class TableThresholds extends DCIPropertyPageDialog
                column.setText(Messages.get().TableThresholds_Condition);
                column.setWidth(200);
                
+      column = new TableColumn(table, SWT.LEFT);
+      column.setText("Samples");
+      column.setWidth(90);
+      
                column = new TableColumn(table, SWT.LEFT);
                column.setText(Messages.get().TableThresholds_ActivationEvent);
                column.setWidth(140);
@@ -336,7 +340,6 @@ public class TableThresholds extends DCIPropertyPageDialog
                        if (dlg.open() == Window.OK)
                        {
                                thresholdList.update(t, null);
-                               editor.modify();
                        }
                }
        }
@@ -353,7 +356,6 @@ public class TableThresholds extends DCIPropertyPageDialog
                        thresholds.add(t);
              thresholdList.setInput(thresholds.toArray());
              thresholdList.setSelection(new StructuredSelection(t));
-                       editor.modify();
                }
        }
        
index dc3454e..06b3e32 100644 (file)
@@ -49,12 +49,12 @@ public class TableThresholdLabelProvider extends LabelProvider implements ITable
                {
                        case 0:
                                return thresholdIcon;
-                       case 1:
+                       case 2:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getActivationEvent());
                                return StatusDisplayInfo.getStatusImage((event != null) ? event.getSeverity() : Severity.UNKNOWN);
                        }
-                       case 2:
+                       case 3:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getDeactivationEvent());
                                return StatusDisplayInfo.getStatusImage((event != null) ? event.getSeverity() : Severity.UNKNOWN);
@@ -74,11 +74,13 @@ public class TableThresholdLabelProvider extends LabelProvider implements ITable
                        case 0:
                                return ((TableThreshold)element).getConditionAsText();
                        case 1:
+                          return Integer.toString(((TableThreshold)element).getSampleCount());
+                       case 2:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getActivationEvent());
                                return eventLabelProvider.getText(event);
                        }
-                       case 2:
+                       case 3:
                        {
                                final EventTemplate event = session.findEventTemplateByCode(((TableThreshold)element).getDeactivationEvent());
                                return eventLabelProvider.getText(event);