main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. #include <signal.h>
  4. #include <unistd.h>
  5. #include <linux/limits.h>
  6. #include "projal.h"
  7. #include "logfile.h"
  8. #include "fileutil.h"
  9. #include "strutil.h"
  10. #include "instance.h"
  11. #include "processclock.h"
  12. #include "debug.h"
  13. ////////////////////////////////////////////////////////////////////////////////////////////////////
  14. // app control
  15. #define _APPID GFA_APPCTRL_APPID_DATALOGGER
  16. #define _APPNAME "Datalogger"
  17. #define _DEPENDENCIES ((appid_t)(GFA_APPCTRL_APPID_REMANENT))
  18. ////////////////////////////////////////////////////////////////////////////////////////////////////
  19. #define _TRACK_TIME 1
  20. #define _WRITE_PAST_NULL_AT_STARTUP 1
  21. #define _LOGFILE_NAME "datalogger.log"
  22. #define _SIG_BLOCK(s) sigprocmask(SIG_BLOCK, (s), NULL)
  23. #define _SIG_UNBLOCK(s) sigprocmask(SIG_UNBLOCK, (s), NULL)
  24. #define BYTES_FROM_MiB(mib) ((mib) * 1024 * 1024)
  25. ////////////////////////////////////////////////////////////////////////////////////////////////////
  26. static volatile bool g_fRun = false;
  27. static volatile bool g_fPauseImp = false;
  28. static volatile bool g_fPauseCmd = false;
  29. static volatile bool g_fZombie = false;
  30. static appid_t g_nDepRunning = 0;
  31. static sigset_t g_set;
  32. static CLogfile g_lf;
  33. int g_nLastSig = -1;
  34. ////////////////////////////////////////////////////////////////////////////////////////////////////
  35. ////////////////////////////////////////////////////////////////////////////////////////////////////
  36. //
  37. static const char* _GetBaseDir(std::string &rstrBaseDir)
  38. {
  39. char szBaseDir[PATH_MAX];
  40. const char *pszBaseDir = NULL;
  41. #ifdef _LOG_BASE_DIR
  42. pszBaseDir = _LOG_BASE_DIR;
  43. if(!pszBaseDir || !*pszBaseDir || !::DirectoryExist(pszBaseDir))
  44. {
  45. CLogfile::StdErr("Invalid base directory config! Using app directory!\n");
  46. pszBaseDir = ::GetAppDirectory(szBaseDir, sizeof(szBaseDir));
  47. }
  48. rstrBaseDir = pszBaseDir;
  49. #else // _LOG_BASE_DIR
  50. UNUSED(pszBaseDir);
  51. rstrBaseDir = ::GetAppDirectory(szBaseDir, sizeof(szBaseDir));
  52. #endif // _LOG_BASE_DIR
  53. rtrim(rstrBaseDir, "/");
  54. return rstrBaseDir.c_str();
  55. }
  56. static void _SigHandler(int sig)
  57. {
  58. g_nLastSig = sig;
  59. g_fRun = g_fPauseImp = g_fPauseCmd = g_fZombie = false;
  60. }
  61. ////////////////////////////////////////////////////////////////////////////////////////////////////
  62. static void _ProcessCtrlMessages(HAPPCTRL hAC, HAPPINFO hAI)
  63. {
  64. ctrlmsg_t nCtrlMsg;
  65. while(g_fRun && (nCtrlMsg = ::GfaIpcAppCtrlGetNextCtrlMsg(hAI)))
  66. {
  67. switch(nCtrlMsg)
  68. {
  69. case GFA_APPCTRL_CTRLMSG_STOP:
  70. g_fRun = false;
  71. g_fPauseImp = false;
  72. g_fPauseCmd = false;
  73. g_fZombie = false;
  74. g_lf.Info("Received Control Message 'Stop'\n");
  75. return;
  76. case GFA_APPCTRL_CTRLMSG_PAUSE:
  77. if(!g_fPauseCmd)
  78. {
  79. g_fPauseCmd = true;
  80. if(!g_fPauseImp)
  81. {
  82. ::GfaIpcAppCtrlSetState(hAC, GIAS_Paused);
  83. g_lf.Info("Received Control Message 'Pause'\n");
  84. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Paused));
  85. TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Paused));
  86. }
  87. }
  88. break;
  89. case GFA_APPCTRL_CTRLMSG_RESUME:
  90. if(g_fPauseCmd)
  91. {
  92. g_fPauseCmd = false;
  93. if(!g_fPauseImp)
  94. {
  95. g_lf.Info("Received Control Message 'Resume'\n");
  96. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Running));
  97. ::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
  98. TRACE("%-8s: State: %s\n", "Me", ::GfaIpcAppCtrlGetStateText(GIAS_Running));
  99. }
  100. }
  101. break;
  102. default:
  103. break;
  104. }
  105. }
  106. }
  107. ////////////////////////////////////////////////////////////////////////////////////////////////////
  108. static void _ProcessStateEvents(HAPPCTRL hAC, HAPPINFO hAI)
  109. {
  110. appid_t nAppIdSrc;
  111. bool fOldPaused = g_fPauseImp;
  112. char szDispName[128];
  113. while(g_fRun && (nAppIdSrc = ::GfaIpcAppCtrlGetNextStateEvtSrc(hAI)))
  114. {
  115. GfaIpcAppStates state = ::GfaIpcAppCtrlGetState(hAC, nAppIdSrc);
  116. GfaIpcAppCtrlGetDisplayName(hAC, nAppIdSrc, szDispName, sizeof(szDispName));
  117. TRACE("%-8s: State: %s\n", szDispName, ::GfaIpcAppCtrlGetStateText(state));
  118. if(nAppIdSrc & _DEPENDENCIES)
  119. {
  120. if(state == GIAS_Running)
  121. {
  122. g_lf.Info("%s -> %s.\n", szDispName, ::GfaIpcAppCtrlGetStateText(state));
  123. g_nDepRunning |= nAppIdSrc;
  124. }
  125. else
  126. {
  127. g_lf.Warning("%s -> %s.\n", szDispName, ::GfaIpcAppCtrlGetStateText(state));
  128. g_nDepRunning &= ~nAppIdSrc;
  129. }
  130. }
  131. }
  132. if(g_fRun)
  133. {
  134. g_fPauseImp = (g_nDepRunning != _DEPENDENCIES);
  135. if(!g_fPauseCmd && (fOldPaused != g_fPauseImp))
  136. {
  137. fOldPaused = g_fPauseImp;
  138. GfaIpcAppStates newState = g_fPauseImp ? GIAS_Paused : GIAS_Running;
  139. ::GfaIpcAppCtrlSetState(hAC, newState);
  140. if(g_fPauseImp)
  141. g_lf.Warning("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(newState));
  142. else
  143. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(newState));
  144. }
  145. }
  146. }
  147. ////////////////////////////////////////////////////////////////////////////////////////////////////
  148. //
  149. int main(int argc, char **argv)
  150. {
  151. int nRet = -1;
  152. HSHM hShm = NULL;
  153. HAPPCTRL hAC = NULL;
  154. HAPPINFO hAI;
  155. void *pShm = NULL;
  156. DLPARAMS dlp;
  157. CProcessInstance pi;
  158. unsigned long nSamplesPerLog, nLogsPerFlush;
  159. char szLogFile[PATH_MAX];
  160. std::string strBaseDir;
  161. const char *pszBaseDir = NULL;
  162. #if _TRACK_TIME
  163. unsigned long long nElapsed;
  164. double fTime;
  165. CProcessClock pc;
  166. #endif // _TRACK_TIME
  167. unsigned long long nUsecWorkTime = 0;
  168. CProcessClock pcWork;
  169. ////////////////////////////////////////////////////////////////////////////////////////////////
  170. // check for multiple instances
  171. if(!pi.LockInstance(UUID_SHM))
  172. {
  173. CLogfile::StdErr("Failed to start instance!\n");
  174. return -1;
  175. }
  176. ////////////////////////////////////////////////////////////////////////////////////////////////
  177. // configure signal handling
  178. struct sigaction sa;
  179. ::sigfillset(&g_set);
  180. sigaddset(&g_set, SIGUSR1);
  181. memset(&sa, 0, sizeof(sa));
  182. sa.sa_handler = _SigHandler;
  183. sigaction(SIGHUP, &sa, NULL); // handles user's terminal disconnect
  184. sigaction(SIGQUIT, &sa, NULL); // handles Ctrl + '\'
  185. sigaction(SIGTERM, &sa, NULL); // handles normal termination
  186. sigaction(SIGABRT, &sa, NULL); // handles abnormal termination (i.e. abort())
  187. sigaction(SIGINT, &sa, NULL); // handles Ctrl + 'C'
  188. sa.sa_handler = SIG_IGN;
  189. sigaction(SIGTSTP, &sa, NULL); // ignores Ctrl + 'Z'
  190. sigaction(SIGSTOP, &sa, NULL); // ignores Stop
  191. sigaction(SIGCONT, &sa, NULL); // ignores Continue
  192. sigaction(SIGCHLD, &sa, NULL); // ignores child process termination
  193. sigaction(0, &sa, NULL); // ignores shell termination
  194. do
  195. {
  196. g_fZombie = true;
  197. ////////////////////////////////////////////////////////////////////////////////////////////
  198. // get the base directory for output files
  199. if(!pszBaseDir)
  200. pszBaseDir = _GetBaseDir(strBaseDir);
  201. CLogfile::StdOut("Using base directory \"%s\".\n", pszBaseDir);
  202. ////////////////////////////////////////////////////////////////////////////////////////////
  203. // initialize log file
  204. sprintf(szLogFile, "%s/%s", pszBaseDir, _LOGFILE_NAME);
  205. if(!g_lf.Open(szLogFile, true, CLogfile::VB_Inf))
  206. {
  207. CLogfile::StdErr("Failed to create/open log file!\n");
  208. break;
  209. }
  210. g_lf.Info("Process enter.\n");
  211. ////////////////////////////////////////////////////////////////////////////////////////////
  212. // initialize app control
  213. g_lf.Info("Acquire AppCtrl-Handle.\n");
  214. if(!(hAC = ::GfaIpcAppCtrlAcquire(_APPID, _APPNAME, _LOG_INTV_SAMPLE * 1000, _LOG_INTV_SAMPLE * 3000)))
  215. {
  216. g_lf.Error("Failed to acquire AppCtrl-Handle!\n");
  217. break;
  218. }
  219. ::GfaIpcAppCtrlSetState(hAC, GIAS_Initializing);
  220. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Initializing));
  221. if(!::GfaIpcAppCtrlSubscribeStateEvents(hAC, _DEPENDENCIES))
  222. {
  223. g_lf.Error("Failed to subscribe state event notifications!\n");
  224. break;
  225. }
  226. ////////////////////////////////////////////////////////////////////////////////////////////
  227. // validate config parameters
  228. if(strlen(_LOG_DB_NAME) >= _DL_MAX_DB_NAME_LENGTH)
  229. {
  230. g_lf.Error("Database name too long!\n");
  231. break;
  232. }
  233. if(strlen(_LOG_DB_USER) >= _DL_MAX_DB_USER_LENGTH)
  234. {
  235. g_lf.Error("Database username too long!\n");
  236. break;
  237. }
  238. if(strlen(_LOG_DB_PASS) >= _DL_MAX_DB_PASS_LENGTH)
  239. {
  240. g_lf.Error("Database password too long!\n");
  241. break;
  242. }
  243. if(strlen(_LOG_TAGS_TABLE) >= _DL_MAX_TABLE_NAME_LENGTH)
  244. {
  245. g_lf.Error("Tag table name too long!\n");
  246. break;
  247. }
  248. if(strlen(_LOG_LOGS_TABLE) >= _DL_MAX_TABLE_NAME_LENGTH)
  249. {
  250. g_lf.Error("Log table name too long!\n");
  251. break;
  252. }
  253. if((_LOG_INTV_LOG < _LOG_INTV_SAMPLE) || (_LOG_INTV_LOG % _LOG_INTV_SAMPLE))
  254. {
  255. g_lf.Error("The log interval must be an integer multiple of the sample interval!\n");
  256. break;
  257. }
  258. if((_LOG_INTV_WRITE < _LOG_INTV_LOG) || (_LOG_INTV_WRITE % _LOG_INTV_LOG))
  259. {
  260. g_lf.Error("The flush interval must be an integer multiple of the log interval!\n");
  261. break;
  262. }
  263. nSamplesPerLog = _LOG_INTV_LOG / _LOG_INTV_SAMPLE;
  264. nLogsPerFlush = _LOG_INTV_WRITE / _LOG_INTV_LOG;
  265. ////////////////////////////////////////////////////////////////////////////////////////////
  266. // configure data logger
  267. memset(&dlp, 0, sizeof(dlp));
  268. strncpy(dlp.szDBName, _LOG_DB_NAME, _DL_MAX_DB_NAME_LENGTH - 1);
  269. strncpy(dlp.szDBUser, _LOG_DB_USER, _DL_MAX_DB_USER_LENGTH - 1);
  270. strncpy(dlp.szDBPass, _LOG_DB_PASS, _DL_MAX_DB_PASS_LENGTH - 1);
  271. strncpy(dlp.szTagsTable, _LOG_TAGS_TABLE, _DL_MAX_TABLE_NAME_LENGTH - 1);
  272. strncpy(dlp.szLogsTable, _LOG_LOGS_TABLE, _DL_MAX_TABLE_NAME_LENGTH - 1);
  273. snprintf(dlp.szLogsTableBD, _DL_MAX_TABLE_NAME_LENGTH - 1, "%s_bad_date", _LOG_LOGS_TABLE);
  274. dlp.nIntvSample = _LOG_INTV_SAMPLE;
  275. dlp.nIntvLog = _LOG_INTV_LOG;
  276. dlp.nIntvFlush = _LOG_INTV_WRITE;
  277. dlp.bMinMax = _LOG_INTV_MIN_MAX;
  278. dlp.pszBaseDir = pszBaseDir;
  279. #if _LOG_MAX_AGE
  280. dlp.nMaxAge = _LOG_MAX_AGE;
  281. g_lf.Info("Max. age of log entries: %lu days.\n", dlp.nMaxAge);
  282. #else // _LOG_MAX_AGE
  283. dlp.nMaxAge = 0;
  284. g_lf.Info("Max. age of log entries not defined.\n");
  285. #endif // _LOG_MAX_AGE
  286. #if _LOG_MAX_SIZE
  287. dlp.nMaxSize = BYTES_FROM_MiB(_LOG_MAX_SIZE);
  288. g_lf.Info("Max. size for logs table: %u MiB.\n", _LOG_MAX_SIZE);
  289. #else // _LOG_MAX_SIZE
  290. dlp.nMaxSize = 0;
  291. g_lf.Info("Max. size for logs table not defined.\n");
  292. #endif // _LOG_MAX_SIZE
  293. ////////////////////////////////////////////////////////////////////////////////////////////
  294. // acquire SHM
  295. if(!(hShm = ::acquire_shm(sizeof(shm_t), 1)))
  296. {
  297. g_lf.Error("Unable to acquire SHM Handle!\n");
  298. break;
  299. }
  300. g_lf.Info("Acquired SHM Handle.\n");
  301. if(!(pShm = ::GfaIpcAcquirePointer(hShm)))
  302. {
  303. g_lf.Error("Unable to acquire SHM Pointer!\n");
  304. break;
  305. }
  306. g_lf.Info("Acquired SHM Pointer.\n");
  307. ::GfaIpcDumpSHMROT();
  308. ////////////////////////////////////////////////////////////////////////////////////////////
  309. // init datalogger and SHM
  310. CDataLoggerClock dlc(_LOG_INTV_SAMPLE * 1000000);
  311. CDataLogger dl(&dlp, g_lf);
  312. CShm_t shm(pShm, hShm);
  313. if(!dl.InitDatabase(false))
  314. break;
  315. shm.InitPath(NULL, NULL);
  316. shm.InitTagID(dl);
  317. bool bTimerUnderrun = false;
  318. unsigned long nSamples = 0, nLogs = 0;
  319. time_t t = time(NULL);
  320. _SIG_BLOCK(&g_set);
  321. #if _WRITE_PAST_NULL_AT_STARTUP
  322. time_t t2 = dl.LastLogTimestamp();
  323. if(t2)
  324. shm.LogValueChanged(t2 + 1, dl, true, true);
  325. #endif // _WRITE_PAST_NULL_AT_STARTUP
  326. shm.LogValueChanged(t, dl, true, false);
  327. #if _TRACK_TIME
  328. pc.ClockTrigger();
  329. #endif // _TRACK_TIME
  330. dl.Flush(t);
  331. _SIG_UNBLOCK(&g_set);
  332. #if _TRACK_TIME
  333. nElapsed = pc.ClockGetElapsed();
  334. fTime = (double)nElapsed / 1000000.0;
  335. TRACE("Flush (%.2f ms).\n", fTime);
  336. #endif // _TRACK_TIME
  337. ////////////////////////////////////////////////////////////////////////////////////////////
  338. // enter running loop
  339. g_fZombie = false;
  340. g_fRun = true;
  341. ::GfaIpcAppCtrlSetState(hAC, GIAS_Running);
  342. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Running));
  343. while(g_fRun)
  344. {
  345. ////////////////////////////////////////////////////////////////////////////////////////
  346. // update app control info
  347. if((hAI = ::GfaIpcAppCtrlInfoUpdate(hAC, nUsecWorkTime)))
  348. {
  349. _ProcessCtrlMessages(hAC, hAI);
  350. if(!g_fRun)
  351. break;
  352. _ProcessStateEvents(hAC, hAI);
  353. }
  354. ////////////////////////////////////////////////////////////////////////////////////////
  355. if(!dlc.Sleep(bTimerUnderrun, true))
  356. {
  357. g_fRun = g_fPauseImp = g_fPauseCmd = false;
  358. g_fZombie = true;
  359. g_lf.Error("CDataLoggerClock::Sleep Error!\n", t);
  360. break;
  361. }
  362. ////////////////////////////////////////////////////////////////////////////////////////
  363. t = time(NULL);
  364. pcWork.ClockTrigger();
  365. if(!bTimerUnderrun && !g_fPauseImp && !g_fPauseCmd)
  366. {
  367. _SIG_BLOCK(&g_set);
  368. shm.LogValueChanged(t, dl, false, false);
  369. shm.Sample();
  370. ++nSamples;
  371. if(nSamples == nSamplesPerLog)
  372. {
  373. #if _TRACK_TIME
  374. pc.ClockTrigger();
  375. #endif // _TRACK_TIME
  376. shm.LogInterval(t, dl);
  377. #if _TRACK_TIME
  378. nElapsed = pc.ClockGetElapsed();
  379. fTime = (double)nElapsed / 1000000.0;
  380. TRACE("Log (%.2f ms).\n", fTime);
  381. #endif // _TRACK_TIME
  382. ++nLogs;
  383. nSamples = 0;
  384. }
  385. if(nLogs == nLogsPerFlush)
  386. {
  387. #if _TRACK_TIME
  388. pc.ClockTrigger();
  389. #endif // _TRACK_TIME
  390. dl.Flush(time(NULL));
  391. #if _TRACK_TIME
  392. nElapsed = pc.ClockGetElapsed();
  393. fTime = (double)nElapsed / 1000000.0;
  394. TRACE("Flush (%.2f ms).\n", fTime);
  395. #endif // _TRACK_TIME
  396. nLogs = 0;
  397. dl.SizeGuardTrigger(t);
  398. }
  399. _SIG_UNBLOCK(&g_set);
  400. }
  401. else if(bTimerUnderrun)
  402. {
  403. g_lf.Warning("Timer underrun (%ld)!\n", t);
  404. bTimerUnderrun = false;
  405. }
  406. nUsecWorkTime = pcWork.ClockGetElapsed() / 1000;
  407. }
  408. dl.Flush(time(NULL));
  409. }
  410. while(false);
  411. if(g_nLastSig >= 0)
  412. {
  413. g_lf.Info("Received signal '%s'.\n", strsignal(g_nLastSig));
  414. g_nLastSig = -1;
  415. }
  416. if(hShm)
  417. {
  418. if(pShm)
  419. {
  420. g_lf.Info("Releasing SHM Pointer ...\n");
  421. ::GfaIpcReleasePointer(hShm, pShm);
  422. }
  423. g_lf.Info("Releasing SHM Handle ...\n");
  424. ::GfaIpcReleaseSHM(hShm);
  425. }
  426. if(g_fZombie)
  427. {
  428. if(hAC)
  429. ::GfaIpcAppCtrlSetState(hAC, GIAS_Zombie);
  430. TRACE("Enter Zombie state ...\n");
  431. g_lf.Warning("Enter Zombie state ...\n");
  432. g_lf.Flush();
  433. pause();
  434. if(g_nLastSig >= 0)
  435. g_lf.Info("Received signal '%s'.\n", strsignal(g_nLastSig));
  436. }
  437. if(hAC)
  438. {
  439. g_lf.Info("Enter state %s ...\n", ::GfaIpcAppCtrlGetStateText(GIAS_Terminating));
  440. ::GfaIpcAppCtrlSetState(hAC, GIAS_Terminating);
  441. g_lf.Info("Releasing App Control ...\n");
  442. ::GfaIpcAppCtrlRelease(hAC);
  443. }
  444. g_lf.Info("Process exit.\n\n");
  445. g_lf.Close();
  446. CLogfile::StdErr("Datalogger exit.\n");
  447. return nRet;
  448. }