Skip to content
Snippets Groups Projects
microcart_cli.c 10 KiB
Newer Older
burneykb's avatar
burneykb committed
/* Author: Kris Burney
 *
 * BlueTooth socket program for passing vrpn data to quad.
 */
//system includes
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <pthread.h>

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

#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();
void *handleQuadResponse();
void *handleCliInput();
int atomic_check(int*, pthread_mutex_t*);
void performCommand(char *cmdName, char * command);
int startsWith(const char *pre, const char *str);
burneykb's avatar
burneykb committed

//static void cb(struct ucart_vrpn_TrackerData *);
burneykb's avatar
burneykb committed

// global variables
static volatile int keepRunning = 1;
const char *TRACKER_IP = "UAV@192.168.0.120:3883";
int quadlog_file;
int zyboSocket, status, bytes_read;
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";

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

// 	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


void *handleQuadResponse() {
	unsigned char buffer[255];

	while(keepRunning) {
		// Clear the buffer and read the message from the socket (from the server)
		memset(buffer, 0, 255);

		// If there was an error reading from the socket, throw an error
		if(read(zyboSocket, buffer, 255) <= 0) {
			fprintf(stderr, "CLI QUAD: ERROR reading from quad.\n");
			continue;
		}

		pthread_mutex_lock(&quadResponseMutex);
		newQuadResponse = 1;
		memcpy(respBuf, buffer, 255);
		pthread_mutex_unlock(&quadResponseMutex);

		//parse_packet(buffer, &respBuf, &metadata);
		// fprintf(stderr, "CLI QUAD: %s\n", buffer);
burneykb's avatar
burneykb committed
	}

	pthread_exit(NULL);
}

void *handleCliInput() {
burneykb's avatar
burneykb committed
	while(keepRunning)
	{
		char userCommand[CMD_MAX_LENGTH] = {};
		fprintf(stderr, "$microcart> ");

		while(fgets(userCommand, sizeof(userCommand), stdin) != NULL && keepRunning) {
			
			// if the user simply hit enter then let them try again
			if((userCommand[0] == '\n') || (userCommand[0] == '\r'))
			{
				fprintf(stderr, "$microcart> ");

				memset(userCommand, 0, CMD_MAX_LENGTH);

				continue;
			}

			if((userCommand[strlen(userCommand) - 1] == '\n') || (userCommand[strlen(userCommand) - 1] == '\r'))
			userCommand[strlen(userCommand) - 1] = '\0';

burneykb's avatar
burneykb committed
			pthread_mutex_lock(&cliInputMutex);
			newCliInput = 1;
			memcpy(commandBuf, &userCommand, CMD_MAX_LENGTH);
			pthread_mutex_unlock(&cliInputMutex);

			fprintf(stderr, "$microcart> ");

			memset(userCommand, 0, CMD_MAX_LENGTH);
		}
	}

	pthread_exit(NULL);
}

