1
.gitignore
vendored
1
.gitignore
vendored
@@ -93,3 +93,4 @@ out/
|
||||
# Internal Docs #
|
||||
#################
|
||||
Docs/~$Capability Matrix.xlsx
|
||||
*.xpl
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <sys/time.h>
|
||||
#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()
|
||||
{
|
||||
@@ -70,7 +71,7 @@ int main(void)
|
||||
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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ****/
|
||||
/*****************************************************************************/
|
||||
@@ -146,10 +146,10 @@ int sendDATA(XPCSocket sock, float data[][9], int rows);
|
||||
/// 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 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 value[], int size);
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 = "<B{0:d}s".format(len(comm))
|
||||
buffer += struct.pack(fmt, len(comm), comm)
|
||||
|
||||
# Send
|
||||
self.sendUDP(buffer)
|
||||
|
||||
class ViewType(object):
|
||||
Forwards = 73
|
||||
Down = 74
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import socket
|
||||
import struct
|
||||
|
||||
|
||||
class XPlaneConnect(object):
|
||||
"""XPlaneConnect (XPC) facilitates communication to and from the XPCPlugin."""
|
||||
socket = None
|
||||
@@ -121,7 +120,7 @@ class XPlaneConnect(object):
|
||||
buffer = self.readUDP()
|
||||
if len(buffer) < 6:
|
||||
return None
|
||||
rows = (len(buffer) - 5) / 36
|
||||
rows = (len(buffer) - 5) // 36
|
||||
data = []
|
||||
for i in range(rows):
|
||||
data.append(struct.unpack_from(b"9f", buffer, 5 + 36*i))
|
||||
@@ -158,10 +157,13 @@ class XPlaneConnect(object):
|
||||
|
||||
# Read response
|
||||
resultBuf = self.readUDP()
|
||||
if len(resultBuf) != 34:
|
||||
if len(resultBuf) == 34:
|
||||
result = struct.unpack(b"<4sxBfffffff", resultBuf)
|
||||
elif len(resultBuf) == 46:
|
||||
result = struct.unpack(b"<4sxBdddffff", resultBuf)
|
||||
else:
|
||||
raise ValueError("Unexpected response length.")
|
||||
|
||||
result = struct.unpack(b"<4sxBfffffff", resultBuf)
|
||||
if result[0] != b"POSI":
|
||||
raise ValueError("Unexpected header: " + result[0])
|
||||
|
||||
@@ -197,6 +199,9 @@ class XPlaneConnect(object):
|
||||
val = -998
|
||||
if i < len(values):
|
||||
val = values[i]
|
||||
if i < 3:
|
||||
buffer += struct.pack(b"<d", val)
|
||||
else:
|
||||
buffer += struct.pack(b"<f", val)
|
||||
|
||||
# Send
|
||||
@@ -257,7 +262,7 @@ class XPlaneConnect(object):
|
||||
val = values[i]
|
||||
if i == 4:
|
||||
val = -1 if (abs(val + 998) < 1e-4) else val
|
||||
buffer += struct.pack(b"b", val)
|
||||
buffer += struct.pack(b"b", int(val))
|
||||
else:
|
||||
buffer += struct.pack(b"<f", val)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Test.h"
|
||||
#include "xplaneConnect.h"
|
||||
|
||||
int doCTRLTest(XPCSocket *sock, char* drefs[7], float values[], int size, int ac, float expected[7])
|
||||
int doCTRLTest(XPCSocket *sock, const char* drefs[7], float values[], int size, int ac, float expected[7])
|
||||
{
|
||||
float* data[7];
|
||||
int sizes[7];
|
||||
@@ -66,7 +66,7 @@ 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 };
|
||||
@@ -117,7 +117,7 @@ int basicCTRLTest(char** drefs, int ac)
|
||||
|
||||
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",
|
||||
|
||||
@@ -10,7 +10,7 @@ int testDATA()
|
||||
{
|
||||
// Initialize
|
||||
int i, j; // Iterator
|
||||
char* drefs[100] =
|
||||
const char* drefs[100] =
|
||||
{
|
||||
"sim/aircraft/parts/acf_gear_deploy"
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
@@ -115,7 +115,7 @@ int basicPOSITest(char** drefs, int ac)
|
||||
|
||||
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",
|
||||
|
||||
434
TestScripts/Python3 Tests/Tests.py
Normal file
434
TestScripts/Python3 Tests/Tests.py
Normal file
@@ -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()
|
||||
45
TestScripts/Python3 Tests/Tests.pyproj
Normal file
45
TestScripts/Python3 Tests/Tests.pyproj
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>6931ebb2-4e01-4c5a-86b6-668c0e75051b</ProjectGuid>
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>Tests.py</StartupFile>
|
||||
<SearchPath>..\..\Python\src\</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>Tests</Name>
|
||||
<RootNamespace>Tests</RootNamespace>
|
||||
<InterpreterId>{9a7a9026-48c1-4688-9d5d-e5699d47d074}</InterpreterId>
|
||||
<InterpreterVersion>2.7</InterpreterVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Tests.py" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\2.7" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<PtvsTargetsFile>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>
|
||||
</PropertyGroup>
|
||||
<Import Condition="Exists($(PtvsTargetsFile))" Project="$(PtvsTargetsFile)" />
|
||||
<Import Condition="!Exists($(PtvsTargetsFile))" Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
<!--<Target Name="CoreCompile" />-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -10,7 +10,7 @@ trigger:
|
||||
- ms-*
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
- "*"
|
||||
# pr:
|
||||
# - '*'
|
||||
variables:
|
||||
@@ -27,58 +27,57 @@ 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'
|
||||
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
|
||||
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)'
|
||||
artifactName: "$(xpcArtifactNameMac)"
|
||||
targetPath: "$(xpcBuildOutputPath)"
|
||||
|
||||
- job: Linux
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
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'
|
||||
displayName: "apt install"
|
||||
|
||||
- script: |
|
||||
cd xpcPlugin
|
||||
cmake .
|
||||
make
|
||||
displayName: 'make'
|
||||
displayName: "make"
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'xpc-linux'
|
||||
targetPath: '$(xpcBuildOutputPath)'
|
||||
artifactName: "xpc-linux"
|
||||
targetPath: "$(xpcBuildOutputPath)"
|
||||
|
||||
- job: Windows
|
||||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
vmImage: "vs2017-win2016"
|
||||
|
||||
variables:
|
||||
solution: 'xpcPlugin/xpcPlugin/xpcPlugin.sln'
|
||||
buildPlatform: 'Win32|x64'
|
||||
buildConfiguration: 'Release'
|
||||
solution: "xpcPlugin/xpcPlugin/xpcPlugin.sln"
|
||||
buildPlatform: "Win32|x64"
|
||||
buildConfiguration: "Release"
|
||||
appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\'
|
||||
|
||||
steps:
|
||||
@@ -86,25 +85,25 @@ stages:
|
||||
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
restoreSolution: '$(solution)'
|
||||
restoreSolution: "$(solution)"
|
||||
|
||||
- task: VSBuild@1
|
||||
inputs:
|
||||
platform: 'Win32'
|
||||
solution: '$(solution)'
|
||||
configuration: '$(buildConfiguration)'
|
||||
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)'
|
||||
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'
|
||||
targetType: "inline"
|
||||
script: |
|
||||
rm $(xpcBuildOutputPath)/win.exp
|
||||
rm $(xpcBuildOutputPath)/win.lib
|
||||
@@ -113,54 +112,54 @@ stages:
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: '$(xpcArtifactNameWindows)'
|
||||
targetPath: '$(xpcBuildOutputPath)'
|
||||
artifactName: "$(xpcArtifactNameWindows)"
|
||||
targetPath: "$(xpcBuildOutputPath)"
|
||||
|
||||
- stage: Deploy
|
||||
jobs:
|
||||
- job: Package_Binaries
|
||||
displayName: 'Package Binaries'
|
||||
displayName: "Package Binaries"
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: '$(xpcArtifactNameMac)'
|
||||
artifactName: "$(xpcArtifactNameMac)"
|
||||
targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: '$(xpcArtifactNameLinux)'
|
||||
artifactName: "$(xpcArtifactNameLinux)"
|
||||
targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
|
||||
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: '$(xpcArtifactNameWindows)'
|
||||
artifactName: "$(xpcArtifactNameWindows)"
|
||||
targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: '$(xpcPackageName)'
|
||||
artifactName: "$(xpcPackageName)"
|
||||
targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
|
||||
|
||||
# Archive Files
|
||||
- task: ArchiveFiles@2
|
||||
inputs:
|
||||
rootFolderOrFile: '$(System.DefaultWorkingDirectory)/$(xpcPackageName)'
|
||||
rootFolderOrFile: "$(System.DefaultWorkingDirectory)/$(xpcPackageName)"
|
||||
includeRootFolder: true
|
||||
archiveType: 'zip'
|
||||
archiveFile: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip'
|
||||
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'
|
||||
gitHubConnection: "$(xpcGitHubConnection)"
|
||||
repositoryName: "$(Build.Repository.Name)"
|
||||
action: "create"
|
||||
target: "$(Build.SourceVersion)"
|
||||
tagSource: "auto"
|
||||
tag: $(tagName)
|
||||
assets: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip'
|
||||
assets: "$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip"
|
||||
isPreRelease: true
|
||||
isDraft: true
|
||||
|
||||
173
xpcPlugin/CameraCallbacks.cpp
Normal file
173
xpcPlugin/CameraCallbacks.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// support.cpp
|
||||
// xpcPlugin
|
||||
//
|
||||
// Created by Kai Lehmkuehler on 14/01/2019.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "MessageHandlers.h"
|
||||
#include "DataManager.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "XPLMUtilities.h"
|
||||
#include "XPLMGraphics.h"
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "XPLMDataAccess.h"
|
||||
#include "XPLMGraphics.h"
|
||||
#include "XPLMUtilities.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
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 ";
|
||||
|
||||
@@ -14,15 +14,16 @@
|
||||
// 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 <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
@@ -30,7 +31,6 @@
|
||||
#define MULTICAST_GROUP "239.255.1.1"
|
||||
#define MULITCAST_PORT 49710
|
||||
|
||||
|
||||
namespace XPC
|
||||
{
|
||||
std::map<std::string, MessageHandlers::ConnectionInfo> MessageHandlers::connections;
|
||||
@@ -42,6 +42,9 @@ namespace XPC
|
||||
|
||||
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)
|
||||
{
|
||||
Log::WriteLine(LOG_TRACE, "MSGH", "Setting 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));
|
||||
@@ -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
|
||||
@@ -723,105 +861,85 @@ namespace XPC
|
||||
// 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<int>(viewRunway) && enable_advanced_camera == true)
|
||||
{
|
||||
static struct CameraProperties campos;
|
||||
static struct CameraProperties campos; // static variable for continuous callback access
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CamCallback_RunwayCam, &campos);
|
||||
}
|
||||
// advanced chase camera view
|
||||
else if(view_type == static_cast<int>(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)
|
||||
void MessageHandlers::HandleComm(const Message& msg)
|
||||
{
|
||||
if (outCameraPosition && !inIsLosingControl)
|
||||
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)
|
||||
{
|
||||
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
|
||||
unsigned char len = buffer[pos++];
|
||||
if (pos + len > size)
|
||||
{
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
else
|
||||
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)
|
||||
{
|
||||
outCameraPosition->roll = campos->direction[0];
|
||||
outCameraPosition->pitch = campos->direction[1];
|
||||
outCameraPosition->heading = campos->direction[2];
|
||||
Log::WriteLine(LOG_ERROR, "COMM", "ERROR: Command did not terminate at the expected position.");
|
||||
}
|
||||
|
||||
outCameraPosition->zoom = campos->zoom;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MessageHandlers::HandleWypt(const Message& msg)
|
||||
|
||||
@@ -15,6 +15,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.
|
||||
///
|
||||
/// \author Jason Watkins
|
||||
@@ -41,6 +58,8 @@ namespace XPC
|
||||
|
||||
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
|
||||
// wiki at https://github.com/nasa/XPlaneConnect/wiki/Network-Information
|
||||
@@ -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];
|
||||
|
||||
@@ -56,23 +56,26 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
3D0F44CD21C6D3E7008A0655 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = "<group>"; };
|
||||
5B36040C23731E4A003ACE12 /* CameraCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CameraCallbacks.cpp; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = "<group>"; };
|
||||
@@ -53,7 +55,6 @@
|
||||
BEABAD3D1AE0498D007BA7DA /* UDPSocket.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UDPSocket.cpp; sourceTree = "<group>"; };
|
||||
BEABAD3E1AE0498D007BA7DA /* UDPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UDPSocket.h; sourceTree = "<group>"; };
|
||||
BEDC620218EDF1A7005DB364 /* xplaneConnect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xplaneConnect.c; path = ../C/src/xplaneConnect.c; sourceTree = "<group>"; };
|
||||
BEDC620318EDF1A7005DB364 /* xplaneConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xplaneConnect.h; path = ../C/src/xplaneConnect.h; sourceTree = "<group>"; };
|
||||
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",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<ProjectGuid>{6AEF5D28-2701-44FF-AE10-1DEF07C5CAEA}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>xpcPlugin</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@@ -210,6 +210,7 @@
|
||||
<ClInclude Include="..\UDPSocket.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\CameraCallbacks.cpp" />
|
||||
<ClCompile Include="..\DataManager.cpp" />
|
||||
<ClCompile Include="..\Drawing.cpp" />
|
||||
<ClCompile Include="..\Log.cpp" />
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
<ClInclude Include="..\MessageHandlers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\XPCPlugin.cpp">
|
||||
@@ -56,6 +59,12 @@
|
||||
<ClCompile Include="..\MessageHandlers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\CameraCallbacks.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="..\SDK\Libraries\Win\XPLM.lib">
|
||||
|
||||
Reference in New Issue
Block a user