/* * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/common_defs.h" #include "../commhub/commhub.h" #include "../commhub/client_utils.h" #define AVLS_COMMUNICATION_VERSION "2.1.1" //----------GLOBAL STATE VARIABLES time_t last_sync_attempt = 0; //Time of the last normal chirp in seconds since epoch time_t last_offline_chirp = 0; //Time of the last offline chirp in seconds since epoch int commhub_fd = -1; //File descriptor of our connection to the comm hub int server_fd = -1; //File descriptor of our connection to the sync server int avls_motion_interval = AVLS_MOTION_INTERVAL; int avls_still_interval = AVLS_STILL_INTERVAL; int load_avls_config() { int retval; FILE *f; char line[LINE_BUFFER_SIZE]; int still = 0, motion = 0; f = fopen(AVLS_CONFIG_FILE, "rb"); if(f) { while( !feof(f) && fgets(line, LINE_BUFFER_SIZE - 1, f) ) { retval = sscanf(line, "%d %d", &motion, &still); if(retval == 2) { avls_motion_interval = motion; avls_still_interval = still; fclose(f); return 0; } } fclose(f); } return -1; } //This function attempts to connect to the pass server... int connect_to_avls_server() { int fd; int retval; struct sockaddr_in addr; fd = socket(PF_INET, SOCK_STREAM, 0); if(fd < 0) return -1; addr.sin_family = AF_INET; addr.sin_port = htons(AVLS_SERVER_PORT); inet_aton(AVLS_SERVER_IP, &addr.sin_addr); retval = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); if(retval < 0) { close(fd); return -2; } return fd; } int construct_avls_chirp(char *chirp, int size) { int len = 0; len += snprintf(chirp + len, size - len, "%d\t", get_equip_num()); len += snprintf(chirp + len, size - len, "%d\t", driver_stat.logged_in_driver); len += snprintf(chirp + len, size - len, "%d\t", stop_stat.paddle); len += snprintf(chirp + len, size - len, "%d\t", stop_stat.route); len += snprintf(chirp + len, size - len, "%d\t", stop_stat.trip); len += snprintf(chirp + len, size - len, "%d\t", stop_stat.stop); len += snprintf(chirp + len, size - len, "%d\t", (int)time(NULL)); len += snprintf(chirp + len, size - len, "%f\t", gps_stat.lat); len += snprintf(chirp + len, size - len, "%f\t", gps_stat.lon); len += snprintf(chirp + len, size - len, "%f\t", gps_stat.heading); len += snprintf(chirp + len, size - len, "%f\n", gps_stat.velocity); return len; } int send_avls_chirp(int fd) { char chirp[1024] = {0}; int len = 0; len = construct_avls_chirp(chirp, sizeof(chirp)); if(send(fd, chirp, len, 0) != len) { return -1; } return 0; } int send_offline_chirp() { char chirp[1024] = {0}; int len = 0; struct message_record outgoing_msg; len = construct_avls_chirp(chirp, sizeof(chirp)); if(len >= sizeof(chirp)) { chirp[sizeof(chirp) - 1] = '\0'; } else { chirp[len]='\0'; } format_log_message(&outgoing_msg, LOGLEVEL_DEBUG, "OFFLINE AVLS: %s", chirp); if(commhub_fd >= 0) { return send_message(commhub_fd, &outgoing_msg); } return -1; } void maintain_ipc_hub_connect(char *progname) { struct message_record outgoing_msg; if(commhub_fd < 0) //if we have no connection to the communication hub { commhub_fd = connect_to_message_server(progname); //try and get one if(commhub_fd >= 0) //if it worked { //Subscribe to the relevant status management mailboxes subscribe_to_default_messages(commhub_fd); //Request updated status information... prepare_message(&outgoing_msg, MAILBOX_STATUS_REQUEST, "", 0); send_message(commhub_fd,&outgoing_msg); } } } int main(int argc, char **argv) { struct pollfd fds[2]; int nfds = 0; int poll_return = 0; int read_return = 0; int i; struct message_record incoming_msg; #ifdef DEBUG_PRINT long long int _usec_now, _usec_prv, _usec_del; _usec_del = 60000000; _usec_now = get_usec_time(); _usec_prv = _usec_now; #endif if ( (argc>1) && ( (strncmp(argv[1], "-h", 3)==0) || (strncmp(argv[1], "-v", 3)==0) ) ) { printf("avls_communication version: %s\n", AVLS_COMMUNICATION_VERSION); exit(0); } //------------------ load_avls_config(); // load current AVLS position from global state // (state.info file). // init_state_info(); gps_stat.lat = state_info.lat; gps_stat.lon = state_info.lon; gps_stat.heading = state_info.heading; gps_stat.velocity = state_info.velocity; //------------------ configure_signal_handlers(argv[0]); maintain_ipc_hub_connect(argv[0]); //Register our default keep-up-with-system status callbacks register_system_status_callbacks(); while( exit_request_status == EXIT_REQUEST_NONE ) { time_t now = time(NULL); int sync_threshold = 0; RESET_WATCHDOG(); #ifdef DEBUG_PRINT _usec_now = get_usec_time(); if ((_usec_now - _usec_prv) > _usec_del) { printf("[%lli] avls: heartbeat\n", get_usec_time()); _usec_prv = _usec_now; } #endif if(hup_request_status) { load_avls_config(); hup_request_status = 0; } maintain_ipc_hub_connect(argv[0]); if(server_fd < 0) //If we don't have a connection to the sync server... { if( (now - last_sync_attempt) > DEFAULT_CONNECT_RETRY ) //See if it is time to try again { if( tunnel_is_up() ) //and if the tunnel thinks it is up { server_fd = connect_to_avls_server(); //if so, try again... if(server_fd >= 0) //if it worked { last_sync_attempt = 0; } else { last_sync_attempt = now; } } } } nfds=0; //----------------------------------------------- // // Figure out what our time threshold is to transmit // a message based on whether we're moving or still. // //----------------------------------------------- if(gps_stat.velocity > MOTION_THRESHOLD) //See if the bus is in motion... { sync_threshold = avls_motion_interval; //if so, we want to send AVLS chirps at higher rate } else { sync_threshold = avls_still_interval; //if not, we send them at a lower default rate } if(server_fd >= 0) //---------- If we have an active connection to the AVLS server through the tunnel { fds[nfds].fd = server_fd; //Add it to the list of things we must poll() fds[nfds].events = 0; // See if we have gone long enough to need to transmit a message AND have valid GPS data to transmit if( ((now - last_sync_attempt) > sync_threshold) && gps_stat.gps_good ) { fds[nfds].events |= POLLOUT; //If so, ask poll to see if we have buffer space to transmit one } nfds++; //either way, add this FD to the poll() list. } else //---------- Otherwise, if we have no connection to the AVLS server { // See if it is time to transmit an offline chirp AND we have valid GPS data to transmit if( ((now - last_offline_chirp) > sync_threshold) && gps_stat.gps_good ) { if(send_offline_chirp() == 0) //If so, stick one in the diagnostic log at DEBUG priority { last_offline_chirp = now; //Remember that we've done so if it worked. } } } if(commhub_fd >= 0) { fds[nfds].fd = commhub_fd; fds[nfds].events = POLLIN; nfds++; } if(nfds > 0) { poll_return = poll(fds, nfds, POLL_TIMEOUT); } else { usleep(POLL_TIMEOUT * 1000); poll_return = 0; } if(poll_return <= 0) { continue; } for(i=0; i < nfds; i++) { if( fds[i].fd == server_fd ) { //If we've lost connection, break this loop and poll all over again if(fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { close(server_fd); server_fd = -1; break; } if(fds[i].revents & POLLOUT) { //sent a query here... read_return = send_avls_chirp(server_fd); //and then update our last sync attempt time last_sync_attempt = now; if(read_return < 0) { close(server_fd); server_fd = -1; break; } } } else if( fds[i].fd == commhub_fd ) { //If we've lost connection, break this loop and poll all over again if(fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { close(commhub_fd); commhub_fd = -1; break; } if(fds[i].revents & POLLIN) { read_return = get_message(commhub_fd, &incoming_msg); if( read_return < 0 ) { close(commhub_fd); commhub_fd = -1; break; } process_message(&incoming_msg); //This passes the received message through the callback list } } } } return 0; }