Skip to content
Snippets Groups Projects
backend.c 34 KiB
Newer Older
/* Original Author: Kris Burney & Jake Drahos
 * Last Edits: Matthew Kelly & Dane Larson
burneykb's avatar
burneykb committed
 *
 * Interface between Quad, front end, Virtual and VRPN tracker.
 * Main uses a standard socket select loop to receive data from any 
 * source described above. Based on the data received from sockets it 
 * calls receive functions based on the socket received from.
burneykb's avatar
burneykb committed
 */

#define _GNU_SOURCE
#define _BSD_SOURCE
burneykb's avatar
burneykb committed
//system includes
Jake Drahos's avatar
Jake Drahos committed
#include <err.h>
burneykb's avatar
burneykb committed
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
Jake Drahos's avatar
Jake Drahos committed
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
Jake Drahos's avatar
Jake Drahos committed
#include <sys/stat.h>
//#include <bluetooth/bluetooth.h> Bluetooth not currently used
//#include <bluetooth/rfcomm.h> Bluetooth not currently used
burneykb's avatar
burneykb committed
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <sys/stat.h>
burneykb's avatar
burneykb committed

//user created includes
#include "commands.h"
burneykb's avatar
burneykb committed
#include "vrpn_tracker.hpp"
#include "type_def.h"
#include "update.h"
Jake Drahos's avatar
Jake Drahos committed
#include "config.h"
#include "source.h"
#include "output.h"
#include "nodes.h"
#include "backend_adapter.h"
#include "backend_utils.h"
burneykb's avatar
burneykb committed

#define CMD_MAX_LENGTH 4096
burneykb's avatar
burneykb committed

/* Backend-internal command magics */
#define TD_MAGIC "TRACKERDATA"

burneykb's avatar
burneykb committed
// function prototypes
void readAndPrint(void);
void sendVrpnPacket(struct ucart_vrpn_TrackerData *);
void getVRPNPacket(struct ucart_vrpn_TrackerData *);
void printVrpnData(struct ucart_vrpn_TrackerData *);
int connectToZybo(int index);
int safe_fd_set(int , fd_set* , int* );
int safe_fd_clr(int , fd_set* , int* );
burneykb's avatar
burneykb committed
static void safe_close_fd(int fd, pthread_mutex_t *mutexLock);
burneykb's avatar
burneykb committed

static void cb(struct ucart_vrpn_TrackerData *);
static int new_client(int fd);
Jake Drahos's avatar
Jake Drahos committed
/* Return index of client, or -1 */
static ssize_t get_client_index(int fd);
/* Returns pointer to client buffer, or -1 */
static char * get_client_buffer(int fd);
/* Return pointer to client pending responses, or -1*/
static int * get_client_pend_responses(int fd);
/* Return positive integer if successful, -1 otherwise */
static int clientAddPendResponses(int fd, uint16_t packet_id);
/* Return positive integer if successful, -1 otherwise */
static int clientRemovePendResponses(int fd, uint16_t packet_id);
/* Returns -1 on error */
burneykb's avatar
burneykb committed
static int remove_client(int fd);
/* Receive data from client */
static void client_recv(int fd);
/* Receive data from quad */
static void quad_recv(int index);
/* Checks to see if socket has disconnected. Returns 1 on disconnect, else returns 0 */
static int wasDisconnected(int fd);
/* handle controller responses from quad to frontend */
static void handleResponse(struct metadata *m, uint8_t * data);
burneykb's avatar
burneykb committed

/* Thread-safe wrappers */
static ssize_t readQuad(char * buf, size_t count, int index);
static ssize_t writeQuad(const uint8_t * buf, size_t count);
static ssize_t writeQuadIndex(const uint8_t * buf, size_t count, int index);
static int local_comm_channel;

static unsigned int currMessageID = 0;
burneykb's avatar
burneykb committed
// global variables
static volatile int keepRunning = 1;
static char trackable[254] = "UAV";
Jake Drahos's avatar
Jake Drahos committed
static int backendSocket;
//TODO - need to remove this
burneykb's avatar
burneykb committed
struct ucart_vrpn_tracker * tracker = NULL;

