//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the //National Aeronautics and Space Administration. All Rights Reserved. // //DISCLAIMERS // No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, // EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY // THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, // WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN // ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, // HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT // SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING // THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." // // Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES // GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF // RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES // OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING // FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE // UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, // TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE // IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. // X-Plane Connect Client // // DESCRIPTION // Communicates with the XPC plugin to facilitate controling and gathering data from X-Plane. // // INSTRUCTIONS // See Readme.md in the root of this repository or the wiki hosted on GitHub at // https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions, // and detailed documentation. // // CONTACT // For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) // // CONTRIBUTORS // CT: Christopher Teubert (christopher.a.teubert@nasa.gov) // JW: Jason Watkins (jason.w.watkins@nasa.gov) #include "xplaneConnect.h" #include #include #include #include #include #include #include #ifdef _WIN32 /* WIN32 SYSTEM */ #include // From http://www.c-plusplus.de/forum/109539-full void usleep(__int64 usec) { HANDLE timer; LARGE_INTEGER ft; ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time timer = CreateWaitableTimer(NULL, TRUE, NULL); SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); } #endif void printError(char *functionName, char *errorMessage); short sendRequest(XPCSocket recfd, char DREFArray[][100], short DREFSizes[], short listLength); void printError(char *functionName, char *errorMessage) { printf("[%s] ERROR: %s\n", functionName, errorMessage); } /*****************************************************************************/ /**** Low Level UDP functions ****/ /*****************************************************************************/ XPCSocket openUDP(unsigned short port, const char *xpIP, unsigned short xpPort) { XPCSocket sock; // Setup Port struct sockaddr_in recvaddr; recvaddr.sin_family = AF_INET; recvaddr.sin_addr.s_addr = INADDR_ANY; recvaddr.sin_port = htons(port); // Set X-Plane Port and IP if (strcmp(xpIP, "localhost") == 0) { xpIP = "127.0.0.1"; } strncpy(sock.xpIP, xpIP, 16); sock.xpPort = xpPort == 0 ? 49009 : xpPort; #ifdef _WIN32 WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printError("OpenUDP", "WSAStartup failed"); exit(EXIT_FAILURE); } #endif if ((sock.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { printError("OpenUDP", "Socket creation failed"); exit(EXIT_FAILURE); } if (bind(sock.sock, (struct sockaddr*)&recvaddr, sizeof(recvaddr)) == -1) { printError("OpenUDP", "Socket bind failed"); exit(EXIT_FAILURE); } #ifdef _WIN32 DWORD timeout = 1; // Minimum socket timeout in Windows is 1ms #else struct timeval timeout; tv.tv_sec = 0; tv.tv_usec = 500; #endif if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { printError("OpenUDP", "Failed to set timeout"); } return sock; } void closeUDP(XPCSocket socketNumber) { #ifdef _WIN32 int result = closesocket(socketNumber.sock); #else int result = close(socketNumber.sock); #endif if (result < 0) { printError("closeUDP", "Failed to close socket"); exit(EXIT_FAILURE); } } int sendUDP(XPCSocket sock, char buffer[], int len) { // Preconditions if (len <= 0) { printError("sendUDP", "Message length must be positive."); return -1; } // Set up destination address struct sockaddr_in dst; dst.sin_family = AF_INET; dst.sin_port = htons(sock.xpPort); inet_pton(AF_INET, sock.xpIP, &dst.sin_addr.s_addr); int result = sendto(sock.sock, buffer, len, 0, (const struct sockaddr*)&dst, sizeof(dst)); if (result < 0) { printError("sendUDP", "Send operation failed."); return -2; } if (result < len) { printError("sendUDP", "Unexpected number of bytes sent."); } return result; } int readUDP(XPCSocket sock, char buffer[], int len, struct sockaddr* recvaddr) { socklen_t recvaddrlen = sizeof(*recvaddr); #ifdef _WIN32 // Windows readUDP needs the select command- minimum timeout is 1ms. // Without this playback becomes choppy // Definitions FD_SET stReadFDS; FD_SET stExceptFDS; // Setup for Select FD_ZERO(&stReadFDS); FD_SET(sock.sock, &stReadFDS); FD_ZERO(&stExceptFDS); FD_SET(sock.sock, &stExceptFDS); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 250; // Select Command int status = select(-1, &stReadFDS, (FD_SET*)0, &stExceptFDS, &tv); if (status < 0) { printError("readUDP", "Select command error"); return -1; } if (status == 0) { // No data return 0; } status = recvfrom(sock.sock, buffer, len, 0, (struct sockaddr*)&recvaddr, &recvaddrlen); #else // For apple or linux-just read - will timeout in 0.5 ms status = (int)recvfrom(sock.sock, dataRef, len, 0, recvaddr, &recvaddrlen); #endif if (status < 0) { printError("readUDP", "Error reading socket"); } return status; } /*****************************************************************************/ /**** End Low Level UDP functions ****/ /*****************************************************************************/ short sendDATA(XPCSocket recfd, float dataRef[][9], unsigned short rows) { int i; unsigned short length = rows*9*sizeof(float) + 5; char message[5000]; unsigned short step = 9*sizeof(float); strncpy(message,"DATA",4); for (i=0;i xpc_WYPT_CLR) { return -1; } //Validate number of points else if (numPoints > 19) { return -2; } //Everything checks out; send the message else { buf[5] = op; buf[6] = numPoints; size_t len = sizeof(float) * 3 * numPoints; memcpy(buf + 7, points, len); sendUDP(sendfd, buf, len + 7); return 0; } } //READ //---------------------------------------- short readDATA(XPCSocket recfd, float dataRef[][9]) { char buf[5000]; int buflen = readUDP(recfd,buf, NULL); if (buf[0]!= '\0') { return parseDATA(buf, buflen, dataRef); } return -1; } short readRequest(XPCSocket recfd, float *dataRef[], short arraySizes[], struct sockaddr *recvaddr) { char buf[5000]; readUDP(recfd,buf, recvaddr); if (buf[0]!= '\0') { return parseRequest(buf, dataRef, arraySizes); } return -1; } short readDREF(XPCSocket recfd, float *resultArray[], short arraySizes[]) { char buf[5000] = {0}; readUDP(recfd,buf, NULL); if (buf[0]!= '\0') { return parseRequest(buf, resultArray, arraySizes); } return -1; } short readPOSI(XPCSocket recfd, float resultArray[], int arraySize, float *gear) { char buf[5000] = {0}; readUDP(recfd,buf, NULL); if (buf[0]!= '\0') // Buffer is not empty { return parsePOSI(buf, resultArray, arraySize, gear); } return -1; } xpcCtrl readCTRL(XPCSocket recfd) { xpcCtrl result; char buf[5000] = { 0 }; readUDP(recfd, buf, NULL); if (buf[0] != '\0') // Buffer is not empty { result = parseCTRL(buf); } else { result.aircraft = -1; } return result; } //PARSE //--------------------- short parseDATA(const char my_message[], short messageLength, float dataRef[][9]) { int i, j; unsigned short totalColumns = ((messageLength-5)/36); float tmp; float data[20][9]; // Input Validation for (i=0;i xpc_WYPT_CLR) { result.op = -1; } //Validate number of points else if (data[6] > 19) { result.op = -2; } //Everything checks out; copy the points into result else { result.op = data[5]; result.numPoints = data[6]; char* ptr = data + 7; for (size_t i = 0; i < result.numPoints; ++i) { result.points[i].latitude = *((float*)ptr); result.points[i].longitude = *((float*)(ptr + 4)); result.points[i].altitude = *((float*)(ptr + 8)); ptr += 12; } } return result; }