fixed bug in script threshold update from template
[public/netxms.git] / src / server / core / dcithreshold.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 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: dcithreshold.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Create new threshold for given DCI
27 */
28 Threshold::Threshold(DCItem *pRelatedItem)
29 {
30 m_id = 0;
31 m_itemId = pRelatedItem->getId();
32 m_targetId = pRelatedItem->getOwnerId();
33 m_eventCode = EVENT_THRESHOLD_REACHED;
34 m_rearmEventCode = EVENT_THRESHOLD_REARMED;
35 m_function = F_LAST;
36 m_operation = OP_EQ;
37 m_dataType = pRelatedItem->getDataType();
38 m_sampleCount = 1;
39 m_scriptSource = NULL;
40 m_script = NULL;
41 m_isReached = FALSE;
42 m_currentSeverity = SEVERITY_NORMAL;
43 m_repeatInterval = -1;
44 m_lastEventTimestamp = 0;
45 m_numMatches = 0;
46 }
47
48 /**
49 * Constructor for NXMP parser
50 */
51 Threshold::Threshold()
52 {
53 m_id = 0;
54 m_itemId = 0;
55 m_targetId = 0;
56 m_eventCode = EVENT_THRESHOLD_REACHED;
57 m_rearmEventCode = EVENT_THRESHOLD_REARMED;
58 m_function = F_LAST;
59 m_operation = OP_EQ;
60 m_dataType = 0;
61 m_sampleCount = 1;
62 m_scriptSource = NULL;
63 m_script = NULL;
64 m_isReached = FALSE;
65 m_currentSeverity = SEVERITY_NORMAL;
66 m_repeatInterval = -1;
67 m_lastEventTimestamp = 0;
68 m_numMatches = 0;
69 }
70
71 /**
72 * Create from another threshold object
73 */
74 Threshold::Threshold(Threshold *src)
75 {
76 m_id = src->m_id;
77 m_itemId = src->m_itemId;
78 m_targetId = src->m_targetId;
79 m_eventCode = src->m_eventCode;
80 m_rearmEventCode = src->m_rearmEventCode;
81 m_value = src->m_value;
82 m_function = src->m_function;
83 m_operation = src->m_operation;
84 m_dataType = src->m_dataType;
85 m_sampleCount = src->m_sampleCount;
86 m_scriptSource = NULL;
87 m_script = NULL;
88 setScript((src->m_scriptSource != NULL) ? _tcsdup(src->m_scriptSource) : NULL);
89 m_isReached = FALSE;
90 m_currentSeverity = SEVERITY_NORMAL;
91 m_repeatInterval = src->m_repeatInterval;
92 m_lastEventTimestamp = 0;
93 m_numMatches = 0;
94 }
95
96 /**
97 * Constructor for creating object from database
98 * This constructor assumes that SELECT query look as following:
99 * SELECT threshold_id,fire_value,rearm_value,check_function,check_operation,
100 * sample_count,script,event_code,current_state,rearm_event_code,
101 * repeat_interval,current_severity,last_event_timestamp,match_count FROM thresholds
102 */
103 Threshold::Threshold(DB_RESULT hResult, int iRow, DCItem *pRelatedItem)
104 {
105 TCHAR szBuffer[MAX_DB_STRING];
106
107 m_id = DBGetFieldULong(hResult, iRow, 0);
108 m_itemId = pRelatedItem->getId();
109 m_targetId = pRelatedItem->getOwnerId();
110 m_eventCode = DBGetFieldULong(hResult, iRow, 7);
111 m_rearmEventCode = DBGetFieldULong(hResult, iRow, 9);
112 DBGetField(hResult, iRow, 1, szBuffer, MAX_DB_STRING);
113 m_value = szBuffer;
114 m_function = (BYTE)DBGetFieldLong(hResult, iRow, 3);
115 m_operation = (BYTE)DBGetFieldLong(hResult, iRow, 4);
116 m_dataType = pRelatedItem->getDataType();
117 m_sampleCount = DBGetFieldLong(hResult, iRow, 5);
118 if ((m_function == F_LAST) && (m_sampleCount < 1))
119 m_sampleCount = 1;
120 m_scriptSource = NULL;
121 m_script = NULL;
122 setScript(DBGetField(hResult, iRow, 6, NULL, 0));
123 m_isReached = DBGetFieldLong(hResult, iRow, 8);
124 m_repeatInterval = DBGetFieldLong(hResult, iRow, 10);
125 m_currentSeverity = (BYTE)DBGetFieldLong(hResult, iRow, 11);
126 m_lastEventTimestamp = (time_t)DBGetFieldULong(hResult, iRow, 12);
127 m_numMatches = DBGetFieldLong(hResult, iRow, 13);
128 }
129
130 /**
131 * Create threshold from import file
132 */
133 Threshold::Threshold(ConfigEntry *config, DCItem *parentItem)
134 {
135 createId();
136 m_itemId = parentItem->getId();
137 m_targetId = parentItem->getOwnerId();
138 m_eventCode = EventCodeFromName(config->getSubEntryValue(_T("activationEvent"), 0, _T("SYS_THRESHOLD_REACHED")));
139 m_rearmEventCode = EventCodeFromName(config->getSubEntryValue(_T("deactivationEvent"), 0, _T("SYS_THRESHOLD_REARMED")));
140 m_function = (BYTE)config->getSubEntryValueAsInt(_T("function"), 0, F_LAST);
141 m_operation = (BYTE)config->getSubEntryValueAsInt(_T("condition"), 0, OP_EQ);
142 m_dataType = parentItem->getDataType();
143 m_value = config->getSubEntryValue(_T("value"), 0, _T(""));
144 m_sampleCount = (config->getSubEntryValue(_T("sampleCount")) != NULL) ? config->getSubEntryValueAsInt(_T("sampleCount"), 0, 1) : config->getSubEntryValueAsInt(_T("param1"), 0, 1);
145 m_scriptSource = NULL;
146 m_script = NULL;
147 const TCHAR *script = config->getSubEntryValue(_T("script"));
148 setScript(_tcsdup_ex(script));
149 m_isReached = FALSE;
150 m_currentSeverity = SEVERITY_NORMAL;
151 m_repeatInterval = config->getSubEntryValueAsInt(_T("repeatInterval"), 0, -1);
152 m_lastEventTimestamp = 0;
153 m_numMatches = 0;
154 }
155
156 /**
157 * Destructor
158 */
159 Threshold::~Threshold()
160 {
161 safe_free(m_scriptSource);
162 delete m_script;
163 }
164
165 /**
166 * Create new unique id for object
167 */
168 void Threshold::createId()
169 {
170 m_id = CreateUniqueId(IDG_THRESHOLD);
171 }
172
173 /**
174 * Save threshold to database
175 */
176 BOOL Threshold::saveToDB(DB_HANDLE hdb, UINT32 dwIndex)
177 {
178 // Prepare and execute query
179 DB_STATEMENT hStmt;
180 if (!IsDatabaseRecordExist(hdb, _T("thresholds"), _T("threshold_id"), m_id))
181 {
182 hStmt = DBPrepare(hdb,
183 _T("INSERT INTO thresholds (item_id,fire_value,rearm_value,")
184 _T("check_function,check_operation,sample_count,script,event_code,")
185 _T("sequence_number,current_state,rearm_event_code,repeat_interval,")
186 _T("current_severity,last_event_timestamp,match_count,threshold_id) ")
187 _T("VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
188 }
189 else
190 {
191 hStmt = DBPrepare(hdb,
192 _T("UPDATE thresholds SET item_id=?,fire_value=?,rearm_value=?,check_function=?,")
193 _T("check_operation=?,sample_count=?,script=?,event_code=?,")
194 _T("sequence_number=?,current_state=?,rearm_event_code=?,")
195 _T("repeat_interval=?,current_severity=?,last_event_timestamp=?,")
196 _T("match_count=? WHERE threshold_id=?"));
197 }
198 if (hStmt == NULL)
199 return FALSE;
200
201 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_itemId);
202 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, m_value.getString(), DB_BIND_STATIC);
203 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, NULL, DB_BIND_STATIC);
204 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (INT32)m_function);
205 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (INT32)m_operation);
206 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (INT32)m_sampleCount);
207 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, m_scriptSource, DB_BIND_STATIC);
208 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, m_eventCode);
209 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, dwIndex);
210 DBBind(hStmt, 10, DB_SQLTYPE_INTEGER, (INT32)(m_isReached ? 1 : 0));
211 DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, m_rearmEventCode);
212 DBBind(hStmt, 12, DB_SQLTYPE_INTEGER, (INT32)m_repeatInterval);
213 DBBind(hStmt, 13, DB_SQLTYPE_INTEGER, (INT32)m_currentSeverity);
214 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (INT32)m_lastEventTimestamp);
215 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, (INT32)m_numMatches);
216 DBBind(hStmt, 16, DB_SQLTYPE_INTEGER, (INT32)m_id);
217
218 BOOL success = DBExecute(hStmt);
219 DBFreeStatement(hStmt);
220 return success;
221 }
222
223 /**
224 * Check threshold
225 * Method will return the following codes:
226 * THRESHOLD_REACHED - when item's value match the threshold condition while previous check doesn't
227 * THRESHOLD_REARMED - when item's value doesn't match the threshold condition while previous check do
228 * NO_ACTION - when there are no changes in item's value match to threshold's condition
229 */
230 ThresholdCheckResult Threshold::check(ItemValue &value, ItemValue **ppPrevValues, ItemValue &fvalue, NetObj *target, DCItem *dci)
231 {
232 // check if there is enough cached data
233 switch(m_function)
234 {
235 case F_DIFF:
236 if (ppPrevValues[0]->getTimeStamp() == 1) // Timestamp 1 means placeholder value inserted by cache loader
237 return m_isReached ? ALREADY_ACTIVE : ALREADY_INACTIVE;
238 break;
239 case F_AVERAGE:
240 case F_SUM:
241 case F_DEVIATION:
242 for(int i = 0; i < m_sampleCount - 1; i++)
243 if (ppPrevValues[i]->getTimeStamp() == 1) // Timestamp 1 means placeholder value inserted by cache loader
244 return m_isReached ? ALREADY_ACTIVE : ALREADY_INACTIVE;
245 break;
246 default:
247 break;
248 }
249
250 BOOL bMatch = FALSE;
251 int iDataType = m_dataType;
252
253 // Execute function on value
254 switch(m_function)
255 {
256 case F_LAST: // Check last value only
257 case F_SCRIPT:
258 fvalue = value;
259 break;
260 case F_AVERAGE: // Check average value for last n polls
261 calculateAverageValue(&fvalue, value, ppPrevValues);
262 break;
263 case F_SUM:
264 calculateSumValue(&fvalue, value, ppPrevValues);
265 break;
266 case F_DEVIATION: // Check mean absolute deviation
267 calculateMDValue(&fvalue, value, ppPrevValues);
268 break;
269 case F_DIFF:
270 calculateDiff(&fvalue, value, ppPrevValues);
271 if (m_dataType == DCI_DT_STRING)
272 iDataType = DCI_DT_INT; // diff() for strings is an integer
273 break;
274 case F_ERROR: // Check for collection error
275 fvalue = (UINT32)0;
276 break;
277 default:
278 break;
279 }
280
281 // Run comparision operation on function result and threshold value
282 if (m_function == F_ERROR)
283 {
284 // Threshold::Check() can be called only for valid values, which
285 // means that error thresholds cannot be active
286 bMatch = FALSE;
287 }
288 else if (m_function == F_SCRIPT)
289 {
290 if (m_script != NULL)
291 {
292 NXSL_VM *vm = new NXSL_VM(new NXSL_ServerEnv());
293 if (vm->load(m_script))
294 {
295 NXSL_Value *parameters[2];
296 parameters[0] = new NXSL_Value(value.getString());
297 parameters[1] = new NXSL_Value(m_value.getString());
298 vm->setGlobalVariable(_T("$object"), target->createNXSLObject());
299 if (target->getObjectClass() == OBJECT_NODE)
300 {
301 vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, target)));
302 }
303 vm->setGlobalVariable(_T("$dci"), dci->createNXSLObject());
304 vm->setGlobalVariable(_T("$isCluster"), new NXSL_Value((target->getObjectClass() == OBJECT_CLUSTER) ? 1 : 0));
305 if (vm->run(2, parameters))
306 {
307 NXSL_Value *result = vm->getResult();
308 if (result != NULL)
309 {
310 bMatch = (result->getValueAsInt32() != 0);
311 }
312 }
313 else
314 {
315 TCHAR buffer[1024];
316 _sntprintf(buffer, 1024, _T("DCI::%s::%d::%d::ThresholdScript"), target->getName(), dci->getId(), m_id);
317 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, dci->getId(), "ssd", buffer, vm->getErrorText(), dci->getId());
318 nxlog_write(MSG_THRESHOLD_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "sdds", target->getName(), dci->getId(), m_id, vm->getErrorText());
319 }
320 }
321 else
322 {
323 TCHAR buffer[1024];
324 _sntprintf(buffer, 1024, _T("DCI::%s::%d::%d::ThresholdScript"), target->getName(), dci->getId(), m_id);
325 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, dci->getId(), "ssd", buffer, vm->getErrorText(), dci->getId());
326 nxlog_write(MSG_THRESHOLD_SCRIPT_EXECUTION_ERROR, NXLOG_WARNING, "sdds", target->getName(), dci->getId(), m_id, vm->getErrorText());
327 }
328 delete vm;
329 }
330 else
331 {
332 DbgPrintf(7, _T("Script not compiled for threshold %d of DCI %d of data collection target %s [%d]"),
333 m_id, dci->getId(), target->getName(), target->getId());
334 }
335 }
336 else
337 {
338 switch(m_operation)
339 {
340 case OP_LE: // Less
341 switch(iDataType)
342 {
343 case DCI_DT_INT:
344 bMatch = ((INT32)fvalue < (INT32)m_value);
345 break;
346 case DCI_DT_UINT:
347 bMatch = ((UINT32)fvalue < (UINT32)m_value);
348 break;
349 case DCI_DT_INT64:
350 bMatch = ((INT64)fvalue < (INT64)m_value);
351 break;
352 case DCI_DT_UINT64:
353 bMatch = ((UINT64)fvalue < (UINT64)m_value);
354 break;
355 case DCI_DT_FLOAT:
356 bMatch = ((double)fvalue < (double)m_value);
357 break;
358 }
359 break;
360 case OP_LE_EQ: // Less or equal
361 switch(iDataType)
362 {
363 case DCI_DT_INT:
364 bMatch = ((INT32)fvalue <= (INT32)m_value);
365 break;
366 case DCI_DT_UINT:
367 bMatch = ((UINT32)fvalue <= (UINT32)m_value);
368 break;
369 case DCI_DT_INT64:
370 bMatch = ((INT64)fvalue <= (INT64)m_value);
371 break;
372 case DCI_DT_UINT64:
373 bMatch = ((UINT64)fvalue <= (UINT64)m_value);
374 break;
375 case DCI_DT_FLOAT:
376 bMatch = ((double)fvalue <= (double)m_value);
377 break;
378 }
379 break;
380 case OP_EQ: // Equal
381 switch(iDataType)
382 {
383 case DCI_DT_INT:
384 bMatch = ((INT32)fvalue == (INT32)m_value);
385 break;
386 case DCI_DT_UINT:
387 bMatch = ((UINT32)fvalue == (UINT32)m_value);
388 break;
389 case DCI_DT_INT64:
390 bMatch = ((INT64)fvalue == (INT64)m_value);
391 break;
392 case DCI_DT_UINT64:
393 bMatch = ((UINT64)fvalue == (UINT64)m_value);
394 break;
395 case DCI_DT_FLOAT:
396 bMatch = ((double)fvalue == (double)m_value);
397 break;
398 case DCI_DT_STRING:
399 bMatch = !_tcscmp(fvalue.getString(), m_value.getString());
400 break;
401 }
402 break;
403 case OP_GT_EQ: // Greater or equal
404 switch(iDataType)
405 {
406 case DCI_DT_INT:
407 bMatch = ((INT32)fvalue >= (INT32)m_value);
408 break;
409 case DCI_DT_UINT:
410 bMatch = ((UINT32)fvalue >= (UINT32)m_value);
411 break;
412 case DCI_DT_INT64:
413 bMatch = ((INT64)fvalue >= (INT64)m_value);
414 break;
415 case DCI_DT_UINT64:
416 bMatch = ((UINT64)fvalue >= (UINT64)m_value);
417 break;
418 case DCI_DT_FLOAT:
419 bMatch = ((double)fvalue >= (double)m_value);
420 break;
421 }
422 break;
423 case OP_GT: // Greater
424 switch(iDataType)
425 {
426 case DCI_DT_INT:
427 bMatch = ((INT32)fvalue > (INT32)m_value);
428 break;
429 case DCI_DT_UINT:
430 bMatch = ((UINT32)fvalue > (UINT32)m_value);
431 break;
432 case DCI_DT_INT64:
433 bMatch = ((INT64)fvalue > (INT64)m_value);
434 break;
435 case DCI_DT_UINT64:
436 bMatch = ((UINT64)fvalue > (UINT64)m_value);
437 break;
438 case DCI_DT_FLOAT:
439 bMatch = ((double)fvalue > (double)m_value);
440 break;
441 }
442 break;
443 case OP_NE: // Not equal
444 switch(iDataType)
445 {
446 case DCI_DT_INT:
447 bMatch = ((INT32)fvalue != (INT32)m_value);
448 break;
449 case DCI_DT_UINT:
450 bMatch = ((UINT32)fvalue != (UINT32)m_value);
451 break;
452 case DCI_DT_INT64:
453 bMatch = ((INT64)fvalue != (INT64)m_value);
454 break;
455 case DCI_DT_UINT64:
456 bMatch = ((UINT64)fvalue != (UINT64)m_value);
457 break;
458 case DCI_DT_FLOAT:
459 bMatch = ((double)fvalue != (double)m_value);
460 break;
461 case DCI_DT_STRING:
462 bMatch = _tcscmp(fvalue.getString(), m_value.getString());
463 break;
464 }
465 break;
466 case OP_LIKE:
467 // This operation can be performed only on strings
468 if (m_dataType == DCI_DT_STRING)
469 bMatch = MatchString(m_value.getString(), fvalue.getString(), true);
470 break;
471 case OP_NOTLIKE:
472 // This operation can be performed only on strings
473 if (m_dataType == DCI_DT_STRING)
474 bMatch = !MatchString(m_value.getString(), fvalue.getString(), true);
475 break;
476 default:
477 break;
478 }
479 }
480
481 // Check for number of consecutive matches
482 if ((m_function == F_LAST) || (m_function == F_SCRIPT))
483 {
484 if (bMatch)
485 {
486 m_numMatches++;
487 if (m_numMatches < m_sampleCount)
488 bMatch = FALSE;
489 }
490 else
491 {
492 m_numMatches = 0;
493 }
494 }
495
496 ThresholdCheckResult result = (bMatch & !m_isReached) ? ACTIVATED : ((!bMatch & m_isReached) ? DEACTIVATED : (m_isReached ? ALREADY_ACTIVE : ALREADY_INACTIVE));
497 m_isReached = bMatch;
498 if (result == ACTIVATED || result == DEACTIVATED)
499 {
500 // Update threshold status in database
501 TCHAR szQuery[256];
502 _sntprintf(szQuery, 256, _T("UPDATE thresholds SET current_state=%d WHERE threshold_id=%d"), (int)m_isReached, (int)m_id);
503 QueueSQLRequest(szQuery);
504 }
505 return result;
506 }
507
508 /**
509 * Mark last activation event
510 */
511 void Threshold::markLastEvent(int severity)
512 {
513 m_lastEventTimestamp = time(NULL);
514 m_currentSeverity = (BYTE)severity;
515
516 // Update threshold in database
517 TCHAR query[256];
518 _sntprintf(query, 256,
519 _T("UPDATE thresholds SET current_severity=%d,last_event_timestamp=%d WHERE threshold_id=%d"),
520 (int)m_currentSeverity, (int)m_lastEventTimestamp, (int)m_id);
521 QueueSQLRequest(query);
522 }
523
524 /**
525 * Check for collection error thresholds
526 * Return same values as Check()
527 */
528 ThresholdCheckResult Threshold::checkError(UINT32 dwErrorCount)
529 {
530 if (m_function != F_ERROR)
531 return m_isReached ? ALREADY_ACTIVE : ALREADY_INACTIVE;
532
533 BOOL bMatch = ((UINT32)m_sampleCount <= dwErrorCount);
534 ThresholdCheckResult result = (bMatch & !m_isReached) ? ACTIVATED : ((!bMatch & m_isReached) ? DEACTIVATED : (m_isReached ? ALREADY_ACTIVE : ALREADY_INACTIVE));
535 m_isReached = bMatch;
536 if (result == ACTIVATED || result == DEACTIVATED)
537 {
538 // Update threshold status in database
539 TCHAR szQuery[256];
540 _sntprintf(szQuery, 256, _T("UPDATE thresholds SET current_state=%d WHERE threshold_id=%d"), m_isReached, m_id);
541 QueueSQLRequest(szQuery);
542 }
543 return result;
544 }
545
546 /**
547 * Fill DCI_THRESHOLD with object's data ready to send over the network
548 */
549 void Threshold::createMessage(NXCPMessage *msg, UINT32 baseId)
550 {
551 UINT32 varId = baseId;
552
553 msg->setField(varId++, m_id);
554 msg->setField(varId++, m_eventCode);
555 msg->setField(varId++, m_rearmEventCode);
556 msg->setField(varId++, (WORD)m_function);
557 msg->setField(varId++, (WORD)m_operation);
558 msg->setField(varId++, (UINT32)m_sampleCount);
559 msg->setField(varId++, CHECK_NULL_EX(m_scriptSource));
560 msg->setField(varId++, (UINT32)m_repeatInterval);
561 msg->setField(varId++, m_value.getString());
562 msg->setField(varId++, (WORD)m_isReached);
563 msg->setField(varId++, (WORD)m_currentSeverity);
564 msg->setField(varId++, (UINT32)m_lastEventTimestamp);
565 }
566
567 /**
568 * Update threshold object from NXCP message
569 */
570 void Threshold::updateFromMessage(NXCPMessage *msg, UINT32 baseId)
571 {
572 TCHAR buffer[MAX_DCI_STRING_VALUE];
573 UINT32 varId = baseId + 1; // Skip ID field
574
575 m_eventCode = msg->getFieldAsUInt32(varId++);
576 m_rearmEventCode = msg->getFieldAsUInt32(varId++);
577 m_function = (BYTE)msg->getFieldAsUInt16(varId++);
578 m_operation = (BYTE)msg->getFieldAsUInt16(varId++);
579 m_sampleCount = (int)msg->getFieldAsUInt32(varId++);
580 setScript(msg->getFieldAsString(varId++));
581 m_repeatInterval = (int)msg->getFieldAsUInt32(varId++);
582 m_value = msg->getFieldAsString(varId++, buffer, MAX_DCI_STRING_VALUE);
583 }
584
585 /**
586 * Calculate average value for parameter
587 */
588 #define CALC_AVG_VALUE(vtype) \
589 { \
590 vtype var; \
591 var = (vtype)lastValue; \
592 for(int i = 1; i < m_sampleCount; i++) \
593 { \
594 var += (vtype)(*ppPrevValues[i - 1]); \
595 } \
596 *pResult = var / (vtype)m_sampleCount; \
597 }
598
599 void Threshold::calculateAverageValue(ItemValue *pResult, ItemValue &lastValue, ItemValue **ppPrevValues)
600 {
601 switch(m_dataType)
602 {
603 case DCI_DT_INT:
604 CALC_AVG_VALUE(INT32);
605 break;
606 case DCI_DT_UINT:
607 CALC_AVG_VALUE(UINT32);
608 break;
609 case DCI_DT_INT64:
610 CALC_AVG_VALUE(INT64);
611 break;
612 case DCI_DT_UINT64:
613 CALC_AVG_VALUE(UINT64);
614 break;
615 case DCI_DT_FLOAT:
616 CALC_AVG_VALUE(double);
617 break;
618 case DCI_DT_STRING:
619 *pResult = _T(""); // Average value for string is meaningless
620 break;
621 default:
622 break;
623 }
624 }
625
626 /**
627 * Calculate sum value for values of given type
628 */
629 #define CALC_SUM_VALUE(vtype) \
630 { \
631 vtype var; \
632 var = (vtype)lastValue; \
633 for(int i = 1; i < m_sampleCount; i++) \
634 { \
635 var += (vtype)(*ppPrevValues[i - 1]); \
636 } \
637 *pResult = var; \
638 }
639
640 /**
641 * Calculate sum value for parameter
642 */
643 void Threshold::calculateSumValue(ItemValue *pResult, ItemValue &lastValue, ItemValue **ppPrevValues)
644 {
645 switch(m_dataType)
646 {
647 case DCI_DT_INT:
648 CALC_SUM_VALUE(INT32);
649 break;
650 case DCI_DT_UINT:
651 CALC_SUM_VALUE(UINT32);
652 break;
653 case DCI_DT_INT64:
654 CALC_SUM_VALUE(INT64);
655 break;
656 case DCI_DT_UINT64:
657 CALC_SUM_VALUE(UINT64);
658 break;
659 case DCI_DT_FLOAT:
660 CALC_SUM_VALUE(double);
661 break;
662 case DCI_DT_STRING:
663 *pResult = _T(""); // Sum value for string is meaningless
664 break;
665 default:
666 break;
667 }
668 }
669
670 /**
671 * Calculate mean absolute deviation for values of given type
672 */
673 #define CALC_MD_VALUE(vtype) \
674 { \
675 vtype mean, dev; \
676 mean = (vtype)lastValue; \
677 for(i = 1; i < m_sampleCount; i++) \
678 { \
679 mean += (vtype)(*ppPrevValues[i - 1]); \
680 } \
681 mean /= (vtype)m_sampleCount; \
682 dev = ABS((vtype)lastValue - mean); \
683 for(i = 1; i < m_sampleCount; i++) \
684 { \
685 dev += ABS((vtype)(*ppPrevValues[i - 1]) - mean); \
686 } \
687 *pResult = dev / (vtype)m_sampleCount; \
688 }
689
690 /**
691 * Calculate mean absolute deviation for parameter
692 */
693 void Threshold::calculateMDValue(ItemValue *pResult, ItemValue &lastValue, ItemValue **ppPrevValues)
694 {
695 int i;
696
697 switch(m_dataType)
698 {
699 case DCI_DT_INT:
700 #define ABS(x) ((x) < 0 ? -(x) : (x))
701 CALC_MD_VALUE(INT32);
702 break;
703 case DCI_DT_INT64:
704 CALC_MD_VALUE(INT64);
705 break;
706 case DCI_DT_FLOAT:
707 CALC_MD_VALUE(double);
708 break;
709 case DCI_DT_UINT:
710 #undef ABS
711 #define ABS(x) (x)
712 CALC_MD_VALUE(UINT32);
713 break;
714 case DCI_DT_UINT64:
715 CALC_MD_VALUE(UINT64);
716 break;
717 case DCI_DT_STRING:
718 *pResult = _T(""); // Mean deviation for string is meaningless
719 break;
720 default:
721 break;
722 }
723 }
724
725 #undef ABS
726
727 /**
728 * Calculate difference between last and previous value
729 */
730 void Threshold::calculateDiff(ItemValue *pResult, ItemValue &lastValue, ItemValue **ppPrevValues)
731 {
732 CalculateItemValueDiff(*pResult, m_dataType, lastValue, *ppPrevValues[0]);
733 }
734
735 /**
736 * Compare to another threshold
737 */
738 BOOL Threshold::compare(Threshold *pThr)
739 {
740 BOOL bMatch;
741
742 if (m_function == F_SCRIPT)
743 {
744 // Threat value field as string for script thresholds
745 bMatch = !_tcscmp(pThr->m_value.getString(), m_value.getString());
746 }
747 else
748 {
749 switch(m_dataType)
750 {
751 case DCI_DT_INT:
752 bMatch = ((INT32)pThr->m_value == (INT32)m_value);
753 break;
754 case DCI_DT_UINT:
755 bMatch = ((UINT32)pThr->m_value == (UINT32)m_value);
756 break;
757 case DCI_DT_INT64:
758 bMatch = ((INT64)pThr->m_value == (INT64)m_value);
759 break;
760 case DCI_DT_UINT64:
761 bMatch = ((UINT64)pThr->m_value == (UINT64)m_value);
762 break;
763 case DCI_DT_FLOAT:
764 bMatch = ((double)pThr->m_value == (double)m_value);
765 break;
766 case DCI_DT_STRING:
767 bMatch = !_tcscmp(pThr->m_value.getString(), m_value.getString());
768 break;
769 default:
770 bMatch = TRUE;
771 break;
772 }
773 }
774 return bMatch &&
775 (pThr->m_eventCode == m_eventCode) &&
776 (pThr->m_rearmEventCode == m_rearmEventCode) &&
777 (pThr->m_dataType == m_dataType) &&
778 (pThr->m_function == m_function) &&
779 (pThr->m_operation == m_operation) &&
780 (pThr->m_sampleCount == m_sampleCount) &&
781 !_tcscmp(CHECK_NULL_EX(pThr->m_scriptSource), CHECK_NULL_EX(m_scriptSource)) &&
782 (pThr->m_repeatInterval == m_repeatInterval);
783 }
784
785 /**
786 * Create management pack record
787 */
788 void Threshold::createNXMPRecord(String &str, int index)
789 {
790 TCHAR activationEvent[MAX_EVENT_NAME], deactivationEvent[MAX_EVENT_NAME];
791
792 EventNameFromCode(m_eventCode, activationEvent);
793 EventNameFromCode(m_rearmEventCode, deactivationEvent);
794 str.appendFormattedString(_T("\t\t\t\t\t\t<threshold id=\"%d\">\n")
795 _T("\t\t\t\t\t\t\t<function>%d</function>\n")
796 _T("\t\t\t\t\t\t\t<condition>%d</condition>\n")
797 _T("\t\t\t\t\t\t\t<value>%s</value>\n")
798 _T("\t\t\t\t\t\t\t<activationEvent>%s</activationEvent>\n")
799 _T("\t\t\t\t\t\t\t<deactivationEvent>%s</deactivationEvent>\n")
800 _T("\t\t\t\t\t\t\t<sampleCount>%d</sampleCount>\n")
801 _T("\t\t\t\t\t\t\t<repeatInterval>%d</repeatInterval>\n"),
802 index, m_function, m_operation,
803 (const TCHAR *)EscapeStringForXML2(m_value.getString()),
804 (const TCHAR *)EscapeStringForXML2(activationEvent),
805 (const TCHAR *)EscapeStringForXML2(deactivationEvent),
806 m_sampleCount, m_repeatInterval);
807 if (m_scriptSource != NULL)
808 {
809 str.append(_T("\t\t\t\t\t\t\t<script>"));
810 str.append(EscapeStringForXML2(m_scriptSource));
811 str.append(_T("</script>\n"));
812 }
813 str.append(_T("\t\t\t\t\t\t</threshold>\n"));
814 }
815
816 /**
817 * Make an association with DCI (used by management pack parser)
818 */
819 void Threshold::associate(DCItem *pItem)
820 {
821 m_itemId = pItem->getId();
822 m_targetId = pItem->getOwnerId();
823 m_dataType = pItem->getDataType();
824 }
825
826 /**
827 * Set new script. Script source must be dynamically allocated
828 * and will be deallocated by threshold object
829 */
830 void Threshold::setScript(TCHAR *script)
831 {
832 safe_free(m_scriptSource);
833 delete m_script;
834 if (script != NULL)
835 {
836 m_scriptSource = script;
837 StrStrip(m_scriptSource);
838 if (m_scriptSource[0] != 0)
839 {
840 TCHAR errorText[1024];
841 m_script = NXSLCompile(m_scriptSource, errorText, 1024, NULL);
842 if (m_script == NULL)
843 {
844 TCHAR buffer[1024], defaultName[32];
845 _sntprintf(defaultName, 32, _T("[%d]"), m_targetId);
846 _sntprintf(buffer, 1024, _T("DCI::%s::%d::%d::ThresholdScript"), GetObjectName(m_targetId, defaultName), m_itemId, m_id);
847 PostDciEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, m_itemId, "ssd", buffer, errorText, m_itemId);
848 nxlog_write(MSG_THRESHOLD_SCRIPT_COMPILATION_ERROR, NXLOG_WARNING, "sdds", GetObjectName(m_targetId, defaultName), m_itemId, m_id, errorText);
849 }
850 }
851 else
852 {
853 m_script = NULL;
854 }
855 }
856 else
857 {
858 m_scriptSource = NULL;
859 m_script = NULL;
860 }
861 }