#define MAX_CLIENTS 32
#define CLIENT_BUFFER_SIZE 1024
#define CLIENT_MAX_PENDING_RESPONSES 15
static char client_buffers[MAX_CLIENTS][CLIENT_BUFFER_SIZE];
static int client_fds[MAX_CLIENTS];
static int client_pending_responses[MAX_CLIENTS][CLIENT_MAX_PENDING_RESPONSES];
fd_set rfds_master;
int max_fd = 0;
static FILE * quadlog_file;
static int quadlog_file_open = 0;
static char user_specified_log_name[256] = "";
static void sig_handler(int s) {
    printf("Caught SIGPIPE from quad fifo..\n");
}

/**
 * Handler function for exit signals. This allows the backend to properly close.
 */
static void sig_exit_handler(int s) {
    printf("Exiting with condition %d\n", s);
    keepRunning = 0;
burneykb's avatar
burneykb committed
// Callback to be ran whenever the tracker receives data.
// Currently doing much more than it should. It will be slimmed down 
// in the future.
static void cb(struct ucart_vrpn_TrackerData * td) {
    static int count = 0;
    sendVrpnPacket(td);
    count++;
burneykb's avatar
burneykb committed
}

int main(int argc, char **argv)
{
    int activity;
    int i,j;

    //Setup exit signals
    signal(SIGINT, sig_exit_handler);
    signal(SIGABRT, sig_exit_handler);
    signal(SIGQUIT, sig_exit_handler);
    signal(SIGTERM, sig_exit_handler);

    FD_ZERO(&rfds_master);

    /* Determine socket path */
    char * backend_socket_path = DEFAULT_SOCKET;
    if (getenv(SOCKET_ENV)) {
        backend_socket_path = getenv(SOCKET_ENV);
    }

    /* Unlink if it exists */
    unlink(backend_socket_path);

    /* Create socket */
    mode_t old_umask = umask(0111);
    backendSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
    if (backendSocket < 0) {
        err(-1, "socket");
    }

    printf("backendSocket = %d\n", backendSocket);

    /* Create sockaddr and bind */
    struct sockaddr_un sa;
    sa.sun_family = AF_UNIX;
    strncpy(sa.sun_path, backend_socket_path, 107);
    sa.sun_path[107] = '\0';
    if (bind(backendSocket, (struct sockaddr *) &sa, sizeof(sa))) {
        err(-1, "bind");
    }
    umask(old_umask);

    /* Listen */
    if (listen(backendSocket, 16)) {
        err(-1, "listen");
    }

    /* Add to socket set */
    safe_fd_set(backendSocket, &rfds_master, &max_fd);

    /* Initialize client buffers */
    for (i = 0; i < MAX_CLIENTS; i++) {
        client_fds[i] = -1;
        client_buffers[i][0] = '\n';
        for(int j = 0; j < CLIENT_MAX_PENDING_RESPONSES; j++) {
            client_pending_responses[i][j] = -1;
        }
    }

    //Socket and communication setup
    for (i = 0; i < NUM_TRACKABLES; i++) {
        //Lock the trackable communication mutex
        if (pthread_mutex_lock(&(trackables[i].socket_mutex))) {
            err(-2, "pthread_mutex_lock (%s:%d):", __FILE__, __LINE__);
        }
        //Check for trackable type
        if (trackables[i].isAdapter == 0) {
            if ((trackables[i].socket = connectToZybo(i)) < 0) {
                perror("Error connecting to Quad...");
                break;
            }
            if (!local_comm_channel) {
            //if (trackables[i].isLocal == 0) {    
                printf("zyboSocket = %d\n", trackables[i].socket);
            } else {
                signal(SIGPIPE, sig_handler);
                printf("zybo_fifo_rx = %d\nzybo_fifo_tx = %d\n", trackables[i].fifo_rx, trackables[i].fifo_tx);
            }
        } else {
            printf("Starting client socket: %s\n", trackables[i].name);
            if (adapterConnect(&(trackables[i])) != 0) {
                perror("Error connecting to Adapter...");
                break;
            }
        }
        //Unlock the trackable communication mutex
        if (pthread_mutex_unlock(&(trackables[i].socket_mutex))) {
            err(-2, "pthread_mutex_unlock (%s:%d):", __FILE__, __LINE__);
        }
    }
    if (i != NUM_TRACKABLES) {
        for (j = 0; j < (i-1); j++) {
            if (trackables[j].isAdapter == 1) {
                adapterDisconnect(&(trackables[j]));
            }
            //else if (trackables[j].isLocal == 1) {
            else if (local_comm_channel) {
                safe_close_fd(trackables[j].fifo_rx, &trackables[j].socket_mutex);
                safe_close_fd(trackables[j].fifo_tx, &trackables[j].socket_mutex);
            } 
            else {
                if (!getenv(NOQUAD_ENV)) {
                    safe_close_fd(trackables[j].socket, &trackables[j].socket_mutex);
                }
            }
        }
        exit(1);
    }

    // watch for input from stdin (fd 0) to see when it has input
    safe_fd_set(fileno(stdin), &rfds_master, &max_fd);

    //Setup safe fd set for all trackables
    for (i = 0; i < NUM_TRACKABLES; i++) {
        if (trackables[i].isAdapter == 0) {
            if (!getenv(NOQUAD_ENV)) {
                // watch for input from the zybo socket
                safe_fd_set(trackables[i].socket, &rfds_master, &max_fd);
            }
        } else {
            // watch for input from the adapter sockets
            safe_fd_set(trackables[i].socket, &rfds_master, &max_fd);
        }
    }

    //Check for a user specified log name
    if(argc >= 2) {
        strncat(user_specified_log_name, argv[1], strlen(argv[1]));
    }

    //Create VRPN trackers if using environment variable not set
    if(!getenv(NOVRPN_ENV)){
        printf("Creating VRPN trackers...\n");
        for (i = 0; i < NUM_TRACKABLES; i++) {
            //create vrpnTracker instance
            trackables[i].tracker = ucart_vrpn_tracker_createInstance(trackables[i].server_name);
            // this function will be called whenever tracker receives data
            ucart_vrpn_tracker_addCallback(trackables[i].tracker, cb);
        }    
        //TODO - remove tracker instance
        tracker = trackables[0].tracker;
        printf("Attempting to Start VRPN thread... ");
        if (ucart_vrpn_start()) {
            warnx("Failed\n");
        }
        else {
            printf("Success\n");
        }
    } else {
        printf("Ignoring VRPN information...\n");
    }

    struct timeval timeout = {
        .tv_sec = 1,
        .tv_usec = 0
    };

    //Main backend loop
    while(keepRunning) {
        fd_set rfds;
        rfds = rfds_master;
        activity = select(max_fd+1, &rfds, NULL, NULL, NULL);
        if(activity == -1) {
            perror("select() ");
        } else if (activity) {
            for(int fd = 0; fd <= max_fd; ++fd) {
                if (FD_ISSET(fd, &rfds)) {
                    if(wasDisconnected(fd)){
                        break;
                    }
                    if (fd == fileno(stdin)) {
                        /**
                         * Ignore stdin from the backend
                         */
                    } else if (fd == backendSocket) {
                        int new_fd = 0;
                        new_fd = accept(backendSocket, NULL, NULL);
                        if (new_fd < 0) {
                            warn("accept");
                        } else {
                            printf("Connection\n");
                            if (new_client(new_fd)) {
                                printf("Added client\n");
                                safe_fd_set(new_fd, &rfds_master, &max_fd);
                            }
                        }
                    } else if (get_client_index(fd) > -1) {
                        /* It is a socket to a frontend */
                        client_recv(fd);
                    } else {
                        for (i = 0; i < NUM_TRACKABLES; i++) {
                            if (fd == trackables[i].socket) {
                                quad_recv(i);
                            }
                        }
                    }
                }
            }
        } else {
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;
        }
    }

    // Free VRPN trackers and stop VRPN Tracker Thread
    if(!getenv(NOVRPN_ENV)){
        if (ucart_vrpn_stop()) {
            warnx("VRPN thread stop failed...\n");
        } else {
            printf("VRPN thread stopped\n");
        }
        for (i = 0; i < NUM_TRACKABLES; i++) {
            ucart_vrpn_tracker_freeInstance(trackables[i].tracker);
        }
    }
 
    //Disconnecting from trackables
    printf("Disconnecting from Trackables\n");
    for (i = 0; i < NUM_TRACKABLES; i++) {
        if (trackables[i].isAdapter == 1) {
            adapterDisconnect(&(trackables[i]));
        }
        //else if (trackables[i].isLocal == 1) {
        else if (local_comm_channel) {
            safe_close_fd(trackables[i].fifo_rx, &trackables[i].socket_mutex);
            safe_close_fd(trackables[i].fifo_tx, &trackables[i].socket_mutex);
        } 
        else {
            if (!getenv(NOQUAD_ENV)) {
                safe_close_fd(trackables[i].socket, &trackables[i].socket_mutex);
            }
        }
    }

    //Close log file if one was open
    if (quadlog_file_open) {
        fclose(quadlog_file);
    }
    
    return 0;
burneykb's avatar
burneykb committed
}

void sendVrpnPacket(struct ucart_vrpn_TrackerData *info) {
    uint8_t packet[64];
    struct metadata m;
    uint8_t data[128];

    struct position_update u;
    u.id = currMessageID;
    u.x = info->x;
    u.y = info->y;
    u.z = info->z;
    u.pitch = info->pitch;
    u.roll = info->roll;
    u.yaw = info->yaw;

    if (EncodeUpdate(&m, data, 128, &u) < 0) {
        warnx("Big problems. sendVrpnPacket . EncodeUpdate");
        return;
    }
    m.msg_id = currMessageID++;

    ssize_t psize;
    if ((psize = EncodePacket(packet, 64, &m, data)) < 0) {
        warnx("Big problems. sendVrpnPacket. EncodePacket");
        return;
    }

    for (int i = 0; i < NUM_TRACKABLES; i++) {
        if (strcmp(trackables[i].server_name, info->server_name) == 0) {
            writeQuadIndex(packet, psize, i);
            break;
        }
    }
burneykb's avatar
burneykb committed
}

void getVRPNPacket(struct ucart_vrpn_TrackerData *td) {
    int status;
#if VRPN_DEBUG_PRINT == 1
    for (int i = 0; i < NUM_TRACKABLES; i++) {
        if (strcmp(trackable, trackables[i].name) == 0) {
            break;
        }
    }
#endif
    //if ((status = ucart_vrpn_tracker_getData(trackables[i].tracker, td)) < 0) {
    // TODO - remove tracker instance
    if((status = ucart_vrpn_tracker_getData(tracker, td)) < 0) {
        perror("Error receiving VRPN data from tracker...");
        keepRunning = 0;
    }
burneykb's avatar
burneykb committed
}

/* void getVRPNPacket(struct ucart_vrpn_TrackerData *td, int index) {
    int status;
    if ((status = ucart_vrpn_tracker_getData(trackables[i].tracker, td)) < 0) {
        perror("Error receiving VRPN data from tracker...");
        keepRunning = 0;
    }
} */

burneykb's avatar
burneykb committed
void printVrpnData(struct ucart_vrpn_TrackerData * td) {
    printf("FPS: %lf Pos (xyz): (%lf %lf %lf) Att (pry): (%lf %lf %lf)\n",
        td->fps, td->x, td->y, td->z, td->pitch, td->roll, td->yaw);
burneykb's avatar
burneykb committed
}

int connectToZybo(int index) {
    int sock;
    int status = -1;

    if (getenv(NOQUAD_ENV)) {
        return 0;
    }

    // Check for local comm channel or bluetooth
    //if (trackables[index].isLocal == 1) {
    if (getenv(QUAD_COMM_ENV) && strcmp(getenv(QUAD_COMM_ENV), "local") == 0) {
        printf("Using Local Fifo Settings\n");
        local_comm_channel = 1;
        char * fifo_rx = trackables[index].fifo_tx_path;
        char * fifo_tx = trackables[index].fifo_rx_path;
            
        trackables[index].fifo_tx = open(fifo_tx, O_WRONLY | O_NONBLOCK);
        if (trackables[index].fifo_tx < 0) {
            warnx("Open zybo_fifo_tx...");
            return -1;
        }

        trackables[index].fifo_rx = open(fifo_rx, O_RDWR | O_NONBLOCK);
        if (trackables[index].fifo_rx < 0) {
            warnx("Opening zybo_fifo_rx...");
            return -1;
        }
        status = 0;
        sock = trackables[index].fifo_rx;
    }
    /*else if (trackables[index].isBluetooth == 1) { //Bluetooth functionality currently disabled
        printf("Using BT Settings\n");
        struct sockaddr_rc addr;

        //allocate a socket
        sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

        //set the connection params ie. who connect to
        addr.rc_family = AF_BLUETOOTH;
        addr.rc_channel = trackables[index].bt_channel;
        str2ba( trackables[index].bt_addr, &addr.rc_bdaddr );

        printf("Attempting to connect to zybo. Please be patient...\n");
        // blocking call to connect to socket sock ie. zybo board
        status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    else {
        printf("Using WIFI settings\n");
        struct sockaddr_in addr;

        addr.sin_family = AF_INET;

        /* Quick and Dirty */
        if (!inet_aton(trackables[index].quad_ip, &addr.sin_addr)) {
            printf("Default IP %s is invalid\n",
            trackables[index].quad_ip);
            return -1;
        }

        /* Quick 'n dirty, oh yeah! */
        addr.sin_port = htons(trackables[index].quad_port);

        sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sock < 0) {
            perror("socket");
            return -1;
        }
        printf("Connecting to Quad @ %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

        status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));

        int flag = 1;
        int result = setsockopt(sock,    /* socket affected */
                           IPPROTO_TCP,     /* set option at TCP level */
                           TCP_NODELAY,     /* name of option */
                           (char *) &flag,  /* the cast is historical cruft */
                           sizeof(int));    /* length of option value */
    }

    // connection failed
    if(status < 0) {
        close(sock);
        perror("connect");
        return -1;
    } else {
        printf("connection successful!...\n");
        return sock;
    }
