summarist.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. #include <string.h>
  4. #include "fileutil.h"
  5. #include "strutil.h"
  6. #include "summarist.h"
  7. #include "debug.h"
  8. #define _SUMMARIST_TIMESTAMP_FILE_NAME "sum.ts"
  9. #define _SECS_PER_DAY 86400
  10. static const unsigned long g_nFactors[] = {1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30, 36, 40, 45, 48, 50, 60, 72, 75, 80, 90, 100, 120, 144, 150, 180, 200, 225, 240, 300, 360, 400, 450, 600, 720, 900, 1200, 1800, 3600};
  11. /////////////////////////////////////////////////////////////////////////////
  12. //
  13. CSummarist::CSummarist(LPCSUMMARIST_PARAMS psup, CLogfile &rlf) : m_rlf(rlf), m_nProcFrequ(1), m_tzo(_SUM_INVALID_TIMESTAMP_VALUE), m_tsBase(_SUM_INVALID_TIMESTAMP_VALUE)
  14. {
  15. memset(&m_sup, 0, sizeof(m_sup));
  16. if(psup)
  17. memcpy(&m_sup, psup, sizeof(m_sup));
  18. if(m_sup.pszBaseDir)
  19. strncpy(m_szAppDir, m_sup.pszBaseDir, sizeof(m_szAppDir) - 1);
  20. else
  21. ::GetAppDirectory(m_szAppDir, sizeof(m_szAppDir));
  22. }
  23. CSummarist::~CSummarist(void)
  24. {
  25. Release();
  26. }
  27. /////////////////////////////////////////////////////////////////////////////
  28. //
  29. void CSummarist::Release(void)
  30. {
  31. m_vTimeWnds.clear();
  32. m_vTimeFrameStart.clear();
  33. m_ilTagList.clear();
  34. }
  35. /////////////////////////////////////////////////////////////////////////////
  36. //
  37. bool CSummarist::Initialze(const unsigned long *pTimeWnds, size_t nCntTimeWnds, bool bDropTables)
  38. {
  39. if(!InitTimeWnd(pTimeWnds, nCntTimeWnds))
  40. return false;
  41. if(!InitTables(bDropTables))
  42. return false;
  43. return true;
  44. }
  45. /////////////////////////////////////////////////////////////////////////////
  46. //
  47. std::string CSummarist::CreateSumTableName(unsigned long tWnd) const
  48. {
  49. return formatString("%s_sum_%lu", m_sup.szLogsTable, tWnd);
  50. }
  51. std::string CSummarist::CreateSumTableName(int nIndex) const
  52. {
  53. unsigned long tWnd;
  54. if((tWnd = GetTimeWindow(nIndex)))
  55. return CreateSumTableName(tWnd);
  56. return "";
  57. }
  58. /////////////////////////////////////////////////////////////////////////////
  59. //
  60. time_t CSummarist::GetTimezoneOffset(CMySqlDB &rdb)
  61. {
  62. time_t tzo = _SUM_INVALID_TIMESTAMP_VALUE;
  63. CMySqlResult res = rdb.Query("SELECT TIME_TO_SEC(TIMEDIFF(NOW(), UTC_TIMESTAMP()))");
  64. if(res.error())
  65. {
  66. m_rlf.Error("CSummarist::GetTimezoneOffset: DB Error: %s\n", rdb.LastError().c_str());
  67. }
  68. else
  69. {
  70. CMySqlVar valTZ;
  71. my_ulonglong nRowCount = res.RowCount();
  72. unsigned int nFldCount = res.FieldCount();
  73. const MYSQL_FIELD *pFields = res.FetchFields();
  74. MYSQL_ROW pRow = res.FetchRow();
  75. if(nRowCount == 1 && nFldCount == 1 && pFields && pRow && valTZ.FromField(pFields[0], pRow[0]))
  76. tzo = (time_t)(uint64_t)valTZ;
  77. }
  78. return tzo;
  79. }
  80. /////////////////////////////////////////////////////////////////////////////
  81. //
  82. bool CSummarist::CreateTagList(CMySqlDB &rdb)
  83. {
  84. bool bRet;
  85. std::string sSql;
  86. m_ilTagList.clear();
  87. sSql = formatString("SET SESSION group_concat_max_len = %u", _SUM_GROUP_CONCAT_MAX_LEN);
  88. if(rdb.Query(sSql.c_str()).error())
  89. {
  90. m_rlf.Error("CSummarist::CreateTagList: DB Error: %s\n", rdb.LastError().c_str());
  91. return false;
  92. }
  93. sSql = formatString("SELECT GROUP_CONCAT(`tagid` ORDER BY `tagid` SEPARATOR ',') FROM `%s`", m_sup.szITagsView);
  94. CMySqlResult res = rdb.Query(sSql.c_str());
  95. if(!(bRet = !res.error()))
  96. {
  97. m_rlf.Error("CSummarist::CreateTagList: DB Error: %s\n", rdb.LastError().c_str());
  98. }
  99. else
  100. {
  101. my_ulonglong nRowCount = res.RowCount();
  102. unsigned int nFldCount = res.FieldCount();
  103. const MYSQL_FIELD *pFields = res.FetchFields();
  104. MYSQL_ROW pRow = res.FetchRow();
  105. if((nRowCount == 1) && (nFldCount == 1) && pFields && (pFields->type != MYSQL_TYPE_BLOB) && pRow)
  106. m_ilTagList = pRow[0];
  107. else
  108. bRet = false;
  109. }
  110. return bRet;
  111. }
  112. /////////////////////////////////////////////////////////////////////////////
  113. //
  114. unsigned long CSummarist::GetTimeWindow(size_t nIndex) const
  115. {
  116. if(nIndex < TimeWndCount())
  117. return m_vTimeWnds[nIndex];
  118. return 0;
  119. }
  120. /////////////////////////////////////////////////////////////////////////////
  121. //
  122. bool CSummarist::Connect(CMySqlDB &rdb)
  123. {
  124. if(!rdb.Connect("localhost", m_sup.szDBUser, m_sup.szDBPass, NULL))
  125. return false;
  126. else if(rdb.SelectDB(m_sup.szDBName))
  127. {
  128. rdb.Close();
  129. return false;
  130. }
  131. m_tzo = GetTimezoneOffset(rdb);
  132. rdb.Query("SET @OLD_FOREIGN_KEY_CHECKS = @@FOREIGN_KEY_CHECKS");
  133. rdb.Query("SET FOREIGN_KEY_CHECKS = 0");
  134. rdb.Query("SET @OLD_TIME_ZONE = @@TIME_ZONE");
  135. return !rdb.Query("SET TIME_ZONE = '+00:00'").error();
  136. }
  137. void CSummarist::Close(CMySqlDB &rdb)
  138. {
  139. rdb.Query("SET FOREIGN_KEY_CHECKS = @OLD_FOREIGN_KEY_CHECKS");
  140. rdb.Query("SET TIME_ZONE = @OLD_TIME_ZONE");
  141. rdb.Close();
  142. }
  143. /////////////////////////////////////////////////////////////////////////////
  144. //
  145. bool CSummarist::LockTables(CMySqlDB &rdb, const char *pszTableName)
  146. {
  147. std::string sSql = formatString("LOCK TABLES `%s` READ, `%s` WRITE", m_sup.szLogsTable, pszTableName);
  148. return !rdb.Query(sSql.c_str()).error();
  149. }
  150. bool CSummarist::UnlockTables(CMySqlDB &rdb)
  151. {
  152. return !rdb.Query("UNLOCK TABLES").error();
  153. }
  154. /////////////////////////////////////////////////////////////////////////////
  155. //
  156. bool CSummarist::DropTables(CMySqlDB &rdb)
  157. {
  158. bool bError;
  159. std::string tbls;
  160. std::string sSql = formatString("SHOW TABLES LIKE '%s_sum_%%'", m_sup.szLogsTable);
  161. CMySqlResult res = rdb.Query(sSql.c_str());
  162. if((bError = res.error()))
  163. {
  164. m_rlf.Error("CSummarist::DropTables: DB Error: %s\n", rdb.LastError().c_str());
  165. }
  166. else
  167. {
  168. MYSQL_ROW pRow;
  169. if((pRow = res.FetchRow()))
  170. tbls = formatString("`%s`", *pRow);
  171. while((pRow = res.FetchRow()))
  172. tbls += formatString(", `%s`", *pRow);
  173. sSql = formatString("DROP TABLE IF EXISTS %s", tbls.c_str());
  174. m_rlf.Warning("Dropping summary tables: %s!\n", tbls.c_str());
  175. CMySqlResult res = rdb.Query(sSql.c_str());
  176. if((bError = res.error()))
  177. m_rlf.Error("CSummarist::DropTables: DB Error: %s\n", rdb.LastError().c_str());
  178. }
  179. return !bError;
  180. }
  181. /////////////////////////////////////////////////////////////////////////////
  182. //
  183. const char* CSummarist::Timestamp2String(time_t t, char *pszBuffer, size_t nCbBuffer)
  184. {
  185. static char szTimestamp[128];
  186. if(!pszBuffer)
  187. {
  188. pszBuffer = szTimestamp;
  189. nCbBuffer = sizeof(szTimestamp);
  190. }
  191. const struct tm * ptm = gmtime(&t);
  192. strftime(pszBuffer, nCbBuffer, "%F %T", ptm);
  193. return pszBuffer;
  194. }
  195. /////////////////////////////////////////////////////////////////////////////
  196. //
  197. time_t CSummarist::ReadTimestampBase(void)
  198. {
  199. char szPath[PATH_MAX];
  200. sprintf(szPath, "%s/%s", m_szAppDir, _SUMMARIST_TIMESTAMP_FILE_NAME);
  201. time_t ts = _SUM_INVALID_TIMESTAMP_VALUE;
  202. FILE *pf = fopen(szPath, "rb");
  203. if(pf)
  204. {
  205. if(fread(&ts, sizeof(ts), 1, pf) != 1)
  206. ts = _SUM_INVALID_TIMESTAMP_VALUE;
  207. fclose(pf);
  208. }
  209. return ts;
  210. }
  211. /////////////////////////////////////////////////////////////////////////////
  212. //
  213. time_t CSummarist::GetBaseTimestamp(time_t tsMinLog)
  214. {
  215. if(m_tsBase == _SUM_INVALID_TIMESTAMP_VALUE)
  216. {
  217. m_tsBase = ReadTimestampBase();
  218. if(m_tsBase == _SUM_INVALID_TIMESTAMP_VALUE)
  219. {
  220. m_tsBase = tsMinLog - (tsMinLog % _SECS_PER_DAY);
  221. WriteTimestampBase(m_tsBase);
  222. }
  223. }
  224. return m_tsBase;
  225. }
  226. /////////////////////////////////////////////////////////////////////////////
  227. //
  228. void CSummarist::WriteTimestampBase(time_t ts)
  229. {
  230. char szPath[PATH_MAX];
  231. sprintf(szPath, "%s/%s", m_szAppDir, _SUMMARIST_TIMESTAMP_FILE_NAME);
  232. FILE *pf = fopen(szPath, "wb");
  233. if(pf)
  234. {
  235. fwrite(&ts, sizeof(ts), 1, pf);
  236. fclose(pf);
  237. }
  238. }
  239. /////////////////////////////////////////////////////////////////////////////
  240. //
  241. bool CSummarist::GetNextTimeFrame(CMySqlDB &rdb, size_t nIndex, time_t tsLast, time_t tsBase, time_t &rtsStart, time_t &rtsEnd) const
  242. {
  243. time_t tsNext, tWnd = (time_t)GetTimeWindow(nIndex);
  244. if(tWnd)
  245. {
  246. if(tsLast == _SUM_INVALID_TIMESTAMP_VALUE)
  247. tsLast = tsBase;
  248. if( GetNextTimestampFromLogs(rdb, tsLast, tsNext) && (tsNext != _SUM_INVALID_TIMESTAMP_VALUE))
  249. rtsStart = tsNext - ((tsNext - tsBase) % tWnd);
  250. else
  251. rtsStart = tsLast - ((tsLast - tsBase) % tWnd) + tWnd;
  252. rtsEnd = rtsStart + tWnd;
  253. return true;
  254. }
  255. return false;
  256. }
  257. /////////////////////////////////////////////////////////////////////////////
  258. //
  259. bool CSummarist::SetNextTimeFrameStart(size_t nIndex, time_t tsStart)
  260. {
  261. if(nIndex < (size_t)m_vTimeFrameStart.size())
  262. {
  263. m_vTimeFrameStart[nIndex] = tsStart;
  264. return true;
  265. }
  266. return false;
  267. }
  268. /////////////////////////////////////////////////////////////////////////////
  269. //
  270. time_t CSummarist::GetNextDueTimeFrameStart(void) const
  271. {
  272. time_t ts = _SUM_INVALID_TIMESTAMP_VALUE;
  273. for(auto i = m_vTimeFrameStart.begin(); i != m_vTimeFrameStart.end(); ++i)
  274. {
  275. time_t t = *i;
  276. if((t != _SUM_INVALID_TIMESTAMP_VALUE) && ((ts == _SUM_INVALID_TIMESTAMP_VALUE) || (ts > t)))
  277. ts = t;
  278. }
  279. return ts;
  280. }
  281. /////////////////////////////////////////////////////////////////////////////
  282. //
  283. bool CSummarist::InitTables(bool bDropTables)
  284. {
  285. CMySqlDB db;
  286. m_rlf.Info("Connecting to database server @'localhost'.\n");
  287. if(!Connect(db))
  288. {
  289. m_rlf.Error("CSummarist::InitTables: DB Error: %s\n", db.LastError().c_str());
  290. return false;
  291. }
  292. m_rlf.Info("Success!\n");
  293. if(bDropTables)
  294. {
  295. return DropTables(db);
  296. }
  297. for(auto i = m_vTimeWnds.begin(); i != m_vTimeWnds.end(); ++i)
  298. {
  299. unsigned long tWnd = *i;
  300. std::string sName = CreateSumTableName(tWnd);
  301. std::string sSql = formatString("CREATE TABLE IF NOT EXISTS `%s` LIKE `%s`", sName.c_str(), m_sup.szLogsTable);
  302. m_rlf.Info("Creating summary table '%s' (if not exists).\n", sName.c_str());
  303. CMySqlResult res = db.Query(sSql.c_str());
  304. bool bError = res.error();
  305. if(bError)
  306. {
  307. m_rlf.Error("CSummarist::InitTables: DB Error: %s\n", db.LastError().c_str());
  308. return false;
  309. }
  310. else
  311. m_rlf.Info("Success!\n");
  312. }
  313. std::string sSql = formatString("CREATE OR REPLACE ALGORITHM = MERGE VIEW `%s` AS SELECT `tagid` FROM `%s` WHERE `logType` IN ('IC', 'IU', 'ICR', 'IUR') ORDER BY `tagid`", m_sup.szITagsView, m_sup.szTagsTable);
  314. m_rlf.Info("Creating view '%s' (if not exists).\n", m_sup.szITagsView);
  315. CMySqlResult res = db.Query(sSql.c_str());
  316. bool bError = res.error();
  317. if(bError)
  318. {
  319. m_rlf.Error("CSummarist::InitTables: DB Error: %s\n", db.LastError().c_str());
  320. return false;
  321. }
  322. else
  323. m_rlf.Info("Success!\n");
  324. if(!CreateTagList(db))
  325. return false;
  326. return true;
  327. }
  328. /////////////////////////////////////////////////////////////////////////////
  329. //
  330. bool CSummarist::InitTimeWnd(const unsigned long *pTimeWnds, size_t nCntTimeWnds)
  331. {
  332. m_nProcFrequ = 1;
  333. if(m_sup.nLogIntv % 1000)
  334. {
  335. m_rlf.Error("Incompatible datalogger log interval: %lu\n", m_sup.nLogIntv);
  336. return false;
  337. }
  338. for(size_t i = 0; i < nCntTimeWnds; i++)
  339. {
  340. m_vTimeWnds.push_back(pTimeWnds[i]);
  341. m_vTimeFrameStart.push_back(_SUM_INVALID_TIMESTAMP_VALUE);
  342. }
  343. size_t nCountWnds = m_vTimeWnds.size();
  344. if(!nCountWnds)
  345. {
  346. m_rlf.Error("No time window specified!\n");
  347. return false;
  348. }
  349. else if(nCountWnds > 1)
  350. {
  351. // sort time window sizes ascending
  352. std::sort(m_vTimeWnds.begin(), m_vTimeWnds.end());
  353. }
  354. unsigned long tWndMin = m_vTimeWnds[0];
  355. for(auto i = m_vTimeWnds.begin(); i != m_vTimeWnds.end(); ++i)
  356. {
  357. unsigned long tWnd = *i;
  358. unsigned long nMod = tWnd % _SUM_TIME_WND_BASE;
  359. if(tWnd < (2 * m_sup.nLogIntv / 1000)) // a time window must at least contain two logs to not being useless!
  360. {
  361. m_rlf.Error("Time window length (%lu) must be at least a double of the log interval (%lu)!\n", tWnd, m_sup.nLogIntv / 1000);
  362. return false;
  363. }
  364. else if(nMod && (_SUM_TIME_WND_BASE % nMod)) // time window length must be either an integer factor or a multiple of an integer factor of 3600
  365. {
  366. m_rlf.Error("Time window length must be either an integer factor or a multiple of an integer factor of 3600: %lu\n", tWnd);
  367. return false;
  368. }
  369. else if(tWndMin && (tWnd % tWndMin))
  370. {
  371. tWndMin = 0;
  372. }
  373. }
  374. if(!tWndMin)
  375. {
  376. // find the greatest common divisor of all time windows. this will be the processing frequency starting at a full hour boundary.
  377. for(int j = (int)(_countof(g_nFactors) - 1); j >= 0; --j)
  378. {
  379. bool bDiv = true;
  380. unsigned long nFac = g_nFactors[j];
  381. for(auto i = m_vTimeWnds.begin(); i != m_vTimeWnds.end(); ++i)
  382. {
  383. unsigned long tWnd = *i;
  384. if((nFac > tWnd) || (tWnd % nFac))
  385. {
  386. bDiv = false;
  387. break;
  388. }
  389. }
  390. if(bDiv)
  391. {
  392. m_nProcFrequ = nFac;
  393. m_rlf.Info("Processing frequency: %lu\n", m_nProcFrequ);
  394. break;
  395. }
  396. }
  397. }
  398. else
  399. {
  400. m_nProcFrequ = tWndMin;
  401. m_rlf.Info("Processing frequency: %lu\n", m_nProcFrequ);
  402. }
  403. return true;
  404. }
  405. /////////////////////////////////////////////////////////////////////////////
  406. //
  407. bool CSummarist::GetLastSummarizeTimestamp(CMySqlDB &rdb, size_t nIndex, time_t &rtsLast) const
  408. {
  409. unsigned long tWnd = GetTimeWindow(nIndex);
  410. if(tWnd)
  411. {
  412. std::string sTbl = CreateSumTableName(tWnd);
  413. return GetLastSummarizeTimestamp(rdb, sTbl.c_str(), rtsLast);
  414. }
  415. rtsLast = _SUM_INVALID_TIMESTAMP_VALUE;
  416. return false;
  417. }
  418. /////////////////////////////////////////////////////////////////////////////
  419. //
  420. bool CSummarist::GetLastSummarizeTimestamp(CMySqlDB &rdb, const char *pszTableName, time_t &rtsLast) const
  421. {
  422. rtsLast = _SUM_INVALID_TIMESTAMP_VALUE;
  423. std::string sSql = formatString("SELECT UNIX_TIMESTAMP(MAX(`tslog`)) FROM `%s`", pszTableName);
  424. CMySqlResult res = rdb.Query(sSql.c_str());
  425. if(res.error())
  426. {
  427. m_rlf.Error("CSummarist::GetLastSummarizeTimestamp: DB Error: %s\n", rdb.LastError().c_str());
  428. }
  429. else
  430. {
  431. do
  432. {
  433. CMySqlVar val;
  434. my_ulonglong nRowCount = res.RowCount();
  435. unsigned int nFldCount = res.FieldCount();
  436. const MYSQL_FIELD *pFields = res.FetchFields();
  437. if(nRowCount != 1 || nFldCount != 1 || !pFields)
  438. {
  439. break;
  440. }
  441. MYSQL_ROW pRow = res.FetchRow();
  442. if( !pRow ||
  443. !val.FromField(pFields[0], pRow[0]))
  444. {
  445. break;
  446. }
  447. rtsLast = (time_t)(uint64_t)val;
  448. }
  449. while(false);
  450. return true;
  451. }
  452. return false;
  453. }
  454. /////////////////////////////////////////////////////////////////////////////
  455. //
  456. bool CSummarist::GetMinMaxTimestampFromLogs(CMySqlDB &rdb, time_t &rMin, time_t &rMax) const
  457. {
  458. bool bRet = true;
  459. int nCntTs = 0;
  460. std::string sSql[2];
  461. CMySqlVar val[2];
  462. rMin = _SUM_INVALID_TIMESTAMP_VALUE;
  463. rMax = _SUM_INVALID_TIMESTAMP_VALUE;
  464. sSql[0] = formatString("SELECT UNIX_TIMESTAMP(`tslog`) FROM `%s` WHERE (`tagid` IN (%s)) AND (`value` IS NOT NULL) ORDER BY `tslog` ASC LIMIT 1;", m_sup.szLogsTable, m_ilTagList.c_str());
  465. sSql[1] = formatString("SELECT UNIX_TIMESTAMP(`tslog`) FROM `%s` WHERE (`tagid` IN (%s)) AND (`value` IS NOT NULL) ORDER BY `tslog` DESC LIMIT 1;", m_sup.szLogsTable, m_ilTagList.c_str());
  466. for(int i = 0; i < 2; ++i)
  467. {
  468. CMySqlResult res = rdb.Query(sSql[i].c_str());
  469. if(res.error())
  470. {
  471. m_rlf.Error("CSummarist::GetMinMaxTimestampFromLogs: DB Error: %s\n", rdb.LastError().c_str());
  472. bRet = false;
  473. break;
  474. }
  475. else
  476. {
  477. my_ulonglong nRowCount = res.RowCount();
  478. unsigned int nFldCount = res.FieldCount();
  479. const MYSQL_FIELD *pFields = res.FetchFields();
  480. MYSQL_ROW pRow = res.FetchRow();
  481. if(nRowCount != 1 || nFldCount != 1 || !pFields || !pRow || !val[i].FromField(pFields[0], pRow[0]))
  482. break;
  483. ++nCntTs;
  484. }
  485. }
  486. if(bRet && nCntTs == 2)
  487. {
  488. rMin = (time_t)(uint64_t)val[0];
  489. rMax = (time_t)(uint64_t)val[1];
  490. }
  491. return bRet;
  492. }
  493. /////////////////////////////////////////////////////////////////////////////
  494. //
  495. bool CSummarist::GetNextTimestampFromLogs(CMySqlDB &rdb, time_t tsLast, time_t &rNext) const
  496. {
  497. std::string sSql = formatString("SELECT UNIX_TIMESTAMP(`tslog`) FROM `%s` WHERE (`tslog` > '%s') AND (`value` IS NOT NULL) AND (`tagid` IN (%s)) ORDER BY `tslog` ASC LIMIT 1;", m_sup.szLogsTable, Timestamp2String(tsLast), m_ilTagList.c_str());
  498. CMySqlResult res = rdb.Query(sSql.c_str());
  499. if(res.error())
  500. {
  501. m_rlf.Error("CSummarist::GetNextTimestampFromLogs: DB Error: %s\n", rdb.LastError().c_str());
  502. return false;
  503. }
  504. else
  505. {
  506. CMySqlVar valNext;
  507. my_ulonglong nRowCount = res.RowCount();
  508. unsigned int nFldCount = res.FieldCount();
  509. const MYSQL_FIELD *pFields = res.FetchFields();
  510. MYSQL_ROW pRow = res.FetchRow();
  511. if(nRowCount != 1 || nFldCount != 1 || !pFields)
  512. {
  513. rNext = _SUM_INVALID_TIMESTAMP_VALUE;
  514. }
  515. else if(!pRow || !valNext.FromField(pFields[0], pRow[0]))
  516. {
  517. rNext = _SUM_INVALID_TIMESTAMP_VALUE;
  518. }
  519. else
  520. {
  521. rNext = (time_t)(uint64_t)valNext;
  522. }
  523. return true;
  524. }
  525. }
  526. /////////////////////////////////////////////////////////////////////////////
  527. //
  528. bool CSummarist::Summarize(CMySqlDB &rdb, size_t nIndex, time_t tsFrom, time_t tsTo)
  529. {
  530. if(nIndex >= TimeWndCount())
  531. {
  532. m_rlf.Error("CSummarist::Summarize: invalid index: %z\n", nIndex);
  533. return false;
  534. }
  535. #define _SUM_FMT_STRING "REPLACE INTO `%s` " \
  536. "SELECT `tagid`, MAX(`tslog`) `tslog`, AVG(`value`) `value`, MIN(`valueMin`) `valueMin`, MAX(`valueMax`) `valueMax` FROM `%s` " \
  537. "WHERE (`tslog` BETWEEN '%s' AND '%s') AND (`value` IS NOT NULL) AND (`%s`.`tagid` IN (%s)) " \
  538. "GROUP BY `%s`.`tagid`"
  539. char szFrom[64], szTo[64];
  540. std::string sTName = CreateSumTableName((int)nIndex);
  541. Timestamp2String(tsFrom, szFrom, sizeof(szFrom));
  542. Timestamp2String(tsTo - 1, szTo, sizeof(szTo));
  543. std::string sSql = formatString(_SUM_FMT_STRING, sTName.c_str(), m_sup.szLogsTable, szFrom, szTo, m_sup.szLogsTable, m_ilTagList.c_str(), m_sup.szLogsTable);
  544. std::string sTbl = formatString("%s, %s", sTName.c_str(), m_sup.szLogsTable);
  545. TRACE("%-16s- Summarize: %s <= logs < %s\n", sTName.c_str(), szFrom, Timestamp2String(tsTo));
  546. if(!LockTables(rdb, sTName.c_str()))
  547. {
  548. m_rlf.Error("CSummarist::Summarize: DB Error: %s\n", rdb.LastError().c_str());
  549. return false;
  550. }
  551. CMySqlResult res = rdb.Query(sSql.c_str());
  552. if(res.error())
  553. {
  554. m_rlf.Error("CSummarist::Summarize: DB Error: %s\n", rdb.LastError().c_str());
  555. return false;
  556. }
  557. UnlockTables(rdb);
  558. return true;
  559. }
  560. /////////////////////////////////////////////////////////////////////////////
  561. //
  562. bool CSummarist::ProcessOutdated(CMySqlDB &rdb, size_t nIndex, time_t tsUpTo)
  563. {
  564. if(nIndex >= TimeWndCount())
  565. {
  566. m_rlf.Error("CSummarist::ProcessOutdated: invalid index: %z\n", nIndex);
  567. return false;
  568. }
  569. char szUpTo[64];
  570. std::string sTName = CreateSumTableName((int)nIndex);
  571. Timestamp2String(tsUpTo, szUpTo, sizeof(szUpTo));
  572. std::string sSql = formatString("DELETE FROM `%s` where `tslog` < '%s'", sTName.c_str(), szUpTo);
  573. CMySqlResult res = rdb.Query(sSql.c_str());
  574. if(res.error())
  575. {
  576. m_rlf.Error("CSummarist::ProcessOutdated: DB Error: %s\n", rdb.LastError().c_str());
  577. return false;
  578. }
  579. TRACE("%-16s- ProcessOutdated: sumlogs < %s\n", sTName.c_str(), szUpTo);
  580. return true;
  581. }