A messenger application for Raspberry Pi Zerofor A.U.TH (Real time Embedded systems).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

657 lines
18 KiB

  1. /*!
  2. * \file core.c
  3. * Core messenger functionality
  4. * \author Christos Choutouridis AEM:8997 <cchoutou@ece.auth.gr>
  5. */
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdarg.h>
  10. #include <pthread.h>
  11. #include "core.h"
  12. //! Global data
  13. //! @{
  14. msgList_t msgList; //!< The message list for our application.
  15. //! @}
  16. /*
  17. * Local data types
  18. */
  19. static pthread_mutex_t lock_msgList; //!< mutex for msgList locking
  20. static pthread_mutex_t lock_stderr; //!< mutex for stderr locking
  21. static pthread_mutex_t lock_stdout; //!< mutex for stderr locking
  22. static pthread_mutex_t lock_stats; //!< mutex for stats locking
  23. //! Helper API
  24. //! @{
  25. //! Macro to create a in_addr_t
  26. #define _ADHOC_SUBNET(A, B, C, D) (((A)<<24) | ((B)<<16) | ((C)<<8) | (D))
  27. /*!
  28. * in_addr_t to devAEM_t conversion utility function
  29. * @param in_addr Internet address (host byte order)
  30. * @return devAEM_t representation of the address
  31. * @example
  32. * @code{.c}
  33. * devAEM_t dev = addr2devAEM (ntohl(clntAddr.sin_addr.s_addr));
  34. * @endcode
  35. */
  36. devAEM_t addr2devAEM (in_addr_t in_addr) {
  37. return (in_addr & 0x000000FF) + ((in_addr >> 8) & 0x000000FF) * 100;
  38. }
  39. /*!
  40. * devAEM_t to in_addr_t conversion utility function
  41. * @param dev devAEM_t address
  42. * @return Internet address (host byte order)
  43. * @example
  44. * @code{.c}
  45. * sockaddr_in_t srvAddr;
  46. * srvAddr.sin_addr.s_addr = htonl (devAEM2addr (dev));
  47. * @endcode
  48. */
  49. in_addr_t devAEM2addr (devAEM_t dev) {
  50. uint32_t add = _ADHOC_SUBNET (ADHOC_NET_A, ADHOC_NET_B, ADHOC_NET_C, ADHOC_NET_D);
  51. add |= (dev % 100) & 0x000000FF;
  52. add |= ((dev / 100) & 0x000000FF) << 8;
  53. return add;
  54. }
  55. /*!
  56. * DevIP_t to devAEM_t conversion utility function
  57. * @param ip pointer to devIP_t type address
  58. * @return devAEM_t representation of the address
  59. * @note
  60. * We discard the first 2 fields
  61. */
  62. devAEM_t ip2AEM (devIP_t* ip) {
  63. return ip->C*100 + ip->D;
  64. }
  65. /*!
  66. * devAEM_t to DevIP_t conversion utility function
  67. * @param dev devAEM_t representation of the address
  68. * @return devIP_t type address
  69. */
  70. devIP_t AEM2ip (devAEM_t dev) {
  71. devIP_t ip = {
  72. .A =ADHOC_NET_A, .B=ADHOC_NET_B, .C=dev/100, .D=dev%100
  73. };
  74. return ip;
  75. }
  76. //! @}
  77. /*
  78. * Log related local data
  79. */
  80. static char_t* _frm_msg_in = "In from dev=%d, message: from=%d, to=%d, timestamp=%lld, text=%s\n";
  81. static char_t* _frm_msg_out = "Out to dev=%d, message: from=%d, to=%d, timestamp=%lld, text=%s\n";
  82. static char_t* _frm_msg_new = "New message: from=%d, to=%d, timestamp=%lld, text=%s\n";
  83. //! log API
  84. //! @{
  85. #define _HEAD_SIZE 25
  86. /*!
  87. * Initialize log functionality
  88. * @return The status of the operation
  89. */
  90. status_t log_init (void) {
  91. // Try to initialize pthreads for logging
  92. if (pthread_mutex_init(&lock_stderr, NULL) != 0) {
  93. fprintf (stderr, "Error %s: mutex init has failed\n", __FUNCTION__ );
  94. return MSG_ERROR;
  95. }
  96. if (pthread_mutex_init(&lock_stdout, NULL) != 0) {
  97. fprintf (stderr, "Error %s: mutex init has failed\n", __FUNCTION__ );
  98. return MSG_ERROR;
  99. }
  100. return MSG_OK;
  101. }
  102. /*!
  103. * Logs incoming messages
  104. * @param msg Pointer to msg to log
  105. * @note
  106. * This function depends on \sa settings.outLevel
  107. */
  108. void log_msg_in (msg_t* msg) {
  109. if (settings.outLevel >= OUTLEVEL_1) {
  110. pthread_mutex_lock(&lock_stdout); // lock stdout
  111. fprintf (stdout, _frm_msg_in, // print and flush
  112. msg->sender,
  113. msg->cMsg.from,
  114. msg->cMsg.to,
  115. msg->cMsg.ts,
  116. msg->cMsg.text
  117. );
  118. fflush(stdout);
  119. pthread_mutex_unlock(&lock_stdout); // unlock stdout
  120. }
  121. }
  122. /*!
  123. * Logs outgoing messages
  124. * @param msg Pointer to msg to log
  125. * @param dev device that accepts the message
  126. * @note
  127. * This function depends on \sa settings.outLevel
  128. */
  129. void log_msg_out (msg_t* msg, devAEM_t dev) {
  130. if (settings.outLevel >= OUTLEVEL_1) {
  131. pthread_mutex_lock(&lock_stdout); // lock stdout
  132. fprintf (stdout, _frm_msg_out, // print and flush
  133. dev,
  134. msg->cMsg.from,
  135. msg->cMsg.to,
  136. msg->cMsg.ts,
  137. msg->cMsg.text
  138. );
  139. fflush(stdout);
  140. pthread_mutex_unlock(&lock_stdout); // unlock stdout
  141. }
  142. }
  143. /*!
  144. * Logs new messages
  145. * @param msg Pointer to msg to log
  146. * @note
  147. * This function depends on \sa settings.outLevel
  148. */
  149. void log_msg_new (msg_t* msg) {
  150. if (settings.outLevel >= OUTLEVEL_1) {
  151. pthread_mutex_lock(&lock_stdout); // lock stdout
  152. fprintf (stdout, _frm_msg_new, // print and flush
  153. msg->cMsg.from,
  154. msg->cMsg.to,
  155. msg->cMsg.ts,
  156. msg->cMsg.text
  157. );
  158. fflush(stdout);
  159. pthread_mutex_unlock(&lock_stdout); // unlock stdout
  160. }
  161. }
  162. /*!
  163. * Debug message log to stdout
  164. * Variadic formating print
  165. * @param fmt Pointer to printf like format string
  166. */
  167. void log_debug (const char *fmt, ...) {
  168. if (settings.outLevel >= OUTLEVEL_2) {
  169. va_list ap;
  170. va_start(ap, fmt);
  171. pthread_mutex_lock(&lock_stdout);
  172. vfprintf (stdout, fmt, ap);
  173. fflush(stdout);
  174. pthread_mutex_unlock(&lock_stdout);
  175. va_end(ap);
  176. }
  177. }
  178. /*!
  179. * Debug error message log to stderr
  180. * Variadic formating print
  181. * @param fmt Pointer to printf like format string
  182. */
  183. void log_error (const char *fmt, ...) {
  184. va_list ap;
  185. va_start(ap, fmt);
  186. pthread_mutex_lock(&lock_stderr);
  187. vfprintf (stderr, fmt, ap);
  188. fflush(stderr);
  189. pthread_mutex_unlock(&lock_stderr);
  190. va_end(ap);
  191. }
  192. //! @}
  193. //! cMsg_t API
  194. //! @{
  195. /*!
  196. * Make a new message
  197. * @param msg Pointer to message to create
  198. */
  199. void cMsg_make (cMsg_t* msg) {
  200. static int msgID =0; // unique msg ID
  201. msg->from = settings.me; // from me
  202. do {
  203. // randomly select recipient device
  204. msg->to = devList[rand() % AEMLIST_SIZE].dev;
  205. } while (msg->to == settings.me);
  206. msg->ts = time(NULL);
  207. // stream the first fields and take the quote text iterator
  208. sprintf (msg->text, "%s #%d", MESSAGE_BODY, msgID++);
  209. }
  210. /*!
  211. * Message concatenation
  212. * This function synthesize the ascii message for transmission
  213. * @param msg Pointer to message to serialize
  214. * @param buffer Pointer to output buffer
  215. * @return The size of the resulting message
  216. */
  217. size_t cMsg_serialize (cMsg_t* msg, char_t* buffer) {
  218. return sprintf (buffer, "%d_%d_%lld_%s",
  219. msg->from,
  220. msg->to,
  221. msg->ts,
  222. msg->text
  223. );
  224. }
  225. /*!
  226. * Custom strtok functionality
  227. * @param str Pointer to string to parse
  228. * @param max The size of string
  229. * @param c Single delimiter character (non compatible with strtok())
  230. * @return Pointer to token (non compatible with strtok())
  231. * @note
  232. * This function alters the given string by adding null termination in the places
  233. * of delimiters
  234. */
  235. char_t* _strtok (char_t* str, size_t max, char_t c) {
  236. static char_t* last = NULL;
  237. char_t* ret = str;
  238. // init last
  239. if (str != NULL) last = str;
  240. // loop
  241. for (size_t i=0 ; i<max ; ++i) {
  242. if (*last == c) {
  243. *last++ = 0;
  244. return ret;
  245. }
  246. ++last;
  247. }
  248. return NULL;
  249. }
  250. /*!
  251. * Parse an incoming message
  252. *
  253. * @param cMsg Pointer to cMsg object to store the parsed data
  254. * @param rawMsg Pointer to raw message
  255. * @param size The size f raw message buffer
  256. * @return The status of the operation
  257. * @arg MSG_OK Success
  258. * @arg MSG_ERROR Parse failure, incoming message format error
  259. */
  260. status_t cMsg_parse (cMsg_t* cMsg, char_t* rawMsg, size_t size) {
  261. // Check message integrity
  262. if (size > MSG_TEXT_SIZE)
  263. return MSG_ERROR;
  264. // Parse message
  265. char_t* rest = rawMsg;
  266. char_t* tok[4];
  267. bool_t done = true;
  268. for (size_t i =0; i < 3; ++i) {
  269. tok[i] = _strtok (rest, size, MSG_DELIMITER);
  270. if (tok[i] == NULL) {
  271. done = false;
  272. break;
  273. }
  274. else {
  275. int l = strlen(rest);
  276. rest += l + 1;
  277. size -= l + 1;
  278. }
  279. }
  280. tok[3] = rest;
  281. if (done) {
  282. cMsg->from = atoi (tok[0]);
  283. cMsg->to = atoi (tok[1]);
  284. cMsg->ts = atoi (tok[2]);
  285. strcpy (cMsg->text, tok[3]);
  286. return MSG_OK;
  287. }
  288. return MSG_ERROR;
  289. }
  290. /*! getter for cMsg_t member fromAEM */
  291. uint32_t cMsg_getFrom(cMsg_t* cMsg) { return cMsg->from; }
  292. /*! getter for cMsg_t member toAEM */
  293. uint32_t cMsg_getTo(cMsg_t* cMsg) { return cMsg->to; }
  294. /*! getter for cMsg_t member fromAEM */
  295. uint64_t cMsg_getTs(cMsg_t* cMsg) { return cMsg->ts; }
  296. /*! getter for payload text member */
  297. char_t* cMsg_getText(cMsg_t* cMsg) { return cMsg->text; }
  298. /*!
  299. * Predicate to check core message equality
  300. * @param m1 Pointer to message 1
  301. * @param m2 Pointer to message 2
  302. * @return Equality result (true, false)
  303. */
  304. bool_t cMsg_equal (cMsg_t* m1, cMsg_t* m2) {
  305. if (m1->from != m2->from) return false;
  306. if (m1->to != m2->to) return false;
  307. if (m1->ts != m2->ts) return false;
  308. if (strncmp (m1->text, m2->text, sizeof(m1->text)))
  309. return false;
  310. return true;
  311. }
  312. //! @}
  313. /*!
  314. * mgs_t API
  315. */
  316. //! @{
  317. /*!
  318. * message initialization
  319. * @param msg
  320. */
  321. void msg_init (msg_t* msg) {
  322. memset ((void*)msg, 0, sizeof(msg_t)); // init to 0
  323. }
  324. //! @}
  325. //! msgList API
  326. //! @{
  327. /*! Macro helper to saturate increased values */
  328. #define _top_saturate(test, apply, value) do { \
  329. if (test >= value) apply = value; \
  330. } while (0)
  331. /*! Macro helper to saturate decreased values */
  332. #define _btm_saturate(test, apply, value) do { \
  333. if (test < value) apply = value; \
  334. while (0)
  335. /*!
  336. * Returns an iterator for \sa devList AND \sa msg_t.recipients
  337. * @param dev The device to search
  338. * @return The iterator, namely the index to devList array in which is the \p dev
  339. */
  340. dIter_t devList_getIter (devAEM_t dev) {
  341. for (dIter_t i =0 ; i<AEMLIST_SIZE ; ++i) {
  342. if (devList[i].dev == dev)
  343. return i;
  344. }
  345. return -1; // return end()
  346. }
  347. /*!
  348. * Initialize the msgList
  349. * @param msgList Pointer to mesList t initialize
  350. * @return The status of the operation
  351. */
  352. status_t msgList_init (msgList_t* msgList) {
  353. if (pthread_mutex_init(&lock_msgList, NULL) != 0) {
  354. log_error ("Error: mutex init has failed\n");
  355. return MSG_ERROR;
  356. }
  357. memset((void*)msgList, 0, sizeof(msgList_t));
  358. msgList->first =-1;
  359. msgList->last =-1;
  360. srand (time(NULL));
  361. return MSG_OK;
  362. }
  363. /*!
  364. * @brief msgList iterator pre-increment in the msg_t direction
  365. *
  366. * This iterator force a ring buffer behavior. This function takes pointer
  367. * to iterator to alter but return the altered value so it can be directly
  368. * used in expressions
  369. *
  370. * @param it Pointer to iterator to increase
  371. * @return The iterator values
  372. */
  373. mIter_t msgList_preInc (mIter_t* it) {
  374. if (++*it >= MSG_LIST_SIZE) *it = 0;
  375. return *it;
  376. }
  377. /*!
  378. * @brief msgList iterator pre-decrement in the msg_t direction
  379. *
  380. * This iterator force a ring buffer behavior. This function takes pointer
  381. * to iterator to alter but return the altered value so it can be directly
  382. * used in expressions
  383. *
  384. * @param it Pointer to iterator to decrease
  385. * @return The iterator values
  386. */
  387. mIter_t msgList_preDec (mIter_t* it) {
  388. if (--*it < 0) *it = MSG_LIST_SIZE;
  389. return *it;
  390. }
  391. /*!
  392. * @param this Pointer to msgList to use
  393. * @return An iterator to the first message of \sa MSG_LIST_SIZE
  394. * of msgList.m[]
  395. * @note
  396. * As the msgList is a ring buffer we can not have a sensible end() iterator.
  397. * end() will eventually merge with begin() when the size reaches \sa MSG_LIST_SIZE
  398. * For that reason in all of our loops through msgList we shall take a begin()
  399. * iterator and loop using size as sentinel
  400. * @example
  401. * @code{.c}
  402. * mIter_t it = msgList_begin (&msgList); // get a message iterator
  403. * size_t size= msgList_size(&msgList); // get current msgList size
  404. * for (size_t i=0 ; i<size ; ++i, msgList_preInc (&it)) { // don't forget to increase iterator
  405. * // use msgList[it]
  406. * }
  407. * @endcode
  408. */
  409. mIter_t msgList_begin (msgList_t* this) {
  410. return this->first;
  411. }
  412. /*!
  413. * @param this Pointer to msgList to use
  414. * @return An iterator to the last inserted message of \sa MSG_LIST_SIZE
  415. * of msgList.m[]
  416. */
  417. mIter_t msgList_last (msgList_t* this) {
  418. return this->last;
  419. }
  420. /*!
  421. * @param this Pointer to msgList to use
  422. * @return The current used slots of msgList
  423. * @note
  424. * As the msgList is a ring buffer we can not have a sensible end() iterator.
  425. * end() will eventually merge with begin() when the size reaches \sa MSG_LIST_SIZE
  426. * For that reason in all of our loops through msgList we shall take a begin()
  427. * iterator and loop using size as sentinel
  428. * @example
  429. * @code{.c}
  430. * mIter_t it = msgList_begin (&msgList); // get a message iterator
  431. * size_t size= msgList_size(&msgList); // get current msgList size
  432. * for (size_t i=0 ; i<size ; ++i, msgList_preInc (&it)) { // don't forget to increase iterator
  433. * // use msgList[it]
  434. * }
  435. * @endcode
  436. */
  437. size_t msgList_size (msgList_t* this) {
  438. return this->size;
  439. }
  440. /*!
  441. * Searches for a message in the message list.
  442. *
  443. * @param this The msgList object to work with
  444. * @param msg Pointer to message to search
  445. * @return Iterator to message if found, or -1 if not
  446. */
  447. mIter_t msgList_find (msgList_t* this, msg_t* msg) {
  448. mIter_t it =this->last; // get iterator
  449. // search from end to start to find msg, return on success
  450. for (size_t i=0 ; i < this->size ; ++i) {
  451. if (cMsg_equal (&this->m[it].cMsg, &msg->cMsg))
  452. return it;
  453. msgList_preDec(&it);
  454. // We start from the end as we think, its more possible
  455. // to find msg in the recent messages.
  456. }
  457. return (mIter_t)-1; // fail to find
  458. }
  459. /*!
  460. * Add a new message in the message list
  461. *
  462. * @param this The msgList object to work with
  463. * @param msg Pointer to message
  464. * @return Iterator to the added item (last)
  465. */
  466. mIter_t msgList_add (msgList_t* this, msg_t* msg) {
  467. if (this->first == -1) // if its first time init "first" iterator
  468. this->first = 0;
  469. this->m[msgList_preInc(&this->last)] = *msg; // store data *++it = *msg;
  470. _top_saturate(++this->size, this->size, MSG_LIST_SIZE); // count the items with saturation
  471. // if we reacher full capacity, move along first also
  472. if ((this->first == this->last) && (this->size > 1))
  473. msgList_preInc(&this->first);
  474. return this->last; // return the iterator to newly created slot
  475. }
  476. //! Acquires msgList resources
  477. void msgList_acquire () { pthread_mutex_lock (&lock_msgList); }
  478. //! releases msgList resources
  479. void msgList_release () { pthread_mutex_unlock (&lock_msgList); }
  480. //! @}
  481. //! Statistics API
  482. //! @{
  483. /*!
  484. * Initialize statistics
  485. * @param s Pointer to \sa stat_t struct to initialize
  486. * @return The status of the operation
  487. */
  488. status_t stats_init (stats_t* s) {
  489. memset ((void*)s, 0, sizeof (stats_t));
  490. if (pthread_mutex_init(&lock_stats, NULL) != 0) {
  491. log_error ("Error: mutex init has failed\n");
  492. return MSG_ERROR;
  493. }
  494. return MSG_OK;
  495. }
  496. /*!
  497. * Update statistics for newly created messages
  498. * @param msg Pointer to msg
  499. */
  500. void statsUpdateCreate (msg_t* msg) {
  501. pthread_mutex_lock (&lock_stats);
  502. ++stats.totalMsg;
  503. ++stats.myMsg;
  504. // average message size
  505. int32_t saved = stats.totalMsg - stats.duplicateMsg;
  506. if ((saved-1) > 0) {
  507. // Append to average
  508. int32_t l = strlen(msg->cMsg.text);
  509. stats.avMsgSize += l / (fpdata_t)(saved -1);
  510. stats.avMsgSize *= (fpdata_t)(saved-1)/saved;
  511. }
  512. pthread_mutex_unlock (&lock_stats);
  513. }
  514. /*!
  515. * Update statistics for incoming message
  516. * @param msg Pointer to incoming message
  517. * @param dup Flag to indicate if the message was duplicate
  518. */
  519. void statsUpdateIn (msg_t* msg, bool_t dup) {
  520. pthread_mutex_lock (&lock_stats);
  521. bool_t forMe = msg->cMsg.to == settings.me;
  522. stats.totalMsg++;
  523. stats.duplicateMsg += (dup) ? 1:0;
  524. stats.forMeMsg += (forMe) ? 1:0;
  525. stats.inDirectMsg += (forMe && (msg->cMsg.from == msg->sender)) ? 1:0;
  526. // averages
  527. int32_t saved = stats.totalMsg - stats.duplicateMsg;
  528. if ((saved-1) > 0) {
  529. // Append to message size average
  530. int32_t l = strlen(msg->cMsg.text);
  531. stats.avMsgSize += l / (fpdata_t)(saved -1);
  532. stats.avMsgSize *= (fpdata_t)(saved-1)/saved;
  533. if (settings.trackTime) {
  534. // append to time to me average
  535. tstamp_t dt = (tstamp_t)time(NULL) - msg->cMsg.ts;
  536. if (dt < 0)
  537. dt = 0;
  538. stats.avTimeToMe += dt / (fpdata_t)(saved -1);
  539. stats.avTimeToMe *= (fpdata_t)(saved-1)/saved;
  540. }
  541. }
  542. pthread_mutex_unlock (&lock_stats);
  543. }
  544. /*!
  545. * Update statistics for outgoing message
  546. * @param msg Pointer to message
  547. * @param dev The recipient device
  548. */
  549. void statsUpdateOut (msg_t* msg, devAEM_t dev) {
  550. pthread_mutex_lock (&lock_stats);
  551. stats.outDirectMsg += (msg->cMsg.to == dev) ? 1:0;
  552. pthread_mutex_unlock (&lock_stats);
  553. }
  554. /*!
  555. * Statistics print functionality
  556. * @param stats Pointer to stats to print
  557. * @return The status of the operation
  558. */
  559. status_t statsPrint (stats_t* stats) {
  560. FILE* fp = fopen ("statistics.txt", "w");
  561. if (fp == NULL) {
  562. fclose (fp);
  563. return MSG_ERROR;
  564. }
  565. fprintf (fp, "\n Statistics\n============\n");
  566. fprintf (fp, "Total messages: %d\n", stats->totalMsg);
  567. fprintf (fp, "Duplicate messages: %d\n", stats->duplicateMsg);
  568. fprintf (fp, "Messages for me: %d\n", stats->forMeMsg);
  569. fprintf (fp, "Messages by me: %d\n",stats->myMsg);
  570. fprintf (fp, "In messages direct for me: %d\n", stats->inDirectMsg);
  571. fprintf (fp, "Out direct messages: %d\n", stats->outDirectMsg);
  572. fprintf (fp, "Average message size: %g\n", stats->avMsgSize);
  573. fprintf (fp, "Average time to me: %g\n", stats->avTimeToMe);
  574. for (size_t i =0 ; i<AEMLIST_SIZE ; ++i) {
  575. fprintf (fp, " Device %u found on %lld, last: %lld\n",
  576. devList[i].dev, devList[i].begin, devList[i].end);
  577. }
  578. fclose (fp);
  579. return MSG_OK;
  580. }
  581. //! @}