A messenger application for Raspberry Pi Zerofor A.U.TH (Real time Embedded systems).
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 

657 Zeilen
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. //! @}