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