Newer
Older
*
* BlueTooth socket program for passing vrpn data to quad.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <pthread.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include "commands.h"
#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
/* Backend-internal command magics */
#define TD_MAGIC "TRACKERDATA"
// 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* );
static void safe_close_fd(int fd, pthread_mutex_t *mutexLock);
static void cb(struct ucart_vrpn_TrackerData *);
/* 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, unsigned char *packet);
/* 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);
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;
// global variables
static volatile int keepRunning = 1;
const char *TRACKER_IP = "UAV@192.168.0.120:3883";
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
#define CLIENT_MAX_PENDING_RESPONSES 5
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];
// Structures to be used throughout
// 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;
if(!(count % 10)) {
sendVrpnPacket(td);
count++;
int activity;
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
* 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 */
/* Initialize client buffers */
for (int 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;
}
if (pthread_mutex_lock(&quadSocketMutex)) {
err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
}
if ((zyboSocket = connectToZybo()) < 0)
{
if (pthread_mutex_unlock(&quadSocketMutex)) {
err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__);
}
{
perror("Error creating log file...");
exit(1);
}
// watch for input from stdin (fd 0) to see when it has input
if (!getenv(NOQUAD_ENV)) {
// watch for input from the zybo socket
safe_fd_set(zyboSocket, &rfds_master, &max_fd);
}
if(!getenv(NOVRPN_ENV)){
// create vrpnTracker instance
tracker = ucart_vrpn_tracker_createInstance(TRACKER_IP);
// this function will be called whenever tracker receives data
ucart_vrpn_tracker_addCallback(tracker, cb);
}
struct timeval timeout = {
.tv_sec = 1,
.tv_usec = 0
};
activity = select(max_fd+1, &rfds, NULL, NULL, NULL);
if(activity == -1) {
perror("select() ");
} else if (activity) {
if (FD_ISSET(fd, &rfds)) {
/**
* Ignore stdin from the backend
*/
} else if (fd == zyboSocket) {
printf("recieving from quad\n");
} else if (fd == backendSocket) {
int new_fd = 0;
new_fd = accept(backendSocket, NULL, NULL);
if (new_fd < 0) {
warn("accept");
} else {
} else if (get_client_index(fd) > -1) {
/* It is a socket to a frontend */
}
}
}
ucart_vrpn_tracker_freeInstance(tracker);
safe_close_fd(zyboSocket, &quadSocketMutex);
return 0;
}
void sendStartPacket() {
unsigned char packet[8] = {0};
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)
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;
int status = writeQuad((char * ) 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(struct ucart_vrpn_TrackerData) + 8;
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)
packet[5] = (sizeof(struct ucart_vrpn_TrackerData) & 0x000000ff); // DATALEN(1)
packet[6] = ((sizeof(struct ucart_vrpn_TrackerData) >> 8) & 0x00000ff); // DATALEN(2)
memcpy(&packet[7], &info, sizeof(struct ucart_vrpn_TrackerData));
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);
if(n < 0) {
perror("vrpnhandler: ERROR writing to socket");
keepRunning = 0;
}
struct timeval tstart;
gettimeofday(&tstart, NULL);
timeArr[currMessageID%MAX_HASH_SIZE] = tstart;
}
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;
if (getenv(NOQUAD_ENV)) {
return 0;
}
if (!getenv(QUAD_WIFI_ENV)) {
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 {
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)) {
QUAD_IP_ENV, getenv(QUAD_IP_ENV));
return -1;
}
} else {
if (!inet_aton(QUAD_IP_DEFAULT, &addr.sin_addr)) {
if (getenv(QUAD_PORT_ENV)) {
/* Quick 'n dirty, oh yeah! */
addr.sin_port = htons(atoi(getenv(QUAD_PORT_ENV)));
addr.sin_port = htons(QUAD_PORT_DEFAULT);
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));
// connection failed
if(status < 0)
{
close(sock);
// int result = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (int[]){1}, sizeof(int));
// printf("result = %d\n", result);
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;
static ssize_t writeQuad(const char * buf, size_t count) {
ssize_t retval;
if (pthread_mutex_lock(&quadSocketMutex)) {
err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__);
}
//setsockopt(zyboSocket, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int));
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);
//int result = setsockopt(zyboSocket, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int));
//printf("result = %d\n", result);
if (pthread_mutex_unlock(&quadSocketMutex)) {
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!");
client_fds[new_slot] = fd;
client_buffers[new_slot][0] = '\0';
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];
}
}
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, unsigned char *packet) {
int *pendingResponses = get_client_pend_responses(fd);
int packetID = (packet[4] << 8) | (packet[3]);
for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) {
if(pendingResponses[i] == -1) {
pendingResponses[i] = packetID;
return i;
}
}
return -1;
}
ssize_t slot = get_client_index(fd);
if(slot == -1)
char *clientBuffer = get_client_buffer(fd);
if(clientBuffer == NULL)
return -1;
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;
}
}
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__);
}
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';
printf("Client(%d) : '%s'\n",fd, buffer);
if(formatCommand(buffer, &packet) == -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;
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));
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.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"));
}
if(clientAddPendResponses(fd, packet) == -1) {
warnx("Ran out of room! Consider increasing CLIENT_MAX_PENDING_RESPONSES!\n");
} else {
int datalen = (packet[6] << 8) | (packet[5]);
printf("sending %lf '", getFloat(packet, 7));
for(int i = 0; i < datalen + 8; ++i) {
printf(" 0x%x", (signed) packet[i]);
}
printf("'\n");
writeQuad((char *) packet, datalen +8);
}
char * rest = &buffer[newline] + 1;
size_t restLen = (strlen(rest) == 0) ? 1 : strlen(rest);
/* Delete parsed data and move the rest to the left */
/* Check to see which command we are receiving. If it is one that needs to be passed on
onto the clients, do so.
*/
int validPacket;
unsigned char *data;
metadata_t metadata;
static unsigned char respBuf[2048];
static size_t respBufLen;
/**
* Read the response from the control loop
*/
int respLen = readQuad((char *) respBuf + respBufLen,
CMD_MAX_LENGTH);
if(respLen <= 0) {
perror("ERROR reading from quad...\n");
return;
}
respBufLen += respLen;
if (respBufLen < 8) {
/* not long enough yet */
return;
}
// Validate the message is correctly formatted
validPacket = parse_packet((unsigned char *) respBuf, &data, &metadata);
if (validPacket == -1) {
warnx("Doesn't have start byte.");
/* nuke packet */
respBufLen = 0;
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/* Get datalen */
size_t datalen = metadata.data_len;
if (datalen > CMD_MAX_LENGTH - 8) {
/* Very invalid packet. Nuke that shit */
respBufLen = 0;
return;
}
if (respBufLen < datalen + 8) {
/* Packet not yet fully read */
return;
}
if (validPacket == 0) {
/* At least enough data read to check checksum, and it was good!*/
char * cmdText = MessageTypes[(int)metadata.msg_type].subtypes[(int)metadata.msg_subtype].cmdText;
float value = getFloat((unsigned char *)respBuf, 7);
printf("Quad : %s, %lf\n", cmdText, value);
/*
Assuming the quad sends the correct info.. This hasn't been tested yet due to a lack of
quad software. We can check how to format by the cmdText and pass to every client.
*/
char buffer[1048];
sprintf(buffer, "%s %lf\n", cmdText, value);
for(int fd = 0; fd <= max_fd; ++fd) {
if (get_client_index(fd) > -1) {
write(fd, buffer, datalen + 8);
}
} else {
warnx("Checksum mismatch!");
memmove(respBuf, respBuf + datalen + 8, respBufLen - (datalen + 8));
respBufLen -= datalen + 8;
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);
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
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);