piu_main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * Copyright (c) 2019 Clementine Computing LLC.
  3. *
  4. * This file is part of PopuFare.
  5. *
  6. * PopuFare is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * PopuFare is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <poll.h>
  23. #include <unistd.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include <signal.h>
  27. #include <time.h>
  28. #include "../common/common_defs.h"
  29. #include "../commhub/commhub.h"
  30. #include "../commhub/client_utils.h"
  31. int piu_fd = -1;
  32. int hub_fd = -1;
  33. device_test_vector piu_test_vector =
  34. {
  35. .dev_id = "rider_ui",
  36. .init_string = "/0:\r/1:\r/m:09 00 03 42 5A 44 56\r",
  37. .n_reply_lines = 4,
  38. .reply_strings = (char *[]){NULL, NULL, "/OK:m:09 00 03 42 5A 44 56", "/M:^"},
  39. };
  40. char diag[DIAG_BUFFER_SIZE] = {0};
  41. #define PIU_NUM_LINES 2
  42. static char *piu_set_line_cmd[PIU_NUM_LINES] = {"/0:", "/1:"};
  43. static piu_message def_msgs[PIU_NUM_LINES] = {{0}};
  44. static piu_message cur_msgs[PIU_NUM_LINES] = {{0}};
  45. int update_piu_display(int line)
  46. {
  47. piu_message *msg;
  48. if( (line >= PIU_NUM_LINES) || (line < 0) )
  49. {
  50. return -1;
  51. }
  52. if(piu_fd < 0)
  53. {
  54. return -1;
  55. }
  56. msg = (cur_msgs[line].seconds == 0)?(def_msgs + line):(cur_msgs + line);
  57. write(piu_fd, piu_set_line_cmd[line], 3); //write the command prefix
  58. write(piu_fd, msg->message, strlen(msg->message)); //write the actual message
  59. write(piu_fd, "\r", 1); //write the CR to signal EOL
  60. // printf("%s%s\n",piu_set_line_cmd[line], msg->message);
  61. return 0;
  62. }
  63. message_callback_return handle_display_message(struct message_record *msg, void *param)
  64. {
  65. piu_message *cmd = (piu_message *) msg->payload;
  66. piu_message *tgt = NULL;
  67. time_t now = time(NULL); //obtain a timestamp for NOW
  68. int touched = 0;
  69. //if we have an update for a line that doesn't exist, ignore it
  70. if( (cmd->line >= PIU_NUM_LINES) || (cmd->line < 0) )
  71. {
  72. return MESSAGE_HANDLED_CONT;
  73. }
  74. if(cmd->seconds == 0) //if we're updating the default message
  75. {
  76. tgt = def_msgs + cmd->line; //get a pointer to our message target
  77. //just do it...
  78. memcpy(tgt, cmd, sizeof(piu_message));
  79. //force terminate the string just in case
  80. tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
  81. //priority is meaningless for default messages
  82. tgt->priority = 0;
  83. //if the active message on this line has expired, a change in
  84. //default message will need to trigger an update
  85. if(cur_msgs[cmd->line].seconds < now)
  86. {
  87. touched = 1;
  88. }
  89. }
  90. else
  91. {
  92. tgt = cur_msgs + cmd->line; //get a pointer to our message target
  93. //If the current message has expired or will expire this second (thus bringing up the default message)
  94. //OR the new message is of higher priority then we need to do the repacement
  95. if( (tgt->seconds <= now) || (cmd->priority >= tgt->priority) )
  96. {
  97. //perform the copy
  98. memcpy(tgt, cmd, sizeof(piu_message));
  99. //force terminate the string just in case
  100. tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
  101. //set the expiration time on the new message to the timestamp of NOW
  102. //plus the desired duration
  103. tgt->seconds = now + cmd->seconds;
  104. touched = 1;
  105. }
  106. }
  107. if(touched) //if we have made any changes...
  108. {
  109. update_piu_display(cmd->line);
  110. }
  111. return MESSAGE_HANDLED_CONT;
  112. }
  113. int handle_message_timeouts()
  114. {
  115. int i;
  116. time_t now = time(NULL);
  117. //For each display line
  118. for(i = 0; i < PIU_NUM_LINES; i++)
  119. {
  120. //Check to see if it has an active current message
  121. if(cur_msgs[i].seconds != 0)
  122. {
  123. //If so, see if that message has passed its expiration time
  124. if(cur_msgs[i].seconds < now)
  125. { //if it has...
  126. cur_msgs[i].seconds = 0; //flag it inactive
  127. update_piu_display(i); //and update the PIU
  128. }
  129. }
  130. }
  131. return 0;
  132. }
  133. #ifdef BEEP_WITH_MAGSTRIPE_READER
  134. char dup_notify_str[MAX_PAYLOAD_LENGTH] = {0};
  135. long long dup_notify_usec = 0;
  136. message_callback_return handle_DIU_beep(struct message_record *msg, void *param)
  137. {
  138. int is_dup;
  139. long long dup_usec_delta = 0;
  140. if(strncmp(msg->payload, dup_notify_str, MAX_PAYLOAD_LENGTH))
  141. {
  142. strncpy(dup_notify_str, msg->payload, MAX_PAYLOAD_LENGTH - 1);
  143. dup_notify_str[MAX_PAYLOAD_LENGTH - 1] = '\0';
  144. is_dup = 0;
  145. dup_notify_usec = 0;
  146. }
  147. else
  148. {
  149. is_dup = 1;
  150. dup_usec_delta = get_usec_time() - dup_notify_usec;
  151. dup_notify_usec = get_usec_time();
  152. }
  153. switch(msg->payload[0])
  154. {
  155. case LOGLEVEL_EVENT:
  156. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD))
  157. {
  158. PIU_ACK_BEEP(piu_fd);
  159. }
  160. break;
  161. case LOGLEVEL_REJECT:
  162. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD))
  163. {
  164. PIU_ERROR_BEEP(piu_fd);
  165. }
  166. break;
  167. case LOGLEVEL_ACCEPT:
  168. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD))
  169. {
  170. PIU_ACK_BEEP(piu_fd);
  171. }
  172. break;
  173. case LOGLEVEL_ERROR:
  174. if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD))
  175. {
  176. PIU_CRITICAL_BEEP(piu_fd);
  177. }
  178. break;
  179. default:
  180. break;
  181. }
  182. return MESSAGE_HANDLED_CONT;
  183. }
  184. #endif
  185. //-------------------------
  186. void maintain_ipc_hub_connect(char *progname)
  187. {
  188. struct message_record outgoing_msg;
  189. if(hub_fd < 0) //if we have no connection to the IPC hub
  190. {
  191. hub_fd = connect_to_message_server(progname); //try and get one
  192. if(hub_fd >= 0)
  193. {
  194. //Subscribe to the default status messages
  195. subscribe_to_default_messages(hub_fd);
  196. //Subscribe to our specific message
  197. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_PIU_MESSAGE, strlen(MAILBOX_PIU_MESSAGE));
  198. send_message(hub_fd,&outgoing_msg);
  199. #ifdef BEEP_WITH_MAGSTRIPE_READER
  200. //if BEEP_WITH_MAGSTRIPE_READER is defined, that means our diu board's speaker/amp is FUCKING BROKEN and we
  201. //need to intercept DIU messages and induce the magstripe reader on the PIU to beep when they occur even
  202. //though it is a stupid idea to do so since any other activity on the magstripe reader (like swiping a card...)
  203. //will cause it to either miss the card or disrupt the beeping.
  204. prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_DRIVER_NOTIFY, strlen(MAILBOX_DRIVER_NOTIFY));
  205. send_message(hub_fd,&outgoing_msg);
  206. #endif
  207. }
  208. else
  209. {
  210. fprintf(stderr, "Cannot connect to IPC hub!\n");
  211. }
  212. }
  213. }
  214. int main(int argc, char **argv)
  215. {
  216. char line[LINE_BUFFER_SIZE] = {0};
  217. struct message_record incoming_msg;
  218. struct message_record outgoing_msg;
  219. struct pollfd fds[2];
  220. int nfd;
  221. int poll_return;
  222. int read_return;
  223. int i, j;
  224. configure_signal_handlers(argv[0]);
  225. maintain_ipc_hub_connect(argv[0]);
  226. //Register our defualt system message processing callbacks
  227. register_system_status_callbacks();
  228. //Register our module specific message processing callbacks
  229. register_dispatch_callback(MAILBOX_PIU_MESSAGE, CALLBACK_USER(1), &handle_display_message, NULL);
  230. #ifdef BEEP_WITH_MAGSTRIPE_READER
  231. register_dispatch_callback(MAILBOX_DRIVER_NOTIFY, CALLBACK_USER(2), &handle_DIU_beep, NULL);
  232. #endif
  233. while( exit_request_status == EXIT_REQUEST_NONE ) //loop until we get asked to exit...
  234. {
  235. RESET_WATCHDOG();
  236. //DEBUG
  237. printf("[%lli] piu_minder: heartbeat\n", get_usec_time());
  238. //DEBUG
  239. maintain_ipc_hub_connect(argv[0]);
  240. if(piu_fd < 0)
  241. {
  242. piu_fd = open_rs232_device(PASSENGER_UI_PORT, USE_DEFAULT_BAUD, RS232_LINE);
  243. if(piu_fd >= 0)
  244. {
  245. read_return = test_and_init_device(piu_fd, &piu_test_vector, diag);
  246. if(read_return)
  247. {
  248. fprintf(stderr, "PIU Init Failed on %s: %s\n", PASSENGER_UI_PORT, diag);
  249. close(piu_fd);
  250. piu_fd = -1;
  251. }
  252. else //If we successfully connected...
  253. {
  254. //iterate through each line
  255. for(j = 0; j < PIU_NUM_LINES; j++)
  256. {
  257. update_piu_display(j); //and update the display to be current
  258. }
  259. }
  260. }
  261. else
  262. {
  263. fprintf(stderr, "Cannot open serial port %s for PIU!\n", PASSENGER_UI_PORT);
  264. }
  265. }
  266. nfd = 0;
  267. if(hub_fd >= 0)
  268. {
  269. fds[nfd].fd = hub_fd;
  270. fds[nfd].events = POLLIN;
  271. fds[nfd].revents = 0;
  272. nfd++;
  273. }
  274. if(piu_fd >= 0)
  275. {
  276. fds[nfd].fd = piu_fd;
  277. fds[nfd].events = POLLIN;
  278. fds[nfd].revents = 0;
  279. nfd++;
  280. }
  281. if(nfd > 0) //if we have any file descriptors, poll them
  282. {
  283. poll_return = poll(fds, nfd, POLL_TIMEOUT);
  284. }
  285. else //otherwise, whistle and look busy
  286. {
  287. poll_return = 0;
  288. sleep(1);
  289. }
  290. //--------------------------------------------------------------------------------
  291. //No matter what poll() returned, we need to do some basic background work here...
  292. //--------------------------------------------------------------------------------
  293. handle_message_timeouts();
  294. //--------------------------------------------------------------------------------
  295. if(poll_return < 1) //if poll didn't net us any work to do,
  296. {
  297. continue; //lets try again
  298. }
  299. for(i = 0; i < nfd; i++)
  300. {
  301. if( fds[i].fd == piu_fd ) //If we're looking at the PIU...
  302. {
  303. if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) //if poll says our serial port has become bogus...
  304. {
  305. fprintf(stderr, "This is very odd... Poll returned flags %d on our serial port...\n", fds[i].revents);
  306. close(piu_fd); //close it
  307. piu_fd = -1; //flag it invalid
  308. break; //and break out of the for loop to allow the while to cycle
  309. }
  310. if(fds[i].revents & POLLIN)
  311. {
  312. read_return = read(fds[i].fd, line, sizeof(line));
  313. if(read_return > 0)
  314. {
  315. char *trav = line;
  316. line[read_return] = '\0';
  317. strip_crlf(line);
  318. while(*trav && (*trav != '/') ) //advance until EOL or we hit our start sentinel
  319. {
  320. trav++;
  321. }
  322. //Check to see that our address header is intact...
  323. if( (trav[0] == '/') && (trav[2] == ':') )
  324. {
  325. switch(trav[1])
  326. {
  327. case 'M':
  328. trav += 3; //advance past the header
  329. //Ignore ACK message from the magstripe reader
  330. if(strcmp(trav, "^"))
  331. {
  332. prepare_message(&outgoing_msg, MAILBOX_TOKEN_MAG, trav, strlen(trav) + 1);
  333. send_message(hub_fd, &outgoing_msg);
  334. }
  335. break;
  336. case 'R':
  337. trav += 3; //advance past the header
  338. //Ignore null reads.
  339. if(strcmp(trav,"0|0"))
  340. {
  341. prepare_message(&outgoing_msg, MAILBOX_TOKEN_RFID, trav, strlen(trav) + 1);
  342. send_message(hub_fd, &outgoing_msg);
  343. }
  344. break;
  345. case '*': //handle warnings
  346. case '#': //debugs
  347. case '!': //and errors
  348. format_log_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3); //send them all to the log server
  349. send_message(hub_fd, &outgoing_msg);
  350. if(trav[1] == '!') //but in the case of errors, send them to the driver too
  351. {
  352. format_driver_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3);
  353. send_message(hub_fd, &outgoing_msg);
  354. }
  355. break;
  356. default: //ignore any message addresses that we don't know what to do with
  357. printf("Ignoring command \"%s\"\n", trav);
  358. break;
  359. }
  360. }
  361. else
  362. {
  363. // printf("Ignoring non-command line \"%s\"\n", trav);
  364. }
  365. }
  366. else
  367. {
  368. fprintf(stderr, "Read from %s returned %d!\n", PASSENGER_UI_PORT, read_return);
  369. close(piu_fd); //close it
  370. piu_fd = -1; //flag it invalid
  371. break; //and break out of the for loop to allow the while to cycle
  372. }
  373. }
  374. }
  375. else if( fds[i].fd == hub_fd ) //If we're looking at the IPC hub...
  376. {
  377. if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) //if poll says our connection to the IPC hub has died...
  378. {
  379. fprintf(stderr, "The connection to the IPC hub has gone away...\n"); //complain
  380. close(hub_fd); //close it
  381. hub_fd = -1; //flag it dead
  382. break; //break out of the for loop
  383. }
  384. if(fds[i].revents & POLLIN) //if we have mail in any of our mailboxes...
  385. {
  386. read_return = get_message(hub_fd, &incoming_msg);
  387. if(read_return < 0)
  388. {
  389. fprintf(stderr, "The connection to the IPC hub has gone away...\n"); //complain
  390. close(hub_fd); //close it
  391. hub_fd = -1; //flag it dead
  392. break; //break out of the for loop
  393. }
  394. else
  395. {
  396. message_callback_return msg_status;
  397. msg_status = process_message(&incoming_msg);
  398. if (msg_status < 0) { }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. printf("Exiting.\n");
  405. close(piu_fd);
  406. close(hub_fd);
  407. return 0;
  408. }