int main(int argc, char **argv)
{
	pthread_t quadResponse, cliInput;
	respBuf = malloc(1024);
	commandBuf = malloc(CMD_MAX_LENGTH);
	
	signal(SIGINT, killHandler);

	if ((zyboSocket = connectToZybo()) < 0)
	{
		perror("Error connecting to Zybo...");
		free(respBuf);
		free(commandBuf);
burneykb's avatar
burneykb committed
	
	// create vrpnTracker instance
//	tracker = ucart_vrpn_tracker_createInstance(TRACKER_IP);

	// Retrieve VRPN data from the control loop
	fprintf(stderr, "CLI: Starting quad receiving thread...\n");
	pthread_create(&quadResponse, NULL, handleQuadResponse, NULL);
	fprintf(stderr, "CLI: Thread quad receiving started.\n");

	// Retrieve user command input
burneykb's avatar
burneykb committed
	fprintf(stderr, "CLI: Starting cli input thread...\n");
	pthread_create(&cliInput, NULL, handleCliInput, NULL);
	fprintf(stderr, "CLI: Thread Cli input started.\n");

	// open the log file
	if( (status = createLogFile(argc, argv[1])) < 0)
	{
		perror("Error creating log file...");
		free(respBuf);
		free(commandBuf);
		exit(1);
	}
	writeStringToLog(logHeader);
burneykb's avatar
burneykb committed

	//tell the quad we are ready to send it vrpn data
	printf("sending Start Packet...\n");
	sendStartPacket();
burneykb's avatar
burneykb committed

	// this function will be called whenever tracker receives data
	// ucart_vrpn_tracker_addCallback(tracker, cb);
	int updatePrompt = 0;
burneykb's avatar
burneykb committed
	{
burneykb's avatar
burneykb committed
		char tmpRespBuf[1024];
		unsigned char tmpCommandBuf[CMD_MAX_LENGTH];


		if(updatePrompt)
		{
			fprintf(stderr, "$microcart> ");
burneykb's avatar
burneykb committed
		//check for user input via cli
		if(atomic_check(&newCliInput, &cliInputMutex))
burneykb's avatar
burneykb committed
		{
			pthread_mutex_lock(&cliInputMutex);
			newCliInput = !newCliInput;
burneykb's avatar
burneykb committed
			memcpy(tmpCommandBuf, commandBuf, CMD_MAX_LENGTH);
			pthread_mutex_unlock(&cliInputMutex);

			// I can use printf becuase the command was gathered using fgets.
			//fprintf(stderr, "\rINPUT FOUND via CLI: '%s'\n", tmpCommandBuf);
			updatePrompt = !updatePrompt;
			unsigned char *packet;
			formatCommand(tmpCommandBuf, &packet);
burneykb's avatar
burneykb committed
		}

		//check for update/response from quad
		if(atomic_check(&newQuadResponse, &quadResponseMutex))
burneykb's avatar
burneykb committed
		{
			pthread_mutex_lock(&quadResponseMutex);
			newQuadResponse = !newQuadResponse;
burneykb's avatar
burneykb committed
			memcpy(tmpRespBuf, respBuf, 1024);
			pthread_mutex_unlock(&quadResponseMutex);

			char buf[1025];
			memcpy(buf, tmpRespBuf, 1024);
			buf[1025] = '\0';
			//fprintf(stderr, "\rINPUT FOUND via QUAD: '%s'\n", buf);
			writeStringToLog(buf);
			updatePrompt = !updatePrompt;
burneykb's avatar
burneykb committed
		}
	}

	//ucart_vrpn_tracker_freeInstance(tracker);
burneykb's avatar
burneykb committed
	//free(vrpnData);
	free(respBuf);
	free(commandBuf);
	pthread_mutex_destroy(&cliInputMutex);
	pthread_mutex_destroy(&quadResponseMutex);
	close(zyboSocket);
	close(quadlog_file);
	return 0;
}

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

void readAndPrint() {
	// read data from the server
	// this is a blocking call.
	//TODO: Implement a non blocking version of this.
	bytes_read = read(zyboSocket, respBuf, sizeof(respBuf) -1);
	if( bytes_read > 0)
	{
		respBuf[bytes_read] = '\0';
		printf("%s", respBuf);
	}
}

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

	metadata_t metadata = 
	{
		BEGIN_CHAR,
		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;
	status = write(zyboSocket, &packet, metadata.data_len + 8);
	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;
	char packet[pSize];
	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 = write(zyboSocket, packet, pSize);
	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 = { -1 };

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

	// connection failed
	if(status < 0)
	{
		close(sock);
		printf("Connection failed!...\n");
		return -1;
	} 
	else
	{
		printf("connection successful!...\n");
		return sock;
	}
}

int atomic_check(int* atomicFlag, pthread_mutex_t* mutex) {
   pthread_mutex_lock(mutex);
   int result = *atomicFlag;
   pthread_mutex_unlock(mutex);
   return result;
}

void performCommand(char *cmdName, char * command) {
	for(int i = 0; i < NUM_COMMANDS; ++i)
	{
		if(startsWith(registeredCommands[i].commandText, command));
			fprintf(stderr, "\r\n You used cmd '%s'\n",registeredCommands[i].commandText);
	}
}

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