windows performance counters with average value for n samples can be configured direc...
[public/netxms.git] / src / server / core / dcitem.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: dcitem.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default constructor for DCItem
27 */
28 DCItem::DCItem() : DCObject()
29 {
30 m_thresholds = NULL;
31 m_dataType = DCI_DT_INT;
32 m_deltaCalculation = DCM_ORIGINAL_VALUE;
33 m_sampleCount = 0;
34 m_instance[0] = 0;
35 m_transformerSource = NULL;
36 m_transformer = NULL;
37 m_dwCacheSize = 0;
38 m_ppValueCache = NULL;
39 m_tPrevValueTimeStamp = 0;
40 m_bCacheLoaded = false;
41 m_nBaseUnits = DCI_BASEUNITS_OTHER;
42 m_nMultiplier = 1;
43 m_customUnitName = NULL;
44 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
45 m_instanceDiscoveryMethod = IDM_NONE;
46 m_instanceDiscoveryData = NULL;
47 m_instanceFilterSource = NULL;
48 m_instanceFilter = NULL;
49 }
50
51 /**
52 * Create DCItem from another DCItem
53 */
54 DCItem::DCItem(const DCItem *pSrc) : DCObject(pSrc)
55 {
56 m_dataType = pSrc->m_dataType;
57 m_deltaCalculation = pSrc->m_deltaCalculation;
58 m_sampleCount = pSrc->m_sampleCount;
59 _tcscpy(m_instance, pSrc->m_instance);
60 m_transformerSource = NULL;
61 m_transformer = NULL;
62 setTransformationScript(pSrc->m_transformerSource);
63 m_dwCacheSize = 0;
64 m_ppValueCache = NULL;
65 m_tPrevValueTimeStamp = 0;
66 m_bCacheLoaded = false;
67 m_nBaseUnits = pSrc->m_nBaseUnits;
68 m_nMultiplier = pSrc->m_nMultiplier;
69 m_customUnitName = (pSrc->m_customUnitName != NULL) ? _tcsdup(pSrc->m_customUnitName) : NULL;
70 m_snmpRawValueType = pSrc->m_snmpRawValueType;
71 m_instanceDiscoveryMethod = pSrc->m_instanceDiscoveryMethod;
72 m_instanceDiscoveryData = (pSrc->m_instanceDiscoveryData != NULL) ? _tcsdup(pSrc->m_instanceDiscoveryData) : NULL;
73 m_instanceFilterSource = NULL;
74 m_instanceFilter = NULL;
75 setInstanceFilter(pSrc->m_instanceFilterSource);
76
77 // Copy thresholds
78 if (pSrc->getThresholdCount() > 0)
79 {
80 m_thresholds = new ObjectArray<Threshold>(pSrc->m_thresholds->size(), 8, true);
81 for(int i = 0; i < pSrc->m_thresholds->size(); i++)
82 {
83 Threshold *t = new Threshold(pSrc->m_thresholds->get(i));
84 t->createId();
85 m_thresholds->add(t);
86 }
87 }
88 else
89 {
90 m_thresholds = NULL;
91 }
92 }
93
94 /**
95 * Constructor for creating DCItem from database
96 * Assumes that fields in SELECT query are in following order:
97 * item_id,name,source,datatype,polling_interval,retention_time,status,
98 * delta_calculation,transformation,template_id,description,instance,
99 * template_item_id,flags,resource_id,proxy_node,base_units,unit_multiplier,
100 * custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,
101 * instd_method,instd_data,instd_filter,samples
102 */
103 DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
104 {
105 m_dwId = DBGetFieldULong(hResult, iRow, 0);
106 DBGetField(hResult, iRow, 1, m_szName, MAX_ITEM_NAME);
107 m_source = (BYTE)DBGetFieldLong(hResult, iRow, 2);
108 m_dataType = (BYTE)DBGetFieldLong(hResult, iRow, 3);
109 m_iPollingInterval = DBGetFieldLong(hResult, iRow, 4);
110 m_iRetentionTime = DBGetFieldLong(hResult, iRow, 5);
111 m_status = (BYTE)DBGetFieldLong(hResult, iRow, 6);
112 m_deltaCalculation = (BYTE)DBGetFieldLong(hResult, iRow, 7);
113 m_transformerSource = NULL;
114 m_transformer = NULL;
115 TCHAR *pszTmp = DBGetField(hResult, iRow, 8, NULL, 0);
116 setTransformationScript(pszTmp);
117 free(pszTmp);
118 m_dwTemplateId = DBGetFieldULong(hResult, iRow, 9);
119 DBGetField(hResult, iRow, 10, m_szDescription, MAX_DB_STRING);
120 DBGetField(hResult, iRow, 11, m_instance, MAX_DB_STRING);
121 m_dwTemplateItemId = DBGetFieldULong(hResult, iRow, 12);
122 m_thresholds = NULL;
123 m_pNode = pNode;
124 m_dwCacheSize = 0;
125 m_ppValueCache = NULL;
126 m_tPrevValueTimeStamp = 0;
127 m_bCacheLoaded = false;
128 m_flags = (WORD)DBGetFieldLong(hResult, iRow, 13);
129 m_dwResourceId = DBGetFieldULong(hResult, iRow, 14);
130 m_dwProxyNode = DBGetFieldULong(hResult, iRow, 15);
131 m_nBaseUnits = DBGetFieldLong(hResult, iRow, 16);
132 m_nMultiplier = DBGetFieldLong(hResult, iRow, 17);
133 m_customUnitName = DBGetField(hResult, iRow, 18, NULL, 0);
134 m_pszPerfTabSettings = DBGetField(hResult, iRow, 19, NULL, 0);
135 DBGetField(hResult, iRow, 20, m_systemTag, MAX_DB_STRING);
136 m_snmpPort = (WORD)DBGetFieldLong(hResult, iRow, 21);
137 m_snmpRawValueType = (WORD)DBGetFieldLong(hResult, iRow, 22);
138 m_instanceDiscoveryMethod = (WORD)DBGetFieldLong(hResult, iRow, 23);
139 m_instanceDiscoveryData = DBGetField(hResult, iRow, 24, NULL, 0);
140 m_instanceFilterSource = NULL;
141 m_instanceFilter = NULL;
142 pszTmp = DBGetField(hResult, iRow, 25, NULL, 0);
143 setInstanceFilter(pszTmp);
144 free(pszTmp);
145 m_sampleCount = DBGetFieldLong(hResult, iRow, 25);
146
147 // Load last raw value from database
148 TCHAR szQuery[256];
149 _sntprintf(szQuery, 256, _T("SELECT raw_value,last_poll_time FROM raw_dci_values WHERE item_id=%d"), m_dwId);
150 DB_RESULT hTempResult = DBSelect(g_hCoreDB, szQuery);
151 if (hTempResult != NULL)
152 {
153 if (DBGetNumRows(hTempResult) > 0)
154 {
155 TCHAR szBuffer[MAX_DB_STRING];
156 m_prevRawValue = DBGetField(hTempResult, 0, 0, szBuffer, MAX_DB_STRING);
157 m_tPrevValueTimeStamp = DBGetFieldULong(hTempResult, 0, 1);
158 m_tLastPoll = m_tPrevValueTimeStamp;
159 }
160 DBFreeResult(hTempResult);
161 }
162
163 loadCustomSchedules();
164 }
165
166 /**
167 * Constructor for creating new DCItem from scratch
168 */
169 DCItem::DCItem(DWORD dwId, const TCHAR *szName, int iSource, int iDataType,
170 int iPollingInterval, int iRetentionTime, Template *pNode,
171 const TCHAR *pszDescription, const TCHAR *systemTag)
172 : DCObject(dwId, szName, iSource, iPollingInterval, iRetentionTime, pNode, pszDescription, systemTag)
173 {
174 m_instance[0] = 0;
175 m_dataType = iDataType;
176 m_deltaCalculation = DCM_ORIGINAL_VALUE;
177 m_sampleCount = 0;
178 m_transformerSource = NULL;
179 m_transformer = NULL;
180 m_thresholds = NULL;
181 m_dwCacheSize = 0;
182 m_ppValueCache = NULL;
183 m_tPrevValueTimeStamp = 0;
184 m_bCacheLoaded = false;
185 m_nBaseUnits = DCI_BASEUNITS_OTHER;
186 m_nMultiplier = 1;
187 m_customUnitName = NULL;
188 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
189 m_instanceDiscoveryMethod = IDM_NONE;
190 m_instanceDiscoveryData = NULL;
191 m_instanceFilterSource = NULL;
192 m_instanceFilter = NULL;
193
194 updateCacheSize();
195 }
196
197 /**
198 * Create DCItem from import file
199 */
200 DCItem::DCItem(ConfigEntry *config, Template *owner) : DCObject(config, owner)
201 {
202 nx_strncpy(m_instance, config->getSubEntryValue(_T("instance"), 0, _T("")), MAX_DB_STRING);
203 m_dataType = (BYTE)config->getSubEntryValueInt(_T("dataType"));
204 m_deltaCalculation = (BYTE)config->getSubEntryValueInt(_T("delta"));
205 m_sampleCount = (BYTE)config->getSubEntryValueInt(_T("samples"));
206 m_dwCacheSize = 0;
207 m_ppValueCache = NULL;
208 m_tPrevValueTimeStamp = 0;
209 m_bCacheLoaded = false;
210 m_nBaseUnits = DCI_BASEUNITS_OTHER;
211 m_nMultiplier = 1;
212 m_customUnitName = NULL;
213 m_snmpRawValueType = (WORD)config->getSubEntryValueInt(_T("snmpRawValueType"));
214 m_instanceDiscoveryMethod = (WORD)config->getSubEntryValueInt(_T("instanceDiscoveryMethod"));
215 const TCHAR *value = config->getSubEntryValue(_T("instanceDiscoveryData"));
216 m_instanceDiscoveryData = (value != NULL) ? _tcsdup(value) : NULL;
217 m_instanceFilterSource = NULL;
218 m_instanceFilter = NULL;
219 setInstanceFilter(config->getSubEntryValue(_T("instanceFilter")));
220
221 if (config->getSubEntryValueInt(_T("allThresholds")))
222 m_flags |= DCF_ALL_THRESHOLDS;
223 if (config->getSubEntryValueInt(_T("rawValueInOctetString")))
224 m_flags |= DCF_RAW_VALUE_OCTET_STRING;
225
226 m_transformerSource = NULL;
227 m_transformer = NULL;
228 setTransformationScript(config->getSubEntryValue(_T("transformation")));
229
230 ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
231 if (thresholdsRoot != NULL)
232 {
233 ConfigEntryList *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
234 m_thresholds = new ObjectArray<Threshold>(thresholds->getSize(), 8, true);
235 for(int i = 0; i < thresholds->getSize(); i++)
236 {
237 m_thresholds->add(new Threshold(thresholds->getEntry(i), this));
238 }
239 delete thresholds;
240 }
241 else
242 {
243 m_thresholds = NULL;
244 }
245
246 updateCacheSize();
247 }
248
249 /**
250 * Destructor
251 */
252 DCItem::~DCItem()
253 {
254 delete m_thresholds;
255 safe_free(m_transformerSource);
256 delete m_transformer;
257 safe_free(m_instanceFilterSource);
258 delete m_instanceFilter;
259 safe_free(m_customUnitName);
260 clearCache();
261 }
262
263 /**
264 * Delete all thresholds
265 */
266 void DCItem::deleteAllThresholds()
267 {
268 lock();
269 delete_and_null(m_thresholds);
270 unlock();
271 }
272
273 /**
274 * Clear data cache
275 */
276 void DCItem::clearCache()
277 {
278 DWORD i;
279
280 for(i = 0; i < m_dwCacheSize; i++)
281 delete m_ppValueCache[i];
282 safe_free(m_ppValueCache);
283 m_ppValueCache = NULL;
284 m_dwCacheSize = 0;
285 }
286
287 /**
288 * Load data collection items thresholds from database
289 */
290 bool DCItem::loadThresholdsFromDB()
291 {
292 bool result = false;
293
294 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB,
295 _T("SELECT threshold_id,fire_value,rearm_value,check_function,")
296 _T("check_operation,parameter_1,parameter_2,event_code,current_state,")
297 _T("rearm_event_code,repeat_interval,current_severity,")
298 _T("last_event_timestamp FROM thresholds WHERE item_id=? ")
299 _T("ORDER BY sequence_number"));
300 if (hStmt != NULL)
301 {
302 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_dwId);
303 DB_RESULT hResult = DBSelectPrepared(hStmt);
304 if (hResult != NULL)
305 {
306 int count = DBGetNumRows(hResult);
307 if (count > 0)
308 {
309 m_thresholds = new ObjectArray<Threshold>(count, 8, true);
310 for(int i = 0; i < count; i++)
311 m_thresholds->add(new Threshold(hResult, i, this));
312 }
313 DBFreeResult(hResult);
314 result = true;
315 }
316 DBFreeStatement(hStmt);
317 }
318 return result;
319 }
320
321 /**
322 * Save to database
323 */
324 BOOL DCItem::saveToDB(DB_HANDLE hdb)
325 {
326 // Prepare and execute query
327 DB_STATEMENT hStmt;
328 if (IsDatabaseRecordExist(hdb, _T("items"), _T("item_id"), m_dwId))
329 {
330 hStmt = DBPrepare(hdb,
331 _T("UPDATE items SET node_id=?,template_id=?,name=?,source=?,")
332 _T("datatype=?,polling_interval=?,retention_time=?,status=?,")
333 _T("delta_calculation=?,transformation=?,description=?,")
334 _T("instance=?,template_item_id=?,flags=?,")
335 _T("resource_id=?,proxy_node=?,base_units=?,")
336 _T("unit_multiplier=?,custom_units_name=?,perftab_settings=?,")
337 _T("system_tag=?,snmp_port=?,snmp_raw_value_type=?,")
338 _T("instd_method=?,instd_data=?,instd_filter=?,samples=? WHERE item_id=?"));
339 }
340 else
341 {
342 hStmt = DBPrepare(hdb,
343 _T("INSERT INTO items (node_id,template_id,name,source,")
344 _T("datatype,polling_interval,retention_time,status,delta_calculation,")
345 _T("transformation,description,instance,template_item_id,flags,")
346 _T("resource_id,proxy_node,base_units,unit_multiplier,")
347 _T("custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,")
348 _T("instd_method,instd_data,instd_filter,samples,item_id) VALUES ")
349 _T("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
350 }
351 if (hStmt == NULL)
352 return FALSE;
353
354 lock();
355
356 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (m_pNode == NULL) ? 0 : m_pNode->Id());
357 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwTemplateId);
358 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, m_szName, DB_BIND_STATIC);
359 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (LONG)m_source);
360 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (LONG)m_dataType);
361 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (LONG)m_iPollingInterval);
362 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (LONG)m_iRetentionTime);
363 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (LONG)m_status);
364 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, (LONG)m_deltaCalculation);
365 DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, m_transformerSource, DB_BIND_STATIC);
366 DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_szDescription, DB_BIND_STATIC);
367 DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, m_instance, DB_BIND_STATIC);
368 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, m_dwTemplateItemId);
369 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (DWORD)m_flags);
370 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_dwResourceId);
371 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_dwProxyNode);
372 DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, (LONG)m_nBaseUnits);
373 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, (LONG)m_nMultiplier);
374 DBBind(hStmt, 19, DB_SQLTYPE_VARCHAR, m_customUnitName, DB_BIND_STATIC);
375 DBBind(hStmt, 20, DB_SQLTYPE_VARCHAR, m_pszPerfTabSettings, DB_BIND_STATIC);
376 DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_systemTag, DB_BIND_STATIC);
377 DBBind(hStmt, 22, DB_SQLTYPE_INTEGER, (LONG)m_snmpPort);
378 DBBind(hStmt, 23, DB_SQLTYPE_INTEGER, (LONG)m_snmpRawValueType);
379 DBBind(hStmt, 24, DB_SQLTYPE_INTEGER, (LONG)m_instanceDiscoveryMethod);
380 DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_instanceDiscoveryData, DB_BIND_STATIC);
381 DBBind(hStmt, 26, DB_SQLTYPE_VARCHAR, m_instanceFilterSource, DB_BIND_STATIC);
382 DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, (LONG)m_sampleCount);
383 DBBind(hStmt, 28, DB_SQLTYPE_INTEGER, m_dwId);
384
385 BOOL bResult = DBExecute(hStmt);
386 DBFreeStatement(hStmt);
387
388 // Save thresholds
389 if (bResult && (m_thresholds != NULL))
390 {
391 for(int i = 0; i < m_thresholds->size(); i++)
392 m_thresholds->get(i)->saveToDB(hdb, i);
393 }
394
395 // Delete non-existing thresholds
396 TCHAR query[256];
397 _sntprintf(query, 256, _T("SELECT threshold_id FROM thresholds WHERE item_id=%d"), m_dwId);
398 DB_RESULT hResult = DBSelect(hdb, query);
399 if (hResult != NULL)
400 {
401 int iNumRows = DBGetNumRows(hResult);
402 for(int i = 0; i < iNumRows; i++)
403 {
404 DWORD dwId = DBGetFieldULong(hResult, i, 0);
405 int j;
406 for(j = 0; j < getThresholdCount(); j++)
407 if (m_thresholds->get(j)->getId() == dwId)
408 break;
409 if (j == getThresholdCount())
410 {
411 _sntprintf(query, 256, _T("DELETE FROM thresholds WHERE threshold_id=%d"), dwId);
412 DBQuery(hdb, query);
413 }
414 }
415 DBFreeResult(hResult);
416 }
417
418 // Create record in raw_dci_values if needed
419 _sntprintf(query, 256, _T("SELECT item_id FROM raw_dci_values WHERE item_id=%d"), m_dwId);
420 hResult = DBSelect(hdb, query);
421 if (hResult != NULL)
422 {
423 if (DBGetNumRows(hResult) == 0)
424 {
425 _sntprintf(query, 256, _T("INSERT INTO raw_dci_values (item_id,raw_value,last_poll_time) VALUES (%d,%s,%ld)"),
426 m_dwId, (const TCHAR *)DBPrepareString(hdb, m_prevRawValue.getString()), (long)m_tPrevValueTimeStamp);
427 DBQuery(hdb, query);
428 }
429 DBFreeResult(hResult);
430 }
431
432 unlock();
433 return bResult ? DCObject::saveToDB(hdb) : FALSE;
434 }
435
436 /**
437 * Check last value for threshold violations
438 */
439 void DCItem::checkThresholds(ItemValue &value)
440 {
441 const TCHAR *paramNamesReach[] = { _T("dciName"), _T("dciDescription"), _T("thresholdValue"), _T("currentValue"), _T("dciId"), _T("instance"), _T("isRepeatedEvent") };
442 const TCHAR *paramNamesRearm[] = { _T("dciName"), _T("dciDescription"), _T("dciId"), _T("instance"), _T("thresholdValue"), _T("currentValue") };
443
444 if (m_thresholds == NULL)
445 return;
446
447 DWORD dwInterval;
448 ItemValue checkValue;
449 EVENT_TEMPLATE *evt;
450 time_t now = time(NULL);
451
452 for(int i = 0; i < m_thresholds->size(); i++)
453 {
454 Threshold *thr = m_thresholds->get(i);
455 int iResult = thr->check(value, m_ppValueCache, checkValue);
456 switch(iResult)
457 {
458 case THRESHOLD_REACHED:
459 PostEventWithNames(thr->getEventCode(), m_pNode->Id(), "ssssisd",
460 paramNamesReach, m_szName, m_szDescription, thr->getStringValue(),
461 (const TCHAR *)checkValue, m_dwId, m_instance, 0);
462 evt = FindEventTemplateByCode(thr->getEventCode());
463 if (evt != NULL)
464 thr->markLastEvent((int)evt->dwSeverity);
465 if (!(m_flags & DCF_ALL_THRESHOLDS))
466 i = m_thresholds->size(); // Stop processing
467 break;
468 case THRESHOLD_REARMED:
469 PostEventWithNames(thr->getRearmEventCode(), m_pNode->Id(), "ssisss",
470 paramNamesRearm, m_szName, m_szDescription, m_dwId, m_instance,
471 thr->getStringValue(), (const TCHAR *)checkValue);
472 break;
473 case NO_ACTION:
474 if (thr->isReached())
475 {
476 // Check if we need to re-sent threshold violation event
477 if (thr->getRepeatInterval() == -1)
478 dwInterval = g_dwThresholdRepeatInterval;
479 else
480 dwInterval = (DWORD)thr->getRepeatInterval();
481 if ((dwInterval != 0) && (thr->getLastEventTimestamp() + (time_t)dwInterval < now))
482 {
483 PostEventWithNames(thr->getEventCode(), m_pNode->Id(), "ssssisd",
484 paramNamesReach, m_szName, m_szDescription, thr->getStringValue(),
485 (const TCHAR *)checkValue, m_dwId, m_instance, 1);
486 evt = FindEventTemplateByCode(thr->getEventCode());
487 if (evt != NULL)
488 thr->markLastEvent((int)evt->dwSeverity);
489 }
490
491 if (!(m_flags & DCF_ALL_THRESHOLDS))
492 {
493 i = m_thresholds->size(); // Threshold condition still true, stop processing
494 }
495 }
496 break;
497 }
498 }
499 }
500
501 /**
502 * Create NXCP message with item data
503 */
504 void DCItem::createMessage(CSCPMessage *pMsg)
505 {
506 DCObject::createMessage(pMsg);
507
508 lock();
509 pMsg->SetVariable(VID_INSTANCE, m_instance);
510 pMsg->SetVariable(VID_DCI_DATA_TYPE, (WORD)m_dataType);
511 pMsg->SetVariable(VID_DCI_DELTA_CALCULATION, (WORD)m_deltaCalculation);
512 pMsg->SetVariable(VID_SAMPLE_COUNT, (WORD)m_sampleCount);
513 pMsg->SetVariable(VID_TRANSFORMATION_SCRIPT, CHECK_NULL_EX(m_transformerSource));
514 pMsg->SetVariable(VID_BASE_UNITS, (WORD)m_nBaseUnits);
515 pMsg->SetVariable(VID_MULTIPLIER, (DWORD)m_nMultiplier);
516 pMsg->SetVariable(VID_SNMP_RAW_VALUE_TYPE, m_snmpRawValueType);
517 pMsg->SetVariable(VID_INSTD_METHOD, m_instanceDiscoveryMethod);
518 if (m_instanceDiscoveryData != NULL)
519 pMsg->SetVariable(VID_INSTD_DATA, m_instanceDiscoveryData);
520 if (m_instanceFilterSource != NULL)
521 pMsg->SetVariable(VID_INSTD_FILTER, m_instanceFilterSource);
522 if (m_customUnitName != NULL)
523 pMsg->SetVariable(VID_CUSTOM_UNITS_NAME, m_customUnitName);
524 if (m_thresholds != NULL)
525 {
526 pMsg->SetVariable(VID_NUM_THRESHOLDS, (DWORD)m_thresholds->size());
527 DWORD dwId = VID_DCI_THRESHOLD_BASE;
528 for(int i = 0; i < m_thresholds->size(); i++, dwId += 20)
529 m_thresholds->get(i)->createMessage(pMsg, dwId);
530 }
531 else
532 {
533 pMsg->SetVariable(VID_NUM_THRESHOLDS, (DWORD)0);
534 }
535 unlock();
536 }
537
538 /**
539 * Delete item and collected data from database
540 */
541 void DCItem::deleteFromDB()
542 {
543 TCHAR szQuery[256];
544
545 DCObject::deleteFromDB();
546
547 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM items WHERE item_id=%d"), m_dwId);
548 QueueSQLRequest(szQuery);
549 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM idata_%d WHERE item_id=%d"), m_pNode->Id(), m_dwId);
550 QueueSQLRequest(szQuery);
551 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM thresholds WHERE item_id=%d"), m_dwId);
552 QueueSQLRequest(szQuery);
553 }
554
555 /**
556 * Update item from NXCP message
557 */
558 void DCItem::updateFromMessage(CSCPMessage *pMsg, DWORD *pdwNumMaps, DWORD **ppdwMapIndex, DWORD **ppdwMapId)
559 {
560 DCObject::updateFromMessage(pMsg);
561
562 lock();
563
564 pMsg->GetVariableStr(VID_INSTANCE, m_instance, MAX_DB_STRING);
565 m_dataType = (BYTE)pMsg->GetVariableShort(VID_DCI_DATA_TYPE);
566 m_deltaCalculation = (BYTE)pMsg->GetVariableShort(VID_DCI_DELTA_CALCULATION);
567 m_sampleCount = (int)pMsg->GetVariableShort(VID_SAMPLE_COUNT);
568 TCHAR *pszStr = pMsg->GetVariableStr(VID_TRANSFORMATION_SCRIPT);
569 setTransformationScript(pszStr);
570 safe_free(pszStr);
571 m_nBaseUnits = pMsg->GetVariableShort(VID_BASE_UNITS);
572 m_nMultiplier = (int)pMsg->GetVariableLong(VID_MULTIPLIER);
573 safe_free(m_customUnitName);
574 m_customUnitName = pMsg->GetVariableStr(VID_CUSTOM_UNITS_NAME);
575 m_snmpRawValueType = pMsg->GetVariableShort(VID_SNMP_RAW_VALUE_TYPE);
576 m_instanceDiscoveryMethod = pMsg->GetVariableShort(VID_INSTD_METHOD);
577
578 safe_free(m_instanceDiscoveryData);
579 m_instanceDiscoveryData = pMsg->GetVariableStr(VID_INSTD_DATA);
580
581 pszStr = pMsg->GetVariableStr(VID_INSTD_FILTER);
582 setInstanceFilter(pszStr);
583 safe_free(pszStr);
584
585 // Update thresholds
586 DWORD dwNum = pMsg->GetVariableLong(VID_NUM_THRESHOLDS);
587 DWORD *newThresholds = (DWORD *)malloc(sizeof(DWORD) * dwNum);
588 *ppdwMapIndex = (DWORD *)malloc(dwNum * sizeof(DWORD));
589 *ppdwMapId = (DWORD *)malloc(dwNum * sizeof(DWORD));
590 *pdwNumMaps = 0;
591
592 // Read all new threshold ids from message
593 for(DWORD i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
594 {
595 newThresholds[i] = pMsg->GetVariableLong(dwId);
596 }
597
598 // Check if some thresholds was deleted, and reposition others if needed
599 Threshold **ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
600 for(int i = 0; i < getThresholdCount(); i++)
601 {
602 DWORD j;
603 for(j = 0; j < dwNum; j++)
604 if (m_thresholds->get(i)->getId() == newThresholds[j])
605 break;
606 if (j == dwNum)
607 {
608 // No threshold with that id in new list, delete it
609 m_thresholds->remove(i);
610 i--;
611 }
612 else
613 {
614 // Move existing thresholds to appropriate positions in new list
615 ppNewList[j] = m_thresholds->get(i);
616 }
617 }
618
619 // Add or update thresholds
620 for(DWORD i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
621 {
622 if (newThresholds[i] == 0) // New threshold?
623 {
624 ppNewList[i] = new Threshold(this);
625 ppNewList[i]->createId();
626
627 // Add index -> id mapping
628 (*ppdwMapIndex)[*pdwNumMaps] = i;
629 (*ppdwMapId)[*pdwNumMaps] = ppNewList[i]->getId();
630 (*pdwNumMaps)++;
631 }
632 ppNewList[i]->updateFromMessage(pMsg, dwId);
633 }
634
635 if (dwNum > 0)
636 {
637 if (m_thresholds != NULL)
638 {
639 m_thresholds->setOwner(false);
640 m_thresholds->clear();
641 m_thresholds->setOwner(true);
642 }
643 else
644 {
645 m_thresholds = new ObjectArray<Threshold>((int)dwNum, 8, true);
646 }
647 for(DWORD i = 0; i < dwNum; i++)
648 m_thresholds->add(ppNewList[i]);
649 }
650 else
651 {
652 delete_and_null(m_thresholds);
653 }
654
655 safe_free(ppNewList);
656 safe_free(newThresholds);
657 updateCacheSize();
658 unlock();
659 }
660
661 /**
662 * Process new value
663 */
664 void DCItem::processNewValue(time_t tmTimeStamp, void *originalValue)
665 {
666 static int updateRawValueTypes[] = { DB_SQLTYPE_VARCHAR, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER };
667 static int updateValueTypes[] = { DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR };
668
669 ItemValue rawValue, *pValue;
670
671 lock();
672
673 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
674 if (m_pNode == NULL)
675 {
676 unlock();
677 return;
678 }
679
680 m_dwErrorCount = 0;
681
682 // Create new ItemValue object and transform it as needed
683 pValue = new ItemValue((const TCHAR *)originalValue, (DWORD)tmTimeStamp);
684 if (m_tPrevValueTimeStamp == 0)
685 m_prevRawValue = *pValue; // Delta should be zero for first poll
686 rawValue = *pValue;
687 transform(*pValue, tmTimeStamp - m_tPrevValueTimeStamp);
688 m_prevRawValue = rawValue;
689 m_tPrevValueTimeStamp = tmTimeStamp;
690
691 // Prepare SQL statement bindings
692 TCHAR dciId[32], pollTime[32];
693 _sntprintf(dciId, 32, _T("%d"), (int)m_dwId);
694 _sntprintf(pollTime, 32, _T("%ld"), (long)tmTimeStamp);
695
696 // Save raw value into database
697 const TCHAR *values[4];
698 if (_tcslen((const TCHAR *)originalValue) >= MAX_DB_STRING)
699 {
700 // need to be truncated
701 TCHAR *temp = _tcsdup((const TCHAR *)originalValue);
702 temp[MAX_DB_STRING - 1] = 0;
703 values[0] = temp;
704 }
705 else
706 {
707 values[0] = (const TCHAR *)originalValue;
708 }
709 values[1] = pValue->getString();
710 values[2] = pollTime;
711 values[3] = dciId;
712 QueueSQLRequest(_T("UPDATE raw_dci_values SET raw_value=?,transformed_value=?,last_poll_time=? WHERE item_id=?"),
713 4, updateRawValueTypes, values);
714 if ((void *)values[0] != originalValue)
715 free((void *)values[0]);
716
717 // Save transformed value to database
718 QueueIDataInsert(tmTimeStamp, m_pNode->Id(), m_dwId, pValue->getString());
719
720 // Check thresholds and add value to cache
721 checkThresholds(*pValue);
722
723 if (m_dwCacheSize > 0)
724 {
725 delete m_ppValueCache[m_dwCacheSize - 1];
726 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_dwCacheSize - 1));
727 m_ppValueCache[0] = pValue;
728 }
729 else
730 {
731 delete pValue;
732 }
733
734 unlock();
735 }
736
737 /**
738 * Process new data collection error
739 */
740 void DCItem::processNewError()
741 {
742 lock();
743
744 // Normally m_pNode shouldn't be NULL for polled items, but who knows...
745 if (m_pNode == NULL)
746 {
747 unlock();
748 return;
749 }
750
751 m_dwErrorCount++;
752
753 for(int i = 0; i < getThresholdCount(); i++)
754 {
755 Threshold *thr = m_thresholds->get(i);
756 int iResult = thr->checkError(m_dwErrorCount);
757 switch(iResult)
758 {
759 case THRESHOLD_REACHED:
760 PostEvent(thr->getEventCode(), m_pNode->Id(), "ssssis", m_szName,
761 m_szDescription, _T(""), _T(""), m_dwId, m_instance);
762 if (!(m_flags & DCF_ALL_THRESHOLDS))
763 i = m_thresholds->size(); // Stop processing
764 break;
765 case THRESHOLD_REARMED:
766 PostEvent(thr->getRearmEventCode(), m_pNode->Id(), "ssis", m_szName,
767 m_szDescription, m_dwId, m_instance);
768 break;
769 case NO_ACTION:
770 if (thr->isReached() && !(m_flags & DCF_ALL_THRESHOLDS))
771 i = m_thresholds->size(); // Threshold condition still true, stop processing
772 break;
773 }
774 }
775
776 unlock();
777 }
778
779 /**
780 * Transform received value
781 */
782 void DCItem::transform(ItemValue &value, time_t nElapsedTime)
783 {
784 switch(m_deltaCalculation)
785 {
786 case DCM_SIMPLE:
787 switch(m_dataType)
788 {
789 case DCI_DT_INT:
790 value = (LONG)value - (LONG)m_prevRawValue;
791 break;
792 case DCI_DT_UINT:
793 value = (DWORD)value - (DWORD)m_prevRawValue;
794 break;
795 case DCI_DT_INT64:
796 value = (INT64)value - (INT64)m_prevRawValue;
797 break;
798 case DCI_DT_UINT64:
799 value = (QWORD)value - (QWORD)m_prevRawValue;
800 break;
801 case DCI_DT_FLOAT:
802 value = (double)value - (double)m_prevRawValue;
803 break;
804 case DCI_DT_STRING:
805 value = (LONG)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
806 break;
807 default:
808 // Delta calculation is not supported for other types
809 break;
810 }
811 break;
812 case DCM_AVERAGE_PER_MINUTE:
813 nElapsedTime /= 60; // Convert to minutes
814 case DCM_AVERAGE_PER_SECOND:
815 // Check elapsed time to prevent divide-by-zero exception
816 if (nElapsedTime == 0)
817 nElapsedTime++;
818
819 switch(m_dataType)
820 {
821 case DCI_DT_INT:
822 value = ((LONG)value - (LONG)m_prevRawValue) / (LONG)nElapsedTime;
823 break;
824 case DCI_DT_UINT:
825 value = ((DWORD)value - (DWORD)m_prevRawValue) / (DWORD)nElapsedTime;
826 break;
827 case DCI_DT_INT64:
828 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
829 break;
830 case DCI_DT_UINT64:
831 value = ((QWORD)value - (QWORD)m_prevRawValue) / (QWORD)nElapsedTime;
832 break;
833 case DCI_DT_FLOAT:
834 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
835 break;
836 case DCI_DT_STRING:
837 // I don't see any meaning in _T("average delta per second (minute)") for string
838 // values, so result will be 0 if there are no difference between
839 // current and previous values, and 1 otherwise
840 value = (LONG)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
841 break;
842 default:
843 // Delta calculation is not supported for other types
844 break;
845 }
846 break;
847 default: // Default is no transformation
848 break;
849 }
850
851 if (m_transformer != NULL)
852 {
853 NXSL_Value *pValue;
854 NXSL_ServerEnv *pEnv;
855
856 pValue = new NXSL_Value((const TCHAR *)value);
857 pEnv = new NXSL_ServerEnv;
858 m_transformer->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
859 m_transformer->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
860
861 if (m_transformer->run(pEnv, 1, &pValue) == 0)
862 {
863 pValue = m_transformer->getResult();
864 if (pValue != NULL)
865 {
866 switch(m_dataType)
867 {
868 case DCI_DT_INT:
869 value = pValue->getValueAsInt32();
870 break;
871 case DCI_DT_UINT:
872 value = pValue->getValueAsUInt32();
873 break;
874 case DCI_DT_INT64:
875 value = pValue->getValueAsInt64();
876 break;
877 case DCI_DT_UINT64:
878 value = pValue->getValueAsUInt64();
879 break;
880 case DCI_DT_FLOAT:
881 value = pValue->getValueAsReal();
882 break;
883 case DCI_DT_STRING:
884 value = CHECK_NULL_EX(pValue->getValueAsCString());
885 break;
886 default:
887 break;
888 }
889 }
890 }
891 else
892 {
893 TCHAR szBuffer[1024];
894
895 _sntprintf(szBuffer, 1024, _T("DCI::%s::%d::Transformer"),
896 (m_pNode != NULL) ? m_pNode->Name() : _T("(null)"), m_dwId);
897 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", szBuffer,
898 m_transformer->getErrorText(), m_dwId);
899 }
900 }
901 }
902
903 /**
904 * Set new ID and node/template association
905 */
906 void DCItem::changeBinding(DWORD dwNewId, Template *pNewNode, BOOL doMacroExpansion)
907 {
908 DCObject::changeBinding(dwNewId, pNewNode, doMacroExpansion);
909
910 lock();
911 if (dwNewId != 0)
912 {
913 for(int i = 0; i < getThresholdCount(); i++)
914 m_thresholds->get(i)->bindToItem(m_dwId);
915 }
916
917 if (doMacroExpansion)
918 expandMacros(m_instance, m_instance, MAX_DB_STRING);
919
920 clearCache();
921 updateCacheSize();
922 unlock();
923 }
924
925 /**
926 * Update required cache size depending on thresholds
927 * dwCondId is an identifier of calling condition object id. If it is not 0,
928 * GetCacheSizeForDCI should be called with bNoLock == TRUE for appropriate
929 * condition object
930 */
931 void DCItem::updateCacheSize(DWORD dwCondId)
932 {
933 DWORD dwSize, dwRequiredSize;
934
935 // Sanity check
936 if (m_pNode == NULL)
937 {
938 DbgPrintf(3, _T("DCItem::updateCacheSize() called for DCI %d when m_pNode == NULL"), m_dwId);
939 return;
940 }
941
942 // Minimum cache size is 1 for nodes (so GetLastValue can work)
943 // and it is always 0 for templates
944 if ((m_pNode->Type() == OBJECT_NODE) || (m_pNode->Type() == OBJECT_MOBILEDEVICE))
945 {
946 dwRequiredSize = 1;
947
948 // Calculate required cache size
949 for(int i = 0; i < getThresholdCount(); i++)
950 if (dwRequiredSize < m_thresholds->get(i)->getRequiredCacheSize())
951 dwRequiredSize = m_thresholds->get(i)->getRequiredCacheSize();
952
953 ObjectArray<NetObj> *conditions = g_idxConditionById.getObjects();
954 for(int i = 0; i < conditions->size(); i++)
955 {
956 Condition *c = (Condition *)conditions->get(i);
957 dwSize = c->getCacheSizeForDCI(m_dwId, dwCondId == c->Id());
958 if (dwSize > dwRequiredSize)
959 dwRequiredSize = dwSize;
960 }
961 delete conditions;
962 }
963 else
964 {
965 dwRequiredSize = 0;
966 }
967
968 // Update cache if needed
969 if (dwRequiredSize < m_dwCacheSize)
970 {
971 // Destroy unneeded values
972 if (m_dwCacheSize > 0)
973 {
974 for(DWORD i = dwRequiredSize; i < m_dwCacheSize; i++)
975 delete m_ppValueCache[i];
976 }
977
978 m_dwCacheSize = dwRequiredSize;
979 if (m_dwCacheSize > 0)
980 {
981 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_dwCacheSize);
982 }
983 else
984 {
985 safe_free(m_ppValueCache);
986 m_ppValueCache = NULL;
987 }
988 }
989 else if (dwRequiredSize > m_dwCacheSize)
990 {
991 // Expand cache
992 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * dwRequiredSize);
993 for(DWORD i = m_dwCacheSize; i < dwRequiredSize; i++)
994 m_ppValueCache[i] = NULL;
995
996 // Load missing values from database
997 if (m_pNode != NULL)
998 {
999 TCHAR szBuffer[MAX_DB_STRING];
1000 BOOL bHasData;
1001
1002 switch(g_nDBSyntax)
1003 {
1004 case DB_SYNTAX_MSSQL:
1005 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT TOP %d idata_value,idata_timestamp FROM idata_%d ")
1006 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1007 dwRequiredSize, m_pNode->Id(), m_dwId);
1008 break;
1009 case DB_SYNTAX_ORACLE:
1010 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1011 _T("WHERE item_id=%d AND ROWNUM <= %d ORDER BY idata_timestamp DESC"),
1012 m_pNode->Id(), m_dwId, dwRequiredSize);
1013 break;
1014 case DB_SYNTAX_MYSQL:
1015 case DB_SYNTAX_PGSQL:
1016 case DB_SYNTAX_SQLITE:
1017 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1018 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC LIMIT %d"),
1019 m_pNode->Id(), m_dwId, dwRequiredSize);
1020 break;
1021 default:
1022 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1023 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1024 m_pNode->Id(), m_dwId);
1025 break;
1026 }
1027 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1028 DB_ASYNC_RESULT hResult = DBAsyncSelect(hdb, szBuffer);
1029 if (hResult != NULL)
1030 {
1031 // Skip already cached values
1032 DWORD i;
1033 for(i = 0, bHasData = TRUE; i < m_dwCacheSize; i++)
1034 bHasData = DBFetch(hResult);
1035
1036 // Create new cache entries
1037 for(; (i < dwRequiredSize) && bHasData; i++)
1038 {
1039 bHasData = DBFetch(hResult);
1040 if (bHasData)
1041 {
1042 DBGetFieldAsync(hResult, 0, szBuffer, MAX_DB_STRING);
1043 m_ppValueCache[i] = new ItemValue(szBuffer, DBGetFieldAsyncULong(hResult, 1));
1044 }
1045 else
1046 {
1047 m_ppValueCache[i] = new ItemValue(_T(""), 1); // Empty value
1048 }
1049 }
1050
1051 // Fill up cache with empty values if we don't have enough values in database
1052 for(; i < dwRequiredSize; i++)
1053 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1054
1055 DBFreeAsyncResult(hResult);
1056 }
1057 else
1058 {
1059 // Error reading data from database, fill cache with empty values
1060 for(DWORD i = m_dwCacheSize; i < dwRequiredSize; i++)
1061 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1062 }
1063 DBConnectionPoolReleaseConnection(hdb);
1064 }
1065 m_dwCacheSize = dwRequiredSize;
1066 }
1067 m_bCacheLoaded = true;
1068 }
1069
1070 /**
1071 * Put last value into CSCP message
1072 */
1073 void DCItem::getLastValue(CSCPMessage *pMsg, DWORD dwId)
1074 {
1075 lock();
1076 pMsg->SetVariable(dwId++, m_dwId);
1077 pMsg->SetVariable(dwId++, m_szName);
1078 pMsg->SetVariable(dwId++, m_szDescription);
1079 pMsg->SetVariable(dwId++, (WORD)m_source);
1080 if (m_dwCacheSize > 0)
1081 {
1082 pMsg->SetVariable(dwId++, (WORD)m_dataType);
1083 pMsg->SetVariable(dwId++, (TCHAR *)m_ppValueCache[0]->getString());
1084 pMsg->SetVariable(dwId++, m_ppValueCache[0]->getTimeStamp());
1085 }
1086 else
1087 {
1088 pMsg->SetVariable(dwId++, (WORD)DCI_DT_NULL);
1089 pMsg->SetVariable(dwId++, _T(""));
1090 pMsg->SetVariable(dwId++, (DWORD)0);
1091 }
1092 pMsg->SetVariable(dwId++, (WORD)m_status);
1093 pMsg->SetVariable(dwId++, (WORD)getType());
1094 pMsg->SetVariable(dwId++, m_dwErrorCount);
1095 pMsg->SetVariable(dwId++, m_dwTemplateItemId);
1096
1097 int i;
1098 for(i = 0; i < getThresholdCount(); i++)
1099 {
1100 if (m_thresholds->get(i)->isReached())
1101 break;
1102 }
1103 if (i < getThresholdCount())
1104 {
1105 pMsg->SetVariable(dwId++, (WORD)1);
1106 m_thresholds->get(i)->createMessage(pMsg, dwId);
1107 }
1108 else
1109 {
1110 pMsg->SetVariable(dwId++, (WORD)0);
1111 }
1112
1113 unlock();
1114 }
1115
1116 /**
1117 * Get item's last value for use in NXSL
1118 */
1119 NXSL_Value *DCItem::getValueForNXSL(int nFunction, int nPolls)
1120 {
1121 NXSL_Value *pValue;
1122
1123 lock();
1124 switch(nFunction)
1125 {
1126 case F_LAST:
1127 pValue = (m_dwCacheSize > 0) ? new NXSL_Value((TCHAR *)m_ppValueCache[0]->getString()) : new NXSL_Value;
1128 break;
1129 case F_DIFF:
1130 if (m_dwCacheSize >= 2)
1131 {
1132 ItemValue result;
1133
1134 CalculateItemValueDiff(result, m_dataType, *m_ppValueCache[0], *m_ppValueCache[1]);
1135 pValue = new NXSL_Value(result.getString());
1136 }
1137 else
1138 {
1139 pValue = new NXSL_Value;
1140 }
1141 break;
1142 case F_AVERAGE:
1143 if (m_dwCacheSize > 0)
1144 {
1145 ItemValue result;
1146
1147 CalculateItemValueAverage(result, m_dataType,
1148 min(m_dwCacheSize, (DWORD)nPolls), m_ppValueCache);
1149 pValue = new NXSL_Value(result.getString());
1150 }
1151 else
1152 {
1153 pValue = new NXSL_Value;
1154 }
1155 break;
1156 case F_DEVIATION:
1157 if (m_dwCacheSize > 0)
1158 {
1159 ItemValue result;
1160
1161 CalculateItemValueMD(result, m_dataType,
1162 min(m_dwCacheSize, (DWORD)nPolls), m_ppValueCache);
1163 pValue = new NXSL_Value(result.getString());
1164 }
1165 else
1166 {
1167 pValue = new NXSL_Value;
1168 }
1169 break;
1170 case F_ERROR:
1171 pValue = new NXSL_Value((LONG)((m_dwErrorCount >= (DWORD)nPolls) ? 1 : 0));
1172 break;
1173 default:
1174 pValue = new NXSL_Value;
1175 break;
1176 }
1177 unlock();
1178 return pValue;
1179 }
1180
1181
1182 //
1183 // Clean expired data
1184 //
1185
1186 void DCItem::deleteExpiredData()
1187 {
1188 TCHAR szQuery[256];
1189 time_t now;
1190
1191 now = time(NULL);
1192 lock();
1193 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE (item_id=%d) AND (idata_timestamp<%ld)"),
1194 (int)m_pNode->Id(), (int)m_dwId, (long)(now - (time_t)m_iRetentionTime * 86400));
1195 unlock();
1196 QueueSQLRequest(szQuery);
1197 }
1198
1199
1200 //
1201 // Delete all collected data
1202 //
1203
1204 bool DCItem::deleteAllData()
1205 {
1206 TCHAR szQuery[256];
1207 bool success;
1208
1209 lock();
1210 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE item_id=%d"), m_pNode->Id(), m_dwId);
1211 success = DBQuery(g_hCoreDB, szQuery) ? true : false;
1212 clearCache();
1213 updateCacheSize();
1214 unlock();
1215 return success;
1216 }
1217
1218 /**
1219 * Update from template item
1220 */
1221 void DCItem::updateFromTemplate(DCObject *src)
1222 {
1223 DCObject::updateFromTemplate(src);
1224
1225 if (src->getType() != DCO_TYPE_ITEM)
1226 {
1227 DbgPrintf(2, _T("INTERNAL ERROR: DCItem::updateFromTemplate(%d, %d): source type is %d"), (int)m_dwId, (int)src->getId(), src->getType());
1228 return;
1229 }
1230
1231 lock();
1232 DCItem *item = (DCItem *)src;
1233
1234 m_dataType = item->m_dataType;
1235 m_deltaCalculation = item->m_deltaCalculation;
1236 setTransformationScript(item->m_transformerSource);
1237
1238 m_nBaseUnits = item->m_nBaseUnits;
1239 m_nMultiplier = item->m_nMultiplier;
1240 safe_free(m_customUnitName);
1241 m_customUnitName = (item->m_customUnitName != NULL) ? _tcsdup(item->m_customUnitName) : NULL;
1242
1243 // Copy thresholds
1244 // ***************************
1245 // First, skip matching thresholds
1246 int count = min(getThresholdCount(), item->getThresholdCount());
1247 int i;
1248 for(i = 0; i < count; i++)
1249 if (!m_thresholds->get(i)->compare(item->m_thresholds->get(i)))
1250 break;
1251 count = i; // First unmatched threshold's position
1252
1253 // Delete all original thresholds starting from first unmatched
1254 while(count < getThresholdCount())
1255 m_thresholds->remove(count);
1256
1257 // (Re)create thresholds starting from first unmatched
1258 if ((m_thresholds == NULL) && (item->getThresholdCount() > 0))
1259 m_thresholds = new ObjectArray<Threshold>(item->getThresholdCount(), 8, true);
1260 for(i = count; i < item->getThresholdCount(); i++)
1261 {
1262 Threshold *t = new Threshold(item->m_thresholds->get(i));
1263 t->createId();
1264 t->bindToItem(m_dwId);
1265 m_thresholds->add(t);
1266 }
1267
1268 expandMacros(item->m_instance, m_instance, MAX_DB_STRING);
1269
1270 updateCacheSize();
1271
1272 unlock();
1273 }
1274
1275 /**
1276 * Set new transformation script
1277 */
1278 void DCItem::setTransformationScript(const TCHAR *pszScript)
1279 {
1280 safe_free(m_transformerSource);
1281 delete m_transformer;
1282 if (pszScript != NULL)
1283 {
1284 m_transformerSource = _tcsdup(pszScript);
1285 StrStrip(m_transformerSource);
1286 if (m_transformerSource[0] != 0)
1287 {
1288 /* TODO: add compilation error handling */
1289 m_transformer = (NXSL_Program *)NXSLCompile(m_transformerSource, NULL, 0);
1290 }
1291 else
1292 {
1293 m_transformer = NULL;
1294 }
1295 }
1296 else
1297 {
1298 m_transformerSource = NULL;
1299 m_transformer = NULL;
1300 }
1301 }
1302
1303 /**
1304 * Set new instance discovery filter script
1305 */
1306 void DCItem::setInstanceFilter(const TCHAR *pszScript)
1307 {
1308 safe_free(m_instanceFilterSource);
1309 delete m_instanceFilter;
1310 if (pszScript != NULL)
1311 {
1312 m_instanceFilterSource = _tcsdup(pszScript);
1313 StrStrip(m_instanceFilterSource);
1314 if (m_instanceFilterSource[0] != 0)
1315 {
1316 /* TODO: add compilation error handling */
1317 m_instanceFilter = (NXSL_Program *)NXSLCompile(m_instanceFilterSource, NULL, 0);
1318 }
1319 else
1320 {
1321 m_instanceFilter = NULL;
1322 }
1323 }
1324 else
1325 {
1326 m_instanceFilterSource = NULL;
1327 m_instanceFilter = NULL;
1328 }
1329 }
1330
1331 /**
1332 * Get list of used events
1333 */
1334 void DCItem::getEventList(DWORD **ppdwList, DWORD *pdwSize)
1335 {
1336 lock();
1337
1338 if (getThresholdCount() > 0)
1339 {
1340 *ppdwList = (DWORD *)realloc(*ppdwList, sizeof(DWORD) * (*pdwSize + m_thresholds->size() * 2));
1341 DWORD j = *pdwSize;
1342 *pdwSize += m_thresholds->size() * 2;
1343 for(int i = 0; i < m_thresholds->size(); i++)
1344 {
1345 (*ppdwList)[j++] = m_thresholds->get(i)->getEventCode();
1346 (*ppdwList)[j++] = m_thresholds->get(i)->getRearmEventCode();
1347 }
1348 }
1349
1350 unlock();
1351 }
1352
1353 /**
1354 * Create management pack record
1355 */
1356 void DCItem::createNXMPRecord(String &str)
1357 {
1358 DWORD i;
1359
1360 lock();
1361
1362 str.addFormattedString(_T("\t\t\t\t<dci id=\"%d\">\n")
1363 _T("\t\t\t\t\t<name>%s</name>\n")
1364 _T("\t\t\t\t\t<description>%s</description>\n")
1365 _T("\t\t\t\t\t<dataType>%d</dataType>\n")
1366 _T("\t\t\t\t\t<samples>%d</samples>\n")
1367 _T("\t\t\t\t\t<origin>%d</origin>\n")
1368 _T("\t\t\t\t\t<interval>%d</interval>\n")
1369 _T("\t\t\t\t\t<retention>%d</retention>\n")
1370 _T("\t\t\t\t\t<instance>%s</instance>\n")
1371 _T("\t\t\t\t\t<systemTag>%s</systemTag>\n")
1372 _T("\t\t\t\t\t<delta>%d</delta>\n")
1373 _T("\t\t\t\t\t<advancedSchedule>%d</advancedSchedule>\n")
1374 _T("\t\t\t\t\t<allThresholds>%d</allThresholds>\n")
1375 _T("\t\t\t\t\t<rawValueInOctetString>%d</rawValueInOctetString>\n")
1376 _T("\t\t\t\t\t<snmpRawValueType>%d</snmpRawValueType>\n")
1377 _T("\t\t\t\t\t<snmpPort>%d</snmpPort>\n"),
1378 (int)m_dwId, (const TCHAR *)EscapeStringForXML2(m_szName),
1379 (const TCHAR *)EscapeStringForXML2(m_szDescription),
1380 m_dataType, m_sampleCount, (int)m_source, m_iPollingInterval, m_iRetentionTime,
1381 (const TCHAR *)EscapeStringForXML2(m_instance),
1382 (const TCHAR *)EscapeStringForXML2(m_systemTag),
1383 (int)m_deltaCalculation, (m_flags & DCF_ADVANCED_SCHEDULE) ? 1 : 0,
1384 (m_flags & DCF_ALL_THRESHOLDS) ? 1 : 0,
1385 (m_flags & DCF_RAW_VALUE_OCTET_STRING) ? 1 : 0,
1386 (int)m_snmpRawValueType, (int)m_snmpPort);
1387
1388 if (m_transformerSource != NULL)
1389 {
1390 str += _T("\t\t\t\t\t<transformation>");
1391 str.addDynamicString(EscapeStringForXML(m_transformerSource, -1));
1392 str += _T("</transformation>\n");
1393 }
1394
1395 if (m_dwNumSchedules > 0)
1396 {
1397 str += _T("\t\t\t\t\t<schedules>\n");
1398 for(i = 0; i < m_dwNumSchedules; i++)
1399 str.addFormattedString(_T("\t\t\t\t\t\t<schedule>%s</schedule>\n"), (const TCHAR *)EscapeStringForXML2(m_ppScheduleList[i]));
1400 str += _T("\t\t\t\t\t</schedules>\n");
1401 }
1402
1403 if (m_thresholds != NULL)
1404 {
1405 str += _T("\t\t\t\t\t<thresholds>\n");
1406 for(i = 0; i < (DWORD)m_thresholds->size(); i++)
1407 {
1408 m_thresholds->get(i)->createNXMPRecord(str, i + 1);
1409 }
1410 str += _T("\t\t\t\t\t</thresholds>\n");
1411 }
1412
1413 unlock();
1414 str += _T("\t\t\t\t</dci>\n");
1415 }
1416
1417 /**
1418 * Add threshold to the list
1419 */
1420 void DCItem::addThreshold(Threshold *pThreshold)
1421 {
1422 if (m_thresholds == NULL)
1423 m_thresholds = new ObjectArray<Threshold>(8, 8, true);
1424 m_thresholds->add(pThreshold);
1425 }
1426
1427 /**
1428 * Enumerate all thresholds
1429 */
1430 BOOL DCItem::enumThresholds(BOOL (* pfCallback)(Threshold *, DWORD, void *), void *pArg)
1431 {
1432 BOOL bRet = TRUE;
1433
1434 lock();
1435 if (m_thresholds != NULL)
1436 {
1437 for(int i = 0; i < m_thresholds->size(); i++)
1438 {
1439 if (!pfCallback(m_thresholds->get(i), i, pArg))
1440 {
1441 bRet = FALSE;
1442 break;
1443 }
1444 }
1445 }
1446 unlock();
1447 return bRet;
1448 }
1449
1450 /**
1451 * Test DCI's transformation script
1452 */
1453 BOOL DCItem::testTransformation(const TCHAR *script, const TCHAR *value, TCHAR *buffer, size_t bufSize)
1454 {
1455 BOOL success = FALSE;
1456 NXSL_Program *pScript;
1457
1458 pScript = NXSLCompile(script, buffer, (int)bufSize);
1459 if (pScript != NULL)
1460 {
1461 NXSL_Value *pValue;
1462 NXSL_ServerEnv *pEnv;
1463
1464 pValue = new NXSL_Value(value);
1465 pEnv = new NXSL_ServerEnv;
1466 pScript->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
1467
1468 lock();
1469 if (pScript->run(pEnv, 1, &pValue) == 0)
1470 {
1471 pValue = pScript->getResult();
1472 if (pValue != NULL)
1473 {
1474 if (pValue->isNull())
1475 {
1476 nx_strncpy(buffer, _T("(null)"), bufSize);
1477 }
1478 else if (pValue->isObject())
1479 {
1480 nx_strncpy(buffer, _T("(object)"), bufSize);
1481 }
1482 else if (pValue->isArray())
1483 {
1484 nx_strncpy(buffer, _T("(array)"), bufSize);
1485 }
1486 else
1487 {
1488 const TCHAR *strval;
1489
1490 strval = pValue->getValueAsCString();
1491 nx_strncpy(buffer, CHECK_NULL(strval), bufSize);
1492 }
1493 }
1494 else
1495 {
1496 nx_strncpy(buffer, _T("(null)"), bufSize);
1497 }
1498 success = TRUE;
1499 }
1500 else
1501 {
1502 nx_strncpy(buffer, pScript->getErrorText(), bufSize);
1503 }
1504 unlock();
1505 }
1506 return success;
1507 }
1508
1509 /**
1510 * Fill NXCP message with thresholds
1511 */
1512 void DCItem::fillMessageWithThresholds(CSCPMessage *msg)
1513 {
1514 lock();
1515
1516 msg->SetVariable(VID_NUM_THRESHOLDS, (DWORD)getThresholdCount());
1517 DWORD id = VID_DCI_THRESHOLD_BASE;
1518 for(int i = 0; i < getThresholdCount(); i++, id += 20)
1519 {
1520 m_thresholds->get(i)->createMessage(msg, id);
1521 }
1522
1523 unlock();
1524 }
1525
1526 /**
1527 * Check if DCI has active threshold
1528 */
1529 bool DCItem::hasActiveThreshold()
1530 {
1531 bool result = false;
1532 lock();
1533 for(int i = 0; i < getThresholdCount(); i++)
1534 {
1535 if (m_thresholds->get(i)->isReached())
1536 {
1537 result = true;
1538 break;
1539 }
1540 }
1541 unlock();
1542 return result;
1543 }
1544
1545 /**
1546 * Returns true if internal cache is loaded. If data collection object
1547 * does not have cache should return true
1548 */
1549 bool DCItem::isCacheLoaded()
1550 {
1551 return m_bCacheLoaded;
1552 }
1553
1554 /**
1555 * Should return true if object has (or can have) value
1556 */
1557 bool DCItem::hasValue()
1558 {
1559 return m_instanceDiscoveryMethod == IDM_NONE;
1560 }
1561
1562 /**
1563 * Expand {instance} macro in name and description
1564 */
1565 void DCItem::expandInstance()
1566 {
1567 String temp = m_szName;
1568 temp.replace(_T("{instance}"), m_instance);
1569 nx_strncpy(m_szName, (const TCHAR *)temp, MAX_ITEM_NAME);
1570
1571 temp = m_szDescription;
1572 temp.replace(_T("{instance}"), m_instance);
1573 nx_strncpy(m_szDescription, (const TCHAR *)temp, MAX_DB_STRING);
1574 }
1575
1576 /**
1577 * Filter instance list
1578 */
1579 void DCItem::filterInstanceList(StringList *instances)
1580 {
1581 if (m_instanceFilter == NULL)
1582 return;
1583
1584 for(int i = 0; i < instances->getSize(); i++)
1585 {
1586 NXSL_Value *pValue;
1587 NXSL_ServerEnv *pEnv;
1588
1589 pValue = new NXSL_Value(instances->getValue(i));
1590 pEnv = new NXSL_ServerEnv;
1591 m_instanceFilter->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pNode)));
1592 m_instanceFilter->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
1593
1594 if (m_instanceFilter->run(pEnv, 1, &pValue) == 0)
1595 {
1596 pValue = m_instanceFilter->getResult();
1597 if (pValue != NULL)
1598 {
1599 bool accepted;
1600 if (pValue->isArray())
1601 {
1602 NXSL_Array *array = pValue->getValueAsArray();
1603 if (array->size() > 0)
1604 {
1605 accepted = array->get(0)->getValueAsInt32() ? true : false;
1606 if (accepted && (array->size() > 1))
1607 {
1608 // transformed value
1609 const TCHAR *newValue = array->get(1)->getValueAsCString();
1610 if (newValue != NULL)
1611 {
1612 DbgPrintf(5, _T("DCItem::filterInstanceList(%s [%d]): instance %d \"%s\" replaced by \"%s\""),
1613 m_szName, m_dwId, i, instances->getValue(i), newValue);
1614 instances->replace(i, newValue);
1615 }
1616 }
1617 }
1618 else
1619 {
1620 accepted = true;
1621 }
1622 }
1623 else
1624 {
1625 accepted = pValue->getValueAsInt32() ? true : false;
1626 }
1627 if (!accepted)
1628 {
1629 DbgPrintf(5, _T("DCItem::filterInstanceList(%s [%d]): instance \"%s\" removed by filtering script"),
1630 m_szName, m_dwId, instances->getValue(i));
1631 instances->remove(i);
1632 i--;
1633 }
1634 }
1635 }
1636 else
1637 {
1638 TCHAR szBuffer[1024];
1639
1640 _sntprintf(szBuffer, 1024, _T("DCI::%s::%d::InstanceFilter"),
1641 (m_pNode != NULL) ? m_pNode->Name() : _T("(null)"), m_dwId);
1642 PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", szBuffer,
1643 m_transformer->getErrorText(), m_dwId);
1644 }
1645 }
1646 }