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.
 
 
 
 
 

429 lines
11 KiB

  1. /*!
  2. * \file core.c
  3. *
  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. pthread_mutex_t lock_msgList;
  13. pthread_mutex_t lock_stderr;
  14. pthread_mutex_t lock_stdout;
  15. pthread_mutex_t lock_stats;
  16. //! Helper API
  17. //! @{
  18. #define _ADHOC_SUBNET(A, B, C, D) (((A)<<24) | ((B)<<16) | ((C)<<8) | (D))
  19. devAEM_t addr2devAEM (uint32_t in_addr) {
  20. return (in_addr & 0x000000FF) + ((in_addr >> 8) & 0x000000FF) * 100;
  21. }
  22. in_addr_t devAEM2addr (devAEM_t dev) {
  23. uint32_t add = _ADHOC_SUBNET (ADHOC_NET_A, ADHOC_NET_B, ADHOC_NET_C, ADHOC_NET_D);
  24. add |= (dev % 100) & 0x000000FF;
  25. add |= ((dev / 100) & 0x000000FF) << 8;
  26. return add;
  27. }
  28. devAEM_t ip2AEM (devIP_t* ip) {
  29. return ip->C*100 + ip->D;
  30. }
  31. devIP_t AEM2ip (devAEM_t dev) {
  32. devIP_t ip = {
  33. .A =ADHOC_NET_A, .B=ADHOC_NET_B, .C=dev/100, .D=dev%100
  34. };
  35. return ip;
  36. }
  37. devIP_t addr2ip (in_addr_t in_addr) {
  38. devIP_t ip = {
  39. .A = ADHOC_NET_A,
  40. .B = ADHOC_NET_B,
  41. .C = (in_addr >> 8) & 0x000000FF,
  42. .D = in_addr & 0x000000FF
  43. };
  44. return ip;
  45. }
  46. //! @}
  47. //! log API
  48. //! @{
  49. static char_t* _frm_msg_io = "dev=%d, message: from=%d, to=%d, timestamp=%lld, text=%s";
  50. static char_t* _frm_msg_new = "new message: from=%d, to=%d, timestamp=%lld, text=%s";
  51. status_t log_init (void) {
  52. if (pthread_mutex_init(&lock_stderr, NULL) != 0) {
  53. fprintf (stderr, "Error %s: mutex init has failed\n", __FUNCTION__ );
  54. return MSG_ERROR;
  55. }
  56. if (pthread_mutex_init(&lock_stdout, NULL) != 0) {
  57. fprintf (stderr, "Error %s: mutex init has failed\n", __FUNCTION__ );
  58. return MSG_ERROR;
  59. }
  60. return MSG_OK;
  61. }
  62. void log_msg_io (msg_t* msg) {
  63. if (settings.outLevel >= OUTLEVEL_1) {
  64. char_t head[16];
  65. strncpy (head, cMsg_getText(&msg->cMsg), sizeof(head));
  66. head[16] = 0;
  67. pthread_mutex_lock(&lock_stdout);
  68. fprintf (stdout, _frm_msg_io,
  69. msg->sender,
  70. cMsg_getFrom (&msg->cMsg),
  71. cMsg_getTo (&msg->cMsg),
  72. cMsg_getTs (&msg->cMsg),
  73. head
  74. );
  75. fflush(stdout);
  76. pthread_mutex_unlock(&lock_stdout);
  77. }
  78. }
  79. void log_msg_new (msg_t* msg) {
  80. if (settings.outLevel >= OUTLEVEL_1) {
  81. char_t head[16];
  82. strncpy (head, cMsg_getText(&msg->cMsg), sizeof(head));
  83. head[16] = 0;
  84. pthread_mutex_lock(&lock_stdout);
  85. fprintf (stdout, _frm_msg_new,
  86. cMsg_getFrom (&msg->cMsg),
  87. cMsg_getTo (&msg->cMsg),
  88. cMsg_getTs (&msg->cMsg),
  89. head
  90. );
  91. fflush(stdout);
  92. pthread_mutex_unlock(&lock_stdout);
  93. }
  94. }
  95. void log_debug (const char *fmt, ...) {
  96. if (settings.outLevel >= OUTLEVEL_2) {
  97. va_list ap;
  98. va_start(ap, fmt);
  99. pthread_mutex_lock(&lock_stdout);
  100. vfprintf (stdout, fmt, ap);
  101. fflush(stdout);
  102. pthread_mutex_unlock(&lock_stdout);
  103. va_end(ap);
  104. }
  105. }
  106. void log_error (const char *fmt, ...) {
  107. va_list ap;
  108. va_start(ap, fmt);
  109. pthread_mutex_lock(&lock_stderr);
  110. vfprintf (stderr, fmt, ap);
  111. fflush(stderr);
  112. pthread_mutex_unlock(&lock_stderr);
  113. va_end(ap);
  114. }
  115. //! @}
  116. //! cMsg_t API
  117. //! @{
  118. /*!
  119. * Make a new message
  120. * @param msg Pointer to message to create
  121. */
  122. void cMsg_make (cMsg_t* msg) {
  123. static int msgID =0; // unique msg ID
  124. msg->from = settings.me; // from me
  125. msg->to = devList[rand()%AEMLIST_SIZE].dev; // randomly select device
  126. msg->ts = time(NULL);
  127. // stream the first fields and take the quote text iterator
  128. msg->text_it = sprintf (msg->text, "%d_%d_%lld_",
  129. msg->from,
  130. msg->to,
  131. msg->ts
  132. );
  133. // stream out the quote with a unique ID
  134. sprintf (&msg->text[msg->text_it], MESSAGE_BODY" #%d", msgID++);
  135. }
  136. /*!
  137. * Parse an incoming message
  138. *
  139. * @param cMsg Pointer to cMsg object to store the parsed data
  140. * @param rawMsg Pointer to raw message
  141. * @param size The size f raw message buffer
  142. * @return The status of the operation
  143. * @arg MSG_OK Success
  144. * @arg MSG_ERROR Parse failure, incoming message format error
  145. */
  146. status_t cMsg_parse (cMsg_t* cMsg, char_t* rawMsg, size_t size) {
  147. // Check message integrity
  148. if (size > MSG_TEXT_SIZE)
  149. return MSG_ERROR;
  150. int d =0;
  151. for (size_t i =0; rawMsg[i] && i<size; ++i) {
  152. d += (rawMsg[i] == MSG_DELIMITER) ? 1:0;
  153. }
  154. if (d != 3)
  155. return MSG_ERROR;
  156. // Get message
  157. strcpy(cMsg->text, rawMsg);
  158. // Parse message
  159. char_t del[2] = {MSG_DELIMITER, '\0'};
  160. char_t* tok;
  161. tok = strtok (cMsg->text, del);
  162. cMsg->from = atoi (tok);
  163. tok = strtok(NULL, del);
  164. cMsg->to = atoi (tok);
  165. tok = strtok(NULL, del);
  166. cMsg->ts = atoll (tok);
  167. tok = strtok(NULL, del);
  168. cMsg->text_it = tok - cMsg->text;
  169. return MSG_OK;
  170. }
  171. /*! getter for cMsg_t member fromAEM */
  172. uint32_t cMsg_getFrom(cMsg_t* cMsg) { return cMsg->from; }
  173. /*! getter for cMsg_t member toAEM */
  174. uint32_t cMsg_getTo(cMsg_t* cMsg) { return cMsg->to; }
  175. /*! getter for cMsg_t member fromAEM */
  176. uint64_t cMsg_getTs(cMsg_t* cMsg) { return cMsg->ts; }
  177. /*! getter for payload text member */
  178. char_t* cMsg_getText(cMsg_t* cMsg) { return (char_t*)& cMsg->text[cMsg->text_it]; }
  179. /*!
  180. * Predicate to check core message equality
  181. * @param m1 Pointer to message 1
  182. * @param m2 Pointer to message 2
  183. * @return Equality result (true, false)
  184. */
  185. bool_t cMsg_equal (cMsg_t* m1, cMsg_t* m2) {
  186. if (m1->from != m2->from) return false;
  187. if (m1->to != m2->to) return false;
  188. if (m1->ts != m2->ts) return false;
  189. if (strncmp (cMsg_getText(m1), cMsg_getText(m2), sizeof(m1->text)))
  190. return false;
  191. return true;
  192. }
  193. //! @}
  194. /*!
  195. * mgs_t API
  196. */
  197. //! @{
  198. void msg_init (msg_t* msg) {
  199. memset ((void*)msg, 0, sizeof(msg_t));
  200. }
  201. //! @}
  202. /*!
  203. * Create a message list for our application.
  204. */
  205. msgList_t msgList;
  206. //! msgList API
  207. //! @{
  208. /*! Macro helper to saturate increased values */
  209. #define _top_saturate(test, apply, value) do { \
  210. if (test >= value) apply = value; \
  211. } while (0)
  212. /*! Macro helper to saturate decreased values */
  213. #define _btm_saturate(test, apply, value) do { \
  214. if (test < value) apply = value; \
  215. while (0)
  216. dIter_t devList_getIter (devAEM_t dev) {
  217. for (dIter_t i =0 ; i<AEMLIST_SIZE ; ++i) {
  218. if (devList[i].dev == dev)
  219. return i;
  220. }
  221. return -1; // return end()
  222. }
  223. status_t msgList_init (msgList_t* msgList) {
  224. if (pthread_mutex_init(&lock_msgList, NULL) != 0) {
  225. log_error ("Error: mutex init has failed\n");
  226. return MSG_ERROR;
  227. }
  228. memset((void*)msgList, 0, sizeof(msgList_t));
  229. msgList->first =-1;
  230. msgList->last =-1;
  231. srand (time(NULL));
  232. return MSG_OK;
  233. }
  234. /*!
  235. * @brief msgList iterator pre-increment in the msg_t direction
  236. *
  237. * This iterator force a ring buffer behavior. This function takes pointer
  238. * to iterator to alter but return the altered value so it can be directly
  239. * used in expressions
  240. *
  241. * @param it Pointer to iterator to increase
  242. * @return The iterator values
  243. */
  244. mIter_t msgList_preInc (mIter_t* it) {
  245. if (++*it >= MSG_LIST_SIZE) *it = 0;
  246. return *it;
  247. }
  248. /*!
  249. * @brief msgList iterator pre-decrement in the msg_t direction
  250. *
  251. * This iterator force a ring buffer behavior. This function takes pointer
  252. * to iterator to alter but return the altered value so it can be directly
  253. * used in expressions
  254. *
  255. * @param it Pointer to iterator to decrease
  256. * @return The iterator values
  257. */
  258. mIter_t msgList_preDec (mIter_t* it) {
  259. if (--*it < 0) *it = MSG_LIST_SIZE;
  260. return *it;
  261. }
  262. mIter_t msgList_begin (msgList_t* this) {
  263. return this->first;
  264. }
  265. mIter_t msgList_last (msgList_t* this) {
  266. return this->last;
  267. }
  268. size_t msgList_size (msgList_t* this) {
  269. return this->size;
  270. }
  271. /*!
  272. * Searches for a message in the message list.
  273. *
  274. * @param this The msgList object to work with
  275. * @param msg Pointer to message to search
  276. * @return Iterator to message if found, or -1 if not
  277. */
  278. mIter_t msgList_find (msgList_t* this, msg_t* msg) {
  279. mIter_t it =this->last; // get iterator
  280. // search from end to start to find msg, return on success
  281. for (size_t i=0 ; i < this->size ; ++i) {
  282. if (cMsg_equal (&this->m[it].cMsg, &msg->cMsg))
  283. return it;
  284. msgList_preDec(&it);
  285. // We start from the end as we think, its more possible
  286. // to find msg in the recent messages.
  287. }
  288. return (mIter_t)-1; // fail to find
  289. }
  290. /*!
  291. * Add a new message in the message list
  292. *
  293. * @param this The msgList object to work with
  294. * @param msg Pointer to message
  295. * @return Iterator to the added item (last)
  296. */
  297. mIter_t msgList_add (msgList_t* this, msg_t* msg) {
  298. if (this->first == -1) // if its first time init "first" iterator
  299. this->first = 0;
  300. this->m[msgList_preInc(&this->last)] = *msg; // store data *++it = *msg;
  301. _top_saturate(++this->size, this->size, MSG_LIST_SIZE); // count the items with saturation
  302. // if we reacher full capacity, move along first also
  303. if ((this->first == this->last) && (this->size > 1))
  304. msgList_preInc(&this->first);
  305. return this->last; // return the iterator to newly created slot
  306. }
  307. void msgList_acquire () { pthread_mutex_lock (&lock_msgList); }
  308. void msgList_release () { pthread_mutex_unlock (&lock_msgList); }
  309. //! @}
  310. //! Statistics API
  311. //! @{
  312. status_t stats_init (stats_t* s) {
  313. memset ((void*)s, 0, sizeof (stats_t));
  314. if (pthread_mutex_init(&lock_stats, NULL) != 0) {
  315. log_error ("Error: mutex init has failed\n");
  316. return MSG_ERROR;
  317. }
  318. return MSG_OK;
  319. }
  320. void statsUpdateCreate (msg_t* msg) {
  321. pthread_mutex_lock (&lock_stats);
  322. ++stats.totalMsg;
  323. ++stats.myMsg;
  324. // average message size
  325. uint32_t saved = stats.totalMsg - stats.duplicateMsg;
  326. stats.avMsgSize += strlen(cMsg_getText(&msg->cMsg)) / (saved -1);
  327. stats.avMsgSize *= (saved-1)/saved;
  328. pthread_mutex_unlock (&lock_stats);
  329. }
  330. void statsUpdateIn (msg_t* msg, bool_t dup) {
  331. pthread_mutex_lock (&lock_stats);
  332. bool_t forMe;
  333. stats.totalMsg++;
  334. stats.duplicateMsg += (dup) ? 1:0;
  335. stats.forMeMsg += ((forMe = msg->cMsg.to == settings.me)) ? 1:0;
  336. stats.inDirectMsg += (forMe && (msg->cMsg.from == msg->sender)) ? 1:0;
  337. // averages
  338. uint32_t saved = stats.totalMsg - stats.duplicateMsg;
  339. stats.avMsgSize += strlen(cMsg_getText(&msg->cMsg)) / (saved -1);
  340. stats.avMsgSize *= (saved-1)/saved;
  341. if (settings.trackTime) {
  342. stats.avTimeToMe += ((tstamp_t)time(NULL) - msg->cMsg.ts) / (saved -1);
  343. stats.avTimeToMe *= (saved-1)/saved;
  344. }
  345. pthread_mutex_unlock (&lock_stats);
  346. }
  347. void statsUpdateOut (msg_t* msg, devAEM_t dev) {
  348. pthread_mutex_lock (&lock_stats);
  349. stats.outDirectMsg += (msg->cMsg.to == dev) ? 1:0;
  350. pthread_mutex_unlock (&lock_stats);
  351. }
  352. //! @}