Skip to content
Snippets Groups Projects
microcart_cli.c 16.6 KiB
Newer Older
/* Author: Kris Burney & Jake Drahos
burneykb's avatar
burneykb committed
 *
 * BlueTooth socket program for passing vrpn data to quad.
 */

#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>
burneykb's avatar
burneykb committed
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
burneykb's avatar
burneykb committed

//user created includes
#include "communication.h"
burneykb's avatar
burneykb committed
#include "vrpn_tracker.hpp"
#include "type_def.h"
#include "logger.h"
Jake Drahos's avatar
Jake Drahos committed
#include "config.h"
burneykb's avatar
burneykb committed

#define QUAD_BT_ADDR  "00:06:66:64:61:D6"
#define QUAD_BT_CHANNEL  0x01
#define CMD_MAX_LENGTH 1024
#define MAX_HASH_SIZE 15
burneykb's avatar
burneykb committed

// function prototypes
void readAndPrint(void);
void sendVrpnPacket(struct ucart_vrpn_TrackerData *);
void sendStartPacket(void);
void getVRPNPacket(struct ucart_vrpn_TrackerData *);
void printVrpnData(struct ucart_vrpn_TrackerData *);
int connectToZybo();
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);
/* 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);
/* Checks to see if socket has disconnected. Returns 1 on disconnect, else returns 0 */
static int wasDisconnected(int fd);
burneykb's avatar
burneykb committed

/* Thread-safe wrappers */
Jake Drahos's avatar
Jake Drahos committed
pthread_mutex_t quadSocketMutex;
static ssize_t writeQuad(const char * buf, size_t count);
static ssize_t readQuad(char * buf, size_t count);

/* Functions for recording Latencies */
void findTimeDiff(int respID);
int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y);
//time stamp checking
static unsigned int currMessageID = 0;
struct timeval timeArr[MAX_HASH_SIZE] = {0};
burneykb's avatar
burneykb committed
// global variables
static volatile int keepRunning = 1;
const char *TRACKER_IP = "UAV@192.168.0.120:3883";
Jake Drahos's avatar
Jake Drahos committed
static int zyboSocket;
Jake Drahos's avatar
Jake Drahos committed
static int backendSocket;
burneykb's avatar
burneykb committed
struct ucart_vrpn_tracker * tracker = NULL;
const char *logHeader = "";//"#\n#\tDefault log header\n#\tEverything after '#'`s will be printed as is in the processed logs.\n#\n\0";

#define MAX_CLIENTS 32
#define CLIENT_BUFFER_SIZE 1024
static char client_buffers[MAX_CLIENTS][CLIENT_BUFFER_SIZE];
static int client_fds[MAX_CLIENTS];
fd_set rfds_master;
int max_fd = 0;
burneykb's avatar
burneykb committed
pthread_mutex_t quadResponseMutex, cliInputMutex ;
char *respBuf, *commandBuf;
burneykb's avatar
burneykb committed
int newQuadResponse = 0, newCliInput = 0;

// Structures to be used throughout
modular_structs_t structs;
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;
burneykb's avatar
burneykb committed

		//updateLogFile(td);
burneykb's avatar
burneykb committed
	}
burneykb's avatar
burneykb committed
}

