diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index 99d3071..0c59442 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -1,733 +1,677 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. // -// xPlaneConnect.c Beta +//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 -// XPLANECONNECT (XPC) Facilitates Communication to and from the XPCPlugin -// -// REQUIREMENTS -// 1. X-Plane Version 9.0 or newer (untested on previous versions) -// 2. XPCPlugin.xpl-must be placed in [X-Plane Directory]/Resources/plugins -// 3. OS X 10.8 or newer (untested on previous versions) +// Communicates with the XPC plugin to facilitate controling and gathering data from X-Plane. // // INSTRUCTIONS -// 1. Include the header "xplaneConnect.h" -// EXAMPLE: #include "xplaneConnect.h" -// 2. Open UDP Socket using openUDP -// EXAMPLE: struct xpcSocket theSocket = openUDP(port_number); -// NOTE: Do not send/receive on the same socket -// 3. Use functions described below -// 4. Close UDP Socket using closeUDP -// -// NOTICES: -// Copyright ã 2013-2014 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 API -// Copyright (c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// * Neither the names of the authors nor that of X-Plane or Laminar Research may be used to endorse or promote products derived from this software without specific prior written permission from the authors or Laminar Research, respectively. -// -// X-Plane API SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// 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) // -// TO DO -// 1. Update SelectData -// 2. RequestDREF: look into removing DREFSizes -// -// BEGIN CODE +// 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 #include -#include -#include "xplaneConnect.h" -#ifdef _WIN32 /* WIN32 SYSTEM */ - #include - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #define snprintf sprintf_s - #define socklen_t int - void usleep(__int64 usec) - { // From http://www.c-plusplus.de/forum/109539-full - 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 -short errorReport(char *functionName, char *errorMessage); -short sendRequest(struct xpcSocket recfd, char DREFArray[][100], short DREFSizes[], short listLength); -short errorReport(char *functionName, char *errorMessage) +void printError(char *functionName, char *format, ...) { - printf("ERROR: %s-%s\n", functionName, errorMessage); - return -1; + va_list args; + va_start(args, format); + + printf("[%s] ERROR: ", functionName); + vprintf(format, args); + printf("\n"); + + va_end(args); } -struct xpcSocket openUDP(unsigned short port_number, const char *xpIP, unsigned short xpPort) +/*****************************************************************************/ +/**** Low Level UDP functions ****/ +/*****************************************************************************/ +XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort) { - struct xpcSocket theSocket; - struct sockaddr_in server; - -#if (__APPLE__ || __linux) - struct timeval tv; - int optval = 1; -#endif - - // Setup Port - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(port_number); - - // Set X-Plane Port and IP - if (strcasecmp(xpIP,"localhost") == 0) // IP - { - strncpy(theSocket.xpIP,"127.0.0.1",9); // Default - } - else - { - memcpy(theSocket.xpIP,xpIP,(size_t) fminl(strlen(xpIP),16)); - } - - if (xpPort <= 0) // Default Port - { - theSocket.xpPort = 49009; // Default - } - else - { - theSocket.xpPort = xpPort; - } - -#ifdef _WIN32 - WSADATA wsa; - if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) - { - perror("ERROR: openUDP- "); - return theSocket; - //ERROR IN Socket Message - } - - if ((theSocket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) - { - perror("ERROR: openUDP- "); - return theSocket; - //ERROR IN Socket Message - } - - if (bind(theSocket.sock, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) - { - perror("ERROR: openUDP- "); - return theSocket; - //ERROR IN Socket Message - } -#elif (__APPLE__ || __linux) - // Create a SOCKET - if ((theSocket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) - { - perror((char*) "ERROR: openUDP- "); - return theSocket; - } - - // Options - setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); - - //Bind - if ( bind(theSocket.sock, (struct sockaddr *) &server, sizeof(server)) == -1) - { - perror( (char*) "ERROR: openUDP- "); - return theSocket; - } -#endif - - //Set Timout - int usTimeOut = 500; - -#ifdef _WIN32 - DWORD msTimeOutWin = 1; // Minimum socket timeout in Windows is 1ms - setsockopt(theSocket.sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&msTimeOutWin, sizeof(msTimeOutWin)); -#else - tv.tv_sec = 0; /* Sec Timeout */ - tv.tv_usec = usTimeOut; // Microsec Timeout - setsockopt(theSocket.sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(struct timeval)); -#endif - - return theSocket; + openUDP(xpIP, xpPort, 0); } -void closeUDP(struct xpcSocket socketNumber) +XPCSocket openUDP(const char *xpIP, unsigned short xpPort, unsigned short port) { -#ifdef _WIN32 - closesocket(socketNumber.sock); -#elif (__APPLE__ || __linux) - close(socketNumber.sock); -#endif -} - -short sendUDP(struct xpcSocket recfd, char my_message[], short messageLength) -{ - struct sockaddr_in servaddr; - - my_message[4] = (char) messageLength; - - // Preconditions - if (messageLength <= 0)// Positive Message Length + 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) { - return errorReport("sendUDP", "message length must be positive >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); } - - // Code - servaddr.sin_family = AF_INET; - servaddr.sin_port = htons(recfd.xpPort); - servaddr.sin_addr.s_addr = inet_addr(recfd.xpIP); #ifdef _WIN32 - const char on = 1; + DWORD timeout = 1; // Minimum socket timeout in Windows is 1ms #else - int on=1; + struct timeval timeout; + tv.tv_sec = 0; + tv.tv_usec = 500; #endif - - setsockopt(recfd.sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - - if (sendto(recfd.sock, my_message, (int) messageLength, 0, (const struct sockaddr *) &servaddr, sizeof(servaddr))<0) - { - perror("ERROR: sendUDP-"); - exit(EXIT_FAILURE); - } - - return 0; -} - -short sendDATA(struct 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) + // Preconditions + if (len <= 0) { + printError("sendUDP", "Message length must be positive."); return -1; } - //Validate number of points - else if (numPoints > 19) + + // 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; } - //Everything checks out; send the message - else + if (result < len) { - 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 readUDP(struct xpcSocket recfd, char *dataRef, struct sockaddr *recvaddr) -{ - socklen_t recvaddrlen = sizeof(*recvaddr); - int status = 0; - -#ifdef _WIN32 - // Windows readUDP needs the select command- minimum timeout is 1ms. - // Without this playback becomes choppy - - // Definitions - FD_SET stReadFDS; - FD_SET stExceptFDS; - struct timeval tv; - struct sockaddr_in testaddr; - int error = 0; - - // Setup for Select - FD_ZERO(&stReadFDS); - FD_SET(recfd.sock, &stReadFDS); - FD_ZERO(&stExceptFDS); - FD_SET(recfd.sock, &stExceptFDS); - tv.tv_sec = 0; /* Sec Timeout */ - tv.tv_usec = 250; // Microsec Timeout - - // Select Command - int tmp = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv); - if (tmp <= 0) // No Data or error - { - return -1; - } - - // If no error: Read Data - recvaddrlen = sizeof(testaddr); - status = (int) recvfrom(recfd.sock,dataRef,5000,0,(SOCKADDR *) &testaddr,&recvaddrlen); - if (status == SOCKET_ERROR) - { - error = WSAGetLastError(); - } -#else - // For apple or linux-just read - will timeout in 0.5 ms - status = (int) recvfrom(recfd.sock,dataRef,5000,0,recvaddr,&recvaddrlen); -#endif - - return status; -} - -short readDATA(struct 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(struct 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(struct 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(struct 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(struct 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; - } + printError("sendUDP", "Unexpected number of bytes sent."); } return result; } + +int readUDP(XPCSocket sock, char buffer[], int len) +{ +#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 = recv(sock.sock, buffer, len, 0); +#else + // For apple or linux-just read - will timeout in 0.5 ms + status = (int)recv(sock.sock, dataRef, len, 0); +#endif + if (status < 0) + { + printError("readUDP", "Error reading socket"); + } + return status; +} +/*****************************************************************************/ +/**** End Low Level UDP functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** Configuration functions ****/ +/*****************************************************************************/ +int setCONN(XPCSocket* sock, unsigned short port) +{ + // Set up command + char buffer[32] = "CONN"; + memcpy(&buffer[5], &port, 2); + + // Send command + if (sendUDP(*sock, buffer, 7) < 0) + { + printError("setCONN", "Failed to send command"); + return -1; + } + + // Switch socket + closeUDP(*sock); + *sock = openUDP(sock->xpIP, sock->xpPort, port); + + // Read response + int result; + for (int i = 0; i < 64; ++i) + { + result = readUDP(*sock, buffer, 32); + if (result != 0) + { + break; + } + } + if (result <= 0) + { + printError("setCONN", "Failed to read response"); + return -2; + } + if (strncmp(buffer, "CONF", 4) == 0) + { + // Response received succesfully. + return 0; + } + // Response incorrect + return -3; +} + +int pauseSim(XPCSocket sock, char pause) +{ + // Setup command + char buffer[6] = "SIMU"; + buffer[5] = pause == 0 ? 0 : 1; + // Send command + if (sendUDP(sock, buffer, 6) < 0) + { + printError("pauseSim", "Failed to send command"); + return -1; + } + return 0; +} +/*****************************************************************************/ +/**** End Configuration functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** X-Plane UDP Data functions ****/ +/*****************************************************************************/ +int sendDATA(XPCSocket sock, float dataRef[][9], int rows) +{ + // Preconditions + // There are only 134 DATA rows in X-Plane. Realistically, clients probably + // shouldn't be trying to set nearly this much data at once anyway. + if (rows > 134) + { + printError("sendDATA", "Too many rows."); + return -1; + } + + // Setup command + // 5 byte header + 134 rows * 9 values * 4 bytes per value => 4829 byte max length. + char buffer[4829] = "DATA"; + int len = 5 + rows * 9 * sizeof(float); + unsigned short step = 9 * sizeof(float); + for (int i=0;i 134) + { + printError("sendDATA", "Too many rows."); + // Read as much as we can anyway + rows = 134; + } + + // Read data + char buffer[4829] = { 0 }; + int result = readUDP(sock, buffer, 5120); + if (result <= 0) + { + printError("readDATA", "Failed to read from socket."); + return -1; + } + // Validate data + int readRows = (result - 5) / 36; + if (readRows > rows) + { + printError("readDATA", "Read more rows than will fit in dataRef."); + // Copy as much data as we can anyway + rows = readRows; + } + + // Parse data + for (int i = 0; i < rows; ++i) + { + dataRef[i][0] = buffer[5 + i * 36]; + memcpy(&dataRef[i][1], &buffer[9 + i * 36], 8 * sizeof(float)); + } + return rows; +} +/*****************************************************************************/ +/**** End X-Plane UDP Data functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** DREF functions ****/ +/*****************************************************************************/ +int setDREF(XPCSocket sock, const char* dref, float values[], int size) +{ + // Setup command + // 5 byte header + max 255 char dref name + max 255 values * 4 bytes per value = 1279 + unsigned char buffer[1279] = "DREF"; + int drefLen = strnlen(dref, 256); + if (drefLen > 255) + { + printError("setDREF", "dref length is too long. Must be less than 256 characters."); + return -1; + } + if (size > 255) + { + printError("setDREF", "size is too big. Must be less than 256."); + return -2; + } + int len = 7 + drefLen + size * sizeof(float); + + // Copy dref to buffer + buffer[5] = (unsigned char)drefLen; + memcpy(buffer + 6, dref, drefLen); + + // Copy values to buffer + buffer[6 + drefLen] = (unsigned char)size; + memcpy(buffer + 7 + drefLen, values, size * sizeof(float)); + + // Send command + if (sendUDP(sock, buffer, len) < 0) + { + printError("setDREF", "Failed to send command"); + return -2; + } + return 0; +} + +int sendDREFRequest(XPCSocket sock, const char* drefs[], unsigned char count) +{ + // Setup command + // 6 byte header + potentially 255 drefs, each 256 chars long. + // Easiest to just round to an even 2^16. + unsigned char buffer[65536] = "GETD"; + buffer[5] = count; + int len = 6; + for (int i = 0; i < count; ++i) + { + size_t drefLen = strnlen(drefs[i], 256); + if (drefLen > 255) + { + printError("getDREFs", "dref %d is too long.", i); + return -1; + } + buffer[len++] = (unsigned char)drefLen; + strncpy(buffer + len, drefs[i], drefLen); + len += drefLen; + } + // Send Command + if (sendUDP(sock, buffer, len) < 0) + { + printError("getDREFs", "Failed to send command"); + return -2; + } + return 0; +} + +int getDREFResponse(XPCSocket sock, float* values[], unsigned char count, int sizes[]) +{ + unsigned char buffer[65536]; + // Read data. Try 40 times to read, then give up. + // TODO: Why not just set the timeout to 40ms? + int result; + for (int i = 0; i < 512; ++i) + { + result = readUDP(sock, buffer, 65536); + if (result > 0) + { + break; + } + if (result < 0) + { +#ifdef _WIN32 + printError("getDREFs", "Read operation failed. (%d)", WSAGetLastError()); +#else + printError("getDREFs", "Read operation failed."); +#endif + return -1; + } + } + + if (result < 6) + { + printError("getDREFs", "Response was too short. Expected at least 6 bytes, but only got %d.", result); + return -2; + } + if (buffer[5] != count) + { + printError("getDREFs", "Unexpected response size. Expected %d rows, got %d instead.", count, buffer[5]); + return -3; + } + + int cur = 6; + for (int i = 0; i < count; ++i) + { + int l = buffer[cur++]; + if (l > sizes[i]) + { + printError("getDREFs", "values is too small. Row had %d values, only room for %d.", l, sizes[i]); + // Copy as many values as we can anyway + memcpy(values[i], buffer + cur, sizes[i] * sizeof(float)); + } + else + { + memcpy(values[i], buffer + cur, l * sizeof(float)); + sizes[i] = l; + } + cur += l * sizeof(float); + } + return 0; +} + +int getDREF(XPCSocket sock, const char* dref, float values[], int* size) +{ + return getDREFs(sock, &dref, &values, 1, size); +} + +int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char count, int sizes[]) +{ + // Send Command + int result = sendDREFRequest(sock, drefs, count); + if (result < 0) + { + // A error ocurred while sending. + // sendDREFRequest will print an error message, so just return. + return -1; + } + + // Read Response + if (getDREFResponse(sock, values, count, sizes) < 0) + { + // A error ocurred while reading the response. + // getDREFResponse will print an error message, so just return. + return -2; + } + return 0; +} +/*****************************************************************************/ +/**** End DREF functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** POSI functions ****/ +/*****************************************************************************/ +int psendPOSI(XPCSocket sock, float values[], int size) +{ + return sendPOSI(sock, values, size, 0); +} + +int sendPOSI(XPCSocket sock, float values[], int size, char ac) +{ + // Validate input + if (ac < 0 || ac > 20) + { + printError("sendPOSI", "aircraft should be a value between 0 and 20."); + return -1; + } + if (size < 1 || size > 7) + { + printError("sendPOSI", "size should be a value between 1 and 7."); + return -2; + } + + // Setup command + // 5 byte header + up to 7 values * 5 bytes each + unsigned char buffer[40] = "POSI"; + buffer[5] = ac; + for (int i = 0; i < 7; i++) + { + float val = -998; + + if (i < size) + { + val = values[i]; + } + *((float*)(buffer + 6 + i * 4)) = val; + } + + // Send Command + if (sendUDP(sock, buffer, 40) < 0) + { + printError("sendPOSI", "Failed to send command"); + return -2; + } + return 0; +} +/*****************************************************************************/ +/**** End POSI functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** CTRL functions ****/ +/*****************************************************************************/ +int psendCTRL(XPCSocket sock, float values[], int size) +{ + return sendCTRL(sock, values, size, 0); +} + +int sendCTRL(XPCSocket sock, float values[], int size, char ac) +{ + // Validate input + if (ac < 0 || ac > 20) + { + printError("sendCTRL", "aircraft should be a value between 0 and 20."); + return -1; + } + if (size < 1 || size > 7) + { + printError("sendCTRL", "size should be a value between 1 and 6."); + return -2; + } + + // Setup Command + // 5 byte header + 5 float values * 4 + 2 byte values + unsigned char buffer[27] = "CTRL"; + int cur = 5; + for (int i = 0; i < 6; i++) + { + float val = -998; + + if (i < size) + { + val = values[i]; + } + if (i == 4) + { + buffer[cur++] = val == -998 ? -1 : (unsigned char)val; + } + else + { + *((float*)(buffer + cur)) = val; + cur += sizeof(float); + } + } + buffer[26] = ac; + + // Send Command + if (sendUDP(sock, buffer, 27) < 0) + { + printError("sendCTRL", "Failed to send command"); + return -2; + } + return 0; +} +/*****************************************************************************/ +/**** End CTRL functions ****/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**** Drawing functions ****/ +/*****************************************************************************/ +int sendTEXT(XPCSocket sock, char* msg, int x, int y) +{ + // Input Validation + if (x < -1) + { + printError("sendTEXT", "x should be positive (or -1 for default)."); + // Technically, this should work, and may print something to the screen. + } + if (y < -1) + { + printError("sendTEXT", "y should be positive (or -1 for default)."); + // Negative y will never result in text being displayed. + return -1; + } + + // Setup command + // 5 byte header + 8 byte position + up to 256 byte message + char buffer[269] = "TEXT"; + size_t msgLen = strnlen(msg, 255); + size_t len = 14 + msgLen; + memcpy(buffer + 5, &x, sizeof(int)); + memcpy(buffer + 9, &y, sizeof(int)); + buffer[13] = msgLen; + strncpy(buffer + 14, msg, msgLen); + + // Send Command + if (sendUDP(sock, buffer, 40) < 0) + { + printError("sendTEXT", "Failed to send command"); + return -2; + } + return 0; +} + +int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count) +{ + // Input Validation + if (op < XPC_WYPT_ADD || op > XPC_WYPT_CLR) + { + printError("sendWYPT", "Unrecognized operation."); + return -1; + } + if (count > 255) + { + printError("sendWYPT", "Too many points. Must be less than 256."); + return -2; + } + + // Setup Command + // 7 byte header + 12 bytes * count + char buffer[3067] = "WYPT"; + buffer[5] = (unsigned char)op; + buffer[6] = (unsigned char)count; + size_t ptLen = sizeof(float) * 3 * count; + memcpy(buffer + 7, points, ptLen); + + // Send Command + if (sendUDP(sock, buffer, 40) < 0) + { + printError("sendWYPT", "Failed to send command"); + return -2; + } + return 0; +} +/*****************************************************************************/ +/**** End Drawing functions ****/ +/*****************************************************************************/ diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h index 86b1615..fda69bf 100644 --- a/C/src/xplaneConnect.h +++ b/C/src/xplaneConnect.h @@ -1,119 +1,255 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. // -// xplaneConnect.h -// -// Created by Chris Teubert on 12/2/13. +//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. +#ifndef xplaneConnect_h +#define xplaneConnect_h #ifdef __cplusplus - extern "C" { +extern "C" { #endif - #ifndef xplaneConnect_h - #define xplaneConnect_h +#include "stdlib.h" +#ifdef _WIN32 /* WIN32 SYSTEM */ +#include +#include +#pragma comment(lib,"ws2_32.lib") //Winsock Library +#elif (__APPLE__ || __linux) +#include +#include +#include +#include +#endif - #ifdef _WIN32 /* WIN32 SYSTEM */ - #include - #include - #pragma comment(lib,"ws2_32.lib") //Winsock Library - #elif (__APPLE__ || __linux) - #include - #include - #include - #include - #endif - - struct xpcSocket { - short open; - unsigned short port; - - // X-Plane IP and Port - char xpIP[16]; - unsigned short xpPort; - - #ifdef _WIN32 /* WIN32 SYSTEM */ - SOCKET sock; - #elif (__APPLE__ || __linux) //OS X/Linux - int sock; - #endif - }; +typedef struct +{ + char open; + unsigned short port; - typedef struct - { - float pitch; - float roll; - float yaw; - float throttle; - char gear; - float flaps; - char aircraft; - } xpcCtrl; + // X-Plane IP and Port + char xpIP[16]; + unsigned short xpPort; - typedef struct - { - double latitude; - double longitude; - double altitude; - } Waypoint; +#ifdef _WIN32 + SOCKET sock; +#else + int sock; +#endif +} XPCSocket; - typedef enum - { - xpc_WYPT_ADD = 1, - xpc_WYPT_DEL = 2, - xpc_WYPT_CLR = 3 - } WYPT_OP; +typedef enum +{ + XPC_WYPT_ADD = 1, + XPC_WYPT_DEL = 2, + XPC_WYPT_CLR = 3 +} WYPT_OP; + +// Low Level UDP Functions - typedef struct - { - WYPT_OP op; - Waypoint points[20]; - size_t numPoints; - } xpcWypt; - - // Basic Functions - struct xpcSocket openUDP(unsigned short port, const char *xpIP, unsigned short xpPort); - void closeUDP(struct xpcSocket); - short sendUDP(struct xpcSocket recfd, char my_message[], short messageLength); - short readUDP(struct xpcSocket recfd, char *dataRef, struct sockaddr *recvaddr); - - // Configuration - short setCONN(struct xpcSocket recfd, unsigned short recPort); - short pauseSim(struct xpcSocket recfd, short); - - // UDP DATA - short parseDATA(const char my_message[], short messageLength, float dataRef[][9]); - short readDATA(struct xpcSocket recfd, float dataRef[][9]); - short sendDATA(struct xpcSocket recfd, float dataRef[][9], unsigned short rows); - - // Position - short parsePOSI(const char my_message[], float resultArray[], int arraySize, float *gear); - short readPOSI(struct xpcSocket recfd, float resultArray[], int arraySize, float *gear); - short sendPOSI(struct xpcSocket recfd, short ACNum, short numArgs, float valueArray[]); - - // Controls - xpcCtrl parseCTRL(const char data[]); - xpcCtrl readCTRL(struct xpcSocket recfd); - short sendCTRL(struct xpcSocket recfd, short numArgs, float valueArray[]); - short sendpCTRL(struct xpcSocket recfd, short numArgs, float valueArray[], char acNum); - - // DREF Manipulation - short readDREF(struct xpcSocket recfd, float *resultArray[], short arraySizes[]); - short parseDREF(const char my_message[], char *dataRef, unsigned short *length_of_DREF, float *values, unsigned short *number_of_values); - short sendDREF(struct xpcSocket recfd, const char *dataRef, unsigned short length_of_DREF, float *values, unsigned short number_of_values); - short requestDREF(struct xpcSocket sendfd, struct xpcSocket recfd, char DREFArray[][100], short DREFSizes[], short listLength, float *resultArray[], short arraySizes[]); - int parseGETD(const char my_message[], char *DREFArray[], int DREFSizes[]); - short parseRequest(const char my_message[], float *resultArray[], short arraySizes[]); - short readRequest(struct xpcSocket recfd, float *dataRef[], short arraySizes[], struct sockaddr *recvaddr); +/// Opens a new connection to XPC on an OS chosen port. +/// +/// \param xpIP A string representing the IP address of the host running X-Plane. +/// \param xpPort The port of the X-Plane Connect plugin is listening on. Usually 49009. +/// \returns An XPCSocket struct representing the newly created connection. +XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort); - // Waypoints - xpcWypt parseWYPT(const char data[]); - short sendWYPT(struct xpcSocket sendfd, WYPT_OP op, float points[], int numPoints); +/// Opens a new connection to XPC on the specified port. +/// +/// \param xpIP A string representing the IP address of the host running X-Plane. +/// \param xpPort The port of the X-Plane Connect plugin is listening on. Usually 49009. +/// \param port The local port to use when sending and receiving data from XPC. +/// \returns An XPCSocket struct representing the newly created connection. +XPCSocket openUDP(const char *xpIP, unsigned short xpPort, unsigned short port); - // Screen Text - short sendTEXT(struct xpcSocket sendfd, char* msg, int x, int y); +/// Closes the specified connection and releases resources associated with it. +/// +/// \param sock The socket to close. +void closeUDP(XPCSocket sock); +/// Sends the given data to the X-Plane plugin. +/// +/// \param sock The socket to use to send the data. +/// \param buffer A pointer to the data to send. +/// \param len The number of bytes to send. +/// \returns If an error occurs, a negative number. Otehrwise, the number of bytes sent. +int sendUDP(XPCSocket sock, char buffer[], int len); + +/// Reads a datagram from the specified socket. +/// +/// \param sock The socket to read from. +/// \param buffer A pointer to the location to store the data. +/// \param len The number of bytes to read. +/// \returns If an error occurs, a negative number. Otehrwise, the number of bytes read. +int readUDP(XPCSocket sock, char buffer[], int len); + +// Configuration + +/// Sets the port on which the socket sends and receives data. +/// +/// \param sock A pointer to the socket to change. +/// \param port The new port to use. +/// \returns 0 if successful, otherwise a negative value. +int setCONN(XPCSocket* sock, unsigned short port); + +/// Pause or unpause the simulation. +/// +/// \param sock The socket to use to send the command. +/// \param pause 0 to unpause the sim; any other value to pause. +/// \returns 0 if successful, otherwise a negative value. +int pauseSim(XPCSocket sock, char pause); - #endif //ifdef _h +// X-Plane UDP DATA + +/// Reads X-Plane data from the specified socket. +/// +/// \details This command is compatible with the X-Plane data API. +/// \param sock The socket to use to send the command. +/// \param dataRef A 2D array of data rows to read into. +/// \param rows The number of rows in dataRef. +/// \returns 0 if successful, otherwise a negative value. +int readDATA(XPCSocket sock, float dataRef[][9], int rows); + +/// Sends X-Plane data on the specified socket. +/// +/// \details This command is compatible with the X-Plane data API. +/// \param sock The socket to use to send the command. +/// \param dataRef A 2D array of data rows to send. +/// \param rows The number of rows in dataRef. +/// \returns 0 if successful, otherwise a negative value. +int sendDATA(XPCSocket sock, float dataRef[][9], int rows); + +// DREF Manipulation + +/// Sets the specified dataref to the specified value. +/// +/// \details dref names and their associated data types can be found on the XPSDK wiki at +/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match +/// the size given on that page. XPC currently sends all values as floats regardless of +/// the type described on the wiki. This doesn't cause any data loss for most datarefs. +/// \param sock The socket to use to send the command. +/// \param dref The name of the dataref to set. +/// \param values An array of values representing the data to set. +/// \param size The number of elements in values. +/// \returns 0 if successful, otherwise a negative value. +int setDREF(XPCSocket sock, const char* dref, float values[], int size); + +/// Gets the value of the specified dataref. +/// +/// \details dref names and their associated data types can be found on the XPSDK wiki at +/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match +/// the size given on that page. XPC currently sends all values as floats regardless of +/// the type described on the wiki. This doesn't cause any data loss for most datarefs. +/// \param sock The socket to use to send the command. +/// \param dref The name of the dataref to get. +/// \param values The array in which the value of the dataref will be stored. +/// \param size The number of elements in values. The actual number of elements copied in will +/// be set when the function returns. +/// \returns 0 if successful, otherwise a negative value. +int getDREF(XPCSocket sock, const char* dref, float values[], int* size); + +/// Gets the value of the specified dataref. +/// +/// \details dref names and their associated data types can be found on the XPSDK wiki at +/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match +/// the size given on that page. XPC currently sends all values as floats regardless of +/// the type described on the wiki. This doesn't cause any data loss for most datarefs. +/// \param sock The socket to use to send the command. +/// \param drefs The names of the datarefs to get. +/// \param values A 2D array in which the values of the datarefs will be stored. +/// \param count The number of datarefs being requested. +/// \param size The number of elements in each row of values. The size of each row will be set +/// to the actual number of elements copied in for that row. +/// \returns 0 if successful, otherwise a negative value. +int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char count, int sizes[]); +// Position + +/// Sets the position and orientation of the player aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param values An array representing position data about the aircraft. The format of values is +/// [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear]. If less than 7 values are specified, +/// the unspecified values will be left unchanged. +/// \param size The number of elements in values. +/// \returns 0 if successful, otherwise a negative value. +int psendPOSI(XPCSocket sock, float values[], int size); + +/// Sets the position and orientation of the specified aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param values An array representing position data about the aircraft. The format of values is +/// [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear]. If less than 7 values are specified, +/// the unspecified values will be left unchanged. +/// \param size The number of elements in values. +/// \param ac The aircraft number to set the position of. 0 for the player aircraft. +/// \returns 0 if successful, otherwise a negative value. +int sendPOSI(XPCSocket sock, float values[], int size, char ac); + +// Controls + +/// Sets the control surfaces of the player aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param values An array representing position data about the aircraft. The format of values is +/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are +/// specified, the unspecified values will be left unchanged. +/// \param size The number of elements in values. +/// \returns 0 if successful, otherwise a negative value. +int psendCTRL(XPCSocket sock, float values[], int size); + +/// Sets the control surfaces of the specified aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param values An array representing position data about the aircraft. The format of values is +/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are +/// specified, the unspecified values will be left unchanged. +/// \param size The number of elements in values. +/// \param ac The aircraft number to set the control surfaces of. 0 for the player aircraft. +/// \returns 0 if successful, otherwise a negative value. +int sendCTRL(XPCSocket sock, float values[], int size, char ac); + +// Drawing + +/// Sets a string to be printed on the screen in X-Plane. +/// +/// \param sock The socket to use to send the command. +/// \param msg The message to print of the screen. +/// \param x The distance in pixels from the left edge of the screen to print the text. +/// \param y The distance in pixels from the bottom edge of the screen to print the top line of text. +/// \returns 0 if successful, otherwise a negative value. +int sendTEXT(XPCSocket sock, char* msg, int x, int y); + +/// Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored +/// and all points are removed. +/// +/// \param sock The socket to use to send the command. +/// \param op The operation to perform. 1=add, 2=remove, 3=clear. +/// \param points An array of values representing points. Each triplet in the array will be +/// interpreted as a (Lat, Lon, Alt) point. +/// \param count The number of points. There should be 3 * count elements in points. +/// \returns 0 if successful, otherwise a negative value. +int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count); + #ifdef __cplusplus } #endif +#endif diff --git a/Docs/Interface Control Document.xlsx b/Docs/Interface Control Document.xlsx index d31f18b..c989b60 100644 Binary files a/Docs/Interface Control Document.xlsx and b/Docs/Interface Control Document.xlsx differ diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index 521abc5..80407e0 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -44,8 +44,7 @@ import java.util.Arrays; public class XPlaneConnect implements AutoCloseable { private static int clientNum; - private DatagramSocket outSocket; - private DatagramSocket inSocket; + private DatagramSocket socket; private InetAddress xplaneAddr; private int xplanePort; @@ -54,7 +53,7 @@ public class XPlaneConnect implements AutoCloseable * * @return The incoming port number. */ - public int getRecvPort() { return inSocket.getLocalPort(); } + public int getRecvPort() { return socket.getLocalPort(); } /** * Gets the port on which the client sends data to X-Plane. @@ -116,62 +115,55 @@ public class XPlaneConnect implements AutoCloseable */ public XPlaneConnect(int timeout) throws SocketException { - this.inSocket = new DatagramSocket(49008); - this.outSocket = new DatagramSocket(50000 + ++clientNum); + this.socket = new DatagramSocket(0); this.xplaneAddr = InetAddress.getLoopbackAddress(); this.xplanePort = 49009; - this.inSocket.setSoTimeout(timeout); + this.socket.setSoTimeout(timeout); } /** * Initializes a new instance of the {@code XPlaneConnect} class using the specified ports and X-Plane host. * - * @param port The port on which the X-Plane Connect plugin is sending data. - * @param xplaneHost The network host on which X-Plane is running. - * @param xplanePort The port on which the X-Plane Connect plugin is listening. + * @param xpHost The network host on which X-Plane is running. + * @param xpPort The port on which the X-Plane Connect plugin is listening. + * @param port The local port to use when sending and receiving data from XPC. * @throws java.net.SocketException If this instance is unable to bind to the specified port. * @throws java.net.UnknownHostException If the specified hostname can not be resolved. */ - public XPlaneConnect(int port, String xplaneHost, int xplanePort) + public XPlaneConnect(String xpHost, int xpPort, int port) throws java.net.SocketException, java.net.UnknownHostException { - this(port, xplaneHost, xplanePort, 100); + this(xpHost, xpPort, port, 100); } /** * Initializes a new instance of the {@code XPlaneConnect} class using the specified ports, hostname, and timeout. * - * @param port The port on which the X-Plane Connect plugin is sending data. - * @param xplaneHost The network host on which X-Plane is running. - * @param xplanePort The port on which the X-Plane Connect plugin is listening. + * @param xpHost The network host on which X-Plane is running. + * @param xpPort The port on which the X-Plane Connect plugin is listening. + * @param port The port on which the X-Plane Connect plugin is sending data. * @param timeout The time, in milliseconds, after which read attempts will timeout. * @throws java.net.SocketException If this instance is unable to bind to the specified port. * @throws java.net.UnknownHostException If the specified hostname can not be resolved. */ - public XPlaneConnect(int port, String xplaneHost, int xplanePort, int timeout) + public XPlaneConnect(String xpHost, int xpPort, int port, int timeout) throws java.net.SocketException, java.net.UnknownHostException { - this.inSocket = new DatagramSocket(port); - this.outSocket = new DatagramSocket(50000 + ++clientNum); - this.xplaneAddr = InetAddress.getByName(xplaneHost); - this.xplanePort = xplanePort; - this.inSocket.setSoTimeout(timeout); + this.socket = new DatagramSocket(port); + this.xplaneAddr = InetAddress.getByName(xpHost); + this.xplanePort = xpPort; + this.socket.setSoTimeout(timeout); } /** - * Closes the underlying inSocket. + * Closes the underlying socket. */ public void close() { - if(inSocket != null) + if(socket != null) { - inSocket.close(); - inSocket = null; - } - if(outSocket != null) - { - outSocket.close(); - outSocket = null; + socket.close(); + socket = null; } } @@ -181,13 +173,13 @@ public class XPlaneConnect implements AutoCloseable * @return The data send from X-Plane. * @throws IOException If the read operation fails */ - private byte[] readUDP() throws IOException //TODO: Store data in a class level buffer to account for partial messages + private byte[] readUDP() throws IOException { byte[] buffer = new byte[2048]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); try { - inSocket.receive(packet); + socket.receive(packet); return Arrays.copyOf(buffer, buffer[4]); } catch (SocketTimeoutException ex) @@ -197,22 +189,15 @@ public class XPlaneConnect implements AutoCloseable } /** - * Send the given data to the X-Plane plugin. This method automatically sets the length byte before sending, - * overwriting any value previously stored in @{code buffer[4]}. + * Send the given data to the X-Plane plugin. * * @param buffer The data to send. * @throws IOException If the send operation fails. */ private void sendUDP(byte[] buffer) throws IOException { - if(buffer.length < 5 || buffer.length > 255) - { - throw new IllegalArgumentException("buffer must be between 5 and 255 bytes long."); - } - buffer[4] = (byte)buffer.length; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length, xplaneAddr, xplanePort); - outSocket.send(packet); + socket.send(packet); } /** @@ -236,9 +221,9 @@ public class XPlaneConnect implements AutoCloseable * @return A byte array representing data dependent on the dref requested. * @throws IOException If either the request or the response fails. */ - public float[] requestDREF(String dref) throws IOException + public float[] getDREF(String dref) throws IOException { - return requestDREFs(new String[]{dref})[0]; + return getDREFs(new String[] {dref})[0]; } /** @@ -248,7 +233,7 @@ public class XPlaneConnect implements AutoCloseable * @return A multidimensional array representing the data for each requested dref. * @throws IOException If either the request or the response fails. */ - public float[][] requestDREFs(String[] drefs) throws IOException + public float[][] getDREFs(String[] drefs) throws IOException { //Preconditions if(drefs == null || drefs.length == 0) @@ -321,19 +306,19 @@ public class XPlaneConnect implements AutoCloseable throw new IOException("No response received."); } - public void sendDREF(String dref, float value) throws IOException + public void setDREF(String dref, float value) throws IOException { - sendDREF(dref, new float[] {value}); + setDREF(dref, new float[] {value}); } /** * Sends a command to X-Plane that sets the given DREF. * - * @param dref The name of the DREF to set. + * @param dref The name of the X-Plane dataref to set. * @param value An array of floating point values whose structure depends on the dref specified. * @throws IOException If the command cannot be sent. */ - public void sendDREF(String dref, float[] value) throws IOException + public void setDREF(String dref, float[] value) throws IOException { //Preconditions if(dref == null) @@ -559,6 +544,36 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Reads X-Plane data + * + * @return The data read. + * @throws IOException If the read operation fails. + */ + public float[][] readData() throws IOException + { + byte[] buffer = readUDP(); + ByteBuffer bb = ByteBuffer.wrap(buffer); + int cur = 5; + int len = bb.get(cur++); + float[][] result = new float[bb.get(len)][9]; + for(int i = 0; i < len; ++i) + { + for(int j = 0; j < 9; ++j) + { + result[i][j] = bb.getFloat(cur); + cur += 4; + } + } + return result; + } + + /** + * Sends data to X-Plane + * + * @param data The data to send. + * @throws IOException If the command cannot be sent. + */ public void sendDATA(float[][] data) throws IOException { //Preconditions @@ -594,6 +609,36 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Selects what data X-Plane will export over UDP. + * + * @param rows The row numbers to select. + * @throws IOException If the command cannot be sent. + */ + public void selectDATA(int[] rows) throws IOException + { + //Preconditions + if(rows == null || rows.length == 0) + { + throw new IllegalArgumentException("rows must be a non-null, non-empty array."); + } + + //Convert data to bytes + ByteBuffer bb = ByteBuffer.allocate(4 * rows.length); + bb.order(ByteOrder.LITTLE_ENDIAN); + for(int i = 0; i < rows.length; ++i) + { + bb.putInt(i * 4, rows[i]); + } + + //Build and send message + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("DSEL".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(bb.array()); + sendUDP(os.toByteArray()); + } + /** * Sets a message to be displayed on the screen in X-Plane at the default screen location. * @@ -643,6 +688,15 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored + * and all points are removed. + * + * @param op The operation to perform. + * @param points An array of values representing points. Each triplet in the array will be + * interpreted as a (Lat, Lon, Alt) point. + * @throws IOException If the command cannot be sent. + */ public void sendWYPT(WaypointOp op, float[] points) throws IOException { //Preconditions @@ -672,12 +726,12 @@ public class XPlaneConnect implements AutoCloseable /** * Sets the port on which the client will receive data from X-Plane. * - * @param recvPort The new incoming port number. + * @param port The new incoming port number. * @throws IOException If the command cannnot be sent. */ - public void setCONN(int recvPort) throws IOException + public void setCONN(int port) throws IOException { - if(recvPort < 0 || recvPort >= 0xFFFF) + if(port < 0 || port >= 0xFFFF) { throw new IllegalArgumentException("Invalid port (must be non-negative and less than 65536)."); } @@ -685,14 +739,14 @@ public class XPlaneConnect implements AutoCloseable ByteArrayOutputStream os = new ByteArrayOutputStream(); os.write("CONN".getBytes(StandardCharsets.UTF_8)); os.write(0xFF); //Placeholder for message length - os.write((byte) recvPort); - os.write((byte) (recvPort >> 8)); + os.write((byte) port); + os.write((byte) (port >> 8)); sendUDP(os.toByteArray()); - int soTimeout = inSocket.getSoTimeout(); - inSocket.close(); - inSocket = new DatagramSocket(recvPort); - inSocket.setSoTimeout(soTimeout); + int soTimeout = socket.getSoTimeout(); + socket.close(); + socket = new DatagramSocket(port); + socket.setSoTimeout(soTimeout); readUDP(); // Try to read response } } diff --git a/MATLAB/+XPlaneConnect/XPlaneConnect.jar b/MATLAB/+XPlaneConnect/XPlaneConnect.jar new file mode 100644 index 0000000..edf9b73 Binary files /dev/null and b/MATLAB/+XPlaneConnect/XPlaneConnect.jar differ diff --git a/MATLAB/+XPlaneConnect/clearUDPBuffer.m b/MATLAB/+XPlaneConnect/clearUDPBuffer.m deleted file mode 100644 index 8237bd5..0000000 --- a/MATLAB/+XPlaneConnect/clearUDPBuffer.m +++ /dev/null @@ -1,33 +0,0 @@ -function socket = clearUDPBuffer(socket,varargin) -% clearUDPBuffer Script that clears an UDP Socket Buffer by closing and -% reopening the socket -% -% Inputs -% Socket: UDP Socket to be cleared -% -% Outputs -% Socket: UDP Socket -% -% Use -% 1. import XPlaneConnect.*; -% 2. Socket = openUDP(49005); -% 3. Socket = clearUDPBuffer(Socket); -% -% Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE - - import XPlaneConnect.* - - %% Close and reopen socket - if ~socket.isClosed() - port = socket.getLocalPort(); %get port of socket - socket.close; %Close Socket - socket = openUDP(port,varargin); %open new socket - end -end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/closeUDP.m b/MATLAB/+XPlaneConnect/closeUDP.m index 0575ddb..f49dd62 100644 --- a/MATLAB/+XPlaneConnect/closeUDP.m +++ b/MATLAB/+XPlaneConnect/closeUDP.m @@ -1,28 +1,27 @@ -function [socket] = closeUDP(socket) -% closeUDP Script that closes a UDP Socket +function closeUDP(socket) +% closeUDP Closes the specified connection and releases resources associated with it. % % Inputs -% Socket: UDP Socket to be closed -% -% Outputs -% Socket: Closed Socket +% socket: An XPC client to close % % Use % 1. import XPlaneConnect.*; -% 2. Socket = openUDP(49005); -% 3. Status = closeUDP(Socket); +% 2. socket = openUDP(); +% 3. closeUDP(socket); % % Contributors % [CT] Christopher Teubert (SGT, Inc.) % christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov +%% Close the socket +assert(isequal(class(socket), 'gov.nasa.xpc.XPlaneConnect'),... + '[closeUDP] ERROR: socket was not an XPC client.'); socket.close; -assert(isequal(socket.isClosed(),1),'closeUDP: Error- Could not close socket'); +%% Track open clients +global clients; +%TODO: Remove stale clients end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/getDREFs.m b/MATLAB/+XPlaneConnect/getDREFs.m new file mode 100644 index 0000000..cee04b9 --- /dev/null +++ b/MATLAB/+XPlaneConnect/getDREFs.m @@ -0,0 +1,38 @@ +function result = requestDREFs( drefs, socket ) +%requestDREF request the value of a specific DataRef from X-Plane over UDP +% +%Inputs +% drefs: Cell Array of DataRefs to be requested +% socket (optional): The client to use when sending the command. +%Outputs +% result: cell array of resulting data. +% +%Use +% 1. import XPlaneConnect.*; +% 2. socket = opendUDP(); +% 3. drefs = {'sim/cockpit2/controls/yoke_heading_ratio','sim/cockpit2/controls/yoke_roll_ratio'}; +% 4. result = requestDREF(drefs, socket); +% +% Contributors +% [CT] Christopher Teubert (SGT, Inc.) +% christopher.a.teubert@nasa.gov +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[getDREFs] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Send command +result = socket.getDREFs(drefs); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/openUDP.m b/MATLAB/+XPlaneConnect/openUDP.m index 77aa43f..bb9bb7e 100644 --- a/MATLAB/+XPlaneConnect/openUDP.m +++ b/MATLAB/+XPlaneConnect/openUDP.m @@ -1,41 +1,41 @@ -function [socket] = openUDP(port, varargin) -%openUDP Script that opens an UDP Socket +function [ socket ] = openUDP(varargin) +%openUDP Initializes a new instance of the XPC client and returns it. % %Inputs -% port: UDP Port for socket +% xpHost: The network host on which X-Plane is running. +% xpPort: The port on which the X-Plane Connect plugin is listening. +% port: The local port to use when sending and receiving data from XPC. % timeout (optional): Optional parameter for time to UDP timeout (in ms) %Outputs -% Socket: UDP Socket +% socket: An instance of the XPC client. % % Use % 1. import XPlaneConnect.*; -% 2. Socket = openUDP(49005); %Open socket at port 49005 with timeout=0.1 sec +% 2. socket = openUDP(); % Open a socket using the default settings % or -% 2. Socket = openUDP(49005); %Open socket at port 49005 with timeout=0.2 sec +% 2. Socket = openUDP('127.0.0.1', 49008, 49005); % Open a socket to connect to X-Plane on the local machine and receive data on port 49005 % % Contributors % [CT] Christopher Teubert (SGT, Inc.) % christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov -import java.net.DatagramSocket +%% Handle input +p = inputParser; +addOptional(p,'xpHost','127.0.0.1',@ischar); +addOptional(p,'xpPort',49009,@isnumeric); +addOptional(p,'port',0,@isnumeric); +parse(p,varargin{:}); -%% create socket - socket = DatagramSocket(port); - socket.setSoTimeout(100); - socket.setReceiveBufferSize(2000); +%% Create client +[folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP')); +javaaddpath([folder, '\XPlaneConnect.jar']); +import gov.nasa.xpc.*; +socket = XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port); -%% interpret input -if ~isempty(varargin) - if isnumeric(varargin(1)) - socket.setSoTimeout(varargin(1)); - end -end +%% Track open clients +global clients; +clients = [clients, socket]; -assert(isequal(socket.isClosed(),0),'openUDP: Error- Could not open port'); - -end +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/pauseSim.m b/MATLAB/+XPlaneConnect/pauseSim.m index 4903236..c15f809 100644 --- a/MATLAB/+XPlaneConnect/pauseSim.m +++ b/MATLAB/+XPlaneConnect/pauseSim.m @@ -1,13 +1,9 @@ -function status = pauseSim( pause, varargin ) -%pauseSim pause Simulation +function pauseSim( pause, socket ) +%pauseSim Pauses or unpauses X-Plane. % %Inputs -% Pause: binary value 0=run, 1=pause -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin -% -%Outputs -% status: If there was an error. Status<0 means there was an error. +% pause: binary value 0=run, 1=pause +% socket (optional): The client to use when sending the command. % %Use % 1. import XPlaneConnect.*; @@ -16,29 +12,26 @@ function status = pauseSim( pause, varargin ) % Contributors % [CT] Christopher Teubert (SGT, Inc.) % christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -%BEGIN CODE +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov import XPlaneConnect.* -%% Handle Input - % Optional parameters - p = inputParser; - addRequired(p,'pause',@isnumeric); - addOptional(p,'IP','127.0.0.1',@ischar); - addOptional(p,'port',49009,@isnumeric); - parse(p,pause,varargin{:}); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[pauseSim] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end - % Check format of input-TODO +%% Validate input +pause = logical(pause); -%% BODY - message = zeros(1,6); - - message(1:4) = 'SIMU'-0; - message(6) = uint8(p.Results.pause); +%% Send command +socket.pauseSim(pause); - status = sendUDP(message, p.Results.IP, p.Results.port); end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/readDATA.m b/MATLAB/+XPlaneConnect/readDATA.m index f0492a5..80f888e 100644 --- a/MATLAB/+XPlaneConnect/readDATA.m +++ b/MATLAB/+XPlaneConnect/readDATA.m @@ -1,9 +1,8 @@ -function [ sensor ] = readDATA( socket ) +function [ result ] = readDATA( socket ) % readDATA Reads UDP Socket and interprets data % % Inputs -% location: Either an opened UDP Socket or integer port number -% +% socket (optional): The client to read from. % Outputs % If data is X-Plane data format: % data: Matlab X-Plane DATA Structure @@ -30,62 +29,30 @@ function [ sensor ] = readDATA( socket ) % NOTE: sending in a port number instead of an opened socket clears the UDP buffer. This only works if the data is being sent at a fast rate. Sending in a UDP Socket reads the first packet in the buffer. % % Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE +% Christopher Teubert (SGT, Inc.) +% Jason Watkins - import XPlaneConnect.* +import XPlaneConnect.* - %% Read UDP Socket - [ sensor.raw] = readUDP(socket); - - %% Interpret Input - bits = size(sensor.raw); - if sensor.raw ~= -998 %If the signal exists - header = char(sensor.raw(1:4)'); - if strcmp(header,'DATA') %DATA signal type - Values = floor((bits-5)/36); - sensor.d = []; - sensor.h = zeros(Values(1),1); - for i=1:Values(1) - sensor.h(i) = sensor.raw(6+(i-1)*36); - sensor.d = [sensor.d; typecast(uint8(sensor.raw(10+(i-1)*36:5+i*36))','single')]; - end - elseif strcmp(header,'STRU') %STRU signal type - a = 6; - while a % no data - -%% Handle Input -p = inputParser; -addRequired(p,'DREFArray'); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,DREFArray,varargin{:}); - -%% BODY - % Header - message(1:4) = 'GETD'-0; - message(6) = length(p.Results.DREFArray); - - % DREFS - for i=1:length(p.Results.DREFArray) - message(len) = length(p.Results.DREFArray{i}); - message(len+1:len+message(len)) = p.Results.DREFArray{i}; - len = len+1+message(len); - end - - % Send UDP - status = sendUDP(message, p.Results.IP, p.Results.port); - - % Look for response - for i=1:40 - data = readUDP(); - if length(data) > 1 % Received Data - status = 0; - counter = 7; - nArrays = data(6); - result = cell(nArrays,1); - for j=1:nArrays - sizeArray = data(counter); - result{j} = typecast(uint8(data(counter+1:counter+sizeArray*4))','single'); - counter = counter + 1 + sizeArray * 4; - end - break; - else - result = cell(0,1); - end - end -end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/selectDATA.m b/MATLAB/+XPlaneConnect/selectDATA.m index 49efca1..dc54fdc 100644 --- a/MATLAB/+XPlaneConnect/selectDATA.m +++ b/MATLAB/+XPlaneConnect/selectDATA.m @@ -1,13 +1,9 @@ -function [ status ] = selectDATA( index, varargin ) +function selectDATA( rows, socket ) % selectDATA Choose specific X-Plane parameters to be send over UDP % % Inputs -% index: An array of the values that to be sent. Corresponds to the numbers on the XPlane Output screen -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp -% -% Outputs -% status: If there was an error. Status<0 means there was an error. +% rows: An array of the values that to be sent. Corresponds to the numbers on the XPlane Output screen +% socket (optional): The client to use when sending the command. % % Use % 1. import XPlaneConnect.*; @@ -15,29 +11,24 @@ function [ status ] = selectDATA( index, varargin ) % 3. status = selectDATA( values, '127.0.0.1', 49005 ); % send to localhose port 49005 % % Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE +% Christopher Teubert (SGT, Inc.) +% Jason Watkins import XPlaneConnect.* -%% Handle Input -p = inputParser; -addRequired(p,'index'); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,index,varargin{:}); - -%% BODY - dataString = ['DSEL'-0,0]; - for i=1:length(index) - dataString = [dataString, p.Results.index(i), 0, 0, 0]; +%% Get client +global clients; +if ~exist('socket', 'var') + assert(istrue(length(clients) < 2), '[selectDATA] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); end - status = sendUDP(dataStream, p.Results.IP, p.Results.port); - end +%% Validate input +rows = int32(rows); + +%% Send command +socket.selectDATA(rows); diff --git a/MATLAB/+XPlaneConnect/sendCTRL.m b/MATLAB/+XPlaneConnect/sendCTRL.m index 821efd0..e6d450c 100644 --- a/MATLAB/+XPlaneConnect/sendCTRL.m +++ b/MATLAB/+XPlaneConnect/sendCTRL.m @@ -1,5 +1,5 @@ -function [ status ] = sendCTRL( ctrl, acft, IP, port ) -% sendCTRL Send X-Plane Aircraft Control Commands over UDP +function sendCTRL( ctrl, ac, socket ) +% sendCTRL Sends command to X-Plane setting control surfaces on the specified aircraft. % % Inputs % ctrl: control array where the elements are as follows: @@ -9,56 +9,45 @@ function [ status ] = sendCTRL( ctrl, acft, IP, port ) % 4. Throttle [-1, 1] % 5. Gear (0=up, 1=down) % 6. Flaps [0, 1] -% acft (optional): Aircraft # (default is 0; 0 = own aircraft) -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp +% ac (optional): The aircraft to set. 0 for the player aircraft. +% socket (optional): The client to use when sending the command. % % Outputs % status: If there was an error. Status<0 means there was an error. % % Use % 1. import XPlaneConnect.*; -% 2. #Send the data array to port 49009 on the computer at IP address 172.0.100.54. -% 3. status = sendCTRL([0, 0, 0, 0.8, 0, 1], 1, '172.0.100.54',49009); +% 2. socket = openUDP(); +% 3. status = sendCTRL([0, 0, 0, 0.8, 0, 1], 0, socket); % Set throttle and flaps on the player aircraft. % -% Note: send the value -998 to not overwrite that parameter. That is, if -998 is sent, the parameter will stay at the current X-Plane value +% Note: send the value -998 to not overwrite that parameter. That is, if +% -998 is sent, the parameter will stay at the current X-Plane value. % % Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE +% Christopher Teubert (SGT, Inc.) +% Jason Watkins import XPlaneConnect.* -%% Handle Input - % Optional parameters - if ~exist('IP','var'), IP = '127.0.0.1'; end - if ~exist('port','var'), port = 49009; end - if ~exist('acft','var'), acft = 0; end - - % Check format of input-TODO - -%%BODY - header = ['CTRL'-0,0]; - dataStream = header; %TODO-ADD ACFT - - % Deal with position update - control = [-998.5, -998.5, -998.5, -998.5, -998.5, -998.5]; - - for i=1:min(length(ctrl),length(control)) - control(i) = ctrl(i); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[sendCTRL] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); end - dataStream = [dataStream, typecast(single(control(1:4)),'uint8')]; - dataStream = [dataStream, uint8(control(5))]; - dataStream = [dataStream, typecast(single(control(6)),'uint8')]; - dataStream = [dataStream, uint8(acft)]; - - % Send DATA - status = sendUDP(dataStream, IP, port); - end +%% Validate input +ctrl = single(ctrl); +if ~exist('ac', 'var') + ac = 0; +end +ac = logical(ac); + +%% Send command +socket.sendCTRL(ctrl, ac); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendDATA.m b/MATLAB/+XPlaneConnect/sendDATA.m index f051fbe..8e4c461 100644 --- a/MATLAB/+XPlaneConnect/sendDATA.m +++ b/MATLAB/+XPlaneConnect/sendDATA.m @@ -1,15 +1,11 @@ -function [ status ] = sendDATA(data, varargin) +function sendDATA(data, socket) % sendDATA Send X-Plane formatted DATA over UDP % % Inputs % data: X-Plane formatted data. Is a matlab structure with the following fields: % .h: Header array containing header numbers corresponding to values in the X-Plane UDP Data screen. % .d: 2-D Data array. Each row contains eight values corresponding to the header value (.h(1) corresponds to .d(1,:)) -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent . Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp interface. -% -% Outputs -% status: 0: no error, <0: error +% socket (optional): The client to use when sending the command. % % Use % 1. import XPlaneConnect.*; @@ -29,29 +25,23 @@ function [ status ] = sendDATA(data, varargin) import XPlaneConnect.* -%% Handle Input -p = inputParser; -addRequired(p,'data'); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,data,varargin{:}); - -%% BODY - - header = ['DATA'-0,0]; - dataStream = header; - - for i=1:length(p.Results.data.h) - dataStream = [dataStream, p.Results.data.h(i), 0, 0, 0, typecast(single(p.Results.data.d(i,1:8)),'uint8')]; %#ok - end - - % Send DATA - if length(dataStream) > 5 - status = sendUDP(dataStream, p.Results.IP, p.Results.port); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[sendDATA] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); else - disp('Warning in sendDATA: Sending empty dataStream') - status = -2; + socket = clients(1); end - end +%% Validate input +javaData = []; +for i = 1:length(data.h); + javaData = [javaData; data.h(i) data.d(i, 1:8)]; +end + +%% Send command +socket.sendDATA(javaData); + diff --git a/MATLAB/+XPlaneConnect/sendDREF.m b/MATLAB/+XPlaneConnect/sendDREF.m deleted file mode 100644 index 4632d67..0000000 --- a/MATLAB/+XPlaneConnect/sendDREF.m +++ /dev/null @@ -1,58 +0,0 @@ -function status = sendDREF( dataRef, Value, varargin ) -% sendDREF Send a command to change any DataRef in X-Plane over UDP. This requires the X-Plane Connect Plugin to be running -% -% Inputs -% dataRef: The X-Plane Dataref that will be chaged -% Value: The value that the above dataref is set to -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp -% -% Outputs -% status: If there was an error. status<0 means there was an error. -% -% Use -% 1. import XPlaneConnect.* -% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear -% 3. Value = 0; -% 4. status = sendDREF(dataRef, Value); -% -% Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE - -import XPlaneConnect.* - -status = 0; - -%% Handle Input -p = inputParser; -addRequired(p,'dataRef'); -addRequired(p,'Value'); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,dataRef, Value ,varargin{:}); - -%% BODY - dataStream = zeros(1,7+length(p.Results.dataRef)+length(p.Results.Value)*4); - % Build Header - dataStream(1:4) = 'DREF'-0; - - % Add DREF - dataStream(6) = uint8(length(p.Results.dataRef)); - len = 6+length(p.Results.dataRef); - dataStream(7:len)=p.Results.dataRef-0; - - % Add Value - dataStream(len+1) =uint8(length(p.Results.Value)); - dataStream(len+2:end)=typecast(single(p.Results.Value),'uint8'); - - % Send DREF - if length(dataStream) > 5 - status = sendUDP(dataStream, p.Results.IP, p.Results.port); - end -end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendPOSI.m b/MATLAB/+XPlaneConnect/sendPOSI.m index 4f85735..bff56fb 100644 --- a/MATLAB/+XPlaneConnect/sendPOSI.m +++ b/MATLAB/+XPlaneConnect/sendPOSI.m @@ -1,5 +1,5 @@ -function [ status ] = sendPOSI( posi, varargin ) -% sendPOSI Send X-Plane Aircraft Position over UDP +function sendPOSI( posi, ac, socket ) +% sendPOSI Sets the position of the specified aircraft. % % Inputs % posi: Position array where the elements are as follows: @@ -10,52 +10,43 @@ function [ status ] = sendPOSI( posi, varargin ) % 5. Pitch (deg) % 6. True Heading (deg) % 7. Gear (0=up, 1=down) -% acft (optional): Aircraft # (default is 0; 0 = own aircraft) -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp -% -% Outputs -% status: If there was an error. Status<0 means there was an error. +% acft (optional): The aircraft to set. 0 for the player aircraft. +% socket (optional): The client to use when sending the command. % % Use -% 1. import XPlaneConnect.*; -% 2. #Send the data array to port 49009 on the computer at IP address 172.0.100.54. -% 3. status = sendPOSI([37.5242422, -122.06899, 2500, 0, 0, 0, 1], 1, '172.0.100.54'); +% 1. import XPlaneConnect.*; +% 2. sendPOSI([37.5242422, -122.06899, 2500, 0, 0, 0, 1], 1); % -% Note: send the value -998 to not overwrite that parameter. That is, if -998 is sent, the parameter will stay at the current X-Plane value +% Note: send the value -998 to not overwrite that parameter. That is, if +% -998 is sent, the parameter will stay at the current X-Plane value. % % Contributors % [CT] Christopher Teubert (SGT, Inc.) % christopher.a.teubert@nasa.gov -% -% To Do -% -% BEGIN CODE +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov import XPlaneConnect.* -%% Handle Input -p = inputParser; -addRequired(p,'posi'); -addOptional(p,'acft',0,@isnumeric); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,posi,varargin{:}); - -%% BODY - header = ['POSI'-0,0]; - dataStream = [header, p.Results.acft]; - - % Deal with position update - position = [37.4185718,-121.935565,500,0,0,0, 0]; - - for i=1:min(length(position),length(p.Results.posi)) - position(i) = p.Results.posi(i); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[sendPOSI] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); end - dataStream = [dataStream, typecast(single(position),'uint8')]; - - % Send POSI - status = sendUDP(dataStream, p.Results.IP, p.Results.port); - end +%% Validate input +posi = single(posi); +if ~exist('ac', 'var') + ac = 0; +end +ac = logical(ac); + +%% Send command +socket.sendPOSI(posi, ac); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendSTRU.m b/MATLAB/+XPlaneConnect/sendSTRU.m deleted file mode 100644 index e00898a..0000000 --- a/MATLAB/+XPlaneConnect/sendSTRU.m +++ /dev/null @@ -1,57 +0,0 @@ -function [ status ] = sendSTRU( STRU, varargin ) -%sendSTRU Send a MATLAB structure over UDP -% -%Inputs -% stru: A MATLAB structure -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin -% -%Outputs -% status: If there was an error. Status<0 means there was an error. -% -%Use -% 1. import XPlaneConnect.*; -% 2. data = struct('a',[1:20],'b',1.853,'name','Example Structure'); -% 3. #Send the data structure to port 49005 on the computer at IP address 172.0.100.54 -% 4. status = sendUDP( data, '172.0.100.54', 49005 ); -% -% Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% -%BEGIN CODE - - import XPlaneConnect.* - - %% Handle Input - p = inputParser; - addRequired(p,'STRU'); - addOptional(p,'IP','127.0.0.1',@ischar); - addOptional(p,'port',49009,@isnumeric); - parse(p,STRU,varargin{:}); - - %% Form Data Array representing the structure - DATA = ['STRU'-0,0]; %array header - fieldName = fieldnames(p.Results.STRU); %all Struct fields - for i=1:length(fieldName) %for each field - field = getfield(p.Results.STRU,fieldName{i}); %get field - if ischar(field) %String - dim1 = 0; %Indicates string - dim2 = length(field); %length - data = field-0; %data - else %Numeric - dim1 = size(field,1); %Array Dim1 - dim2 = size(field,2); %Array Dim2 - data = typecast(single(reshape(field',1,dim1*dim2)),'uint8'); %Data - end - - DATA = [DATA,length(fieldName{i}),fieldName{i}-0,dim1,dim2,data]; %add to array - end - - %% Send Array - status = sendUDP(DATA, p.Results.IP, p.Results.port); - -end - diff --git a/MATLAB/+XPlaneConnect/sendTEXT.m b/MATLAB/+XPlaneConnect/sendTEXT.m index 499c12b..5edfaeb 100644 --- a/MATLAB/+XPlaneConnect/sendTEXT.m +++ b/MATLAB/+XPlaneConnect/sendTEXT.m @@ -1,4 +1,4 @@ -function [ status ] = sendTEXT( msg, varargin ) +function sendTEXT( msg, x, y, socket ) % sendTEXT Sends a string to be displayed in X-Plane. % % Inputs @@ -13,35 +13,35 @@ function [ status ] = sendTEXT( msg, varargin ) % % Use % 1. import XPlaneConnect.*; -% 2. #Set a message to be displayed near the top middle of the screen. +% 2. % Set a message to be displayed near the top middle of the screen. % 3. status = sendTEXT('Some text', 512, 600); % % Contributors -% Jason Watkins -% jason.w.watkins@nasa.gov -% -% To Do -% -% BEGIN CODE +% Jason Watkins (jason.w.watkins@nasa.gov) import XPlaneConnect.* -%% Handle Input -p = inputParser; -addRequired(p,'msg'); -addOptional(p,'x',-1,@isnumeric); -addOptional(p,'y',-1,@isnumeric); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,msg,varargin{:}); -%% Body - header = ['TEXT'-0,0]; - dataStream = [header,... - typecast(uint32(p.Results.x), 'uint8'),... - typecast(uint32(p.Results.y), 'uint8'),... - uint8(length(msg)), msg-0]; - - % Send TEXT - status = sendUDP(dataStream, p.Results.IP, p.Results.port); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[sendTEXT] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end end +%% Validate input +if ~exist('x', 'var') + x = -1; +end +if ~exist('y', 'var') + y = -1; +end +x = int32(x); +y = int32(y); + +%% Send command +socket.sendTEXT(msg, x, y); +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendUDP.m b/MATLAB/+XPlaneConnect/sendUDP.m deleted file mode 100644 index 6b0b112..0000000 --- a/MATLAB/+XPlaneConnect/sendUDP.m +++ /dev/null @@ -1,49 +0,0 @@ -function [ status ] = sendUDP( data, IP, port ) -%sendUDP Send an one dimensional array of type uint8 data over an UDP connection using a java DatagramSocket -% -%Inputs -% Data: 1-D array of type uint8 data to be sent -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin -% -%Outputs -% status: If there was an error. Status<0 means there was an error. -% -%Use -% 1. import XPlaneConnect.*; -% 2. data = uint8([1:20]); -% 3. #Send the data array to port 49005 on the computer at IP address 172.0.100.54. -% 4. status = sendUDP( data, '172.0.100.54', 49005 ); -% -% Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -%BEGIN CODE - - import java.net.DatagramSocket - import java.net.DatagramPacket - import java.net.InetAddress - - data(5) = length(data); - status = 0; - - %% Send array - persistent socket - if isempty(socket) - try - socket = DatagramSocket; - catch err - status = 1; - disp(err) - end - end - - IP = InetAddress.getByName(IP); - packet = DatagramPacket(data, length(data), IP, port); %create packet - socket.send(packet); -end - diff --git a/MATLAB/+XPlaneConnect/sendWYPT.m b/MATLAB/+XPlaneConnect/sendWYPT.m index ed64b04..a326ade 100644 --- a/MATLAB/+XPlaneConnect/sendWYPT.m +++ b/MATLAB/+XPlaneConnect/sendWYPT.m @@ -1,53 +1,55 @@ -function [ status ] = sendWYPT( op, points, varargin ) +function sendWYPT( op, points, socket ) % sendWYPT Adds, removes, or clears a set of waypoints to be rendered in % the simulator. % % Inputs -% msg: The string to be displayed -% x (optional): The distance from the left edge of the screen to display the message. -% y (optional): The distance from the bottom edge of the screen to display the message. -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp -% -% Outputs -% status: 0 if successful, otherwise a negative value. +% op: The operation to perform. 1=add, 2=remove, 3=clear. +% points: An array of values representing points. Each triplet in the +% array will be interpreted as a (Lat, Lon, Alt) point. +% socket (optional): The client to use when sending the command. % % Use % 1. import XPlaneConnect.*; -% 2. #Set a message to be displayed near the top middle of the screen. +% 2. %Set a message to be displayed near the top middle of the screen. % 3. status = sendTEXT('Some text', 512, 600); % % Contributors -% Jason Watkins -% jason.w.watkins@nasa.gov +% Jason Watkins (jason.w.watkins@nasa.gov) % % To Do % % BEGIN CODE import XPlaneConnect.* -%% Handle Input -p = inputParser; -addRequired(p,'op'); -addRequired(p,'points'); -addOptional(p,'IP','127.0.0.1',@ischar); -addOptional(p,'port',49009,@isnumeric); -parse(p,op,points,varargin{:}); -%% Validate Input -len = uint32(length(points)); -assert(op > 0 && op < 4); -assert(mod(len, 3) == 0); -assert(len / 3 < 20); - -%% Body -header = ['WYPT'-0,0]; -dataStream = [header,... - uint8(op),... - uint8(len / 3),... - typecast(single(points), 'uint8')]; - -% Send TEXT -status = sendUDP(dataStream, p.Results.IP, p.Results.port); +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[sendWYPT] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end end +%% Get WaypointOp class +[folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP')); +javaaddpath([folder, '\XPlaneConnect.jar']); +import gov.nasa.xpc.*; + +%% Validate input +len = uint32(length(points)); +assert(op > 0 && op < 4); +wyptOp = WaypointOp.Add; +if isequal(op, 2) + wyptOp = WaypointOp.Del; +elseif isequal(op, 3) + wyptOp = WaypointOp.Clr; +end +assert(mod(len, 3) == 0); + +%% Send command +socket.sendWYPT(wyptOp, points); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/setConn.m b/MATLAB/+XPlaneConnect/setConn.m index 39c2fbf..905c440 100644 --- a/MATLAB/+XPlaneConnect/setConn.m +++ b/MATLAB/+XPlaneConnect/setConn.m @@ -1,51 +1,36 @@ -function status = setConn( recvPort, IP, port ) +function setConn(port, socket) % setConn Send a command to set up the port where you will receive data on % this computer. % % Inputs -% Receiving Port: Port that data will be sent to in the future for this connection -% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) -% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp -% -% Outputs -% status: If there was an error. status<0 means there was an error. +% port: Port that data will be sent to in the future for this connection. +% socket (optional): The client to use when sending the command. % % Use % 1. import XPlaneConnect.* % 2. status = setConn(49011); % % Contributors -% [CT] Christopher Teubert (SGT, Inc.) -% christopher.a.teubert@nasa.gov -% -% To Do -% 1. Verify Input -% -% BEGIN CODE - import XPlaneConnect.* +% Christopher Teubert (SGT, Inc.) +% Jason Watkins - status = 0; - message = zeros(1,7); - -%% Handle Input - % Optional parameters - if ~exist('IP','var'), IP = '127.0.0.1'; end - if ~exist('port','var'), port = 49009; end +import XPlaneConnect.* - % Check format of input-TODO +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[setCONN] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end -%% BODY - % Header - message(1:4) = 'CONN'-0; - - % RecvPort - message(6:7) = typecast(uint16(recvPort),'uint8'); - - % Send - sendUDP(message,IP,port); - - global udpReadPort; - udpReadPort = recvPort; - readUDP(); % Read and discard CONF message +%% Validate input +port = int32(port); + +%% Send command +socket.setCONN(port); end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/setDREF.m b/MATLAB/+XPlaneConnect/setDREF.m new file mode 100644 index 0000000..13e6f00 --- /dev/null +++ b/MATLAB/+XPlaneConnect/setDREF.m @@ -0,0 +1,38 @@ +function setDREF( dref, value, socket ) +% sendDREF Sends a command to X-Plane that sets the given DREF. +% +% Inputs +% dref: The name of the X-Plane dataref to set. +% Value: An array of floating point values whose structure depends on the dref specified. +% socket (optional): The client to use when sending the command. +% +% Use +% 1. import XPlaneConnect.* +% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear +% 3. Value = 0; +% 4. status = setDREF(dataRef, Value); +% +% Contributors +% [CT] Christopher Teubert (SGT, Inc.) +% christopher.a.teubert@nasa.gov +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +value = single(value); + +%%Send command +socket.setDREF(dref, value); \ No newline at end of file diff --git a/TestScripts/C Tests.win/CTests.vcxproj b/TestScripts/C Tests.win/CTests.vcxproj index 9ba4d07..c057e7d 100644 --- a/TestScripts/C Tests.win/CTests.vcxproj +++ b/TestScripts/C Tests.win/CTests.vcxproj @@ -22,6 +22,9 @@ + + + {BC701AF4-552C-4C9D-82A1-B352542783A4} CTests diff --git a/TestScripts/C Tests.win/CTests.vcxproj.filters b/TestScripts/C Tests.win/CTests.vcxproj.filters index fae10d4..cd3ca2c 100644 --- a/TestScripts/C Tests.win/CTests.vcxproj.filters +++ b/TestScripts/C Tests.win/CTests.vcxproj.filters @@ -22,4 +22,9 @@ Source Files + + + Header Files + + \ No newline at end of file diff --git a/TestScripts/C Tests/main.c b/TestScripts/C Tests/main.c index 4ee58ba..9ecca83 100644 --- a/TestScripts/C Tests/main.c +++ b/TestScripts/C Tests/main.c @@ -16,12 +16,10 @@ int testFailed = 0; int testPassed = 0; -void runTest(short (*f)()) +void runTest(int (*test)(), char* name) { - short result; - - printf("Test %i: ",testPassed+testFailed+1); - result = (*f)(); // Run Test + int result = test(); // Run Test + printf("Test %i: %s - ", testPassed + testFailed + 1, name); if (result == 0) { printf("PASSED\n"); @@ -34,63 +32,54 @@ void runTest(short (*f)()) } } -short openTest() // openUDP Test +int openTest() // openUDP Test { - printf("openUDP - "); - struct xpcSocket sendPort = openUDP( 49062, "127.0.0.1", 49009 ); + XPCSocket sock = openUDP("127.0.0.1", 49009, 49062); + closeUDP(sock); return 0; } -short closeTest() // closeUDP test +int closeTest() // closeUDP test { - printf("closeUDP - "); - struct xpcSocket sendPort = openUDP( 49063, "127.0.0.1", 49009 ); + XPCSocket sendPort = openUDP("127.0.0.1", 49009, 49063); closeUDP(sendPort); - sendPort = openUDP(49063, "127.0.0.1", 49009); + sendPort = openUDP("127.0.0.1", 49009, 49063); closeUDP(sendPort); return 0; } -short sendReadTest() // send/read Test +int sendReadTest() // send/read Test { - printf("send/readUDP - "); - // Initialization - int i; // Iterator char test[] = {0, 1, 2, 3, 5}; char buf[5] = {0}; - struct xpcSocket sendPort, recvPort; - - // Setup - sendPort = openUDP( 49064, "127.0.0.1", 49063 ); - recvPort = openUDP( 49063, "127.0.0.1", 49009 ); + XPCSocket outSock = openUDP("127.0.0.1", 49063, 49064); + XPCSocket inSock = openUDP("127.0.0.1", 49009, 49063); // Execution - sendUDP( sendPort, test, sizeof(test) ); - readUDP( recvPort, buf, NULL ); // Test + sendUDP(outSock, test, sizeof(test)); + readUDP(inSock, buf, sizeof(buf), NULL); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(outSock); + closeUDP(inSock); // Tests - for (i=0; i<4; i++) - { - if (test[i] != buf[i]) // Not received correctly - { - return -1; - } - } + for (int i = 0; i < sizeof(buf); i++) + { + if (test[i] != buf[i]) // Not received correctly + { + return -1; + } + } return 0; } -short sendTEXTTest() +int sendTEXTTest() { - printf("sendTEXT - "); - // Setup - struct xpcSocket sendPort = openUDP(49064, "127.0.0.1", 49009); + XPCSocket sendPort = openUDP("127.0.0.1", 49009, 49064); int x = 100; int y = 700; char* msg = "XPlaneConnect test message. Now with 100% fewer new lines!"; @@ -104,262 +93,177 @@ short sendTEXTTest() return 0; } -short requestDREFTest() // Request DREF Test (Required for next tests) +int requestDREFTest() // Request DREF Test (Required for next tests) { - printf("requestDREF - "); - // Initialization - int i; // Iterator - char DREFArray[100][100]; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result = 0; + char* drefs[100] = + { + "sim/cockpit/switches/gear_handle_status", + "sim/cockpit2/switches/panel_brightness_ratio" + }; + float* data[100]; + int sizes[100]; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49064); // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *) malloc(40*sizeof(float)); - memset(DREFArray[i],0,100); - } - sendPort = openUDP( 49064, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status"); - strcpy(DREFArray[1],"sim/cockpit2/switches/panel_brightness_ratio"); - for (i=0;i<2;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); - } + for (int i = 0; i < 100; ++i) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } // Execution - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 2, recDATA, DREFSizes); + int result = getDREFs(sock, drefs, data, 2, sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Tests if ( result < 0)// Request 2 values { return -1; } - if (DREFSizes[0] != 1 || DREFSizes[1] != 4) + if (sizes[0] != 1 || sizes[1] != 4) { return -2; } return 0; } -short sendDREFTest() // sendDREF test +int sendDREFTest() // sendDREF test { - printf("sendDREF - "); - // Initialization - int i; // Iterator - char DREFArray[100][100]; - float *recDATA[100]; - short DREFSizes[100]; - float value = 0.0; - struct xpcSocket sendPort, recvPort; - short result = 0; - - // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *) malloc(40*sizeof(float)); - memset(DREFArray[i],0,100); - } - sendPort = openUDP( 49066, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status"); - for (i=0;i<1;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); - } - + char* drefs[100] = + { + "sim/cockpit/switches/gear_handle_status" + }; + float* data[100]; + int sizes[100]; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49066); + float value = 1.0F; + + // Setup + for (int i = 0; i < 100; ++i) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } + // Execution - sendDREF(sendPort, DREFArray[0], DREFSizes[0], &value, 1); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); + setDREF(sock, drefs[0], &value, 1); + int result = getDREFs(sock, drefs, data, 1, sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Tests if (result < 0)// Request 1 value { return -1; } - if (DREFSizes[0] != 1) + if (sizes[0] != 1) { return -2; } - if (*recDATA[0] != value) + if (data[0][0] != value) { return -3; - } + } return 0; } -short sendDATATest() // sendDATA test +int sendDATATest() // sendDATA test { - printf("sendData - "); - // Initialize int i,j; // Iterator - char DREFArray[100][100]; - float data[4][9] = {0}; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result = 0; - - // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *) malloc(40*sizeof(float)); - memset(DREFArray[i],0,100); - } - sendPort = openUDP( 49066, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/aircraft/parts/acf_gear_deploy"); - for (i=0;i<1;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); - } - for (i=0;i<4;i++) { // Set array to -999 - for (j=0;j<9;j++) data[i][j] = -999; - } + char* drefs[100] = + { + "sim/aircraft/parts/acf_gear_deploy" + }; + float* data[100]; + int sizes[100]; + float DATA[4][9]; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49066); + + // Setup + for (int i = 0; i < 100; ++i) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 9; j++) + { + data[i][j] = -998; + } + } data[0][0] = 14; // Gear data[0][1] = 1; data[0][2] = 0; // Execution - sendDATA(sendPort, data, 1); // Gear - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); // Test + sendDATA(sock, DATA, 1); + int result = getDREFs(sock, drefs, data, 1, sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Tests if ( result < 0 )// Request 1 value { return -1; } - if (DREFSizes[0] != 10) + if (sizes[0] != 10) { return -2; } - if (*recDATA[0] != data[0][1]) + if (*data[0] != data[0][1]) { return -3; } return 0; } -short sendCTRLTest() // sendCTRL test +int psendCTRLTest() // sendCTRL test { - printf("sendCTRL - "); - // Initialize - int i; // Iterator - char DREFArray[100][100]; - float CTRL[6] = { 0.0 }; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result; + char* drefs[100] = + { + "sim/cockpit2/controls/yoke_pitch_ratio", + "sim/cockpit2/controls/yoke_roll_ratio", + "sim/cockpit2/controls/yoke_heading_ratio", + "sim/flightmodel/engine/ENGN_thro", + "sim/cockpit/switches/gear_handle_status", + "sim/flightmodel/controls/flaprqst" + }; + float* data[100]; + int sizes[100]; + float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49066); // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *)malloc(40 * sizeof(float)); - memset(DREFArray[i], 0, 100); + for (int i = 0; i < 100; i++) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; } - sendPort = openUDP(49066, "127.0.0.1", 49009); - recvPort = openUDP(49008, "127.0.0.1", 49009); - strcpy(DREFArray[0], "sim/cockpit2/controls/yoke_pitch_ratio"); - strcpy(DREFArray[1], "sim/cockpit2/controls/yoke_roll_ratio"); - strcpy(DREFArray[2], "sim/cockpit2/controls/yoke_heading_ratio"); - strcpy(DREFArray[3], "sim/flightmodel/engine/ENGN_thro"); - strcpy(DREFArray[4], "sim/cockpit/switches/gear_handle_status"); - strcpy(DREFArray[5], "sim/flightmodel/controls/flaprqst"); - for (i = 0; i < 100; i++) { - DREFSizes[i] = (int)strlen(DREFArray[i]); - } - CTRL[3] = 0.8; // Throttle - CTRL[4] = 1; // Gear - CTRL[5] = 0.5; // Flaps // Execute - sendCTRL(sendPort, 6, CTRL); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 6, recDATA, DREFSizes); // Test + psendCTRL(sock, CTRL, 6); + int result = getDREFs(sock, drefs, data, 6, sizes); - // Close - closeUDP(sendPort); - closeUDP(recvPort); + // Close socket + closeUDP(sock); // Tests if (result < 0)// Request 1 value { return -6; } - for (i = 0; i<6; i++) + for (int i = 0; i < 6; i++) { - if (fabs(recDATA[i][0] - CTRL[i])>1e-4) - { - return -i - 1; - } - } - - return 0; -} - -short sendpCTRLTest() -{ - printf("sendNonPlayerCTRL - "); - - // Initialize - int i; // Iterator - char DREFArray[100][100]; - float CTRL[6] = { 0.0 }; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result; - - // Setup - for (i = 0; i < 100; i++) - { - recDATA[i] = (float *)malloc(40 * sizeof(float)); - memset(DREFArray[i], 0, 100); - } - sendPort = openUDP(49066, "127.0.0.1", 49009); - recvPort = openUDP(49008, "127.0.0.1", 49009); - strcpy(DREFArray[0], "sim/multiplayer/position/plane1_yolk_pitch"); - strcpy(DREFArray[1], "sim/multiplayer/position/plane1_yolk_roll"); - strcpy(DREFArray[2], "sim/multiplayer/position/plane1_yolk_yaw"); - strcpy(DREFArray[3], "sim/multiplayer/position/plane1_throttle"); - strcpy(DREFArray[4], "sim/multiplayer/position/plane1_gear_deploy"); - strcpy(DREFArray[5], "sim/multiplayer/position/plane1_flap_ratio"); - for (i = 0; i < 100; i++) - { - DREFSizes[i] = (int)strlen(DREFArray[i]); - } - CTRL[3] = 0.8; // Throttle - CTRL[4] = 1; // Gear - CTRL[5] = 0.5; // Flaps - - // Execute - sendpCTRL(sendPort, 6, CTRL, 1); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 9, recDATA, DREFSizes); // Test - - // Close - closeUDP(sendPort); - closeUDP(recvPort); - - // Tests - if (result < 0)// Request 1 value - { - return -6; - } - for (i = 0; i<6; i++) - { - if (fabs(recDATA[i][0] - CTRL[i])>1e-4) + if (fabs(data[i][0] - CTRL[i]) > 1e-4) { return -i - 1; } @@ -368,51 +272,85 @@ short sendpCTRLTest() return 0; } -short sendPOSITest() // sendPOSI test +int sendCTRLTest() +{ + // Initialize + char* drefs[100] = + { + "sim/multiplayer/position/plane1_yolk_pitch", + "sim/multiplayer/position/plane1_yolk_roll", + "sim/multiplayer/position/plane1_yolk_yaw", + "sim/multiplayer/position/plane1_throttle", + "sim/multiplayer/position/plane1_gear_deploy", + "sim/multiplayer/position/plane1_flap_ratio", + }; + float* data[100]; + int sizes[100]; + float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49066); + + // Setup + for (int i = 0; i < 100; i++) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } + + // Execute + sendCTRL(sock, CTRL, 6, 1); + int result = getDREFs(sock, drefs, data, 6, sizes); + + // Close socket + closeUDP(sock); + + // Tests + if (result < 0)// Request 1 value + { + return -6; + } + for (int i = 0; i < 6; i++) + { + if (fabs(data[i][0] - CTRL[i]) > 1e-4) + { + return -i - 1; + } + } + + return 0; +} + +int sendPOSITest() // sendPOSI test { - printf("sendPOSI - "); - // Initialization int i; // Iterator - char DREFArray[100][100]; - float POSI[8] = {0.0}; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result; + char* drefs[100] = + { + "sim/flightmodel/position/latitude", + "sim/flightmodel/position/longitude", + "sim/flightmodel/position/y_agl", + "sim/flightmodel/position/phi", + "sim/flightmodel/position/theta", + "sim/flightmodel/position/psi", + "sim/cockpit/switches/gear_handle_status" + }; + float* data[100]; + int sizes[100]; + float POSI[8] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49063); // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *) malloc(40*sizeof(float)); - memset(DREFArray[i],0,100); + for (i = 0; i < 100; i++) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; } - sendPort = openUDP( 49063, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/flightmodel/position/latitude"); - strcpy(DREFArray[1],"sim/flightmodel/position/longitude"); - strcpy(DREFArray[2],"sim/flightmodel/position/y_agl"); - strcpy(DREFArray[3],"sim/flightmodel/position/phi"); - strcpy(DREFArray[4],"sim/flightmodel/position/theta"); - strcpy(DREFArray[5],"sim/flightmodel/position/psi"); - strcpy(DREFArray[6],"sim/cockpit/switches/gear_handle_status"); - for (i=0;i<7;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); - } - POSI[0] = 37.524; // Lat - POSI[1] = -122.06899; // Lon - POSI[2] = 2500; // Alt - POSI[3] = 0; // Pitch - POSI[4] = 0; // Roll - POSI[5] = 0; // Heading - POSI[6] = 1; // Gear // Execution - sendPOSI( sendPort, 0, 7, POSI ); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 7, recDATA, DREFSizes); // Test + sendPOSI(sock, POSI, 7, 0); + int result = getDREFs(sock, drefs, data, 7, sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Tests if ( result < 0 )// Request 1 value @@ -421,114 +359,104 @@ short sendPOSITest() // sendPOSI test } for (i=0;i<7-1;i++) { - if (i==2) - { - continue; - } - if (fabs(recDATA[i][0]-POSI[i])>1e-4) - { - return -i; - } + if (i == 2) + { + continue; + } + if (fabs(data[i][0] - POSI[i]) > 1e-4) + { + return -i; + } } return 0; } -short sendWYPTTest() +int sendWYPTTest() { - printf("sendWYPT - "); - // Setup - struct xpcSocket sendPort = openUDP(49064, "127.0.0.1", 49009); + XPCSocket sock = openUDP("127.0.0.1", 49009, 49064); float points[] = { - 37.5245, -122.06899, 2500, - 37.455397, -122.050037, 2500, - 37.469567, -122.051411, 2500, - 37.479376, -122.060509, 2300, - 37.482237, -122.076130, 2100, - 37.474881, -122.087288, 1900, - 37.467660, -122.079391, 1700, - 37.466298, -122.090549, 1500, - 37.362562, -122.039223, 1000, - 37.361448, -122.034416, 1000, - 37.361994, -122.026348, 1000, - 37.365541, -122.022572, 1000, - 37.373727, -122.024803, 1000, - 37.403869, -122.041283, 50, - 37.418544, -122.049222, 6 + 37.5245F, -122.06899F, 2500, + 37.455397F, -122.050037F, 2500, + 37.469567F, -122.051411F, 2500, + 37.479376F, -122.060509F, 2300, + 37.482237F, -122.076130F, 2100, + 37.474881F, -122.087288F, 1900, + 37.467660F, -122.079391F, 1700, + 37.466298F, -122.090549F, 1500, + 37.362562F, -122.039223F, 1000, + 37.361448F, -122.034416F, 1000, + 37.361994F, -122.026348F, 1000, + 37.365541F, -122.022572F, 1000, + 37.373727F, -122.024803F, 1000, + 37.403869F, -122.041283F, 50, + 37.418544F, -122.049222F, 6 }; // Test - sendWYPT(sendPort, xpc_WYPT_ADD, points, 15); + sendWYPT(sock, XPC_WYPT_ADD, points, 15); + // NOTE: Visually ensure waypoints are added in the sim // Cleanup - closeUDP(sendPort); + closeUDP(sock); return 0; } -short pauseTest() // pauseSim test +int pauseTest() // pauseSim test { - printf("pauseSim - "); - // Initialize int i; // Iterator - char DREFArray[100][100]; - float *recDATA[100]; - short DREFSizes[100],RECSizes[100]; - struct xpcSocket sendPort, recvPort; - short result; + char* drefs[100] = + { + "sim/operation/override/override_planepath" + }; + float* data[100]; + int sizes[100]; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49064); // Setup - for (i = 0; i < 100; i++) { - recDATA[i] = (float *) malloc(40*sizeof(float)); - memset(DREFArray[i],0,100); - } - - // Setup - sendPort = openUDP( 49064, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/operation/override/override_planepath"); - for (i=0;i<1;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); + for (i = 0; i < 100; i++) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; } // Execute - pauseSim(sendPort, 1); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, RECSizes); // Test + pauseSim(sock, 1); + int result = getDREF(sock, drefs[0], data[0], sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Test - if (result < 0) { + if (result < 0) + { return -1; } - if (recDATA[0][0] != 1) + if (data[0][0] != 1) { return -2; } // Reopen - sendPort = openUDP( 49064, "127.0.0.1", 49009 ); - recvPort = openUDP( 49008, "127.0.0.1", 49009 ); + sock = openUDP("127.0.0.1", 49009, 49064); // Execute 2 - pauseSim(sendPort, 0); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, RECSizes); // Test + pauseSim(sock, 0); + result = getDREF(sock, drefs[0], data[0], sizes); // Close 2 - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Test 2 if (result < 0) { return -3; } - if (recDATA[0][0] != 0) + if (data[0][0] != 0) { return -4; } @@ -536,48 +464,39 @@ short pauseTest() // pauseSim test return 0; } -short connTest() // setConn test +int connTest() // setConn test { - printf("setConn - "); - // Initialize - int i; // Iterator - char DREFArray[100][100]; - float *recDATA[100]; - short DREFSizes[100]; - struct xpcSocket sendPort, recvPort; - short result = 0; + char* drefs[100] = + { + "sim/cockpit/switches/gear_handle_status" + }; + float* data[100]; + int sizes[100]; + XPCSocket sock = openUDP("127.0.0.1", 49009, 49067); #if (__APPLE__ || __linux) usleep(0); #endif // Setup - sendPort = openUDP( 49067, "127.0.0.1", 49009 ); - recvPort = openUDP( 49055, "127.0.0.1", 49009 ); - strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status"); - for (i=0;i<1;i++) { - DREFSizes[i] = (int) strlen(DREFArray[i]); - } + for (int i = 0; i < 100; ++i) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } // Execution - setCONN(sendPort, 49055); - result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); // Test + setCONN(&sock, 49055); + int result = getDREF(sock, drefs[0], data[0], sizes); // Close - closeUDP(sendPort); - closeUDP(recvPort); + closeUDP(sock); // Test if ( result < 0 )// No data received { return -1; } - - - // Set up for next test - sendPort = openUDP( 49067, "127.0.0.1", 49009 ); - setCONN(sendPort, 49008); - closeUDP(sendPort); return 0; } @@ -593,19 +512,19 @@ int main(int argc, const char * argv[]) printf("(Linux) \n"); #endif - runTest(openTest); - runTest(closeTest); - runTest(sendReadTest); - runTest(sendTEXTTest); - runTest(requestDREFTest); - runTest(sendDREFTest); - runTest(sendDATATest); - runTest(sendCTRLTest); - runTest(sendpCTRLTest); - runTest(sendPOSITest); - runTest(sendWYPTTest); - runTest(pauseTest); - runTest(connTest); + runTest(openTest, "open"); + runTest(closeTest, "close"); + runTest(sendReadTest, "send/read"); + runTest(sendTEXTTest, "TEXT"); + runTest(requestDREFTest, "GETD"); + runTest(sendDREFTest, "DREF"); + runTest(sendDATATest, "DATA"); + runTest(sendCTRLTest, "CTRL"); + runTest(psendCTRLTest, "CTRL (player)"); + runTest(sendPOSITest, "POSI"); + runTest(sendWYPTTest, "WYPT"); + runTest(pauseTest, "SIMU"); + runTest(connTest, "CONN"); printf( "----------------\nTest Summary\n\tFailed: %i\n\tPassed: %i\n", testFailed, testPassed ); diff --git a/TestScripts/Java Tests/XPlaneConnectTest.java b/TestScripts/Java Tests/XPlaneConnectTest.java index e76928f..8eb2b69 100644 --- a/TestScripts/Java Tests/XPlaneConnectTest.java +++ b/TestScripts/Java Tests/XPlaneConnectTest.java @@ -31,7 +31,7 @@ public class XPlaneConnectTest { fail(); } - try(XPlaneConnect xpc = new XPlaneConnect(49007, "127.0.0.1", 49009)) + try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49007)) { assertNotNull(xpc); } @@ -39,7 +39,7 @@ public class XPlaneConnectTest { fail(); } - try(XPlaneConnect xpc = new XPlaneConnect(49007, "127.0.0.1", 49009, 200)) + try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49007, 200)) { assertNotNull(xpc); } @@ -50,9 +50,9 @@ public class XPlaneConnectTest } @Test - public void testGetRecvPort() throws SocketException + public void testGetRecvPort() throws Exception { - try(XPlaneConnect xpc = new XPlaneConnect()) + try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49008)) { assertEquals(49008, xpc.getRecvPort()); } @@ -87,11 +87,11 @@ public class XPlaneConnectTest } @Test - public void constructorTest_SocketAlreadyBound() throws SocketException + public void constructorTest_SocketAlreadyBound() throws Exception { - try(XPlaneConnect xpc = new XPlaneConnect()) + try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49008)) { - try(XPlaneConnect xpc2 = new XPlaneConnect()) + try(XPlaneConnect xpc2 = new XPlaneConnect("127.0.0.1", 49009, 49008)) { fail(); } @@ -106,7 +106,7 @@ public class XPlaneConnectTest @Test(expected = UnknownHostException.class) public void constructorTest_InvalidHost() throws UnknownHostException { - try(XPlaneConnect xpc = new XPlaneConnect(49007, "notarealhost", 49009)) + try(XPlaneConnect xpc = new XPlaneConnect("notarealhost", 49009, 49007)) { } @@ -123,7 +123,7 @@ public class XPlaneConnectTest String dref = "sim/cockpit/switches/gear_handle_status"; try(XPlaneConnect xpc = new XPlaneConnect()) { - float[] result = xpc.requestDREF(dref); + float[] result = xpc.getDREF(dref); assertEquals(1, result.length); } } @@ -138,7 +138,7 @@ public class XPlaneConnectTest }; try(XPlaneConnect xpc = new XPlaneConnect()) { - float[][] result = xpc.requestDREFs(drefs); + float[][] result = xpc.getDREFs(drefs); assertEquals(2, result.length); assertEquals(1, result[0].length); assertEquals(4, result[1].length); @@ -150,7 +150,7 @@ public class XPlaneConnectTest { try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.requestDREFs(null); + xpc.getDREFs(null); fail(); } } @@ -161,7 +161,7 @@ public class XPlaneConnectTest String[] drefs = new String[0]; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.requestDREFs(drefs); + xpc.getDREFs(drefs); fail(); } } @@ -172,7 +172,7 @@ public class XPlaneConnectTest String[] drefs = new String[300]; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.requestDREFs(drefs); + xpc.getDREFs(drefs); fail(); } } @@ -184,7 +184,7 @@ public class XPlaneConnectTest String[] drefs = new String[]{longDREF}; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.requestDREFs(drefs); + xpc.getDREFs(drefs); fail(); } } @@ -195,7 +195,7 @@ public class XPlaneConnectTest String dref = ""; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.requestDREF(dref); + xpc.getDREF(dref); fail(); } } @@ -207,12 +207,12 @@ public class XPlaneConnectTest try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.pauseSim(true); - float[] result = xpc.requestDREF(dref); + float[] result = xpc.getDREF(dref); //assertEquals(1, result.length); //TODO: Why is this result 20 elements long in Java? (It's only one in MATLAB) assertEquals(1, result[0], 1e-4); xpc.pauseSim(false); - result = xpc.requestDREF(dref); + result = xpc.getDREF(dref); //assertEquals(1, result.length); assertEquals(0, result[0], 1e-4); } @@ -305,12 +305,12 @@ public class XPlaneConnectTest String dref = "sim/cockpit/switches/gear_handle_status"; try(XPlaneConnect xpc = new XPlaneConnect()) { - float gearHandle = xpc.requestDREF(dref)[0]; + float gearHandle = xpc.getDREF(dref)[0]; float value = gearHandle > 0.5 ? 0 : 1; - xpc.sendDREF(dref, value); + xpc.setDREF(dref, value); - float result = xpc.requestDREF(dref)[0]; + float result = xpc.getDREF(dref)[0]; assertEquals(value, result, 1e-4); } } @@ -320,7 +320,7 @@ public class XPlaneConnectTest { try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(null, 0); + xpc.setDREF(null, 0); fail(); } } @@ -332,7 +332,7 @@ public class XPlaneConnectTest try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(dref, null); + xpc.setDREF(dref, null); fail(); } } @@ -343,19 +343,22 @@ public class XPlaneConnectTest String dref = "sim/cockpit/switches/gear_handle_status"; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(dref, new float[0]); + xpc.setDREF(dref, new float[0]); fail(); } } - @Test(expected = IllegalArgumentException.class) + @Test public void testSendDREF_MessageTooLong() throws IOException { + // NOTE: This test originally ensured that the client restricted messages to 255 bytes in length. + // This restriction is no longer necessary, however removing it uncovered a bug that caused + // the plugin to do a bad memcpy and crash. This test is left here to ensure that X-Plane + // does not crash when given erroneously large value arrays. String dref = "sim/cockpit/switches/gear_handle_status"; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(dref, new float[200]); - fail(); + xpc.setDREF(dref, new float[200]); } } @@ -365,7 +368,7 @@ public class XPlaneConnectTest String dref = "sim/cockpit/switches/i/am/a/very/long/fake/dref/that/is/over/255/characters/./which/means/that/my/length/cant/be/encoded/in/the/single/byte/allocated/by/the/message/format/,/so/i/should/cause/an/exception/instead/./i/am/still/not/long/enough/./almost/there"; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(dref, 0); + xpc.setDREF(dref, 0); fail(); } } @@ -376,7 +379,7 @@ public class XPlaneConnectTest String dref = ""; try(XPlaneConnect xpc = new XPlaneConnect()) { - xpc.sendDREF(dref, 0); + xpc.setDREF(dref, 0); fail(); } } @@ -396,7 +399,7 @@ public class XPlaneConnectTest try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.sendCTRL(ctrl); - float[][] result = xpc.requestDREFs(drefs); + float[][] result = xpc.getDREFs(drefs); if(result.length < ctrl.length) { fail(); @@ -425,8 +428,8 @@ public class XPlaneConnectTest try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.sendCTRL(ctrl, 1); - float[][] result1 = xpc.requestDREFs(drefs1); - float[][] result2 = xpc.requestDREFs(drefs2); + float[][] result1 = xpc.getDREFs(drefs1); + float[][] result2 = xpc.getDREFs(drefs2); if(result1.length != 4 || result2.length != 2) { fail(); @@ -480,7 +483,7 @@ public class XPlaneConnectTest xpc.sendPOSI(posi); //TODO: It seems that these calls are a bit too fast. The dref request often gets stale data, causing the test to fail incorrectly. try {Thread.sleep(100);}catch(InterruptedException ex){} - float[][] result = xpc.requestDREFs(drefs); + float[][] result = xpc.getDREFs(drefs); xpc.pauseSim(false); if(result.length < posi.length) { @@ -543,7 +546,7 @@ public class XPlaneConnectTest try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.sendDATA(data); - float[] result = xpc.requestDREF(dref); + float[] result = xpc.getDREF(dref); assertEquals(data[0][1], result[0], 1e-4); } } @@ -575,15 +578,9 @@ public class XPlaneConnectTest String dref = "sim/cockpit/switches/gear_handle_status"; try(XPlaneConnect xpc = new XPlaneConnect()) { - int p = xpc.getRecvPort(); xpc.setCONN(49055); assertEquals(49055, xpc.getRecvPort()); - float[] result = xpc.requestDREF(dref); - assertEquals(1, result.length); - - xpc.setCONN(p); - assertEquals(p, xpc.getRecvPort()); - result = xpc.requestDREF(dref); + float[] result = xpc.getDREF(dref); assertEquals(1, result.length); } } diff --git a/TestScripts/MATLAB Tests/CTRLTest.m b/TestScripts/MATLAB Tests/CTRLTest.m index 6ba5386..7b8a9bc 100644 --- a/TestScripts/MATLAB Tests/CTRLTest.m +++ b/TestScripts/MATLAB Tests/CTRLTest.m @@ -1,6 +1,7 @@ function CTRLTest( ) %CTRLTest Summary of this function goes here % Detailed explanation goes here +%% Test player aircraft addpath('../../MATLAB') import XPlaneConnect.* @@ -14,14 +15,15 @@ THROT = rand(); CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0]; sendCTRL(CTRL); -result = requestDREF(DREFS); +result = getDREFs(DREFS); -assert(isequal(length(result),6),'CTRLTest: requestDREF unsucessful-wrong number of elements returned'); -assert(isequal(length(result{4}),8),'CTRLTest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); +assert(isequal(length(result),6),'CTRLTest: getDREFs unsucessful-wrong number of elements returned'); +assert(isequal(length(result{4}),8),'CTRLTest: getDREFs unsucessful- element 1 incorrect size (should be size 8)'); for i=1:length(CTRL)-1 assert(abs(result{i}(1)-CTRL(i))<1e-4,['CTRLTest: DATA set unsucessful-',num2str(i)]); end +%% Test NPC aircraft DREFS = {'sim/multiplayer/position/plane1_yolk_pitch',... 'sim/multiplayer/position/plane1_yolk_roll',... 'sim/multiplayer/position/plane1_yolk_yaw',... @@ -32,7 +34,7 @@ THROT = rand(); CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0]; sendCTRL(CTRL, 1); -result = requestDREF(DREFS); +result = getDREFs(DREFS); assert(isequal(length(result),6),'CTRLTest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result{4}),8),'CTRLTest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); diff --git a/TestScripts/MATLAB Tests/DATATest.m b/TestScripts/MATLAB Tests/DATATest.m index 4c69a5d..d70e7be 100644 --- a/TestScripts/MATLAB Tests/DATATest.m +++ b/TestScripts/MATLAB Tests/DATATest.m @@ -10,11 +10,10 @@ value = rand(); data = struct('h',25,'d',[value,-998,-998,-998,-998,-998,-998,-998]); sendDATA(data); -result = requestDREF(DREFS); +result = getDREFs(DREFS); -assert(isequal(length(result),1),'DATATest: requestDREF unsucessful-wrong number of elements returned'); -assert(isequal(length(result{1}),8),'DATATest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); -assert(abs(result{1}(1)-value)<1e-4,'DATATest: DATA set unsucessful'); +assert(isequal(length(result),8),'DATATest: getDREFs unsucessful-wrong number of elements returned'); +assert(abs(result(1)-value)<1e-4,'DATATest: DATA set unsucessful'); end diff --git a/TestScripts/MATLAB Tests/POSITest.m b/TestScripts/MATLAB Tests/POSITest.m index 110d769..16b6c8d 100644 --- a/TestScripts/MATLAB Tests/POSITest.m +++ b/TestScripts/MATLAB Tests/POSITest.m @@ -14,14 +14,14 @@ DREFS = {'sim/flightmodel/position/latitude', ... POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1]; % Gear sendPOSI(POSI); -result = requestDREF(DREFS); +result = getDREFs(DREFS); -assert(isequal(length(result),7),'POSITest: requestDREF unsucessful-wrong number of elements returned'); +assert(isequal(length(result),7),'POSITest: getDREFs unsucessful-wrong number of elements returned'); for i=1:length(POSI) if i==3 continue end - assert(abs(result{i}(1)-POSI(i))<1e-4,['POSITest: DATA set unsucessful-',num2str(i)]); + assert(abs(result(i)-POSI(i))<1e-4,['POSITest: DATA set unsucessful-',num2str(i)]); end end diff --git a/TestScripts/MATLAB Tests/requestDREFTest.m b/TestScripts/MATLAB Tests/getDREFsTest.m similarity index 86% rename from TestScripts/MATLAB Tests/requestDREFTest.m rename to TestScripts/MATLAB Tests/getDREFsTest.m index 36e5f70..43633c3 100644 --- a/TestScripts/MATLAB Tests/requestDREFTest.m +++ b/TestScripts/MATLAB Tests/getDREFsTest.m @@ -1,4 +1,4 @@ -function requestDREFTest( ) +function getDREFsTest( ) %SENDREADTEST Summary of this function goes here % Detailed explanation goes here addpath('../../MATLAB') @@ -7,7 +7,7 @@ import XPlaneConnect.* DREFS = {'sim/cockpit/switches/gear_handle_status',... 'sim/cockpit2/switches/panel_brightness_ratio'}; -result = requestDREF(DREFS); +result = getDREFs(DREFS); assert(isequal(length(result),2)); assert(isequal(length(result{1}),1)); diff --git a/TestScripts/MATLAB Tests/openCloseTest.m b/TestScripts/MATLAB Tests/openCloseTest.m index fc008ac..26af4ce 100644 --- a/TestScripts/MATLAB Tests/openCloseTest.m +++ b/TestScripts/MATLAB Tests/openCloseTest.m @@ -4,13 +4,10 @@ function openCloseTest() addpath('../../MATLAB') import XPlaneConnect.* -socket = openUDP( 49007 ); - -closeUDP( socket ); -assert(isequal(socket.isClosed(),1),'openCloseTest: socket is still open'); - -socket = openUDP( 49007 ); -closeUDP( socket ); +socket = openUDP(); +closeUDP(socket); +socket = openUDP(); +closeUDP(socket); end diff --git a/TestScripts/MATLAB Tests/pauseTest.m b/TestScripts/MATLAB Tests/pauseTest.m index 2e17fe6..7150f9c 100644 --- a/TestScripts/MATLAB Tests/pauseTest.m +++ b/TestScripts/MATLAB Tests/pauseTest.m @@ -9,17 +9,15 @@ DREFS = {'sim/operation/override/override_planepath'}; value = 1; % Pause pauseSim(value); -result = requestDREF(DREFS); +result = getDREFs(DREFS); -assert(isequal(length(result),1),'pauseTest: requestDREF unsucessful-wrong number of elements returned'); -assert(abs(result{1}(1)-value)<1e-4,'pauseTest: Pause Unsuccessful'); +assert(abs(result(1)-value)<1e-4,'pauseTest: Pause Unsuccessful'); value = 0; % Resume pauseSim(value); -result = requestDREF(DREFS); +result = getDREFs(DREFS); -assert(isequal(length(result),1),'pauseTest: requestDREF unsucessful-wrong number of elements returned'); -assert(abs(result{1}(1)-value)<1e-4,'pauseTest: Pause Unsuccessful'); +assert(abs(result(1)-value)<1e-4,'pauseTest: Pause Unsuccessful'); end diff --git a/TestScripts/MATLAB Tests/sendDREFTest.m b/TestScripts/MATLAB Tests/sendDREFTest.m deleted file mode 100644 index e688d08..0000000 --- a/TestScripts/MATLAB Tests/sendDREFTest.m +++ /dev/null @@ -1,19 +0,0 @@ -function sendDREFTest( ) -%SENDREADTEST Summary of this function goes here -% Detailed explanation goes here -addpath('../../MATLAB') -import XPlaneConnect.* - -DREFS = {'sim/cockpit/switches/gear_handle_status'}; -value = randi([0 10]); - -sendDREF(DREFS{1},value); -result = requestDREF(DREFS); - -assert(isequal(length(result),1),'sendDREFTest: requestDREF unsucessful-wrong number of elements returned'); -assert(isequal(length(result{1}),1),'sendDREFTest: requestDREF unsucessful- element 1 incorrect size (should be size 1)'); - -assert(isequal(result{1},value),'sendDREFTest: DREF set unsucessful'); - -end - diff --git a/TestScripts/MATLAB Tests/sendReadTest.m b/TestScripts/MATLAB Tests/sendReadTest.m deleted file mode 100644 index 62bca30..0000000 --- a/TestScripts/MATLAB Tests/sendReadTest.m +++ /dev/null @@ -1,18 +0,0 @@ -function sendReadTest( ) -%SENDREADTEST Summary of this function goes here -% Detailed explanation goes here -addpath('../../MATLAB') -import XPlaneConnect.* - -recvPortNum = 49074; -recvPort = openUDP(49074); - -sendUDP('test2'-0,'127.0.0.1',49074); -result = readUDP(recvPort); -closeUDP(recvPort); - -assert(~isequal(result,-998), 'no data received'); -assert(all(result(1:4)'=='test'-0)); - -end - diff --git a/TestScripts/MATLAB Tests/setConnTest.m b/TestScripts/MATLAB Tests/setConnTest.m index c06ae9b..2cc063d 100644 --- a/TestScripts/MATLAB Tests/setConnTest.m +++ b/TestScripts/MATLAB Tests/setConnTest.m @@ -8,7 +8,7 @@ import XPlaneConnect.* DREFS = {'sim/cockpit/switches/gear_handle_status'}; setConn(49055); -result = requestDREF(DREFS); +result = getDREFs(DREFS); assert(isequal(length(result),1),'setConnTest: requestDREF unsucessful-wrong number of elements returned'); diff --git a/TestScripts/MATLAB Tests/setDREFTest.m b/TestScripts/MATLAB Tests/setDREFTest.m new file mode 100644 index 0000000..70077b3 --- /dev/null +++ b/TestScripts/MATLAB Tests/setDREFTest.m @@ -0,0 +1,18 @@ +function setDREFTest( ) +%SENDREADTEST Summary of this function goes here +% Detailed explanation goes here +addpath('../../MATLAB') +import XPlaneConnect.* + +DREFS = {'sim/cockpit/switches/gear_handle_status'}; +value = randi([0 10]); + +setDREF(DREFS{1},value); +result = getDREFs(DREFS); + +assert(isequal(length(result),1),'setDREFTest: requestDREF unsucessful-wrong number of elements returned'); + +assert(isequal(result(1),value),'setDREFTest: DREF set unsucessful'); + +end + diff --git a/TestScripts/MATLAB Tests/struTest.m b/TestScripts/MATLAB Tests/struTest.m deleted file mode 100644 index 028e9bd..0000000 --- a/TestScripts/MATLAB Tests/struTest.m +++ /dev/null @@ -1,19 +0,0 @@ -function struTest( ) -%STRUTEST Summary of this function goes here -% Detailed explanation goes here -addpath('../../MATLAB') -import XPlaneConnect.* - -recvNum = 49076; -recvPort = openUDP(recvNum); - -testSTRU = struct('h',15,'d',[12, 34, 55, 99, 1, 0],'test','theTest'); - -sendSTRU(testSTRU,'127.0.0.1',recvNum); -resultSTRU = readDATA(recvPort); -closeUDP(recvPort); -resultSTRU = rmfield(resultSTRU,'raw'); -assert(isequal(testSTRU,resultSTRU),'struTest: Error-structs are not equal'); - -end - diff --git a/TestScripts/MATLAB Tests/tests.m b/TestScripts/MATLAB Tests/tests.m index bfa45d9..ec2329f 100644 --- a/TestScripts/MATLAB Tests/tests.m +++ b/TestScripts/MATLAB Tests/tests.m @@ -11,16 +11,14 @@ end disp(['XPC Tests-MATLAB (', os, ')']); theTests = {{@openCloseTest, 'Open/Close Test', 0},... - {@sendReadTest,'Send/Read Test', 0},... {@sendTEXTTest,'TEXT Test', 0},... - {@requestDREFTest,'Request DREF Test', 0},... - {@sendDREFTest,'Send DREF Test', 0},... + {@getDREFsTest,'Request DREF Test', 0},... + {@setDREFTest,'Send DREF Test', 0},... {@DATATest,'DATA Test', 0},... {@CTRLTest,'CTRL Test', 0},... {@POSITest,'POSI Test', 0},... {@sendWYPTTest,'WYPT Test', 0},... {@pauseTest,'Pause Test', 0},... - {@struTest,'Struct Test', 0},... {@setConnTest, 'setConn Test', 0}}; for i=1:length(theTests) diff --git a/xpcPlugin/Drawing.h b/xpcPlugin/Drawing.h index dbc57e4..0187810 100644 --- a/xpcPlugin/Drawing.h +++ b/xpcPlugin/Drawing.h @@ -3,12 +3,17 @@ #ifndef XPC_DRAWING_H #define XPC_DRAWING_H -#include "xplaneConnect.h" - #include namespace XPC { + typedef struct + { + double latitude; + double longitude; + double altitude; + } Waypoint; + /// Handles tasks that involve drawing to the screen in X-Plane. /// /// \author Jason Watkins diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp index 331562b..65daf31 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -2,7 +2,6 @@ //National Aeronautics and Space Administration. All Rights Reserved. #include "Message.h" #include "Log.h" -#include "xplaneConnect.h" #include #include @@ -71,6 +70,7 @@ namespace XPC } Log::WriteLine(ss.str()); + ss << std::dec; ss.str(""); ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; switch (GetMagicNumber()) // Binary version of head @@ -84,25 +84,41 @@ namespace XPC } case 0x4C525443: // CTRL { - xpcCtrl ctrl = parseCTRL((char*)buffer); - ss << " (" << ctrl.pitch << " " << ctrl.roll << " " << ctrl.yaw << ") "; - ss << ctrl.throttle << " " << ctrl.gear << " " << ctrl.flaps; + // Parse message data + float pitch = *((float*)(buffer + 5)); + float roll = *((float*)(buffer + 9)); + float yaw = *((float*)(buffer + 13)); + float thr = *((float*)(buffer + 17)); + char gear = buffer[21]; + float flaps = *((float*)(buffer + 22)); + std::uint8_t aircraft = 0; + if (size == 27) + { + aircraft = buffer[26]; + } + ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; + ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; Log::WriteLine(ss.str()); break; } case 0x41544144: // DATA - { - float dataRef[30][9]; - short numCols = parseDATA((char*)buffer, size, dataRef); + { + std::size_t numCols = (size - 5) / 36; + float values[32][9]; + for (int i = 0; i < numCols; ++i) + { + values[i][0] = buffer[5 + 36 * i]; + memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); + } ss << " (" << numCols << " lines)"; Log::WriteLine(ss.str()); for (int i = 0; i < numCols; ++i) { ss.str(""); - ss << "\t#" << dataRef[i][0]; + ss << "\t#" << values[i][0]; for (int j = 1; j < 9; ++j) { - ss << " " << dataRef[i][j]; + ss << " " << values[i][j]; } Log::WriteLine(ss.str()); } @@ -136,14 +152,16 @@ namespace XPC break; } case 0x49534F50: // POSI - { - float position[6] = { 0.0 }; - short aircraft = 0; - float gear = -1.0; - aircraft = parsePOSI((char*)buffer, position, 6, &gear); - ss << ' ' << aircraft; - ss << " (" << position[0] << ' ' << position[1] << ' ' << position[2] << ") ("; - ss << position[3] << ' ' << position[4] << ' ' << position[5] << ") "; + { + char aircraft = buffer[5]; + float gear = *((float*)(buffer + 30)); + float pos[3]; + float orient[3]; + memcpy(pos, buffer + 6, 12); + memcpy(orient, buffer + 18, 12); + ss << " AC:" << (int)aircraft; + ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; + ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; ss << gear; Log::WriteLine(ss.str()); break; diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index fd10665..5b461aa 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -55,48 +55,6 @@ namespace XPC MessageHandlers::sock = socket; } - static std::string getIP(sockaddr* sa) - { - char ip[INET6_ADDRSTRLEN + 6] = { 0 }; - switch (sa->sa_family) - { - case AF_INET: - { - sockaddr_in* sin = reinterpret_cast(sa); - inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN); - break; - } - case AF_INET6: - { - sockaddr_in6* sin = reinterpret_cast(sa); - inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN); - break; - } - default: - return "UNKNOWN"; - } - return std::string(ip); - } - - static unsigned short getPort(sockaddr* sa) - { - switch (sa->sa_family) - { - case AF_INET: - { - sockaddr_in *sin = reinterpret_cast(sa); - return ntohs((*sin).sin_port); - } - case AF_INET6: - { - sockaddr_in6 *sin = reinterpret_cast(sa); - return ntohs((*sin).sin6_port); - } - default: - return ~0; - } - } - void MessageHandlers::HandleMessage(Message& msg) { // Make sure we really have a message to handle. @@ -109,9 +67,7 @@ namespace XPC // Set current connection sockaddr sourceaddr = msg.GetSource(); - std::string ip = getIP(&sourceaddr); - unsigned short port = getPort(&sourceaddr); - connectionKey = ip + ":" + std::to_string(port); + connectionKey = UDPSocket::GetHost(&sourceaddr); #if LOG_VERBOSITY > 4 Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str()); #endif @@ -124,22 +80,20 @@ namespace XPC // to connections. As long as we never remove elements, the size of // connections will serve as a unique id. static_cast(connections.size()), - ip, - 49008, // By default, send information to the client on this port. - port, + sourceaddr, 0 }; connections[connectionKey] = connection; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s:%u", connection.id, ip.c_str(), port); +#if LOG_VERBOSITY > 2 + Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str()); #endif } else { connection = (*conn).second; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s:%u", - connection.id, connection.ip.c_str(), connection.srcPort); +#if LOG_VERBOSITY > 3 + Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s", + connection.id, connectionKey.c_str()); #endif } @@ -162,7 +116,30 @@ namespace XPC const unsigned char* buffer = msg.GetBuffer(); // Store new port - connection.dstPort = *((unsigned short*)(buffer + 5)); + std::uint16_t port = *((std::uint16_t*)(buffer + 5)); + sockaddr* sa = &connection.addr; + switch (sa->sa_family) + { + case AF_INET: + { + sockaddr_in* sin = reinterpret_cast(sa); + (*sin).sin_port = htons(port); + break; + } + case AF_INET6: + { + sockaddr_in6* sin = reinterpret_cast(sa); + (*sin).sin6_port = htons(port); + break; + } + default: +#if LOG_VERBOSITY > 0 + Log::WriteLine("[CONN] ERROR: Unknown address type."); + return; +#endif + } + connections.erase(connectionKey); + connectionKey = UDPSocket::GetHost(&connection.addr); connections[connectionKey] = connection; // Create response @@ -170,19 +147,19 @@ namespace XPC response[5] = connection.id; // Update log -#if LOG_VERBOSITY > 0 +#if LOG_VERBOSITY > 1 Log::FormatLine("[CONN] ID: %u New destination port: %u", - connection.id, connection.dstPort); + connection.id, port); #endif // Send response - sock->SendTo(response, 6, connection.ip, connection.dstPort); + sock->SendTo(response, 6, &connection.addr); } void MessageHandlers::HandleCtrl(Message& msg) { // Update Log -#if LOG_VERBOSITY > 0 +#if LOG_VERBOSITY > 2 Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id); #endif @@ -192,7 +169,7 @@ namespace XPC //Packets specifying an A/C num should be 27 bytes. if (size != 26 && size != 27) { -#if LOG_VERBOSITY > 1 +#if LOG_VERBOSITY > 0 Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size); #endif return; @@ -244,20 +221,20 @@ namespace XPC std::size_t numCols = (size - 5) / 36; if (numCols > 0) { -#if LOG_VERBOSITY > 3 +#if LOG_VERBOSITY > 2 Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id); #endif } if (numCols > 32) // Error. Will overflow values { -#if LOG_VERBOSITY > 2 +#if LOG_VERBOSITY > 0 Log::FormatLine("[DATA] ERROR numCols to large."); #endif return; } else { -#if LOG_VERBOSITY > 2 +#if LOG_VERBOSITY > 1 Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id); #endif return; @@ -405,8 +382,7 @@ namespace XPC std::string dref = std::string((char*)buffer + 6, len); unsigned char valueCount = buffer[6 + len]; - float values[40]; - memcpy(values, buffer + len + 7, valueCount * sizeof(float)); + float* values = (float*)(buffer + 7 + len); #if LOG_VERBOSITY > 1 Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str()); @@ -462,7 +438,7 @@ namespace XPC cur += count * sizeof(float); } - sock->SendTo(response, cur, connection.ip, connection.dstPort); + sock->SendTo(response, cur, &connection.addr); } void MessageHandlers::HandlePosi(Message& msg) @@ -599,13 +575,13 @@ namespace XPC #endif switch (op) { - case xpc_WYPT_ADD: + case 1: Drawing::AddWaypoints(points, count); break; - case xpc_WYPT_DEL: + case 2: Drawing::RemoveWaypoints(points, count); break; - case xpc_WYPT_CLR: + case 3: Drawing::ClearWaypoints(); break; default: @@ -621,7 +597,11 @@ namespace XPC #if LOG_VERBOSITY > 1 Log::WriteLine("[MSGH] Sending raw data to X-Plane"); #endif - sock->SendTo((unsigned char*)msg.GetBuffer(), msg.GetSize(), "127.0.0.1", 49000); + sockaddr_in loopback; + loopback.sin_family = AF_INET; + loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + loopback.sin_port = htons(49000); + sock->SendTo((std::uint8_t*)msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback); } void MessageHandlers::HandleUnknown(Message& msg) diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h index fe07e14..749c00c 100644 --- a/xpcPlugin/MessageHandlers.h +++ b/xpcPlugin/MessageHandlers.h @@ -52,9 +52,7 @@ namespace XPC typedef struct { unsigned char id; - std::string ip; - unsigned short dstPort; - unsigned short srcPort; + sockaddr addr; unsigned char getdCount; std::string getdRequest[255]; } ConnectionInfo; diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index 19958b9..41c04b4 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -133,41 +133,49 @@ namespace XPC return status; } - // TODO: Add IPV6 support - void UDPSocket::SendTo(unsigned char* buffer, std::size_t len, std::string remoteHost, unsigned short remotePort) + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, sockaddr* remote) { -#if LOG_VERBOSITY > 4 - Log::FormatLine("[SOCK] Sending message to %s:%u (length=%u)", remoteHost.c_str(), remotePort, len); -#endif - if (remoteHost == "localhost") - { - remoteHost = "127.0.0.1"; - } - unsigned long ip; - inet_pton(AF_INET, remoteHost.c_str(), &ip); - SendTo(buffer, len, ip, remotePort); - } - - void UDPSocket::SendTo(unsigned char* buffer, std::size_t len, unsigned long remoteIP, unsigned short remotePort) - { - struct sockaddr_in remoteAddr; - remoteAddr.sin_family = AF_INET; - remoteAddr.sin_port = htons(remotePort); - remoteAddr.sin_addr.s_addr = remoteIP; - -#ifdef _WIN32 - const char on = 1; -#else - int on = 1; -#endif - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - - buffer[4] = (unsigned char)len; - if (sendto(sock, (char*)buffer, (int)len, 0, (const struct sockaddr *) &remoteAddr, sizeof(remoteAddr)) < 0) + buffer[4] = (std::uint8_t)len; + if (sendto(sock, (char*)buffer, (int)len, 0, remote, sizeof(*remote)) < 0) { #if LOG_VERBOSITY > 0 - Log::FormatLine("[SOCK] Send failed. (remote: %s:%d)", remoteAddr, remotePort); + Log::FormatLine("[SOCK] Send failed. (remote: %s)", GetHost(remote).c_str()); +#endif + } + else + { +#if LOG_VERBOSITY > 3 + Log::FormatLine("[SOCK] Datagram sent. (remote: %s)", GetHost(remote).c_str()); #endif } } + + std::string UDPSocket::GetHost(sockaddr* sa) + { + char ip[INET6_ADDRSTRLEN + 6] = { 0 }; + switch (sa->sa_family) + { + case AF_INET: + { + sockaddr_in* sin = reinterpret_cast(sa); + inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN); + int len = strnlen(ip, INET6_ADDRSTRLEN); + ip[len++] = ':'; + sprintf(ip + len, "%d", ntohs((*sin).sin_port)); + break; + } + case AF_INET6: + { + sockaddr_in6* sin = reinterpret_cast(sa); + inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN); + int len = strnlen(ip, INET6_ADDRSTRLEN); + ip[len++] = ':'; + sprintf(ip + len, "%d", ntohs((*sin).sin6_port)); + break; + } + default: + return "UNKNOWN"; + } + return std::string(ip); + } } \ No newline at end of file diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h index fb6ef35..f2f83f4 100644 --- a/xpcPlugin/UDPSocket.h +++ b/xpcPlugin/UDPSocket.h @@ -52,20 +52,16 @@ namespace XPC /// Sends data to the specified remote endpoint. /// - /// \param data The data to be sent. - /// \param len The number of bytes to send. - /// \param remoteHost The hostname of the destination client. - /// \param remotePort The port of the destination client. - void SendTo(unsigned char* buffer, std::size_t len, std::string remoteHost, unsigned short remotePort); + /// \param data The data to be sent. + /// \param len The number of bytes to send. + /// \param remote The destination socket. + void SendTo(std::uint8_t* buffer, std::size_t len, sockaddr* remote); - /// Sends data to the specified remote endpoint. + /// Gets a string containing the IP address and port contained in the given sockaddr. /// - /// \param data The data to be sent. - /// \param len The number of bytes to send. - /// \param remoteHost The hostname of the destination client. - /// \param remotePort The port of the destination client. - void SendTo(unsigned char* buffer, std::size_t len, unsigned long remoteIP, unsigned short remotePort); - + /// \param addr The socket address to parse. + /// \returns A string representation of the socket address. + static std::string GetHost(sockaddr* addr); private: #ifdef _WIN32 SOCKET sock; diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index d822798..08e119b 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -70,11 +70,9 @@ #endif #define RECVPORT 49009 // Port that the plugin receives commands on -#define SENDPORT 49097 // Port that the plugin sends on #define OPS_PER_CYCLE 20 // Max Number of operations per cycle -XPC::UDPSocket* recvSocket = nullptr; -XPC::UDPSocket* sendSocket = nullptr; +XPC::UDPSocket* sock = nullptr; double start,lap; static double timeConvert = 0.0; @@ -125,10 +123,8 @@ PLUGIN_API void XPluginStop(void) PLUGIN_API void XPluginDisable(void) { // Close sockets - delete recvSocket; - delete sendSocket; - recvSocket = nullptr; - sendSocket = nullptr; + delete sock; + sock = nullptr; // Stop rendering messages to screen. XPC::Drawing::ClearMessage(); @@ -142,9 +138,8 @@ PLUGIN_API void XPluginDisable(void) PLUGIN_API int XPluginEnable(void) { // Open sockets - recvSocket = new XPC::UDPSocket(RECVPORT); - sendSocket = new XPC::UDPSocket(SENDPORT); - XPC::MessageHandlers::SetSocket(sendSocket); + sock = new XPC::UDPSocket(RECVPORT); + XPC::MessageHandlers::SetSocket(sock); XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened"); if (benchmarkingSwitch > 0) @@ -188,7 +183,7 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall, #endif } - XPC::Message msg = XPC::Message::ReadFrom(*recvSocket); + XPC::Message msg = XPC::Message::ReadFrom(*sock); if (msg.GetHead() == "") { break; @@ -208,8 +203,8 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall, if (cyclesToClear != -1 && counter%cyclesToClear == 0) { XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); - delete recvSocket; - recvSocket = new XPC::UDPSocket(RECVPORT); + delete sock; + sock = new XPC::UDPSocket(RECVPORT); } return -1; } \ No newline at end of file diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index 3438995..e171bde 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index b94dc15..395ca48 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index 3ed8c71..ac3d36c 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -42,7 +42,7 @@ true ..\XPlaneConnect\ .xpl - ..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath) + ..\SDK\CHeaders\XPLM;$(IncludePath) ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) win @@ -99,7 +99,6 @@ - @@ -109,7 +108,6 @@ - diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters index 21d7598..e921a5d 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters @@ -15,9 +15,6 @@ - - Header Files - Header Files @@ -41,9 +38,6 @@ - - Source Files - Source Files