burneykb's avatar
burneykb committed
}
/* add a fd to fd_set, and update max_fd */
int safe_fd_set(int fd, fd_set* fds, int* max_fd) {
    assert(max_fd != NULL);

    FD_SET(fd, fds);
    if (fd > *max_fd) {
        *max_fd = fd;
    }
    return 0;
}

/* clear fd from fds, update max fd if needed */
int safe_fd_clr(int fd, fd_set* fds, int* max_fd) {
    assert(max_fd != NULL);

    FD_CLR(fd, fds);
    if (fd == *max_fd) {
        (*max_fd)--;
    }
    return 0;
static ssize_t writeQuad(const uint8_t * buf, size_t count) {
    ssize_t retval;
    int index = 0;
    for (int i = 0; i < NUM_TRACKABLES; i++) {
        if (strcmp(trackables[i].name, trackable) == 0) {
            index = i;
#if BACKEND_DEBUG_PRINT == 1
            printf("Index: %d\tName: %s\n", index, trackables[index].name);
            printf("packetToQuad = '");
            for(int i = 0; i < (int)count; ++i) {
                printf(" %.2x ", buf[i]);
            }
            printf("'\n");
#endif
            return writeQuadIndex(buf, count, index);
        }
    }
    //FIXME - Should never reach here
    return -1;
}

static ssize_t writeQuadIndex(const uint8_t * buf, size_t count, int index) {
    ssize_t retval;
    
#if BACKEND_DEBUG_PRINT == 1
    printf("packetToQuad = '");
    for(int i = 0; i < (int)count; ++i) {
        printf(" %.2x ", buf[i]);
    }
    printf("'\n");
#endif

    if (trackables[index].isAdapter == 0 && getenv(NOQUAD_ENV)) {
        return count;
    }

    if (pthread_mutex_lock(&trackables[index].socket_mutex)) {
        err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
    }

    //if (trackables[index].isLocal == 1) {
    if (trackables[index].isAdapter == 0 && local_comm_channel) {
        retval = write(trackables[index].fifo_tx, buf, count);
    } else {
        if (trackables[index].isAdapter == 0) {
            retval = write(trackables[index].socket, buf, count);
        } else {
            retval = adapterWrite(trackables[index].conn, (const char *) buf, count);
        }
    }

    if (pthread_mutex_unlock(&trackables[index].socket_mutex)) {
        err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
    }

    return retval;
static ssize_t readQuad(char * buf, size_t count, int index) {
    ssize_t retval;
    if (trackables[index].isAdapter == 0 && getenv(NOQUAD_ENV)) {
        return count;
    }
    if (pthread_mutex_lock(&trackables[index].socket_mutex)) {
        err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
    }

    retval = read(trackables[index].socket, buf, count);

    if (pthread_mutex_unlock(&trackables[index].socket_mutex)) {
        err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
    }
    return retval;
}

static int new_client(int fd) {
    ssize_t new_slot = -1;
    for (ssize_t i = 0; i < MAX_CLIENTS; i++) {
        if (client_fds[i] < 0) {
            new_slot = i;
            break;
        }
    }
    if (new_slot == -1) {
        warnx("Ran out of room! Consider increasing MAX_CLIENTS!");
        return 0;
    }

    client_fds[new_slot] = fd;
    client_buffers[new_slot][0] = '\0';

    return 1;
Jake Drahos's avatar
Jake Drahos committed

static ssize_t get_client_index(int fd) {
    for (ssize_t i = 0; i < MAX_CLIENTS; i++) {
        if (client_fds[i] == fd) {
            return i;
        }
    }
Jake Drahos's avatar
Jake Drahos committed

    return -1;
static char * get_client_buffer(int fd) {
    ssize_t slot = get_client_index(fd);
    if (slot == -1) {
        return NULL;
    } else {
        return client_buffers[slot];
    }
Jake Drahos's avatar
Jake Drahos committed
}
static int * get_client_pend_responses(int fd) {
    ssize_t slot = get_client_index(fd);
    if (slot == -1) {
        return NULL;
    } else {
        return client_pending_responses[slot];
    }
static int clientAddPendResponses(int fd, uint16_t packet_id) {
    int *pendingResponses = get_client_pend_responses(fd);
    for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) {
        if(pendingResponses[i] == -1) {
            pendingResponses[i] = packet_id;
            return i;
        }
    }
    return -1;
}

static int clientRemovePendResponses(int fd, uint16_t packet_id) {
    int *pendingResponses = get_client_pend_responses(fd);
    for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) {
        if(pendingResponses[i] == packet_id) {
            pendingResponses[i] = -1;
            return i;
        }
    }
    return -1;
burneykb's avatar
burneykb committed
static int remove_client(int fd) {
    ssize_t slot = get_client_index(fd);
    if(slot == -1)
        return -1;
    char *clientBuffer = get_client_buffer(fd);
    if(clientBuffer == NULL)
        return -1;
    clientBuffer[0] = '\0';
    int *pendingResponses = get_client_pend_responses(fd);
    if(pendingResponses == NULL)
        return -1;
    for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) {
        pendingResponses[i] = -1;
    }
    client_fds[slot] = -1;
    return 0;
burneykb's avatar
burneykb committed
}

static void safe_close_fd(int fd, pthread_mutex_t *mutexLock) {
    if (pthread_mutex_lock(mutexLock)) {
        err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
    }
    close(fd);
    if (pthread_mutex_unlock(mutexLock)) {
        err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
    }
static void client_recv(int fd) {
    char * buffer;
    ssize_t len_pre;
    char cmdString[64];
    char * cursor;
    ssize_t r;
    int index = 0;
burneykb's avatar
burneykb committed


    buffer = get_client_buffer(fd);
    len_pre = strlen(buffer);
    cursor = buffer + len_pre;

    r = read(fd, cursor, CLIENT_BUFFER_SIZE - len_pre - 1);
    if (r < 0) {
        warn("read (fd: %d)", fd);
    }
    buffer[len_pre + r] = '\0';

    /* Parse buffer and handle commands */
    while (1) {
        /* not using strtok because reasons */
        size_t len = strlen(buffer);
        ssize_t newline = -1;
        for (size_t i = 0; i < len; i++) {
            if (buffer[i] == '\n') {
                newline = i;
                break;
            }
        }

        /* No newline found. End parsing */
        if (newline == -1) {
            break;
        }
        buffer[newline] = '\0';
        printf("Client(%d) : '%s'\n",fd, buffer);

        char * first_word;
        char * tmp = strdup(buffer);
#if BACKEND_DEBUG_PRINT == 1
        printf("tmpbuff = '%s'\n", tmp);
#endif
        first_word = strtok(tmp, " ");
#if BACKEND_DEBUG_PRINT == 1
        printf("first word = '%s'\n", first_word);
#endif
        ssize_t msg_type, i;
        for (i = 0; i < MAX_TYPE_ID; ++i) {
            if ((msg_type = findCommand(first_word)) != -1)
                break;
        }

        free(tmp);

        if (msg_type == -1) {
            /* buffer was not a quad command, handling internally to
             * backend instead of forwarding to quad
             */
            if (strncmp(buffer, TD_MAGIC, strlen(TD_MAGIC)) == 0) {
                /* Request for tracker data */
                struct ucart_vrpn_TrackerData td;
                // TODO - check tracker of a specific trackable, also implement a request
                // that can get the vrpn of a specific trackable
                if (tracker == NULL) {
                    char * dummy = TD_MAGIC " 1.0 2.0 3.0 4.0 5.0 6.0\n";
                    write(fd, dummy, strlen(dummy));
                } else if (ucart_vrpn_tracker_getData(tracker, &td)) {
                    write(fd, TD_MAGIC " ERROR\n", strlen(TD_MAGIC " ERROR\n"));
                } else {
                    /* more than sufficient buffer */
                    char buffer[2048];
                    /* Map VRPN XYZ to Height Lat Long (right now it's
                     * a guess). Format is Height Lat Long P R Y */
                    if (snprintf(buffer,
                            2048,
                            TD_MAGIC " %lf %lf %lf %lf %lf %lf\n",
                            td.z,
                            td.y,
                            td.x,
                            td.pitch,
                            td.roll,
                            td.yaw) >= 2048) {

                        /* Output longer than buffer */
                        warnx("Increase format buffer size, output was too long!");
                        write(fd, TD_MAGIC " ERROR\n", strlen(TD_MAGIC " ERROR\n"));
                    }
                    write(fd, buffer, strlen(buffer));
                }
            }
            else if (strncmp(buffer, "gettrackable", strlen("gettrackable")) == 0) {
            char buffer[256];
                if (snprintf(buffer,
                    256,
                    "gettrackable %s\n",
                    trackable) >= 256) {
                    warnx("Increase format buffer size, output was too long!");
                    write(fd, "gettrackable ERROR\n", strlen("gettrackable ERROR\n"));
                }
                write(fd, buffer, strlen(buffer));  
            }
            else if (strncmp(buffer, "settrackable", strlen("settrackable")) == 0) {
                char check[256];
                sscanf(buffer,
                    "settrackable %s",
                    check);
                if (strcmp(check,"(null)") != 0) {
                    for (i = 0; i < NUM_TRACKABLES; i++) {
                        if (strcmp(trackables[i].name, check) == 0) {
                            sscanf(buffer,
                                "settrackable %s",
                                trackable);
                            break;
                        }
                    }
                }
            }

        } else {
            uint8_t packet[64];
            struct metadata m;
            uint8_t *data = malloc(sizeof(*data) * 128);
            ssize_t result;
            ssize_t psize;

            printf(" found a msg_type of %ld\n", msg_type);

            switch (msg_type) {
                case SETPARAM_ID:
                    result = EncodeSetParam(&m, data, 128, buffer);
                    break;
                case GETPARAM_ID:
                    result = EncodeGetParam(&m, data, 128, buffer);
                    break;
                case SETSOURCE_ID:
                    result = EncodeSetSource(&m, data, 128, buffer);
                    break;
                case GETSOURCE_ID:
                    result = EncodeGetSource(&m, data, 128, buffer);
                    break;
                case GETOUTPUT_ID:
                    result = EncodeGetOutput(&m, data, 128, buffer);
                    break;
                case GETNODES_ID:
                    result = EncodeGetNodes(&m, data, 128, buffer);
                    break;
                case ADDNODE_ID:
                    result = EncodeAddNode(&m, data, 128, buffer);
                    break;
                case OUTPUT_OVERRIDE_ID:
		    result = EncodeSetOutputOverride(&m, data, 128, buffer);
		    break;

                default:
                    result = -1;
                    break;
            }

            if (result < 0) {
                warnx("Big problems. client_recv. EncodeMetaData");
                free(data);
                return;
            }

            m.msg_id = currMessageID++;

            if ((psize = EncodePacket(packet, 64, &m, data)) < 0) {
                warnx("Big problems. client_recv. EncodePacket");
                free(data);
                return;
            }

            /* Only add the client to the pending responses if it was a getparam command */
            if (m.msg_type == GETPARAM_ID || m.msg_type == GETOUTPUT_ID ||
                m.msg_type == GETSOURCE_ID || m.msg_type == GETNODES_ID || m.msg_type == ADDNODE_ID) {
                if (clientAddPendResponses(fd, BytesTo16(packet[ID_L], packet[ID_H])) == -1) {
                    warnx("Ran out of room! Consider increasing CLIENT_MAX_PENDING_RESPONSES!\n");
                }
            }


            int retval = writeQuad(packet, psize);
            // printf("sent %d bytes\n", retval);

            free(data);
        }

        char * rest = &buffer[newline] + 1;
        size_t restLen = (strlen(rest) == 0) ? 1 : strlen(rest);
        /* Delete parsed data and move the rest to the left */
        memmove(buffer, rest, restLen +1);
    }
static void quad_recv(int index) {
    static unsigned char respBuf[CMD_MAX_LENGTH];
    static size_t respBufLen;
    static int receiving_logs; 

    struct metadata m;
    uint8_t data[CMD_MAX_LENGTH];
    size_t respLen;
    ssize_t datalen;
    size_t packetlen;

    respLen = readQuad((char *) respBuf + respBufLen,
            CMD_MAX_LENGTH - respBufLen, index);
    if (respLen <= 0) {
        perror("ERROR reading from quad...\n");
        respBufLen = 0;
        return;
    }
    respBufLen += respLen;

#if BACKEND_DEBUG_PRINT == 1
    printf("packetFromQuad = '");
    for(int i = 0; i < (int)respBufLen; ++i) {
        printf(" %.2x ", respBuf[i]);
    }
    printf("'\n");
#endif

    while(respBufLen) {
        datalen = DecodePacket(&m, data, CMD_MAX_LENGTH, respBuf, respBufLen);
        if (datalen == -1) {
            warnx("No start Byte");
            for (size_t i = 0; i < respBufLen; ++i) {
                if (respBuf[i] == BEGIN_CHAR) {
                    memmove(respBuf, respBuf + i, respBufLen - i);
                    respBufLen -=i;
                    return;
                }

            }
            respBufLen = 0;
            return;
        }
        if (datalen == -5) {
            warnx("Chechsum mismatch");
            for (size_t i = 0; i < respBufLen; ++i) {
                if (respBuf[i] == BEGIN_CHAR) {
                    memmove(respBuf, respBuf + i, respBufLen - i);
                    respBufLen -=i;
                    return;
                }
            }
            respBufLen = 0;
            return;
        }
        if (datalen < 0){
            /* Not enough data yet. We need to wait for more */
            return;
        }

        packetlen = PacketSize(&m);
        memmove(respBuf, respBuf + packetlen, respBufLen - packetlen);
        respBufLen -= packetlen;

        char * debug_string;
        switch (m.msg_type) {
            case DEBUG_ID:
                /* in case of debug. Quad send null terminated string in data */
                debug_string = strndup((char *)data, m.data_len);
                printf(" (Quad) : %s\n", debug_string);
                free(debug_string);
                break;
            case LOG_ID:
                if (!quadlog_file_open) {
                    char log_file[256];
                    create_log_name(log_file, 256, user_specified_log_name);
                    printf("New log file created: '%s'\n", log_file);
                    quadlog_file = fopen(log_file, "w");