d41f6c8953ff7566db4ce57af53dd7d6ccce8fdf
[public/netxms.git] / src / server / core / dcitem.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2017 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 #ifdef WITH_ZMQ
26 #include "zeromq.h"
27 #endif
28
29 /**
30 * Event parameter names
31 */
32 static const TCHAR *s_paramNamesReach[] = { _T("dciName"), _T("dciDescription"), _T("thresholdValue"), _T("currentValue"), _T("dciId"), _T("instance"), _T("isRepeatedEvent"), _T("dciValue") };
33 static const TCHAR *s_paramNamesRearm[] = { _T("dciName"), _T("dciDescription"), _T("dciId"), _T("instance"), _T("thresholdValue"), _T("currentValue"), _T("dciValue") };
34
35 /**
36 * DCI cache loader queue
37 */
38 extern Queue g_dciCacheLoaderQueue;
39
40 /**
41 * Default constructor for DCItem
42 */
43 DCItem::DCItem() : DCObject()
44 {
45 m_thresholds = NULL;
46 m_dataType = DCI_DT_INT;
47 m_deltaCalculation = DCM_ORIGINAL_VALUE;
48 m_sampleCount = 0;
49 m_cacheSize = 0;
50 m_requiredCacheSize = 0;
51 m_ppValueCache = NULL;
52 m_tPrevValueTimeStamp = 0;
53 m_bCacheLoaded = false;
54 m_nBaseUnits = DCI_BASEUNITS_OTHER;
55 m_nMultiplier = 1;
56 m_customUnitName = NULL;
57 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
58 m_predictionEngine[0] = 0;
59 }
60
61 /**
62 * Create DCItem from another DCItem
63 */
64 DCItem::DCItem(const DCItem *pSrc) : DCObject(pSrc)
65 {
66 m_dataType = pSrc->m_dataType;
67 m_deltaCalculation = pSrc->m_deltaCalculation;
68 m_sampleCount = pSrc->m_sampleCount;
69 m_cacheSize = 0;
70 m_requiredCacheSize = 0;
71 m_ppValueCache = NULL;
72 m_tPrevValueTimeStamp = 0;
73 m_bCacheLoaded = false;
74 m_nBaseUnits = pSrc->m_nBaseUnits;
75 m_nMultiplier = pSrc->m_nMultiplier;
76 m_customUnitName = (pSrc->m_customUnitName != NULL) ? _tcsdup(pSrc->m_customUnitName) : NULL;
77 m_snmpRawValueType = pSrc->m_snmpRawValueType;
78 _tcscpy(m_predictionEngine, pSrc->m_predictionEngine);
79
80 // Copy thresholds
81 if (pSrc->getThresholdCount() > 0)
82 {
83 m_thresholds = new ObjectArray<Threshold>(pSrc->m_thresholds->size(), 8, true);
84 for(int i = 0; i < pSrc->m_thresholds->size(); i++)
85 {
86 Threshold *t = new Threshold(pSrc->m_thresholds->get(i));
87 t->createId();
88 m_thresholds->add(t);
89 }
90 }
91 else
92 {
93 m_thresholds = NULL;
94 }
95 }
96
97 /**
98 * Constructor for creating DCItem from database
99 * Assumes that fields in SELECT query are in following order:
100 * item_id,name,source,datatype,polling_interval,retention_time,status,
101 * delta_calculation,transformation,template_id,description,instance,
102 * template_item_id,flags,resource_id,proxy_node,base_units,unit_multiplier,
103 * custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,
104 * instd_method,instd_data,instd_filter,samples,comments,guid,npe_name,visibility_rights
105 */
106 DCItem::DCItem(DB_HANDLE hdb, DB_RESULT hResult, int iRow, Template *pNode) : DCObject()
107 {
108 m_id = DBGetFieldULong(hResult, iRow, 0);
109 DBGetField(hResult, iRow, 1, m_name, MAX_ITEM_NAME);
110 m_source = (BYTE)DBGetFieldLong(hResult, iRow, 2);
111 m_dataType = (BYTE)DBGetFieldLong(hResult, iRow, 3);
112 m_iPollingInterval = DBGetFieldLong(hResult, iRow, 4);
113 m_iRetentionTime = DBGetFieldLong(hResult, iRow, 5);
114 m_status = (BYTE)DBGetFieldLong(hResult, iRow, 6);
115 m_deltaCalculation = (BYTE)DBGetFieldLong(hResult, iRow, 7);
116 TCHAR *pszTmp = DBGetField(hResult, iRow, 8, NULL, 0);
117 setTransformationScript(pszTmp);
118 free(pszTmp);
119 m_dwTemplateId = DBGetFieldULong(hResult, iRow, 9);
120 DBGetField(hResult, iRow, 10, m_description, MAX_DB_STRING);
121 DBGetField(hResult, iRow, 11, m_instance, MAX_DB_STRING);
122 m_dwTemplateItemId = DBGetFieldULong(hResult, iRow, 12);
123 m_thresholds = NULL;
124 m_owner = pNode;
125 m_cacheSize = 0;
126 m_requiredCacheSize = 0;
127 m_ppValueCache = NULL;
128 m_tPrevValueTimeStamp = 0;
129 m_bCacheLoaded = false;
130 m_flags = (WORD)DBGetFieldLong(hResult, iRow, 13);
131 m_dwResourceId = DBGetFieldULong(hResult, iRow, 14);
132 m_sourceNode = DBGetFieldULong(hResult, iRow, 15);
133 m_nBaseUnits = DBGetFieldLong(hResult, iRow, 16);
134 m_nMultiplier = DBGetFieldLong(hResult, iRow, 17);
135 m_customUnitName = DBGetField(hResult, iRow, 18, NULL, 0);
136 m_pszPerfTabSettings = DBGetField(hResult, iRow, 19, NULL, 0);
137 DBGetField(hResult, iRow, 20, m_systemTag, MAX_DB_STRING);
138 m_snmpPort = (WORD)DBGetFieldLong(hResult, iRow, 21);
139 m_snmpRawValueType = (WORD)DBGetFieldLong(hResult, iRow, 22);
140 m_instanceDiscoveryMethod = (WORD)DBGetFieldLong(hResult, iRow, 23);
141 m_instanceDiscoveryData = DBGetField(hResult, iRow, 24, NULL, 0);
142 m_instanceFilterSource = NULL;
143 m_instanceFilter = NULL;
144 pszTmp = DBGetField(hResult, iRow, 25, NULL, 0);
145 setInstanceFilter(pszTmp);
146 free(pszTmp);
147 m_sampleCount = DBGetFieldLong(hResult, iRow, 26);
148 m_comments = DBGetField(hResult, iRow, 27, NULL, 0);
149 m_guid = DBGetFieldGUID(hResult, iRow, 28);
150 DBGetField(hResult, iRow, 29, m_predictionEngine, MAX_NPE_NAME_LEN);
151 //set visibility rights
152 pszTmp = DBGetField(hResult, iRow, 30, NULL, 0);
153 StringList list;
154 list.splitAndAdd(pszTmp, _T(","));
155 for(int i = 0; i < list.size(); i++)
156 {
157 if(*(list.get(i)) != 0)
158 m_visibilityRights->add(_tcstoll(list.get(i), NULL, 0));
159 }
160 free(pszTmp);
161
162 // Load last raw value from database
163 TCHAR szQuery[256];
164 _sntprintf(szQuery, 256, _T("SELECT raw_value,last_poll_time FROM raw_dci_values WHERE item_id=%d"), m_id);
165 DB_RESULT hTempResult = DBSelect(hdb, szQuery);
166 if (hTempResult != NULL)
167 {
168 if (DBGetNumRows(hTempResult) > 0)
169 {
170 TCHAR szBuffer[MAX_DB_STRING];
171 m_prevRawValue = DBGetField(hTempResult, 0, 0, szBuffer, MAX_DB_STRING);
172 m_tPrevValueTimeStamp = DBGetFieldULong(hTempResult, 0, 1);
173 m_tLastPoll = m_tPrevValueTimeStamp;
174 }
175 DBFreeResult(hTempResult);
176 }
177
178 loadCustomSchedules(hdb);
179 }
180
181 /**
182 * Constructor for creating new DCItem from scratch
183 */
184 DCItem::DCItem(UINT32 dwId, const TCHAR *szName, int iSource, int iDataType,
185 int iPollingInterval, int iRetentionTime, Template *pNode,
186 const TCHAR *pszDescription, const TCHAR *systemTag)
187 : DCObject(dwId, szName, iSource, iPollingInterval, iRetentionTime, pNode, pszDescription, systemTag)
188 {
189 m_dataType = iDataType;
190 m_deltaCalculation = DCM_ORIGINAL_VALUE;
191 m_sampleCount = 0;
192 m_thresholds = NULL;
193 m_cacheSize = 0;
194 m_requiredCacheSize = 0;
195 m_ppValueCache = NULL;
196 m_tPrevValueTimeStamp = 0;
197 m_bCacheLoaded = false;
198 m_nBaseUnits = DCI_BASEUNITS_OTHER;
199 m_nMultiplier = 1;
200 m_customUnitName = NULL;
201 m_snmpRawValueType = SNMP_RAWTYPE_NONE;
202 m_predictionEngine[0] = 0;
203
204 updateCacheSizeInternal(false);
205 }
206
207 /**
208 * Create DCItem from import file
209 */
210 DCItem::DCItem(ConfigEntry *config, Template *owner) : DCObject(config, owner)
211 {
212 m_dataType = (BYTE)config->getSubEntryValueAsInt(_T("dataType"));
213 m_deltaCalculation = (BYTE)config->getSubEntryValueAsInt(_T("delta"));
214 m_sampleCount = (BYTE)config->getSubEntryValueAsInt(_T("samples"));
215 m_cacheSize = 0;
216 m_requiredCacheSize = 0;
217 m_ppValueCache = NULL;
218 m_tPrevValueTimeStamp = 0;
219 m_bCacheLoaded = false;
220 m_nBaseUnits = DCI_BASEUNITS_OTHER;
221 m_nMultiplier = 1;
222 m_customUnitName = NULL;
223 m_snmpRawValueType = (WORD)config->getSubEntryValueAsInt(_T("snmpRawValueType"));
224 nx_strncpy(m_predictionEngine, config->getSubEntryValue(_T("predictionEngine"), 0, _T("")), MAX_NPE_NAME_LEN);
225
226 // for compatibility with old format
227 if (config->getSubEntryValueAsInt(_T("allThresholds")))
228 m_flags |= DCF_ALL_THRESHOLDS;
229 if (config->getSubEntryValueAsInt(_T("rawValueInOctetString")))
230 m_flags |= DCF_RAW_VALUE_OCTET_STRING;
231
232 ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
233 if (thresholdsRoot != NULL)
234 {
235 ObjectArray<ConfigEntry> *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
236 m_thresholds = new ObjectArray<Threshold>(thresholds->size(), 8, true);
237 for(int i = 0; i < thresholds->size(); i++)
238 {
239 m_thresholds->add(new Threshold(thresholds->get(i), this));
240 }
241 delete thresholds;
242 }
243 else
244 {
245 m_thresholds = NULL;
246 }
247
248 updateCacheSizeInternal(false);
249 }
250
251 /**
252 * Destructor
253 */
254 DCItem::~DCItem()
255 {
256 delete m_thresholds;
257 free(m_customUnitName);
258 clearCache();
259 }
260
261 /**
262 * Delete all thresholds
263 */
264 void DCItem::deleteAllThresholds()
265 {
266 lock();
267 delete_and_null(m_thresholds);
268 unlock();
269 }
270
271 /**
272 * Clear data cache
273 */
274 void DCItem::clearCache()
275 {
276 for(UINT32 i = 0; i < m_cacheSize; i++)
277 delete m_ppValueCache[i];
278 free(m_ppValueCache);
279 m_ppValueCache = NULL;
280 m_cacheSize = 0;
281 }
282
283 /**
284 * Load data collection items thresholds from database
285 */
286 bool DCItem::loadThresholdsFromDB(DB_HANDLE hdb)
287 {
288 bool result = false;
289
290 DB_STATEMENT hStmt = DBPrepare(hdb,
291 _T("SELECT threshold_id,fire_value,rearm_value,check_function,")
292 _T("check_operation,sample_count,script,event_code,current_state,")
293 _T("rearm_event_code,repeat_interval,current_severity,")
294 _T("last_event_timestamp,match_count FROM thresholds WHERE item_id=? ")
295 _T("ORDER BY sequence_number"));
296 if (hStmt != NULL)
297 {
298 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
299 DB_RESULT hResult = DBSelectPrepared(hStmt);
300 if (hResult != NULL)
301 {
302 int count = DBGetNumRows(hResult);
303 if (count > 0)
304 {
305 m_thresholds = new ObjectArray<Threshold>(count, 8, true);
306 for(int i = 0; i < count; i++)
307 m_thresholds->add(new Threshold(hResult, i, this));
308 }
309 DBFreeResult(hResult);
310 result = true;
311 }
312 DBFreeStatement(hStmt);
313 }
314 return result;
315 }
316
317 /**
318 * Save to database
319 */
320 bool DCItem::saveToDatabase(DB_HANDLE hdb)
321 {
322 // Prepare and execute query
323 DB_STATEMENT hStmt;
324 if (IsDatabaseRecordExist(hdb, _T("items"), _T("item_id"), m_id))
325 {
326 hStmt = DBPrepare(hdb,
327 _T("UPDATE items SET node_id=?,template_id=?,name=?,source=?,")
328 _T("datatype=?,polling_interval=?,retention_time=?,status=?,")
329 _T("delta_calculation=?,transformation=?,description=?,")
330 _T("instance=?,template_item_id=?,flags=?,")
331 _T("resource_id=?,proxy_node=?,base_units=?,")
332 _T("unit_multiplier=?,custom_units_name=?,perftab_settings=?,")
333 _T("system_tag=?,snmp_port=?,snmp_raw_value_type=?,")
334 _T("instd_method=?,instd_data=?,instd_filter=?,samples=?,")
335 _T("comments=?,guid=?,npe_name=?,visibility_rights=? WHERE item_id=?"));
336 }
337 else
338 {
339 hStmt = DBPrepare(hdb,
340 _T("INSERT INTO items (node_id,template_id,name,source,")
341 _T("datatype,polling_interval,retention_time,status,delta_calculation,")
342 _T("transformation,description,instance,template_item_id,flags,")
343 _T("resource_id,proxy_node,base_units,unit_multiplier,")
344 _T("custom_units_name,perftab_settings,system_tag,snmp_port,snmp_raw_value_type,")
345 _T("instd_method,instd_data,instd_filter,samples,comments,guid,npe_name,visibility_rights,item_id) VALUES ")
346 _T("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
347 }
348 if (hStmt == NULL)
349 return false;
350
351 lock();
352
353 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (m_owner == NULL) ? 0 : m_owner->getId());
354 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, m_dwTemplateId);
355 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
356 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (INT32)m_source);
357 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)m_dataType);
358 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (INT32)m_iPollingInterval);
359 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (INT32)m_iRetentionTime);
360 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (INT32)m_status);
361 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, (INT32)m_deltaCalculation);
362 DBBind(hStmt, 10, DB_SQLTYPE_TEXT, m_transformationScriptSource, DB_BIND_STATIC);
363 DBBind(hStmt, 11, DB_SQLTYPE_VARCHAR, m_description, DB_BIND_STATIC);
364 DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, m_instance, DB_BIND_STATIC);
365 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, m_dwTemplateItemId);
366 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (UINT32)m_flags);
367 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, m_dwResourceId);
368 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, m_sourceNode);
369 DBBind(hStmt, 17, DB_SQLTYPE_INTEGER, (INT32)m_nBaseUnits);
370 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, (INT32)m_nMultiplier);
371 DBBind(hStmt, 19, DB_SQLTYPE_VARCHAR, m_customUnitName, DB_BIND_STATIC);
372 DBBind(hStmt, 20, DB_SQLTYPE_VARCHAR, m_pszPerfTabSettings, DB_BIND_STATIC);
373 DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_systemTag, DB_BIND_STATIC);
374 DBBind(hStmt, 22, DB_SQLTYPE_INTEGER, (INT32)m_snmpPort);
375 DBBind(hStmt, 23, DB_SQLTYPE_INTEGER, (INT32)m_snmpRawValueType);
376 DBBind(hStmt, 24, DB_SQLTYPE_INTEGER, (INT32)m_instanceDiscoveryMethod);
377 DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_instanceDiscoveryData, DB_BIND_STATIC);
378 DBBind(hStmt, 26, DB_SQLTYPE_TEXT, m_instanceFilterSource, DB_BIND_STATIC);
379 DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, (INT32)m_sampleCount);
380 DBBind(hStmt, 28, DB_SQLTYPE_TEXT, m_comments, DB_BIND_STATIC);
381 DBBind(hStmt, 29, DB_SQLTYPE_VARCHAR, m_guid);
382 DBBind(hStmt, 30, DB_SQLTYPE_VARCHAR, m_predictionEngine, DB_BIND_STATIC);
383 String tmp;
384 int size = m_visibilityRights->size();
385 for(int i = 0; i < size; i++)
386 {
387 tmp.append(m_visibilityRights->get(i));
388 if(i != (size - 1))
389 tmp.append(_T(','));
390 }
391 DBBind(hStmt, 31, DB_SQLTYPE_VARCHAR, tmp, DB_BIND_STATIC);
392 DBBind(hStmt, 32, DB_SQLTYPE_INTEGER, m_id);
393
394 bool bResult = DBExecute(hStmt);
395 DBFreeStatement(hStmt);
396
397 // Save thresholds
398 if (bResult && (m_thresholds != NULL))
399 {
400 for(int i = 0; i < m_thresholds->size(); i++)
401 m_thresholds->get(i)->saveToDB(hdb, i);
402 }
403
404 // Delete non-existing thresholds
405 TCHAR query[256];
406 _sntprintf(query, 256, _T("SELECT threshold_id FROM thresholds WHERE item_id=%d"), m_id);
407 DB_RESULT hResult = DBSelect(hdb, query);
408 if (hResult != NULL)
409 {
410 int iNumRows = DBGetNumRows(hResult);
411 for(int i = 0; i < iNumRows; i++)
412 {
413 UINT32 dwId = DBGetFieldULong(hResult, i, 0);
414 int j;
415 for(j = 0; j < getThresholdCount(); j++)
416 if (m_thresholds->get(j)->getId() == dwId)
417 break;
418 if (j == getThresholdCount())
419 {
420 _sntprintf(query, 256, _T("DELETE FROM thresholds WHERE threshold_id=%d"), dwId);
421 DBQuery(hdb, query);
422 }
423 }
424 DBFreeResult(hResult);
425 }
426
427 // Create record in raw_dci_values if needed
428 if (!IsDatabaseRecordExist(hdb, _T("raw_dci_values"), _T("item_id"), m_id))
429 {
430 hStmt = DBPrepare(hdb, _T("INSERT INTO raw_dci_values (item_id,raw_value,last_poll_time) VALUES (?,?,?)"));
431 if (hStmt == NULL)
432 {
433 unlock();
434 return false;
435 }
436 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
437 DBBind(hStmt, 2, DB_SQLTYPE_TEXT, m_prevRawValue.getString(), DB_BIND_STATIC, 255);
438 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT64)m_tPrevValueTimeStamp);
439 bResult = DBExecute(hStmt);
440 DBFreeStatement(hStmt);
441 }
442
443 unlock();
444 return bResult ? DCObject::saveToDatabase(hdb) : false;
445 }
446
447 /**
448 * Check last value for threshold violations
449 */
450 void DCItem::checkThresholds(ItemValue &value)
451 {
452 if (m_thresholds == NULL)
453 return;
454
455 bool thresholdDeactivated = false;
456 for(int i = 0; i < m_thresholds->size(); i++)
457 {
458 Threshold *t = m_thresholds->get(i);
459 ItemValue checkValue;
460 ThresholdCheckResult result = t->check(value, m_ppValueCache, checkValue, m_owner, this);
461 switch(result)
462 {
463 case ACTIVATED:
464 {
465 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
466 s_paramNamesReach, m_name, m_description, t->getStringValue(),
467 (const TCHAR *)checkValue, m_id, m_instance, 0, (const TCHAR *)value);
468 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
469 if (evt != NULL)
470 {
471 t->markLastEvent(evt->getSeverity());
472 evt->decRefCount();
473 }
474 if (!(m_flags & DCF_ALL_THRESHOLDS))
475 i = m_thresholds->size(); // Stop processing
476 }
477 break;
478 case DEACTIVATED:
479 PostDciEventWithNames(t->getRearmEventCode(), m_owner->getId(), m_id, "ssissss",
480 s_paramNamesRearm, m_name, m_description, m_id, m_instance,
481 t->getStringValue(), (const TCHAR *)checkValue, (const TCHAR *)value);
482 if (!(m_flags & DCF_ALL_THRESHOLDS))
483 {
484 // this flag used to re-send activation event for next active threshold
485 thresholdDeactivated = true;
486 }
487 break;
488 case ALREADY_ACTIVE:
489 {
490 // Check if we need to re-sent threshold violation event
491 time_t now = time(NULL);
492 UINT32 repeatInterval = (t->getRepeatInterval() == -1) ? g_thresholdRepeatInterval : (UINT32)t->getRepeatInterval();
493 if (thresholdDeactivated || ((repeatInterval != 0) && (t->getLastEventTimestamp() + (time_t)repeatInterval < now)))
494 {
495 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
496 s_paramNamesReach, m_name, m_description, t->getStringValue(),
497 (const TCHAR *)checkValue, m_id, m_instance, 1, (const TCHAR *)value);
498 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
499 if (evt != NULL)
500 {
501 t->markLastEvent(evt->getSeverity());
502 evt->decRefCount();
503 }
504 }
505 }
506 if (!(m_flags & DCF_ALL_THRESHOLDS))
507 {
508 i = m_thresholds->size(); // Threshold condition still true, stop processing
509 }
510 thresholdDeactivated = false;
511 break;
512 default:
513 break;
514 }
515 }
516 }
517
518 /**
519 * Create NXCP message with item data
520 */
521 void DCItem::createMessage(NXCPMessage *pMsg)
522 {
523 DCObject::createMessage(pMsg);
524
525 lock();
526 pMsg->setField(VID_DCI_DATA_TYPE, (WORD)m_dataType);
527 pMsg->setField(VID_DCI_DELTA_CALCULATION, (WORD)m_deltaCalculation);
528 pMsg->setField(VID_SAMPLE_COUNT, (WORD)m_sampleCount);
529 pMsg->setField(VID_BASE_UNITS, (WORD)m_nBaseUnits);
530 pMsg->setField(VID_MULTIPLIER, (UINT32)m_nMultiplier);
531 pMsg->setField(VID_SNMP_RAW_VALUE_TYPE, m_snmpRawValueType);
532 pMsg->setField(VID_NPE_NAME, m_predictionEngine);
533 if (m_customUnitName != NULL)
534 pMsg->setField(VID_CUSTOM_UNITS_NAME, m_customUnitName);
535 if (m_thresholds != NULL)
536 {
537 pMsg->setField(VID_NUM_THRESHOLDS, (UINT32)m_thresholds->size());
538 UINT32 dwId = VID_DCI_THRESHOLD_BASE;
539 for(int i = 0; i < m_thresholds->size(); i++, dwId += 20)
540 m_thresholds->get(i)->createMessage(pMsg, dwId);
541 }
542 else
543 {
544 pMsg->setField(VID_NUM_THRESHOLDS, (UINT32)0);
545 }
546 unlock();
547 }
548
549 /**
550 * Delete item and collected data from database
551 */
552 void DCItem::deleteFromDatabase()
553 {
554 TCHAR szQuery[256];
555
556 DCObject::deleteFromDatabase();
557
558 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM items WHERE item_id=%d"), m_id);
559 QueueSQLRequest(szQuery);
560 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM thresholds WHERE item_id=%d"), m_id);
561 QueueSQLRequest(szQuery);
562 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM raw_dci_values WHERE item_id=%d"), m_id);
563 QueueSQLRequest(szQuery);
564
565 if (m_owner->isDataCollectionTarget())
566 static_cast<DataCollectionTarget*>(m_owner)->scheduleItemDataCleanup(m_id);
567 }
568
569 /**
570 * Update item from NXCP message
571 */
572 void DCItem::updateFromMessage(NXCPMessage *pMsg, UINT32 *pdwNumMaps, UINT32 **ppdwMapIndex, UINT32 **ppdwMapId)
573 {
574 DCObject::updateFromMessage(pMsg);
575
576 lock();
577
578 m_dataType = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DATA_TYPE);
579 m_deltaCalculation = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DELTA_CALCULATION);
580 m_sampleCount = (int)pMsg->getFieldAsUInt16(VID_SAMPLE_COUNT);
581 m_nBaseUnits = pMsg->getFieldAsUInt16(VID_BASE_UNITS);
582 m_nMultiplier = (int)pMsg->getFieldAsUInt32(VID_MULTIPLIER);
583 safe_free(m_customUnitName);
584 m_customUnitName = pMsg->getFieldAsString(VID_CUSTOM_UNITS_NAME);
585 m_snmpRawValueType = pMsg->getFieldAsUInt16(VID_SNMP_RAW_VALUE_TYPE);
586 pMsg->getFieldAsString(VID_NPE_NAME, m_predictionEngine, MAX_NPE_NAME_LEN);
587
588 // Update thresholds
589 UINT32 dwNum = pMsg->getFieldAsUInt32(VID_NUM_THRESHOLDS);
590 UINT32 *newThresholds = (UINT32 *)malloc(sizeof(UINT32) * dwNum);
591 *ppdwMapIndex = (UINT32 *)malloc(dwNum * sizeof(UINT32));
592 *ppdwMapId = (UINT32 *)malloc(dwNum * sizeof(UINT32));
593 *pdwNumMaps = 0;
594
595 // Read all new threshold ids from message
596 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
597 {
598 newThresholds[i] = pMsg->getFieldAsUInt32(dwId);
599 }
600
601 // Check if some thresholds was deleted, and reposition others if needed
602 Threshold **ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
603 memset(ppNewList, 0, sizeof(Threshold *) * dwNum);
604 for(int i = 0; i < getThresholdCount(); i++)
605 {
606 UINT32 j;
607 for(j = 0; j < dwNum; j++)
608 if (m_thresholds->get(i)->getId() == newThresholds[j])
609 break;
610 if (j == dwNum)
611 {
612 // No threshold with that id in new list, delete it
613 m_thresholds->remove(i);
614 i--;
615 }
616 else
617 {
618 // Move existing thresholds to appropriate positions in new list
619 ppNewList[j] = m_thresholds->get(i);
620 }
621 }
622
623 // Add or update thresholds
624 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
625 {
626 if (newThresholds[i] == 0) // New threshold?
627 {
628 ppNewList[i] = new Threshold(this);
629 ppNewList[i]->createId();
630
631 // Add index -> id mapping
632 (*ppdwMapIndex)[*pdwNumMaps] = i;
633 (*ppdwMapId)[*pdwNumMaps] = ppNewList[i]->getId();
634 (*pdwNumMaps)++;
635 }
636 if (ppNewList[i] != NULL)
637 ppNewList[i]->updateFromMessage(pMsg, dwId);
638 }
639
640 if (dwNum > 0)
641 {
642 if (m_thresholds != NULL)
643 {
644 m_thresholds->setOwner(false);
645 m_thresholds->clear();
646 m_thresholds->setOwner(true);
647 }
648 else
649 {
650 m_thresholds = new ObjectArray<Threshold>((int)dwNum, 8, true);
651 }
652 for(UINT32 i = 0; i < dwNum; i++)
653 {
654 if (ppNewList[i] != NULL)
655 m_thresholds->add(ppNewList[i]);
656 }
657 }
658 else
659 {
660 delete_and_null(m_thresholds);
661 }
662
663 // Update data type in thresholds
664 for(int i = 0; i < getThresholdCount(); i++)
665 m_thresholds->get(i)->setDataType(m_dataType);
666
667 free(ppNewList);
668 free(newThresholds);
669 updateCacheSizeInternal(true);
670 unlock();
671 }
672
673 /**
674 * Process new collected value. Should return true on success.
675 * If returns false, current poll result will be converted into data collection error.
676 *
677 * @return true on success
678 */
679 bool DCItem::processNewValue(time_t tmTimeStamp, const void *originalValue, bool *updateStatus)
680 {
681 ItemValue rawValue, *pValue;
682
683 lock();
684
685 // Normally m_owner shouldn't be NULL for polled items, but who knows...
686 if (m_owner == NULL)
687 {
688 unlock();
689 return false;
690 }
691
692 // Create new ItemValue object and transform it as needed
693 pValue = new ItemValue((const TCHAR *)originalValue, tmTimeStamp);
694 if (m_tPrevValueTimeStamp == 0)
695 m_prevRawValue = *pValue; // Delta should be zero for first poll
696 rawValue = *pValue;
697
698 // Cluster can have only aggregated data, and transformation
699 // should not be used on aggregation
700 if ((m_owner->getObjectClass() != OBJECT_CLUSTER) || (m_flags & DCF_TRANSFORM_AGGREGATED))
701 {
702 if (!transform(*pValue, (tmTimeStamp > m_tPrevValueTimeStamp) ? (tmTimeStamp - m_tPrevValueTimeStamp) : 0))
703 {
704 unlock();
705 return false;
706 }
707 }
708
709 m_dwErrorCount = 0;
710
711 if (isStatusDCO() && (tmTimeStamp > m_tPrevValueTimeStamp) && ((m_cacheSize == 0) || !m_bCacheLoaded || ((UINT32)*pValue != (UINT32)*m_ppValueCache[0])))
712 {
713 *updateStatus = true;
714 }
715 else
716 {
717 *updateStatus = false;
718 }
719
720 if (tmTimeStamp > m_tPrevValueTimeStamp)
721 {
722 m_prevRawValue = rawValue;
723 m_tPrevValueTimeStamp = tmTimeStamp;
724
725 // Save raw value into database
726 QueueRawDciDataUpdate(tmTimeStamp, m_id, (const TCHAR *)originalValue, pValue->getString());
727 }
728
729 // Save transformed value to database
730 if ((m_flags & DCF_NO_STORAGE) == 0)
731 QueueIDataInsert(tmTimeStamp, m_owner->getId(), m_id, (const TCHAR *)originalValue, pValue->getString());
732 if (g_flags & AF_PERFDATA_STORAGE_DRIVER_LOADED)
733 PerfDataStorageRequest(this, tmTimeStamp, pValue->getString());
734
735 // Update prediction engine
736 if (m_predictionEngine[0] != 0)
737 {
738 PredictionEngine *engine = FindPredictionEngine(m_predictionEngine);
739 if (engine != NULL)
740 engine->update(m_id, tmTimeStamp, pValue->getDouble());
741 }
742
743 // Check thresholds and add value to cache
744 if (m_bCacheLoaded && (tmTimeStamp >= m_tPrevValueTimeStamp) &&
745 ((g_offlineDataRelevanceTime <= 0) || (tmTimeStamp > (time(NULL) - g_offlineDataRelevanceTime))))
746 {
747 checkThresholds(*pValue);
748 }
749
750 if ((m_cacheSize > 0) && (tmTimeStamp >= m_tPrevValueTimeStamp))
751 {
752 delete m_ppValueCache[m_cacheSize - 1];
753 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_cacheSize - 1));
754 m_ppValueCache[0] = pValue;
755 }
756 else
757 {
758 delete pValue;
759 }
760
761 unlock();
762
763 #ifdef WITH_ZMQ
764 ZmqPublishData(m_owner->getId(), m_id, m_name, pValue->getString());
765 #endif
766
767 return true;
768 }
769
770 /**
771 * Process new data collection error
772 */
773 void DCItem::processNewError(bool noInstance, time_t now)
774 {
775 lock();
776
777 // Normally m_owner shouldn't be NULL for polled items, but who knows...
778 if (m_owner == NULL)
779 {
780 unlock();
781 return;
782 }
783
784 m_dwErrorCount++;
785
786 for(int i = 0; i < getThresholdCount(); i++)
787 {
788 Threshold *t = m_thresholds->get(i);
789 ThresholdCheckResult result = t->checkError(m_dwErrorCount);
790 switch(result)
791 {
792 case ACTIVATED:
793 {
794 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
795 s_paramNamesReach, m_name, m_description, _T(""), _T(""),
796 m_id, m_instance, 0, _T(""));
797 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
798 if (evt != NULL)
799 {
800 t->markLastEvent(evt->getSeverity());
801 evt->decRefCount();
802 }
803 if (!(m_flags & DCF_ALL_THRESHOLDS))
804 {
805 i = m_thresholds->size(); // Stop processing
806 }
807 }
808 break;
809 case DEACTIVATED:
810 PostDciEventWithNames(t->getRearmEventCode(), m_owner->getId(), m_id, "ssissss",
811 s_paramNamesRearm, m_name, m_description, m_id, m_instance, _T(""), _T(""), _T(""));
812 break;
813 case ALREADY_ACTIVE:
814 {
815 // Check if we need to re-sent threshold violation event
816 UINT32 repeatInterval = (t->getRepeatInterval() == -1) ? g_thresholdRepeatInterval : (UINT32)t->getRepeatInterval();
817 if ((repeatInterval != 0) && (t->getLastEventTimestamp() + (time_t)repeatInterval < now))
818 {
819 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
820 s_paramNamesReach, m_name, m_description, _T(""), _T(""),
821 m_id, m_instance, 1, _T(""));
822 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
823 if (evt != NULL)
824 {
825 t->markLastEvent(evt->getSeverity());
826 evt->decRefCount();
827 }
828 }
829 }
830 if (!(m_flags & DCF_ALL_THRESHOLDS))
831 {
832 i = m_thresholds->size(); // Threshold condition still true, stop processing
833 }
834 break;
835 default:
836 break;
837 }
838 }
839
840 unlock();
841 }
842
843 /**
844 * Transform received value
845 */
846 bool DCItem::transform(ItemValue &value, time_t nElapsedTime)
847 {
848 bool success = true;
849
850 switch(m_deltaCalculation)
851 {
852 case DCM_SIMPLE:
853 switch(m_dataType)
854 {
855 case DCI_DT_INT:
856 value = (INT32)value - (INT32)m_prevRawValue;
857 break;
858 case DCI_DT_UINT:
859 value = (UINT32)value - (UINT32)m_prevRawValue;
860 break;
861 case DCI_DT_INT64:
862 value = (INT64)value - (INT64)m_prevRawValue;
863 break;
864 case DCI_DT_UINT64:
865 value = (UINT64)value - (UINT64)m_prevRawValue;
866 break;
867 case DCI_DT_FLOAT:
868 value = (double)value - (double)m_prevRawValue;
869 break;
870 case DCI_DT_STRING:
871 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
872 break;
873 default:
874 // Delta calculation is not supported for other types
875 break;
876 }
877 break;
878 case DCM_AVERAGE_PER_MINUTE:
879 nElapsedTime /= 60; // Convert to minutes
880 /* no break */
881 case DCM_AVERAGE_PER_SECOND:
882 // Check elapsed time to prevent divide-by-zero exception
883 if (nElapsedTime == 0)
884 nElapsedTime++;
885
886 switch(m_dataType)
887 {
888 case DCI_DT_INT:
889 value = ((INT32)value - (INT32)m_prevRawValue) / (INT32)nElapsedTime;
890 break;
891 case DCI_DT_UINT:
892 value = ((UINT32)value - (UINT32)m_prevRawValue) / (UINT32)nElapsedTime;
893 break;
894 case DCI_DT_INT64:
895 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
896 break;
897 case DCI_DT_UINT64:
898 value = ((UINT64)value - (UINT64)m_prevRawValue) / (UINT64)nElapsedTime;
899 break;
900 case DCI_DT_FLOAT:
901 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
902 break;
903 case DCI_DT_STRING:
904 // I don't see any meaning in _T("average delta per second (minute)") for string
905 // values, so result will be 0 if there are no difference between
906 // current and previous values, and 1 otherwise
907 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
908 break;
909 default:
910 // Delta calculation is not supported for other types
911 break;
912 }
913 break;
914 default: // Default is no transformation
915 break;
916 }
917
918 if (m_transformationScript != NULL)
919 {
920 NXSL_VM *vm = new NXSL_VM(new NXSL_ServerEnv());
921 if (vm->load(m_transformationScript))
922 {
923 NXSL_Value *pValue = new NXSL_Value((const TCHAR *)value);
924 vm->setGlobalVariable(_T("$object"), m_owner->createNXSLObject());
925 if (m_owner->getObjectClass() == OBJECT_NODE)
926 {
927 vm->setGlobalVariable(_T("$node"), m_owner->createNXSLObject());
928 }
929 vm->setGlobalVariable(_T("$dci"), createNXSLObject());
930 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((m_owner->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
931
932 // remove lock from DCI for script execution to avoid deadlocks
933 unlock();
934 success = vm->run(1, &pValue);
935 lock();
936 if (success)
937 {
938 pValue = vm->getResult();
939 if (pValue != NULL)
940 {
941 switch(m_dataType)
942 {
943 case DCI_DT_INT:
944 value = pValue->getValueAsInt32();
945 break;
946 case DCI_DT_UINT:
947 value = pValue->getValueAsUInt32();
948 break;
949 case DCI_DT_INT64:
950 value = pValue->getValueAsInt64();
951 break;
952 case DCI_DT_UINT64:
953 value = pValue->getValueAsUInt64();
954 break;
955 case DCI_DT_FLOAT:
956 value = pValue->getValueAsReal();
957 break;
958 case DCI_DT_STRING:
959 value = CHECK_NULL_EX(pValue->getValueAsCString());
960 break;
961 default:
962 break;
963 }
964 }
965 }
966 else if (vm->getErrorCode() == NXSL_ERR_EXECUTION_ABORTED)
967 {
968 DbgPrintf(6, _T("Transformation script for DCI \"%s\" [%d] on node %s [%d] aborted"),
969 m_description, m_id, getOwnerName(), getOwnerId());
970 }
971 else
972 {
973 TCHAR buffer[1024];
974 _sntprintf(buffer, 1024, _T("DCI::%s::%d::TransformationScript"), getOwnerName(), m_id);
975 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, m_id, "ssd", buffer, vm->getErrorText(), m_id);
976 nxlog_write(MSG_TRANSFORMATION_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dsdss",
977 getOwnerId(), getOwnerName(), m_id, m_name, vm->getErrorText());
978 }
979 }
980 else
981 {
982 TCHAR buffer[1024];
983 _sntprintf(buffer, 1024, _T("DCI::%s::%d::TransformationScript"), getOwnerName(), m_id);
984 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, m_id, "ssd", buffer, vm->getErrorText(), m_id);
985 nxlog_write(MSG_TRANSFORMATION_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dsdss",
986 getOwnerId(), getOwnerName(), m_id, m_name, vm->getErrorText());
987 success = false;
988 }
989 delete vm;
990 }
991 return success;
992 }
993
994 /**
995 * Set new ID and node/template association
996 */
997 void DCItem::changeBinding(UINT32 dwNewId, Template *pNewNode, BOOL doMacroExpansion)
998 {
999 DCObject::changeBinding(dwNewId, pNewNode, doMacroExpansion);
1000
1001 lock();
1002 if (dwNewId != 0)
1003 {
1004 for(int i = 0; i < getThresholdCount(); i++)
1005 m_thresholds->get(i)->bindToItem(m_id, m_owner->getId());
1006 }
1007
1008 clearCache();
1009 updateCacheSizeInternal(true);
1010 unlock();
1011 }
1012
1013 /**
1014 * Update required cache size depending on thresholds
1015 * dwCondId is an identifier of calling condition object id. If it is not 0,
1016 * GetCacheSizeForDCI should be called with bNoLock == TRUE for appropriate
1017 * condition object
1018 */
1019 void DCItem::updateCacheSizeInternal(bool allowLoad, UINT32 conditionId)
1020 {
1021 // Sanity check
1022 if (m_owner == NULL)
1023 {
1024 nxlog_debug(3, _T("DCItem::updateCacheSize() called for DCI %d when m_owner == NULL"), m_id);
1025 return;
1026 }
1027
1028 // Minimum cache size is 1 for nodes (so GetLastValue can work)
1029 // and it is always 0 for templates
1030 if (((m_owner->isDataCollectionTarget() && (m_owner->getObjectClass() != OBJECT_CLUSTER)) ||
1031 ((m_owner->getObjectClass() == OBJECT_CLUSTER) && isAggregateOnCluster())) &&
1032 (m_instanceDiscoveryMethod == IDM_NONE))
1033 {
1034 UINT32 requiredSize = 1;
1035
1036 // Calculate required cache size
1037 for(int i = 0; i < getThresholdCount(); i++)
1038 if (requiredSize < m_thresholds->get(i)->getRequiredCacheSize())
1039 requiredSize = m_thresholds->get(i)->getRequiredCacheSize();
1040
1041 ObjectArray<NetObj> *conditions = g_idxConditionById.getObjects(true);
1042 for(int i = 0; i < conditions->size(); i++)
1043 {
1044 ConditionObject *c = (ConditionObject *)conditions->get(i);
1045 UINT32 size = c->getCacheSizeForDCI(m_id, conditionId == c->getId());
1046 if (size > requiredSize)
1047 requiredSize = size;
1048 c->decRefCount();
1049 }
1050 delete conditions;
1051
1052 m_requiredCacheSize = requiredSize;
1053 }
1054 else
1055 {
1056 m_requiredCacheSize = 0;
1057 }
1058
1059 nxlog_debug_tag(_T("obj.dc.cache"), 8, _T("DCItem::updateCacheSizeInternal(dci=\"%s\", node=%s [%d]): requiredSize=%d cacheSize=%d"),
1060 m_name, m_owner->getName(), m_owner->getId(), m_requiredCacheSize, m_cacheSize);
1061
1062 // Update cache if needed
1063 if (m_requiredCacheSize < m_cacheSize)
1064 {
1065 // Destroy unneeded values
1066 if (m_cacheSize > 0)
1067 {
1068 for(UINT32 i = m_requiredCacheSize; i < m_cacheSize; i++)
1069 delete m_ppValueCache[i];
1070 }
1071
1072 m_cacheSize = m_requiredCacheSize;
1073 if (m_cacheSize > 0)
1074 {
1075 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_cacheSize);
1076 }
1077 else
1078 {
1079 free(m_ppValueCache);
1080 m_ppValueCache = NULL;
1081 }
1082 }
1083 else if (m_requiredCacheSize > m_cacheSize)
1084 {
1085 // Load missing values from database
1086 // Skip caching for DCIs where estimated time to fill the cache is less then 5 minutes
1087 // to reduce load on database at server startup
1088 if (allowLoad && (m_owner != NULL) && (((m_requiredCacheSize - m_cacheSize) * m_iPollingInterval > 300) || (m_source == DS_PUSH_AGENT)))
1089 {
1090 m_bCacheLoaded = false;
1091 g_dciCacheLoaderQueue.put(new DCObjectInfo(this));
1092 }
1093 else
1094 {
1095 // will not read data from database, fill cache with empty values
1096 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_requiredCacheSize);
1097 for(UINT32 i = m_cacheSize; i < m_requiredCacheSize; i++)
1098 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1099 DbgPrintf(7, _T("Cache load skipped for parameter %s [%d]"), m_name, (int)m_id);
1100 m_cacheSize = m_requiredCacheSize;
1101 m_bCacheLoaded = true;
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Reload cache from database
1108 */
1109 void DCItem::reloadCache()
1110 {
1111 TCHAR szBuffer[MAX_DB_STRING];
1112
1113 switch(g_dbSyntax)
1114 {
1115 case DB_SYNTAX_MSSQL:
1116 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT TOP %d idata_value,idata_timestamp FROM idata_%d ")
1117 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1118 m_requiredCacheSize, m_owner->getId(), m_id);
1119 break;
1120 case DB_SYNTAX_ORACLE:
1121 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT * FROM (SELECT idata_value,idata_timestamp FROM idata_%d ")
1122 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC) WHERE ROWNUM <= %d"),
1123 m_owner->getId(), m_id, m_requiredCacheSize);
1124 break;
1125 case DB_SYNTAX_MYSQL:
1126 case DB_SYNTAX_PGSQL:
1127 case DB_SYNTAX_SQLITE:
1128 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1129 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC LIMIT %d"),
1130 m_owner->getId(), m_id, m_requiredCacheSize);
1131 break;
1132 case DB_SYNTAX_DB2:
1133 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1134 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC FETCH FIRST %d ROWS ONLY"),
1135 m_owner->getId(), m_id, m_requiredCacheSize);
1136 break;
1137 default:
1138 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1139 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1140 m_owner->getId(), m_id);
1141 break;
1142 }
1143
1144 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1145 DB_UNBUFFERED_RESULT hResult = DBSelectUnbuffered(hdb, szBuffer);
1146
1147 lock();
1148
1149 UINT32 i;
1150 for(i = 0; i < m_cacheSize; i++)
1151 delete m_ppValueCache[i];
1152
1153 if (m_cacheSize != m_requiredCacheSize)
1154 {
1155 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_requiredCacheSize);
1156 }
1157
1158 nxlog_debug_tag(_T("obj.dc.cache"), 8, _T("DCItem::reloadCache(dci=\"%s\", node=%s [%d]): requiredSize=%d cacheSize=%d"),
1159 m_name, m_owner->getName(), m_owner->getId(), m_requiredCacheSize, m_cacheSize);
1160 if (hResult != NULL)
1161 {
1162 // Create cache entries
1163 bool moreData = true;
1164 for(i = 0; (i < m_requiredCacheSize) && moreData; i++)
1165 {
1166 moreData = DBFetch(hResult);
1167 if (moreData)
1168 {
1169 DBGetField(hResult, 0, szBuffer, MAX_DB_STRING);
1170 m_ppValueCache[i] = new ItemValue(szBuffer, DBGetFieldULong(hResult, 1));
1171 }
1172 else
1173 {
1174 m_ppValueCache[i] = new ItemValue(_T(""), 1); // Empty value
1175 }
1176 }
1177
1178 // Fill up cache with empty values if we don't have enough values in database
1179 if (i < m_requiredCacheSize)
1180 {
1181 nxlog_debug_tag(_T("obj.dc.cache"), 8, _T("DCItem::reloadCache(dci=\"%s\", node=%s [%d]): %d values missing in DB"),
1182 m_name, m_owner->getName(), m_owner->getId(), m_requiredCacheSize - i);
1183 for(; i < m_requiredCacheSize; i++)
1184 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1185 }
1186 DBFreeResult(hResult);
1187 }
1188 else
1189 {
1190 // Error reading data from database, fill cache with empty values
1191 for(i = 0; i < m_requiredCacheSize; i++)
1192 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1193 }
1194
1195 m_cacheSize = m_requiredCacheSize;
1196 m_bCacheLoaded = true;
1197 unlock();
1198
1199 DBConnectionPoolReleaseConnection(hdb);
1200 }
1201
1202 /**
1203 * Put last value into CSCP message
1204 */
1205 void DCItem::fillLastValueMessage(NXCPMessage *pMsg, UINT32 dwId)
1206 {
1207 lock();
1208 pMsg->setField(dwId++, m_id);
1209 pMsg->setField(dwId++, m_name);
1210 pMsg->setField(dwId++, m_description);
1211 pMsg->setField(dwId++, (UINT16)m_source);
1212 if (m_cacheSize > 0)
1213 {
1214 pMsg->setField(dwId++, (UINT16)m_dataType);
1215 pMsg->setField(dwId++, m_ppValueCache[0]->getString());
1216 pMsg->setFieldFromTime(dwId++, m_ppValueCache[0]->getTimeStamp());
1217 }
1218 else
1219 {
1220 pMsg->setField(dwId++, (UINT16)DCI_DT_NULL);
1221 pMsg->setField(dwId++, _T(""));
1222 pMsg->setField(dwId++, (UINT32)0);
1223 }
1224 pMsg->setField(dwId++, (WORD)(matchClusterResource() ? m_status : ITEM_STATUS_DISABLED)); // show resource-bound DCIs as inactive if cluster resource is not on this node
1225 pMsg->setField(dwId++, (WORD)getType());
1226 pMsg->setField(dwId++, m_dwErrorCount);
1227 pMsg->setField(dwId++, m_dwTemplateItemId);
1228
1229 int i;
1230 for(i = 0; i < getThresholdCount(); i++)
1231 {
1232 if (m_thresholds->get(i)->isReached())
1233 break;
1234 }
1235 if (i < getThresholdCount())
1236 {
1237 pMsg->setField(dwId++, (WORD)1);
1238 m_thresholds->get(i)->createMessage(pMsg, dwId);
1239 }
1240 else
1241 {
1242 pMsg->setField(dwId++, (WORD)0);
1243 }
1244
1245 unlock();
1246 }
1247
1248 /**
1249 * Get item's last value for use in NXSL
1250 */
1251 NXSL_Value *DCItem::getRawValueForNXSL()
1252 {
1253 lock();
1254 NXSL_Value *value = new NXSL_Value(m_prevRawValue.getString());
1255 unlock();
1256 return value;
1257 }
1258
1259 /**
1260 * Get item's last value for use in NXSL
1261 */
1262 NXSL_Value *DCItem::getValueForNXSL(int nFunction, int nPolls)
1263 {
1264 NXSL_Value *pValue;
1265
1266 lock();
1267 switch(nFunction)
1268 {
1269 case F_LAST:
1270 // cache placeholders will have timestamp 1
1271 pValue = (m_bCacheLoaded && (m_cacheSize > 0) && (m_ppValueCache[0]->getTimeStamp() != 1)) ? new NXSL_Value(m_ppValueCache[0]->getString()) : new NXSL_Value;
1272 break;
1273 case F_DIFF:
1274 if (m_bCacheLoaded && (m_cacheSize >= 2))
1275 {
1276 ItemValue result;
1277 CalculateItemValueDiff(result, m_dataType, *m_ppValueCache[0], *m_ppValueCache[1]);
1278 pValue = new NXSL_Value(result.getString());
1279 }
1280 else
1281 {
1282 pValue = new NXSL_Value;
1283 }
1284 break;
1285 case F_AVERAGE:
1286 if (m_bCacheLoaded && (m_cacheSize > 0))
1287 {
1288 ItemValue result;
1289 CalculateItemValueAverage(result, m_dataType, std::min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1290 pValue = new NXSL_Value(result.getString());
1291 }
1292 else
1293 {
1294 pValue = new NXSL_Value;
1295 }
1296 break;
1297 case F_DEVIATION:
1298 if (m_bCacheLoaded && (m_cacheSize > 0))
1299 {
1300 ItemValue result;
1301 CalculateItemValueMD(result, m_dataType, std::min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1302 pValue = new NXSL_Value(result.getString());
1303 }
1304 else
1305 {
1306 pValue = new NXSL_Value;
1307 }
1308 break;
1309 case F_ERROR:
1310 pValue = new NXSL_Value((INT32)((m_dwErrorCount >= (UINT32)nPolls) ? 1 : 0));
1311 break;
1312 default:
1313 pValue = new NXSL_Value;
1314 break;
1315 }
1316 unlock();
1317 return pValue;
1318 }
1319
1320 /**
1321 * Get last value
1322 */
1323 const TCHAR *DCItem::getLastValue()
1324 {
1325 lock();
1326 const TCHAR *v = (m_cacheSize > 0) ? (const TCHAR *)m_ppValueCache[0]->getString() : NULL;
1327 unlock();
1328 return v;
1329 }
1330
1331 /**
1332 * Get copy of internal last value object. Caller is responsible for destroying returned object.
1333 */
1334 ItemValue *DCItem::getInternalLastValue()
1335 {
1336 lock();
1337 ItemValue *v = (m_cacheSize > 0) ? new ItemValue(m_ppValueCache[0]) : NULL;
1338 unlock();
1339 return v;
1340 }
1341
1342 /**
1343 * Get aggregate value. Returned value must be deallocated by caller.
1344 *
1345 * @return dynamically allocated value or NULL on error
1346 */
1347 TCHAR *DCItem::getAggregateValue(AggregationFunction func, time_t periodStart, time_t periodEnd)
1348 {
1349 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1350 TCHAR query[1024];
1351 TCHAR *result = NULL;
1352
1353 static const TCHAR *functions[] = { _T(""), _T("min"), _T("max"), _T("avg"), _T("sum") };
1354
1355 if (g_dbSyntax == DB_SYNTAX_ORACLE)
1356 {
1357 _sntprintf(query, 1024, _T("SELECT %s(coalesce(to_number(idata_value),0)) FROM idata_%u ")
1358 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ?"),
1359 functions[func], m_owner->getId());
1360 }
1361 else if (g_dbSyntax == DB_SYNTAX_MSSQL)
1362 {
1363 _sntprintf(query, 1024, _T("SELECT %s(coalesce(cast(idata_value as float),0)) FROM idata_%u ")
1364 _T("WHERE item_id=? AND (idata_timestamp BETWEEN ? AND ?) AND isnumeric(idata_value)=1"),
1365 functions[func], m_owner->getId());
1366 }
1367 else if (g_dbSyntax == DB_SYNTAX_PGSQL)
1368 {
1369 _sntprintf(query, 1024, _T("SELECT %s(idata_value::double precision) FROM idata_%u ")
1370 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ? AND idata_value~E'^\\\\d+(\\\\.\\\\d+)*$'"),
1371 functions[func], m_owner->getId());
1372 }
1373 else
1374 {
1375 _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value,0)) FROM idata_%u ")
1376 _T("WHERE item_id=? and idata_timestamp between ? and ?"),
1377 functions[func], m_owner->getId());
1378 }
1379
1380 DB_STATEMENT hStmt = DBPrepare(hdb, query);
1381 if (hStmt != NULL)
1382 {
1383 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
1384 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)periodStart);
1385 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)periodEnd);
1386 DB_RESULT hResult = DBSelectPrepared(hStmt);
1387 if (hResult != NULL)
1388 {
1389 if (DBGetNumRows(hResult) == 1)
1390 {
1391 result = DBGetField(hResult, 0, 0, NULL, 0);
1392 }
1393 DBFreeResult(hResult);
1394 }
1395 DBFreeStatement(hStmt);
1396 }
1397
1398 DBConnectionPoolReleaseConnection(hdb);
1399 return result;
1400 }
1401
1402 /**
1403 * Delete all collected data
1404 */
1405 bool DCItem::deleteAllData()
1406 {
1407 TCHAR szQuery[256];
1408 bool success;
1409
1410 lock();
1411 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1412 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE item_id=%d"), m_owner->getId(), m_id);
1413 success = DBQuery(hdb, szQuery) ? true : false;
1414 DBConnectionPoolReleaseConnection(hdb);
1415 clearCache();
1416 updateCacheSizeInternal(true);
1417 unlock();
1418 return success;
1419 }
1420
1421 /**
1422 * Update from template item
1423 */
1424 void DCItem::updateFromTemplate(DCObject *src)
1425 {
1426 DCObject::updateFromTemplate(src);
1427
1428 if (src->getType() != DCO_TYPE_ITEM)
1429 {
1430 DbgPrintf(2, _T("INTERNAL ERROR: DCItem::updateFromTemplate(%d, %d): source type is %d"), (int)m_id, (int)src->getId(), src->getType());
1431 return;
1432 }
1433
1434 lock();
1435 DCItem *item = (DCItem *)src;
1436
1437 m_dataType = item->m_dataType;
1438 m_deltaCalculation = item->m_deltaCalculation;
1439 m_sampleCount = item->m_sampleCount;
1440 m_snmpRawValueType = item->m_snmpRawValueType;
1441
1442 m_nBaseUnits = item->m_nBaseUnits;
1443 m_nMultiplier = item->m_nMultiplier;
1444 safe_free(m_customUnitName);
1445 m_customUnitName = (item->m_customUnitName != NULL) ? _tcsdup(item->m_customUnitName) : NULL;
1446
1447 // Copy thresholds
1448 // ***************************
1449 // First, skip matching thresholds
1450 int count = std::min(getThresholdCount(), item->getThresholdCount());
1451 int i;
1452 for(i = 0; i < count; i++)
1453 if (!m_thresholds->get(i)->compare(item->m_thresholds->get(i)))
1454 break;
1455 count = i; // First unmatched threshold's position
1456
1457 // Delete all original thresholds starting from first unmatched
1458 while(count < getThresholdCount())
1459 m_thresholds->remove(count);
1460
1461 // (Re)create thresholds starting from first unmatched
1462 if ((m_thresholds == NULL) && (item->getThresholdCount() > 0))
1463 m_thresholds = new ObjectArray<Threshold>(item->getThresholdCount(), 8, true);
1464 for(i = count; i < item->getThresholdCount(); i++)
1465 {
1466 Threshold *t = new Threshold(item->m_thresholds->get(i));
1467 t->createId();
1468 t->bindToItem(m_id, m_owner->getId());
1469 m_thresholds->add(t);
1470 }
1471
1472 // Update data type in thresholds
1473 for(i = 0; i < getThresholdCount(); i++)
1474 m_thresholds->get(i)->setDataType(m_dataType);
1475
1476 updateCacheSizeInternal(true);
1477 unlock();
1478 }
1479
1480 /**
1481 * Get list of used events
1482 */
1483 void DCItem::getEventList(IntegerArray<UINT32> *eventList)
1484 {
1485 lock();
1486 if (m_thresholds != NULL)
1487 {
1488 for(int i = 0; i < m_thresholds->size(); i++)
1489 {
1490 eventList->add(m_thresholds->get(i)->getEventCode());
1491 eventList->add(m_thresholds->get(i)->getRearmEventCode());
1492 }
1493 }
1494 unlock();
1495 }
1496
1497 /**
1498 * Create management pack record
1499 */
1500 void DCItem::createExportRecord(String &str)
1501 {
1502 lock();
1503
1504 str.appendFormattedString(_T("\t\t\t\t<dci id=\"%d\">\n")
1505 _T("\t\t\t\t\t<guid>%s</guid>\n")
1506 _T("\t\t\t\t\t<name>%s</name>\n")
1507 _T("\t\t\t\t\t<description>%s</description>\n")
1508 _T("\t\t\t\t\t<dataType>%d</dataType>\n")
1509 _T("\t\t\t\t\t<samples>%d</samples>\n")
1510 _T("\t\t\t\t\t<origin>%d</origin>\n")
1511 _T("\t\t\t\t\t<interval>%d</interval>\n")
1512 _T("\t\t\t\t\t<retention>%d</retention>\n")
1513 _T("\t\t\t\t\t<instance>%s</instance>\n")
1514 _T("\t\t\t\t\t<systemTag>%s</systemTag>\n")
1515 _T("\t\t\t\t\t<delta>%d</delta>\n")
1516 _T("\t\t\t\t\t<flags>%d</flags>\n")
1517 _T("\t\t\t\t\t<snmpRawValueType>%d</snmpRawValueType>\n")
1518 _T("\t\t\t\t\t<snmpPort>%d</snmpPort>\n")
1519 _T("\t\t\t\t\t<instanceDiscoveryMethod>%d</instanceDiscoveryMethod>\n"),
1520 (int)m_id, (const TCHAR *)m_guid.toString(),
1521 (const TCHAR *)EscapeStringForXML2(m_name),
1522 (const TCHAR *)EscapeStringForXML2(m_description),
1523 m_dataType, m_sampleCount, (int)m_source, m_iPollingInterval, m_iRetentionTime,
1524 (const TCHAR *)EscapeStringForXML2(m_instance),
1525 (const TCHAR *)EscapeStringForXML2(m_systemTag),
1526 (int)m_deltaCalculation, (int)m_flags,
1527 (int)m_snmpRawValueType, (int)m_snmpPort, (int)m_instanceDiscoveryMethod);
1528
1529 if (m_transformationScriptSource != NULL)
1530 {
1531 str += _T("\t\t\t\t\t<transformation>");
1532 str.appendPreallocated(EscapeStringForXML(m_transformationScriptSource, -1));
1533 str += _T("</transformation>\n");
1534 }
1535
1536 if ((m_schedules != NULL) && (m_schedules->size() > 0))
1537 {
1538 str += _T("\t\t\t\t\t<schedules>\n");
1539 for(int i = 0; i < m_schedules->size(); i++)
1540 str.appendFormattedString(_T("\t\t\t\t\t\t<schedule>%s</schedule>\n"), (const TCHAR *)EscapeStringForXML2(m_schedules->get(i)));
1541 str += _T("\t\t\t\t\t</schedules>\n");
1542 }
1543
1544 if (m_thresholds != NULL)
1545 {
1546 str += _T("\t\t\t\t\t<thresholds>\n");
1547 for(int i = 0; i < m_thresholds->size(); i++)
1548 {
1549 m_thresholds->get(i)->createNXMPRecord(str, i + 1);
1550 }
1551 str += _T("\t\t\t\t\t</thresholds>\n");
1552 }
1553
1554 if (m_pszPerfTabSettings != NULL)
1555 {
1556 str += _T("\t\t\t\t\t<perfTabSettings>");
1557 str.appendPreallocated(EscapeStringForXML(m_pszPerfTabSettings, -1));
1558 str += _T("</perfTabSettings>\n");
1559 }
1560
1561 if (m_instanceDiscoveryData != NULL)
1562 {
1563 str += _T("\t\t\t\t\t<instanceDiscoveryData>");
1564 str.appendPreallocated(EscapeStringForXML(m_instanceDiscoveryData, -1));
1565 str += _T("</instanceDiscoveryData>\n");
1566 }
1567
1568 if (m_instanceFilterSource != NULL)
1569 {
1570 str += _T("\t\t\t\t\t<instanceFilter>");
1571 str.appendPreallocated(EscapeStringForXML(m_instanceFilterSource, -1));
1572 str += _T("</instanceFilter>\n");
1573 }
1574
1575 unlock();
1576 str += _T("\t\t\t\t</dci>\n");
1577 }
1578
1579 /**
1580 * Add threshold to the list
1581 */
1582 void DCItem::addThreshold(Threshold *pThreshold)
1583 {
1584 if (m_thresholds == NULL)
1585 m_thresholds = new ObjectArray<Threshold>(8, 8, true);
1586 m_thresholds->add(pThreshold);
1587 }
1588
1589 /**
1590 * Enumerate all thresholds
1591 */
1592 BOOL DCItem::enumThresholds(BOOL (* pfCallback)(Threshold *, UINT32, void *), void *pArg)
1593 {
1594 BOOL bRet = TRUE;
1595
1596 lock();
1597 if (m_thresholds != NULL)
1598 {
1599 for(int i = 0; i < m_thresholds->size(); i++)
1600 {
1601 if (!pfCallback(m_thresholds->get(i), i, pArg))
1602 {
1603 bRet = FALSE;
1604 break;
1605 }
1606 }
1607 }
1608 unlock();
1609 return bRet;
1610 }
1611
1612 /**
1613 * Test DCI's transformation script
1614 */
1615 bool DCItem::testTransformation(DataCollectionTarget *object, const TCHAR *script, const TCHAR *value, TCHAR *buffer, size_t bufSize)
1616 {
1617 bool success = false;
1618 NXSL_VM *vm = NXSLCompileAndCreateVM(script, buffer, (int)bufSize, new NXSL_ServerEnv);
1619 if (vm != NULL)
1620 {
1621 NXSL_Value *pValue = new NXSL_Value(value);
1622 vm->setGlobalVariable(_T("$object"), object->createNXSLObject());
1623 if (object->getObjectClass() == OBJECT_NODE)
1624 {
1625 vm->setGlobalVariable(_T("$node"), object->createNXSLObject());
1626 }
1627 //FIXME: vm->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
1628 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((object->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
1629
1630 if (vm->run(1, &pValue))
1631 {
1632 pValue = vm->getResult();
1633 if (pValue != NULL)
1634 {
1635 if (pValue->isNull())
1636 {
1637 nx_strncpy(buffer, _T("(null)"), bufSize);
1638 }
1639 else if (pValue->isObject())
1640 {
1641 nx_strncpy(buffer, _T("(object)"), bufSize);
1642 }
1643 else if (pValue->isArray())
1644 {
1645 nx_strncpy(buffer, _T("(array)"), bufSize);
1646 }
1647 else
1648 {
1649 const TCHAR *strval;
1650
1651 strval = pValue->getValueAsCString();
1652 nx_strncpy(buffer, CHECK_NULL(strval), bufSize);
1653 }
1654 }
1655 else
1656 {
1657 nx_strncpy(buffer, _T("(null)"), bufSize);
1658 }
1659 success = true;
1660 }
1661 else
1662 {
1663 nx_strncpy(buffer, vm->getErrorText(), bufSize);
1664 }
1665 }
1666 delete vm;
1667 return success;
1668 }
1669
1670 /**
1671 * Fill NXCP message with thresholds
1672 */
1673 void DCItem::fillMessageWithThresholds(NXCPMessage *msg)
1674 {
1675 lock();
1676
1677 msg->setField(VID_NUM_THRESHOLDS, (UINT32)getThresholdCount());
1678 UINT32 id = VID_DCI_THRESHOLD_BASE;
1679 for(int i = 0; i < getThresholdCount(); i++, id += 20)
1680 {
1681 m_thresholds->get(i)->createMessage(msg, id);
1682 }
1683
1684 unlock();
1685 }
1686
1687 /**
1688 * Check if DCI has active threshold
1689 */
1690 bool DCItem::hasActiveThreshold()
1691 {
1692 bool result = false;
1693 lock();
1694 for(int i = 0; i < getThresholdCount(); i++)
1695 {
1696 if (m_thresholds->get(i)->isReached())
1697 {
1698 result = true;
1699 break;
1700 }
1701 }
1702 unlock();
1703 return result;
1704 }
1705
1706 /**
1707 * Get severity of active threshold. If no active threshold exist, returns SEVERITY_NORMAL.
1708 */
1709 int DCItem::getThresholdSeverity()
1710 {
1711 int result = SEVERITY_NORMAL;
1712 lock();
1713 for(int i = 0; i < getThresholdCount(); i++)
1714 {
1715 Threshold *t = m_thresholds->get(i);
1716 if (t->isReached())
1717 {
1718 result = t->getCurrentSeverity();
1719 break;
1720 }
1721 }
1722 unlock();
1723 return result;
1724 }
1725
1726 /**
1727 * Returns true if internal cache is loaded. If data collection object
1728 * does not have cache should return true
1729 */
1730 bool DCItem::isCacheLoaded()
1731 {
1732 return m_bCacheLoaded;
1733 }
1734
1735 /**
1736 * Create DCI from import file
1737 */
1738 void DCItem::updateFromImport(ConfigEntry *config)
1739 {
1740 DCObject::updateFromImport(config);
1741
1742 lock();
1743 m_dataType = (BYTE)config->getSubEntryValueAsInt(_T("dataType"));
1744 m_deltaCalculation = (BYTE)config->getSubEntryValueAsInt(_T("delta"));
1745 m_sampleCount = (BYTE)config->getSubEntryValueAsInt(_T("samples"));
1746 m_snmpRawValueType = (WORD)config->getSubEntryValueAsInt(_T("snmpRawValueType"));
1747
1748 ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
1749 if (thresholdsRoot != NULL)
1750 {
1751 ObjectArray<ConfigEntry> *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
1752 if (m_thresholds != NULL)
1753 m_thresholds->clear();
1754 else
1755 m_thresholds = new ObjectArray<Threshold>(thresholds->size(), 8, true);
1756 for(int i = 0; i < thresholds->size(); i++)
1757 {
1758 m_thresholds->add(new Threshold(thresholds->get(i), this));
1759 }
1760 delete thresholds;
1761 }
1762 else
1763 {
1764 delete_and_null(m_thresholds);
1765 }
1766
1767 updateCacheSizeInternal(true);
1768 unlock();
1769 }
1770
1771 /*
1772 * Clone DCI
1773 */
1774 DCObject *DCItem::clone()
1775 {
1776 return new DCItem(this);
1777 }
1778
1779 /**
1780 * Serialize object to JSON
1781 */
1782 json_t *DCItem::toJson()
1783 {
1784 json_t *root = DCObject::toJson();
1785 json_object_set_new(root, "deltaCalculation", json_integer(m_deltaCalculation));
1786 json_object_set_new(root, "dataType", json_integer(m_dataType));
1787 json_object_set_new(root, "sampleCount", json_integer(m_sampleCount));
1788 json_object_set_new(root, "thresholds", json_object_array(m_thresholds));
1789 json_object_set_new(root, "prevRawValue", json_string_t(m_prevRawValue));
1790 json_object_set_new(root, "prevValueTimeStamp", json_integer(m_tPrevValueTimeStamp));
1791 json_object_set_new(root, "baseUnits", json_integer(m_nBaseUnits));
1792 json_object_set_new(root, "multiplier", json_integer(m_nMultiplier));
1793 json_object_set_new(root, "customUnitName", json_string_t(m_customUnitName));
1794 json_object_set_new(root, "snmpRawValueType", json_integer(m_snmpRawValueType));
1795 json_object_set_new(root, "predictionEngine", json_string_t(m_predictionEngine));
1796 return root;
1797 }
1798
1799 /**
1800 * Prepare DCI object for recalculation (should be executed on DCI copy)
1801 */
1802 void DCItem::prepareForRecalc()
1803 {
1804 m_tPrevValueTimeStamp = 0;
1805 m_tLastPoll = 0;
1806 updateCacheSizeInternal(false);
1807 }
1808
1809 /**
1810 * Recalculate old value (should be executed on DCI copy)
1811 */
1812 void DCItem::recalculateValue(ItemValue &value)
1813 {
1814 if (m_tPrevValueTimeStamp == 0)
1815 m_prevRawValue = value; // Delta should be zero for first poll
1816 ItemValue rawValue = value;
1817
1818 // Cluster can have only aggregated data, and transformation
1819 // should not be used on aggregation
1820 if ((m_owner->getObjectClass() != OBJECT_CLUSTER) || (m_flags & DCF_TRANSFORM_AGGREGATED))
1821 {
1822 if (!transform(value, (value.getTimeStamp() > m_tPrevValueTimeStamp) ? (value.getTimeStamp() - m_tPrevValueTimeStamp) : 0))
1823 {
1824 return;
1825 }
1826 }
1827
1828 if (value.getTimeStamp() > m_tPrevValueTimeStamp)
1829 {
1830 m_prevRawValue = rawValue;
1831 m_tPrevValueTimeStamp = value.getTimeStamp();
1832 }
1833
1834 if ((m_cacheSize > 0) && (value.getTimeStamp() >= m_tPrevValueTimeStamp))
1835 {
1836 delete m_ppValueCache[m_cacheSize - 1];
1837 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_cacheSize - 1));
1838 m_ppValueCache[0] = new ItemValue(&value);
1839 }
1840
1841 m_tLastPoll = value.getTimeStamp();
1842 }