Skip to content
Snippets Groups Projects
microcart_cli.c 13.8 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

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

// function prototypes
void killHandler(int);
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 startsWith(const char *, const char *);
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);
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 ;
unsigned char *respBuf, *commandBuf;
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

	if(!(count % 10)) {
		sendVrpnPacket(td);
		updateLogFile(td);
burneykb's avatar
burneykb committed
	}

	count++;
	// This will print the vrpn data to the terminal if necissary.
	// Commented out because the callback will cover quad log data
	//   at the end of flight.
	/**if(!(count % 100)) {
	 	printVrpnData(td);
	 	printf("[Info] Received %d tracker updates.\n", count);
	}**/
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';
	}

	//signal(SIGINT, killHandler);
burneykb's avatar
burneykb committed

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
Jake Drahos's avatar
Jake Drahos committed
	//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
	printf("sending Start Packet...\n");
burneykb's avatar
burneykb committed

	// this function will be called whenever tracker receives data
	// ucart_vrpn_tracker_addCallback(tracker, cb);
	// fprintf(stdout, "$microcart> ");
Jake Drahos's avatar
Jake Drahos committed

	struct timeval timeout = {
		.tv_sec = 1,
		.tv_usec = 0
	};
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
			fprintf(stderr, "Select activity = %d\n", 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) {
						// 	// 	fprintf(stdout, "CLI: ERROR writing to socket\n");
						// 	// }
						// }

						// fprintf(stdout, "$microcart> ");
						// memset(userCommand, 0, cmdLen);
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 {
Jake Drahos's avatar
Jake Drahos committed
							fprintf(stderr, "Connection\n");
							if (new_client(new_fd)) {
								fprintf(stderr, "Added client\n");
								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;
}

// signal handler to exit while loop of main function
void killHandler(int dummy) {
	keepRunning = 0;
	printf("\nleaving Bluetooth module\n");
}

void sendStartPacket() {
	unsigned char packet[8] = {0};

	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] = 1; 			// MSG ID(1)
	packet[4] =	0; // MSG ID(2)
	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");
	}
}

void sendVrpnPacket(struct ucart_vrpn_TrackerData *info) {
	int pSize = sizeof(info) + 8;
	int n;
	unsigned char packet[pSize];
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] = (0x00 & 0x000000ff); 			// MSG ID(1)
	packet[4] =	((0x00 >> 8) & 0x000000ff); // MSG ID(2)
	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;
	}
}

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;
	struct sockaddr_rc addr;
burneykb's avatar
burneykb committed

	// 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
Jake Drahos's avatar
Jake Drahos committed
	int status = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
burneykb's avatar
burneykb committed

	// connection failed
	if(status < 0)
	{
		close(sock);
		printf("Connection failed!...\n");
		return -1;
	} 
	else
	{
		printf("connection successful!...\n");
		return sock;
	}
}
int startsWith(const char *pre, const char *str) {
    size_t lenpre = strlen(pre),
           lenstr = strlen(str);
    return lenstr < lenpre ? 0 : (strncmp(pre, str, lenpre) == 0);
}

/* 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 (1) {//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 = writeQuad(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)
{
	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)
{
	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;
		// fprintf(stderr, "newline =%li, Client sent: '%s'\n", newline, buffer);
		if(formatCommand(buffer, &packet) != -1) {
			fprintf(stdout, "Backend sees as: %f\n", getFloat(packet, 7));
		} else {
			fprintf(stderr, "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);
		fprintf(stderr, "fd %d has disconnect and was removed\n", fd);
		return 1;
	}
	return 0;
}