diff --git a/.gitignore b/.gitignore index d77d831..8c184fe 100644 --- a/.gitignore +++ b/.gitignore @@ -92,4 +92,5 @@ out/ # Internal Docs # ################# -Docs/~$Capability Matrix.xlsx \ No newline at end of file +Docs/~$Capability Matrix.xlsx +*.xpl diff --git a/C/monitorExample/main.c b/C/monitorExample/main.c index e6d2e62..e0af291 100644 --- a/C/monitorExample/main.c +++ b/C/monitorExample/main.c @@ -26,6 +26,8 @@ #include #include "stdio.h" +struct timeval tv; + #ifdef WIN32 HANDLE hStdIn = NULL; INPUT_RECORD buffer; @@ -51,7 +53,6 @@ int waitForInput() #else int fdstdin = 0; fd_set fds; -struct timeval tv; int waitForInput() { @@ -67,10 +68,10 @@ int main(void) { XPCSocket client = openUDP("127.0.0.1"); const int aircraftNum = 0; - tv.tv_usec = 100 * 1000; + tv.tv_usec = 100 * 1000; while (1) { - float posi[7]; // FIXME: change this to the 64-bit lat/lon/h + double posi[7]; int result = getPOSI(client, posi, aircraftNum); if (result < 0) // Error in getPOSI { diff --git a/C/playbackExample/src/playback.c b/C/playbackExample/src/playback.c index 4d2e594..dd92e45 100644 --- a/C/playbackExample/src/playback.c +++ b/C/playbackExample/src/playback.c @@ -65,7 +65,7 @@ void record(char* path, int interval, int duration) XPCSocket sock = openUDP("127.0.0.1"); for (int i = 0; i < count; ++i) { - float posi[7]; + double posi[7]; int result = getPOSI(sock, posi, 0); playbackSleep(interval); if (result < 0) diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index ed984fb..f1da967 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -117,13 +117,16 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port) exit(EXIT_FAILURE); } - // Set timeout to 100ms + // Set socket timeout period for sendUDP to 1 millisecond + // Without this, playback may become choppy due to process blocking #ifdef _WIN32 - DWORD timeout = 100; + // Minimum socket timeout in Windows is 1 millisecond (0 makes it blocking) + DWORD timeout = 1; #else + // Set socket timeout to 1 millisecond = 1,000 microseconds to make it the same as Windows (0 makes it blocking) struct timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = 250000; + timeout.tv_usec = 1000; #endif if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { @@ -185,16 +188,16 @@ int sendUDP(XPCSocket sock, char buffer[], int len) /// \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. +/// \returns If an error occurs, a negative number. Otherwise, the number of bytes read. 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 + // For readUDP, use the select command - minimum timeout of 0 makes it polling. + // Without this, playback may become choppy due to process blocking // Definitions - FD_SET stReadFDS; - FD_SET stExceptFDS; + fd_set stReadFDS; + fd_set stExceptFDS; + struct timeval timeout; // Setup for Select FD_ZERO(&stReadFDS); @@ -202,12 +205,13 @@ int readUDP(XPCSocket sock, char buffer[], int len) FD_ZERO(&stExceptFDS); FD_SET(sock.sock, &stExceptFDS); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100000; + // Set timeout period for select to 0.05 sec = 50 milliseconds = 50,000 microseconds (0 makes it polling) + // TO DO - This could be set to 0 if a message handling system were implemented, like in the plugin. + timeout.tv_sec = 0; + timeout.tv_usec = 50000; // Select Command - int status = select(-1, &stReadFDS, (FD_SET*)0, &stExceptFDS, &tv); + int status = select(sock.sock+1, &stReadFDS, NULL, &stExceptFDS, &timeout); if (status < 0) { printError("readUDP", "Select command error"); @@ -218,11 +222,9 @@ int readUDP(XPCSocket sock, char buffer[], int len) // No data return 0; } + + // If no error: Read Data status = recv(sock.sock, buffer, len, 0); -#else - // For apple or linux-just read - will timeout in 0.5 ms - int status = (int)recv(sock.sock, buffer, len, 0); -#endif if (status < 0) { printError("readUDP", "Error reading socket"); @@ -378,16 +380,16 @@ int readDATA(XPCSocket sock, float data[][9], int rows) /*****************************************************************************/ /**** DREF functions ****/ /*****************************************************************************/ -int sendDREF(XPCSocket sock, const char* dref, float value[], int size) +int sendDREF(XPCSocket sock, const char* dref, float values[], int size) { - return sendDREFs(sock, &dref, &value, &size, 1); + return sendDREFs(sock, &dref, &values, &size, 1); } int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], int count) { // Setup command // Max size is technically unlimited. - unsigned char buffer[65536] = "DREF"; + char buffer[65536] = "DREF"; int pos = 5; int i; // Iterator for (i = 0; i < count; ++i) @@ -433,7 +435,7 @@ 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"; + char buffer[65536] = "GETD"; buffer[5] = count; int len = 6; int i; // iterator @@ -460,7 +462,7 @@ int sendDREFRequest(XPCSocket sock, const char* drefs[], unsigned char count) int getDREFResponse(XPCSocket sock, float* values[], unsigned char count, int sizes[]) { - unsigned char buffer[65536]; + char buffer[65536]; int result = readUDP(sock, buffer, 65536); if (result < 0) @@ -516,7 +518,7 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char int result = sendDREFRequest(sock, drefs, count); if (result < 0) { - // A error ocurred while sending. + // An error ocurred while sending. // sendDREFRequest will print an error message, so just return. return -1; } @@ -524,7 +526,7 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char // Read Response if (getDREFResponse(sock, values, count, sizes) < 0) { - // A error ocurred while reading the response. + // An error ocurred while reading the response. // getDREFResponse will print an error message, so just return. return -2; } @@ -537,10 +539,10 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char /*****************************************************************************/ /**** POSI functions ****/ /*****************************************************************************/ -int getPOSI(XPCSocket sock, float values[7], char ac) +int getPOSI(XPCSocket sock, double values[7], char ac) { // Setup send command - unsigned char buffer[6] = "GETP"; + char buffer[6] = "GETP"; buffer[5] = ac; // Send command @@ -551,22 +553,41 @@ int getPOSI(XPCSocket sock, float values[7], char ac) } // Get response - unsigned char readBuffer[34]; - int readResult = readUDP(sock, readBuffer, 34); + char readBuffer[46]; + float f[7]; + int readResult = readUDP(sock, readBuffer, 46); + + // Copy response into values if (readResult < 0) { printError("getPOSI", "Failed to read response."); return -2; } - if (readResult != 34) + else if (readResult == 34) /* lat/lon/h as 32-bit float */ + { + memcpy(f, readBuffer + 6, 7 * sizeof(float)); + values[0] = (double)f[0]; + values[1] = (double)f[1]; + values[2] = (double)f[2]; + values[3] = (double)f[3]; + values[4] = (double)f[4]; + values[5] = (double)f[5]; + values[6] = (double)f[6]; + } + else if (readResult == 46) /* lat/lon/h as 64-bit double */ + { + memcpy(values, readBuffer + 6, 3 * sizeof(double)); + memcpy(f, readBuffer + 30, 4 * sizeof(float)); + values[3] = (double)f[0]; + values[4] = (double)f[1]; + values[5] = (double)f[2]; + values[6] = (double)f[3]; + } + else { printError("getPOSI", "Unexpected response length."); return -3; } - // TODO: change this to the 64-bit lat/lon/h - - // Copy response into values - memcpy(values, readBuffer + 6, 7 * sizeof(float)); return 0; } @@ -585,7 +606,7 @@ int sendPOSI(XPCSocket sock, double values[], int size, char ac) } // Setup command - unsigned char buffer[46] = "POSI"; + char buffer[46] = "POSI"; buffer[4] = 0xff; //Placeholder for message length buffer[5] = ac; int i; // iterator @@ -621,13 +642,147 @@ int sendPOSI(XPCSocket sock, double values[], int size, char ac) /**** End POSI functions ****/ /*****************************************************************************/ +/*****************************************************************************/ +/**** TERR functions ****/ +/*****************************************************************************/ +int sendTERRRequest(XPCSocket sock, double posi[3], char ac) +{ + // Setup send command + char buffer[30] = "GETT"; + buffer[5] = ac; + memcpy(&buffer[6], posi, 3 * sizeof(double)); + + // Send command + if (sendUDP(sock, buffer, 30) < 0) + { + printError("getTERR", "Failed to send command."); + return -1; + } + return 0; +} + +int getTERRResponse(XPCSocket sock, double values[11], char ac) +{ + // Get response + char readBuffer[62]; + int readResult = readUDP(sock, readBuffer, 62); + if (readResult < 0) + { + printError("getTERR", "Failed to read response."); + return -2; + } + if (readResult != 62) + { + printError("getTERR", "Unexpected response length."); + return -3; + } + + // Copy response into outputs + float f[8]; + ac = readBuffer[5]; + memcpy(values, readBuffer + 6, 3 * sizeof(double)); + memcpy(f, readBuffer + 30, 8 * sizeof(float)); + values[ 3] = (double)f[0]; + values[ 4] = (double)f[1]; + values[ 5] = (double)f[2]; + values[ 6] = (double)f[3]; + values[ 7] = (double)f[4]; + values[ 8] = (double)f[5]; + values[ 9] = (double)f[6]; + values[10] = (double)f[7]; + + return 0; +} + +int sendPOST(XPCSocket sock, double posi[], int size, double values[11], char ac) +{ + // Validate input + if (ac < 0 || ac > 20) + { + printError("sendPOST", "aircraft should be a value between 0 and 20."); + return -1; + } + if (size < 1 || size > 7) + { + printError("sendPOST", "size should be a value between 1 and 7."); + return -2; + } + + // Setup command + char buffer[46] = "POST"; + buffer[4] = 0xff; //Placeholder for message length + buffer[5] = ac; + int i; // iterator + + for (i = 0; i < 7; i++) // double for lat/lon/h + { + double val = -998; + + if (i < size) + { + val = posi[i]; + } + if (i < 3) /* lat/lon/h */ + { + memcpy(&buffer[6 + i*8], &val, sizeof(double)); + } + else /* attitude and gear */ + { + float f = (float)val; + memcpy(&buffer[18 + i*4], &f, sizeof(float)); + } + } + + // Send Command + if (sendUDP(sock, buffer, 46) < 0) + { + printError("sendPOST", "Failed to send command"); + return -3; + } + + // Read Response + int result = getTERRResponse(sock, values, ac); + if (result < 0) + { + // A error ocurred while reading the response. + // getTERRResponse will print an error message, so just return. + return result; + } + return 0; +} + +int getTERR(XPCSocket sock, double posi[3], double values[11], char ac) +{ + // Send Command + int result = sendTERRRequest(sock, posi, ac); + if (result < 0) + { + // An error ocurred while sending. + // sendTERRRequest will print an error message, so just return. + return result; + } + + // Read Response + result = getTERRResponse(sock, values, ac); + if (result < 0) + { + // An error ocurred while reading the response. + // getTERRResponse will print an error message, so just return. + return result; + } + return 0; +} +/*****************************************************************************/ +/**** End TERR functions ****/ +/*****************************************************************************/ + /*****************************************************************************/ /**** CTRL functions ****/ /*****************************************************************************/ int getCTRL(XPCSocket sock, float values[7], char ac) { // Setup send command - unsigned char buffer[6] = "GETC"; + char buffer[6] = "GETC"; buffer[5] = ac; // Send command @@ -638,7 +793,7 @@ int getCTRL(XPCSocket sock, float values[7], char ac) } // Get response - unsigned char readBuffer[31]; + char readBuffer[31]; int readResult = readUDP(sock, readBuffer, 31); if (readResult < 0) { @@ -675,7 +830,7 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac) // Setup Command // 5 byte header + 5 float values * 4 + 2 byte values - unsigned char buffer[31] = "CTRL"; + char buffer[31] = "CTRL"; int cur = 5; int i; // iterator for (i = 0; i < 6; i++) @@ -818,3 +973,40 @@ int sendVIEW(XPCSocket sock, VIEW_TYPE view) /*****************************************************************************/ /**** End View functions ****/ /*****************************************************************************/ + +/*****************************************************************************/ +/**** Comm functions ****/ +/*****************************************************************************/ +int sendCOMM(XPCSocket sock, const char* comm) { + // Setup command + // Max size is technically unlimited. + unsigned char buffer[65536] = "COMM"; + int pos = 5; + + int commLen = strnlen(comm, 256); + if (pos + commLen + 2 > 65536) + { + printError("sendCOMM", "About to overrun the send buffer!"); + return -4; + } + if (commLen > 255) + { + printError("sendCOMM", "comm is too long. Must be less than 256 characters."); + return -1; + } + // Copy comm to buffer + buffer[pos++] = (unsigned char)commLen; + memcpy(buffer + pos, comm, commLen); + pos += commLen; + + // Send command + if (sendUDP(sock, buffer, pos) < 0) + { + printError("setDREF", "Failed to send command"); + return -3; + } + return 0; +} +/*****************************************************************************/ +/**** End Comm functions ****/ +/*****************************************************************************/ \ No newline at end of file diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h index 8cfb06f..3dcf236 100644 --- a/C/src/xplaneConnect.h +++ b/C/src/xplaneConnect.h @@ -113,7 +113,7 @@ int setCONN(XPCSocket* sock, unsigned short port); /// /// \param sock The socket to use to send the command. /// \param pause 0 to unpause the sim; 1 to pause, 100:119 to pause a/c 0:19, 200:219 to unpause a/c 0:19. -/// \returns 0 if successful, otherwise a negative value. +/// \returns 0 if successful, otherwise a negative value. int pauseSim(XPCSocket sock, char pause); // X-Plane UDP DATA @@ -122,7 +122,7 @@ int pauseSim(XPCSocket sock, char pause); /// /// \details This command is compatible with the X-Plane data API. /// \param sock The socket to use to send the command. -/// \param data A 2D array of data rows to read into. +/// \param data 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 data[][9], int rows); @@ -131,7 +131,7 @@ int readDATA(XPCSocket sock, float data[][9], int rows); /// /// \details This command is compatible with the X-Plane data API. /// \param sock The socket to use to send the command. -/// \param data A 2D array of data rows to send. +/// \param data 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 data[][9], int rows); @@ -144,12 +144,12 @@ int sendDATA(XPCSocket sock, float data[][9], int rows); /// 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 value 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 sendDREF(XPCSocket sock, const char* dref, float value[], int size); +/// \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 sendDREF(XPCSocket sock, const char* dref, float values[], int size); /// Sets the specified datarefs to the specified values. /// @@ -159,7 +159,7 @@ int sendDREF(XPCSocket sock, const char* dref, float value[], int size); /// 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 set. -/// \param values A multidimensional array containing the values for each dataref to set. +/// \param values A 2D array containing the values for each dataref to set. /// \param sizes The number of elements in each array in values /// \param count The number of datarefs being set. /// \returns 0 if successful, otherwise a negative value. @@ -173,13 +173,13 @@ int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], /// 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 values The array in which the values 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. +/// Gets the values of the specified datarefs. /// /// \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 @@ -189,7 +189,7 @@ int getDREF(XPCSocket sock, const char* dref, float values[], int* size); /// \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 +/// \param sizes 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[]); @@ -201,20 +201,56 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char /// \param sock The socket used to send the command and receive the response. /// \param values An array to store the position information returned by the /// plugin. The format of values is [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear] +/// \param ac The aircraft number to get the position of. 0 for the main/user's aircraft. /// \returns 0 if successful, otherwise a negative value. -int getPOSI(XPCSocket sock, float values[7], char ac); +int getPOSI(XPCSocket sock, double values[7], char ac); /// 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 +/// \param values A double 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. +/// \param ac The aircraft number to set the position of. 0 for the main/user's aircraft. /// \returns 0 if successful, otherwise a negative value. int sendPOSI(XPCSocket sock, double values[], int size, char ac); +// Terrain + +/// Sets the position and orientation and gets the terrain information of the specified aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param posi A double 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 posi. +/// \param values A double array with the information for the terrain output. The format of values is +/// [Lat, Lon, Alt, Nx, Ny, Nz, Vx, Vy, Vz, wet, result]. The first three are for output of +/// the Lat and Lon of the aircraft with the terrain height directly below. The next three +/// represent the terrain normal. The next three represent the velocity of the terrain. +/// The wet variable is 0.0 if the terrain is dry and 1.0 if wet. +/// The last output is the terrain probe result parameter. +/// \param ac The aircraft number to set the position of. 0 for the main/user's aircraft. +/// \returns 0 if successful, otherwise a negative value. +int sendPOST(XPCSocket sock, double posi[], int size, double values[11], char ac); + +/// Gets the terrain information of the specified aircraft. +/// +/// \param sock The socket to use to send the command. +/// \param posi A double array representing position data about the aircraft. The format of values is +/// [Lat, Lon, Alt]. +/// -998 used for [Lat, Lon, Alt] to request terrain info at the current aircraft position. +/// \param values A double array with the information for the terrain output. The format of values is +/// [Lat, Lon, Alt, Nx, Ny, Nz, Vx, Vy, Vz, wet, result]. The first three are for output of +/// the Lat and Lon of the aircraft with the terrain height directly below. The next three +/// represent the terrain normal. The next three represent the velocity of the terrain. +/// The wet variable is 0.0 if the terrain is dry and 1.0 if wet. +/// The last output is the terrain probe result parameter. +/// \param ac The aircraft number to get the terrain data of. 0 for the main/user's aircraft. +/// \returns 0 if successful, otherwise a negative value. +int getTERR(XPCSocket sock, double posi[3], double values[11], char ac); + // Controls /// Gets the control surface information for the specified aircraft. @@ -223,7 +259,7 @@ int sendPOSI(XPCSocket sock, double values[], int size, char ac); /// \param values An array to store the position information returned by the /// plugin. The format of values is [Elevator, Aileron, Rudder, /// Throttle, Gear, Flaps, Speed Brakes] -/// \param ac The aircraft to set the control surfaces of. 0 is the main/player aircraft. +/// \param ac The aircraft to get the control surfaces of. 0 is the main/user's aircraft. /// \returns 0 if successful, otherwise a negative value. int getCTRL(XPCSocket sock, float values[7], char ac); @@ -234,7 +270,7 @@ int getCTRL(XPCSocket sock, float values[7], char ac); /// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps, Speed Brakes]. 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. +/// \param ac The aircraft to set the control surfaces of. 0 for the main/user's aircraft. /// \returns 0 if successful, otherwise a negative value. int sendCTRL(XPCSocket sock, float values[], int size, char ac); @@ -246,7 +282,7 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac); /// \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. +/// \returns 0 if successful, otherwise a negative value. int sendTEXT(XPCSocket sock, char* msg, int x, int y); /// Sets the camera view in X-Plane. @@ -267,6 +303,13 @@ int sendVIEW(XPCSocket sock, VIEW_TYPE view); /// \returns 0 if successful, otherwise a negative value. int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count); +/// Sends commands. +/// +/// \param sock The socket to use to send the command. +/// \param comm The command string. +/// \returns 0 if successful, otherwise a negative value. +int sendCOMM(XPCSocket sock, const char* comm); + #ifdef __cplusplus } #endif diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index 675da9b..df32cff 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -889,6 +889,42 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Send a command to X-Plane. + * + * @param comm The name of the X-Plane command to send. + * @throws IOException If the command cannot be sent. + */ + public void sendCOMM(String comm) throws IOException + { + //Preconditions + if(comm == null || comm.length() == 0) + { + throw new IllegalArgumentException(("comm must be non-empty.")); + } + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("COMM".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + + //Convert comm to bytes. + byte[] commBytes = comm.getBytes(StandardCharsets.UTF_8); + if (commBytes.length == 0) + { + throw new IllegalArgumentException("COMM is an empty string!"); + } + if (commBytes.length > 255) + { + throw new IllegalArgumentException("comm must be less than 255 bytes in UTF-8. Are you sure this is a valid comm?"); + } + + //Build and send message + os.write(commBytes.length); + os.write(commBytes); + sendUDP(os.toByteArray()); + } + + /** * Sets the port on which the client will receive data from X-Plane. * diff --git a/Python/src/xpc.py b/Python/src/xpc.py index 71b7eb2..4bbf640 100644 --- a/Python/src/xpc.py +++ b/Python/src/xpc.py @@ -416,6 +416,27 @@ class XPlaneConnect(object): buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points) self.sendUDP(buffer) + def sendCOMM(self, comm): + '''Sets the specified datarefs to the specified values. + + Args: + drefs: A list of names of the datarefs to set. + values: A list of scalar or vector values to set. + ''' + if comm == None: + raise ValueError("comm must be non-empty.") + + buffer = struct.pack("<4sx", "COMM") + if len(comm) == 0 or len(comm) > 255: + raise ValueError("comm must be a non-empty string less than 256 characters.") + + # Pack message + fmt = "= 0) { result = getDREFs(*sock, drefs, data, 7, sizes); @@ -66,19 +66,19 @@ int doGETCTest(float values[7], int ac, float expected[7]) return 0; } -int basicCTRLTest(char** drefs, int ac) +int basicCTRLTest(const char** drefs, int ac) { // Set control surfaces to known state. float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; float expected[7] = { 0.0F, 0.0F, 0.0F, NAN, NAN, NAN, NAN }; - XPCSocket sock = openUDP(IP); - pauseSim(sock, 1); // Pause so the controls wont change between 2 and 3 + XPCSocket sock = openUDP(IP); + pauseSim(sock, 1); // Pause so the controls wont change between 2 and 3 int result = doCTRLTest(&sock, drefs, CTRL, 3, ac, expected); if (result < 0) { return -10000 + result; } - crossPlatformUSleep(SLEEP_AMOUNT); + crossPlatformUSleep(SLEEP_AMOUNT); // Test control surfaces and set other values to known state. expected[0] = CTRL[0] = 0.2F; @@ -92,19 +92,19 @@ int basicCTRLTest(char** drefs, int ac) { return -20000 + result; } - crossPlatformUSleep(SLEEP_AMOUNT); + crossPlatformUSleep(SLEEP_AMOUNT); // Test other values and verify control surfaces unchanged. expected[0] = CTRL[0] = 0.15F; expected[1] = CTRL[1] = 0.15F; expected[2] = CTRL[2] = 0.15F; expected[3] = CTRL[3] = 0.9F; - CTRL[4] = -998; + CTRL[4] = -998; CTRL[5] = -998; result = doCTRLTest(&sock, drefs, CTRL, 6, ac, expected); - pauseSim(sock, 0); - closeUDP(sock); + pauseSim(sock, 0); + closeUDP(sock); if (result < 0) { if (result == -1004) { @@ -112,12 +112,12 @@ int basicCTRLTest(char** drefs, int ac) } return -30000 + result; } - return 0; + return 0; } int testCTRL_Player() { - char* drefs[] = + const char* drefs[] = { "sim/cockpit2/controls/yoke_pitch_ratio", "sim/cockpit2/controls/yoke_roll_ratio", @@ -132,7 +132,7 @@ int testCTRL_Player() int testCTRL_NonPlayer() { - char* drefs[] = + const char* drefs[] = { "sim/multiplayer/position/plane1_yolk_pitch", "sim/multiplayer/position/plane1_yolk_roll", @@ -147,7 +147,7 @@ int testCTRL_NonPlayer() int testCTRL_Speedbrakes() { - char* drefs[] = + const char* drefs[] = { "sim/cockpit2/controls/yoke_pitch_ratio", "sim/cockpit2/controls/yoke_roll_ratio", @@ -161,8 +161,8 @@ int testCTRL_Speedbrakes() // Arm float CTRL[7] = { -998, -998, -998, -998, -998, -998, -0.5F }; float expected[7] = { NAN, NAN, NAN, NAN, NAN, NAN, -0.5F }; - XPCSocket sock = openUDP(IP); - pauseSim(sock, 1); // Pause so the controls wont change between 2 and 3 + XPCSocket sock = openUDP(IP); + pauseSim(sock, 1); // Pause so the controls wont change between 2 and 3 int result = doCTRLTest(&sock, drefs, CTRL, 7, 0, expected); if (result < 0) { @@ -180,13 +180,13 @@ int testCTRL_Speedbrakes() // Retract expected[6] = CTRL[6] = 0.0F; result = doCTRLTest(&sock, drefs, CTRL, 7, 0, expected); - pauseSim(sock, 0); - closeUDP(sock); + pauseSim(sock, 0); + closeUDP(sock); if (result < 0) { return -30000 + result; } - return 0; + return 0; } int testGETC() diff --git a/TestScripts/C Tests/DataTests.h b/TestScripts/C Tests/DataTests.h index 4fde0ab..233602c 100644 --- a/TestScripts/C Tests/DataTests.h +++ b/TestScripts/C Tests/DataTests.h @@ -10,7 +10,7 @@ int testDATA() { // Initialize int i, j; // Iterator - char* drefs[100] = + const char* drefs[100] = { "sim/aircraft/parts/acf_gear_deploy" }; diff --git a/TestScripts/C Tests/DrefTests.h b/TestScripts/C Tests/DrefTests.h index c64ce6d..586b408 100644 --- a/TestScripts/C Tests/DrefTests.h +++ b/TestScripts/C Tests/DrefTests.h @@ -6,7 +6,7 @@ #include "Test.h" #include "xplaneConnect.h" -int doGETDTest(char* drefs[], float* expected[], int count, int sizes[]) +int doGETDTest(const char* drefs[], float* expected[], int count, int sizes[]) { // Setup memory int* asizes = (int*)malloc(sizeof(int) * count); @@ -37,7 +37,7 @@ int doGETDTest(char* drefs[], float* expected[], int count, int sizes[]) return result; } -int doDREFTest(char* drefs[], float* values[], float* expected[], int count, int sizes[]) +int doDREFTest(const char* drefs[], float* values[], float* expected[], int count, int sizes[]) { // Setup memory int* asizes = (int*)malloc(sizeof(int) * count); @@ -74,7 +74,7 @@ int doDREFTest(char* drefs[], float* values[], float* expected[], int count, int int testGETD_Basic() { - char* drefs[] = + const char* drefs[] = { "sim/cockpit/switches/gear_handle_status", //int "sim/cockpit/autopilot/altitude", //float @@ -99,20 +99,20 @@ int testGETD_Basic() int testGETD_TestFloat() { - char* dref = "sim/test/test_float"; + const char* dref = "sim/test/test_float"; int size = 1; float* expected[1]; expected[0] = (float*)malloc(sizeof(float)); expected[0][0] = 0.0F; - int result = doGETDTest(&dref, &expected, 1, &size); + int result = doGETDTest(&dref, expected, 1, &size); free(expected[0]); return result; } int testGETD_Types() { - char* drefs[] = + const char* drefs[] = { "sim/cockpit/switches/gear_handle_status", //int "sim/cockpit/autopilot/altitude", //float @@ -173,7 +173,7 @@ int testGETD_Types() int testDREF() { - char* drefs[] = + const char* drefs[] = { "sim/cockpit/switches/gear_handle_status", //int "sim/cockpit/autopilot/altitude", //float diff --git a/TestScripts/C Tests/PosiTests.h b/TestScripts/C Tests/PosiTests.h index fc084ec..1b3983a 100644 --- a/TestScripts/C Tests/PosiTests.h +++ b/TestScripts/C Tests/PosiTests.h @@ -6,7 +6,7 @@ #include "Test.h" #include "xplaneConnect.h" -int doPOSITest(char* drefs[7], double values[], int size, int ac, double expected[7]) +int doPOSITest(const char* drefs[7], double values[], int size, int ac, double expected[7]) { float* data[7]; int sizes[7]; @@ -43,7 +43,7 @@ int doPOSITest(char* drefs[7], double values[], int size, int ac, double expecte int doGETPTest(double values[7], int ac, double expected[7]) { // Execute Test - float actual[7]; + double actual[7]; XPCSocket sock = openUDP(IP); int result = sendPOSI(sock, values, 7, ac); if (result >= 0) @@ -67,7 +67,7 @@ int doGETPTest(double values[7], int ac, double expected[7]) return 0; } -int basicPOSITest(char** drefs, int ac) +int basicPOSITest(const char** drefs, int ac) { // Set psoition and initial orientation double POSI[7] = { 37.524, -122.06899, 2500, 0, 0, 0, 1 }; @@ -110,12 +110,12 @@ int basicPOSITest(char** drefs, int ac) { return -20000 + result; } - return 0; + return 0; } int testPOSI_Player() { - char* drefs[] = + const char* drefs[] = { "sim/flightmodel/position/latitude", "sim/flightmodel/position/longitude", @@ -130,7 +130,7 @@ int testPOSI_Player() int testPOSI_NonPlayer() { - char* drefs[] = + const char* drefs[] = { "sim/multiplayer/position/plane1_lat", "sim/multiplayer/position/plane1_lon", diff --git a/TestScripts/Python3 Tests/Tests.py b/TestScripts/Python3 Tests/Tests.py new file mode 100644 index 0000000..41e2bd4 --- /dev/null +++ b/TestScripts/Python3 Tests/Tests.py @@ -0,0 +1,434 @@ +import random +import unittest +import importlib +import time + +from importlib.machinery import SourceFileLoader +xpc = SourceFileLoader('xpc', '../../Python3/src/xpc.py').load_module() + +class XPCTests(unittest.TestCase): + """Tests the functionality of the XPlaneConnect class.""" + + def test_init(self): + try: + client = xpc.XPlaneConnect() + except: + self.fail("Default constructor failed.") + + try: + client = xpc.XPlaneConnect("I'm not a real host") + self.fail("Failed to catch invalid XP host.") + except ValueError: + pass + + try: + client = xpc.XPlaneConnect("127.0.0.1", 90001) + self.fail("Failed to catch invalid XP port.") + except ValueError: + pass + try: + client = xpc.XPlaneConnect("127.0.0.1", -1) + self.fail("Failed to catch invalid XP port.") + except ValueError: + pass + + try: + client = xpc.XPlaneConnect("127.0.0.1", 49009, 90001) + self.fail("Failed to catch invalid local port.") + except ValueError: + pass + try: + client = xpc.XPlaneConnect("127.0.0.1", 49009, -1) + self.fail("Failed to catch invalid XP port.") + except ValueError: + pass + + try: + client = xpc.XPlaneConnect("127.0.0.1", 49009, 0, -1) + self.fail("Failed to catch invalid timeout.") + except ValueError: + pass + + def test_close(self): + client = xpc.XPlaneConnect("127.0.0.1", 49009, 49063) + client.close() + self.assertIsNone(client.socket) + client = xpc.XPlaneConnect("127.0.0.1", 49009, 49063) + + def test_send_read(self): + # Init + test = "\x00\x01\x02\x03\x05" + + # Setup + sender = xpc.XPlaneConnect("127.0.0.1", 49063, 49064) + receiver = xpc.XPlaneConnect("127.0.0.1", 49009, 49063) + + # Execution + sender.sendUDP(test.encode()) + buf = receiver.readUDP() + + # Cleanup + sender.close() + receiver.close() + + # Tests + for a, b in zip(test, buf.decode()): + self.assertEqual(a, b) + + def test_getDREFs(self): + # Setup + client = xpc.XPlaneConnect() + drefs = ["sim/cockpit/switches/gear_handle_status",\ + "sim/cockpit2/switches/panel_brightness_ratio"] + + # Execution + result = client.getDREFs(drefs) + + # Cleanup + client.close() + + # Tests + self.assertEqual(2, len(result)) + self.assertEqual(1, len(result[0])) + self.assertEqual(4, len(result[1])) + + def test_sendDREF(self): + dref = "sim/cockpit/switches/gear_handle_status" + value = None + def do_test(): + # Setup + client = xpc.XPlaneConnect() + + # Execute + client.sendDREF(dref, value) + result = client.getDREF(dref) + + # Cleanup + client.close() + + # Tests + self.assertEqual(1, len(result)) + self.assertEqual(value, result[0]) + + # Test 1 + value = 1 + do_test() + + # Test 2 + value = 0 + do_test() + + def test_sendDREFs(self): + drefs = [\ + "sim/cockpit/switches/gear_handle_status",\ + "sim/cockpit/autopilot/altitude"] + values = None + def do_test(): + # Setup + client = xpc.XPlaneConnect() + + # Execute + client.sendDREFs(drefs, values) + result = client.getDREFs(drefs) + + # Cleanup + client.close() + + # Tests + self.assertEqual(2, len(result)) + self.assertEqual(1, len(result[0])) + self.assertEqual(values[0], result[0][0]) + self.assertEqual(1, len(result[1])) + self.assertEqual(values[1], result[1][0]) + + # Test 1 + values = [1, 2000] + do_test() + + # Test 2 + values = [0, 4000] + do_test() + + def test_sendDATA(self): + # Setup + dref = "sim/aircraft/parts/acf_gear_deploy" + data = [[ 14, 1, 0, -998, -998, -998, -998, -998, -998 ]] + client = xpc.XPlaneConnect() + + # Execute + client.sendDATA(data) + result = client.getDREF(dref) + + # Cleanup + client.close() + + #Tests + self.assertEqual(result[0], data[0][1]) + + def test_pauseSim(self): + dref = "sim/operation/override/override_planepath" + value = None + expected = None + def do_test(): + # Setup + client = xpc.XPlaneConnect() + + # Execute + client.pauseSim(value) + result = client.getDREF(dref) + + # Cleanup + client.close() + + # Test + self.assertAlmostEqual(expected, result[0]) + + # Test 1 + value = True + expected = 1.0 + do_test() + + # Test 2 + value = False + expected = 0.0 + do_test() + + # Test 3 + value = 1 + expected = 1.0 + do_test() + + # Test 4 + value = 2 + expected = 0.0 + do_test() + + def test_getCTRL(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.sendCTRL(values, ac) + result = client.getCTRL(ac) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [0.0, 0.0, 0.0, 0.8, 1.0, 0.5, -1.5] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + + + def test_sendCTRL(self): + # Setup + drefs = ["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"] + ctrl = [] + + def do_test(): + client = xpc.XPlaneConnect() + + # Execute + client.sendCTRL(ctrl) + result = client.getDREFs(drefs) + + # Cleanup + client.close() + + # Tests + self.assertEqual(6, len(result)) + for i in range(6): + self.assertAlmostEqual(ctrl[i], result[i][0], 4) + + # Test 1 + ctrl = [ -1.0, -1.0, -1.0, 0.0, 1.0, 1.0 ] + do_test() + + # Test 2 + ctrl = [ 1.0, 1.0, 1.0, 0.0, 1.0, 0.5 ] + do_test() + + # Test 2 + ctrl = [ 0.0, 0.0, 0.0, 0.8, 1.0, 0.0 ] + do_test() + + def test_sendCTRL_speedbrake(self): + # Setup + dref = "sim/flightmodel/controls/sbrkrqst" + ctrl = [] + + def do_test(): + client = xpc.XPlaneConnect() + + # Execute + client.sendCTRL(ctrl) + result = client.getDREF(dref) + + # Cleanup + client.close() + + # Tests + self.assertAlmostEqual(result[0], ctrl[6]) + + # Test 1 + ctrl = [-998, -998, -998, -998, -998, -998, -0.5] + do_test() + + # Test 2 + ctrl[6] = 1.0 + do_test() + + # Test 2 + ctrl[6] = 0.0 + do_test() + + def test_getPOSI(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.pauseSim(True) + client.sendPOSI(values, ac) + result = client.getPOSI(ac) + client.pauseSim(False) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + + def test_sendPOSI(self): + # Setup + drefs = ["sim/flightmodel/position/latitude",\ + "sim/flightmodel/position/longitude",\ + "sim/flightmodel/position/elevation",\ + "sim/flightmodel/position/theta",\ + "sim/flightmodel/position/phi",\ + "sim/flightmodel/position/psi",\ + "sim/cockpit/switches/gear_handle_status"] + posi = None + def do_test(): + client = xpc.XPlaneConnect() + + # Execute + client.pauseSim(True) + client.sendPOSI(posi) + result = client.getDREFs(drefs) + client.pauseSim(False) + + # Cleanup + client.close() + + # Tests + self.assertEqual(7, len(result)) + for i in range(7): + self.assertAlmostEqual(posi[i], result[i][0], 4) + + # Test 1 + posi = [ 37.524, -122.06899, 2500, 5, 7, 11, 1 ] + do_test() + + # Test 2 + posi = [ 38, -121.0, 2000, -10, 0, 0, 0 ] + do_test() + + def test_sendTEXT(self): + # Setup + client = xpc.XPlaneConnect() + x = 200 + y = 700 + msg = "Python sendTEXT test message." + + # Execution + client.sendTEXT(msg, x, y) + # NOTE: Manually verify that msg appears on the screen in X-Plane + + # Cleanup + client.close() + + def test_sendView(self): + # Setup + dref = "sim/graphics/view/view_type" + fwd = 1000 + chase = 1017 + + #Execution + with xpc.XPlaneConnect() as client: + client.sendVIEW(xpc.ViewType.Forwards) + result = client.getDREF(dref) + self.assertAlmostEqual(fwd, result[0], 1e-4) + client.sendVIEW(xpc.ViewType.Chase) + result = client.getDREF(dref) + self.assertAlmostEqual(chase, result[0], 1e-4) + + + def test_sendWYPT(self): + # Setup + client = xpc.XPlaneConnect() + 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] + + # Execution + client.sendPOSI([37.5245, -122.06899, 2500]) + client.sendWYPT(3, []) + client.sendWYPT(1, points) + # NOTE: Manually verify that points appear on the screen in X-Plane + + # Cleanup + client.close() + + def test_setCONN(self): + # Setup + dref = "sim/cockpit/switches/gear_handle_status"; + client = xpc.XPlaneConnect() + + # Execute + client.setCONN(49055) + result = client.getDREF(dref) + + # Cleanup + client.close() + + # Test + self.assertEqual(1, len(result)) + + + +if __name__ == '__main__': + unittest.main() diff --git a/TestScripts/Python3 Tests/Tests.pyproj b/TestScripts/Python3 Tests/Tests.pyproj new file mode 100644 index 0000000..c1626b5 --- /dev/null +++ b/TestScripts/Python3 Tests/Tests.pyproj @@ -0,0 +1,45 @@ + + + + Debug + 2.0 + 6931ebb2-4e01-4c5a-86b6-668c0e75051b + . + Tests.py + ..\..\Python\src\ + . + . + Tests + Tests + {9a7a9026-48c1-4688-9d5d-e5699d47d074} + 2.7 + + + true + false + + + true + false + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + + + + \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 473b29a..37ca5f7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,11 +6,11 @@ trigger: branches: include: - # Build all branches starting with 'ms-' This is useful when making and testing chanes made to this file - - ms-* + # Build all branches starting with 'ms-' This is useful when making and testing chanes made to this file + - ms-* tags: include: - - '*' + - "*" # pr: # - '*' variables: @@ -27,140 +27,139 @@ variables: # https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-github xpcGitHubConnection: XPC-Azure - stages: - stage: Build jobs: - - job: macOS - pool: - vmImage: 'macos-latest' + - job: macOS + pool: + vmImage: "macos-latest" - steps: - - task: Xcode@5 - inputs: - actions: 'build' - scheme: '' - sdk: 'macosx10.14' - configuration: 'Release' - xcWorkspacePath: 'xpcPlugin/xpcPlugin.xcodeproj' - xcodeVersion: 'default' # Options: 8, 9, 10, default, specifyPath - - task: PublishPipelineArtifact@0 - inputs: - artifactName: '$(xpcArtifactNameMac)' - targetPath: '$(xpcBuildOutputPath)' + steps: + - task: Xcode@5 + inputs: + actions: "build" + scheme: "" + sdk: "macosx10.15" + configuration: "Release" + xcWorkspacePath: "xpcPlugin/xpcPlugin.xcodeproj" + xcodeVersion: "default" # Options: 8, 9, 10, default, specifyPath + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "$(xpcArtifactNameMac)" + targetPath: "$(xpcBuildOutputPath)" - - job: Linux - pool: - vmImage: 'ubuntu-latest' + - job: Linux + pool: + vmImage: "ubuntu-latest" - steps: - - script: | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install -y g++-5 mesa-common-dev g++-multilib g++-5-multilib - displayName: 'apt install' + steps: + - script: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install -y g++-5 mesa-common-dev g++-multilib g++-5-multilib + displayName: "apt install" - - script: | - cd xpcPlugin - cmake . - make - displayName: 'make' + - script: | + cd xpcPlugin + cmake . + make + displayName: "make" - - task: PublishPipelineArtifact@0 - inputs: - artifactName: 'xpc-linux' - targetPath: '$(xpcBuildOutputPath)' + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "xpc-linux" + targetPath: "$(xpcBuildOutputPath)" - - job: Windows - pool: - vmImage: 'vs2017-win2016' + - job: Windows + pool: + vmImage: "vs2017-win2016" - variables: - solution: 'xpcPlugin/xpcPlugin/xpcPlugin.sln' - buildPlatform: 'Win32|x64' - buildConfiguration: 'Release' - appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\' + variables: + solution: "xpcPlugin/xpcPlugin/xpcPlugin.sln" + buildPlatform: "Win32|x64" + buildConfiguration: "Release" + appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\' - steps: - - task: NuGetToolInstaller@0 + steps: + - task: NuGetToolInstaller@0 - - task: NuGetCommand@2 - inputs: - restoreSolution: '$(solution)' + - task: NuGetCommand@2 + inputs: + restoreSolution: "$(solution)" - - task: VSBuild@1 - inputs: - platform: 'Win32' - solution: '$(solution)' - configuration: '$(buildConfiguration)' - msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' + - task: VSBuild@1 + inputs: + platform: "Win32" + solution: "$(solution)" + configuration: "$(buildConfiguration)" + msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' - - task: VSBuild@1 - inputs: - platform: 'x64' - solution: '$(solution)' - configuration: '$(buildConfiguration)' - msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' + - task: VSBuild@1 + inputs: + platform: "x64" + solution: "$(solution)" + configuration: "$(buildConfiguration)" + msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' - - task: Bash@3 - inputs: - targetType: 'inline' - script: | - rm $(xpcBuildOutputPath)/win.exp - rm $(xpcBuildOutputPath)/win.lib - rm $(xpcBuildOutputPath)/64/win.exp - rm $(xpcBuildOutputPath)/64/win.lib + - task: Bash@3 + inputs: + targetType: "inline" + script: | + rm $(xpcBuildOutputPath)/win.exp + rm $(xpcBuildOutputPath)/win.lib + rm $(xpcBuildOutputPath)/64/win.exp + rm $(xpcBuildOutputPath)/64/win.lib - - task: PublishPipelineArtifact@0 - inputs: - artifactName: '$(xpcArtifactNameWindows)' - targetPath: '$(xpcBuildOutputPath)' + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "$(xpcArtifactNameWindows)" + targetPath: "$(xpcBuildOutputPath)" - stage: Deploy jobs: - - job: Package_Binaries - displayName: 'Package Binaries' - pool: - vmImage: 'ubuntu-latest' + - job: Package_Binaries + displayName: "Package Binaries" + pool: + vmImage: "ubuntu-latest" - steps: - - task: DownloadPipelineArtifact@0 - inputs: - artifactName: '$(xpcArtifactNameMac)' - targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) - - task: DownloadPipelineArtifact@0 - inputs: - artifactName: '$(xpcArtifactNameLinux)' - targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) + steps: + - task: DownloadPipelineArtifact@0 + inputs: + artifactName: "$(xpcArtifactNameMac)" + targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) + - task: DownloadPipelineArtifact@0 + inputs: + artifactName: "$(xpcArtifactNameLinux)" + targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) - - task: DownloadPipelineArtifact@0 - inputs: - artifactName: '$(xpcArtifactNameWindows)' - targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) + - task: DownloadPipelineArtifact@0 + inputs: + artifactName: "$(xpcArtifactNameWindows)" + targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) - - task: PublishPipelineArtifact@0 - inputs: - artifactName: '$(xpcPackageName)' - targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: "$(xpcPackageName)" + targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName) - # Archive Files - - task: ArchiveFiles@2 - inputs: - rootFolderOrFile: '$(System.DefaultWorkingDirectory)/$(xpcPackageName)' - includeRootFolder: true - archiveType: 'zip' - archiveFile: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip' + # Archive Files + - task: ArchiveFiles@2 + inputs: + rootFolderOrFile: "$(System.DefaultWorkingDirectory)/$(xpcPackageName)" + includeRootFolder: true + archiveType: "zip" + archiveFile: "$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip" - # Uploads the .zip to GitHub and creates a new "Release" with the commit tag - # This tasks runs only on pushed tags - - task: GitHubRelease@0 - inputs: - gitHubConnection: '$(xpcGitHubConnection)' - repositoryName: '$(Build.Repository.Name)' - action: 'create' - target: '$(Build.SourceVersion)' - tagSource: 'auto' - tag: $(tagName) - assets: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip' - isPreRelease: true - isDraft: true + # Uploads the .zip to GitHub and creates a new "Release" with the commit tag + # This tasks runs only on pushed tags + - task: GitHubRelease@0 + inputs: + gitHubConnection: "$(xpcGitHubConnection)" + repositoryName: "$(Build.Repository.Name)" + action: "create" + target: "$(Build.SourceVersion)" + tagSource: "auto" + tag: $(tagName) + assets: "$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip" + isPreRelease: true + isDraft: true diff --git a/xpcPlugin/CameraCallbacks.cpp b/xpcPlugin/CameraCallbacks.cpp new file mode 100644 index 0000000..bb839b2 --- /dev/null +++ b/xpcPlugin/CameraCallbacks.cpp @@ -0,0 +1,173 @@ +// +// support.cpp +// xpcPlugin +// +// Created by Kai Lehmkuehler on 14/01/2019. +// + +#include + +#include "MessageHandlers.h" +#include "DataManager.h" +#include "Log.h" + +#include "XPLMUtilities.h" +#include "XPLMGraphics.h" + + +#include +#include + +namespace XPC +{ + // Runway Camera Callback: Places the camera at campos.loc and points it at the aircraft, similar to a remote piloting experience. + // Optionally, the direction of the camera can be specified by the user. + int MessageHandlers::CamCallback_RunwayCam( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon) + { + if (outCameraPosition && !inIsLosingControl) + { + struct CameraProperties* campos = (struct CameraProperties*)inRefcon; + + // camera position + double clat = campos->loc[0]; + double clon = campos->loc[1]; + double calt = campos->loc[2]; + + double cX; + double cY; + double cZ; + XPLMWorldToLocal(clat, clon, calt, &cX, &cY, &cZ); + + outCameraPosition->x = cX; + outCameraPosition->y = cY; + outCameraPosition->z = cZ; + + // Log::FormatLine(LOG_TRACE, "CAM", "Cam pos %f %f %f", clat, clon, calt); + + // calculate camera direction to point at the aircraft + if(campos->direction[0] < -180) + { + // local aircraft position + double x = XPC::DataManager::GetDouble(XPC::DREF_LocalX, 0); + double y = XPC::DataManager::GetDouble(XPC::DREF_LocalY, 0); + double z = XPC::DataManager::GetDouble(XPC::DREF_LocalZ, 0); + + // relative position vector camera to aircraft + double dx = x - cX; + double dy = y - cY; + double dz = z - cZ; + + // Log::FormatLine(LOG_TRACE, "CAM", "Cam vect %f %f %f", dx, dy, dz); + + double pi = 3.141592653589793; + + // horizontal distance + double dist = sqrt(dx*dx + dz*dz); + + outCameraPosition->pitch = atan2(dy, dist) * 180.0/pi; + + double angle = atan2(dz, dx) * 180.0/pi; // rel to pos right (pos X) + + outCameraPosition->heading = 90 + angle; // rel to north + + // Log::FormatLine(LOG_TRACE, "CAM", "Cam p %f hdg %f ", outCameraPosition->pitch, outCameraPosition->heading); + + outCameraPosition->roll = 0; + } + // point camera at specified direction + else + { + outCameraPosition->roll = campos->direction[0]; + outCameraPosition->pitch = campos->direction[1]; + outCameraPosition->heading = campos->direction[2]; + } + + outCameraPosition->zoom = campos->zoom; + } + + return 1; + } + + // Chase Camera Callback: Places the camera at campos.loc RELATIVE to and MOVING with the aircraft, pointing at the specified + // direction. + int MessageHandlers::CamCallback_ChaseCam( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon) + { + if (outCameraPosition && !inIsLosingControl) + { + double DTR = 3.141592653589793 / 180.0; + + struct CameraProperties* campos = (struct CameraProperties*)inRefcon; + + // Camera location relative to aircraft (local axes) + + // local aircraft position + double x = XPC::DataManager::GetDouble(XPC::DREF_LocalX, 0); // +east + double y = XPC::DataManager::GetDouble(XPC::DREF_LocalY, 0); // +up + double z = XPC::DataManager::GetDouble(XPC::DREF_LocalZ, 0); // +south + + // aircraft attitude - degrees + double phi = XPC::DataManager::GetFloat(XPC::DREF_Roll, 0); + double the = XPC::DataManager::GetFloat(XPC::DREF_Pitch, 0); + double psi = XPC::DataManager::GetFloat(XPC::DREF_HeadingTrue, 0); + + // camera position vector with respect to aircraft CG [m] (body axes) + double c_x = campos->loc[0]; // back + double c_y = campos->loc[1]; // right + double c_z = campos->loc[2]; // up + + // camera position vector in local axes - will move with aircraft if not along principal axes + // cLocal = Leb * cBody + // http://www.xsquawkbox.net/xpsdk/mediawiki/ScreenCoordinates + + double x_phi=c_y*cos(phi*DTR) + c_z*sin(phi*DTR); + double y_phi=c_z*cos(phi*DTR) - c_y*sin(phi*DTR); + double z_phi=c_x; + + double x_the=x_phi; + double y_the=y_phi*cos(the*DTR) - z_phi*sin(the*DTR); + double z_the=z_phi*cos(the*DTR) + y_phi*sin(the*DTR); + + double x_wrl=x_the*cos(psi*DTR) - z_the*sin(psi*DTR); + double y_wrl=y_the ; + double z_wrl=z_the*cos(psi*DTR) + x_the*sin(psi*DTR); + + outCameraPosition->x = x + x_wrl; + outCameraPosition->y = y + y_wrl; + outCameraPosition->z = z + z_wrl; + + // set direction value to -998 to keep camera pointing straight along that axis + if(campos->direction[0] < -180) + { + outCameraPosition->roll = 0; + } + else + { + outCameraPosition->roll = phi + campos->direction[0]; + } + + if(campos->direction[1] < -180) + { + outCameraPosition->pitch = 0; + } + else + { + outCameraPosition->pitch = the + campos->direction[1]; + } + + if(campos->direction[2] < -180) + { + outCameraPosition->heading = 0; + } + else + { + outCameraPosition->heading = psi + campos->direction[2]; + } + + outCameraPosition->zoom = campos->zoom; + } + + return 1; + + } + +} diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 6e66abb..33d87f6 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -19,6 +19,7 @@ #include "XPLMDataAccess.h" #include "XPLMGraphics.h" +#include "XPLMUtilities.h" #include #include @@ -668,6 +669,11 @@ namespace XPC return; } + if (IsDefault(pos[0]) && IsDefault(pos[1]) && IsDefault(pos[2])) + { + Log::WriteLine(LOG_INFO, "DMAN", "Skipped SetPosition. All values were default"); + return; + } if (IsDefault(pos[0])) { pos[0] = GetDouble(DREF_Latitude, aircraft); @@ -718,6 +724,12 @@ namespace XPC return; } + + if (IsDefault(orient[0]) && IsDefault(orient[1]) && IsDefault(orient[2])) + { + Log::WriteLine(LOG_INFO, "DMAN", "Skipped SetPosition. All values were default"); + return; + } if (IsDefault(orient[0])) { orient[0] = GetFloat(DREF_Pitch, aircraft); @@ -780,6 +792,21 @@ namespace XPC Set(DREF_FlapActual, value); } + void DataManager::Execute(const std::string& comm) + { + Log::FormatLine(LOG_INFO, "DMAN", "Executing command (value:%s)", comm.c_str()); + + XPLMCommandRef xcref = XPLMFindCommand(comm.c_str()); + if (!xcref) + { + // COMM does not exist + Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid COMM %s", comm.c_str()); + return; + } + + XPLMCommandOnce(xcref); + } + float DataManager::GetDefaultValue() { return -998.0F; diff --git a/xpcPlugin/DataManager.h b/xpcPlugin/DataManager.h index 15f1218..27e3a40 100644 --- a/xpcPlugin/DataManager.h +++ b/xpcPlugin/DataManager.h @@ -410,6 +410,11 @@ namespace XPC /// \param value The flaps settings. Should be between 0.0 (no flaps) and 1.0 (full flaps). static void SetFlaps(float value); + /// Executes a command + /// + /// \param comm The name of the command to execute. + static void Execute(const std::string& comm); + /// Gets a default value that indicates that a dataref should not be changed. static float GetDefaultValue(); diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp index f3401f0..318cf5e 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -121,7 +121,7 @@ namespace XPC } Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } - else if (head == "GETC" || head == "GETP") + else if (head == "GETC" || head == "GETP" || head == "GETT") { ss << " Aircraft:" << (int)buffer[5]; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); @@ -138,14 +138,28 @@ namespace XPC cur += 1 + buffer[cur]; } } - else if (head == "POSI") + else if (head == "POSI" || head == "POST") { char aircraft = buffer[5]; - float gear = *((float*)(buffer + 30)); - float pos[3]; + float gear; + double pos[3]; float orient[3]; - memcpy(pos, buffer + 6, 12); - memcpy(orient, buffer + 18, 12); + if (size == 34) /* lat/lon/h as 32-bit float */ + { + float posd_32[3]; + memcpy(posd_32, buffer + 6, 12); + pos[0] = posd_32[0]; + pos[1] = posd_32[1]; + pos[2] = posd_32[2]; + memcpy(orient, buffer + 18, 12); + memcpy(&gear, buffer + 30, 4); + } + else if (size == 46) /* lat/lon/h as 64-bit double */ + { + memcpy(pos, buffer + 6, 24); + memcpy(orient, buffer + 30, 12); + memcpy(&gear, buffer + 42, 4); + } ss << " AC:" << (int)aircraft; ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; ss << orient[0] << ' ' << orient[1] << ' ' << orient[2] << ") Gear:"; @@ -162,6 +176,11 @@ namespace XPC ss << "Type:" << *((unsigned long*)(buffer + 5)); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } + else if (head == "COMM") + { + ss << "Type:" << *((unsigned long*)(buffer + 5)); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } else { ss << " UNKNOWN HEADER "; diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index cc4ee18..5b1d79d 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -8,21 +8,22 @@ // 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. +// * 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. + #include "MessageHandlers.h" #include "DataManager.h" #include "Drawing.h" #include "Log.h" #include "XPLMUtilities.h" +#include "XPLMScenery.h" #include "XPLMGraphics.h" - #include #include #include @@ -30,7 +31,6 @@ #define MULTICAST_GROUP "239.255.1.1" #define MULITCAST_PORT 49710 - namespace XPC { std::map MessageHandlers::connections; @@ -39,8 +39,11 @@ namespace XPC std::string MessageHandlers::connectionKey; MessageHandlers::ConnectionInfo MessageHandlers::connection; UDPSocket* MessageHandlers::sock; - + static sockaddr multicast_address = UDPSocket::GetAddr(MULTICAST_GROUP, MULITCAST_PORT); + + // define a static terrain probe handler (do not re-create probe for each query) + XPLMProbeRef Terrain_probe = nullptr; void MessageHandlers::SetSocket(UDPSocket* socket) { @@ -60,12 +63,15 @@ namespace XPC handlers.insert(std::make_pair("DREF", MessageHandlers::HandleDref)); handlers.insert(std::make_pair("GETD", MessageHandlers::HandleGetD)); handlers.insert(std::make_pair("POSI", MessageHandlers::HandlePosi)); + handlers.insert(std::make_pair("POST", MessageHandlers::HandlePosT)); handlers.insert(std::make_pair("SIMU", MessageHandlers::HandleSimu)); handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText)); handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt)); handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleView)); handlers.insert(std::make_pair("GETC", MessageHandlers::HandleGetC)); handlers.insert(std::make_pair("GETP", MessageHandlers::HandleGetP)); + handlers.insert(std::make_pair("COMM", MessageHandlers::HandleComm)); + handlers.insert(std::make_pair("GETT", MessageHandlers::HandleGetT)); // X-Plane data messages handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData)); @@ -136,11 +142,11 @@ namespace XPC MessageHandlers::HandleUnknown(msg); } } - + void MessageHandlers::SendBeacon(const std::string& pluginVersion, unsigned short pluginReceivePort, int xplaneVersion) { - + unsigned char response[128] = "BECN"; - + std::size_t cur = 5; // 2 bytes plugin port @@ -150,12 +156,12 @@ namespace XPC // 4 bytes xplane version *((uint32_t*)(response + cur)) = xplaneVersion; cur += sizeof(uint32_t); - + // plugin version int len = pluginVersion.length(); memcpy(response + cur, pluginVersion.c_str(), len); cur += strlen(pluginVersion.c_str()) + len; - + sock->SendTo(response, cur, &multicast_address); } @@ -554,21 +560,20 @@ namespace XPC unsigned char aircraft = buffer[5]; Log::FormatLine(LOG_TRACE, "GPOS", "Getting position information for aircraft %u", aircraft); - unsigned char response[34] = "POSI"; + unsigned char response[46] = "POSI"; response[5] = (char)DataManager::GetInt(DREF_GearHandle, aircraft); - // TODO change lat/lon/h to double? - *((float*)(response + 6)) = (float)DataManager::GetDouble(DREF_Latitude, aircraft); - *((float*)(response + 10)) = (float)DataManager::GetDouble(DREF_Longitude, aircraft); - *((float*)(response + 14)) = (float)DataManager::GetDouble(DREF_Elevation, aircraft); - *((float*)(response + 18)) = DataManager::GetFloat(DREF_Pitch, aircraft); - *((float*)(response + 22)) = DataManager::GetFloat(DREF_Roll, aircraft); - *((float*)(response + 26)) = DataManager::GetFloat(DREF_HeadingTrue, aircraft); + *((double*)(response + 6)) = DataManager::GetDouble(DREF_Latitude, aircraft); + *((double*)(response + 14)) = DataManager::GetDouble(DREF_Longitude, aircraft); + *((double*)(response + 22)) = DataManager::GetDouble(DREF_Elevation, aircraft); + *((float*)(response + 30)) = DataManager::GetFloat(DREF_Pitch, aircraft); + *((float*)(response + 34)) = DataManager::GetFloat(DREF_Roll, aircraft); + *((float*)(response + 38)) = DataManager::GetFloat(DREF_HeadingTrue, aircraft); float gear[10]; DataManager::GetFloatArray(DREF_GearDeploy, gear, 10, aircraft); - *((float*)(response + 30)) = gear[0]; + *((float*)(response + 42)) = gear[0]; - sock->SendTo(response, 34, &connection.addr); + sock->SendTo(response, 46, &connection.addr); } void MessageHandlers::HandlePosi(const Message& msg) @@ -580,21 +585,26 @@ namespace XPC const std::size_t size = msg.GetSize(); char aircraftNumber = buffer[5]; - float gear = *((float*)(buffer + 42)); + float gear; double posd[3]; float orient[3]; if (size == 34) /* lat/lon/h as 32-bit float */ { - posd[0] = *((float*)&buffer[6]); - posd[1] = *((float*)&buffer[10]); - posd[2] = *((float*)&buffer[14]); + float posd_32[3]; + memcpy(posd_32, buffer + 6, 12); + /* convert float to double */ + posd[0] = posd_32[0]; + posd[1] = posd_32[1]; + posd[2] = posd_32[2]; memcpy(orient, buffer + 18, 12); + memcpy(&gear, buffer + 30, 4); } else if (size == 46) /* lat/lon/h as 64-bit double */ { - memcpy(posd, buffer + 6, 3*8); + memcpy(posd, buffer + 6, 24); memcpy(orient, buffer + 30, 12); + memcpy(&gear, buffer + 42, 4); } else { @@ -602,7 +612,6 @@ namespace XPC return; } - /* convert float to double */ DataManager::SetPosition(posd, aircraftNumber); DataManager::SetOrientation(orient, aircraftNumber); if (gear >= 0) @@ -623,6 +632,135 @@ namespace XPC } } + void MessageHandlers::HandlePosT(const Message& msg) + { + MessageHandlers::HandlePosi(msg); + + const unsigned char* buffer = msg.GetBuffer(); + char aircraftNumber = buffer[5]; + Log::FormatLine(LOG_TRACE, "POST", "Getting terrain information for aircraft %u", aircraftNumber); + + double pos[3]; + pos[0] = DataManager::GetDouble(DREF_Latitude, aircraftNumber); + pos[1] = DataManager::GetDouble(DREF_Longitude, aircraftNumber); + pos[2] = 0.0; + MessageHandlers::SendTerr(pos, aircraftNumber); + } + + void MessageHandlers::HandleGetT(const Message& msg) + { + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + if (size != 30) + { + Log::FormatLine(LOG_ERROR, "GETT", "Unexpected message length: %u", size); + return; + } + unsigned char aircraft = buffer[5]; + Log::FormatLine(LOG_TRACE, "GETT", "Getting terrain information for aircraft %u", aircraft); + + double pos[3]; + memcpy(pos, buffer + 6, 24); + + if(pos[0] == -998 || pos[1] == -998 || pos[2] == -998) + { + // get terrain properties at aircraft location + pos[0] = DataManager::GetDouble(DREF_Latitude, aircraft); + pos[1] = DataManager::GetDouble(DREF_Longitude, aircraft); + pos[2] = 0.0; + } + + MessageHandlers::SendTerr(pos, aircraft); + } + + void MessageHandlers::SendTerr(double pos[3], char aircraft) + { + double lat, lon, alt, X, Y, Z; + + // Init terrain probe (if required) and probe data struct + static XPLMProbeInfo_t probe_data; + probe_data.structSize = sizeof(XPLMProbeInfo_t); + + if(Terrain_probe == nullptr) + { + Log::FormatLine(LOG_TRACE, "TERR", "Create terrain probe for aircraft %u", aircraft); + Terrain_probe = XPLMCreateProbe(0); + } + + // terrain probe at specified location + // Follow the process in the following post to get accurate results + // https://forums.x-plane.org/index.php?/forums/topic/38688-how-do-i-use-xplmprobeterrainxyz/&page=2 + + // transform probe location to local coordinates + // Step 1. Convert lat/lon/0 to XYZ + XPLMWorldToLocal(pos[0], pos[1], pos[2], &X, &Y, &Z); + + // query probe + // Step 2. Probe XYZ to get a new Y + int rc = XPLMProbeTerrainXYZ(Terrain_probe, X, Y, Z, &probe_data); + if(rc > 0) + { + Log::FormatLine(LOG_ERROR, "TERR", "Probe failed. Return Value %u", rc); + XPLMDestroyProbe(Terrain_probe); + Terrain_probe = nullptr; + return; + } + + // transform probe location to world coordinates + // Step 3. Convert that new XYZ back to LLE + XPLMLocalToWorld(probe_data.locationX, probe_data.locationY, probe_data.locationZ, &lat, &lon, &alt); + Log::FormatLine(LOG_TRACE, "TERR", "Conv LLA=%f, %f, %f", lat, lon, alt); + + // transform probe location to local coordinates + // Step 4. NOW convert your original lat/lon with the elevation from step 3 to XYZ + XPLMWorldToLocal(pos[0], pos[1], alt, &X, &Y, &Z); + + // query probe + // Step 5. Re-probe with the NEW XYZ + rc = XPLMProbeTerrainXYZ(Terrain_probe, X, Y, Z, &probe_data); + if(rc == 0) + { + // transform probe location to world coordinates + // Step 6. You now have a new Y, and your XYZ will be closer to correct for high elevations far from the origin. + XPLMLocalToWorld(probe_data.locationX, probe_data.locationY, probe_data.locationZ, &lat, &lon, &alt); + + Log::FormatLine(LOG_TRACE, "TERR", "Probe LLA %lf %lf %lf", lat, lon, alt); + } + else + { + lat = -998; + lon = -998; + alt = -998; + + Log::FormatLine(LOG_TRACE, "TERR", "Probe failed. Return Value %u", rc); + } + + // keep probe for next query + // XPLMDestroyProbe(Terrain_probe); + + // Assemble response message + unsigned char response[62] = "TERR"; + response[5] = aircraft; + // terrain height over msl at lat/lon point + memcpy(response + 6, &lat, 8); + memcpy(response + 14, &lon, 8); + memcpy(response + 22, &alt, 8); + // terrain normal vector + memcpy(response + 30, &probe_data.normalX, 4); + memcpy(response + 34, &probe_data.normalY, 4); + memcpy(response + 38, &probe_data.normalZ, 4); + // terrain velocity + memcpy(response + 42, &probe_data.velocityX, 4); + memcpy(response + 46, &probe_data.velocityY, 4); + memcpy(response + 50, &probe_data.velocityZ, 4); + // terrain type + memcpy(response + 54, &probe_data.is_wet, 4); + // probe status + memcpy(response + 58, &rc, 4); + + sock->SendTo(response, 62, &connection.addr); + } + void MessageHandlers::HandleSimu(const Message& msg) { // Update log @@ -717,112 +855,92 @@ namespace XPC Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text set"); } } - + void MessageHandlers::HandleView(const Message& msg) { // Update Log Log::FormatLine(LOG_TRACE, "VIEW", "Message Received(Conn %i)", connection.id); - - int enable_camera_location = 0; + + bool enable_advanced_camera = false; const std::size_t size = msg.GetSize(); if (size == 9) { // default view switcher as before } - else if (size == 37) + else if (size == 49) { // Allow camera location control - enable_camera_location = 1; + enable_advanced_camera = true; } else { - Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9 or 37.", size); + Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9 or 49.", size); return; } + + // get msg data const unsigned char* buffer = msg.GetBuffer(); - int type = *((int*)(buffer + 5)); - XPLMCommandKeyStroke(type); - if(type == 79 && enable_camera_location == 1) // runway camera view + // get view type + int view_type; + memcpy(&view_type, buffer + 5, 4); + + // set view by calling the corresponding key stroke + XPLMCommandKeyStroke(view_type); + + + VIEW_TYPE viewRunway = VIEW_TYPE::XPC_VIEW_RUNWAY; + VIEW_TYPE viewChase = VIEW_TYPE::XPC_VIEW_CHASE; + + // advanced runway camera view + if(view_type == static_cast(viewRunway) && enable_advanced_camera == true) { - static struct CameraProperties campos; - - campos.loc[0] = *(double*)(buffer+9); - campos.loc[1] = *(double*)(buffer+17); - campos.loc[2] = *(double*)(buffer+25); - campos.direction[0] = -998; - campos.direction[1] = -998; - campos.direction[2] = -998; - campos.zoom = *(float*)(buffer+33); + static struct CameraProperties campos; // static variable for continuous callback access - Log::FormatLine(LOG_TRACE, "VIEW", "Cam pos %f %f %f zoom %f", campos.loc[0], campos.loc[1], campos.loc[2],campos.zoom); - - XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CamFunc, &campos); + memcpy(&campos, buffer+9 , sizeof(struct CameraProperties)); + + Log::FormatLine(LOG_TRACE, "VIEW", "Cam pos %f %f %f zoom %f", campos.loc[0], campos.loc[1], campos.loc[2], campos.zoom); + + XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CamCallback_RunwayCam, &campos); + } + // advanced chase camera view + else if(view_type == static_cast(viewChase) && enable_advanced_camera == true) + { + static struct CameraProperties campos; // static variable for continuous callback access + + memcpy(&campos, buffer+9 , sizeof(struct CameraProperties)); + + Log::FormatLine(LOG_TRACE, "VIEW", "Cam pos %f %f %f zoom %f", campos.loc[0], campos.loc[1], campos.loc[2], campos.zoom); + + XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CamCallback_ChaseCam, &campos); } } - - int MessageHandlers::CamFunc( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon) - { - if (outCameraPosition && !inIsLosingControl) - { - struct CameraProperties* campos = (struct CameraProperties*)inRefcon; - - // camera position - double clat = campos->loc[0]; - double clon = campos->loc[1]; - double calt = campos->loc[2]; - - double cX, cY, cZ; - XPLMWorldToLocal(clat, clon, calt, &cX, &cY, &cZ); - - outCameraPosition->x = cX; - outCameraPosition->y = cY; - outCameraPosition->z = cZ; - -// Log::FormatLine(LOG_TRACE, "CAM", "Cam pos %f %f %f", clat, clon, calt); - - if(campos->direction[0] == -998) // calculate camera direction - { - // aircraft position - double x = XPC::DataManager::GetDouble(XPC::DREF_LocalX, 0); - double y = XPC::DataManager::GetDouble(XPC::DREF_LocalY, 0); - double z = XPC::DataManager::GetDouble(XPC::DREF_LocalZ, 0); - - // relative position vector cam to plane - double dx = x - cX; - double dy = y - cY; - double dz = z - cZ; - -// Log::FormatLine(LOG_TRACE, "CAM", "Cam vect %f %f %f", dx, dy, dz); - - double pi = 3.141592653589793; - - // horizontal distance - double dist = sqrt(dx*dx + dz*dz); - - outCameraPosition->pitch = atan2(dy, dist) * 180.0/pi; - - double angle = atan2(dz, dx) * 180.0/pi; // rel to pos right (pos X) - - outCameraPosition->heading = 90 + angle; // rel to north - -// Log::FormatLine(LOG_TRACE, "CAM", "Cam p %f hdg %f ", outCameraPosition->pitch, outCameraPosition->heading); - - outCameraPosition->roll = 0; - } - else - { - outCameraPosition->roll = campos->direction[0]; - outCameraPosition->pitch = campos->direction[1]; - outCameraPosition->heading = campos->direction[2]; - } - - outCameraPosition->zoom = campos->zoom; - } - - return 1; - } + + void MessageHandlers::HandleComm(const Message& msg) + { + Log::FormatLine(LOG_TRACE, "COMM", "Request to execute COMM command received (Conn %i)", connection.id); + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + std::size_t pos = 5; + while (pos < size) + { + unsigned char len = buffer[pos++]; + if (pos + len > size) + { + break; + } + std::string comm = std::string((char*)buffer + pos, len); + pos += len; + + DataManager::Execute(comm); + Log::FormatLine(LOG_DEBUG, "COMM", "Execute command %s", comm.c_str()); + } + if (pos != size) + { + Log::WriteLine(LOG_ERROR, "COMM", "ERROR: Command did not terminate at the expected position."); + } + } void MessageHandlers::HandleWypt(const Message& msg) { diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h index a065c96..15379c4 100644 --- a/xpcPlugin/MessageHandlers.h +++ b/xpcPlugin/MessageHandlers.h @@ -14,6 +14,23 @@ namespace XPC { /// A function that handles a message. typedef void(*MessageHandler)(const Message&); + + enum class VIEW_TYPE + { + XPC_VIEW_FORWARDS = 73, + XPC_VIEW_DOWN, + XPC_VIEW_LEFT, + XPC_VIEW_RIGHT, + XPC_VIEW_BACK, + XPC_VIEW_TOWER, + XPC_VIEW_RUNWAY, + XPC_VIEW_CHASE, + XPC_VIEW_FOLLOW, + XPC_VIEW_FOLLOWWITHPANEL, + XPC_VIEW_SPOT, + XPC_VIEW_FULLSCREENWITHHUD, + XPC_VIEW_FULLSCREENNOHUD, + }; /// Handles incoming messages and manages connections. /// @@ -40,6 +57,8 @@ namespace XPC static void SetSocket(UDPSocket* socket); static void SendBeacon(const std::string& pluginVersion, unsigned short pluginReceivePort, int xplaneVersion); + + static void SendTerr(double pos[3], char aircraft); private: // One handler per message type. Message types are descripbed on the @@ -51,16 +70,20 @@ namespace XPC static void HandleGetC(const Message& msg); static void HandleGetD(const Message& msg); static void HandleGetP(const Message& msg); + static void HandleGetT(const Message& msg); static void HandlePosi(const Message& msg); + static void HandlePosT(const Message& msg); static void HandleSimu(const Message& msg); static void HandleText(const Message& msg); static void HandleWypt(const Message& msg); static void HandleView(const Message& msg); + static void HandleComm(const Message& msg); static void HandleXPlaneData(const Message& msg); static void HandleUnknown(const Message& msg); - static int CamFunc( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon); + static int CamCallback_RunwayCam( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon); + static int CamCallback_ChaseCam( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon); struct CameraProperties{ double loc[3]; diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index 6eaf3e1..5925ebe 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -56,22 +56,25 @@ namespace XPC return; } - // Set Timout - int usTimeOut = 500; - + // Set timeout period for SendTo to 1 millisecond + // Without this, playback may become choppy due to process blocking #ifdef _WIN32 - DWORD msTimeOutWin = 1; // Minimum socket timeout in Windows is 1ms - if(setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&msTimeOutWin, sizeof(msTimeOutWin)) != 0) + // Minimum socket timeout in Windows is 1 millisecond (0 makes it blocking) + DWORD timeout = 1; +#else + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; +#endif + if (setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { +#ifdef _WIN32 int err = WSAGetLastError(); Log::FormatLine(LOG_ERROR, tag, "ERROR: Failed to set timeout. (Error code %i)", err); - } #else - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = usTimeOut; - setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); + Log::WriteLine(LOG_ERROR, tag, "ERROR: Failed to set timeout."); #endif + } } UDPSocket::~UDPSocket() @@ -87,43 +90,54 @@ namespace XPC int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr) const { 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 + // For Read, use the select command - minimum timeout of 0 makes it polling. + // Without this, playback may become choppy due to process blocking // Definitions - FD_SET stReadFDS; - FD_SET stExceptFDS; - timeval tv; + fd_set stReadFDS; + fd_set stExceptFDS; + struct timeval timeout; // Setup for Select FD_ZERO(&stReadFDS); FD_SET(sock, &stReadFDS); FD_ZERO(&stExceptFDS); FD_SET(sock, &stExceptFDS); - tv.tv_sec = 0; - tv.tv_usec = 250; + + // Set timeout period for select to 0 to poll the socket + timeout.tv_sec = 0; + timeout.tv_usec = 0; // Select Command - int result = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv); - if (result == SOCKET_ERROR) + int status = select(sock+1, &stReadFDS, NULL, &stExceptFDS, &timeout); + if (status < 0) { +#ifdef _WIN32 int err = WSAGetLastError(); Log::FormatLine(LOG_ERROR, tag, "ERROR: Select failed. (Error code %i)", err); +#else + Log::WriteLine(LOG_ERROR, tag, "ERROR: Select failed."); +#endif + return -1; } - if (result <= 0) // No Data or error + if (status == 0) { + // No data return -1; } // If no error: Read Data status = recvfrom(sock, (char*)dst, maxLen, 0, recvAddr, &recvaddrlen); + if (status < 0) + { +#ifdef _WIN32 + int err = WSAGetLastError(); + Log::FormatLine(LOG_ERROR, tag, "ERROR: Receive failed. (Error code %i)", err); #else - // For apple or linux-just read - will timeout in 0.5 ms - status = (int)recvfrom(sock, dst, 5000, 0, recvAddr, &recvaddrlen); + Log::WriteLine(LOG_ERROR, tag, "ERROR: Receive failed."); #endif + } return status; } diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h index a1ee920..ea86930 100644 --- a/xpcPlugin/UDPSocket.h +++ b/xpcPlugin/UDPSocket.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #endif diff --git a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj index 4a4fe23..6af7a97 100755 --- a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj +++ b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 3D0F44CE21C6D3E7008A0655 /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */; }; + 5B36040D23731E4A003ACE12 /* CameraCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B36040C23731E4A003ACE12 /* CameraCallbacks.cpp */; }; BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */; }; BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; }; BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */; }; @@ -39,6 +40,7 @@ /* Begin PBXFileReference section */ 3D0F44CC21C6D3E7008A0655 /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = ""; }; 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = ""; }; + 5B36040C23731E4A003ACE12 /* CameraCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CameraCallbacks.cpp; sourceTree = ""; }; BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XPCPlugin.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataManager.cpp; sourceTree = ""; }; BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = ""; }; @@ -53,7 +55,6 @@ BEABAD3D1AE0498D007BA7DA /* UDPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UDPSocket.cpp; sourceTree = ""; }; BEABAD3E1AE0498D007BA7DA /* UDPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UDPSocket.h; sourceTree = ""; }; BEDC620218EDF1A7005DB364 /* xplaneConnect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xplaneConnect.c; path = ../C/src/xplaneConnect.c; sourceTree = ""; }; - BEDC620318EDF1A7005DB364 /* xplaneConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xplaneConnect.h; path = ../C/src/xplaneConnect.h; sourceTree = ""; }; D607B19909A556E400699BC3 /* mac.xpl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = mac.xpl; sourceTree = BUILT_PRODUCTS_DIR; }; D6A7BDA916A1DEA200D1426A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; D6A7BDC016A1DEC000D1426A /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; @@ -79,6 +80,7 @@ AC4E46B809C2E0B3006B7E1B /* src */ = { isa = PBXGroup; children = ( + 5B36040C23731E4A003ACE12 /* CameraCallbacks.cpp */, 3D0F44CD21C6D3E7008A0655 /* Timer.cpp */, BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */, BEDC620218EDF1A7005DB364 /* xplaneConnect.c */, @@ -96,7 +98,6 @@ isa = PBXGroup; children = ( 3D0F44CC21C6D3E7008A0655 /* Timer.h */, - BEDC620318EDF1A7005DB364 /* xplaneConnect.h */, BEABAD2C1AE041A3007BA7DA /* DataManager.h */, BEABAD301AE041A3007BA7DA /* Drawing.h */, BEABAD321AE041A3007BA7DA /* Log.h */, @@ -191,6 +192,7 @@ files = ( BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */, BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */, + 5B36040D23731E4A003ACE12 /* CameraCallbacks.cpp in Sources */, BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */, BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */, BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */, @@ -219,6 +221,7 @@ "APL=1", "IBM=0", "LIN=0", + XPLM200, ); GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_VERSION = ""; @@ -230,6 +233,10 @@ MACH_O_TYPE = mh_bundle; MACOSX_DEPLOYMENT_TARGET = 10.7; ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DXPLM200", + ); OTHER_LDFLAGS = ( "$(OTHER_LDFLAGS)", "-Wl,-exported_symbol", @@ -268,6 +275,7 @@ "APL=1", "IBM=0", "LIN=0", + XPLM200, ); GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_VERSION = ""; @@ -278,6 +286,10 @@ ); MACH_O_TYPE = mh_bundle; MACOSX_DEPLOYMENT_TARGET = 10.7; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DXPLM200", + ); OTHER_LDFLAGS = ( "$(OTHER_LDFLAGS)", "-Wl,-exported_symbol", diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index 2c2c85b..a8de464 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -1,231 +1,232 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {6AEF5D28-2701-44FF-AE10-1DEF07C5CAEA} - Win32Proj - xpcPlugin - 8.1 - - - - DynamicLibrary - true - $(DefaultPlatformToolset) - MultiByte - - - DynamicLibrary - true - $(DefaultPlatformToolset) - MultiByte - - - DynamicLibrary - true - $(DefaultPlatformToolset) - MultiByte - - - DynamicLibrary - true - $(DefaultPlatformToolset) - MultiByte - - - - - - - - - - - - - - - - - - - true - ..\XPlaneConnect\ - .xpl - ..\SDK\CHeaders\XPLM;$(IncludePath) - ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) - win - - - true - ..\XPlaneConnect\ - .xpl - ..\SDK\CHeaders\XPLM;$(IncludePath) - ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) - win - - - .xpl - ..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath) - ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) - win - true - ..\XPlaneConnect\64\ - - - .xpl - ..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath) - ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) - win - true - ..\XPlaneConnect\64\ - - - - NotUsing - Level3 - Disabled - IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - OldStyle - true - - false - - - false - MultiThreadedDebug - - - Windows - true - Opengl32.lib;%(AdditionalDependencies) - - - - - NotUsing - Level3 - Full - IBM=1;XPLM200;%(PreprocessorDefinitions) - OldStyle - true - - - false - - - false - MultiThreaded - Default - true - false - - - Windows - false - Opengl32.lib;%(AdditionalDependencies) - UseLinkTimeCodeGeneration - true - - - - - NotUsing - Level3 - Disabled - IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - OldStyle - true - - - false - 4996 - false - MultiThreadedDebug - - - Windows - true - OpenGL32.lib;%(AdditionalDependencies) - - - - - NotUsing - Level3 - Full - IBM=1;XPLM200;%(PreprocessorDefinitions) - - - OldStyle - true - - - false - - - false - MultiThreaded - Default - true - - false - - - Windows - false - OpenGL32.lib;%(AdditionalDependencies) - UseLinkTimeCodeGeneration - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {6AEF5D28-2701-44FF-AE10-1DEF07C5CAEA} + Win32Proj + xpcPlugin + 10.0.17134.0 + + + + DynamicLibrary + true + $(DefaultPlatformToolset) + MultiByte + + + DynamicLibrary + true + $(DefaultPlatformToolset) + MultiByte + + + DynamicLibrary + true + $(DefaultPlatformToolset) + MultiByte + + + DynamicLibrary + true + $(DefaultPlatformToolset) + MultiByte + + + + + + + + + + + + + + + + + + + true + ..\XPlaneConnect\ + .xpl + ..\SDK\CHeaders\XPLM;$(IncludePath) + ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) + win + + + true + ..\XPlaneConnect\ + .xpl + ..\SDK\CHeaders\XPLM;$(IncludePath) + ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) + win + + + .xpl + ..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath) + ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) + win + true + ..\XPlaneConnect\64\ + + + .xpl + ..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath) + ..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath) + win + true + ..\XPlaneConnect\64\ + + + + NotUsing + Level3 + Disabled + IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + OldStyle + true + + false + + + false + MultiThreadedDebug + + + Windows + true + Opengl32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Full + IBM=1;XPLM200;%(PreprocessorDefinitions) + OldStyle + true + + + false + + + false + MultiThreaded + Default + true + false + + + Windows + false + Opengl32.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration + true + + + + + NotUsing + Level3 + Disabled + IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + OldStyle + true + + + false + 4996 + false + MultiThreadedDebug + + + Windows + true + OpenGL32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Full + IBM=1;XPLM200;%(PreprocessorDefinitions) + + + OldStyle + true + + + false + + + false + MultiThreaded + Default + true + + false + + + Windows + false + OpenGL32.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters index e62f3d6..5b9a631 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters @@ -1,74 +1,83 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + \ No newline at end of file