aee2f3e05e911dff9a88fc2205058777f75d35d3
[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
544 if (m_owner->isDataCollectionTarget())
545 static_cast<DataCollectionTarget*>(m_owner)->scheduleItemDataCleanup(m_id);
546 }
547
548 /**
549 * Update item from NXCP message
550 */
551 void DCItem::updateFromMessage(NXCPMessage *pMsg, UINT32 *pdwNumMaps, UINT32 **ppdwMapIndex, UINT32 **ppdwMapId)
552 {
553 DCObject::updateFromMessage(pMsg);
554
555 lock();
556
557 m_dataType = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DATA_TYPE);
558 m_deltaCalculation = (BYTE)pMsg->getFieldAsUInt16(VID_DCI_DELTA_CALCULATION);
559 m_sampleCount = (int)pMsg->getFieldAsUInt16(VID_SAMPLE_COUNT);
560 m_nBaseUnits = pMsg->getFieldAsUInt16(VID_BASE_UNITS);
561 m_nMultiplier = (int)pMsg->getFieldAsUInt32(VID_MULTIPLIER);
562 safe_free(m_customUnitName);
563 m_customUnitName = pMsg->getFieldAsString(VID_CUSTOM_UNITS_NAME);
564 m_snmpRawValueType = pMsg->getFieldAsUInt16(VID_SNMP_RAW_VALUE_TYPE);
565 pMsg->getFieldAsString(VID_NPE_NAME, m_predictionEngine, MAX_NPE_NAME_LEN);
566
567 // Update thresholds
568 UINT32 dwNum = pMsg->getFieldAsUInt32(VID_NUM_THRESHOLDS);
569 UINT32 *newThresholds = (UINT32 *)malloc(sizeof(UINT32) * dwNum);
570 *ppdwMapIndex = (UINT32 *)malloc(dwNum * sizeof(UINT32));
571 *ppdwMapId = (UINT32 *)malloc(dwNum * sizeof(UINT32));
572 *pdwNumMaps = 0;
573
574 // Read all new threshold ids from message
575 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
576 {
577 newThresholds[i] = pMsg->getFieldAsUInt32(dwId);
578 }
579
580 // Check if some thresholds was deleted, and reposition others if needed
581 Threshold **ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
582 memset(ppNewList, 0, sizeof(Threshold *) * dwNum);
583 for(int i = 0; i < getThresholdCount(); i++)
584 {
585 UINT32 j;
586 for(j = 0; j < dwNum; j++)
587 if (m_thresholds->get(i)->getId() == newThresholds[j])
588 break;
589 if (j == dwNum)
590 {
591 // No threshold with that id in new list, delete it
592 m_thresholds->remove(i);
593 i--;
594 }
595 else
596 {
597 // Move existing thresholds to appropriate positions in new list
598 ppNewList[j] = m_thresholds->get(i);
599 }
600 }
601
602 // Add or update thresholds
603 for(UINT32 i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId += 10)
604 {
605 if (newThresholds[i] == 0) // New threshold?
606 {
607 ppNewList[i] = new Threshold(this);
608 ppNewList[i]->createId();
609
610 // Add index -> id mapping
611 (*ppdwMapIndex)[*pdwNumMaps] = i;
612 (*ppdwMapId)[*pdwNumMaps] = ppNewList[i]->getId();
613 (*pdwNumMaps)++;
614 }
615 if (ppNewList[i] != NULL)
616 ppNewList[i]->updateFromMessage(pMsg, dwId);
617 }
618
619 if (dwNum > 0)
620 {
621 if (m_thresholds != NULL)
622 {
623 m_thresholds->setOwner(false);
624 m_thresholds->clear();
625 m_thresholds->setOwner(true);
626 }
627 else
628 {
629 m_thresholds = new ObjectArray<Threshold>((int)dwNum, 8, true);
630 }
631 for(UINT32 i = 0; i < dwNum; i++)
632 {
633 if (ppNewList[i] != NULL)
634 m_thresholds->add(ppNewList[i]);
635 }
636 }
637 else
638 {
639 delete_and_null(m_thresholds);
640 }
641
642 // Update data type in thresholds
643 for(int i = 0; i < getThresholdCount(); i++)
644 m_thresholds->get(i)->setDataType(m_dataType);
645
646 safe_free(ppNewList);
647 safe_free(newThresholds);
648 updateCacheSizeInternal();
649 unlock();
650 }
651
652 /**
653 * Process new collected value. Should return true on success.
654 * If returns false, current poll result will be converted into data collection error.
655 *
656 * @return true on success
657 */
658 bool DCItem::processNewValue(time_t tmTimeStamp, const void *originalValue, bool *updateStatus)
659 {
660 ItemValue rawValue, *pValue;
661
662 lock();
663
664 // Normally m_owner shouldn't be NULL for polled items, but who knows...
665 if (m_owner == NULL)
666 {
667 unlock();
668 return false;
669 }
670
671 // Create new ItemValue object and transform it as needed
672 pValue = new ItemValue((const TCHAR *)originalValue, tmTimeStamp);
673 if (m_tPrevValueTimeStamp == 0)
674 m_prevRawValue = *pValue; // Delta should be zero for first poll
675 rawValue = *pValue;
676
677 // Cluster can have only aggregated data, and transformation
678 // should not be used on aggregation
679 if ((m_owner->getObjectClass() != OBJECT_CLUSTER) || (m_flags & DCF_TRANSFORM_AGGREGATED))
680 {
681 if (!transform(*pValue, (tmTimeStamp > m_tPrevValueTimeStamp) ? (tmTimeStamp - m_tPrevValueTimeStamp) : 0))
682 {
683 unlock();
684 return false;
685 }
686 }
687
688 m_dwErrorCount = 0;
689
690 if (isStatusDCO() && (tmTimeStamp > m_tPrevValueTimeStamp) && ((m_cacheSize == 0) || !m_bCacheLoaded || ((UINT32)*pValue != (UINT32)*m_ppValueCache[0])))
691 {
692 *updateStatus = true;
693 }
694 else
695 {
696 *updateStatus = false;
697 }
698
699 if (tmTimeStamp > m_tPrevValueTimeStamp)
700 {
701 m_prevRawValue = rawValue;
702 m_tPrevValueTimeStamp = tmTimeStamp;
703
704 // Save raw value into database
705 QueueRawDciDataUpdate(tmTimeStamp, m_id, (const TCHAR *)originalValue, pValue->getString());
706 }
707
708 // Save transformed value to database
709 if ((m_flags & DCF_NO_STORAGE) == 0)
710 QueueIDataInsert(tmTimeStamp, m_owner->getId(), m_id, pValue->getString());
711 if (g_flags & AF_PERFDATA_STORAGE_DRIVER_LOADED)
712 PerfDataStorageRequest(this, tmTimeStamp, pValue->getString());
713
714 // Update prediction engine
715 if (m_predictionEngine[0] != 0)
716 {
717 PredictionEngine *engine = FindPredictionEngine(m_predictionEngine);
718 if (engine != NULL)
719 engine->update(m_id, tmTimeStamp, pValue->getDouble());
720 }
721
722 // Check thresholds and add value to cache
723 if (m_bCacheLoaded && (tmTimeStamp >= m_tPrevValueTimeStamp) &&
724 ((g_offlineDataRelevanceTime <= 0) || (tmTimeStamp > (time(NULL) - g_offlineDataRelevanceTime))))
725 {
726 checkThresholds(*pValue);
727 }
728
729 if ((m_cacheSize > 0) && (tmTimeStamp >= m_tPrevValueTimeStamp))
730 {
731 delete m_ppValueCache[m_cacheSize - 1];
732 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_cacheSize - 1));
733 m_ppValueCache[0] = pValue;
734 }
735 else
736 {
737 delete pValue;
738 }
739
740 unlock();
741
742 #ifdef WITH_ZMQ
743 ZmqPublishData(m_owner->getId(), m_id, m_name, pValue->getString());
744 #endif
745
746 return true;
747 }
748
749 /**
750 * Process new data collection error
751 */
752 void DCItem::processNewError(bool noInstance, time_t now)
753 {
754 lock();
755
756 // Normally m_owner shouldn't be NULL for polled items, but who knows...
757 if (m_owner == NULL)
758 {
759 unlock();
760 return;
761 }
762
763 m_dwErrorCount++;
764
765 for(int i = 0; i < getThresholdCount(); i++)
766 {
767 Threshold *t = m_thresholds->get(i);
768 ThresholdCheckResult result = t->checkError(m_dwErrorCount);
769 switch(result)
770 {
771 case ACTIVATED:
772 {
773 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
774 s_paramNamesReach, m_name, m_description, _T(""), _T(""),
775 m_id, m_instance, 0, _T(""));
776 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
777 if (evt != NULL)
778 {
779 t->markLastEvent(evt->getSeverity());
780 evt->decRefCount();
781 }
782 if (!(m_flags & DCF_ALL_THRESHOLDS))
783 {
784 i = m_thresholds->size(); // Stop processing
785 }
786 }
787 break;
788 case DEACTIVATED:
789 PostDciEventWithNames(t->getRearmEventCode(), m_owner->getId(), m_id, "ssissss",
790 s_paramNamesRearm, m_name, m_description, m_id, m_instance, _T(""), _T(""), _T(""));
791 break;
792 case ALREADY_ACTIVE:
793 {
794 // Check if we need to re-sent threshold violation event
795 UINT32 repeatInterval = (t->getRepeatInterval() == -1) ? g_thresholdRepeatInterval : (UINT32)t->getRepeatInterval();
796 if ((repeatInterval != 0) && (t->getLastEventTimestamp() + (time_t)repeatInterval < now))
797 {
798 PostDciEventWithNames(t->getEventCode(), m_owner->getId(), m_id, "ssssisds",
799 s_paramNamesReach, m_name, m_description, _T(""), _T(""),
800 m_id, m_instance, 1, _T(""));
801 EventTemplate *evt = FindEventTemplateByCode(t->getEventCode());
802 if (evt != NULL)
803 {
804 t->markLastEvent(evt->getSeverity());
805 evt->decRefCount();
806 }
807 }
808 }
809 if (!(m_flags & DCF_ALL_THRESHOLDS))
810 {
811 i = m_thresholds->size(); // Threshold condition still true, stop processing
812 }
813 break;
814 default:
815 break;
816 }
817 }
818
819 unlock();
820 }
821
822 /**
823 * Transform received value
824 */
825 bool DCItem::transform(ItemValue &value, time_t nElapsedTime)
826 {
827 bool success = true;
828
829 switch(m_deltaCalculation)
830 {
831 case DCM_SIMPLE:
832 switch(m_dataType)
833 {
834 case DCI_DT_INT:
835 value = (INT32)value - (INT32)m_prevRawValue;
836 break;
837 case DCI_DT_UINT:
838 value = (UINT32)value - (UINT32)m_prevRawValue;
839 break;
840 case DCI_DT_INT64:
841 value = (INT64)value - (INT64)m_prevRawValue;
842 break;
843 case DCI_DT_UINT64:
844 value = (UINT64)value - (UINT64)m_prevRawValue;
845 break;
846 case DCI_DT_FLOAT:
847 value = (double)value - (double)m_prevRawValue;
848 break;
849 case DCI_DT_STRING:
850 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
851 break;
852 default:
853 // Delta calculation is not supported for other types
854 break;
855 }
856 break;
857 case DCM_AVERAGE_PER_MINUTE:
858 nElapsedTime /= 60; // Convert to minutes
859 /* no break */
860 case DCM_AVERAGE_PER_SECOND:
861 // Check elapsed time to prevent divide-by-zero exception
862 if (nElapsedTime == 0)
863 nElapsedTime++;
864
865 switch(m_dataType)
866 {
867 case DCI_DT_INT:
868 value = ((INT32)value - (INT32)m_prevRawValue) / (INT32)nElapsedTime;
869 break;
870 case DCI_DT_UINT:
871 value = ((UINT32)value - (UINT32)m_prevRawValue) / (UINT32)nElapsedTime;
872 break;
873 case DCI_DT_INT64:
874 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
875 break;
876 case DCI_DT_UINT64:
877 value = ((UINT64)value - (UINT64)m_prevRawValue) / (UINT64)nElapsedTime;
878 break;
879 case DCI_DT_FLOAT:
880 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
881 break;
882 case DCI_DT_STRING:
883 // I don't see any meaning in _T("average delta per second (minute)") for string
884 // values, so result will be 0 if there are no difference between
885 // current and previous values, and 1 otherwise
886 value = (INT32)((_tcscmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
887 break;
888 default:
889 // Delta calculation is not supported for other types
890 break;
891 }
892 break;
893 default: // Default is no transformation
894 break;
895 }
896
897 if (m_transformationScript != NULL)
898 {
899 NXSL_VM *vm = new NXSL_VM(new NXSL_ServerEnv());
900 if (vm->load(m_transformationScript))
901 {
902 NXSL_Value *pValue = new NXSL_Value((const TCHAR *)value);
903 vm->setGlobalVariable(_T("$object"), m_owner->createNXSLObject());
904 if (m_owner->getObjectClass() == OBJECT_NODE)
905 {
906 vm->setGlobalVariable(_T("$node"), m_owner->createNXSLObject());
907 }
908 vm->setGlobalVariable(_T("$dci"), createNXSLObject());
909 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((m_owner->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
910
911 // remove lock from DCI for script execution to avoid deadlocks
912 unlock();
913 success = vm->run(1, &pValue);
914 lock();
915 if (success)
916 {
917 pValue = vm->getResult();
918 if (pValue != NULL)
919 {
920 switch(m_dataType)
921 {
922 case DCI_DT_INT:
923 value = pValue->getValueAsInt32();
924 break;
925 case DCI_DT_UINT:
926 value = pValue->getValueAsUInt32();
927 break;
928 case DCI_DT_INT64:
929 value = pValue->getValueAsInt64();
930 break;
931 case DCI_DT_UINT64:
932 value = pValue->getValueAsUInt64();
933 break;
934 case DCI_DT_FLOAT:
935 value = pValue->getValueAsReal();
936 break;
937 case DCI_DT_STRING:
938 value = CHECK_NULL_EX(pValue->getValueAsCString());
939 break;
940 default:
941 break;
942 }
943 }
944 }
945 else if (vm->getErrorCode() == NXSL_ERR_EXECUTION_ABORTED)
946 {
947 DbgPrintf(6, _T("Transformation script for DCI \"%s\" [%d] on node %s [%d] aborted"),
948 m_description, m_id, getOwnerName(), getOwnerId());
949 }
950 else
951 {
952 TCHAR buffer[1024];
953 _sntprintf(buffer, 1024, _T("DCI::%s::%d::TransformationScript"), getOwnerName(), m_id);
954 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, m_id, "ssd", buffer, vm->getErrorText(), m_id);
955 nxlog_write(MSG_TRANSFORMATION_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dsdss",
956 getOwnerId(), getOwnerName(), m_id, m_name, vm->getErrorText());
957 }
958 }
959 else
960 {
961 TCHAR buffer[1024];
962 _sntprintf(buffer, 1024, _T("DCI::%s::%d::TransformationScript"), getOwnerName(), m_id);
963 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, m_id, "ssd", buffer, vm->getErrorText(), m_id);
964 nxlog_write(MSG_TRANSFORMATION_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "dsdss",
965 getOwnerId(), getOwnerName(), m_id, m_name, vm->getErrorText());
966 success = false;
967 }
968 delete vm;
969 }
970 return success;
971 }
972
973 /**
974 * Set new ID and node/template association
975 */
976 void DCItem::changeBinding(UINT32 dwNewId, Template *pNewNode, BOOL doMacroExpansion)
977 {
978 DCObject::changeBinding(dwNewId, pNewNode, doMacroExpansion);
979
980 lock();
981 if (dwNewId != 0)
982 {
983 for(int i = 0; i < getThresholdCount(); i++)
984 m_thresholds->get(i)->bindToItem(m_id, m_owner->getId());
985 }
986
987 clearCache();
988 updateCacheSizeInternal();
989 unlock();
990 }
991
992 /**
993 * Update required cache size depending on thresholds
994 * dwCondId is an identifier of calling condition object id. If it is not 0,
995 * GetCacheSizeForDCI should be called with bNoLock == TRUE for appropriate
996 * condition object
997 */
998 void DCItem::updateCacheSizeInternal(UINT32 conditionId)
999 {
1000 UINT32 dwSize, dwRequiredSize;
1001
1002 // Sanity check
1003 if (m_owner == NULL)
1004 {
1005 nxlog_debug(3, _T("DCItem::updateCacheSize() called for DCI %d when m_owner == NULL"), m_id);
1006 return;
1007 }
1008
1009 // Minimum cache size is 1 for nodes (so GetLastValue can work)
1010 // and it is always 0 for templates
1011 if (((m_owner->isDataCollectionTarget() && (m_owner->getObjectClass() != OBJECT_CLUSTER)) ||
1012 ((m_owner->getObjectClass() == OBJECT_CLUSTER) && isAggregateOnCluster())) &&
1013 (m_instanceDiscoveryMethod == IDM_NONE))
1014 {
1015 dwRequiredSize = 1;
1016
1017 // Calculate required cache size
1018 for(int i = 0; i < getThresholdCount(); i++)
1019 if (dwRequiredSize < m_thresholds->get(i)->getRequiredCacheSize())
1020 dwRequiredSize = m_thresholds->get(i)->getRequiredCacheSize();
1021
1022 ObjectArray<NetObj> *conditions = g_idxConditionById.getObjects(true);
1023 for(int i = 0; i < conditions->size(); i++)
1024 {
1025 ConditionObject *c = (ConditionObject *)conditions->get(i);
1026 dwSize = c->getCacheSizeForDCI(m_id, conditionId == c->getId());
1027 if (dwSize > dwRequiredSize)
1028 dwRequiredSize = dwSize;
1029 c->decRefCount();
1030 }
1031 delete conditions;
1032 }
1033 else
1034 {
1035 dwRequiredSize = 0;
1036 }
1037
1038 // Update cache if needed
1039 if (dwRequiredSize < m_cacheSize)
1040 {
1041 // Destroy unneeded values
1042 if (m_cacheSize > 0)
1043 {
1044 for(UINT32 i = dwRequiredSize; i < m_cacheSize; i++)
1045 delete m_ppValueCache[i];
1046 }
1047
1048 m_cacheSize = dwRequiredSize;
1049 m_requiredCacheSize = dwRequiredSize;
1050 if (m_cacheSize > 0)
1051 {
1052 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_cacheSize);
1053 }
1054 else
1055 {
1056 free(m_ppValueCache);
1057 m_ppValueCache = NULL;
1058 }
1059 }
1060 else if (dwRequiredSize > m_cacheSize)
1061 {
1062 // Load missing values from database
1063 // Skip caching for DCIs where estimated time to fill the cache is less then 5 minutes
1064 // to reduce load on database at server startup
1065 if ((m_owner != NULL) && (((dwRequiredSize - m_cacheSize) * m_iPollingInterval > 300) || (m_source == DS_PUSH_AGENT)))
1066 {
1067 m_requiredCacheSize = dwRequiredSize;
1068 m_bCacheLoaded = false;
1069 g_dciCacheLoaderQueue.put(new DCObjectInfo(this));
1070 }
1071 else
1072 {
1073 // will not read data from database, fill cache with empty values
1074 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * dwRequiredSize);
1075 for(UINT32 i = m_cacheSize; i < dwRequiredSize; i++)
1076 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1077 DbgPrintf(7, _T("Cache load skipped for parameter %s [%d]"), m_name, (int)m_id);
1078 m_cacheSize = dwRequiredSize;
1079 m_bCacheLoaded = true;
1080 }
1081 }
1082 }
1083
1084 /**
1085 * Reload cache from database
1086 */
1087 void DCItem::reloadCache()
1088 {
1089 TCHAR szBuffer[MAX_DB_STRING];
1090
1091 switch(g_dbSyntax)
1092 {
1093 case DB_SYNTAX_MSSQL:
1094 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT TOP %d idata_value,idata_timestamp FROM idata_%d ")
1095 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1096 m_requiredCacheSize, m_owner->getId(), m_id);
1097 break;
1098 case DB_SYNTAX_ORACLE:
1099 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT * FROM (SELECT idata_value,idata_timestamp FROM idata_%d ")
1100 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC) WHERE ROWNUM <= %d"),
1101 m_owner->getId(), m_id, m_requiredCacheSize);
1102 break;
1103 case DB_SYNTAX_MYSQL:
1104 case DB_SYNTAX_PGSQL:
1105 case DB_SYNTAX_SQLITE:
1106 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1107 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC LIMIT %d"),
1108 m_owner->getId(), m_id, m_requiredCacheSize);
1109 break;
1110 case DB_SYNTAX_DB2:
1111 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1112 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC FETCH FIRST %d ROWS ONLY"),
1113 m_owner->getId(), m_id, m_requiredCacheSize);
1114 break;
1115 default:
1116 _sntprintf(szBuffer, MAX_DB_STRING, _T("SELECT idata_value,idata_timestamp FROM idata_%d ")
1117 _T("WHERE item_id=%d ORDER BY idata_timestamp DESC"),
1118 m_owner->getId(), m_id);
1119 break;
1120 }
1121
1122 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1123 DB_UNBUFFERED_RESULT hResult = DBSelectUnbuffered(hdb, szBuffer);
1124
1125 lock();
1126
1127 UINT32 i;
1128 for(i = 0; i < m_cacheSize; i++)
1129 delete m_ppValueCache[i];
1130
1131 if (m_cacheSize != m_requiredCacheSize)
1132 {
1133 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_requiredCacheSize);
1134 }
1135
1136 if (hResult != NULL)
1137 {
1138 // Create cache entries
1139 bool moreData = true;
1140 for(i = 0; (i < m_requiredCacheSize) && moreData; i++)
1141 {
1142 moreData = DBFetch(hResult);
1143 if (moreData)
1144 {
1145 DBGetField(hResult, 0, szBuffer, MAX_DB_STRING);
1146 m_ppValueCache[i] = new ItemValue(szBuffer, DBGetFieldULong(hResult, 1));
1147 }
1148 else
1149 {
1150 m_ppValueCache[i] = new ItemValue(_T(""), 1); // Empty value
1151 }
1152 }
1153
1154 // Fill up cache with empty values if we don't have enough values in database
1155 for(; i < m_requiredCacheSize; i++)
1156 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1157
1158 DBFreeResult(hResult);
1159 }
1160 else
1161 {
1162 // Error reading data from database, fill cache with empty values
1163 for(i = 0; i < m_requiredCacheSize; i++)
1164 m_ppValueCache[i] = new ItemValue(_T(""), 1);
1165 }
1166
1167 m_cacheSize = m_requiredCacheSize;
1168 m_bCacheLoaded = true;
1169 unlock();
1170
1171 DBConnectionPoolReleaseConnection(hdb);
1172 }
1173
1174 /**
1175 * Put last value into CSCP message
1176 */
1177 void DCItem::fillLastValueMessage(NXCPMessage *pMsg, UINT32 dwId)
1178 {
1179 lock();
1180 pMsg->setField(dwId++, m_id);
1181 pMsg->setField(dwId++, m_name);
1182 pMsg->setField(dwId++, m_description);
1183 pMsg->setField(dwId++, (UINT16)m_source);
1184 if (m_cacheSize > 0)
1185 {
1186 pMsg->setField(dwId++, (UINT16)m_dataType);
1187 pMsg->setField(dwId++, m_ppValueCache[0]->getString());
1188 pMsg->setFieldFromTime(dwId++, m_ppValueCache[0]->getTimeStamp());
1189 }
1190 else
1191 {
1192 pMsg->setField(dwId++, (UINT16)DCI_DT_NULL);
1193 pMsg->setField(dwId++, _T(""));
1194 pMsg->setField(dwId++, (UINT32)0);
1195 }
1196 pMsg->setField(dwId++, (WORD)(matchClusterResource() ? m_status : ITEM_STATUS_DISABLED)); // show resource-bound DCIs as inactive if cluster resource is not on this node
1197 pMsg->setField(dwId++, (WORD)getType());
1198 pMsg->setField(dwId++, m_dwErrorCount);
1199 pMsg->setField(dwId++, m_dwTemplateItemId);
1200
1201 int i;
1202 for(i = 0; i < getThresholdCount(); i++)
1203 {
1204 if (m_thresholds->get(i)->isReached())
1205 break;
1206 }
1207 if (i < getThresholdCount())
1208 {
1209 pMsg->setField(dwId++, (WORD)1);
1210 m_thresholds->get(i)->createMessage(pMsg, dwId);
1211 }
1212 else
1213 {
1214 pMsg->setField(dwId++, (WORD)0);
1215 }
1216
1217 unlock();
1218 }
1219
1220 /**
1221 * Get item's last value for use in NXSL
1222 */
1223 NXSL_Value *DCItem::getRawValueForNXSL()
1224 {
1225 lock();
1226 NXSL_Value *value = new NXSL_Value(m_prevRawValue.getString());
1227 unlock();
1228 return value;
1229 }
1230
1231 /**
1232 * Get item's last value for use in NXSL
1233 */
1234 NXSL_Value *DCItem::getValueForNXSL(int nFunction, int nPolls)
1235 {
1236 NXSL_Value *pValue;
1237
1238 lock();
1239 switch(nFunction)
1240 {
1241 case F_LAST:
1242 // cache placeholders will have timestamp 1
1243 pValue = (m_bCacheLoaded && (m_cacheSize > 0) && (m_ppValueCache[0]->getTimeStamp() != 1)) ? new NXSL_Value(m_ppValueCache[0]->getString()) : new NXSL_Value;
1244 break;
1245 case F_DIFF:
1246 if (m_bCacheLoaded && (m_cacheSize >= 2))
1247 {
1248 ItemValue result;
1249 CalculateItemValueDiff(result, m_dataType, *m_ppValueCache[0], *m_ppValueCache[1]);
1250 pValue = new NXSL_Value(result.getString());
1251 }
1252 else
1253 {
1254 pValue = new NXSL_Value;
1255 }
1256 break;
1257 case F_AVERAGE:
1258 if (m_bCacheLoaded && (m_cacheSize > 0))
1259 {
1260 ItemValue result;
1261 CalculateItemValueAverage(result, m_dataType, std::min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1262 pValue = new NXSL_Value(result.getString());
1263 }
1264 else
1265 {
1266 pValue = new NXSL_Value;
1267 }
1268 break;
1269 case F_DEVIATION:
1270 if (m_bCacheLoaded && (m_cacheSize > 0))
1271 {
1272 ItemValue result;
1273 CalculateItemValueMD(result, m_dataType, std::min(m_cacheSize, (UINT32)nPolls), m_ppValueCache);
1274 pValue = new NXSL_Value(result.getString());
1275 }
1276 else
1277 {
1278 pValue = new NXSL_Value;
1279 }
1280 break;
1281 case F_ERROR:
1282 pValue = new NXSL_Value((INT32)((m_dwErrorCount >= (UINT32)nPolls) ? 1 : 0));
1283 break;
1284 default:
1285 pValue = new NXSL_Value;
1286 break;
1287 }
1288 unlock();
1289 return pValue;
1290 }
1291
1292 /**
1293 * Get last value
1294 */
1295 const TCHAR *DCItem::getLastValue()
1296 {
1297 lock();
1298 const TCHAR *v = (m_cacheSize > 0) ? (const TCHAR *)m_ppValueCache[0]->getString() : NULL;
1299 unlock();
1300 return v;
1301 }
1302
1303 /**
1304 * Get copy of internal last value object. Caller is responsible for destroying returned object.
1305 */
1306 ItemValue *DCItem::getInternalLastValue()
1307 {
1308 lock();
1309 ItemValue *v = (m_cacheSize > 0) ? new ItemValue(m_ppValueCache[0]) : NULL;
1310 unlock();
1311 return v;
1312 }
1313
1314 /**
1315 * Get aggregate value. Returned value must be deallocated by caller.
1316 *
1317 * @return dynamically allocated value or NULL on error
1318 */
1319 TCHAR *DCItem::getAggregateValue(AggregationFunction func, time_t periodStart, time_t periodEnd)
1320 {
1321 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1322 TCHAR query[1024];
1323 TCHAR *result = NULL;
1324
1325 static const TCHAR *functions[] = { _T(""), _T("min"), _T("max"), _T("avg"), _T("sum") };
1326
1327 if (g_dbSyntax == DB_SYNTAX_ORACLE)
1328 {
1329 _sntprintf(query, 1024, _T("SELECT %s(coalesce(to_number(idata_value),0)) FROM idata_%u ")
1330 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ?"),
1331 functions[func], m_owner->getId());
1332 }
1333 else if (g_dbSyntax == DB_SYNTAX_MSSQL)
1334 {
1335 _sntprintf(query, 1024, _T("SELECT %s(coalesce(cast(idata_value as float),0)) FROM idata_%u ")
1336 _T("WHERE item_id=? AND (idata_timestamp BETWEEN ? AND ?) AND isnumeric(idata_value)=1"),
1337 functions[func], m_owner->getId());
1338 }
1339 else if (g_dbSyntax == DB_SYNTAX_PGSQL)
1340 {
1341 _sntprintf(query, 1024, _T("SELECT %s(idata_value::double precision) FROM idata_%u ")
1342 _T("WHERE item_id=? AND idata_timestamp BETWEEN ? AND ? AND idata_value~E'^\\\\d+(\\\\.\\\\d+)*$'"),
1343 functions[func], m_owner->getId());
1344 }
1345 else
1346 {
1347 _sntprintf(query, 1024, _T("SELECT %s(coalesce(idata_value,0)) FROM idata_%u ")
1348 _T("WHERE item_id=? and idata_timestamp between ? and ?"),
1349 functions[func], m_owner->getId());
1350 }
1351
1352 DB_STATEMENT hStmt = DBPrepare(hdb, query);
1353 if (hStmt != NULL)
1354 {
1355 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
1356 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)periodStart);
1357 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (INT32)periodEnd);
1358 DB_RESULT hResult = DBSelectPrepared(hStmt);
1359 if (hResult != NULL)
1360 {
1361 if (DBGetNumRows(hResult) == 1)
1362 {
1363 result = DBGetField(hResult, 0, 0, NULL, 0);
1364 }
1365 DBFreeResult(hResult);
1366 }
1367 DBFreeStatement(hStmt);
1368 }
1369
1370 DBConnectionPoolReleaseConnection(hdb);
1371 return result;
1372 }
1373
1374 /**
1375 * Delete all collected data
1376 */
1377 bool DCItem::deleteAllData()
1378 {
1379 TCHAR szQuery[256];
1380 bool success;
1381
1382 lock();
1383 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1384 _sntprintf(szQuery, 256, _T("DELETE FROM idata_%d WHERE item_id=%d"), m_owner->getId(), m_id);
1385 success = DBQuery(hdb, szQuery) ? true : false;
1386 DBConnectionPoolReleaseConnection(hdb);
1387 clearCache();
1388 updateCacheSizeInternal();
1389 unlock();
1390 return success;
1391 }
1392
1393 /**
1394 * Update from template item
1395 */
1396 void DCItem::updateFromTemplate(DCObject *src)
1397 {
1398 DCObject::updateFromTemplate(src);
1399
1400 if (src->getType() != DCO_TYPE_ITEM)
1401 {
1402 DbgPrintf(2, _T("INTERNAL ERROR: DCItem::updateFromTemplate(%d, %d): source type is %d"), (int)m_id, (int)src->getId(), src->getType());
1403 return;
1404 }
1405
1406 lock();
1407 DCItem *item = (DCItem *)src;
1408
1409 m_dataType = item->m_dataType;
1410 m_deltaCalculation = item->m_deltaCalculation;
1411 m_sampleCount = item->m_sampleCount;
1412 m_snmpRawValueType = item->m_snmpRawValueType;
1413
1414 m_nBaseUnits = item->m_nBaseUnits;
1415 m_nMultiplier = item->m_nMultiplier;
1416 safe_free(m_customUnitName);
1417 m_customUnitName = (item->m_customUnitName != NULL) ? _tcsdup(item->m_customUnitName) : NULL;
1418
1419 // Copy thresholds
1420 // ***************************
1421 // First, skip matching thresholds
1422 int count = std::min(getThresholdCount(), item->getThresholdCount());
1423 int i;
1424 for(i = 0; i < count; i++)
1425 if (!m_thresholds->get(i)->compare(item->m_thresholds->get(i)))
1426 break;
1427 count = i; // First unmatched threshold's position
1428
1429 // Delete all original thresholds starting from first unmatched
1430 while(count < getThresholdCount())
1431 m_thresholds->remove(count);
1432
1433 // (Re)create thresholds starting from first unmatched
1434 if ((m_thresholds == NULL) && (item->getThresholdCount() > 0))
1435 m_thresholds = new ObjectArray<Threshold>(item->getThresholdCount(), 8, true);
1436 for(i = count; i < item->getThresholdCount(); i++)
1437 {
1438 Threshold *t = new Threshold(item->m_thresholds->get(i));
1439 t->createId();
1440 t->bindToItem(m_id, m_owner->getId());
1441 m_thresholds->add(t);
1442 }
1443
1444 // Update data type in thresholds
1445 for(i = 0; i < getThresholdCount(); i++)
1446 m_thresholds->get(i)->setDataType(m_dataType);
1447
1448 updateCacheSizeInternal();
1449 unlock();
1450 }
1451
1452 /**
1453 * Get list of used events
1454 */
1455 void DCItem::getEventList(IntegerArray<UINT32> *eventList)
1456 {
1457 lock();
1458 if (m_thresholds != NULL)
1459 {
1460 for(int i = 0; i < m_thresholds->size(); i++)
1461 {
1462 eventList->add(m_thresholds->get(i)->getEventCode());
1463 eventList->add(m_thresholds->get(i)->getRearmEventCode());
1464 }
1465 }
1466 unlock();
1467 }
1468
1469 /**
1470 * Create management pack record
1471 */
1472 void DCItem::createExportRecord(String &str)
1473 {
1474 lock();
1475
1476 str.appendFormattedString(_T("\t\t\t\t<dci id=\"%d\">\n")
1477 _T("\t\t\t\t\t<guid>%s</guid>\n")
1478 _T("\t\t\t\t\t<name>%s</name>\n")
1479 _T("\t\t\t\t\t<description>%s</description>\n")
1480 _T("\t\t\t\t\t<dataType>%d</dataType>\n")
1481 _T("\t\t\t\t\t<samples>%d</samples>\n")
1482 _T("\t\t\t\t\t<origin>%d</origin>\n")
1483 _T("\t\t\t\t\t<interval>%d</interval>\n")
1484 _T("\t\t\t\t\t<retention>%d</retention>\n")
1485 _T("\t\t\t\t\t<instance>%s</instance>\n")
1486 _T("\t\t\t\t\t<systemTag>%s</systemTag>\n")
1487 _T("\t\t\t\t\t<delta>%d</delta>\n")
1488 _T("\t\t\t\t\t<flags>%d</flags>\n")
1489 _T("\t\t\t\t\t<snmpRawValueType>%d</snmpRawValueType>\n")
1490 _T("\t\t\t\t\t<snmpPort>%d</snmpPort>\n")
1491 _T("\t\t\t\t\t<instanceDiscoveryMethod>%d</instanceDiscoveryMethod>\n"),
1492 (int)m_id, (const TCHAR *)m_guid.toString(),
1493 (const TCHAR *)EscapeStringForXML2(m_name),
1494 (const TCHAR *)EscapeStringForXML2(m_description),
1495 m_dataType, m_sampleCount, (int)m_source, m_iPollingInterval, m_iRetentionTime,
1496 (const TCHAR *)EscapeStringForXML2(m_instance),
1497 (const TCHAR *)EscapeStringForXML2(m_systemTag),
1498 (int)m_deltaCalculation, (int)m_flags,
1499 (int)m_snmpRawValueType, (int)m_snmpPort, (int)m_instanceDiscoveryMethod);
1500
1501 if (m_transformationScriptSource != NULL)
1502 {
1503 str += _T("\t\t\t\t\t<transformation>");
1504 str.appendPreallocated(EscapeStringForXML(m_transformationScriptSource, -1));
1505 str += _T("</transformation>\n");
1506 }
1507
1508 if ((m_schedules != NULL) && (m_schedules->size() > 0))
1509 {
1510 str += _T("\t\t\t\t\t<schedules>\n");
1511 for(int i = 0; i < m_schedules->size(); i++)
1512 str.appendFormattedString(_T("\t\t\t\t\t\t<schedule>%s</schedule>\n"), (const TCHAR *)EscapeStringForXML2(m_schedules->get(i)));
1513 str += _T("\t\t\t\t\t</schedules>\n");
1514 }
1515
1516 if (m_thresholds != NULL)
1517 {
1518 str += _T("\t\t\t\t\t<thresholds>\n");
1519 for(int i = 0; i < m_thresholds->size(); i++)
1520 {
1521 m_thresholds->get(i)->createNXMPRecord(str, i + 1);
1522 }
1523 str += _T("\t\t\t\t\t</thresholds>\n");
1524 }
1525
1526 if (m_pszPerfTabSettings != NULL)
1527 {
1528 str += _T("\t\t\t\t\t<perfTabSettings>");
1529 str.appendPreallocated(EscapeStringForXML(m_pszPerfTabSettings, -1));
1530 str += _T("</perfTabSettings>\n");
1531 }
1532
1533 if (m_instanceDiscoveryData != NULL)
1534 {
1535 str += _T("\t\t\t\t\t<instanceDiscoveryData>");
1536 str.appendPreallocated(EscapeStringForXML(m_instanceDiscoveryData, -1));
1537 str += _T("</instanceDiscoveryData>\n");
1538 }
1539
1540 if (m_instanceFilterSource != NULL)
1541 {
1542 str += _T("\t\t\t\t\t<instanceFilter>");
1543 str.appendPreallocated(EscapeStringForXML(m_instanceFilterSource, -1));
1544 str += _T("</instanceFilter>\n");
1545 }
1546
1547 unlock();
1548 str += _T("\t\t\t\t</dci>\n");
1549 }
1550
1551 /**
1552 * Add threshold to the list
1553 */
1554 void DCItem::addThreshold(Threshold *pThreshold)
1555 {
1556 if (m_thresholds == NULL)
1557 m_thresholds = new ObjectArray<Threshold>(8, 8, true);
1558 m_thresholds->add(pThreshold);
1559 }
1560
1561 /**
1562 * Enumerate all thresholds
1563 */
1564 BOOL DCItem::enumThresholds(BOOL (* pfCallback)(Threshold *, UINT32, void *), void *pArg)
1565 {
1566 BOOL bRet = TRUE;
1567
1568 lock();
1569 if (m_thresholds != NULL)
1570 {
1571 for(int i = 0; i < m_thresholds->size(); i++)
1572 {
1573 if (!pfCallback(m_thresholds->get(i), i, pArg))
1574 {
1575 bRet = FALSE;
1576 break;
1577 }
1578 }
1579 }
1580 unlock();
1581 return bRet;
1582 }
1583
1584 /**
1585 * Test DCI's transformation script
1586 */
1587 bool DCItem::testTransformation(DataCollectionTarget *object, const TCHAR *script, const TCHAR *value, TCHAR *buffer, size_t bufSize)
1588 {
1589 bool success = false;
1590 NXSL_VM *vm = NXSLCompileAndCreateVM(script, buffer, (int)bufSize, new NXSL_ServerEnv);
1591 if (vm != NULL)
1592 {
1593 NXSL_Value *pValue = new NXSL_Value(value);
1594 vm->setGlobalVariable(_T("$object"), object->createNXSLObject());
1595 if (object->getObjectClass() == OBJECT_NODE)
1596 {
1597 vm->setGlobalVariable(_T("$node"), object->createNXSLObject());
1598 }
1599 //FIXME: vm->setGlobalVariable(_T("$dci"), new NXSL_Value(new NXSL_Object(&g_nxslDciClass, this)));
1600 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((object->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
1601
1602 if (vm->run(1, &pValue))
1603 {
1604 pValue = vm->getResult();
1605 if (pValue != NULL)
1606 {
1607 if (pValue->isNull())
1608 {
1609 nx_strncpy(buffer, _T("(null)"), bufSize);
1610 }
1611 else if (pValue->isObject())
1612 {
1613 nx_strncpy(buffer, _T("(object)"), bufSize);
1614 }
1615 else if (pValue->isArray())
1616 {
1617 nx_strncpy(buffer, _T("(array)"), bufSize);
1618 }
1619 else
1620 {
1621 const TCHAR *strval;
1622
1623 strval = pValue->getValueAsCString();
1624 nx_strncpy(buffer, CHECK_NULL(strval), bufSize);
1625 }
1626 }
1627 else
1628 {
1629 nx_strncpy(buffer, _T("(null)"), bufSize);
1630 }
1631 success = true;
1632 }
1633 else
1634 {
1635 nx_strncpy(buffer, vm->getErrorText(), bufSize);
1636 }
1637 }
1638 delete vm;
1639 return success;
1640 }
1641
1642 /**
1643 * Fill NXCP message with thresholds
1644 */
1645 void DCItem::fillMessageWithThresholds(NXCPMessage *msg)
1646 {
1647 lock();
1648
1649 msg->setField(VID_NUM_THRESHOLDS, (UINT32)getThresholdCount());
1650 UINT32 id = VID_DCI_THRESHOLD_BASE;
1651 for(int i = 0; i < getThresholdCount(); i++, id += 20)
1652 {
1653 m_thresholds->get(i)->createMessage(msg, id);
1654 }
1655
1656 unlock();
1657 }
1658
1659 /**
1660 * Check if DCI has active threshold
1661 */
1662 bool DCItem::hasActiveThreshold()
1663 {
1664 bool result = false;
1665 lock();
1666 for(int i = 0; i < getThresholdCount(); i++)
1667 {
1668 if (m_thresholds->get(i)->isReached())
1669 {
1670 result = true;
1671 break;
1672 }
1673 }
1674 unlock();
1675 return result;
1676 }
1677
1678 /**
1679 * Get severity of active threshold. If no active threshold exist, returns SEVERITY_NORMAL.
1680 */
1681 int DCItem::getThresholdSeverity()
1682 {
1683 int result = SEVERITY_NORMAL;
1684 lock();
1685 for(int i = 0; i < getThresholdCount(); i++)
1686 {
1687 Threshold *t = m_thresholds->get(i);
1688 if (t->isReached())
1689 {
1690 result = t->getCurrentSeverity();
1691 break;
1692 }
1693 }
1694 unlock();
1695 return result;
1696 }
1697
1698 /**
1699 * Returns true if internal cache is loaded. If data collection object
1700 * does not have cache should return true
1701 */
1702 bool DCItem::isCacheLoaded()
1703 {
1704 return m_bCacheLoaded;
1705 }
1706
1707 /**
1708 * Create DCI from import file
1709 */
1710 void DCItem::updateFromImport(ConfigEntry *config)
1711 {
1712 DCObject::updateFromImport(config);
1713
1714 lock();
1715 m_dataType = (BYTE)config->getSubEntryValueAsInt(_T("dataType"));
1716 m_deltaCalculation = (BYTE)config->getSubEntryValueAsInt(_T("delta"));
1717 m_sampleCount = (BYTE)config->getSubEntryValueAsInt(_T("samples"));
1718 m_snmpRawValueType = (WORD)config->getSubEntryValueAsInt(_T("snmpRawValueType"));
1719
1720 ConfigEntry *thresholdsRoot = config->findEntry(_T("thresholds"));
1721 if (thresholdsRoot != NULL)
1722 {
1723 ObjectArray<ConfigEntry> *thresholds = thresholdsRoot->getSubEntries(_T("threshold#*"));
1724 if (m_thresholds != NULL)
1725 m_thresholds->clear();
1726 else
1727 m_thresholds = new ObjectArray<Threshold>(thresholds->size(), 8, true);
1728 for(int i = 0; i < thresholds->size(); i++)
1729 {
1730 m_thresholds->add(new Threshold(thresholds->get(i), this));
1731 }
1732 delete thresholds;
1733 }
1734 else
1735 {
1736 delete_and_null(m_thresholds);
1737 }
1738
1739 updateCacheSizeInternal();
1740 unlock();
1741 }
1742
1743 /*
1744 * Clone DCI
1745 */
1746 DCObject *DCItem::clone()
1747 {
1748 return new DCItem(this);
1749 }
1750
1751 /**
1752 * Serialize object to JSON
1753 */
1754 json_t *DCItem::toJson()
1755 {
1756 json_t *root = DCObject::toJson();
1757 json_object_set_new(root, "deltaCalculation", json_integer(m_deltaCalculation));
1758 json_object_set_new(root, "dataType", json_integer(m_dataType));
1759 json_object_set_new(root, "sampleCount", json_integer(m_sampleCount));
1760 json_object_set_new(root, "thresholds", json_object_array(m_thresholds));
1761 json_object_set_new(root, "prevRawValue", json_string_t(m_prevRawValue));
1762 json_object_set_new(root, "prevValueTimeStamp", json_integer(m_tPrevValueTimeStamp));
1763 json_object_set_new(root, "baseUnits", json_integer(m_nBaseUnits));
1764 json_object_set_new(root, "multiplier", json_integer(m_nMultiplier));
1765 json_object_set_new(root, "customUnitName", json_string_t(m_customUnitName));
1766 json_object_set_new(root, "snmpRawValueType", json_integer(m_snmpRawValueType));
1767 json_object_set_new(root, "predictionEngine", json_string_t(m_predictionEngine));
1768 return root;
1769 }