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