| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- /*
- * Copyright (c) 2019 Clementine Computing LLC.
- *
- * This file is part of PopuFare.
- *
- * PopuFare is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * PopuFare is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
- *
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <poll.h>
- #include <unistd.h>
- #include <errno.h>
- #include <string.h>
- #include <signal.h>
- #include <time.h>
- #include "../common/common_defs.h"
- #include "../commhub/commhub.h"
- #include "../commhub/client_utils.h"
- #define PIU_MINDER_VERSION "1.0.2"
- int piu_fd = -1;
- int hub_fd = -1;
- device_test_vector piu_test_vector = {
- .dev_id = "rider_ui",
- .init_string = "/0:\r/1:\r/m:09 00 03 42 5A 44 56\r",
- .n_reply_lines = 4,
- .reply_strings = (char *[]){NULL, NULL, "/OK:m:09 00 03 42 5A 44 56", "/M:^"},
- };
- char diag[DIAG_BUFFER_SIZE] = {0};
- #define PIU_NUM_LINES 2
- static char *piu_set_line_cmd[PIU_NUM_LINES] = {"/0:", "/1:"};
- static piu_message def_msgs[PIU_NUM_LINES] = {{0}};
- static piu_message cur_msgs[PIU_NUM_LINES] = {{0}};
- int update_piu_display(int line) {
- piu_message *msg;
- if( (line >= PIU_NUM_LINES) || (line < 0) ) {
- return -1;
- }
- if(piu_fd < 0) {
- return -1;
- }
- msg = (cur_msgs[line].seconds == 0)?(def_msgs + line):(cur_msgs + line);
- // write the command prefix
- //
- write(piu_fd, piu_set_line_cmd[line], 3);
- // write the actual message
- //
- write(piu_fd, msg->message, strlen(msg->message));
- // write the CR to signal EOL
- //
- write(piu_fd, "\r", 1);
- //printf("%s%s\n",piu_set_line_cmd[line], msg->message);
- return 0;
- }
- message_callback_return handle_display_message(struct message_record *msg, void *param) {
- piu_message *cmd = (piu_message *) msg->payload;
- piu_message *tgt = NULL;
- // obtain a timestamp for NOW
- //
- time_t now = time(NULL);
- int touched = 0;
- // if we have an update for a line that doesn't exist, ignore it
- //
- if( (cmd->line >= PIU_NUM_LINES) || (cmd->line < 0) ) {
- return MESSAGE_HANDLED_CONT;
- }
- // if we're updating the default message
- //
- if(cmd->seconds == 0) {
- // get a pointer to our message target
- //
- tgt = def_msgs + cmd->line;
- // just do it...
- //
- memcpy(tgt, cmd, sizeof(piu_message));
- // force terminate the string just in case
- //
- tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
- // priority is meaningless for default messages
- //
- tgt->priority = 0;
- //if the active message on this line has expired, a change in
- // default message will need to trigger an update
- //
- if(cur_msgs[cmd->line].seconds < now) {
- touched = 1;
- }
- }
- else
- {
- // get a pointer to our message target
- //
- tgt = cur_msgs + cmd->line;
- // If the current message has expired or will expire this second (thus bringing up the default message)
- // OR the new message is of higher priority then we need to do the repacement
- //
- if( (tgt->seconds <= now) || (cmd->priority >= tgt->priority) ) {
- // perform the copy
- //
- memcpy(tgt, cmd, sizeof(piu_message));
- // force terminate the string just in case
- //
- tgt->message[PIU_MESSAGE_LEN - 1] = '\0';
- // set the expiration time on the new message to the timestamp of NOW
- // plus the desired duration
- //
- tgt->seconds = now + cmd->seconds;
- touched = 1;
- }
- }
- // if we have made any changes...
- //
- if(touched) {
- update_piu_display(cmd->line);
- }
- return MESSAGE_HANDLED_CONT;
- }
- int handle_message_timeouts() {
- int i;
- time_t now = time(NULL);
- // For each display line
- //
- for(i = 0; i < PIU_NUM_LINES; i++) {
- // Check to see if it has an active current message
- //
- if(cur_msgs[i].seconds != 0) {
- // If so, see if that message has passed its expiration time
- //
- if(cur_msgs[i].seconds < now) {
- // if it has...
- //
- cur_msgs[i].seconds = 0;
- // flag it inactive
- // and update the PIU
- //
- update_piu_display(i);
- }
- }
- }
- return 0;
- }
- #ifdef BEEP_WITH_MAGSTRIPE_READER
- char dup_notify_str[MAX_PAYLOAD_LENGTH] = {0};
- long long dup_notify_usec = 0;
- message_callback_return handle_DIU_beep(struct message_record *msg, void *param) {
- int is_dup;
- long long dup_usec_delta = 0;
- if(strncmp(msg->payload, dup_notify_str, MAX_PAYLOAD_LENGTH)) {
- strncpy(dup_notify_str, msg->payload, MAX_PAYLOAD_LENGTH - 1);
- dup_notify_str[MAX_PAYLOAD_LENGTH - 1] = '\0';
- is_dup = 0;
- dup_notify_usec = 0;
- }
- else {
- is_dup = 1;
- dup_usec_delta = get_usec_time() - dup_notify_usec;
- dup_notify_usec = get_usec_time();
- }
- switch(msg->payload[0])
- {
- case LOGLEVEL_EVENT:
- if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
- PIU_ACK_BEEP(piu_fd);
- }
- break;
- case LOGLEVEL_REJECT:
- if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
- PIU_ERROR_BEEP(piu_fd);
- }
- break;
- case LOGLEVEL_ACCEPT:
- if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
- PIU_ACK_BEEP(piu_fd);
- }
- break;
- case LOGLEVEL_ERROR:
- if(!is_dup || (dup_usec_delta >= DUP_USEC_BEEP_THRESHOLD)) {
- PIU_CRITICAL_BEEP(piu_fd);
- }
- break;
- default:
- break;
- }
- return MESSAGE_HANDLED_CONT;
- }
- #endif
- //-------------------------
- void maintain_ipc_hub_connect(char *progname) {
- struct message_record outgoing_msg;
- // if we have no connection to the IPC hub
- if(hub_fd < 0) {
- // try and get one
- //
- hub_fd = connect_to_message_server(progname);
- if(hub_fd >= 0)
- {
- // Subscribe to the default status messages
- //
- subscribe_to_default_messages(hub_fd);
- // Subscribe to our specific message
- //
- prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_PIU_MESSAGE, strlen(MAILBOX_PIU_MESSAGE));
- send_message(hub_fd,&outgoing_msg);
- #ifdef BEEP_WITH_MAGSTRIPE_READER
- // if BEEP_WITH_MAGSTRIPE_READER is defined, that means our diu board's speaker/amp is FUCKING BROKEN and we
- // need to intercept DIU messages and induce the magstripe reader on the PIU to beep when they occur even
- // though it is a stupid idea to do so since any other activity on the magstripe reader (like swiping a card...)
- // will cause it to either miss the card or disrupt the beeping.
- //
- prepare_message(&outgoing_msg, MAILBOX_SUBSCRIBE, MAILBOX_DRIVER_NOTIFY, strlen(MAILBOX_DRIVER_NOTIFY));
- send_message(hub_fd,&outgoing_msg);
- #endif
- }
- else
- {
- fprintf(stderr, "Cannot connect to IPC hub!\n");
- }
- }
- }
- int main(int argc, char **argv) {
- char line[LINE_BUFFER_SIZE] = {0};
- struct message_record incoming_msg;
- struct message_record outgoing_msg;
- struct pollfd fds[2];
- int nfd;
- int poll_return;
- int read_return;
- int i, j;
- #ifdef DEBUG_PRINT
- long long int _usec_now, _usec_prv, _usec_del;
- _usec_now = get_usec_time();
- _usec_prv = _usec_now;
- _usec_del = 60000000;
- #endif
- long long int _usec_ratelimit_now, _usec_ratelimit_prv, _usec_ratelimit_del;
- _usec_ratelimit_now = get_usec_time();
- _usec_ratelimit_prv = _usec_ratelimit_now;
- _usec_ratelimit_del = 1000000;
- if ( (argc>1) && (
- (strncmp(argv[1], "-h", 3)==0) ||
- (strncmp(argv[1], "-v", 3)==0) ) ) {
- printf("piu_minder version: %s\n", PIU_MINDER_VERSION);
- exit(0);
- }
- configure_signal_handlers(argv[0]);
- maintain_ipc_hub_connect(argv[0]);
- // Register our defualt system message processing callbacks
- //
- register_system_status_callbacks();
- // Register our module specific message processing callbacks
- //
- register_dispatch_callback(MAILBOX_PIU_MESSAGE, CALLBACK_USER(1), &handle_display_message, NULL);
- #ifdef BEEP_WITH_MAGSTRIPE_READER
- register_dispatch_callback(MAILBOX_DRIVER_NOTIFY, CALLBACK_USER(2), &handle_DIU_beep, NULL);
- #endif
- // loop until we get asked to exit...
- //
- while( exit_request_status == EXIT_REQUEST_NONE ) {
- RESET_WATCHDOG();
- #ifdef DEBUG_PRINT
- _usec_now = get_usec_time();
- if ((_usec_now - _usec_prv) > _usec_del) {
- printf("[%lli] piu_minder: heartbeat\n", get_usec_time());
- _usec_prv = _usec_now;
- }
- #endif
- maintain_ipc_hub_connect(argv[0]);
- if(piu_fd < 0) {
- piu_fd = open_rs232_device(PASSENGER_UI_PORT, USE_DEFAULT_BAUD, RS232_LINE);
- if(piu_fd >= 0) {
- read_return = test_and_init_device(piu_fd, &piu_test_vector, diag);
- if(read_return) {
- fprintf(stderr, "PIU Init Failed on %s: %s\n", PASSENGER_UI_PORT, diag);
- close(piu_fd);
- piu_fd = -1;
- }
- // If we successfully connected...
- //
- else {
- // iterate through each line
- //
- for(j = 0; j < PIU_NUM_LINES; j++) {
- // and update the display to be current
- //
- update_piu_display(j);
- }
- }
- }
- else {
- _usec_ratelimit_now = get_usec_time();
- if ((_usec_ratelimit_now - _usec_ratelimit_prv) > _usec_ratelimit_del) {
- fprintf(stderr, "Cannot open serial port %s for PIU!\n", PASSENGER_UI_PORT);
- _usec_ratelimit_prv = _usec_ratelimit_now;
- }
- }
- }
- nfd = 0;
- if(hub_fd >= 0) {
- fds[nfd].fd = hub_fd;
- fds[nfd].events = POLLIN;
- fds[nfd].revents = 0;
- nfd++;
- }
- if(piu_fd >= 0) {
- fds[nfd].fd = piu_fd;
- fds[nfd].events = POLLIN;
- fds[nfd].revents = 0;
- nfd++;
- }
- // if we have any file descriptors, poll them
- //
- if(nfd > 0) {
- poll_return = poll(fds, nfd, POLL_TIMEOUT);
- }
- // otherwise, whistle and look busy
- //
- else {
- poll_return = 0;
- sleep(1);
- }
- //--------------------------------------------------------------------------------
- // No matter what poll() returned, we need to do some basic background work here...
- //
- //--------------------------------------------------------------------------------
- handle_message_timeouts();
- //--------------------------------------------------------------------------------
- // if poll didn't net us any work to do,
- //
- if(poll_return < 1) {
- //lets try again
- //
- continue;
- }
- for(i = 0; i < nfd; i++) {
- //If we're looking at the PIU...
- //
- if( fds[i].fd == piu_fd ) {
- // if poll says our serial port has become bogus...
- //
- if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) {
- fprintf(stderr, "This is very odd... Poll returned flags %d on our serial port...\n", fds[i].revents);
- // close it
- //
- close(piu_fd);
- // flag it invalid
- piu_fd = -1;
- // and break out of the for loop to allow the while to cycle
- //
- break;
- }
- if(fds[i].revents & POLLIN) {
- read_return = read(fds[i].fd, line, sizeof(line));
- if(read_return > 0) {
- char *trav = line;
- line[read_return] = '\0';
- strip_crlf(line);
- // advance until EOL or we hit our start sentinel
- //
- while(*trav && (*trav != '/') ) {
- trav++;
- }
- // Check to see that our address header is intact...
- //
- if( (trav[0] == '/') && (trav[2] == ':') ) {
- switch(trav[1]) {
- case 'M':
- // advance past the header
- //
- trav += 3;
- // Ignore ACK message from the magstripe reader
- //
- if(strcmp(trav, "^")) {
- prepare_message(&outgoing_msg, MAILBOX_TOKEN_MAG, trav, strlen(trav) + 1);
- send_message(hub_fd, &outgoing_msg);
- }
- break;
- case 'R':
- // advance past the header
- //
- trav += 3;
- // Ignore null reads and
- // spurious 1-bit reads
- //
- if ( (strcmp(trav,"0|0")!=0) &&
- (strcmp(trav,"1|0")!=0) &&
- (strcmp(trav,"1|1")!=0) ) {
- prepare_message(&outgoing_msg, MAILBOX_TOKEN_RFID, trav, strlen(trav) + 1);
- send_message(hub_fd, &outgoing_msg);
- }
- break;
- case 'Q':
- trav += 3;
- // Ignore ACK message
- //
- if(strcmp(trav, "^")) {
- prepare_message(&outgoing_msg, MAILBOX_TOKEN_QR, trav, strlen(trav) + 1);
- send_message(hub_fd, &outgoing_msg);
- }
- break;
- // handle warnings
- //
- case '*':
- // debugs
- //
- case '#':
- // and errors
- //
- case '!':
- // send them all to the log server
- //
- format_log_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3);
- send_message(hub_fd, &outgoing_msg);
- // but in the case of errors, send them to the driver too
- //
- if(trav[1] == '!') {
- format_driver_message(&outgoing_msg, trav[1], "PIU Reports: %s", trav + 3);
- send_message(hub_fd, &outgoing_msg);
- }
- break;
- // ignore any message addresses that we don't know what to do with
- //
- default:
- printf("Ignoring command \"%s\"\n", trav);
- break;
- }
- }
- else {
- //printf("Ignoring non-command line \"%s\"\n", trav);
- }
- }
- else
- {
- fprintf(stderr, "Read from %s returned %d!\n", PASSENGER_UI_PORT, read_return);
- //close it
- close(piu_fd);
- //flag it invalid
- piu_fd = -1;
- //and break out of the for loop to allow the while to cycle
- break;
- }
- }
- }
- //If we're looking at the IPC hub...
- else if( fds[i].fd == hub_fd )
- {
- //if poll says our connection to the IPC hub has died...
- if(fds[i].revents & (POLLHUP | POLLERR | POLLNVAL))
- {
- //complain
- fprintf(stderr, "The connection to the IPC hub has gone away...\n");
- //close it
- close(hub_fd);
- //flag it dead
- hub_fd = -1;
- //break out of the for loop
- break;
- }
- // if we have mail in any of our mailboxes...
- //
- if(fds[i].revents & POLLIN) {
- read_return = get_message(hub_fd, &incoming_msg);
- if(read_return < 0) {
- // complain
- //
- fprintf(stderr, "The connection to the IPC hub has gone away...\n");
- // close it
- //
- close(hub_fd);
- // flag it dead
- //
- hub_fd = -1;
- // break out of the for loop
- //
- break;
- }
- else {
- message_callback_return msg_status;
- msg_status = process_message(&incoming_msg);
- if (msg_status < 0) { }
- }
- }
- }
- }
- }
- printf("Exiting.\n");
- close(piu_fd);
- close(hub_fd);
- return 0;
- }
|