DCI cache update from DB optimized
[public/netxms.git] / src / server / core / dcitem.cpp
CommitLineData
3ea35b38
VK
1/*
2** NetXMS - Network Management System
d05ea248 3** Copyright (C) 2003, 2004, 2005 Victor Kirhenshtein
3ea35b38
VK
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** $module: dcitem.cpp
20**
21**/
22
a551fe4d 23#include "nxcore.h"
3ea35b38
VK
24
25
26//
27// Default constructor for DCItem
28//
29
30DCItem::DCItem()
31{
32 m_dwId = 0;
333ece94 33 m_dwTemplateId = 0;
3ea35b38
VK
34 m_dwNumThresholds = 0;
35 m_ppThresholdList = NULL;
36 m_iBusy = 0;
0ae741b9 37 m_iDataType = DCI_DT_INT;
3ea35b38
VK
38 m_iPollingInterval = 3600;
39 m_iRetentionTime = 0;
333ece94 40 m_iDeltaCalculation = DCM_ORIGINAL_VALUE;
3ea35b38
VK
41 m_iSource = DS_INTERNAL;
42 m_iStatus = ITEM_STATUS_NOT_SUPPORTED;
43 m_szName[0] = 0;
b900a78b 44 m_szDescription[0] = 0;
079c3851 45 m_szInstance[0] = 0;
3ea35b38 46 m_tLastPoll = 0;
933aee5c 47 m_pszFormula = _tcsdup(_T(""));
3ea35b38 48 m_pNode = NULL;
07a45e04 49 m_hMutex = MutexCreate();
333ece94
VK
50 m_dwCacheSize = 0;
51 m_ppValueCache = NULL;
3ea35b38
VK
52}
53
54
933aee5c
VK
55//
56// Create DCItem from another DCItem
57//
58
59DCItem::DCItem(const DCItem *pSrc)
60{
61 DWORD i;
62
63 m_dwId = pSrc->m_dwId;
64 m_dwTemplateId = pSrc->m_dwTemplateId;
65 m_iBusy = 0;
66 m_iDataType = pSrc->m_iDataType;
67 m_iPollingInterval = pSrc->m_iPollingInterval;
68 m_iRetentionTime = pSrc->m_iRetentionTime;
69 m_iDeltaCalculation = pSrc->m_iDeltaCalculation;
70 m_iSource = pSrc->m_iSource;
71 m_iStatus = pSrc->m_iStatus;
72 _tcscpy(m_szName, pSrc->m_szName);
73 _tcscpy(m_szDescription, pSrc->m_szDescription);
079c3851 74 _tcscpy(m_szInstance, pSrc->m_szInstance);
933aee5c
VK
75 m_tLastPoll = 0;
76 m_pszFormula = _tcsdup(pSrc->m_pszFormula);
77 m_pNode = NULL;
78 m_hMutex = MutexCreate();
79 m_dwCacheSize = 0;
80 m_ppValueCache = NULL;
81
82 // Copy thresholds
83 m_dwNumThresholds = pSrc->m_dwNumThresholds;
84 m_ppThresholdList = (Threshold **)malloc(sizeof(Threshold *) * m_dwNumThresholds);
85 for(i = 0; i < m_dwNumThresholds; i++)
86 {
87 m_ppThresholdList[i] = new Threshold(pSrc->m_ppThresholdList[i]);
88 m_ppThresholdList[i]->CreateId();
89 }
282381ab
VK
90
91 UpdateCacheSize();
933aee5c
VK
92}
93
94
3ea35b38
VK
95//
96// Constructor for creating DCItem from database
97// Assumes that fields in SELECT query are in following order:
333ece94 98// item_id,name,source,datatype,polling_interval,retention_time,status,
079c3851 99// delta_calculation,transformation,template_id,description,instance
3ea35b38
VK
100//
101
b50f1100 102DCItem::DCItem(DB_RESULT hResult, int iRow, Template *pNode)
3ea35b38
VK
103{
104 m_dwId = DBGetFieldULong(hResult, iRow, 0);
b900a78b 105 strncpy(m_szName, DBGetField(hResult, iRow, 1), MAX_ITEM_NAME);
282381ab 106 DecodeSQLString(m_szName);
3ea35b38
VK
107 m_iSource = (BYTE)DBGetFieldLong(hResult, iRow, 2);
108 m_iDataType = (BYTE)DBGetFieldLong(hResult, iRow, 3);
109 m_iPollingInterval = DBGetFieldLong(hResult, iRow, 4);
110 m_iRetentionTime = DBGetFieldLong(hResult, iRow, 5);
111 m_iStatus = (BYTE)DBGetFieldLong(hResult, iRow, 6);
333ece94
VK
112 m_iDeltaCalculation = (BYTE)DBGetFieldLong(hResult, iRow, 7);
113 m_pszFormula = strdup(DBGetField(hResult, iRow, 8));
114 m_dwTemplateId = DBGetFieldULong(hResult, iRow, 9);
b900a78b 115 strncpy(m_szDescription, DBGetField(hResult, iRow, 10), MAX_DB_STRING);
48cd76d1 116 DecodeSQLString(m_szDescription);
079c3851
VK
117 strncpy(m_szInstance, DBGetField(hResult, iRow, 11), MAX_DB_STRING);
118 DecodeSQLString(m_szInstance);
3ea35b38
VK
119 m_iBusy = 0;
120 m_tLastPoll = 0;
121 m_dwNumThresholds = 0;
122 m_ppThresholdList = NULL;
123 m_pNode = pNode;
07a45e04 124 m_hMutex = MutexCreate();
333ece94
VK
125 m_dwCacheSize = 0;
126 m_ppValueCache = NULL;
3ea35b38
VK
127}
128
129
130//
131// Constructor for creating new DCItem from scratch
132//
133
134DCItem::DCItem(DWORD dwId, char *szName, int iSource, int iDataType,
0fd7132e
VK
135 int iPollingInterval, int iRetentionTime, Template *pNode,
136 char *pszDescription)
3ea35b38
VK
137{
138 m_dwId = dwId;
333ece94 139 m_dwTemplateId = 0;
3ea35b38 140 strncpy(m_szName, szName, MAX_ITEM_NAME);
0fd7132e
VK
141 if (pszDescription != NULL)
142 strncpy(m_szDescription, pszDescription, MAX_DB_STRING);
143 else
144 strcpy(m_szDescription, m_szName);
079c3851 145 m_szInstance[0] = 0;
3ea35b38
VK
146 m_iSource = iSource;
147 m_iDataType = iDataType;
148 m_iPollingInterval = iPollingInterval;
149 m_iRetentionTime = iRetentionTime;
333ece94 150 m_iDeltaCalculation = DCM_ORIGINAL_VALUE;
3ea35b38
VK
151 m_iStatus = ITEM_STATUS_ACTIVE;
152 m_iBusy = 0;
153 m_tLastPoll = 0;
333ece94 154 m_pszFormula = strdup("");
3ea35b38
VK
155 m_dwNumThresholds = 0;
156 m_ppThresholdList = NULL;
157 m_pNode = pNode;
07a45e04 158 m_hMutex = MutexCreate();
333ece94
VK
159 m_dwCacheSize = 0;
160 m_ppValueCache = NULL;
3ea35b38
VK
161}
162
163
164//
165// Destructor for DCItem
166//
167
168DCItem::~DCItem()
169{
170 DWORD i;
171
172 for(i = 0; i < m_dwNumThresholds; i++)
173 delete m_ppThresholdList[i];
174 safe_free(m_ppThresholdList);
333ece94
VK
175 safe_free(m_pszFormula);
176 for(i = 0; i < m_dwCacheSize; i++)
177 delete m_ppValueCache[i];
178 safe_free(m_ppValueCache);
07a45e04 179 MutexDestroy(m_hMutex);
3ea35b38
VK
180}
181
182
3ea35b38
VK
183//
184// Load data collection items thresholds from database
185//
186
187BOOL DCItem::LoadThresholdsFromDB(void)
188{
189 DWORD i;
190 char szQuery[256];
191 DB_RESULT hResult;
192 BOOL bResult = FALSE;
193
194 sprintf(szQuery, "SELECT threshold_id,fire_value,rearm_value,check_function,"
195 "check_operation,parameter_1,parameter_2,event_code FROM thresholds "
06e7be2f 196 "WHERE item_id=%ld ORDER BY sequence_number", m_dwId);
3ea35b38
VK
197 hResult = DBSelect(g_hCoreDB, szQuery);
198 if (hResult != NULL)
199 {
200 m_dwNumThresholds = DBGetNumRows(hResult);
201 if (m_dwNumThresholds > 0)
202 {
203 m_ppThresholdList = (Threshold **)malloc(sizeof(Threshold *) * m_dwNumThresholds);
204 for(i = 0; i < m_dwNumThresholds; i++)
205 m_ppThresholdList[i] = new Threshold(hResult, i, this);
206 }
207 DBFreeResult(hResult);
208 bResult = TRUE;
209 }
210
5c33a0aa
VK
211 UpdateCacheSize();
212
3ea35b38
VK
213 return bResult;
214}
215
216
217//
218// Save to database
219//
220
221BOOL DCItem::SaveToDB(void)
222{
282381ab 223 TCHAR *pszEscName, *pszEscFormula, *pszEscDescr, *pszEscInstance, szQuery[1024];
3ea35b38
VK
224 DB_RESULT hResult;
225 BOOL bNewObject = TRUE, bResult;
226
07a45e04
VK
227 Lock();
228
3ea35b38
VK
229 // Check for object's existence in database
230 sprintf(szQuery, "SELECT item_id FROM items WHERE item_id=%ld", m_dwId);
231 hResult = DBSelect(g_hCoreDB, szQuery);
232 if (hResult != 0)
233 {
234 if (DBGetNumRows(hResult) > 0)
235 bNewObject = FALSE;
236 DBFreeResult(hResult);
237 }
238
239 // Prepare and execute query
282381ab 240 pszEscName = EncodeSQLString(m_szName);
333ece94 241 pszEscFormula = EncodeSQLString(m_pszFormula);
b900a78b 242 pszEscDescr = EncodeSQLString(m_szDescription);
079c3851 243 pszEscInstance = EncodeSQLString(m_szInstance);
3ea35b38 244 if (bNewObject)
333ece94
VK
245 sprintf(szQuery, "INSERT INTO items (item_id,node_id,template_id,name,description,source,"
246 "datatype,polling_interval,retention_time,status,delta_calculation,"
22c38feb 247 "transformation,instance) VALUES (%ld,%ld,%ld,'%s','%s',%d,%d,%ld,%ld,%d,%d,'%s','%s')",
333ece94 248 m_dwId, (m_pNode == NULL) ? 0 : m_pNode->Id(), m_dwTemplateId,
282381ab 249 pszEscName, pszEscDescr, m_iSource, m_iDataType, m_iPollingInterval,
079c3851 250 m_iRetentionTime, m_iStatus, m_iDeltaCalculation, pszEscFormula, pszEscInstance);
3ea35b38 251 else
333ece94
VK
252 sprintf(szQuery, "UPDATE items SET node_id=%ld,template_id=%ld,name='%s',source=%d,"
253 "datatype=%d,polling_interval=%ld,retention_time=%ld,status=%d,"
079c3851
VK
254 "delta_calculation=%d,transformation='%s',description='%s',"
255 "instance='%s' WHERE item_id=%ld",
333ece94 256 (m_pNode == NULL) ? 0 : m_pNode->Id(), m_dwTemplateId,
282381ab 257 pszEscName, m_iSource, m_iDataType, m_iPollingInterval,
b900a78b 258 m_iRetentionTime, m_iStatus, m_iDeltaCalculation, pszEscFormula,
079c3851 259 pszEscDescr, pszEscInstance, m_dwId);
3ea35b38 260 bResult = DBQuery(g_hCoreDB, szQuery);
282381ab 261 free(pszEscName);
333ece94 262 free(pszEscFormula);
b900a78b 263 free(pszEscDescr);
079c3851 264 free(pszEscInstance);
3ea35b38
VK
265
266 // Save thresholds
267 if (bResult)
268 {
269 DWORD i;
270
271 for(i = 0; i < m_dwNumThresholds; i++)
06e7be2f
VK
272 m_ppThresholdList[i]->SaveToDB(i);
273 }
274
275 // Delete non-existing thresholds
276 sprintf(szQuery, "SELECT threshold_id FROM thresholds WHERE item_id=%ld", m_dwId);
277 hResult = DBSelect(g_hCoreDB, szQuery);
278 if (hResult != 0)
279 {
280 int i, iNumRows;
281 DWORD j, dwId;
282
283 iNumRows = DBGetNumRows(hResult);
284 for(i = 0; i < iNumRows; i++)
285 {
286 dwId = DBGetFieldULong(hResult, i, 0);
287 for(j = 0; j < m_dwNumThresholds; j++)
288 if (m_ppThresholdList[j]->Id() == dwId)
289 break;
290 if (j == m_dwNumThresholds)
291 {
292 sprintf(szQuery, "DELETE FROM thresholds WHERE threshold_id=%ld", dwId);
293 DBQuery(g_hCoreDB, szQuery);
294 }
295 }
296 DBFreeResult(hResult);
3ea35b38
VK
297 }
298
07a45e04 299 Unlock();
3ea35b38
VK
300 return bResult;
301}
302
303
304//
305// Check last value for threshold breaches
306//
307
333ece94 308void DCItem::CheckThresholds(ItemValue &value)
3ea35b38
VK
309{
310 DWORD i, iResult;
da072e3d 311 ItemValue checkValue;
3ea35b38
VK
312
313 for(i = 0; i < m_dwNumThresholds; i++)
314 {
da072e3d 315 iResult = m_ppThresholdList[i]->Check(value, m_ppValueCache, checkValue);
3ea35b38
VK
316 switch(iResult)
317 {
318 case THRESHOLD_REACHED:
079c3851 319 PostEvent(m_ppThresholdList[i]->EventCode(), m_pNode->Id(), "ssssis", m_szName,
647bfd64 320 m_szDescription, m_ppThresholdList[i]->StringValue(),
da072e3d 321 (const char *)checkValue, m_dwId, m_szInstance);
06e7be2f 322 i = m_dwNumThresholds; // Stop processing
3ea35b38
VK
323 break;
324 case THRESHOLD_REARMED:
e9ab1622
VK
325 PostEvent(EVENT_THRESHOLD_REARMED, m_pNode->Id(), "ssi", m_szName,
326 m_szDescription, m_dwId);
3ea35b38 327 break;
06e7be2f
VK
328 case NO_ACTION:
329 if (m_ppThresholdList[i]->IsReached())
330 i = m_dwNumThresholds; // Threshold condition still true, stop processing
331 break;
3ea35b38
VK
332 }
333 }
334}
7257eb7d
VK
335
336
337//
338// Create CSCP message with item data
339//
340
341void DCItem::CreateMessage(CSCPMessage *pMsg)
342{
07a45e04
VK
343 DCI_THRESHOLD dct;
344 DWORD i, dwId;
345
346 Lock();
7257eb7d
VK
347 pMsg->SetVariable(VID_DCI_ID, m_dwId);
348 pMsg->SetVariable(VID_NAME, m_szName);
b900a78b 349 pMsg->SetVariable(VID_DESCRIPTION, m_szDescription);
079c3851 350 pMsg->SetVariable(VID_INSTANCE, m_szInstance);
7257eb7d
VK
351 pMsg->SetVariable(VID_POLLING_INTERVAL, (DWORD)m_iPollingInterval);
352 pMsg->SetVariable(VID_RETENTION_TIME, (DWORD)m_iRetentionTime);
353 pMsg->SetVariable(VID_DCI_SOURCE_TYPE, (WORD)m_iSource);
354 pMsg->SetVariable(VID_DCI_DATA_TYPE, (WORD)m_iDataType);
355 pMsg->SetVariable(VID_DCI_STATUS, (WORD)m_iStatus);
333ece94
VK
356 pMsg->SetVariable(VID_DCI_DELTA_CALCULATION, (WORD)m_iDeltaCalculation);
357 pMsg->SetVariable(VID_DCI_FORMULA, m_pszFormula);
2b5a7f36 358 pMsg->SetVariable(VID_NUM_THRESHOLDS, m_dwNumThresholds);
07a45e04
VK
359 for(i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < m_dwNumThresholds; i++, dwId++)
360 {
361 m_ppThresholdList[i]->CreateMessage(&dct);
362 pMsg->SetVariable(dwId, (BYTE *)&dct, sizeof(DCI_THRESHOLD));
363 }
364 Unlock();
7257eb7d 365}
9ed4eaff
VK
366
367
368//
369// Delete item and collected data from database
370//
371
372void DCItem::DeleteFromDB(void)
373{
374 char szQuery[256];
375
376 sprintf(szQuery, "DELETE FROM items WHERE item_id=%d", m_dwId);
377 QueueSQLRequest(szQuery);
378 sprintf(szQuery, "DELETE FROM idata_%d WHERE item_id=%d", m_pNode->Id(), m_dwId);
379 QueueSQLRequest(szQuery);
380 sprintf(szQuery, "DELETE FROM thresholds WHERE item_id=%d", m_dwId);
381 QueueSQLRequest(szQuery);
382}
383
384
385//
386// Update item from CSCP message
387//
388
07a45e04
VK
389void DCItem::UpdateFromMessage(CSCPMessage *pMsg, DWORD *pdwNumMaps,
390 DWORD **ppdwMapIndex, DWORD **ppdwMapId)
9ed4eaff 391{
07a45e04
VK
392 DWORD i, j, dwNum, dwId;
393 DCI_THRESHOLD *pNewThresholds;
06e7be2f 394 Threshold **ppNewList;
07a45e04
VK
395
396 Lock();
397
9ed4eaff 398 pMsg->GetVariableStr(VID_NAME, m_szName, MAX_ITEM_NAME);
b900a78b 399 pMsg->GetVariableStr(VID_DESCRIPTION, m_szDescription, MAX_DB_STRING);
079c3851 400 pMsg->GetVariableStr(VID_INSTANCE, m_szInstance, MAX_DB_STRING);
9ed4eaff
VK
401 m_iSource = (BYTE)pMsg->GetVariableShort(VID_DCI_SOURCE_TYPE);
402 m_iDataType = (BYTE)pMsg->GetVariableShort(VID_DCI_DATA_TYPE);
403 m_iPollingInterval = pMsg->GetVariableLong(VID_POLLING_INTERVAL);
404 m_iRetentionTime = pMsg->GetVariableLong(VID_RETENTION_TIME);
405 m_iStatus = (BYTE)pMsg->GetVariableShort(VID_DCI_STATUS);
333ece94
VK
406 m_iDeltaCalculation = (BYTE)pMsg->GetVariableShort(VID_DCI_DELTA_CALCULATION);
407 safe_free(m_pszFormula);
408 m_pszFormula = pMsg->GetVariableStr(VID_DCI_FORMULA);
07a45e04
VK
409
410 // Update thresholds
411 dwNum = pMsg->GetVariableLong(VID_NUM_THRESHOLDS);
412 pNewThresholds = (DCI_THRESHOLD *)malloc(sizeof(DCI_THRESHOLD) * dwNum);
413 *ppdwMapIndex = (DWORD *)malloc(dwNum * sizeof(DWORD));
414 *ppdwMapId = (DWORD *)malloc(dwNum * sizeof(DWORD));
415 *pdwNumMaps = 0;
416
417 // Read all thresholds from message
418 for(i = 0, dwId = VID_DCI_THRESHOLD_BASE; i < dwNum; i++, dwId++)
419 {
420 pMsg->GetVariableBinary(dwId, (BYTE *)&pNewThresholds[i], sizeof(DCI_THRESHOLD));
421 pNewThresholds[i].dwId = ntohl(pNewThresholds[i].dwId);
422 }
423
06e7be2f
VK
424 // Check if some thresholds was deleted, and reposition others if needed
425 ppNewList = (Threshold **)malloc(sizeof(Threshold *) * dwNum);
07a45e04
VK
426 for(i = 0; i < m_dwNumThresholds; i++)
427 {
428 for(j = 0; j < dwNum; j++)
429 if (m_ppThresholdList[i]->Id() == pNewThresholds[j].dwId)
430 break;
431 if (j == dwNum)
432 {
433 // No threshold with that id in new list, delete it
434 delete m_ppThresholdList[i];
435 m_dwNumThresholds--;
436 memmove(&m_ppThresholdList[i], &m_ppThresholdList[i + 1], sizeof(Threshold *) * (m_dwNumThresholds - i));
437 i--;
438 }
06e7be2f
VK
439 else
440 {
441 // Move existing thresholds to appropriate positions in new list
442 ppNewList[j] = m_ppThresholdList[i];
443 }
07a45e04 444 }
06e7be2f
VK
445 safe_free(m_ppThresholdList);
446 m_ppThresholdList = ppNewList;
447 m_dwNumThresholds = dwNum;
07a45e04
VK
448
449 // Add or update thresholds
450 for(i = 0; i < dwNum; i++)
451 {
452 if (pNewThresholds[i].dwId == 0) // New threshold?
453 {
06e7be2f
VK
454 m_ppThresholdList[i] = new Threshold(this);
455 m_ppThresholdList[i]->CreateId();
07a45e04
VK
456
457 // Add index -> id mapping
458 (*ppdwMapIndex)[*pdwNumMaps] = i;
06e7be2f
VK
459 (*ppdwMapId)[*pdwNumMaps] = m_ppThresholdList[i]->Id();
460 (*pdwNumMaps)++;
07a45e04 461 }
06e7be2f 462 m_ppThresholdList[i]->UpdateFromMessage(&pNewThresholds[i]);
07a45e04
VK
463 }
464
465 safe_free(pNewThresholds);
282381ab 466 UpdateCacheSize();
07a45e04 467 Unlock();
9ed4eaff 468}
333ece94
VK
469
470
471//
472// Process new value
473//
474
475void DCItem::NewValue(DWORD dwTimeStamp, const char *pszOriginalValue)
476{
477 char szQuery[MAX_LINE_SIZE + 128];
a60d4f27 478 ItemValue rawValue, *pValue;
333ece94 479
282381ab
VK
480 Lock();
481
333ece94
VK
482 // Normally m_pNode shouldn't be NULL for polled items,
483 // but who knows...
484 if (m_pNode == NULL)
282381ab
VK
485 {
486 Unlock();
333ece94 487 return;
282381ab 488 }
333ece94
VK
489
490 // Create new ItemValue object and transform it as needed
491 pValue = new ItemValue(pszOriginalValue);
a60d4f27
VK
492 if (m_tLastPoll == 0)
493 m_prevRawValue = *pValue; // Delta should be zero for first poll
494 rawValue = *pValue;
e94ce275 495 Transform(*pValue, (long)(dwTimeStamp - m_tLastPoll));
a60d4f27 496 m_prevRawValue = rawValue;
333ece94
VK
497
498 // Save transformed value to database
499 sprintf(szQuery, "INSERT INTO idata_%ld (item_id,idata_timestamp,idata_value)"
500 " VALUES (%ld,%ld,'%s')", m_pNode->Id(), m_dwId, dwTimeStamp,
501 pValue->String());
502 QueueSQLRequest(szQuery);
503
504 // Check thresholds and add value to cache
505 CheckThresholds(*pValue);
a60d4f27 506
5c33a0aa
VK
507 if (m_dwCacheSize > 0)
508 {
5c33a0aa
VK
509 delete m_ppValueCache[m_dwCacheSize - 1];
510 memmove(&m_ppValueCache[1], m_ppValueCache, sizeof(ItemValue *) * (m_dwCacheSize - 1));
511 m_ppValueCache[0] = pValue;
5c33a0aa
VK
512 }
513 else
514 {
515 delete pValue;
516 }
282381ab
VK
517
518 Unlock();
333ece94
VK
519}
520
521
522//
523// Transform received value
524//
525
e94ce275 526void DCItem::Transform(ItemValue &value, long nElapsedTime)
333ece94
VK
527{
528 switch(m_iDeltaCalculation)
529 {
530 case DCM_SIMPLE:
531 switch(m_iDataType)
532 {
0ae741b9 533 case DCI_DT_INT:
333ece94
VK
534 value = (long)value - (long)m_prevRawValue;
535 break;
0ae741b9
VK
536 case DCI_DT_UINT:
537 value = (DWORD)value - (DWORD)m_prevRawValue;
538 break;
333ece94
VK
539 case DCI_DT_INT64:
540 value = (INT64)value - (INT64)m_prevRawValue;
541 break;
0ae741b9
VK
542 case DCI_DT_UINT64:
543 value = (QWORD)value - (QWORD)m_prevRawValue;
544 break;
333ece94
VK
545 case DCI_DT_FLOAT:
546 value = (double)value - (double)m_prevRawValue;
547 break;
282381ab
VK
548 case DCI_DT_STRING:
549 value = (long)((strcmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
550 break;
333ece94
VK
551 default:
552 // Delta calculation is not supported for other types
553 break;
554 }
555 break;
257defd7
VK
556 case DCM_AVERAGE_PER_MINUTE:
557 nElapsedTime /= 60; // Convert to minutes
333ece94 558 case DCM_AVERAGE_PER_SECOND:
e94ce275
VK
559 // Check elapsed time to prevent divide-by-zero exception
560 if (nElapsedTime == 0)
561 nElapsedTime++;
562
563 switch(m_iDataType)
564 {
565 case DCI_DT_INT:
566 value = ((long)value - (long)m_prevRawValue) / nElapsedTime;
567 break;
568 case DCI_DT_UINT:
569 value = ((DWORD)value - (DWORD)m_prevRawValue) / (DWORD)nElapsedTime;
570 break;
571 case DCI_DT_INT64:
572 value = ((INT64)value - (INT64)m_prevRawValue) / (INT64)nElapsedTime;
573 break;
574 case DCI_DT_UINT64:
575 value = ((QWORD)value - (QWORD)m_prevRawValue) / (QWORD)nElapsedTime;
576 break;
577 case DCI_DT_FLOAT:
578 value = ((double)value - (double)m_prevRawValue) / (double)nElapsedTime;
579 break;
580 case DCI_DT_STRING:
257defd7 581 // I don't see any meaning in "average delta per second (minute)" for string
e94ce275 582 // values, so result will be 0 if there are no difference between
257defd7 583 // current and previous values, and 1 otherwise
e94ce275
VK
584 value = (long)((strcmp((const TCHAR *)value, (const TCHAR *)m_prevRawValue) == 0) ? 0 : 1);
585 break;
586 default:
587 // Delta calculation is not supported for other types
588 break;
589 }
333ece94 590 break;
333ece94
VK
591 default: // Default is no transformation
592 break;
593 }
594}
090e01b8
VK
595
596
597//
598// Set new ID
599//
600
601void DCItem::SetId(DWORD dwNewId)
602{
603 DWORD i;
604
605 Lock();
606 m_dwId = dwNewId;
607 for(i = 0; i < m_dwNumThresholds; i++)
608 m_ppThresholdList[i]->BindToItem(m_dwId);
609 Unlock();
610}
5c33a0aa
VK
611
612
613//
614// Update required cache size depending on thresholds
615//
616
617void DCItem::UpdateCacheSize(void)
618{
619 DWORD i, dwRequiredSize;
620
20aaa307
VK
621 // Minimum cache size is 1 (so GetLastValue can work)
622 for(i = 0, dwRequiredSize = 1; i < m_dwNumThresholds; i++)
5c33a0aa
VK
623 if (dwRequiredSize < m_ppThresholdList[i]->RequiredCacheSize())
624 dwRequiredSize = m_ppThresholdList[i]->RequiredCacheSize();
625 if (dwRequiredSize < m_dwCacheSize)
626 {
627 m_dwCacheSize = dwRequiredSize;
628 if (m_dwCacheSize > 0)
629 {
630 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * m_dwCacheSize);
631 }
632 else
633 {
634 safe_free(m_ppValueCache);
635 m_ppValueCache = NULL;
636 }
637 }
638 else if (dwRequiredSize > m_dwCacheSize)
639 {
640 // Expand cache
641 m_ppValueCache = (ItemValue **)realloc(m_ppValueCache, sizeof(ItemValue *) * dwRequiredSize);
642 for(i = m_dwCacheSize; i < dwRequiredSize; i++)
643 m_ppValueCache[i] = NULL;
644
645 // Load missing values from database
646 if (m_pNode != NULL)
647 {
648 DB_ASYNC_RESULT hResult;
649 char szBuffer[MAX_DB_STRING];
650 BOOL bHasData;
651
bac1a556
VK
652 switch(g_dwDBSyntax)
653 {
654 case DB_SYNTAX_MSSQL:
655 sprintf(szBuffer, "SELECT TOP %ld idata_value FROM idata_%ld "
656 "WHERE item_id=%ld ORDER BY idata_timestamp DESC",
657 m_dwCacheSize, m_pNode->Id(), m_dwId);
658 break;
659 case DB_SYNTAX_MYSQL:
660 case DB_SYNTAX_PGSQL:
661 sprintf(szBuffer, "SELECT idata_value FROM idata_%ld "
662 "WHERE item_id=%ld ORDER BY idata_timestamp DESC LIMIT %ld",
663 m_pNode->Id(), m_dwId, m_dwCacheSize);
664 break;
665 default:
666 sprintf(szBuffer, "SELECT idata_value FROM idata_%ld "
667 "WHERE item_id=%ld ORDER BY idata_timestamp DESC",
668 m_pNode->Id(), m_dwId);
669 break;
670 }
5c33a0aa
VK
671 hResult = DBAsyncSelect(g_hCoreDB, szBuffer);
672 if (hResult != NULL)
673 {
674 // Skip already cached values
675 for(i = 0, bHasData = TRUE; (i < m_dwCacheSize) && bHasData; i++)
676 bHasData = DBFetch(hResult);
677
678 // Create new cache entries
679 for(; (i < dwRequiredSize) && bHasData; i++)
680 {
681 bHasData = DBFetch(hResult);
682 if (bHasData)
683 {
684 m_ppValueCache[i] = new ItemValue(DBGetFieldAsync(hResult, 0, szBuffer, MAX_DB_STRING));
685 }
da072e3d
VK
686 else
687 {
688 m_ppValueCache[i] = new ItemValue(_T("")); // Empty value
689 }
5c33a0aa
VK
690 }
691
da072e3d
VK
692 // Fill up cache with empty values if we don't have enough values in database
693 for(; i < dwRequiredSize; i++)
694 m_ppValueCache[i] = new ItemValue(_T(""));
695
5c33a0aa
VK
696 DBFreeAsyncResult(hResult);
697 }
da072e3d
VK
698 else
699 {
700 // Error reading data from database, fill cache with empty values
701 for(i = 0; i < dwRequiredSize; i++)
702 m_ppValueCache[i] = new ItemValue(_T(""));
703 }
5c33a0aa
VK
704 }
705 m_dwCacheSize = dwRequiredSize;
706 }
da072e3d 707}
20aaa307
VK
708
709
710//
711// Put last value into CSCP message
712//
713
714void DCItem::GetLastValue(CSCPMessage *pMsg, DWORD dwId)
715{
716 pMsg->SetVariable(dwId++, m_dwId);
717 pMsg->SetVariable(dwId++, m_szName);
718 pMsg->SetVariable(dwId++, m_szDescription);
719 if (m_dwCacheSize > 0)
720 {
721 pMsg->SetVariable(dwId++, (WORD)m_iDataType);
722 pMsg->SetVariable(dwId++, (TCHAR *)m_ppValueCache[0]->String());
723 pMsg->SetVariable(dwId++, (DWORD)m_tLastPoll);
724 }
725 else
726 {
727 pMsg->SetVariable(dwId++, (WORD)DCI_DT_NULL);
728 pMsg->SetVariable(dwId++, _T(""));
729 pMsg->SetVariable(dwId++, (DWORD)0);
730 }
731}