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