int main(int argc, char **argv)
{
Jake Drahos's avatar
Jake Drahos committed
	FD_ZERO(&rfds_master);
Jake Drahos's avatar
Jake Drahos committed

	/* 
	 * Create backend listening socket
	 */
	/* 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 */
	backendSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
	if (backendSocket < 0) {
		err(-1, "socket");
	}

	/* 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");
	}

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

	/* Add to socket set */
Jake Drahos's avatar
Jake Drahos committed
	safe_fd_set(backendSocket, &rfds_master, &max_fd);
burneykb's avatar
burneykb committed
	

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

Jake Drahos's avatar
Jake Drahos committed
	if (pthread_mutex_lock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
	}

	if ((zyboSocket = connectToZybo()) < 0)
	{
		perror("Error connecting to Zybo...");
		free(respBuf);
		free(commandBuf);
		exit(1);
	}
Jake Drahos's avatar
Jake Drahos committed

	if (pthread_mutex_unlock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
	}
burneykb's avatar
burneykb committed
	
	// create vrpnTracker instance
	tracker = ucart_vrpn_tracker_createInstance(TRACKER_IP);
burneykb's avatar
burneykb committed

	// open the log file
Jake Drahos's avatar
Jake Drahos committed
	if(createLogFile(argc, argv[1]))
	{
		perror("Error creating log file...");
		exit(1);
	}
//	writeStringToLog(logHeader);

	// watch for input from stdin (fd 0) to see when it has input
Jake Drahos's avatar
Jake Drahos committed
	safe_fd_set(fileno(stdin), &rfds_master, &max_fd);
	// watch for input from the zybo socket
	safe_fd_set(zyboSocket, &rfds_master, &max_fd);

	//printf("zyboSocket = %d, max_fd = %d\n", zyboSocket, max_fd);
burneykb's avatar
burneykb committed

	//tell the quad we are ready to send it vrpn data
	sendStartPacket();
burneykb's avatar
burneykb committed

	// this function will be called whenever tracker receives data
	ucart_vrpn_tracker_addCallback(tracker, cb);
Jake Drahos's avatar
Jake Drahos committed

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

	respBuf = calloc(CMD_MAX_LENGTH, sizeof(unsigned char));
	sleep(3);
burneykb's avatar
burneykb committed
	{
Jake Drahos's avatar
Jake Drahos committed
		fd_set rfds;
		rfds = rfds_master;
		activity = select(max_fd+1, &rfds, NULL, NULL, NULL);
		if(activity == -1) {
			perror("select() ");
		} else if (activity) {
Jake Drahos's avatar
Jake Drahos committed
			for(int fd = 0; fd <= max_fd; ++fd) {
					if(wasDisconnected(fd))
						break;
					if (fd == fileno(stdin)) {
						// break;
						// unsigned char userCommand[CMD_MAX_LENGTH];
						// read(fileno(stdin), (char *)userCommand, sizeof(userCommand));
						// unsigned int cmdLen = strlen((char*) userCommand);
						// // if the user simply hit enter then let them try again
						// if((userCommand[0] != '\n') && (userCommand[0] != '\r'))
						// {
						// 	// remove newline and return line chars
						// 	if((userCommand[cmdLen - 1] == '\n') || (userCommand[cmdLen - 1] == '\r'))
						// 	userCommand[cmdLen - 1] = '\0';

						// 	unsigned char *packet;
						// 	formatCommand(userCommand, &packet);

						// 	printf("received input from cli: '%s'\n", userCommand);

						// 	// Write the command to the control_loop socket
						// 	// int n = writeQuad(packet, ((packet[6] << 8) | packet[5]) + 8);
						// 	// if(n < 0) {
						// 	// 	printf("CLI: ERROR writing to socket\n");
						// printf("$microcart> ");
						// memset(userCommand, 0, cmdLen);
						// Read the response from the control loop
						int available;
						ioctl(fd, FIONREAD, &available);
						if (available < 12)
							continue;
						int respLen = readQuad(respBuf, 12);
						if(respLen <= 0) {
							printf("CLI: ERROR reading from socket %d: %s\n", respLen, strerror(errno));
						}
						//printf("recognized info from quad\n");
						int id = getInt((unsigned char *)respBuf, 7);
						findTimeDiff(id);
						// if(respLen == 11) {
						// 	int id = getInt((unsigned char *)respBuf, 7);
						// 	findTimeDiff(id);
						// 	printf("respLen = %d : id = %d'\n", respLen, id);
						// 	for(int i = 0; i <= respLen -1; ++i)
						// 		printf("%x ", (unsigned char)respBuf[i]);
						// 	printf("'\n");
						// }
						memset(respBuf, 0, respLen);
Jake Drahos's avatar
Jake Drahos committed
					} else if (fd == backendSocket) {
						int new_fd = 0;
						new_fd = accept(backendSocket, NULL, NULL);
						if (new_fd < 0) {
							warn("accept");
						} else {
							printf("Connection\n");
Jake Drahos's avatar
Jake Drahos committed
							if (new_client(new_fd)) {
								printf("Added client\n");
Jake Drahos's avatar
Jake Drahos committed
								safe_fd_set(new_fd, &rfds_master, &max_fd);
Jake Drahos's avatar
Jake Drahos committed
						}
					} else if (get_client_index(fd) > -1) {
						client_recv(fd);
		} else {
Jake Drahos's avatar
Jake Drahos committed
			timeout.tv_sec = 1;
			timeout.tv_usec = 0;
burneykb's avatar
burneykb committed
		}
	}

	ucart_vrpn_tracker_freeInstance(tracker);
burneykb's avatar
burneykb committed
	safe_close_fd(zyboSocket, &quadSocketMutex);

Jake Drahos's avatar
Jake Drahos committed
	closeLogFile();
burneykb's avatar
burneykb committed
	return 0;
}

void sendStartPacket() {
	unsigned char packet[8] = {0};
	currMessageID++;
burneykb's avatar
burneykb committed
	metadata_t metadata = 
	{
		(char) BEGIN_CHAR,
burneykb's avatar
burneykb committed
		0x04,
		0x01,
		0x01,
		0
	};

	packet[0] = metadata.begin_char; // BEGIN					//PACKET_START_BYTE;
	packet[1] = metadata.msg_type;	// UPDATE				//'U'; // U for vrpn camera update, C for command
	packet[2] = metadata.msg_subtype; // BEGIN UPDATE
	packet[3] = (currMessageID & 0x000000ff); 			// MSG ID(1)
	packet[4] =	((currMessageID >> 8) & 0x000000ff); // MSG ID(2)
burneykb's avatar
burneykb committed
	packet[5] = 0; 			// DATALEN(1)
	packet[6] = 0; 	// DATALEN(2)
	
	char checksum = 0;
	int i;
	for(i=0; i < metadata.data_len + 7; i++)
		checksum ^= packet[i];

	packet[metadata.data_len + 7] = checksum; //PACKET_END_BYTE;
Jake Drahos's avatar
Jake Drahos committed
	int status = writeQuad((char * ) packet, metadata.data_len + 8);
burneykb's avatar
burneykb committed
	if (status != 8)
	{
		perror("Error sending start packet...\n");
		keepRunning = 0;
	}else
	{
		printf("Start packet successfuly sent...\n");
burneykb's avatar
burneykb committed
}

void sendVrpnPacket(struct ucart_vrpn_TrackerData *info) {
	int pSize = sizeof(info) + 8;
	int n;
	unsigned char packet[pSize];
	currMessageID++;
burneykb's avatar
burneykb committed
	packet[0] = 0xBE; // BEGIN					//PACKET_START_BYTE;
	packet[1] = 0x04;	// UPDATE				//'U'; // U for vrpn camera update, C for command
	packet[2] = 0x00; // N/A
	//TODO Figure out Packet ID with this new ucar_vrpn_TrackerData struct
	packet[3] = (currMessageID & 0x000000ff); 			// MSG ID(1)
	packet[4] =	((currMessageID >> 8) & 0x000000ff); // MSG ID(2)
burneykb's avatar
burneykb committed
	packet[5] = (sizeof(info) & 0x000000ff); 			// DATALEN(1)
	packet[6] = ((sizeof(info) >> 8) & 0x00000ff); 	// DATALEN(2)
	memcpy(&packet[7], &info, sizeof(info));

	char checksum = 0;
	int i;
	for(i=0; i < pSize - 1; i++)
		checksum ^= packet[i];

	packet[pSize - 1] = checksum; //PACKET_END_BYTE;

	n = writeQuad((char *) packet, pSize);
burneykb's avatar
burneykb committed
	if(n < 0) {	
		perror("vrpnhandler: ERROR writing to socket");
		keepRunning = 0;
	}
	struct timeval tstart;
	gettimeofday(&tstart, NULL);
	timeArr[currMessageID%MAX_HASH_SIZE] = tstart;
burneykb's avatar
burneykb committed
}

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

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);
}

int connectToZybo() {
	int sock;
	int status = 0;
burneykb's avatar
burneykb committed

	/* Use bluetooth by default */
	if (getenv(QUAD_WIFI_ENV) == NULL && 0) {
		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 to connect to	
		addr.rc_family = AF_BLUETOOTH;
		addr.rc_channel = (uint8_t) QUAD_BT_CHANNEL;
		str2ba( QUAD_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 (getenv(QUAD_IP_ENV)) {
			 if (!inet_aton(getenv(QUAD_IP_ENV), &addr.sin_addr)) {
				printf("Env var %s invalid IP %s\n",
					     	QUAD_IP_ENV, getenv(QUAD_IP_ENV));
				return -1;
			 }
		} else {
			if (!inet_aton(QUAD_IP_DEFAULT, &addr.sin_addr)) {
				printf("Default IP %s is invalid\n",
					     	QUAD_IP_DEFAULT);
				return -1;
			}
		}
burneykb's avatar
burneykb committed

		if (getenv(QUAD_PORT_ENV)) {
			/* Quick 'n dirty, oh yeah! */
			addr.sin_port = htons(atoi(getenv(QUAD_PORT_ENV)));		
		} else {
			printf("using default port\n");
			addr.sin_port = htons(QUAD_PORT_DEFAULT);
		sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (sock < 0) {
			perror("socket");
			return -1;
		}

		status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
burneykb's avatar
burneykb committed

	// connection failed
	if(status < 0)
	{
		close(sock);
		perror("connect");
burneykb's avatar
burneykb committed
		return -1;
burneykb's avatar
burneykb committed
	else
	{
		printf("connection successful!...\n");
		return sock;
	}
}
/* 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;
Jake Drahos's avatar
Jake Drahos committed

static ssize_t writeQuad(const char * buf, size_t count) {
	ssize_t retval;
	if (0) {//getenv(NOQUAD_ENV)) {
Jake Drahos's avatar
Jake Drahos committed
		return count;
	}
Jake Drahos's avatar
Jake Drahos committed
	if (pthread_mutex_lock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
	}
	retval = write(zyboSocket, buf, count);
Jake Drahos's avatar
Jake Drahos committed
	if (pthread_mutex_unlock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
	}

	return retval;
}
static ssize_t readQuad(char * buf, size_t count) {
	ssize_t retval;
	if (pthread_mutex_lock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
	}
	retval = read(zyboSocket, buf, count);
	if (pthread_mutex_unlock(&quadSocketMutex)) {
		err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
	}
	return retval;
}

static int new_client(int fd) {
Jake Drahos's avatar
Jake Drahos committed
	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!");
Jake Drahos's avatar
Jake Drahos committed
	client_fds[new_slot] = fd;
	client_buffers[new_slot][0] = '\0';
Jake Drahos's avatar
Jake Drahos committed

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

	return -1;
}

static char * get_client_buffer(int fd) {
Jake Drahos's avatar
Jake Drahos committed
	ssize_t slot = get_client_index(fd);
	if (slot == -1) {
		return NULL;
	} else {
		return client_buffers[slot];
	}
}
burneykb's avatar
burneykb committed
static int remove_client(int fd) {
	ssize_t slot = get_client_index(fd);
	if(slot == -1)
burneykb's avatar
burneykb committed
		return -1;
	char *clientBuffer = get_client_buffer(fd);
	if(clientBuffer == NULL)
		return -1;
	clientBuffer[0] = '\0';
burneykb's avatar
burneykb committed
	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;
     	buffer = get_client_buffer(fd);
	len_pre = strlen(buffer);

	char * cursor;
	cursor = buffer + len_pre;

	ssize_t r;
	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';
		unsigned char * packet;
		// printf("newline =%li, Client sent: '%s'\n", newline, buffer);
		if(formatCommand(buffer, &packet) != -1) {
			printf("Backend sees as: %f\n", getFloat(packet, 7));
		} else {
			printf("Could not recognize command '%s'\n", buffer);
		}
		writeQuad((char *) packet, len);
		//free(packet);
		
		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);

static int wasDisconnected(int fd) {
	char buff;
	if(recv(fd, &buff, 1, MSG_PEEK | MSG_DONTWAIT) == 0)
	{
		remove_client(fd);
		safe_fd_clr(fd, &rfds_master, &max_fd);
		printf("fd %d has disconnect and was removed\n", fd);
		return 1;
	}
	return 0;
}

int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y) {
  /* Perform the carry for the later subtraction by updating y. */
  if (x->tv_usec < y->tv_usec) {
    int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
    y->tv_usec -= 1000000 * nsec;
    y->tv_sec += nsec;
  }
  if (x->tv_usec - y->tv_usec > 1000000) {
    int nsec = (x->tv_usec - y->tv_usec) / 1000000;
    y->tv_usec += 1000000 * nsec;
    y->tv_sec -= nsec;
  }
  /* Compute the time remaining to wait.
     tv_usec is certainly positive. */
  result->tv_sec = x->tv_sec - y->tv_sec;
  result->tv_usec = x->tv_usec - y->tv_usec;
  /* Return 1 if result is negative. */
  return x->tv_sec < y->tv_sec;
}

void findTimeDiff(int respID) {
	struct timeval result, tend;
	gettimeofday(&tend, NULL);
	timeval_subtract(&result, &tend, &timeArr[respID%MAX_HASH_SIZE]);
	printf("elapsed time = %ld ms\n", result.tv_usec/1000);
	// char tmp[8];
	// snprintf(tmp, 8, "%ld \tms\n", result.tv_usec/1000);
	// writeStringToLog(tmp);
}