· 6 years ago · Jul 16, 2019, 11:42 AM
1///////////////////////////////////////////////////////////
2// Archivers.cpp
3// Implementation of the Class archiverDBFirebird
4// Created on: 30-мар-2016 9:31:46
5// Original author: usertkv
6///////////////////////////////////////////////////////////
7#define IBPP_WINDOWS
8#include <time.h>
9
10//#include <sqlite/sqlite3.h>
11#include <boost/filesystem.hpp>
12#include <boost/date_time/posix_time/posix_time.hpp>
13#include <boost/date_time/gregorian/gregorian.hpp>
14#include <C:\Users\888\Desktop\AS\AS\ibpp\core\all_in_one.cpp>
15#include <C:\Users\888\Desktop\AS\AS\ibpp\core\ibpp.h>
16#include <Nodes.h>
17#include <Archivers.h>
18#include <NodesCollection.h>
19#include <BasicNode.h>
20#include <BasicPacket.h>
21#include <NodeState.h>
22#include <ConfigNode.h>
23#include <stringutils.h>
24#include <dateutils.h>
25#include <widgetChart.h>
26#include <BasicProperty.h>
27#include <Reports.h>
28
29#include <sysDataArchiver.h>
30
31#include "ArchiverFirebird.h"
32
33using namespace Nodes;
34using namespace boost;
35
36archiverDBFirebird::archiverDBFirebird()
37{
38 m_strName = "archiverDBFirebird";
39 m_ntType = NodeType(NodeType::enNodeType::ntArchiver, NodeType::enNodeSubtype::nstArchiverFirebird);
40
41 // m_vecChildNodeTypes.push_back(NodeType(NodeType::enNodeType::ntSource));
42 // m_vecChildNodeTypes.push_back(NodeType(NodeType::enNodeType::ntSignal));
43
44 // m_cmdQueue.m_pArchiver = this;
45 m_cmdQueue.m_pSystem = this;
46
47 m_db = NULL;
48 m_fDBCurrentSize = 0;
49 m_fDBMaxSize = 0;
50
51 // m_vecNodes.m_bInheritNodes = true;
52 // m_wVersion = 2;
53}
54
55archiverDBFirebird::~archiverDBFirebird()
56{
57
58}
59
60void archiverDBFirebird::SerializeParams(MsgPackMap& mmap, SerializationParams& params)
61{
62 BasicArchiver::SerializeParams(mmap, params);
63}
64
65void archiverDBFirebird::DeserializeParams(MsgPackMap& mmap, SerializationParams& params)
66{
67 BasicArchiver::DeserializeParams(mmap, params);
68}
69
70Result archiverDBFirebird::SaveSignal()
71{
72
73 return Result(this, Result::STATUS_SUCCESS, 0);
74}
75
76Result archiverDBFirebird::LoadSignal()
77{
78
79 return Result(this, Result::STATUS_SUCCESS, 0);
80}
81
82//static int callback_GetSignalHistory(void *NotUsed, int argc, char **argv, char **azColName)
83//{
84// commandFirebirdGetValues* cmd = (commandFirebirdGetValues*)NotUsed;
85//
86// int i;
87// SignalHistoryResponse sd;
88//
89// //(SignalDateTime, SignalId, SignalValue)
90//
91// for (i = 0; i<argc; i++)
92// {
93// if (i == 0)
94// {
95// sd.m_uiDateTime = atoll(argv[i]);
96// }
97// // else if(i==1)
98// // {
99// // sd.m_dwMilliseconds = atoi(argv[i]);
100// // }
101// else if (i == 1)
102// {
103// sd.m_dwSignalId = atoi(argv[i]);
104// }
105// else if (i == 2)
106// {
107// string str;
108//
109// if (argv[i] == NULL)
110// {
111// sd.m_dValue = numeric_limits<double>::quiet_NaN();
112// }
113// else
114// {
115// sd.m_dValue = atof(argv[i]);
116// }
117// }
118// }
119//
120// //Protecting vector
121// // cmd->m_Mutex.lock();
122// cmd->m_vecResponseData.push_back(sd);
123// // cmd->m_Mutex.unlock();
124//
125// // printf("\n");
126// return 0;
127//}
128//
129//static int callback_TrimByDate(void *NotUsed, int argc, char **argv, char **azColName)
130//{
131// SignalHistoryResponse* sd = (SignalHistoryResponse*)NotUsed;
132//
133// int i;
134//
135// for (i = 0; i<argc; i++)
136// {
137// if (i == 0)
138// {
139// sd->m_uiDateTime = atoi(argv[i]);
140// }
141// // else if (i == 1)
142// // {
143// // sd->m_dwMilliseconds = atoi(argv[i]);
144// // }
145// else if (i == 1)
146// {
147// sd->m_dwSignalId = atoi(argv[i]);
148// }
149// else if (i == 2)
150// {
151// string str;
152//
153// if (argv[i] == NULL)
154// {
155// str = "(null)";
156// }
157//
158// if (str == "(null)")
159// {
160// sd->m_dValue = numeric_limits<double>::quiet_NaN();
161// }
162// else
163// {
164// sd->m_dValue = atof(argv[i]);
165// }
166// }
167// }
168//
169// return 0;
170//}
171//
172//static int callback(void *NotUsed, int argc, char **argv, char **azColName)
173//{
174// return 0;
175//}
176//
177//static int callback_count(void *iValue, int argc, char **argv, char **azColName)
178//{
179// int* val = (int*)iValue;
180//
181// *val = atoi(argv[0]);
182//
183// return 0;
184//}
185//
186//static int callback_DBItem(void *NotUsed, int argc, char **argv, char **azColName)
187//{
188// vector<DBItem>* vecItems = (vector<DBItem>*)NotUsed;
189//
190// int i;
191//
192// DBItem item;
193//
194// for (i = 0; i < argc; i++)
195// {
196// if (i == 0)
197// {
198// item.m_dwSignalId = atoi(argv[i]);
199// }
200// else if (i == 1)
201// {
202// item.m_strSignalName = string(argv[i]);
203// }
204// else if (i == 2)
205// {
206// item.m_strSignalShortName = string(argv[i]);
207// }
208// else if (i == 3)
209// {
210// item.m_strSignalDescription = string(argv[i]);
211// }
212// }
213//
214// vecItems->push_back(item);
215//
216// return 0;
217//}
218
219void archiverDBFirebird::PushCommand(BasicCommand* pCommand)
220{
221 BasicCommand* pNewCommand = NULL;
222
223 if (pCommand->GetType() == COMMAND_ARCHIVER_GETVALUES)
224 {
225 pNewCommand = new commandFirebirdGetValues((commandArchiverGetValues*)pCommand);
226 }
227
228 if (pNewCommand != NULL)
229 {
230 m_cmdQueue.PushCommand(pNewCommand);
231 }
232
233 delete pCommand;
234}
235
236void archiverDBFirebird::GetSignalHistory(IBPP::Database db, commandFirebirdGetValues* command)
237{
238 //char *zErrMsg = 0;
239 // char sql[512];
240 const char* data = "Callback function called";
241 //int rc;
242 int pf_ret;
243
244 string strQuery;
245 string strSql;
246
247 //printf("%s: GetSignalHistory\n", GetName().c_str());
248 for (int t = 0; t<command->m_vecRequestData.size(); t++)
249 {
250 //Print all data to process
251 SignalHistoryRequest sh = command->m_vecRequestData[t];
252 //printf("%s: archiver: %u, signal: %u, start: %llu, stop: %llu\n", GetName().c_str(), /*sh.m_dwArchiver*/GetId(), sh.m_dwSignal, sh.m_iBegin, sh.m_iEnd);
253
254 /* Create SQL statement */
255 if ((command->m_vecRequestData[t].m_iBegin == 0) && (command->m_vecRequestData[t].m_iEnd == 0))
256 {
257 if (t == 0)
258 {
259 strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u)", command->m_vecRequestData[t].m_dwSignal);
260 }
261 else
262 {
263 strSql = util::string_format(" or (SignalId=%u)", command->m_vecRequestData[t].m_dwSignal);
264 }
265 }
266 else if ((command->m_vecRequestData[t].m_iBegin == 0) && (command->m_vecRequestData[t].m_iEnd != 0))
267 {
268 if (t == 0)
269 {
270 strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec<=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iEnd);
271 }
272 else
273 {
274 strSql = util::string_format(" or (SignalId=%u and SignalDataSec<=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iEnd);
275 }
276 }
277 else if ((command->m_vecRequestData[t].m_iBegin != 0) && (command->m_vecRequestData[t].m_iEnd == 0))
278 {
279 if (t == 0)
280 {
281 strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec>=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iBegin);
282 }
283 else
284 {
285 strSql = util::string_format(" or (SignalId=%u and SignalDataSec>=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iBegin);
286 }
287 }
288 else if ((command->m_vecRequestData[t].m_iBegin != 0) && (command->m_vecRequestData[t].m_iEnd != 0))
289 {
290 if (t == 0)
291 {
292 strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec>=%llu and SignalDataSec<=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iBegin, command->m_vecRequestData[t].m_iEnd);
293 }
294 else
295 {
296 strSql = util::string_format(" or (SignalId=%u and SignalDataSec>=%llu and SignalDataSec<=%llu)", command->m_vecRequestData[t].m_dwSignal, command->m_vecRequestData[t].m_iBegin, command->m_vecRequestData[t].m_iEnd);
297 }
298 }
299 else
300 {
301 //printf("Unhandled request!\n");
302 }
303
304 strQuery += strSql;
305 }
306
307
308 /* Execute SQL statement */
309 // rc = sqlite3_exec(db, sql, callback_GetSignalHistory_BP, (void*)command, &zErrMsg);
310 if (db->Connected() == 0)
311 {
312 db->Connect();
313 }
314 IBPP::Transaction tr = IBPP::TransactionFactory(db);
315 bool done_good = true;
316 try
317 {
318 tr->Start();
319 IBPP::Statement st = StatementFactory(db, tr);
320 st->Prepare(strQuery);
321 st->Execute();
322 /*std::vector<IBPP::Row> rows;
323
324 while (st->Fetch(r))
325 rows.push_back(r);*/
326 }
327 catch (IBPP::Exception& e)
328 {
329 /*tr->Rollback();*/
330 done_good = false;
331 cout << e.ErrorMessage() << endl;
332 }
333
334 if (done_good)
335 {
336 cout << "Operation done successfully\n";
337
338 // command->m_State = NodeState(NodeState::STATE_RUNNING, RESULTCODE_NOERROR);
339 }
340}
341
342Result archiverDBFirebird::DBClear(IBPP::Database db)
343{
344 /* bpCommandGeneric* cmd = (bpCommandGeneric*)command;
345
346 char *zErrMsg = 0;
347 char sql[512];
348 const char* data = "Callback function called";
349 int rc;
350 int pf_ret;
351
352 string strQuery;
353
354 printf("%s: ClearDB (node %u) \n", GetName().c_str(), cmd->m_dwParameter);
355
356 pf_ret = sprintf_s(sql, sizeof(sql), "delete from SIGNALDATA");
357
358 strQuery += sql;
359
360 rc = sqlite3_exec(m_db, strQuery.c_str(), callback_ClearDB_BP, (void*)command, &zErrMsg);
361 if (rc != SQLITE_OK)
362 {
363 printf("SQL error: %s\n", zErrMsg);
364 sqlite3_free(zErrMsg);
365
366 command->m_State.m_State = SystemState::STATE_ERROR;
367 }
368 else
369 {
370 printf("Operation ClearDB done successfully\n");
371
372 command->m_State.m_State = SystemState::STATE_RUNNING;
373 }
374 */
375 return Result(this, Result::STATUS_SUCCESS, 0);
376}
377
378Result archiverDBFirebird::DBInit(IBPP::Database db)
379{
380 string strSql;
381
382 int rc;
383 //char *zErrMsg = 0;
384
385
386 /*
387 sql = "create table if not exists SIGNALDATA(" \
388 "SignalDataSec BIGINT NOT NULL," \
389 "SignalDataMsec INT NOT NULL," \
390 "SignalId INT NOT NULL," \
391 "SignalValue FLOAT NULL);" \
392 "create index if not exists IDX_SIGNALDATASEC on SIGNALDATA (SignalDataSec);";
393 */
394
395 strSql = "CREATE TABLE IF NOT EXISTS SignalData(" \
396 "SignalDateTime BIGINT NOT NULL," \
397 "SignalId INT NOT NULL," \
398 "SignalValue FLOAT NULL);" \
399 "CREATE INDEX IF NOT EXISTS IDX_SignalDateTime ON SignalData (SignalDateTime);";
400 if (db->Connected() == 0)
401 {
402 db->Connect();
403 }
404 IBPP::Transaction tr = IBPP::TransactionFactory(db);
405 bool done_good = true;
406 try
407 {
408 tr->Start();
409 IBPP::Statement st = StatementFactory(db, tr);
410 st->Prepare(strSql);
411 st->Execute();
412 }
413 catch (IBPP::Exception& e)
414 {
415 /*tr->Rollback();*/
416 done_good = false;
417 cout << e.ErrorMessage() << endl;
418 }
419 tr->Commit();
420 if (done_good)
421 cout << "Table SignalData opened/created successfully\n";
422
423 return Result(Result::STATUS_SUCCESS);
424}
425
426Result archiverDBFirebird::DBUpdateInfo()
427{
428 char *zErrMsg = 0;
429 // char sql[512];
430 const char* data = "Callback function called";
431 int rc;
432 int pf_ret;
433
434 string strQuery;
435 string strSql;
436
437 if (m_db != NULL)
438 {
439 strSql = "CREATE TABLE IF NOT EXISTS SignalInfo(" \
440 "SignalId INT NOT NULL," \
441 "SignalName STRING NOT NULL," \
442 "SignalShortName STRING NOT NULL," \
443 "SignalDescription STRING NOT NULL);";
444
445 m_mtxDBOperations.lock();
446 if (m_db->Connected() == 0)
447 {
448 m_db->Connect();
449 }
450 IBPP::Transaction tr = IBPP::TransactionFactory(m_db);
451 bool done_good = true;
452 try
453 {
454 tr->Start();
455 IBPP::Statement st = StatementFactory(m_db, tr);
456 st->Prepare(strSql);
457 st->Execute();
458 }
459 catch (IBPP::Exception& e)
460 {
461 /*tr->Rollback();*/
462 done_good = false;
463 cout << e.ErrorMessage() << endl;
464 }
465 tr->Commit();
466
467 m_mtxDBOperations.unlock();
468
469 if (done_good)
470 cout << "Table SignalInfo opened/created successfully\n";
471 NodeArray naConnectors = GetConnectors();
472
473 for (auto nlConnector : naConnectors)
474 {
475 archiverSourceConnector* pConnector = (archiverSourceConnector*)nlConnector.GetNode();
476 BasicSource* pSource = pConnector->GetSource();
477
478 if (pSource != NULL)
479 {
480 strSql = util::string_format("SELECT COUNT(*) FROM SignalInfo WHERE SignalId = %u", pSource->GetId());
481
482 int ct;
483
484 m_mtxDBOperations.lock();
485 if (m_db->Connected() == 0)
486 {
487 m_db->Connect();
488 }
489 IBPP::Transaction tr = IBPP::TransactionFactory(m_db);
490 bool done_good = true;
491 try
492 {
493 tr->Start();
494 IBPP::Statement st = StatementFactory(m_db, tr);
495 st->Prepare(strSql);
496 st->Execute();
497 IBPP::Row r;
498 while (st->Fetch(r))
499 {
500 r->Get(1, ct);
501 }
502 }
503 catch (IBPP::Exception& e)
504 {
505 /*tr->Rollback();*/
506 done_good = false;
507 cout << e.ErrorMessage() << endl;
508 }
509 tr->Commit();
510 //rc = sqlite3_exec(m_db, strSql.c_str(), callback_count, &ct, &zErrMsg);
511
512 m_mtxDBOperations.unlock();
513
514 if (done_good)
515 {
516 if (ct > 0)
517 //Record exists, update it
518 {
519 //printf("Record %s exists, update it\n", pSource->GetName().c_str());
520 strSql = util::string_format("UPDATE SignalInfo SET SignalName='%s', SignalShortName='%s', SignalDescription='%s' WHERE SignalId=%u",
521 pSource->GetName().c_str(),
522 pSource->GetShortName().c_str(),
523 pSource->GetDescription().c_str(),
524 pSource->GetId()
525 );
526 }
527 else
528 //Record not exists, create it
529 {
530 //printf("Record %s not exists, create it\n", pSource->GetName().c_str());
531 strSql = util::string_format("INSERT INTO SignalInfo (SignalId, SignalName, SignalShortName, SignalDescription) VALUES (%u, '%s', '%s', '%s')",
532 pSource->GetId(),
533 pSource->GetName().c_str(),
534 pSource->GetShortName().c_str(),
535 pSource->GetDescription().c_str()
536 );
537 }
538
539 m_mtxDBOperations.lock();
540 if (m_db->Connected() == 0)
541 {
542 m_db->Connect();
543 }
544 IBPP::Transaction tr = IBPP::TransactionFactory(m_db);
545 bool done_good = true;
546 try
547 {
548 tr->Start();
549 IBPP::Statement st = StatementFactory(m_db, tr);
550 st->Prepare(strSql);
551 st->Execute();
552 }
553 catch (IBPP::Exception& e)
554 {
555 /*tr->Rollback();*/
556 done_good = false;
557 cout << e.ErrorMessage() << endl;
558 }
559 tr->Commit();
560 //rc = sqlite3_exec(m_db, strSql.c_str(), callback, 0, &zErrMsg);
561
562 m_mtxDBOperations.unlock();
563
564 if (done_good)
565 {
566
567 }
568 }
569 }
570 }
571 }
572
573 return Result(Result::STATUS_SUCCESS);
574}
575
576vector<DBItem> archiverDBFirebird::GetDBInfo()
577{
578 int rc;
579 char *zErrMsg = 0;
580 string strSql;
581
582 vector<DBItem> vecRet;
583
584 if (m_db != NULL)
585 {
586 strSql = "SELECT * FROM SignalInfo";
587
588 int ct;
589
590 m_mtxDBOperations.lock();
591 if (m_db->Connected() == 0)
592 {
593 m_db->Connect();
594 }
595 IBPP::Transaction tr = IBPP::TransactionFactory(m_db);
596 bool done_good = true;
597 try
598 {
599 tr->Start();
600 IBPP::Statement st = StatementFactory(m_db, tr);
601 st->Prepare(strSql);
602 st->Execute();
603 IBPP::Row r;
604 while (st->Fetch(r))
605 {
606 int count_colums = r->Columns();
607 DBItem item;
608 for (int i = 0; i < count_colums; i++)
609 {
610 if (i == 0)
611 {
612 int temp;
613 r->Get(i + 1, temp);
614 item.m_dwSignalId = temp;
615 }
616 else if (i == 1)
617 {
618 string temp;
619 r->Get(i + 1, temp);
620 item.m_strSignalName = temp;
621 }
622 else if (i == 2)
623 {
624 string temp;
625 r->Get(i + 1, temp);
626 item.m_strSignalShortName = temp;
627 }
628 else if (i == 3)
629 {
630 string temp;
631 r->Get(i + 1, temp);
632 item.m_strSignalDescription = temp;
633 }
634 }
635 vecRet.push_back(item);
636 }
637 }
638 catch (IBPP::Exception& e)
639 {
640 /*tr->Rollback();*/
641 done_good = false;
642 cout << e.ErrorMessage() << endl;
643 }
644 tr->Commit();
645 //rc = sqlite3_exec(m_db, strSql.c_str(), callback_DBItem, &vecRet, &zErrMsg);
646
647 m_mtxDBOperations.unlock();
648
649 if (done_good)
650 {
651
652 }
653 }
654
655 return vecRet;
656}
657
658Result archiverDBFirebird::StoreSignal(IBPP::Database db, archiverSourceConnector* pConnector)
659{
660 commandFirebirdStoreValues* pCommand = new commandFirebirdStoreValues();
661 BasicSource* pSource = pConnector->GetSource();
662
663 SignalValue sValue;
664
665 if (pSource != NULL)
666 {
667 sValue = pConnector->GetSource()->GetValue();
668 }
669 else
670 {
671 sValue = Result(pConnector, Result::STATUS_ERROR, RESULTCODE_NODE_NONODESADDED);
672 }
673
674 cmdStoreValuesContainer container;
675
676 //This is important because if SignalValue not changed, it's time also not changed
677 //By this string we are renewing time
678 sValue = SignalValue(sValue.GetValue());
679
680 container.m_vecValues.push_back(sValue);
681 container.m_nlSource = pConnector->GetNodeLink();
682
683 pCommand->m_vecContainers.push_back(container);
684 IBPP::Database* s_db;
685 s_db[0] = db;
686 pCommand->m_pDB = s_db;
687 pCommand->m_pArchiver = this;
688
689 m_cmdQueue.PushCommand(pCommand);
690
691 //If set corresponding property - return STORING values
692 if (m_enValueReturnMode == BasicArchiver::enValueReturnMode::vrStoring)
693 {
694 pConnector->ValuesStored(container.m_vecValues);
695 }
696
697
698 //Log message
699 NodeTime nt = sValue.GetNodeTime();
700 StoreMessage(EventLevel::elDebug, util::string_format("Push command commandFirebirdStoreValue, archiver %s, connector=%s, value=%s, %s", GetName().c_str(), container.m_nlSource.GetLinkName().c_str(), sValue.ToString().c_str(), nt.ToString().c_str()));
701
702 return Result(Result::STATUS_SUCCESS);
703}
704
705Result archiverDBFirebird::StoreNaN(IBPP::Database db, archiverSourceConnector* pConnector)
706{
707 commandFirebirdStoreValues* pCommand = new commandFirebirdStoreValues();
708 BasicSource* pSource = pConnector->GetSource();
709
710 SignalValue sValue = Result(pConnector, Result::STATUS_NOTICE, RESULTCODE_ARCHIVER_STOPPED);
711
712 cmdStoreValuesContainer cont;
713
714 cont.m_vecValues.push_back(sValue);
715 cont.m_nlSource = pConnector->GetNodeLink();
716
717 pCommand->m_vecContainers.push_back(cont);
718 IBPP::Database* s_db;
719 s_db[0] = db;
720 pCommand->m_pDB = s_db;
721 pCommand->m_pArchiver = this;
722
723 m_cmdQueue.PushCommand(pCommand);
724
725 //Log message
726 NodeTime nt = sValue.GetNodeTime();
727 StoreMessage(EventLevel::elDebug, util::string_format("Push command commandSQLiteStoreValue, archiver %s, connector=%s, value=%s, %s", GetName().c_str(), cont.m_nlSource.GetLinkName().c_str(), sValue.ToString().c_str(), nt.ToString().c_str()));
728
729 return Result(Result::STATUS_SUCCESS);
730}
731
732IBPP::Database archiverDBFirebird::DBOpen(string strDBPath/*, string server, string user, string password*/)
733{
734 IBPP::Database db = NULL;
735 char *zErrMsg = 0;
736 int rc;
737 bool done_good = true;
738 try
739 {
740 //db = IBPP::DatabaseFactory(server, strDBPath, user, password);
741 //db->Connect();
742 }
743 catch (IBPP::Exception &e)
744 {
745 done_good = false;
746 StoreMessage(EventLevel::elError, util::string_format("Невозможно открыть БД: %s, ошибка %s\n", strDBPath.c_str(), e), "System/Archives");
747 db = NULL;
748 }
749 if (done_good)
750 {
751 StoreMessage(EventLevel::elInfo, util::string_format("БД %s открыта успешно", strDBPath.c_str()), "System/Archives");
752 }
753
754 return db;
755}
756
757IBPP::Database archiverDBFirebird::DBOpen(/*string server, string user, string password*/)
758{
759 IBPP::Database db = NULL;
760 char *zErrMsg = 0;
761 int rc;
762
763 string ws_dbpath;
764 string ws_cmdline;
765
766
767 string dbpath_full;
768
769 if ((m_strFilepath.at(0) == '\\') || (m_strFilepath.at(0) == '/') ||
770 (m_strFilepath.find(":\\") != string::npos))
771 //Filepath holds absolute path
772 {
773 ws_dbpath = m_strFilepath;
774 replace(ws_dbpath.begin(), ws_dbpath.end(), '\\', '/');
775
776 int pos = ws_dbpath.find_last_of('/', ws_dbpath.length() - 1);
777 ws_dbpath.erase(pos + 1, ws_dbpath.length() - pos - 1);
778
779 dbpath_full = m_strFilepath;
780 replace(dbpath_full.begin(), dbpath_full.end(), '\\', '/');
781 }
782 else
783 {
784 ws_cmdline = GetNodesCollection()->GetProjectPath();
785 ws_dbpath = ws_cmdline;
786
787 ws_dbpath.append(m_strFilepath);
788 replace(ws_dbpath.begin(), ws_dbpath.end(), '\\', '/');
789
790 int pos = ws_dbpath.find_last_of('/', ws_dbpath.length() - 1);
791 ws_dbpath.erase(pos + 1, ws_dbpath.length() - pos - 1);
792
793 dbpath_full = ws_cmdline;
794 dbpath_full.append(m_strFilepath);
795 replace(dbpath_full.begin(), dbpath_full.end(), '\\', '/');
796 }
797
798 if (m_bDBSplitting == true)
799 {
800 string strDate = boost::gregorian::to_iso_extended_string(m_dtCurrent);
801
802 ws_dbpath = dbpath_full + "/";
803 dbpath_full = dbpath_full + "/" + strDate + ".db3";
804 }
805
806 sysDataArchiver* pSystem = (sysDataArchiver*)GetParent();
807
808 pSystem->m_mtxSpecial.lock();
809
810 bool bDirExists = DirectoryExists(util::utf8_to_wstring(ws_dbpath));
811
812 pSystem->m_mtxSpecial.unlock();
813
814 if (bDirExists == true)
815 {
816
817 }
818 else
819 {
820 //Log message
821 StoreMessage(EventLevel::elNotice, util::string_format("Каталог %s не существует, производится создание", ws_dbpath.c_str()), "System/Archives");
822
823 int nError = 0;
824
825 pSystem->m_mtxSpecial.lock();
826
827 bool bDirCreated = DirectoryCreate(util::utf8_to_wstring(ws_dbpath));
828
829 pSystem->m_mtxSpecial.unlock();
830
831 if (bDirCreated == false)
832 {
833 //Log message
834 StoreMessage(EventLevel::elError, util::string_format("Не удалось создать каталог %s", ws_dbpath.c_str()), "System/Archives");
835 }
836 }
837
838 m_strDBPath = dbpath_full;
839
840 db = DBOpen(m_strDBPath/*, server, user, password*/);
841
842 m_db = db;
843
844 return db;
845}
846
847void archiverDBFirebird::WorkerThread()
848{
849 SetState(NodeState::STATE_RUNNING);
850
851 //printf("%ls started\n", GetName().c_str());
852
853 boost::posix_time::ptime tmNow = boost::posix_time::microsec_clock::local_time();
854 boost::posix_time::ptime tmPrev;
855
856 m_dtCurrent = tmNow.date();
857
858 IBPP::Database db = DBOpen();
859
860 if (db == NULL)
861 {
862 return;
863 }
864 else
865 {
866 DBInit(db);
867
868 // m_db = db;
869
870 unsigned long trim_ct = 0;
871 int i_it = 0;
872 int amount = 0;
873
874 if (m_dwInterval <= 100)
875 {
876 i_it = 1;
877 amount = m_dwInterval;
878 }
879 else if (m_dwInterval <= 1000)
880 {
881 i_it = 2;
882 amount = m_dwInterval / 2;
883 }
884 else if (m_dwInterval <= 3000)
885 {
886 i_it = 10;
887 amount = m_dwInterval / 10;
888 }
889 else
890 {
891 i_it = m_dwInterval / 1000;
892 amount = 1000;
893 }
894
895 m_cmdQueue.Initialize();
896
897 //printf("Amount: %i, ct: %i\n", amount, i_it);
898
899 // bool bUpdateSizes = true;
900
901 UpdateDBSize();
902 DBUpdateInfo();
903
904 NodeArray naConnectors = GetConnectors();
905
906 //Main cycle
907 while (GetState() == NodeState::STATE_RUNNING)
908 {
909 for (auto nlConnector : naConnectors)
910 {
911 archiverSourceConnector* pConnector = (archiverSourceConnector*)nlConnector.GetNode();
912 StoreSignal(m_db, pConnector);
913 }
914
915 if (m_bDBSplitting == true)
916 {
917 tmPrev = tmNow;
918
919 //Get current time
920 tmNow = boost::posix_time::microsec_clock::local_time();
921
922 m_dtCurrent = tmNow.date();
923
924 //Check for a new day begins
925 if (tmNow.date() > tmPrev.date())
926 //New day begins - rotate DB if necessary
927 {
928 DBRotate();
929 }
930 }
931
932 //Check DBs sizes
933
934 if (tmNow.time_of_day().seconds() == 0)
935 {
936 UpdateDBSize();
937
938 boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
939 }
940
941 trim_ct += m_dwInterval;
942
943 if ((m_bDBSplitting == false) && (trim_ct >= 1000 * 120))
944 //Start DB trimming procedure
945 {
946 StoreMessage(EventLevel::elDebug, util::string_format("Database %s: starting trimming procedure", m_strDBPath.c_str()));
947
948 commandFirebirdTrimDB* pTrimCmd = new commandFirebirdTrimDB();
949 pTrimCmd->m_pArchiver = this;
950 pTrimCmd->m_pDB = m_db;
951
952 m_cmdQueue.PushCommand(pTrimCmd);
953
954 trim_ct = 0;
955 }
956
957 for (int t = 0; t < i_it; t++)
958 {
959 boost::this_thread::sleep(boost::posix_time::milliseconds(amount));
960
961 if (GetState() != NodeState::STATE_RUNNING)
962 {
963 break;
964 }
965 }
966 }
967
968 //Store NaN's in DB to show gap
969 StoreMessage(EventLevel::elInfo, util::string_format("Archiver %s, creating gap in data", GetName().c_str()));
970
971 for (auto nlConnector : naConnectors)
972 {
973 archiverSourceConnector* pConnector = (archiverSourceConnector*)nlConnector.GetNode();
974
975 StoreNaN(db, pConnector);
976 }
977
978 }//DB opened successfully
979
980 m_db->Disconnect();
981
982 m_db = NULL;
983}
984
985void archiverDBFirebird::UpdateDBSize()
986{
987 vector<string> vecFiles;
988 int iDBSize = 0;
989
990 sysDataArchiver* pSystem = (sysDataArchiver*)GetParent();
991
992 //printf("UpdateDBSize started\n");
993
994 if (m_bDBSplitting == true)
995 {
996 vecFiles = GetSplitFiles();
997 }
998 else
999 {
1000 vecFiles.push_back(/*util::utf8_to_wstring(*/m_strDBPath/*)*/);
1001 }
1002
1003 pSystem->m_mtxSpecial.lock();
1004
1005 for (string strFile : vecFiles)
1006 {
1007 boost::filesystem::wpath pathDB(strFile);
1008
1009 try
1010 {
1011 iDBSize += boost::filesystem::file_size(pathDB);
1012 }
1013 catch (...)
1014 {
1015
1016 }
1017 }
1018
1019 pSystem->m_mtxSpecial.unlock();
1020
1021 //printf("UpdateDBSize file ops finished\n");
1022
1023 m_fDBCurrentSize = iDBSize / 1048576.0;
1024
1025 BasicProperty* pPropCurrentSize = GetPropertyByValueId(BASICARCHIVER_PARAM_DBCURRENTSIZE);
1026
1027 if (pPropCurrentSize != NULL)
1028 {
1029 PropertyUpdated(pPropCurrentSize);
1030
1031 pPropCurrentSize->ValueUpdated(SignalValue(m_fDBCurrentSize));
1032 }
1033
1034 //Calculate estimated DB max size
1035 float RecsPerSec = 1000.0 / m_dwInterval;
1036 int iRecordsCt = (RecsPerSec)* (m_dwRetention * 86400) * m_vecConnectors.size();
1037
1038 m_fDBMaxSize = (iRecordsCt * 40.0) / 1048576.0;
1039
1040 BasicProperty* pPropMaxSize = GetPropertyByValueId(BASICARCHIVER_PARAM_DBMAXSIZE);
1041 if (pPropMaxSize != NULL)
1042 {
1043 PropertyUpdated(pPropMaxSize);
1044
1045 pPropMaxSize->ValueUpdated(SignalValue(m_fDBMaxSize));
1046 }
1047
1048 //printf("UpdateDBSize finished\n");
1049}
1050
1051Result archiverDBFirebird::DBRotate()
1052{
1053 Result res(this);
1054
1055 m_mtxDBOperations.lock();
1056
1057 sysDataArchiver* pSystem = (sysDataArchiver*)GetParent();
1058
1059 //Close current DB
1060 m_db->Disconnect();
1061 m_db = NULL;
1062
1063 //Rotate DB files
1064
1065 boost::gregorian::date_duration ddRetention(m_dwRetention);
1066 boost::gregorian::date dtLast = m_dtCurrent - ddRetention;
1067
1068 string strDateCurrent = boost::gregorian::to_iso_extended_string(m_dtCurrent);
1069 string strDateLast = boost::gregorian::to_iso_extended_string(dtLast);
1070
1071 //printf("db %s rotating. current date - %s, last saved date - %s.\n", m_strName.c_str(), strDateCurrent.c_str(), strDateLast.c_str());
1072 StoreMessage(EventLevel::elTrace, util::string_format("Ротация фалов БД. Текущая дата - %s, крайняя сохраняемая БД - %s.", strDateCurrent.c_str(), strDateLast.c_str()), "System/Archives");
1073
1074 vector<string> vecFiles = GetSplitFiles();
1075
1076 //Check how many files in folder already exists
1077 string strTmp = m_strDBPath;
1078 replace(strTmp.begin(), strTmp.end(), '\\', '/');
1079 int pos = strTmp.find_last_of('/', strTmp.length() - 1);
1080 strTmp.erase(pos + 1, strTmp.length() - pos - 1);
1081
1082 int iFilesCt = vecFiles.size();
1083
1084 //printf("db rotating. db retention - %u days, folder %s contains %u files.\n", m_dwRetention, strTmp.c_str(), iFilesCt);
1085 StoreMessage(EventLevel::elTrace, util::string_format("Ротация фалов БД. Период хранения - %u дней, папка %s содержит %i файлов.", m_dwRetention, strTmp.c_str(), iFilesCt), "System/Archives");
1086
1087 int iRemovedFilesCt = 0;
1088
1089 pSystem->m_mtxSpecial.lock();
1090
1091 while (vecFiles.size() > m_dwRetention)
1092 //Perform deletion of older files
1093 {
1094 string strFile = vecFiles.front();
1095
1096 try
1097 {
1098 boost::filesystem::path pathFile(util::utf8_to_wstring(strFile));
1099 vecFiles.erase(vecFiles.begin());
1100
1101 boost::filesystem::remove(pathFile);
1102
1103 iRemovedFilesCt++;
1104 }
1105 catch (...)
1106 {
1107 // printf("db rotating. failed to remove file %s\n", util::to_string(strFile).c_str());
1108 }
1109 }
1110
1111 pSystem->m_mtxSpecial.unlock();
1112
1113 if (iRemovedFilesCt > 0)
1114 {
1115 //printf("DB rotating. Deleted %u files.\n", iRemovedFilesCt);
1116 StoreMessage(EventLevel::elTrace, util::string_format("Ротация фалов БД. Удалено %i файлов.", iRemovedFilesCt), "System/Archives");
1117 }
1118
1119 //Open new DB
1120 IBPP::Database db = DBOpen();
1121
1122 if (db == NULL)
1123 {
1124 res.m_Status = Result::STATUS_ERROR;
1125 res.m_dwErrorCode = RESULTCODE_ARCHIVER_DBERROR;
1126 }
1127 else
1128 {
1129 res.m_Status = Result::STATUS_SUCCESS;
1130 res.m_dwErrorCode = RESULTCODE_NOERROR;
1131
1132 DBInit(db);
1133
1134 m_db = db;
1135 }
1136
1137 m_mtxDBOperations.unlock();
1138
1139 return res;
1140}
1141
1142// bool archiverDBFirebird::AddConnectors(vector<DBItem> vecItems)
1143// {
1144// for (DBItem item : vecItems)
1145// {
1146// archiverSourceConnector* pConnector = new archiverSourceConnector();
1147//
1148// pConnector->m_dwId = item.m_dwSignalId;
1149// }
1150// }
1151
1152////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1153// commandArchiverStoreValues
1154////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1155
1156commandFirebirdStoreValues::commandFirebirdStoreValues()
1157{
1158 m_dwType = COMMAND_ARCHIVER_STOREVALUE;
1159 // printf("Creating commandSQLiteStoreValues\n");
1160}
1161
1162commandFirebirdStoreValues::~commandFirebirdStoreValues()
1163{
1164 // printf("Deleting commandSQLiteStoreValues\n");
1165}
1166
1167#ifdef linux
1168typedef unsigned long long ULONG64;
1169#endif
1170
1171Result commandFirebirdStoreValues::Execute()
1172{
1173 int rc = 0;
1174 char *zErrMsg = 0;
1175
1176 // string strQuery = "INSERT INTO SIGNALDATA (SignalDataSec, SignalDataMsec, SignalId, SignalValue) VALUES ";
1177 string strQuery = "INSERT INTO SignalData (SignalDateTime, SignalId, SignalValue) VALUES ";
1178
1179 // m_pArchiver->StoreMessage(EventLevel::elDebug, util::string_format("Storing signal %s to DB started, values count=%u", m_pConnector->GetName().c_str(), m_vecValues.size()));
1180
1181 int iQueryValues = 0;
1182 vector<archiverSourceConnector*> vecConnectors;
1183
1184 for (int iCont = 0; iCont < m_vecContainers.size(); iCont++)
1185 {
1186 cmdStoreValuesContainer container = m_vecContainers[iCont];
1187
1188 archiverSourceConnector* pConnector = (archiverSourceConnector*)container.m_nlSource.GetNode();
1189 ConfigNode* pSource = pConnector->GetSource();// m_pArchiver->GetNodesCollection()->GetNode(container.m_dwSource);
1190
1191 if (pSource != NULL)
1192 {
1193 DWORD dwSignalId = pSource->GetId();
1194 vecConnectors.push_back(pConnector);
1195
1196 for (int iVal = 0; iVal < container.m_vecValues.size(); iVal++)//each (SignalValue Value in m_vecValues)
1197 {
1198 SignalValue Value = container.m_vecValues.at(iVal);
1199 // vecValues.push_back(Value);
1200
1201 if (Value.IsNumber() == false)
1202 {
1203 // m_pArchiver->StoreMessage(EventLevel::elDebug, util::string_format("Skipping signal %s storing to DB, value type is %u", m_pConnector->GetName().c_str(), Value.GetType()));
1204
1205 // Result res(m_pArchiver, Result::STATUS_NOTICE, RESULTCODE_ARCHIVER_WRONGSIGNALTYPE);
1206
1207 // continue;
1208 }
1209
1210 boost::posix_time::ptime time_t_epoch(boost::gregorian::date(1970, 1, 1));
1211 boost::posix_time::ptime now = Value.GetPTime();// boost::posix_time::microsec_clock::local_time();
1212 boost::posix_time::time_duration diff = now - time_t_epoch;
1213 boost::posix_time::time_duration diff1 = boost::posix_time::seconds(diff.total_seconds());
1214
1215 uint64_t dt_ll = diff.total_seconds();
1216 boost::posix_time::ptime time_diff(boost::gregorian::date(1970, 1, 1), diff1);
1217 boost::posix_time::time_duration diff_ms = now - time_diff;
1218 int ms = diff_ms.total_milliseconds();
1219
1220 dt_ll *= 1000;
1221 dt_ll += ms;
1222
1223 if (Value.IsNumber() == true)
1224 {
1225 strQuery += util::string_format("(%llu, %u, '%f')", dt_ll, dwSignalId, Value.ToDouble());
1226 }
1227 else
1228 {
1229 strQuery += util::string_format("(%llu, %u, NULL)", dt_ll, dwSignalId);
1230 }
1231
1232 if (iVal < (container.m_vecValues.size() - 1))
1233 {
1234 strQuery += ", ";
1235 }
1236
1237 iQueryValues++;
1238 }
1239
1240 if (iCont < (m_vecContainers.size() - 1))
1241 {
1242 strQuery += ", ";
1243 }
1244 }
1245 }
1246 bool done_good = true;
1247 strQuery += ";";
1248 if (iQueryValues > 0)
1249 {
1250 // m_pArchiver->m_mtxDBOperations.lock();
1251 IBPP::Database* temp = (IBPP::Database*)m_pDB;
1252 if (temp[0]->Connected() == 0)
1253 {
1254 temp[0]->Connect();
1255 }
1256 IBPP::Transaction tr = IBPP::TransactionFactory(temp[0]);
1257 try
1258 {
1259 tr->Start();
1260 IBPP::Statement st = StatementFactory(temp[0], tr);
1261 st->Prepare(strQuery);
1262 st->Execute();
1263 }
1264 catch (IBPP::Exception& e)
1265 {
1266 /*tr->Rollback();*/
1267 m_pArchiver->StoreMessage(EventLevel::elError, util::string_format("Ошибка SQL запроса %s", strQuery.c_str()), "System/Archives");
1268 return Result(m_pArchiver, Result::STATUS_ERROR, RESULTCODE_ARCHIVER_DBERROR);
1269 done_good = false;
1270 }
1271 tr->Commit();
1272 //rc = sqlite3_exec((sqlite3*)m_pDB, strQuery.c_str(), callback, 0, &zErrMsg);
1273
1274 // m_pArchiver->m_mtxDBOperations.unlock();
1275 }
1276
1277 m_pArchiver->m_iDBStatus = rc;
1278
1279 BasicProperty* pPropDBStatus = m_pArchiver->GetPropertyByValueId(BASICARCHIVER_PARAM_DBSTATUS);
1280
1281 if (pPropDBStatus != NULL)
1282 {
1283 m_pArchiver->PropertyUpdated(pPropDBStatus);
1284
1285 pPropDBStatus->ValueUpdated(SignalValue(m_pArchiver->m_iDBStatus));
1286 }
1287
1288 if (done_good)
1289 {
1290 m_pArchiver->StoreMessage(EventLevel::elDebug, util::string_format("Storing archiver %s signals to DB finished, values count=%u", m_pArchiver->GetName().c_str(), iQueryValues));
1291
1292 for (cmdStoreValuesContainer container : m_vecContainers)
1293 {
1294 archiverSourceConnector* pConnector = (archiverSourceConnector*)container.m_nlSource.GetNode();
1295
1296 //If set corresponding property - return STORED values
1297 if (m_pArchiver->m_enValueReturnMode == BasicArchiver::enValueReturnMode::vrStored)
1298 {
1299 pConnector->ValuesStored(container.m_vecValues);
1300 }
1301
1302 //This is for widgets connected with connectors
1303 SignalValue svLast = container.m_vecValues.back();
1304
1305 pConnector->SetValue(svLast);
1306
1307 pConnector->ValueUpdated(svLast);
1308 }
1309 }
1310}
1311
1312////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1313// commandFirebirdGetValues
1314////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1315
1316commandFirebirdGetValues::commandFirebirdGetValues(commandArchiverGetValues* pCommand) : commandArchiverGetValues()
1317{
1318 m_dwType = COMMAND_ARCHIVER_GETVALUES;
1319
1320 // m_pDB = (sqlite3*)pCommand->m_pDB;
1321 archiverDBFirebird* pArchiver = (archiverDBFirebird*)pCommand->m_pArchiver;
1322 m_pArchiver = pArchiver;
1323 IBPP::Database* temp;
1324 temp[0] = pArchiver->GetDB();
1325 m_pDB = temp;
1326 m_pConnector = pCommand->m_pConnector;
1327 m_pWidget = pCommand->m_pWidget;
1328 m_pFetch = pCommand->m_pFetch;
1329
1330 m_vecRequestData = pCommand->m_vecRequestData;
1331
1332 m_enPriority = enPriority::pHigh;
1333}
1334
1335commandFirebirdGetValues::~commandFirebirdGetValues()
1336{
1337 // printf("Deleting commandArchiverGetValues\n");
1338}
1339
1340Result commandFirebirdGetValues::Execute()
1341{
1342 char *zErrMsg = 0;
1343 string strSql;
1344
1345 Result ret;
1346
1347 archiverDBFirebird* pArchiver = (archiverDBFirebird*)m_pArchiver;
1348
1349 const char* data = "Callback function called";
1350 int rc;
1351 int pf_ret;
1352
1353 string strQuery;
1354
1355 // printf("%s: GetSignalHistory\n", m_pArchiver->GetName().c_str());
1356
1357 if (m_pArchiver->m_bDBSplitting == true)
1358 //Archiver stores data in split db
1359 {
1360 vector<string> vecDBFiles = pArchiver->GetSplitFiles();
1361
1362 if (vecDBFiles.size() == 0)
1363 {
1364 ret = Result(pArchiver, Result::STATUS_ERROR, RESULTCODE_ARCHIVER_NOSPLITFILES);
1365 }
1366 else
1367 {
1368 vector<boost::gregorian::date> vecDates;
1369
1370 for (auto strFile : vecDBFiles)
1371 {
1372 //Convert string date to gregorian date
1373 replace(strFile.begin(), strFile.end(), '\\', '/');
1374
1375 int pos = strFile.find_last_of('/', strFile.length() - 1);
1376
1377 if (pos > 0)
1378 {
1379 try
1380 {
1381 //4 is the length of string ".db3"
1382 strFile = strFile.substr(pos + 1, strFile.length() - (pos + 1) - 4);
1383
1384 if (strFile.size() > 4)
1385 {
1386 boost::gregorian::date dt = boost::gregorian::from_string(strFile);
1387
1388 vecDates.push_back(dt);
1389 }
1390 }
1391 catch (...)
1392 {
1393
1394 }
1395 }
1396 }
1397
1398 //SignalHistoryRequest.m_dwArchiver is const
1399 //SignalHistoryRequest.m_iBegin is const
1400 //SignalHistoryRequest.m_iEnd is const
1401
1402 vector<DBReq> vecRequests;
1403 vector<DWORD> vecSignals;
1404
1405 uint64_t llStart = ULLONG_MAX;
1406 uint64_t llStop = 0;
1407
1408 //Collect all signals, because i think start and end time_t is all the same across all items
1409 for (int t = 0; t < m_vecRequestData.size(); t++)
1410 {
1411 SignalHistoryRequest shr = m_vecRequestData[t];
1412
1413 if (shr.m_iEnd > llStop)
1414 {
1415 llStop = shr.m_iEnd;
1416 }
1417
1418 if (shr.m_iBegin < llStart)
1419 {
1420 llStart = shr.m_iBegin;
1421 }
1422
1423 vecSignals.push_back(shr.m_dwSignal);
1424 }
1425
1426 //Calculate dates of start and end
1427 // SignalHistoryRequest shr = m_vecRequestData[0];
1428 // boost::posix_time::ptime ptStart = boost::posix_time::from_time_t(shr.m_iBegin);
1429 // boost::posix_time::ptime ptStop = boost::posix_time::from_time_t(shr.m_iEnd);
1430
1431 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues start: %llu, stop: %llu", llStart, llStop), "System/Archives");
1432 printf("commandFirebirdGetValues start: %llu, stop: %llu\n", llStart, llStop);
1433
1434 if (llStop == 0)
1435 {
1436 // llStop = UINT_MAX;
1437 // llStop = ULLONG_MAX;
1438 llStop = UINT_MAX;
1439 llStop *= 1000;
1440 }
1441
1442 boost::posix_time::ptime ptStart = boost::posix_time::from_time_t(llStart / 1000);
1443 boost::posix_time::ptime ptStop = boost::posix_time::from_time_t(llStop / 1000);
1444
1445 /* if (shr.m_iEnd == 0)
1446 {
1447 ptStop = boost::posix_time::second_clock::local_time();
1448 }
1449 */
1450 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues (ptime) start: %llu, stop: %llu", llStart, llStop), "System/Archives");
1451
1452 boost::posix_time::time_duration tdDay(boost::posix_time::seconds(24 * 60 * 60));
1453
1454 //Create requests to DBs
1455 //Start from end for faster processing
1456 // for (auto it = vecDates.rbegin(); it != vecDates.rend(); ++it)
1457 for (unsigned int iDate = vecDates.size() - 1; iDate >= 0; iDate--)
1458 // do
1459 {
1460 // boost::gregorian::date dtDBStart = *it;
1461 boost::gregorian::date dtDBStart = vecDates[iDate];
1462
1463 boost::posix_time::ptime ptDBStart(dtDBStart);
1464 boost::posix_time::ptime ptDBStop(dtDBStart, tdDay);
1465
1466 boost::posix_time::ptime ptIterationStart;
1467 boost::posix_time::ptime ptIterationStop;
1468
1469 if ((ptStart >= ptDBStart))
1470 //In range
1471 {
1472 ptIterationStart = ptStart;
1473 }
1474 else
1475 {
1476 ptIterationStart = ptDBStart;
1477 }
1478
1479 if ((ptStop <= ptDBStop))
1480 //In range
1481 {
1482 ptIterationStop = ptStop;
1483 }
1484 else
1485 {
1486 ptIterationStop = ptDBStop;
1487 }
1488
1489 uint64_t ttStart = util::to_milliseconds(ptIterationStart);
1490 uint64_t ttStop = util::to_milliseconds(ptIterationStop);
1491
1492 strQuery = "";
1493
1494 //Process all signal ids
1495 for (unsigned int t = 0; t < vecSignals.size(); t++)
1496 {
1497 DWORD dwSignal = vecSignals[t];
1498
1499 string strSql;
1500
1501 if (t == 0)
1502 {
1503 // strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec>=%lu and SignalDataSec<=%lu)", dwSignal, ttStart, ttStop);
1504 strSql = util::string_format("SELECT * FROM SignalData WHERE (SignalId=%u AND SignalDateTime>=%llu AND SignalDateTime<=%llu)", dwSignal, ttStart, ttStop);
1505 }
1506 else
1507 {
1508 // strSql = util::string_format(" or (SignalId=%u and SignalDataSec>=%lu and SignalDataSec<=%lu)", dwSignal, ttStart, ttStop);
1509 strSql = util::string_format(" OR (SignalId=%u AND SignalDateTime>=%llu AND SignalDateTime<=%llu)", dwSignal, ttStart, ttStop);
1510 }
1511
1512 strQuery += strSql;
1513 }
1514
1515 DBReq dbr;
1516
1517 dbr.m_strDBRequest = strQuery;
1518 dbr.m_strDBPath = vecDBFiles[iDate];
1519
1520 vecRequests.push_back(dbr);
1521
1522 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues creating request %u: %s", vecRequests.size(), strQuery.c_str()), "System/Archives");
1523
1524 if ((ptStart >= ptDBStart) || (iDate == 0))
1525 //Reached finishing DB
1526 {
1527 break;
1528 }
1529 }
1530 // while (dtStart < dtDB);
1531
1532 //Now we are ready to execute requests
1533 for (unsigned int t = 0; t < vecRequests.size(); t++)
1534 {
1535 DBReq dbr = vecRequests[t];
1536
1537 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues post query: %s", dbr.m_strDBRequest.c_str()), "System/Archives");
1538
1539 IBPP::Database db = NULL;
1540
1541 bool bNeedToCloseBD = true;
1542
1543 // m_pArchiver->m_mtxDBOperations.lock();
1544
1545 if (dbr.m_strDBPath == pArchiver->m_strDBPath)
1546 {
1547 IBPP::Database* temp = (IBPP::Database*)m_pDB;
1548 db = temp[0];
1549 bNeedToCloseBD = false;
1550 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("Current open DB is %s, using it for request", dbr.m_strDBPath.c_str()), "System/Archives");
1551 }
1552 else
1553 {
1554 db = pArchiver->DBOpen(dbr.m_strDBPath);
1555 }
1556
1557 commandFirebirdGetValues* cmd = new commandFirebirdGetValues(this);
1558
1559 /* Execute SQL statement */
1560 bool done_good = true;
1561 if (db->Connected() == 0)
1562 {
1563 db->Connect();
1564 }
1565 IBPP::Transaction tr = IBPP::TransactionFactory(db);
1566 try
1567 {
1568 tr->Start();
1569 IBPP::Statement st = StatementFactory(db, tr);
1570 st->Prepare(strQuery);
1571 st->Execute();
1572 IBPP::Row r;
1573 while (st->Fetch(r))
1574 {
1575 SignalHistoryResponse sd;
1576 for (int i = 0; i < r->Columns(); i++)
1577 {
1578 if (i == 0)
1579 {
1580 long long temp;
1581 r->Get(i + 1, temp);
1582 sd.m_uiDateTime = temp;
1583 }
1584 // else if(i==1)
1585 // {
1586 // sd.m_dwMilliseconds = atoi(argv[i]);
1587 // }
1588 else if (i == 1)
1589 {
1590 int temp;
1591 r->Get(i + 1, temp);
1592 sd.m_dwSignalId = temp;
1593 }
1594 else if (i == 2)
1595 {
1596 //string str;
1597 double temp;
1598 r->Get(i + 1, temp);
1599 if (temp != NULL)
1600 {
1601 sd.m_dValue = temp;
1602 }
1603 else
1604 {
1605 sd.m_dValue = numeric_limits<double>::quiet_NaN();
1606 }
1607 }
1608 }
1609 cmd->m_vecResponseData.push_back(sd);
1610 }
1611 }
1612 catch (IBPP::Exception& e)
1613 {
1614 /*tr->Rollback();*/
1615 m_pArchiver->StoreMessage(EventLevel::elError, util::string_format("SQL error: %s", e));
1616 done_good = false;
1617 }
1618 tr->Commit();
1619 //rc = sqlite3_exec(db, dbr.m_strDBRequest.c_str(), callback_GetSignalHistory, (void*)cmd, &zErrMsg);
1620 if (done_good)
1621 rc = 0;
1622 else
1623 rc = 4;
1624 m_pArchiver->m_iDBStatus = rc;
1625
1626 BasicProperty* pPropDBStatus = m_pArchiver->GetPropertyByValueId(BASICARCHIVER_PARAM_DBSTATUS);
1627
1628 if (pPropDBStatus != NULL)
1629 {
1630 m_pArchiver->PropertyUpdated(pPropDBStatus);
1631
1632 pPropDBStatus->ValueUpdated(SignalValue(m_pArchiver->m_iDBStatus));
1633 }
1634
1635 if(done_good)
1636 {
1637 m_vecResponseData.insert(m_vecResponseData.end(), cmd->m_vecResponseData.begin(), cmd->m_vecResponseData.end());
1638
1639 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues finished successfully, got %u records", cmd->m_vecResponseData.size()));
1640
1641 delete cmd;
1642
1643 // printf("commandSQLiteGetValues finished successfully\n");
1644 }
1645
1646 if (bNeedToCloseBD)
1647 {
1648 db->Disconnect();
1649 }
1650
1651 // m_pArchiver->m_mtxDBOperations.unlock();
1652 }
1653
1654 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues finished successfully, total %u records", m_vecResponseData.size()), "System/Archives");
1655 }
1656
1657 //Signalize command finish
1658 ExecuteCompleted(m_vecResponseData);
1659
1660 if (m_pArchiver != NULL)
1661 {
1662 m_pArchiver->CommandProcessed(this);
1663 }
1664
1665 if (m_pConnector != NULL)
1666 {
1667 widgetChartHistorySerie* pSerie = (widgetChartHistorySerie*)m_pConnector->GetParent();
1668
1669 if (pSerie != NULL)
1670 {
1671 pSerie->GetHistoryCompleted(m_vecResponseData);
1672 }
1673 }
1674
1675 if (m_pConnector != NULL)
1676 {
1677 m_pConnector->GetHistoryCompleted(m_vecResponseData);
1678 }
1679
1680 if (m_pWidget != NULL)
1681 {
1682 widgetChartHistory* pChart = (widgetChartHistory*)m_pWidget;
1683 pChart->GetHistoryCompleted(m_vecResponseData);
1684 }
1685
1686 if (m_pFetch != NULL)
1687 {
1688 m_pFetch->GetHistoryCompleted(m_vecResponseData);
1689 }
1690 }
1691 else
1692 //Usual, single file DB
1693 {
1694 for (int t = 0; t < m_vecRequestData.size(); t++)
1695 {
1696 SignalHistoryRequest shr = m_vecRequestData[t];
1697
1698 //Print all data to process
1699 printf("%s: archiver: %u, signal: %u, start: %lu, stop: %lu\n", m_pArchiver->GetName().c_str(), m_pArchiver->GetId(), shr.m_dwSignal, shr.m_iBegin, shr.m_iEnd);
1700
1701 /* Create SQL statement */
1702 if ((m_vecRequestData[t].m_iBegin == 0) && (m_vecRequestData[t].m_iEnd == 0))
1703 {
1704 if (t == 0)
1705 {
1706 // strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u)", m_vecRequestData[t].m_dwSignal);
1707 strSql = util::string_format("select * from SignalData where (SignalId=%u)", m_vecRequestData[t].m_dwSignal);
1708 }
1709 else
1710 {
1711 strSql = util::string_format(" or (SignalId=%u)", m_vecRequestData[t].m_dwSignal);
1712 }
1713 }
1714 else if ((m_vecRequestData[t].m_iBegin == 0) && (m_vecRequestData[t].m_iEnd != 0))
1715 {
1716 if (t == 0)
1717 {
1718 // strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec<=%lu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iEnd);
1719 strSql = util::string_format("select * from SignalData where (SignalId=%u and SignalDateTime<=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iEnd);
1720 }
1721 else
1722 {
1723 strSql = util::string_format(" or (SignalId=%u and SignalDataSec<=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iEnd);
1724 }
1725 }
1726 else if ((m_vecRequestData[t].m_iBegin != 0) && (m_vecRequestData[t].m_iEnd == 0))
1727 {
1728 if (t == 0)
1729 {
1730 // strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec>=%lu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin);
1731 strSql = util::string_format("select * from SignalData where (SignalId=%u and SignalDateTime>=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin);
1732 }
1733 else
1734 {
1735 // strSql = util::string_format(" or (SignalId=%u and SignalDataSec>=%lu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin);
1736 strSql = util::string_format(" or (SignalId=%u and SignalDateTime>=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin);
1737 }
1738 }
1739 else if ((m_vecRequestData[t].m_iBegin != 0) && (m_vecRequestData[t].m_iEnd != 0))
1740 {
1741 if (t == 0)
1742 {
1743 // strSql = util::string_format("select * from SIGNALDATA where (SignalId=%u and SignalDataSec>=%lu and SignalDataSec<=%lu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin, m_vecRequestData[t].m_iEnd);
1744 strSql = util::string_format("select * from SignalData where (SignalId=%u and SignalDateTime>=%llu and SignalDateTime<=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin, m_vecRequestData[t].m_iEnd);
1745 }
1746 else
1747 {
1748 // strSql = util::string_format(" or (SignalId=%u and SignalDataSec>=%lu and SignalDataSec<=%lu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin, m_vecRequestData[t].m_iEnd);
1749 strSql = util::string_format(" or (SignalId=%u and SignalDateTime>=%llu and SignalDateTime<=%llu)", m_vecRequestData[t].m_dwSignal, m_vecRequestData[t].m_iBegin, m_vecRequestData[t].m_iEnd);
1750 }
1751 }
1752 else
1753 {
1754 printf("Unhandled request!\n");
1755 }
1756
1757 strQuery += strSql;
1758 }
1759
1760
1761 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues post query: %s", strQuery.c_str()), "System/Archives");
1762
1763 /* Execute SQL statement */
1764 IBPP::Database* temp_1 = (IBPP::Database*)m_pDB;
1765 bool done_good = true;
1766 if (temp_1[0]->Connected() == 0)
1767 {
1768 temp_1[0]->Connect();
1769 }
1770 IBPP::Transaction tr = IBPP::TransactionFactory(temp_1[0]);
1771 try
1772 {
1773 tr->Start();
1774 IBPP::Statement st = StatementFactory(temp_1[0], tr);
1775 st->Prepare(strQuery);
1776 st->Execute();
1777 IBPP::Row r;
1778 while (st->Fetch(r))
1779 {
1780 SignalHistoryResponse sd;
1781 for (int i = 0; i < r->Columns(); i++)
1782 {
1783 if (i == 0)
1784 {
1785 long long temp;
1786 r->Get(i + 1, temp);
1787 sd.m_uiDateTime = temp;
1788 }
1789 // else if(i==1)
1790 // {
1791 // sd.m_dwMilliseconds = atoi(argv[i]);
1792 // }
1793 else if (i == 1)
1794 {
1795 int temp;
1796 r->Get(i + 1, temp);
1797 sd.m_dwSignalId = temp;
1798 }
1799 else if (i == 2)
1800 {
1801 //string str;
1802 double temp;
1803 r->Get(i + 1, temp);
1804 if (temp != NULL)
1805 {
1806 sd.m_dValue = temp;
1807 }
1808 else
1809 {
1810 sd.m_dValue = numeric_limits<double>::quiet_NaN();
1811 }
1812 }
1813 }
1814 this->m_vecResponseData.push_back(sd);
1815 }
1816 }
1817 catch (IBPP::Exception& e)
1818 {
1819 /*tr->Rollback();*/
1820 m_pArchiver->StoreMessage(EventLevel::elError, util::string_format("SQL error: %s", e));
1821 done_good = false;
1822 }
1823 tr->Commit();
1824 //rc = sqlite3_exec((sqlite3*)m_pDB, strQuery.c_str(), callback_GetSignalHistory, (void*)this, &zErrMsg);
1825
1826 if (done_good)
1827 {
1828 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("commandFirebirdGetValues finished successfully, got %u records", m_vecResponseData.size()), "System/Archives");
1829
1830 if (m_pArchiver != NULL)
1831 {
1832 m_pArchiver->CommandProcessed(this);
1833 }
1834
1835 if (m_pConnector != NULL)
1836 {
1837 widgetChartHistorySerie* pSerie = (widgetChartHistorySerie*)m_pConnector->GetParent();
1838
1839 if (pSerie != NULL)
1840 {
1841 pSerie->GetHistoryCompleted(m_vecResponseData);
1842 }
1843 }
1844
1845 if (m_pConnector != NULL)
1846 {
1847 m_pConnector->GetHistoryCompleted(m_vecResponseData);
1848 }
1849
1850 if (m_pWidget != NULL)
1851 {
1852 widgetChartHistory* pChart = (widgetChartHistory*)m_pWidget;
1853 pChart->GetHistoryCompleted(m_vecResponseData);
1854 }
1855
1856 if (m_pFetch != NULL)
1857 {
1858 m_pFetch->GetHistoryCompleted(m_vecResponseData);
1859 }
1860
1861 // command->m_State = NodeState(NodeState::STATE_RUNNING, RESULTCODE_NOERROR);
1862 }
1863 }
1864
1865 return ret;
1866}
1867
1868
1869////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1870// commandFirebirdTrimDB
1871////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1872
1873
1874commandFirebirdTrimDB::commandFirebirdTrimDB(/*commandArchiverGetValues* pCommand*/)// : commandArchiverGetValues()
1875{
1876 m_dwType = COMMAND_ARCHIVER_TRIM;
1877
1878 // m_pArchiver = (archiverDBFirebird*)pCommand->m_pArchiver;
1879 // m_pDB = m_pArchiver->GetDB();
1880}
1881
1882commandFirebirdTrimDB::~commandFirebirdTrimDB()
1883{
1884
1885}
1886
1887Result commandFirebirdTrimDB::Execute()
1888{
1889 char *sql;
1890 int rc;
1891 char *zErrMsg = 0;
1892
1893 SignalHistoryResponse *sd_start = new SignalHistoryResponse();
1894 sd_start->m_uiDateTime = 0;
1895
1896 // sql = "select * from SIGNALDATA order by SignalDataSec asc limit 1";
1897 sql = "select * from SignalData order by SignalDateTime asc limit 1";
1898 bool done_good = true;
1899 if (m_pDB->Connected() == 0)
1900 {
1901 m_pDB->Connect();
1902 }
1903 IBPP::Transaction tr = IBPP::TransactionFactory(m_pDB);
1904 try
1905 {
1906 tr->Start();
1907 IBPP::Statement st = IBPP::StatementFactory(m_pDB, tr);
1908 st->Prepare(sql);
1909 st->Execute();
1910 IBPP::Row r;
1911 while (st->Fetch(r))
1912 {
1913 int i;
1914
1915 for (i = 0; i<r->Columns(); i++)
1916 {
1917 if (i == 0)
1918 {
1919 int temp;
1920 r->Get(i + 1, temp);
1921 sd_start->m_uiDateTime = temp;
1922 }
1923 // else if (i == 1)
1924 // {
1925 // sd->m_dwMilliseconds = atoi(argv[i]);
1926 // }
1927 else if (i == 1)
1928 {
1929 int temp;
1930 r->Get(i + 1, temp);
1931 sd_start->m_dwSignalId = temp;
1932 }
1933 else if (i == 2)
1934 {
1935 string str;
1936 double temp;
1937 r->Get(i + 1, temp);
1938
1939 if (temp == NULL)
1940 {
1941 sd_start->m_dValue = numeric_limits<double>::quiet_NaN();
1942 }
1943 else
1944 {
1945 sd_start->m_dValue = temp;
1946 }
1947 }
1948 }
1949 }
1950 }
1951 catch (IBPP::Exception &e)
1952 {
1953 done_good = false;
1954 printf("SQL error: %s\n", e);
1955 }
1956 tr->Commit();
1957 //rc = sqlite3_exec(m_pDB, sql, callback_TrimByDate, (void*)sd_start, &zErrMsg);
1958
1959 if (done_good)
1960 {
1961 while (sd_start->m_uiDateTime == 0)
1962 {
1963 boost::this_thread::sleep(boost::posix_time::milliseconds(1));
1964 }
1965
1966 printf("DBTrim: first record %llu\n", sd_start->m_uiDateTime);
1967
1968 // sql = "select * from SIGNALDATA order by SignalDataSec desc limit 1";
1969 sql = "select * from SignalData order by SignalDateTime desc limit 1";
1970
1971 SignalHistoryResponse *sd_stop = new SignalHistoryResponse();
1972 sd_stop->m_uiDateTime = 0;
1973 bool done_good = true;
1974 if (m_pDB->Connected() == 0)
1975 {
1976 m_pDB->Connect();
1977 }
1978 IBPP::Transaction tr = IBPP::TransactionFactory(m_pDB);
1979 try
1980 {
1981 tr->Start();
1982 IBPP::Statement st = IBPP::StatementFactory(m_pDB, tr);
1983 st->Prepare(sql);
1984 st->Execute();
1985 IBPP::Row r;
1986 while (st->Fetch(r))
1987 {
1988 int i;
1989
1990 for (i = 0; i<r->Columns(); i++)
1991 {
1992 if (i == 0)
1993 {
1994 int temp;
1995 r->Get(i + 1, temp);
1996 sd_stop->m_uiDateTime = temp;
1997 }
1998 // else if (i == 1)
1999 // {
2000 // sd->m_dwMilliseconds = atoi(argv[i]);
2001 // }
2002 else if (i == 1)
2003 {
2004 int temp;
2005 r->Get(i + 1, temp);
2006 sd_stop->m_dwSignalId = temp;
2007 }
2008 else if (i == 2)
2009 {
2010 string str;
2011 double temp;
2012 r->Get(i + 1, temp);
2013
2014 if (temp == NULL)
2015 {
2016 sd_stop->m_dValue = numeric_limits<double>::quiet_NaN();
2017 }
2018 else
2019 {
2020 sd_stop->m_dValue = temp;
2021 }
2022 }
2023 }
2024 }
2025 }
2026 catch (IBPP::Exception &e)
2027 {
2028 done_good = false;
2029 printf("SQL error: %s\n", e);
2030 }
2031 tr->Commit();
2032 //rc = sqlite3_exec(m_pDB, sql, callback_TrimByDate, (void*)sd_stop, &zErrMsg);
2033
2034 if (done_good)
2035 {
2036 while (sd_stop->m_uiDateTime == 0)
2037 {
2038 boost::this_thread::sleep(boost::posix_time::milliseconds(1));
2039 }
2040
2041 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("DBTrim: last record %llu", sd_stop->m_uiDateTime));
2042
2043 // int trim_last_sec = sd_stop->m_tmDateTime - m_pArchiver->m_dwRetention * 86400;
2044 int64_t trim_last_sec = sd_stop->m_uiDateTime - m_pArchiver->m_dwRetention * 86400000;
2045
2046 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("DBTrim: data retention %u days, removing records preceding to %llu seconds", m_pArchiver->m_dwRetention, trim_last_sec));
2047
2048 string strSql;
2049 // strSql = util::string_format("delete from SIGNALDATA where SignalDataSec<%u;", trim_last_sec);
2050 strSql = util::string_format("delete from SignalData where SignalDateTime<%llu;", trim_last_sec);
2051 bool done_good = true;
2052 if (m_pDB->Connected() == 0)
2053 {
2054 m_pDB->Connect();
2055 }
2056 IBPP::Transaction tr = IBPP::TransactionFactory(m_pDB);
2057 try
2058 {
2059 tr->Start();
2060 IBPP::Statement st = IBPP::StatementFactory(m_pDB, tr);
2061 st->Prepare(strSql);
2062 st->Execute();
2063
2064 }
2065 catch (IBPP::Exception &e)
2066 {
2067 printf("SQL error: %s\n", e);
2068 done_good = false;
2069 }
2070 tr->Commit();
2071 //rc = sqlite3_exec(m_pDB, strSql.c_str(), callback, 0, &zErrMsg);
2072
2073 if (done_good)
2074 {
2075 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("DB trimmed successfully"));
2076 }
2077
2078 sql = "vacuum";
2079 done_good = true;
2080 if (m_pDB->Connected() == 0)
2081 {
2082 m_pDB->Connect();
2083 }
2084 IBPP::Transaction tr_1 = IBPP::TransactionFactory(m_pDB);
2085 try
2086 {
2087 tr_1->Start();
2088 IBPP::Statement st = IBPP::StatementFactory(m_pDB, tr_1);
2089 st->Prepare(sql);
2090 st->Execute();
2091
2092 }
2093 catch (IBPP::Exception &e)
2094 {
2095 printf("SQL error: %s\n", e);
2096 done_good = false;
2097 }
2098 tr_1->Commit();
2099 //rc = sqlite3_exec(m_pDB, sql, callback, 0, &zErrMsg);
2100
2101 if (done_good)
2102 {
2103 m_pArchiver->StoreMessage(EventLevel::elTrace, util::string_format("DB vacuumed successfully"));
2104 printf("DB vacuumed successfully\n");
2105 }
2106 }
2107
2108 delete sd_stop;
2109 }
2110
2111 delete sd_start;
2112
2113 return Result(Result::STATUS_SUCCESS);
2114}