diff --git a/.gitignore b/.gitignore index e1c7a44..2575f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.exe *.o *.so +*.pyc # Logs and databases # ###################### diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index ce65c76..e4e633e 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -52,7 +52,6 @@ int sendUDP(XPCSocket sock, char buffer[], int len); int readUDP(XPCSocket sock, char buffer[], int len); int sendDREFRequest(XPCSocket sock, const char* drefs[], unsigned char count); int getDREFResponse(XPCSocket sock, float* values[], unsigned char count, int sizes[]); - void printError(char *functionName, char *format, ...) { va_list args; @@ -266,9 +265,17 @@ int setCONN(XPCSocket* sock, unsigned short port) int pauseSim(XPCSocket sock, char pause) { + // Validte input + if (pause < 0 || pause > 2) + { + printError("pauseSim", "Invalid argument: %i", pause); + return -2; + } + // Setup command char buffer[6] = "SIMU"; - buffer[5] = pause == 0 ? 0 : 1; + buffer[5] = pause; + // Send command if (sendUDP(sock, buffer, 6) < 0) { @@ -360,32 +367,46 @@ int readDATA(XPCSocket sock, float data[][9], int rows) /*****************************************************************************/ int sendDREF(XPCSocket sock, const char* dref, float value[], int size) { - // Setup command - // 5 byte header + max 255 char dref name + max 255 values * 4 bytes per value = 1279 - unsigned char buffer[1279] = "DREF"; - int drefLen = strnlen(dref, 256); - if (drefLen > 255) - { - printError("setDREF", "dref length is too long. Must be less than 256 characters."); - return -1; - } - if (size > 255) - { - printError("setDREF", "size is too big. Must be less than 256."); - return -2; - } - int len = 7 + drefLen + size * sizeof(float); - - // Copy dref to buffer - buffer[5] = (unsigned char)drefLen; - memcpy(buffer + 6, dref, drefLen); + return sendDREFs(sock, &dref, &value, &size, 1); +} - // Copy values to buffer - buffer[6 + drefLen] = (unsigned char)size; - memcpy(buffer + 7 + drefLen, value, size * sizeof(float)); +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"; + int pos = 5; + for (int i = 0; i < count; ++i) + { + int drefLen = strnlen(drefs[i], 256); + if (pos + drefLen + sizes[i] * 4 + 2 > 65536) + { + printError("sendDREF", "About to overrun the send buffer!"); + return -4; + } + if (drefLen > 255) + { + printError("sendDREF", "dref %d is too long. Must be less than 256 characters.", i); + return -1; + } + if (sizes[i] > 255) + { + printError("sendDREF", "size %d is too big. Must be less than 256.", i); + return -2; + } + // Copy dref to buffer + buffer[pos++] = (unsigned char)drefLen; + memcpy(buffer + pos, drefs[i], drefLen); + pos += drefLen; + + // Copy values to buffer + buffer[pos++] = (unsigned char)sizes[i]; + memcpy(buffer + pos, values[i], sizes[i] * sizeof(float)); + pos += sizes[i] * sizeof(float); + } // Send command - if (sendUDP(sock, buffer, len) < 0) + if (sendUDP(sock, buffer, pos) < 0) { printError("setDREF", "Failed to send command"); return -3; @@ -554,13 +575,13 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac) } if (size < 1 || size > 7) { - printError("sendCTRL", "size should be a value between 1 and 6."); + printError("sendCTRL", "size should be a value between 1 and 7."); return -2; } // Setup Command // 5 byte header + 5 float values * 4 + 2 byte values - unsigned char buffer[27] = "CTRL"; + unsigned char buffer[31] = "CTRL"; int cur = 5; for (int i = 0; i < 6; i++) { @@ -581,9 +602,10 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac) } } buffer[26] = ac; + *((float*)(buffer + 27)) = size == 7 ? values[6]: -998; // Send Command - if (sendUDP(sock, buffer, 27) < 0) + if (sendUDP(sock, buffer, 31) < 0) { printError("sendCTRL", "Failed to send command"); return -3; @@ -663,7 +685,7 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count) memcpy(buffer + 7, points, ptLen); // Send Command - if (sendUDP(sock, buffer, 40) < 0) + if (sendUDP(sock, buffer, 7 + 12 * count) < 0) { printError("sendWYPT", "Failed to send command"); return -2; @@ -673,3 +695,31 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count) /*****************************************************************************/ /**** End Drawing functions ****/ /*****************************************************************************/ + +/*****************************************************************************/ +/**** View functions ****/ +/*****************************************************************************/ +int sendVIEW(XPCSocket sock, VIEW_TYPE view) +{ + // Validate Input + if (view < XPC_VIEW_FORWARDS || view > XPC_VIEW_FULLSCREENNOHUD) + { + printError("sendVIEW", "Unrecognized view"); + return -1; + } + + // Setup Command + char buffer[9] = "VIEW"; + *((int*)(buffer + 5)) = view; + + // Send Command + if (sendUDP(sock, buffer, 9) < 0) + { + printError("sendVIEW", "Failed to send command"); + return -2; + } + return 0; +} +/*****************************************************************************/ +/**** End View functions ****/ +/*****************************************************************************/ \ No newline at end of file diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h index e8520c3..3827a68 100644 --- a/C/src/xplaneConnect.h +++ b/C/src/xplaneConnect.h @@ -61,6 +61,23 @@ typedef enum XPC_WYPT_DEL = 2, XPC_WYPT_CLR = 3 } WYPT_OP; + +typedef enum +{ + 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, +} VIEW_TYPE; // Low Level UDP Functions @@ -127,13 +144,27 @@ int sendDATA(XPCSocket sock, float data[][9], int rows); /// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match /// the size given on that page. XPC currently sends all values as floats regardless of /// the type described on the wiki. This doesn't cause any data loss for most datarefs. -/// \param sock The socket to use to send the command. -/// \param dref The name of the dataref to set. -/// \param 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. +/// \param sock The socket to use to send the command. +/// \param dref The name of the dataref to set. +/// \param value An array of values representing the data to set. +/// \param size The number of elements in values. +/// \returns 0 if successful, otherwise a negative value. int sendDREF(XPCSocket sock, const char* dref, float value[], int size); +/// Sets the specified datarefs to the specified values. +/// +/// \details dref names and their associated data types can be found on the XPSDK wiki at +/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match +/// the size given on that page. XPC currently sends all values as floats regardless of +/// the type described on the wiki. This doesn't cause any data loss for most datarefs. +/// \param sock The socket to use to send the command. +/// \param drefs The names of the datarefs to set. +/// \param values A multidimensional 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. +int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], int count); + /// Gets the value of the specified dataref. /// /// \details dref names and their associated data types can be found on the XPSDK wiki at @@ -182,8 +213,8 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac); /// /// \param sock The socket to use to send the command. /// \param values An array representing position data about the aircraft. The format of values is -/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are -/// specified, the unspecified values will be left unchanged. +/// [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. /// \returns 0 if successful, otherwise a negative value. @@ -200,6 +231,13 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac); /// \returns 0 if successful, otherwise a negative value. int sendTEXT(XPCSocket sock, char* msg, int x, int y); +/// Sets the camera view in X-Plane. +/// +/// \param sock The socket to use to send the command. +/// \param view The view to use. +/// \returns 0 if successful, otherwise a negative value. +int sendVIEW(XPCSocket sock, VIEW_TYPE view); + /// Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored /// and all points are removed. /// diff --git a/C/xpcExample/xpcExample/src/main.cpp b/C/xpcExample/xpcExample/src/main.cpp index 17b2a81..5792368 100644 --- a/C/xpcExample/xpcExample/src/main.cpp +++ b/C/xpcExample/xpcExample/src/main.cpp @@ -23,6 +23,13 @@ int main() // Open Socket const char* IP = "127.0.0.1"; //IP Address of computer running X-Plane XPCSocket sock = openUDP(IP); + float tVal[1]; + int tSize; + if (getDREF(sock, "sim/test/test_float", tVal, &tSize) < 0) + { + printf("Error establishing connecting. Unable to read data from X-Plane."); + return EXIT_FAILURE; + } // Set Location/Orientation (sendPOSI) // Set Up Position Array diff --git a/Docs/Capability Matrix.xlsx b/Docs/Capability Matrix.xlsx deleted file mode 100644 index 4809af9..0000000 Binary files a/Docs/Capability Matrix.xlsx and /dev/null differ diff --git a/Docs/Interface Control Document.xlsx b/Docs/Interface Control Document.xlsx deleted file mode 100644 index c989b60..0000000 Binary files a/Docs/Interface Control Document.xlsx and /dev/null differ diff --git a/Docs/PBS.pdf b/Docs/PBS.pdf deleted file mode 100644 index cc5ad05..0000000 Binary files a/Docs/PBS.pdf and /dev/null differ diff --git a/Java/Examples/BasicOperation/src/Main.java b/Java/Examples/BasicOperation/src/Main.java index 1ab5bcc..dc82c9f 100644 --- a/Java/Examples/BasicOperation/src/Main.java +++ b/Java/Examples/BasicOperation/src/Main.java @@ -21,8 +21,8 @@ public class Main System.out.println("Setting up simulation..."); try(XPlaneConnect xpc = new XPlaneConnect()) { - System.out.println("Setting inbound port"); - xpc.setCONN(49055); + // Ensure connection established. + xpc.getDREF("sim/test/test_float"); System.out.println("Setting player aircraft position"); float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1}; diff --git a/Java/src/ViewType.java b/Java/src/ViewType.java new file mode 100644 index 0000000..2f24c3d --- /dev/null +++ b/Java/src/ViewType.java @@ -0,0 +1,60 @@ +//NOTICES: +// Copyright ã 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +// +// DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE +// SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT +// SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO +// THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY +// GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, +// SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. +// FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY +// SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, +// EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR +// RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD +// HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY +// PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER +// SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. +package gov.nasa.xpc; + +/** + * Represents a camera view in X-Plane + * + * @author Jason Watkins + * @version 1.1 + * @since 2015-05-08 + */ +public enum ViewType +{ + Forwards(73), + Down(74), + Left(75), + Right(76), + Back(77), + Tower(78), + Runway(79), + Chase(80), + Follow(81), + FollowWithPanel(82), + Spot(83), + FullscreenWithHud(84), + FullscreenNoHud(85); + + private final int value; + private ViewType(int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } +} diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index 245e7c8..ba545d5 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -175,7 +175,7 @@ public class XPlaneConnect implements AutoCloseable */ private byte[] readUDP() throws IOException { - byte[] buffer = new byte[2048]; + byte[] buffer = new byte[65536]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); try { @@ -214,6 +214,26 @@ public class XPlaneConnect implements AutoCloseable sendUDP(msg); } + /** + * Pauses, unpauses, or switches the pause state of X-Plane. + * + * @param pause {@code 1} to pause the simulator, {@code 0} to unpause, or {@code 2} to switch. + * @throws IllegalArgumentException If the values of {@code pause} is not a valid command. + * @throws IOException If the command cannot be sent. + */ + public void pauseSim(int pause) throws IOException + { + if(pause < 0 || pause > 2) + { + throw new IllegalArgumentException("pause must be a value in the range [0, 2]."); + } + + // S I M U LEN VAL + byte[] msg = {0x53, 0x49, 0x4D, 0x55, 0x00, 0x00}; + msg[5] = (byte)pause; + sendUDP(msg); + } + /** * Requests a single dref value from X-Plane. * @@ -273,37 +293,29 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); //Read response - for(int i = 0; i < 40; ++i) + byte[] data = readUDP(); + if(data.length == 0) { - byte[] data = readUDP(); - if(data.length == 0) - { - continue; - } - if(data.length < 6) - { - throw new Error("Response too short"); //TODO: Make custom error type - } - if(data[5] != drefs.length) - { - throw new Error("Unexpected response length"); //TODO: Make custom error type - } - float[][] result = new float[drefs.length][]; - ByteBuffer bb = ByteBuffer.wrap(data); - bb.order(ByteOrder.LITTLE_ENDIAN); - int cur = 6; - for(int j = 0; j < result.length; ++j) - { - result[j] = new float[data[cur++]]; - for(int k = 0; k < result[j].length; ++k) //TODO: There must be a better way to do this - { - result[j][k] = bb.getFloat(cur); - cur += 4; - } - } - return result; + throw new IOException("No response received."); } - throw new IOException("No response received."); + if(data.length < 6) + { + throw new IOException("Response too short"); + } + float[][] result = new float[drefs.length][]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.order(ByteOrder.LITTLE_ENDIAN); + int cur = 6; + for(int j = 0; j < result.length; ++j) + { + result[j] = new float[data[cur++]]; + for(int k = 0; k < result[j].length; ++k) //TODO: There must be a better way to do this + { + result[j][k] = bb.getFloat(cur); + cur += 4; + } + } + return result; } public void sendDREF(String dref, float value) throws IOException @@ -319,43 +331,70 @@ public class XPlaneConnect implements AutoCloseable * @throws IOException If the command cannot be sent. */ public void sendDREF(String dref, float[] value) throws IOException + { + sendDREFs(new String[] {dref}, new float[][] {value}); + } + + /** + * Sends a command to X-Plane that sets the given DREF. + * + * @param drefs The names of the X-Plane datarefs to set. + * @param values A sequence of arrays of floating point values whose structure depends on the drefs specified. + * @throws IOException If the command cannot be sent. + */ + public void sendDREFs(String[] drefs, float[][] values) throws IOException { //Preconditions - if(dref == null) + if(drefs == null || drefs.length == 0) { - throw new IllegalArgumentException("dref must be a valid string."); + throw new IllegalArgumentException(("drefs must be non-empty.")); } - if(value == null || value.length == 0) + if(values == null || values.length != drefs.length) { - throw new IllegalArgumentException("value must be non-null and should contain at least one value."); + throw new IllegalArgumentException("values must be of the same size as drefs."); } - //Convert drefs to bytes. - byte[] drefBytes = dref.getBytes(StandardCharsets.UTF_8); - if(drefBytes.length == 0) - { - throw new IllegalArgumentException("DREF is an empty string!"); - } - if(drefBytes.length > 255) - { - throw new IllegalArgumentException("dref must be less than 255 bytes in UTF-8. Are you sure this is a valid dref?"); - } - - ByteBuffer bb = ByteBuffer.allocate(4 * value.length); - bb.order(ByteOrder.LITTLE_ENDIAN); - for(int i = 0; i < value.length; ++i) - { - bb.putFloat(i * 4, value[i]); - } - - //Build and send message ByteArrayOutputStream os = new ByteArrayOutputStream(); os.write("DREF".getBytes(StandardCharsets.UTF_8)); os.write(0xFF); //Placeholder for message length - os.write(drefBytes.length); - os.write(drefBytes, 0, drefBytes.length); - os.write(value.length); - os.write(bb.array()); + for(int i = 0; i < drefs.length; ++i) + { + String dref = drefs[i]; + float[] value = values[i]; + + if (dref == null) + { + throw new IllegalArgumentException("dref must be a valid string."); + } + if (value == null || value.length == 0) + { + throw new IllegalArgumentException("value must be non-null and should contain at least one value."); + } + + //Convert drefs to bytes. + byte[] drefBytes = dref.getBytes(StandardCharsets.UTF_8); + if (drefBytes.length == 0) + { + throw new IllegalArgumentException("DREF is an empty string!"); + } + if (drefBytes.length > 255) + { + throw new IllegalArgumentException("dref must be less than 255 bytes in UTF-8. Are you sure this is a valid dref?"); + } + + ByteBuffer bb = ByteBuffer.allocate(4 * value.length); + bb.order(ByteOrder.LITTLE_ENDIAN); + for (int j = 0; j < value.length; ++j) + { + bb.putFloat(j * 4, value[j]); + } + + //Build and send message + os.write(drefBytes.length); + os.write(drefBytes, 0, drefBytes.length); + os.write(value.length); + os.write(bb.array()); + } sendUDP(os.toByteArray()); } @@ -410,9 +449,9 @@ public class XPlaneConnect implements AutoCloseable { throw new IllegalArgumentException("ctrl must no be null."); } - if(values.length > 6) + if(values.length > 7) { - throw new IllegalArgumentException("ctrl must have 6 or fewer elements."); + throw new IllegalArgumentException("ctrl must have 7 or fewer elements."); } if(ac < 0 || ac > 9) { @@ -422,35 +461,28 @@ public class XPlaneConnect implements AutoCloseable //Pad command values and convert to bytes int i; int cur = 0; - ByteBuffer bb = ByteBuffer.allocate(22); + ByteBuffer bb = ByteBuffer.allocate(26); bb.order(ByteOrder.LITTLE_ENDIAN); - for(i = 0; i < values.length; ++i) + for(i = 0; i < 6; ++i) { if(i == 4) { bb.put(cur, (byte) values[i]); cur += 1; } + else if (i >= values.length) + { + bb.putFloat(cur, -998); + cur+= 4; + } else { bb.putFloat(cur, values[i]); cur += 4; } } - for(; i < 6; ++i) - { - if(i == 4) - { - bb.put(cur, (byte) 0); - cur += 1; - } - else - { - bb.putFloat(cur, -998); - cur += 4; - } - } - bb.put(cur, (byte) ac); + bb.put(cur++, (byte) ac); + bb.putFloat(cur, values.length == 7 ? values[6] : -998); //Build and send message ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -688,6 +720,26 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Sets the camera view in X-Plane. + * + * @param view The view to use. + * @throws IOException If the command cannot be sent. + */ + public void sendVIEW(ViewType view) throws IOException + { + ByteBuffer bb = ByteBuffer.allocate(4); + bb.order(ByteOrder.LITTLE_ENDIAN); + bb.putInt(view.getValue()); + + //Build and send message + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("VIEW".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(bb.array()); + sendUDP(os.toByteArray()); + } + /** * Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored * and all points are removed. diff --git a/MATLAB/+XPlaneConnect/XPlaneConnect.jar b/MATLAB/+XPlaneConnect/XPlaneConnect.jar index 997803d..c779711 100644 Binary files a/MATLAB/+XPlaneConnect/XPlaneConnect.jar and b/MATLAB/+XPlaneConnect/XPlaneConnect.jar differ diff --git a/MATLAB/+XPlaneConnect/pauseSim.m b/MATLAB/+XPlaneConnect/pauseSim.m index c15f809..da44b71 100644 --- a/MATLAB/+XPlaneConnect/pauseSim.m +++ b/MATLAB/+XPlaneConnect/pauseSim.m @@ -29,7 +29,7 @@ if ~exist('socket', 'var') end %% Validate input -pause = logical(pause); +pause = int32(pause); %% Send command socket.pauseSim(pause); diff --git a/MATLAB/+XPlaneConnect/sendCTRL.m b/MATLAB/+XPlaneConnect/sendCTRL.m index b3d85b3..822f5a0 100644 --- a/MATLAB/+XPlaneConnect/sendCTRL.m +++ b/MATLAB/+XPlaneConnect/sendCTRL.m @@ -9,6 +9,7 @@ function sendCTRL( values, ac, socket ) % 4. Throttle [-1, 1] % 5. Gear (0=up, 1=down) % 6. Flaps [0, 1] +% 7. Speed Brakes [-0.5, 1.5] % ac (optional): The aircraft to set. 0 for the player aircraft. % socket (optional): The client to use when sending the command. % diff --git a/MATLAB/+XPlaneConnect/sendDREF.m b/MATLAB/+XPlaneConnect/sendDREF.m index cbf2119..e9a1b5c 100644 --- a/MATLAB/+XPlaneConnect/sendDREF.m +++ b/MATLAB/+XPlaneConnect/sendDREF.m @@ -1,17 +1,8 @@ function sendDREF( dref, value, socket ) -% sendDREF Sends a command to X-Plane that sets the given DREF. +% sendDREFs Sends a command to X-Plane that sets the given DREF. This +% function is now an alias to sendDREFs. It is kept only for backwards +% compatibility. % -% Inputs -% dref: The name of the X-Plane dataref to set. -% Value: An array of floating point values whose structure depends on the dref specified. -% socket (optional): The client to use when sending the command. -% -% Use -% 1. import XPlaneConnect.* -% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear -% 3. Value = 0; -% 4. status = setDREF(dataRef, Value); -% % Contributors % [CT] Christopher Teubert (SGT, Inc.) % christopher.a.teubert@nasa.gov @@ -20,19 +11,8 @@ function sendDREF( dref, value, socket ) import XPlaneConnect.* -%% Get client -global clients; if ~exist('socket', 'var') - assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.'); - if isempty(clients) - socket = openUDP(); - else - socket = clients(1); - end -end - -%% Validate input -value = single(value); - -%%Send command -socket.sendDREF(dref, value); \ No newline at end of file + sendDREFs(dref, value) +else + sendDREFs(dref, value, socket) +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendDREFs.m b/MATLAB/+XPlaneConnect/sendDREFs.m new file mode 100644 index 0000000..88fa3b2 --- /dev/null +++ b/MATLAB/+XPlaneConnect/sendDREFs.m @@ -0,0 +1,37 @@ +function sendDREFs( drefs, values, socket ) +% sendDREFs Sends a command to X-Plane that sets the given DREF(s). +% +% Inputs +% dref: The name or names of the X-Plane dataref(s) to set. +% Value: An array or an multidimensional array of floating point values +% whose structure depends on the dref specified. +% socket (optional): The client to use when sending the command. +% +% Use +% 1. import XPlaneConnect.* +% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear +% 3. Value = 0; +% 4. status = setDREF(dataRef, Value); +% +% Contributors +% [JW] Jason Watkins +% jason.w.watkins@nasa.gov + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +values = single(values); + +%%Send command +socket.sendDREFs(drefs, values); \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/sendVIEW.m b/MATLAB/+XPlaneConnect/sendVIEW.m new file mode 100644 index 0000000..d95814c --- /dev/null +++ b/MATLAB/+XPlaneConnect/sendVIEW.m @@ -0,0 +1,35 @@ +function sendVIEW(view, socket) +% sendVIEW Sets the camera +% +%Inputs +% view: The view to use. +% socket (optional): The client to use when sending the command. +% +%Use +% 1. import XPlaneConnect.*; +% 2. sendView(Forwards); +% +% Contributors +% [JW] Jason Watkins + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[pauseSim] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input + + +%% Send command +socket.sendVIEW(view) + +end + diff --git a/MATLAB/Example/Example.m b/MATLAB/Example/Example.m index 9803791..f25e848 100644 --- a/MATLAB/Example/Example.m +++ b/MATLAB/Example/Example.m @@ -11,6 +11,10 @@ import XPlaneConnect.* disp('xplaneconnect Example Script-'); disp('Setting up Simulation'); Socket = openUDP(49005); + +%% Ensure connected +getDREFs('sim/test/test_float') + %% Set position of the player aircraft disp('Setting position'); pauseSim(1); diff --git a/Python/src/example.py b/Python/src/example.py new file mode 100644 index 0000000..f381e5d --- /dev/null +++ b/Python/src/example.py @@ -0,0 +1,72 @@ +from time import sleep +import xpc + +def ex(): + print "X-Plane Connect example script" + print "Setting up simulation" + with xpc.XPlaneConnect() as client: + # Verify connection + try: + # If X-Plane does not respond to the request, a timeout error + # will be raised. + client.getDREF("sim/test/test_float") + except: + print "Error establishing connection to X-Plane." + print "Exiting..." + return + + # Set position of the player aircraft + print "Setting position" + # Lat Lon Alt Pitch Roll Yaw Gear + posi = [37.524, -122.06899, 2500, 0, 0, 0, 1] + client.sendPOSI(posi) + + # Set position of a non-player aircraft + print "Setting NPC position" + # Lat Lon Alt Pitch Roll Yaw Gear + posi = [37.52465, -122.06899, 2500, 0, 20, 0, 1] + client.sendPOSI(posi, 1) + + # Set angle of attack, velocity, and orientation using the DATA command + print "Setting orientation" + data = [\ + [18, 0, -998, 0, -998, -998, -998, -998, -998],\ + [ 3, 130, 130, 130, 130, -998, -998, -998, -998],\ + [16, 0, 0, 0, -998, -998, -998, -998, -998]\ + ] + client.sendDATA(data) + + # Set control surfaces and throttle of the player aircraft using sendCTRL + print "Setting controls" + ctrl = [0.0, 0.0, 0.0, 0.8] + client.sendCTRL(ctrl) + + # Pause the sim + print "Pausing" + client.pauseSim(True) + sleep(2) + + # Toggle pause state to resume + print "Resuming" + client.pauseSim(False) + + # Stow landing gear using a dataref + print "Stowing gear" + gear_dref = "sim/cockpit/switches/gear_handle_status" + client.sendDREF(gear_dref, 0) + + # Let the sim run for a bit. + sleep(4) + + # Make sure gear was stowed successfully + gear_status = client.getDREF(gear_dref) + if gear_status[0] == 0: + print "Gear stowed" + else: + print "Error stowing gear" + + print "End of Python client example" + raw_input("Press any key to exit...") + +if __name__ == "__main__": + ex() \ No newline at end of file diff --git a/Python/src/xpc.py b/Python/src/xpc.py new file mode 100644 index 0000000..c19bbdb --- /dev/null +++ b/Python/src/xpc.py @@ -0,0 +1,384 @@ +import socket +import struct + +class XPlaneConnect(object): + '''XPlaneConnect (XPC) facilitates communication to and from the XPCPlugin.''' + socket = None + + # Basic Functions + def __init__(self, xpHost = 'localhost', xpPort = 49009, port = 0, timeout = 100): + '''Sets up a new connection to an X-Plane Connect plugin running in X-Plane. + + Args: + xpHost: The hostname of the machine running X-Plane. + xpPort: The port on which the XPC plugin is listening. Usually 49007. + port: The port which will be used to send and receive data. + timeout: The period (in milliseconds) after which read attempts will fail. + ''' + + # Validate parameters + xpIP = None + try: + xpIP = socket.gethostbyname(xpHost) + except: + raise ValueError("Unable to resolve xpHost.") + + if xpPort < 0 or xpPort > 65535: + raise ValueError("The specified X-Plane port is not a valid port number.") + if port < 0 or port > 65535: + raise ValueError("The specified port is not a valid port number.") + if timeout < 0: + raise ValueError("timeout must be non-negative.") + + # Setup XPlane IP and port + self.xpDst = (xpIP, xpPort) + + # Create and bind socket + clientAddr = ("0.0.0.0", port) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + self.socket.bind(clientAddr) + timeout /= 1000.0 + self.socket.settimeout(timeout) + + def __del__(self): + self.close() + + # Define __enter__ and __exit__ to support the `with` construct. + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + '''Closes the specified connection and releases resources associated with it.''' + if self.socket is not None: + self.socket.close() + self.socket = None + + def sendUDP(self, buffer): + '''Sends a message over the underlying UDP socket.''' + # Preconditions + if(len(buffer) == 0): + raise ValueError("sendUDP: buffer is empty.") + + self.socket.sendto(buffer, 0, self.xpDst) + + def readUDP(self): + '''Reads a message from the underlying UDP socket.''' + return self.socket.recv(16384) + + # Configuration + def setCONN(self, port): + '''Sets the port on which the client sends and receives data. + + Args: + port: The new port to use. + ''' + #Validate parameters + if port < 0 or port > 65535: + raise ValueError("The specified port is not a valid port number.") + + #Send command + buffer = struct.pack("<4sxH", "CONN", port) + self.sendUDP(buffer) + + #Rebind socket + clientAddr = ("0.0.0.0", port) + timeout = self.socket.gettimeout(); + self.socket.close(); + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + self.socket.bind(clientAddr) + self.socket.settimeout(timeout) + + #Read response + buffer = self.socket.recv(1024) + + def pauseSim(self, pause): + '''Pauses or un-pauses the physics simulation engine in X-Plane. + + Args: + pause: True to pause the simulation; False to resume. + ''' + pause = int(pause) + if pause < 0 or pause > 2: + raise ValueError("Invalid argument for pause command.") + + buffer = struct.pack("<4sxB", "SIMU", pause) + self.sendUDP(buffer) + + # X-Plane UDP Data + def readDATA(self): + '''Reads X-Plane data. + + Returns: A 2 dimensional array containing 0 or more rows of data. Each array + in the result will have 9 elements, the first of which is the row number which + that array represents data for, and the rest of which are the data elements in + that row. + ''' + buffer = self.readUDP(); + if len(buffer) < 6: + return None + rows = (len(buffer) - 5) / 36 + data = [] + for i in range(rows): + data.append(struct.unpack_from("9f", buffer, 5 + 36*i)) + return data + + def sendDATA(self, data): + '''Sends X-Plane data over the underlying UDP socket. + + Args: + data: An array of values representing data rows to be set. Each array in `data` + should have 9 elements, the first of which is a row number in the range (0-134), + and the rest of which are the values to set for that data row. + ''' + if len(data) > 134: + raise ValueError("Too many rows in data.") + + buffer = struct.pack("<4sx", "DATA") + for row in data: + if len(row) != 9: + raise ValueError("Row does not contain exactly 9 values. <" + str(row) + ">") + buffer += struct.pack(" 7: + raise ValueError("Must have between 0 and 7 items in values.") + if ac < 0 or ac > 20: + raise ValueError("Aircraft number must be between 0 and 20.") + + # Pack message + buffer = struct.pack("<4sxB", "POSI", ac) + for i in range(7): + val = -998 + if i < len(values): + val = values[i] + buffer += struct.pack(" 7: + raise ValueError("Must have between 0 and 6 items in values.") + if ac < 0 or ac > 20: + raise ValueError("Aircraft number must be between 0 and 20.") + + # Pack message + buffer = struct.pack("<4sx", "CTRL") + for i in range(6): + val = -998 + if i < len(values): + val = values[i] + if i == 4: + val = -1 if (abs(val + 998) < 1e-4) else val + buffer += struct.pack("b", val) + else: + buffer += struct.pack(" 255: + raise ValueError("dref must be a non-empty string less than 256 characters.") + if value == None: + raise ValueError("value must be a scalar or sequence of floats.") + + # Pack message + if hasattr(value, "__len__"): + if len(value) > 255: + raise ValueError("value must have less than 256 items.") + fmt = " ViewType.FullscreenNoHud: + raise ValueError("Unknown view command.") + + # Pack buffer + buffer = struct.pack("<4sxi", "VIEW", view) + + # Send message + self.sendUDP(buffer) + + def sendWYPT(self, op, points): + '''Adds, removes, or clears waypoints. Waypoints are three dimensional points on or + above the Earth's surface that are represented visually in the simulator. Each + point consists of a latitude and longitude expressed in fractional degrees and + an altitude expressed as meters above sea level. + + Args: + op: The operation to perform. Pass `1` to add waypoints, + `2` to remove waypoints, and `3` to clear all waypoints. + points: A sequence of floating point values representing latitude, longitude, and + altitude triples. The length of this array should always be divisible by 3. + ''' + if op < 1 or op > 3: + raise ValueError("Invalid operation specified.") + if len(points) % 3 != 0: + raise ValueError("Invalid points. Points should be divisible by 3.") + if len(points) / 3 > 255: + raise ValueError("Too many points. You can only send 255 points at a time.") + + if op == 3: + buffer = struct.pack("<4sxBB", "WYPT", 3, 0) + else: + buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points) + self.sendUDP(buffer) + +class ViewType(object): + Forwards = 73 + Down = 74 + Left = 75 + Right = 76 + Back = 77 + Tower = 78 + Runway = 79 + Chase = 80 + Follow = 81 + FollowWithPanel = 82 + Spot = 83 + FullscreenWithHud = 84 + FullscreenNoHud = 85 diff --git a/Python/xplaneConnect.pyproj b/Python/xplaneConnect.pyproj new file mode 100644 index 0000000..c288a9c --- /dev/null +++ b/Python/xplaneConnect.pyproj @@ -0,0 +1,52 @@ + + + + Debug + 2.0 + 3c7a940d-17c8-4e91-882f-9bc8b1d2f54b + . + src\example.py + + + . + . + XPlaneConnect + XPlaneConnect + {9a7a9026-48c1-4688-9d5d-e5699d47d074} + 2.7 + + + true + false + + + true + false + + + + + Code + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + + + + \ No newline at end of file diff --git a/Python/xplaneConnect.sln b/Python/xplaneConnect.sln new file mode 100644 index 0000000..862da8d --- /dev/null +++ b/Python/xplaneConnect.sln @@ -0,0 +1,24 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "XPlaneConnect", "XPlaneConnect.pyproj", "{3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}" +EndProject +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Tests", "..\TestScripts\Python Tests\Tests.pyproj", "{6931EBB2-4E01-4C5A-86B6-668C0E75051B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestScripts/C Tests.win/CTests.vcxproj b/TestScripts/C Tests.win/CTests.vcxproj index c057e7d..6c55744 100644 --- a/TestScripts/C Tests.win/CTests.vcxproj +++ b/TestScripts/C Tests.win/CTests.vcxproj @@ -21,9 +21,20 @@ + + + + + + + + + + + {BC701AF4-552C-4C9D-82A1-B352542783A4} diff --git a/TestScripts/C Tests.win/CTests.vcxproj.filters b/TestScripts/C Tests.win/CTests.vcxproj.filters index cd3ca2c..6e294b0 100644 --- a/TestScripts/C Tests.win/CTests.vcxproj.filters +++ b/TestScripts/C Tests.win/CTests.vcxproj.filters @@ -21,10 +21,40 @@ Source Files + + Source Files + Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/TestScripts/C Tests.xcodeproj/project.pbxproj b/TestScripts/C Tests.xcodeproj/project.pbxproj index 6235e48..a933529 100644 --- a/TestScripts/C Tests.xcodeproj/project.pbxproj +++ b/TestScripts/C Tests.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */ = {isa = PBXBuildFile; fileRef = BE7CF62B1B0CFA34008B1E07 /* Test.c */; }; BEB0F5071A28F9A3001975A6 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F5061A28F9A3001975A6 /* main.c */; }; BEB0F5091A28F9A3001975A6 /* C_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = BEB0F5081A28F9A3001975A6 /* C_Tests.1 */; }; BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F50F1A28F9D5001975A6 /* xplaneConnect.c */; }; @@ -27,6 +28,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CtrlTests.h; sourceTree = ""; }; + BE7CF6271B0CFA34008B1E07 /* DataTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataTests.h; sourceTree = ""; }; + BE7CF6281B0CFA34008B1E07 /* DrefTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrefTests.h; sourceTree = ""; }; + BE7CF6291B0CFA34008B1E07 /* PosiTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PosiTests.h; sourceTree = ""; }; + BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimuTests.h; sourceTree = ""; }; + BE7CF62B1B0CFA34008B1E07 /* Test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Test.c; sourceTree = ""; }; + BE7CF62C1B0CFA34008B1E07 /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = ""; }; + BE7CF62D1B0CFA34008B1E07 /* TextTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTests.h; sourceTree = ""; }; + BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UDPTests.h; sourceTree = ""; }; + BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewTests.h; sourceTree = ""; }; + BE7CF6301B0CFA34008B1E07 /* WyptTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WyptTests.h; sourceTree = ""; }; BEB0F5031A28F9A3001975A6 /* C Tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "C Tests"; sourceTree = BUILT_PRODUCTS_DIR; }; BEB0F5061A28F9A3001975A6 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; BEB0F5081A28F9A3001975A6 /* C_Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = C_Tests.1; sourceTree = ""; }; @@ -66,6 +78,17 @@ BEB0F5051A28F9A3001975A6 /* C Tests */ = { isa = PBXGroup; children = ( + BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */, + BE7CF6271B0CFA34008B1E07 /* DataTests.h */, + BE7CF6281B0CFA34008B1E07 /* DrefTests.h */, + BE7CF6291B0CFA34008B1E07 /* PosiTests.h */, + BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */, + BE7CF62B1B0CFA34008B1E07 /* Test.c */, + BE7CF62C1B0CFA34008B1E07 /* Test.h */, + BE7CF62D1B0CFA34008B1E07 /* TextTests.h */, + BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */, + BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */, + BE7CF6301B0CFA34008B1E07 /* WyptTests.h */, BEB0F5061A28F9A3001975A6 /* main.c */, BEB0F5081A28F9A3001975A6 /* C_Tests.1 */, ); @@ -126,6 +149,7 @@ BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */, BEB0F5121A28F9D5001975A6 /* xplaneConnect.h in Sources */, BEB0F5071A28F9A3001975A6 /* main.c in Sources */, + BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TestScripts/C Tests/CtrlTests.h b/TestScripts/C Tests/CtrlTests.h new file mode 100644 index 0000000..ccf0569 --- /dev/null +++ b/TestScripts/C Tests/CtrlTests.h @@ -0,0 +1,149 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef CTRLTESTS_H +#define CTRLTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int doCTRLTest(char* drefs[7], float values[], int size, int ac, float expected[7]) +{ + float* data[7]; + int sizes[7]; + for (int i = 0; i < 7; ++i) + { + data[i] = (float*)malloc(sizeof(float) * 16); + sizes[i] = 16; + } + + // Execute command + XPCSocket sock = openUDP(IP); + int result = sendCTRL(sock, values, size, ac); + if (result >= 0) + { + result = getDREFs(sock, drefs, data, 7, sizes); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + float actual[7]; + for (int i = 0; i < 7; ++i) + { + actual[i] = data[i][0]; + } + return compareArray(expected, actual, 7); +} + +int basicCTRLTest(char** drefs, int ac) +{ + // Set control surfaces to known state. + float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; + float expected[7] = { 0.0F, 0.0F, 0.0F, NAN, NAN, NAN, NAN }; + int result = doCTRLTest(drefs, CTRL, 3, ac, expected); + if (result < 0) + { + return -10000 + result; + } + + // Test control surfaces and set other values to known state. + expected[0] = CTRL[0] = 0.2F; + expected[1] = CTRL[1] = 0.1F; + expected[2] = CTRL[2] = 0.1F; + expected[3] = 0.8F; + expected[4] = 1.0F; + expected[5] = 0.5F; + result = doCTRLTest(drefs, CTRL, 6, ac, expected); + if (result < 0) + { + return -20000 + result; + } + + // Test other values and verify control surfaces unchanged. + CTRL[0] = -998; + CTRL[1] = -998; + CTRL[2] = -998; + expected[3] = CTRL[3] = 0.9F; + expected[4] = CTRL[4] = 0.0F; + expected[5] = CTRL[5] = 0.75F; + result = doCTRLTest(drefs, CTRL, 6, ac, expected); + if (result < 0) + { + return -30000 + result; + } + return 0; +} + +int testCTRL_Player() +{ + char* 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", + "sim/flightmodel/controls/sbrkrqst" + }; + return basicCTRLTest(drefs, 0); +} + +int testCTRL_NonPlayer() +{ + char* drefs[] = + { + "sim/multiplayer/position/plane1_yolk_pitch", + "sim/multiplayer/position/plane1_yolk_roll", + "sim/multiplayer/position/plane1_yolk_yaw", + "sim/multiplayer/position/plane1_throttle", + "sim/multiplayer/position/plane1_gear_deploy", + "sim/multiplayer/position/plane1_flap_ratio", + "sim/multiplayer/position/plane1_sbrkrqst" + }; + return basicCTRLTest(drefs, 1); +} + +int testCTRL_Speedbrakes() +{ + char* 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", + "sim/flightmodel/controls/sbrkrqst" + }; + + // Arm + float CTRL[7] = { -998, -998, -998, -998, -998, -998, -0.5F }; + float expected[7] = { NAN, NAN, NAN, NAN, NAN, NAN, -0.5F }; + int result = doCTRLTest(drefs, CTRL, 7, 0, expected); + if (result < 0) + { + return -10000 + result; + } + + // Set to full + expected[6] = CTRL[6] = 1.5F; + result = doCTRLTest(drefs, CTRL, 7, 0, expected); + if (result < 0) + { + return -20000 + result; + } + + // Retract + expected[6] = CTRL[6] = 0.0F; + result = doCTRLTest(drefs, CTRL, 7, 0, expected); + if (result < 0) + { + return -30000 + result; + } + return 0; +} +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/DataTests.h b/TestScripts/C Tests/DataTests.h new file mode 100644 index 0000000..2bf1564 --- /dev/null +++ b/TestScripts/C Tests/DataTests.h @@ -0,0 +1,62 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef DATATESTS_H +#define DATATESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int testDATA() +{ + // Initialize + int i, j; // Iterator + char* drefs[100] = + { + "sim/aircraft/parts/acf_gear_deploy" + }; + float* data[100]; // array for result of getDREFs + int sizes[100]; + float DATA[4][9]; // Array for sendDATA + XPCSocket sock = openUDP(IP); + + // Setup + for (int i = 0; i < 100; ++i) + { + data[i] = (float*)malloc(40 * sizeof(float)); + sizes[i] = 40; + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 9; j++) + { + data[i][j] = -998; + } + } + DATA[0][0] = 14; // Gear + DATA[0][1] = 1; + DATA[0][2] = 0; + + // Execution + sendDATA(sock, DATA, 1); + int result = getDREFs(sock, drefs, data, 1, sizes); + + // Close + closeUDP(sock); + + // Tests + if (result < 0)// Request 1 value + { + return -1; + } + if (sizes[0] != 10) + { + return -2; + } + if (*data[0] != data[0][1]) + { + return -3; + } + return 0; +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/DrefTests.h b/TestScripts/C Tests/DrefTests.h new file mode 100644 index 0000000..50338a7 --- /dev/null +++ b/TestScripts/C Tests/DrefTests.h @@ -0,0 +1,247 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef DREFTESTS_H +#define DREFTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int doGETDTest(char* drefs[], float* expected[], int count, int sizes[]) +{ + // Setup memory + int* asizes = (int*)malloc(sizeof(int) * count); + float** actual = (float**)malloc(sizeof(float*) * count); + for (int i = 0; i < count; ++i) + { + asizes[i] = sizes[i]; + actual[i] = (float*)malloc(sizeof(float) * sizes[i]); + } + + // Execute command + XPCSocket sock = openUDP(IP); + int result = getDREFs(sock, drefs, actual, count, asizes); + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test sizes and values + result = compareArrays(expected, sizes, actual, asizes, count); + for (int i = 0; i < count; ++i) + { + free(actual[i]); + } + free(actual); + free(asizes); + return result; +} + +int doDREFTest(char* drefs[], float* values[], float* expected[], int count, int sizes[]) +{ + // Setup memory + int* asizes = (int*)malloc(sizeof(int) * count); + float** actual = (float**)malloc(sizeof(float*) * count); + for (int i = 0; i < count; ++i) + { + asizes[i] = sizes[i]; + actual[i] = (float*)malloc(sizeof(float) * sizes[i]); + } + + // Execute command + XPCSocket sock = openUDP(IP); + int result = sendDREFs(sock, drefs, values, sizes, count); + if (result >= 0) + { + result = getDREFs(sock, drefs, actual, count, asizes); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test sizes and values + result = compareArrays(expected, sizes, actual, asizes, count); + for (int i = 0; i < count; ++i) + { + free(actual[i]); + } + free(actual); + free(asizes); + return result; +} + +int testGETD_Basic() +{ + char* drefs[] = + { + "sim/cockpit/switches/gear_handle_status", //int + "sim/cockpit/autopilot/altitude", //float + "sim/aircraft/prop/acf_prop_type", //int[8] + "sim/cockpit2/switches/panel_brightness_ratio", //float[4] + "sim/aircraft/view/acf_tailnum", //byte[40] + "sim/flightmodel/position/elevation" //double + }; + int sizes[] = { 1, 1, 8, 4, 40, 1 }; + float* expected[6]; + for (int i = 0; i < 6; ++i) + { + expected[i] = (float*)malloc(sizeof(float) * sizes[i]); + for (int j = 0; j < sizes[i]; ++j) + { + expected[i][j] = NAN; + } + } + + return doGETDTest(drefs, expected, 6, sizes); +} + +int testGETD_TestFloat() +{ + 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); + free(expected[0]); + return result; +} + +int testGETD_Types() +{ + char* drefs[] = + { + "sim/cockpit/switches/gear_handle_status", //int + "sim/cockpit/autopilot/altitude", //float + "sim/aircraft/prop/acf_prop_type", //int[8] + "sim/cockpit2/switches/panel_brightness_ratio", //float[4] + "sim/aircraft/view/acf_tailnum", //byte[40] + "sim/flightmodel/position/elevation" //double + }; + int sizes[] = { 1, 1, 8, 4, 40, 1 }; + float* data[6]; + for (int i = 0; i < 6; ++i) + { + data[i] = (float*)malloc(sizeof(float) * sizes[i]); + } + + // Execute command + XPCSocket sock = openUDP(IP); + int result = getDREFs(sock, drefs, data, 6, sizes); + closeUDP(sock); + + // Tests + if (result < 0) + { + return -1; + } + // Verify sizes + if (sizes[0] != 1 || sizes[1] != 1 || sizes[2] != 8 + || sizes[3] != 4 || sizes[4] != 40 || sizes[5] != 1) + { + return -2; + } + // Verify integer drefs are integers + if ((float)((int)data[0][0]) != data[0][0]) + { + return -3; + } + for (int i = 0; i < 8; ++i) + { + if ((float)((int)data[2][i]) != data[2][i]) + { + return -3; + } + } + for (int i = 0; i < 40; ++i) + { + if ((float)((char)data[4][i]) != data[4][i]) + { + return -3; + } + } + // Verify tail number has at least one valid character + if (data[4][0] <= 0 || data[4][0] > 127) + { + return -4; + } + return 0; +} + +int testDREF() +{ + char* drefs[] = + { + "sim/cockpit/switches/gear_handle_status", //int + "sim/cockpit/autopilot/altitude", //float + "sim/aircraft/prop/acf_prop_type", //int[8] + "sim/cockpit2/switches/panel_brightness_ratio", //float[4] + "sim/aircraft/view/acf_tailnum", //byte[40] + "sim/flightmodel/position/elevation" //double - Read only + }; + float* values[6]; + float* expected[6]; + int sizes[6]; + + // Setup + sizes[0] = 1; + values[0] = (float*)malloc(sizes[0] * sizeof(float)); + expected[0] = values[0]; + values[0][0] = 1; + + sizes[1] = 1; + values[1] = (float*)malloc(sizes[1] * sizeof(float)); + expected[1] = values[1]; + values[1][0] = 4000.0F; + + sizes[2] = 8; + values[2] = (float*)malloc(sizes[2] * sizeof(float)); + expected[2] = values[2]; + for (int i = 0; i < 8; ++i) + { + values[2][i] = 0; + } + + sizes[3] = 4; + values[3] = (float*)malloc(sizes[3] * sizeof(float)); + expected[3] = values[3]; + for (int i = 0; i < 4; ++i) + { + values[3][i] = 0.25F; + } + + sizes[4] = 40; + values[4] = (float*)malloc(sizes[4] * sizeof(float)); + expected[4] = (float*)malloc(sizes[4] * sizeof(float)); + memset(values[4], 0, sizes[4] * sizeof(float)); + values[4][0] = 78.0F; //N + values[4][1] = 55.0F; //7 + values[4][2] = 52.0F; //4 + values[4][3] = 56.0F; //8 + values[4][4] = 53.0F; //5 + values[4][5] = 89.0F; //Y + + expected[4][0] = 78.0F; //N + expected[4][1] = 55.0F; //7 + expected[4][2] = 52.0F; //4 + expected[4][3] = 56.0F; //8 + expected[4][4] = 53.0F; //5 + expected[4][5] = 89.0F; //Y + for (int i = 6; i < sizes[4]; ++i) + { + expected[4][i] = NAN; + } + + sizes[5] = 1; + values[5] = (float*)malloc(sizes[5] * sizeof(float)); + expected[5] = (float*)malloc(sizes[5] * sizeof(float)); + values[5][0] = 5000.0F; + expected[5][0] = NAN; + + return doDREFTest(drefs, values, expected, 6, sizes); +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/PosiTests.h b/TestScripts/C Tests/PosiTests.h new file mode 100644 index 0000000..d8b5673 --- /dev/null +++ b/TestScripts/C Tests/PosiTests.h @@ -0,0 +1,121 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef POSITESTS_H +#define POSITESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int doPOSITest(char* drefs[7], float values[], int size, int ac, float expected[7]) +{ + float* data[7]; + int sizes[7]; + for (int i = 0; i < 7; ++i) + { + data[i] = (float*)malloc(sizeof(float) * 10); + sizes[i] = 10; + } + + // Execute command + XPCSocket sock = openUDP(IP); + pauseSim(sock, 1); + int result = sendPOSI(sock, values, size, ac); + if (result >= 0) + { + result = getDREFs(sock, drefs, data, 7, sizes); + } + pauseSim(sock, 0); + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + float actual[7]; + for (int i = 0; i < 7; ++i) + { + actual[i] = data[i][0]; + } + return compareArray(expected, actual, 7); +} + +int basicPOSITest(char** drefs, int ac) +{ + // Set psoition and initial orientation + float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; + float expected[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; + int result = doPOSITest(drefs, POSI, 7, ac, expected); + if (result < 0) + { + return -10000 + result; + } + + // Set orientation + POSI[0] = -998.0F; + POSI[1] = -998.0F; + POSI[2] = -998.0F; + POSI[3] = 5.0F; + POSI[4] = -5.0F; + POSI[5] = 10.0F; + POSI[6] = 0; + + float *loc[3]; + for (int i = 0; i < 3; ++i) + { + loc[i] = (float*)malloc(sizeof(float)); + } + int sizes[3] = { 1, 1, 1 }; + XPCSocket sock = openUDP(IP); + pauseSim(sock, 1); + getDREFs(sock, drefs, loc, 3, sizes); + closeUDP(sock); + + expected[0] = loc[0][0]; + expected[1] = loc[1][0]; + expected[2] = loc[2][0]; + expected[3] = 5.0F; + expected[4] = -5.0F; + expected[5] = 10.0F; + expected[6] = 0.0F; + result = doPOSITest(drefs, POSI, 7, ac, expected); + if (result < 0) + { + return -20000 + result; + } + return 0; +} + +int testPOSI_Player() +{ + char* 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" + }; + return basicPOSITest(drefs, 0); +} + +int testPOSI_NonPlayer() +{ + char* drefs[] = + { + "sim/multiplayer/position/plane1_lat", + "sim/multiplayer/position/plane1_lon", + "sim/multiplayer/position/plane1_el", + "sim/multiplayer/position/plane1_the", + "sim/multiplayer/position/plane1_phi", + "sim/multiplayer/position/plane1_psi", + "sim/multiplayer/position/plane1_gear_deploy" + }; + return basicPOSITest(drefs, 1); +} + + + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/SimuTests.h b/TestScripts/C Tests/SimuTests.h new file mode 100644 index 0000000..0d12012 --- /dev/null +++ b/TestScripts/C Tests/SimuTests.h @@ -0,0 +1,81 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef SIMUTESTS_H +#define SIMIUTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int doSIMUTest(int value, float expected) +{ + int size = 20; + float actual[20]; + char* dref = "sim/operation/override/override_planepath"; + + XPCSocket sock = openUDP(IP); + int result = pauseSim(sock, value); + if (result >= 0) + { + result = getDREF(sock, dref, &actual, &size); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + for (int i = 0; i < 20; ++i) + { + if (!feq(actual[i], expected) && !isnan(expected)) + { + return -100 - i; + } + } + return 0; +} + +int testSIMU_Basic() +{ + int result = doSIMUTest(0, 0); + if (result < 0) + { + return -1; + } + + result = doSIMUTest(1, 1); + if (result < 0) + { + return -2; + } + + result = doSIMUTest(0, 0); + if (result < 0) + { + return -3; + } + return 0; +} + +int testSIMU_Toggle() +{ + int result = doSIMUTest(0, 0); + if (result < 0) + { + return -1; + } + + result = doSIMUTest(2, 1); + if (result < 0) + { + return -1; + } + + result = doSIMUTest(2, 0); + if (result < 0) + { + return -3; + } + return 0; +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/Test.c b/TestScripts/C Tests/Test.c new file mode 100644 index 0000000..cce713c --- /dev/null +++ b/TestScripts/C Tests/Test.c @@ -0,0 +1,51 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "Test.h" + +int testFailed = 0; +int testPassed = 0; + +void runTest(int(*test)(), char* name) +{ + printf("Running test %s... ", name); + int result = test(); // Run Test + if (result == 0) + { + printf("PASSED\n"); + testPassed++; + } + else + { + printf("Test %s - FAILED\n\tError: %i\n", name, result); + testFailed++; + } +} + +int compareFloat(float expected, float actual) +{ + return feq(expected, actual) || isnan(expected) ? 0 : -1; +} + +int compareArray(float expected[], float actual[], int size) +{ + return compareArrays(&expected, &size, &actual, &size, 1); +} + +int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[], int count) +{ + for (int i = 0; i < count; ++i) + { + if (esizes[i] != asizes[i]) + { + return -100 - i; + } + for (int j = 0; j < esizes[i]; ++j) + { + if (!feq(actual[i][j], expected[i][j]) && !isnan(expected[i][j])) + { + return -1000 - i * 100 - j; + } + } + } + return 0; +} \ No newline at end of file diff --git a/TestScripts/C Tests/Test.h b/TestScripts/C Tests/Test.h new file mode 100644 index 0000000..c05a5eb --- /dev/null +++ b/TestScripts/C Tests/Test.h @@ -0,0 +1,25 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef TESTRUNNER_H +#define TESTRUNNER_H + +#include +#include +#include +#include +#include + +#define feq(x, y) (fabs(x - y) < 1e-4) + +#define IP "127.0.0.1" + +extern int testFailed; +extern int testPassed; + +void runTest(int(*test)(), char* name); + +int compareFloat(float expected, float actual); +int compareArray(float expected[], float actual[], int size); +int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[], int count); + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/TextTests.h b/TestScripts/C Tests/TextTests.h new file mode 100644 index 0000000..badf16e --- /dev/null +++ b/TestScripts/C Tests/TextTests.h @@ -0,0 +1,32 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef TEXTTESTS_H +#define TEXTTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int testTEXT() +{ + // Setup + XPCSocket sendPort = openUDP(IP); + int x = 100; + int y = 700; + char* msg = "This is an X-Plane Connect test message.\nThis should be a new line.\r\nThat will be parsed as two line breaks."; + + // Test + sendTEXT(sendPort, msg, x, y); + // NOTE: Manually verify that msg appears on the screen in X-Plane! + + sendTEXT(sendPort, "Another test message", x, y); + // NOTE: Manually verify that msg appears on the screen and that no part of the previous + // message is visible. + + sendTEXT(sendPort, NULL, -1, -1); + + // Cleanup + closeUDP(sendPort); + return 0; +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/UDPTests.h b/TestScripts/C Tests/UDPTests.h new file mode 100644 index 0000000..3c3d3fa --- /dev/null +++ b/TestScripts/C Tests/UDPTests.h @@ -0,0 +1,55 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef UDPTESTS_H +#define UDPTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int testOpen() +{ + XPCSocket sock = openUDP("localhost"); + int result = strncmp(sock.xpIP, "127.0.0.1", 16); + closeUDP(sock); + return result; +} + +int testClose() +{ + XPCSocket sendPort = aopenUDP(IP, 49009, 49063); + closeUDP(sendPort); + sendPort = aopenUDP(IP, 49009, 49063); + closeUDP(sendPort); + return 0; +} + +int testCONN() +{ + // Initialize + char* drefs[] = + { + "sim/cockpit/switches/gear_handle_status" + }; + float data[1]; + int size = 1; + XPCSocket sock = openUDP(IP); +#if (__APPLE__ || __linux) + usleep(0); +#endif + + // Execution + setCONN(&sock, 49055); + int result = getDREF(sock, drefs[0], data, &size); + + // Close + closeUDP(sock); + + // Test + if (result < 0)// No data received + { + return -1; + } + return 0; +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/ViewTests.h b/TestScripts/C Tests/ViewTests.h new file mode 100644 index 0000000..a25c14f --- /dev/null +++ b/TestScripts/C Tests/ViewTests.h @@ -0,0 +1,73 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef VIEWTESTS_H +#define VIEWTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +typedef enum +{ + XPC_VDREF_FORWARDS = 1000, + XPC_VDREF_DOWN4 = 1001, + XPC_VDREF_DOWN8 = 1002, + XPC_VDREF_LEFT45 = 1004, + XPC_VDREF_RIGHT45 = 1005, + XPC_VDREF_LEFT90 = 1006, + XPC_VDREF_RIGHT90 = 1007, + XPC_VDREF_LEFT135 = 1008, + XPC_VDREF_RIGHT135 = 1009, + XPC_VDREF_BACKWARD = 1010, + XPC_VDREF_LEFTUP = 1011, + XPC_VDREF_RIGHTUP = 1012, + XPC_VDREF_AIRPORTBEACONTOWER = 1014, + XPC_VDREF_ONRUNWAY = 1015, + XPC_VDREF_CHASE = 1017, + XPC_VDREF_FOLLOW = 1018, + XPC_VDREF_FOLLOWWITHPANEL = 1019, + XPC_VDREF_SPOT = 1020, + XPC_VDREF_SPOTMOVING = 1021, + XPC_VDREF_FULLSCREENWITHHUD = 1023, + XPC_VDREF_FULLSCREENNOHUD = 1024, + XPC_VDREF_STRAIGHTDOWN = 1025, + XPC_VDREF_3DCOCKPIT = 1026 +} VIEW_DREF; + +int doViewTest(VIEW_TYPE viewCommand, VIEW_DREF viewResult) +{ + // Setup + char* dref = "sim/graphics/view/view_type"; + float value; + int size = 1; + + // Execute command + XPCSocket sock = openUDP(IP); + int result = sendVIEW(sock, viewCommand); + if (result >= 0) + { + result = getDREF(sock, dref, &value, &size); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + if ((int)value != viewResult) + { + return -2; + } + return 0; +} + +int testView() +{ + int result = doViewTest(XPC_VIEW_FORWARDS, XPC_VDREF_FORWARDS); + if (result < 0) + { + return -1; + } + + return doViewTest(XPC_VIEW_CHASE, XPC_VDREF_CHASE); +} +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/WyptTests.h b/TestScripts/C Tests/WyptTests.h new file mode 100644 index 0000000..fce3c2c --- /dev/null +++ b/TestScripts/C Tests/WyptTests.h @@ -0,0 +1,42 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef WYPTTESTS_H +#define WYPTTESTS_H + +#include "Test.h" +#include "xplaneConnect.h" + +int testWYPT() +{ + // Setup + XPCSocket sock = openUDP(IP); + float points[] = + { + 37.5245F, -122.06899F, 2500, + 37.455397F, -122.050037F, 2500, + 37.469567F, -122.051411F, 2500, + 37.479376F, -122.060509F, 2300, + 37.482237F, -122.076130F, 2100, + 37.474881F, -122.087288F, 1900, + 37.467660F, -122.079391F, 1700, + 37.466298F, -122.090549F, 1500, + 37.362562F, -122.039223F, 1000, + 37.361448F, -122.034416F, 1000, + 37.361994F, -122.026348F, 1000, + 37.365541F, -122.022572F, 1000, + 37.373727F, -122.024803F, 1000, + 37.403869F, -122.041283F, 50, + 37.418544F, -122.049222F, 6 + }; + + // Test + sendWYPT(sock, XPC_WYPT_CLR, NULL, 0); + sendWYPT(sock, XPC_WYPT_ADD, points, 15); + // NOTE: Visually ensure waypoints are added in the sim + + // Cleanup + closeUDP(sock); + return 0; +} + +#endif \ No newline at end of file diff --git a/TestScripts/C Tests/main.c b/TestScripts/C Tests/main.c index ffce4ce..0f5cdd9 100644 --- a/TestScripts/C Tests/main.c +++ b/TestScripts/C Tests/main.c @@ -1,930 +1,15 @@ -// -// main.cpp -// XPC Tests -// -// Created by Chris Teubert on 11/25/14. -// Copyright (c) 2014 Chris Teubert. All rights reserved. -// - -#include -#include -#include -#include -#include -#include "xplaneConnect.h" - -#define IP "127.0.0.1" - -int testFailed = 0; -int testPassed = 0; - -void runTest(int (*test)(), char* name) -{ - int result = test(); // Run Test - printf("Test %i: %s - ", testPassed + testFailed + 1, name); - if (result == 0) - { - printf("PASSED\n"); - testPassed++; - } - else - { - printf("FAILED\n\tError: %i\n", result); - testFailed++; - } -} - -int openTest() // openUDP Test -{ - XPCSocket sock = openUDP("localhost"); - int result = strncmp(sock.xpIP, "127.0.0.1", 16); - closeUDP(sock); - return result; -} - -int closeTest() // closeUDP test -{ - XPCSocket sendPort = aopenUDP(IP, 49009, 49063); - closeUDP(sendPort); - sendPort = aopenUDP(IP, 49009, 49063); - closeUDP(sendPort); - return 0; -} - -int sendTEXTTest() -{ - // Setup - XPCSocket sendPort = openUDP(IP); - int x = 100; - int y = 700; - char* msg = "This is an X-Plane Connect test message.\nThis should be a new line.\r\nThat will be parsed as two line breaks."; - - // Test - sendTEXT(sendPort, msg, x, y); - // NOTE: Manually verify that msg appears on the screen in X-Plane! - - sendTEXT(sendPort, "Another test message", x, y); - // NOTE: Manually verify that msg appears on the screen and that no part of the previous - // message is visible. - - sendTEXT(sendPort, NULL, -1, -1); - - // Cleanup - closeUDP(sendPort); - return 0; -} - -int getDREFTest() // Request DREF Test (Required for next tests) -{ - // Initialization - // Get one DREF of each type (int, float, int[], float[], double, byte[]) - #define GETD_COUNT 6 - char* drefs[GETD_COUNT] = - { - "sim/cockpit/switches/gear_handle_status", //int - "sim/cockpit/autopilot/altitude", //float - "sim/aircraft/prop/acf_prop_type", //int[8] - "sim/cockpit2/switches/panel_brightness_ratio", //float[4] - "sim/aircraft/view/acf_tailnum", //byte[40] - "sim/flightmodel/position/elevation" //double - }; - float* data[GETD_COUNT]; - int sizes[GETD_COUNT]; - XPCSocket sock = openUDP(IP); - - // Setup - for (int i = 0; i < GETD_COUNT; ++i) - { - data[i] = (float*)malloc(256 * sizeof(float)); - sizes[i] = 256; - } - - // Execution - int result = getDREFs(sock, drefs, data, GETD_COUNT, sizes); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - // Verify sizes - if (sizes[0] != 1 || sizes[1] != 1 || sizes[2] != 8 - || sizes[3] != 4 || sizes[4] != 40 || sizes[5] != 1) - { - return -2; - } - // Verify integer drefs are integers - if ((float)((int)data[0][0]) != data[0][0]) - { - return -3; - } - for (int i = 0; i < 8; ++i) - { - if ((float)((int)data[2][i]) != data[2][i]) - { - return -3; - } - } - for (int i = 0; i < 40; ++i) - { - if ((float)((char)data[4][i]) != data[4][i]) - { - return -3; - } - } - // Verify tail number has at least one valid character - if (data[4][0] <= 0 || data[4][0] > 127) - { - return -4; - } - return 0; -} - -int sendDREFTest() // sendDREF test -{ - // Initialization - // Set one DREF of each type (int, float, int[], float[], double, byte[]) - // Also set one read-only to make sure it fails - #define DREF_COUNT 6 - char* drefs[DREF_COUNT] = - { - "sim/cockpit/switches/gear_handle_status", //int - "sim/cockpit/autopilot/altitude", //float - "sim/aircraft/prop/acf_prop_type", //int[8] - "sim/cockpit2/switches/panel_brightness_ratio", //float[4] - "sim/aircraft/view/acf_tailnum", //byte[40] - "sim/flightmodel/position/elevation" //double - Read only - }; - float* data[DREF_COUNT]; - int sizes[DREF_COUNT]; - float* values[DREF_COUNT]; - XPCSocket sock = openUDP(IP); - - // Setup - sizes[0] = 1; - values[0] = (float*)malloc(sizes[0] * sizeof(float)); - values[0][0] = 1; - - sizes[1] = 1; - values[1] = (float*)malloc(sizes[1] * sizeof(float)); - values[1][0] = 4000.0F; - - sizes[2] = 8; - values[2] = (float*)malloc(sizes[2] * sizeof(float)); - for (int i = 0; i < 8; ++i) - { - values[2][i] = 0; - } - - sizes[3] = 4; - values[3] = (float*)malloc(sizes[3] * sizeof(float)); - for (int i = 0; i < 4; ++i) - { - values[3][i] = 0.25F; - } - - sizes[4] = 40; - values[4] = (float*)malloc(sizes[4] * sizeof(float)); - memset(values[4], 0, sizes[4] * sizeof(float)); - values[4][0] = 78.0F; //N - values[4][1] = 55.0F; //7 - values[4][2] = 52.0F; //4 - values[4][3] = 56.0F; //8 - values[4][4] = 53.0F; //5 - values[4][5] = 89.0F; //Y - - sizes[5] = 1; - values[5] = (float*)malloc(sizes[5] * sizeof(float)); - values[5][0] = 5000.0F; - - // Execution - for (int i = 0; i < DREF_COUNT; ++i) - { - sendDREF(sock, drefs[i], values[i], sizes[i]); - data[i] = (float*)malloc(256 * sizeof(float)); - sizes[i] = 256; - } - int result = getDREFs(sock, drefs, data, DREF_COUNT, sizes); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - // Verify gear handle was set - if (sizes[0] != 1 || data[0][0] != 1) - { - return -2; - } - // Verify autopilot altitude was set - if (sizes[1] != 1 || data[1][0] != 4000.0F) - { - return -3; - } - // Verify prop type was set - if (sizes[2] != 8) - { - return -4; - } - for (int i = 0; i < 8; ++i) - { - if (data[2][i] != values[2][i]) - { - return -4; - } - } - // Verify panel brightness was set - if (sizes[3] != 4) - { - return -5; - } - for (int i = 0; i < 4; ++i) - { - if (data[3][i] != values[3][i]) - { - return -5; - } - } - // Verify tail number was set - for (int i = 0; i < 6; ++i) - { - if (data[4][i] != values[4][i]) - { - return -6; - } - } - // Verify aircraft elevation was NOT set - if (sizes[5] != 1 || data[5][0] == 5000.0F) - { - return -7; - } - return 0; -} - -int sendDATATest() // sendDATA test -{ - // Initialize - int i,j; // Iterator - char* drefs[100] = - { - "sim/aircraft/parts/acf_gear_deploy" - }; - float* data[100]; // array for result of getDREFs - int sizes[100]; - float DATA[4][9]; // Array for sendDATA - XPCSocket sock = openUDP(IP); - - // Setup - for (int i = 0; i < 100; ++i) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - for (i = 0; i < 4; i++) - { - for (j = 0; j < 9; j++) - { - data[i][j] = -998; - } - } - DATA[0][0] = 14; // Gear - DATA[0][1] = 1; - DATA[0][2] = 0; - - // Execution - sendDATA(sock, DATA, 1); - int result = getDREFs(sock, drefs, data, 1, sizes); - - // Close - closeUDP(sock); - - // Tests - if ( result < 0 )// Request 1 value - { - return -1; - } - if (sizes[0] != 10) - { - return -2; - } - if (*data[0] != data[0][1]) - { - return -3; - } - return 0; -} - -int psendCTRLTest() // sendCTRL test -{ - // Initialize - char* drefs[100] = - { - "sim/cockpit2/controls/yoke_pitch_ratio", - "sim/cockpit2/controls/yoke_roll_ratio", - "sim/cockpit2/controls/yoke_heading_ratio", - "sim/flightmodel/engine/ENGN_thro", - "sim/cockpit/switches/gear_handle_status", - "sim/flightmodel/controls/flaprqst" - }; - float* data[100]; - int sizes[100]; - float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; - XPCSocket sock = openUDP(IP); - - // Setup - for (int i = 0; i < 100; i++) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execute 1 - // 0 pitch, roll, yaw - sendCTRL(sock, CTRL, 3, 0); - int result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - for (int i = 0; i < 3; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-4) - { - return -i - 11; - } - } - - sock = openUDP(IP); - // Execute 2 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - CTRL[0] = 0.2F; - CTRL[1] = 0.1F; - CTRL[2] = 0.1F; - sendCTRL(sock, CTRL, 6, 0); - result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -2; - } - for (int i = 0; i < 6; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-4) - { - return -i - 21; - } - } - - sock = openUDP(IP); - // Execute 2 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - CTRL[0] = -998.0F; - CTRL[1] = -998.0F; - CTRL[2] = -998.0F; - sendCTRL(sock, CTRL, 6, 0); - result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -3; - } - for (int i = 0; i < 6; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-2) - { - return -i - 31; - } - } - - return 0; -} - -int sendCTRLTest() -{ - // Initialize - char* drefs[100] = - { - "sim/multiplayer/position/plane1_yolk_pitch", - "sim/multiplayer/position/plane1_yolk_roll", - "sim/multiplayer/position/plane1_yolk_yaw", - "sim/multiplayer/position/plane1_throttle", - "sim/multiplayer/position/plane1_gear_deploy", - "sim/multiplayer/position/plane1_flap_ratio", - }; - float* data[100]; - int sizes[100]; - float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F }; - XPCSocket sock = openUDP(IP); - - // Setup - for (int i = 0; i < 100; i++) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execute 1 - // 0 pitch, roll, yaw - sendCTRL(sock, CTRL, 3, 1); - int result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - for (int i = 0; i < 3; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-4) - { - return -i - 11; - } - } - - sock = openUDP(IP); - // Execute 2 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - CTRL[0] = 0.2F; - CTRL[1] = 0.1F; - CTRL[2] = 0.1F; - sendCTRL(sock, CTRL, 6, 1); - result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -2; - } - for (int i = 0; i < 6; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-4) - { - return -i - 21; - } - } - - sock = openUDP(IP); - // Execute 2 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - CTRL[0] = -998.0F; - CTRL[1] = -998.0F; - CTRL[2] = -998.0F; - sendCTRL(sock, CTRL, 6, 1); - result = getDREFs(sock, drefs, data, 6, sizes); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -3; - } - for (int i = 0; i < 6; i++) - { - if (fabs(data[i][0] - CTRL[i]) > 1e-2) - { - return -i - 31; - } - } - - return 0; -} - -int psendPOSITest() // sendPOSI test -{ - // Initialization - int i; // Iterator - char* drefs[100] = - { - "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" - }; - float* data[100]; - int sizes[100]; - float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; - XPCSocket sock = openUDP(IP); - - // Setup - for (i = 0; i < 100; i++) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execution 1 - pauseSim(sock, 1); - sendPOSI(sock, POSI, 7, 0); - int result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - for (i = 0; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 11; - } - } - - // Setup 2 - sock = openUDP(IP); - POSI[0] = -998.0F; - POSI[1] = -998.0F; - POSI[2] = -998.0F; - POSI[3] = 5.0F; - POSI[4] = -5.0F; - POSI[5] = 10.0F; - POSI[6] = 0; - - // Execution 2 - pauseSim(sock, 1); - float *loc[3]; - for(int i = 0; i < 3; ++i) - { - loc[i] = (float*)malloc(sizeof(float)); - } - getDREFs(sock, drefs, &loc, 3, sizes); - sendPOSI(sock, POSI, 7, 0); - result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -2; - } - // Compare position to make sure they weren't set - for (int i = 0; i < 3; ++i) - { - // Note: Because the sim was paused when both of these were read, we really do expect *exactly* - // the same value even though we are comparing floats. - if (data[i][0] != loc[i][0]) - { - return -i - 21; - } - } - // Compare everything else. - for (i = 3; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 21; - } - } - - // Setup 3 - sock = openUDP(IP); - POSI[0] = 37.524F; - POSI[1] = -122.06899F; - POSI[2] = 20000; - POSI[3] = 15.0F; - POSI[4] = -25.0F; - POSI[5] = -10.0F; - POSI[6] = 1; - - // Execution 2 - pauseSim(sock, 1); - sendPOSI(sock, POSI, 3, 0); - result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -3; - } - // Compare position to make sure it was set. - for (int i = 0; i < 3; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 31; - } - } - // Compare everything else to make sure it *wasn't*. - for (i = 3; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) < 1) - { - return -i - 31; - } - } - - return 0; -} - -int sendPOSITest() // sendPOSI test -{ - // Initialization - int i; // Iterator - char* drefs[100] = - { - // TODO: Can't get global position for multiplayer a/c? - "sim/multiplayer/position/plane1_lat", - "sim/multiplayer/position/plane1_lon", - "sim/multiplayer/position/plane1_el", - "sim/multiplayer/position/plane1_the", - "sim/multiplayer/position/plane1_phi", - "sim/multiplayer/position/plane1_psi", - "sim/multiplayer/position/plane1_gear_deploy" - }; - float* data[100]; - int sizes[100]; - float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; - XPCSocket sock = openUDP(IP); - - // Setup - for (i = 0; i < 100; i++) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execution 1 - pauseSim(sock, 1); - sendPOSI(sock, POSI, 7, 1); - int result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - for (i = 0; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 11; - } - } - - // Setup 2 - sock = openUDP(IP); - POSI[0] = -998.0F; - POSI[1] = -998.0F; - POSI[2] = -998.0F; - POSI[3] = 5.0F; - POSI[4] = -5.0F; - POSI[5] = 10.0F; - POSI[6] = 0; - - // Execution 2 - pauseSim(sock, 1); - float* loc[3]; - for(int i = 0; i < 3; ++i) - { - loc[i] = (float*)malloc(sizeof(float)); - } - getDREFs(sock, drefs, loc, 3, sizes); - sendPOSI(sock, POSI, 7, 1); - result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -2; - } - // Compare position to make sure they weren't set - for (int i = 0; i < 3; ++i) - { - // Note: Because the sim was paused when both of these were read, we really do expect *exactly* - // the same value even though we are comparing floats. - if (data[i][0] != loc[i][0]) - { - return -i - 21; - } - } - // Compare everything else. - for (i = 3; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 21; - } - } - - // Setup 3 - sock = openUDP(IP); - POSI[0] = 37.524F; - POSI[1] = -122.06899; - POSI[2] = 20000; - POSI[3] = 15.0F; - POSI[4] = -25.0F; - POSI[5] = -10.0F; - POSI[6] = 1; - - // Execution 2 - pauseSim(sock, 1); - sendPOSI(sock, POSI, 3, 1); - result = getDREFs(sock, drefs, data, 7, sizes); - pauseSim(sock, 0); - - // Close - closeUDP(sock); - - // Tests - if (result < 0) - { - return -3; - } - // Compare position to make sure it was set. - for (int i = 0; i < 3; ++i) - { - if (fabs(data[i][0] - POSI[i]) > 1e-4) - { - return -i - 31; - } - } - // Compare everything else to make sure it *wasn't*. - for (i = 3; i < 7; ++i) - { - if (fabs(data[i][0] - POSI[i]) < 1) - { - return -i - 31; - } - } - - return 0; -} - -int sendWYPTTest() -{ - // Setup - XPCSocket sock = openUDP(IP); - float points[] = - { - 37.5245F, -122.06899F, 2500, - 37.455397F, -122.050037F, 2500, - 37.469567F, -122.051411F, 2500, - 37.479376F, -122.060509F, 2300, - 37.482237F, -122.076130F, 2100, - 37.474881F, -122.087288F, 1900, - 37.467660F, -122.079391F, 1700, - 37.466298F, -122.090549F, 1500, - 37.362562F, -122.039223F, 1000, - 37.361448F, -122.034416F, 1000, - 37.361994F, -122.026348F, 1000, - 37.365541F, -122.022572F, 1000, - 37.373727F, -122.024803F, 1000, - 37.403869F, -122.041283F, 50, - 37.418544F, -122.049222F, 6 - }; - - // Test - sendWYPT(sock, XPC_WYPT_CLR, NULL, 0); - sendWYPT(sock, XPC_WYPT_ADD, points, 15); - // NOTE: Visually ensure waypoints are added in the sim - - // Cleanup - closeUDP(sock); - return 0; -} - -int pauseTest() // pauseSim test -{ - // Initialize - // Note: Always run this test to the end so that the sim ends up unpaused in the - // case where commands are working but reading results isn't. - int result = 0; - char* drefs[100] = - { - "sim/operation/override/override_planepath" - }; - float* data[100]; - int sizes[100]; - XPCSocket sock = openUDP(IP); - - // Setup - for (int i = 0; i < 100; i++) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execute - pauseSim(sock, 1); - result = getDREF(sock, drefs[0], data[0], sizes); - - // Test - if (result < 0) - { - result = -1; - } - if (data[0][0] != 1) - { - result = -2; - } - - if (result == 0) - { - // Execute 2 - pauseSim(sock, 0); - result = getDREF(sock, drefs[0], data[0], sizes); - - // Test 2 - if (result < 0) - { - result = -3; - } - if (data[0][0] != 0) - { - result = -4; - } - } - - // Close - closeUDP(sock); - - return result; -} - -int connTest() // setConn test -{ - // Initialize - char* drefs[100] = - { - "sim/cockpit/switches/gear_handle_status" - }; - float* data[100]; - int sizes[100]; - XPCSocket sock = openUDP(IP); -#if (__APPLE__ || __linux) - usleep(0); -#endif - - // Setup - for (int i = 0; i < 100; ++i) - { - data[i] = (float*)malloc(40 * sizeof(float)); - sizes[i] = 40; - } - - // Execution - setCONN(&sock, 49055); - int result = getDREF(sock, drefs[0], data[0], sizes); - - // Close - closeUDP(sock); - - // Test - if ( result < 0 )// No data received - { - return -1; - } - return 0; -} +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "Test.h" +#include "UDPTests.h" +#include "DrefTests.h" +#include "SimuTests.h" +#include "CtrlTests.h" +#include "PosiTests.h" +#include "DataTests.h" +#include "TextTests.h" +#include "ViewTests.h" +#include "WyptTests.h" int main(int argc, const char * argv[]) { @@ -936,23 +21,42 @@ int main(int argc, const char * argv[]) printf("(Mac) \n"); #elif (__linux) printf("(Linux) \n"); +#else + printf("(Unable to determine operating system) \n") #endif - runTest(openTest, "open"); - runTest(closeTest, "close"); - runTest(pauseTest, "SIMU"); - runTest(getDREFTest, "GETD"); - runTest(sendDREFTest, "DREF"); - runTest(sendDATATest, "DATA"); - runTest(sendCTRLTest, "CTRL"); - runTest(psendCTRLTest, "CTRL (player)"); - runTest(sendPOSITest, "POSI"); - runTest(psendPOSITest, "POSI (player)"); - runTest(sendWYPTTest, "WYPT"); - runTest(sendTEXTTest, "TEXT"); - runTest(connTest, "CONN"); + // Basic Networking + runTest(testOpen, "open"); + runTest(testClose, "close"); + // Datarefs + runTest(testGETD_Basic, "GETD"); + runTest(testGETD_Types, "GETD (types)"); + runTest(testGETD_TestFloat, "GETD (test float)"); + runTest(testDREF, "DREF"); + // Pause + runTest(testSIMU_Basic, "SIMU"); + runTest(testSIMU_Toggle, "SIMU (toggle)"); + // CTRL + runTest(testCTRL_Player, "CTRL (player)"); + runTest(testCTRL_NonPlayer, "CTRL (non-player)"); + runTest(testCTRL_Speedbrakes, "CTRL (speedbrakes)"); + // POSI + runTest(testPOSI_Player, "POSI (player)"); + runTest(testPOSI_NonPlayer, "POSI (non-player)"); + // Data + runTest(testDATA, "DATA"); + // Text + runTest(testTEXT, "TEXT"); + // Waypoints + runTest(testWYPT, "WYPT"); + // View + runTest(testView, "VIEW"); + // setConn + runTest(testCONN, "CONN"); printf( "----------------\nTest Summary\n\tFailed: %i\n\tPassed: %i\n", testFailed, testPassed ); + printf("Press any key to exit."); + getchar(); return 0; } diff --git a/TestScripts/Java Tests/XPlaneConnectTest.java b/TestScripts/Java Tests/XPlaneConnectTest.java index aed0883..fc7c76e 100644 --- a/TestScripts/Java Tests/XPlaneConnectTest.java +++ b/TestScripts/Java Tests/XPlaneConnectTest.java @@ -1,5 +1,6 @@ package gov.nasa.xpc.test; +import gov.nasa.xpc.ViewType; import gov.nasa.xpc.WaypointOp; import gov.nasa.xpc.XPlaneConnect; @@ -208,12 +209,30 @@ public class XPlaneConnectTest { xpc.pauseSim(true); float[] result = xpc.getDREF(dref); - //assertEquals(1, result.length); //TODO: Why is this result 20 elements long in Java? (It's only one in MATLAB) + assertEquals(20, result.length); assertEquals(1, result[0], 1e-4); xpc.pauseSim(false); result = xpc.getDREF(dref); - //assertEquals(1, result.length); + assertEquals(20, result.length); + assertEquals(0, result[0], 1e-4); + } + } + + @Test + public void testPauseSim_Switch() throws IOException + { + String dref = "sim/operation/override/override_planepath"; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.pauseSim(true); + float[] result = xpc.getDREF(dref); + assertEquals(20, result.length); + assertEquals(1, result[0], 1e-4); + + xpc.pauseSim(2); + result = xpc.getDREF(dref); + assertEquals(20, result.length); assertEquals(0, result[0], 1e-4); } } @@ -315,6 +334,33 @@ public class XPlaneConnectTest } } + @Test + public void testSendDREFs() throws IOException + { + String[] drefs = + { + "sim/cockpit/switches/gear_handle_status", + "sim/cockpit/autopilot/altitude" + }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + float[][] values = {{1}, {2000}}; + xpc.sendDREFs(drefs, values); + + float[][] result = xpc.getDREFs(drefs); + assertEquals(values[0][0], result[0][0], 1e-4); + assertEquals(values[1][0], result[1][0], 1e-4); + + values[0][0] = 0; + values[1][0] = 4000; + xpc.sendDREFs(drefs, values); + + result = xpc.getDREFs(drefs); + assertEquals(values[0][0], result[0][0], 1e-4); + assertEquals(values[1][0], result[1][0], 1e-4); + } + } + @Test(expected = IllegalArgumentException.class) public void testSendDREF_NullDREF() throws IOException { @@ -445,6 +491,34 @@ public class XPlaneConnectTest } } + @Test + public void testSendCTRL_Speedbrakes() throws IOException + { + String dref = "sim/flightmodel/controls/sbrkrqst"; + float[] ctrl = new float[] { -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -0.5F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + // Speedbrakes armed + xpc.sendCTRL(ctrl); + float[] result = xpc.getDREF(dref); + + assertEquals(-0.5F, result[0], 1e-4); + + ctrl[6] = 1.0F; // Deploy speedbrakes + xpc.sendCTRL(ctrl); + result = xpc.getDREF(dref); + + assertEquals(1.0F, result[0], 1e-4); + + ctrl[6] = 0.0F; // Retract speedbrakes + xpc.sendCTRL(ctrl); + result = xpc.getDREF(dref); + + assertEquals(0.0F, result[0], 1e-4); + } + + } + @Test(expected = IllegalArgumentException.class) public void testSendCTRL_NullCtrl() throws IOException { @@ -457,7 +531,7 @@ public class XPlaneConnectTest @Test(expected = IllegalArgumentException.class) public void testSendCTRL_LongCtrl() throws IOException { - float[] ctrl = new float[] {0, 0, 1, 0.8F, 0, 1, -998}; + float[] ctrl = new float[] {0, 0, 1, 0.8F, 0, 1, 0, -998}; try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.sendCTRL(ctrl); @@ -604,4 +678,24 @@ public class XPlaneConnectTest fail(); } } + + @Test + public void testSendView() throws IOException + { + String dref = "sim/graphics/view/view_type"; + float fwd = 1000; + float chase = 1017; + + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.sendVIEW(ViewType.Forwards); + float result = xpc.getDREF(dref)[0]; + assertEquals(fwd, result, 1e-4); + + xpc.sendVIEW(ViewType.Chase); + result = xpc.getDREF(dref)[0]; + assertEquals(chase, result, 1e-4); + } + + } } \ No newline at end of file diff --git a/TestScripts/MATLAB Tests/sendVIEWTest.m b/TestScripts/MATLAB Tests/sendVIEWTest.m new file mode 100644 index 0000000..4cfe29c --- /dev/null +++ b/TestScripts/MATLAB Tests/sendVIEWTest.m @@ -0,0 +1,26 @@ +function sendVIEWTest() +%% Setup +addpath('../../MATLAB') +import XPlaneConnect.* + +if ~exist('gov.nasa.xpc.ViewType', 'class') + [folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP')); + javaaddpath(fullfile(folder, 'XPlaneConnect.jar')); +end + +dref = 'sim/graphics/view/view_type'; +fwd = 1000; +chase = 1017; + +%% Excecute +sendVIEW(gov.nasa.xpc.ViewType.Forwards); +result = getDREFs(dref); +assert(isequal(result, fwd)) + +sendVIEW(gov.nasa.xpc.ViewType.Chase); +result = getDREFs(dref); +assert(isequal(result, chase)) + + +end + diff --git a/TestScripts/MATLAB Tests/tests.m b/TestScripts/MATLAB Tests/tests.m index 6871686..13e0be4 100644 --- a/TestScripts/MATLAB Tests/tests.m +++ b/TestScripts/MATLAB Tests/tests.m @@ -1,3 +1,6 @@ +addpath('../../MATLAB') +import XPlaneConnect.* + testsPassed=0; testsFailed=0; if ismac() @@ -18,9 +21,11 @@ theTests = {{@openCloseTest, 'Open/Close Test', 0},... {@CTRLTest,'CTRL Test', 0},... {@POSITest,'POSI Test', 0},... {@sendWYPTTest,'WYPT Test', 0},... + {@sendVIEWTest,'VIEW Test', 0},... {@pauseTest,'Pause Test', 0},... {@setConnTest, 'setConn Test', 0}}; +socket = openUDP(); for i=1:length(theTests) fprintf(['Test ',num2str(i),': ',theTests{i}{2},' - ']); try @@ -35,6 +40,7 @@ for i=1:length(theTests) testsFailed = testsFailed + 1; end end +closeUDP(socket); disp('Results Summary:'); fprintf('Passed: %i\tFailed: %i\n',testsPassed, testsFailed); \ No newline at end of file diff --git a/TestScripts/Python Tests/Tests.py b/TestScripts/Python Tests/Tests.py new file mode 100644 index 0000000..2cdc825 --- /dev/null +++ b/TestScripts/Python Tests/Tests.py @@ -0,0 +1,385 @@ +import random +import unittest +import imp +import time + +import xpc + +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) + buf = receiver.readUDP() + + # Cleanup + sender.close() + receiver.close() + + # Tests + for a, b in zip(test, buf): + 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_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_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() \ No newline at end of file diff --git a/TestScripts/Python Tests/Tests.pyproj b/TestScripts/Python Tests/Tests.pyproj new file mode 100644 index 0000000..c1626b5 --- /dev/null +++ b/TestScripts/Python Tests/Tests.pyproj @@ -0,0 +1,45 @@ + + + + Debug + 2.0 + 6931ebb2-4e01-4c5a-86b6-668c0e75051b + . + Tests.py + ..\..\Python\src\ + . + . + Tests + Tests + {9a7a9026-48c1-4688-9d5d-e5699d47d074} + 2.7 + + + true + false + + + true + false + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + + + + \ No newline at end of file diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 15335ec..42048e5 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -1,26 +1,26 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. // -//X-Plane API -//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -//associated documentation files(the "Software"), to deal in the Software without restriction, -//including without limitation the rights to use, copy, modify, merge, publish, distribute, -//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions : -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Neither the names of the authors nor that of X - Plane or Laminar Research -// may be used to endorse or promote products derived from this software -// without specific prior written permission from the authors or -// Laminar Research, respectively. +// X-Plane API +// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files(the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Neither the names of the authors nor that of X - Plane or Laminar Research +// may be used to endorse or promote products derived from this software +// without specific prior written permission from the authors or +// Laminar Research, respectively. #include "DataManager.h" -#include "DataMaps.h" #include "Log.h" #include "XPLMDataAccess.h" #include "XPLMGraphics.h" +#include #include #include #include @@ -29,12 +29,17 @@ namespace XPC { using namespace std; + const size_t PLANE_COUNT = 20; static map drefs; - static map mdrefs[20]; + static map mdrefs[PLANE_COUNT]; static map sdrefs; + DREF XPData[134][8] = { DREF_None }; + void DataManager::Initialize() { + Log::WriteLine(LOG_TRACE, "DMAN", "Initializing drefs"); + drefs.insert(make_pair(DREF_None, XPLMFindDataRef("sim/test/test_float"))); drefs.insert(make_pair(DREF_Pause, XPLMFindDataRef("sim/operation/override/override_planepath"))); @@ -68,6 +73,9 @@ namespace XPC drefs.insert(make_pair(DREF_FlapSetting, XPLMFindDataRef("sim/flightmodel/controls/flaprqst"))); drefs.insert(make_pair(DREF_FlapActual, XPLMFindDataRef("sim/flightmodel/controls/flaprat"))); + drefs.insert(make_pair(DREF_SpeedBrakeSet, XPLMFindDataRef("sim/flightmodel/controls/sbrkrqst"))); + drefs.insert(make_pair(DREF_SpeedBrakeActual, XPLMFindDataRef("sim/flightmodel/controls/sbrkrat"))); + drefs.insert(make_pair(DREF_GearDeploy, XPLMFindDataRef("sim/aircraft/parts/acf_gear_deploy"))); drefs.insert(make_pair(DREF_GearHandle, XPLMFindDataRef("sim/cockpit/switches/gear_handle_status"))); drefs.insert(make_pair(DREF_BrakeParking, XPLMFindDataRef("sim/flightmodel/controls/parkbrakel"))); @@ -138,7 +146,7 @@ namespace XPC drefs.insert(make_pair(DREF_MP7Alt, XPLMFindDataRef("sim/multiplayer/position/plane7_el"))); char multi[256]; - for (int i = 1; i < 20; i++) + for (int i = 1; i < PLANE_COUNT; i++) { sprintf(multi, "sim/multiplayer/position/plane%i_x", i); mdrefs[i][DREF_LocalX] = XPLMFindDataRef(multi); @@ -168,7 +176,7 @@ namespace XPC sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); mdrefs[i][DREF_Spoiler] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_speedbrake_ratio", i); - mdrefs[i][DREF_BrakeSpeed] = XPLMFindDataRef(multi); + mdrefs[i][DREF_SpeedBrakeSet] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_slat_ratio", i); mdrefs[i][DREF_Slats] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_wing_sweep", i); @@ -295,8 +303,9 @@ namespace XPC XPData[26][0] = DREF_ThrottleActual; } - int DataManager::Get(string dref, float values[], int size) + int DataManager::Get(const string& dref, float values[], int size) { + Log::WriteLine(LOG_TRACE, "DMAN", "Entered Get(string, float*, int)"); XPLMDataRef& xdref = sdrefs[dref]; if (xdref == NULL) { @@ -304,24 +313,18 @@ namespace XPC } if (!xdref) // DREF does not exist { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str()); -#endif + Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str()); return 0; } XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType); // XPLMDataTypeID is a bit flag, so it may contain more than one of the // following types. We prefer types as close to float as possible. if ((dataType & 2) == 2) // Float { values[0] = XPLMGetDataf(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %f", values[0]); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]); return 1; } if ((dataType & 8) == 8) // Float array @@ -329,99 +332,80 @@ namespace XPC int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); if (drefSize > size) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); - Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size); -#endif + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size); drefSize = size; } XPLMGetDatavf(xdref, values, 0, drefSize); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); return drefSize; } if ((dataType & 4) == 4) // Double { values[0] = (float)XPLMGetDatad(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %f", values[0]); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]); return 1; } if ((dataType & 1) == 1) // Integer { - values[0] = (float)XPLMGetDatai(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %i", (int)values[0]); -#endif + int iValue = XPLMGetDatai(xdref); + values[0] = (float)iValue; + Log::FormatLine(LOG_INFO, "DMAN", " -- Real value was %i, cast to %f", iValue, values[0]); return 1; } if ((dataType & 16) == 16) // Integer array { - int iValues[200]; + const std::size_t TMP_SIZE = 200; + int iValues[TMP_SIZE]; int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); if (drefSize > size) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); - Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size); -#endif + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size); drefSize = size; } - if (drefSize > 200) + if (drefSize > TMP_SIZE) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer"); - Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize); -#endif - drefSize = 200; + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE); + drefSize = TMP_SIZE; } XPLMGetDatavi(xdref, iValues, 0, drefSize); for (int i = 0; i < drefSize; ++i) { values[i] = (float)iValues[i]; } -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); return drefSize; } if ((dataType & 32) == 32) // Byte array { - char bValues[1024]; + const std::size_t TMP_SIZE = 1024; + char bValues[TMP_SIZE]; int drefSize = XPLMGetDatab(xdref, NULL, 0, 0); if (drefSize > size) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); - Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size); -#endif + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size); drefSize = size; } - if (drefSize > 1024) + if (drefSize > TMP_SIZE) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer"); - Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize); -#endif - drefSize = 1024; + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE); + drefSize = TMP_SIZE; } XPLMGetDatab(xdref, bValues, 0, drefSize); for (int i = 0; i < drefSize; ++i) { values[i] = (float)bValues[i]; } -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); return drefSize; } // No match -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Unrecognized data type."); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unrecognized data type."); return 0; } @@ -429,9 +413,8 @@ namespace XPC { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; double value = XPLMGetDatad(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i", + dref, xdref, value, aircraft); return value; } @@ -439,9 +422,8 @@ namespace XPC { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; float value = XPLMGetDataf(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i", + dref, xdref, value, aircraft); return value; } @@ -449,9 +431,8 @@ namespace XPC { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; int value = XPLMGetDatai(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %i for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %i for a/c %i", + dref, xdref, value, aircraft); return value; } @@ -459,9 +440,8 @@ namespace XPC { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; int resultSize = XPLMGetDatavf(xdref, values, 0, size); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i", + dref, xdref, resultSize, aircraft); return resultSize; } @@ -469,62 +449,66 @@ namespace XPC { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; int resultSize = XPLMGetDatavi(xdref, values, 0, size); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i", + dref, xdref, resultSize, aircraft); return resultSize; } void DataManager::Set(DREF dref, double value, char aircraft) { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Setting DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i", + dref, xdref, value, aircraft); XPLMSetDatad(xdref, value); } void DataManager::Set(DREF dref, float value, char aircraft) { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i", + dref, xdref, value, aircraft); XPLMSetDataf(xdref, value); } void DataManager::Set(DREF dref, int value, char aircraft) { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %i for a/c %i", dref, xdref, value, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %i for a/c %i", + dref, xdref, value, aircraft); XPLMSetDatai(xdref, value); } void DataManager::Set(DREF dref, float values[], int size, char aircraft) { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Set DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i", + dref, xdref, size, aircraft); int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); + if (drefSize < size) + { + Log::FormatLine(LOG_WARN, "DMAN", "Warning: Too many values when setting DREF %i. Expected %i, got %i", + dref, drefSize, size); + } drefSize = min(drefSize, size); XPLMSetDatavf(xdref, values, 0, drefSize); } - + void DataManager::Set(DREF dref, int values[], int size, char aircraft) { const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Setting DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i", + dref, xdref, size, aircraft); int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); + if (drefSize < size) + { + Log::FormatLine(LOG_WARN, "DMAN", "Warning: Too many values when setting DREF %i. Expected %i, got %i", + dref, drefSize, size); + } drefSize = min(drefSize, size); XPLMSetDatavi(xdref, values, 0, drefSize); } - void DataManager::Set(string dref, float values[], int size) + void DataManager::Set(const string& dref, float values[], int size) { XPLMDataRef& xdref = sdrefs[dref]; if (xdref == NULL) @@ -534,149 +518,121 @@ namespace XPC if (!xdref) { // DREF does not exist -#if LOG_VERBOSITY > 0 - Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str()); -#endif + Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str()); return; } if (isnan(values[0])) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Value must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Value must be a number (NaN received)"); return; } XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %s (x:%X) Type: %i", xdref, dataType); if ((dataType & 2) == 2) // Float { XPLMSetDataf(xdref, values[0]); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %f", values[0]); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]); } else if ((dataType & 8) == 8) // Float Array { int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); -#if LOG_VERBOSITY > 1 if (size > drefSize) { - Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); - Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i", + drefSize, size); } -#endif drefSize = min(drefSize, size); XPLMSetDatavf(xdref, values, 0, drefSize); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); } else if ((dataType & 4) == 4) // Double { XPLMSetDatad(xdref, values[0]); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %f", values[0]); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]); } else if ((dataType & 1) == 1) // Integer { XPLMSetDatai(xdref, (int)values[0]); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value was %i", (int)values[0]); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value was %i", (int)values[0]); } else if ((dataType & 16) == 16) // Integer Array { - int iValues[200]; + const std::size_t TMP_SIZE = 200; + int iValues[TMP_SIZE]; int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); -#if LOG_VERBOSITY > 1 if (size > drefSize) { - Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); - Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i", + drefSize, size); } -#endif drefSize = min(drefSize, size); - if (drefSize > 200) + if (drefSize > TMP_SIZE) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size."); - Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize); -#endif - drefSize = 200; + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i", + drefSize, TMP_SIZE); + drefSize = TMP_SIZE; } for (int i = 0; i < drefSize; ++i) { iValues[i] = (int)values[i]; } XPLMSetDatavi(xdref, iValues, 0, drefSize); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); } else if ((dataType & 32) == 32) // Byte Array { - char bValues[1024]; + const std::size_t TMP_SIZE = 1024; + char bValues[TMP_SIZE]; int drefSize = XPLMGetDatab(xdref, NULL, 0, 0); -#if LOG_VERBOSITY > 1 if (size > drefSize) { - Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); - Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i", + drefSize, size); } -#endif drefSize = min(drefSize, size); - if (drefSize > 1024) + if (drefSize > TMP_SIZE) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size."); - Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize); -#endif - drefSize = 1024; + Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size"); + Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i", + drefSize, TMP_SIZE); + drefSize = TMP_SIZE; } for (int i = 0; i < drefSize; ++i) { bValues[i] = (char)values[i]; } XPLMSetDatab(xdref, bValues, 0, drefSize); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[DMAN] -- value count was %i", drefSize); -#endif + Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize); } else { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Unknown type."); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unknown type."); } -#if LOG_VERBOSITY > 1 if (!XPLMCanWriteDataRef(xdref)) { - Log::WriteLine("[DMAN] WARN: dref is not writable. The write operation probably failed."); + Log::WriteLine(LOG_WARN, "DMAN", "WARN: dref is not writable. The write operation probably failed."); } -#endif } void DataManager::SetGear(float gear, bool immediate, char aircraft) { -#if LOG_VERBOSITY > 3 - Log::FormatLine("[DMAN] Setting gear (value:%f, immediate:%i) for aircraft %i", gear, immediate, aircraft); -#endif - if (isnan(gear) || gear < 0 || gear > 1) + Log::FormatLine(LOG_INFO, "DMAN", "Setting gear (value:%f, immediate:%i) for aircraft %i", + gear, immediate, aircraft); + + if ((gear < -8.5 && gear > -9.5) || IsDefault(gear)) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[GEAR] ERROR: Value must be 0 or 1"); -#endif return; } - - if ((gear < -8.5 && gear > -9.5) || (gear < -997.9 && gear > -999.1)) + if (isnan(gear) || gear < 0 || gear > 1) { + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Gear value must be 0 or 1"); return; } @@ -702,26 +658,23 @@ namespace XPC void DataManager::SetPosition(float pos[3], char aircraft) { -#if LOG_VERBOSITY > 3 - Log::FormatLine("[DMAN] Setting position (%f, %f, %f) for aircraft %i", pos[0], pos[1], pos[2], aircraft); -#endif + Log::FormatLine(LOG_INFO, "DMAN", "Setting position (%f, %f, %f) for aircraft %i", + pos[0], pos[1], pos[2], aircraft); if (isnan(pos[0] + pos[1] + pos[2])) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Position must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Position must be a number (NaN received)"); return; } - if (pos[0] < -997.9 && pos[0] > -999.1) + if (IsDefault(pos[0])) { pos[0] = (float)GetDouble(DREF_Latitude, aircraft); } - if (pos[1] < -997.9 && pos[1] > -999.1) + if (IsDefault(pos[1])) { pos[1] = (float)GetDouble(DREF_Longitude, aircraft); } - if (pos[2] < -997.9 && pos[2] > -999.1) + if (IsDefault(pos[2])) { pos[2] = (float)GetDouble(DREF_Elevation, aircraft); } @@ -734,7 +687,6 @@ namespace XPC Set(DREF_LocalY, local[1], aircraft); Set(DREF_LocalZ, local[2], aircraft); // If the sim is unpaused, this will override the above settings. - // TODO: Are these setable when paused? Are these necessary? Set(DREF_Latitude, (double)pos[0], aircraft); Set(DREF_Longitude, (double)pos[1], aircraft); Set(DREF_Elevation, (double)pos[2], aircraft); @@ -742,27 +694,23 @@ namespace XPC void DataManager::SetOrientation(float orient[3], char aircraft) { -#if LOG_VERBOSITY > 3 - Log::FormatLine("[DMAN] Setting orientation (%f, %f, %f) for aircraft %i", + Log::FormatLine(LOG_INFO, "DMAN", "Setting orientation (%f, %f, %f) for aircraft %i", orient[0], orient[1], orient[2], aircraft); -#endif if (isnan(orient[0] + orient[1] + orient[2])) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Orientation must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Orientation must be a number (NaN received)"); return; } - if (orient[0] < -997.9 && orient[0] > -999.1) + if (IsDefault(orient[0])) { orient[0] = GetFloat(DREF_Pitch, aircraft); } - if (orient[1] < -997.9 && orient[1] > -999.1) + if (IsDefault(orient[1])) { orient[1] = GetFloat(DREF_Roll, aircraft); } - if (orient[2] < -997.9 && orient[2] > -999.1) + if (IsDefault(orient[2])) { orient[2] = GetFloat(DREF_HeadingTrue, aircraft); } @@ -778,7 +726,7 @@ namespace XPC { // Convert to Quaternions // from: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane - // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf + // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf float q[4]; float halfRad = 0.00872664625997F; orient[2] = halfRad * orient[2]; @@ -797,14 +745,14 @@ namespace XPC void DataManager::SetFlaps(float value) { + Log::FormatLine(LOG_INFO, "DMAN", "Setting flaps (value:%f)", value); + if (isnan(value)) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DMAN] ERROR: Flap value must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Flap value must be a number (NaN received)"); return; } - if (value < -997.9 && value > -999.1) + if (IsDefault(value)) { return; } @@ -815,4 +763,14 @@ namespace XPC Set(DREF_FlapSetting, value); Set(DREF_FlapActual, value); } -} \ No newline at end of file + + float DataManager::GetDefaultValue() + { + return -998.0F; + } + + bool DataManager::IsDefault(float value) + { + return value < -997.9 && value > -999.1; + } +} diff --git a/xpcPlugin/DataManager.h b/xpcPlugin/DataManager.h index 71787dd..cc38619 100644 --- a/xpcPlugin/DataManager.h +++ b/xpcPlugin/DataManager.h @@ -1,7 +1,7 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_DATAMANAGER_H -#define XPC_DATAMANAGER_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_DATAMANAGER_H_ +#define XPCPLUGIN_DATAMANAGER_H_ #include @@ -62,7 +62,7 @@ namespace XPC DREF_L, DREF_N, - //PQR (Angular Velocities) + // PQR (Angular Velocities) DREF_QRad = 1600, DREF_PRad, DREF_RRad, @@ -105,7 +105,8 @@ namespace XPC // Multiplayer Aircraft DREF_FlapActual2, DREF_Spoiler, - DREF_BrakeSpeed, + DREF_SpeedBrakeSet, + DREF_SpeedBrakeActual, DREF_Sweep, DREF_Slats, @@ -135,13 +136,16 @@ namespace XPC DREF_MP7Alt }; - /// Marshals data between the plugin and X-Plane. + /// Maps X-Plane dataref lines to XPC DREF values. + extern DREF XPData[134][8]; + + /// Contains methods to martial data between the plugin and X-Plane. /// /// \author Jason Watkins - /// \version 1.0.1 + /// \version 1.1 /// \since 1.0.0 /// \date Intial Version: 2015-04-13 - /// \date Last Updated: 2015-04-29 + /// \date Last Updated: 2015-05-14 class DataManager { public: @@ -161,7 +165,7 @@ namespace XPC /// \remarks The first time this method is called for a given dataref, it must /// perform a relatively expensive lookup operation to translate the /// given string into an X-Plane internal pointer. This value is cached, - /// so subsequent calls will incure minimal extra overhead compared to + /// so subsequent calls will incur minimal extra overhead compared to /// the other methods in this class. /// /// \remarks For simplicity, this method is provided with only one output type. @@ -170,7 +174,7 @@ namespace XPC /// doubles where high precision is required, using this method may result /// in a loss of precision. In that case, consider using one of the /// strongly typed methods instead. - static int Get(std::string dref, float values[], int size); + static int Get(const std::string& dref, float values[], int size); /// Gets the value of a double dataref. /// @@ -290,7 +294,7 @@ namespace XPC /// doubles where high precision is required, using this method may result /// in a loss of precision. In that case, consider using one of the /// strongly typed methods instead. - static void Set(std::string dref, float values[], int size); + static void Set(const std::string& dref, float values[], int size); /// Sets the value of a double dataref. /// @@ -405,7 +409,15 @@ namespace XPC /// /// \param value The flaps settings. Should be between 0.0 (no flaps) and 1.0 (full flaps). static void SetFlaps(float value); + + /// Gets a default value that indicates that a dataref should not be changed. + static float GetDefaultValue(); + + /// Checks whether the given value should be treated as a default value. + /// + /// \param value The value to check. + /// \returns true if value is a default value; otherwise false. + static bool IsDefault(float value); }; } - -#endif \ No newline at end of file +#endif diff --git a/xpcPlugin/DataMaps.cpp b/xpcPlugin/DataMaps.cpp deleted file mode 100644 index 977194e..0000000 --- a/xpcPlugin/DataMaps.cpp +++ /dev/null @@ -1,8 +0,0 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#include "DataManager.h" - -namespace XPC -{ - DREF XPData[134][8] = { DREF_None }; -} \ No newline at end of file diff --git a/xpcPlugin/DataMaps.h b/xpcPlugin/DataMaps.h deleted file mode 100644 index ef600b7..0000000 --- a/xpcPlugin/DataMaps.h +++ /dev/null @@ -1,13 +0,0 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_DATAMAPS_H -#define XPC_DATAMAPS_H -#include "DataManager.h" - -namespace XPC -{ - /// Maps X-Plane dataref lines to XPC DREF values. - extern DREF XPData[134][8]; -} - -#endif \ No newline at end of file diff --git a/xpcPlugin/Drawing.cpp b/xpcPlugin/Drawing.cpp index 63caf55..d7539b6 100644 --- a/xpcPlugin/Drawing.cpp +++ b/xpcPlugin/Drawing.cpp @@ -1,19 +1,19 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. // -//X-Plane API -//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -//associated documentation files(the "Software"), to deal in the Software without restriction, -//including without limitation the rights to use, copy, modify, merge, publish, distribute, -//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions : -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Neither the names of the authors nor that of X - Plane or Laminar Research -// may be used to endorse or promote products derived from this software -// without specific prior written permission from the authors or -// Laminar Research, respectively. +// X-Plane API +// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files(the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Neither the names of the authors nor that of X - Plane or Laminar Research +// may be used to endorse or promote products derived from this software +// without specific prior written permission from the authors or +// Laminar Research, respectively. #include "Drawing.h" #include "XPLMDisplay.h" @@ -23,7 +23,7 @@ #include #include #include -//OpenGL includes +// OpenGL includes #if IBM #include #endif @@ -31,11 +31,11 @@ # include #else # include -#endif/*__APPLE__*/ +#endif namespace XPC { - //Internal Structures + // Internal Structures typedef struct { double x; @@ -43,7 +43,7 @@ namespace XPC double z; } LocalPoint; - //Internal Memory + // Internal Memory static const size_t MSG_MAX = 1024; static const size_t MSG_LINE_MAX = MSG_MAX / 16; static bool msgEnabled = false; @@ -64,7 +64,9 @@ namespace XPC XPLMDataRef planeYref; XPLMDataRef planeZref; - //Internal Functions + // Internal Functions + + /// Comparse two size_t integers. Used by qsort in RemoveWaypoints. static int cmp(const void * a, const void * b) { std::size_t sa = *(size_t*)a; @@ -80,36 +82,42 @@ namespace XPC return 0; } + /// Draws a cube centered at the specified OpenGL world coordinates. + /// + /// \param x The X coordinate. + /// \param y The Y coordinate. + /// \param z The Z coordinate. + /// \param d The distance from the player airplane to the center of the cube. static void gl_drawCube(float x, float y, float z, float d) { - //tan(0.25) degrees. Should scale all markers to appear about the same size + // tan(0.25) degrees. Should scale all markers to appear about the same size const float TAN = 0.00436335F; float h = d * TAN; glBegin(GL_QUAD_STRIP); - //Top + // Top glVertex3f(x - h, y + h, z - h); glVertex3f(x + h, y + h, z - h); glVertex3f(x - h, y + h, z + h); glVertex3f(x + h, y + h, z + h); - //Front + // Front glVertex3f(x - h, y - h, z + h); glVertex3f(x + h, y - h, z + h); - //Bottom + // Bottom glVertex3f(x - h, y - h, z - h); glVertex3f(x + h, y - h, z - h); - //Back + // Back glVertex3f(x - h, y + h, z - h); glVertex3f(x + h, y + h, z - h); glEnd(); glBegin(GL_QUADS); - //Left + // Left glVertex3f(x - h, y + h, z - h); glVertex3f(x - h, y + h, z + h); glVertex3f(x - h, y - h, z + h); glVertex3f(x - h, y - h, z - h); - //Right + // Right glVertex3f(x + h, y + h, z + h); glVertex3f(x + h, y + h, z - h); glVertex3f(x + h, y - h, z - h); @@ -118,25 +126,28 @@ namespace XPC glEnd(); } + /// Draws the string set by the TEXT command. static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) { + const int LINE_HEIGHT = 16; XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic); - int y = msgY - 16; + int y = msgY - LINE_HEIGHT; for (size_t i = 0; i < newLineCount; ++i) { XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic); - y -= 16; + y -= LINE_HEIGHT; } return 1; } + /// Draws waypoints. static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) { float px = XPLMGetDataf(planeXref); float py = XPLMGetDataf(planeYref); float pz = XPLMGetDataf(planeZref); - //Convert to local + // Convert to local for (size_t i = 0; i < numWaypoints; ++i) { Waypoint* g = &waypoints[i]; @@ -146,7 +157,7 @@ namespace XPC } - //Draw posts + // Draw posts glColor3f(1.0F, 1.0F, 1.0F); glBegin(GL_LINES); for (size_t i = 0; i < numWaypoints; ++i) @@ -157,7 +168,7 @@ namespace XPC } glEnd(); - //Draw route + // Draw route glColor3f(1.0F, 0.0F, 0.0F); glBegin(GL_LINE_STRIP); for (size_t i = 0; i < numWaypoints; ++i) @@ -167,7 +178,7 @@ namespace XPC } glEnd(); - //Draw markers + // Draw markers glColor3f(1.0F, 1.0F, 1.0F); for (size_t i = 0; i < numWaypoints; ++i) { @@ -181,7 +192,7 @@ namespace XPC return 1; } - //Public Functions + // Public Functions void Drawing::ClearMessage() { XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); @@ -190,8 +201,7 @@ namespace XPC void Drawing::SetMessage(int x, int y, char* msg) { - //Determine size of message and clear instead if the message string - //is empty. + // Determine the size of the message and clear it if it is empty. size_t len = strnlen(msg, MSG_MAX - 1); if (len == 0) { @@ -199,7 +209,7 @@ namespace XPC return; } - //Set the message, location, and mark new lines. + // Set the message, location, and mark new lines. strncpy(msgVal, msg, len + 1); newLineCount = 0; for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i) @@ -213,7 +223,7 @@ namespace XPC msgX = x < 0 ? 10 : x; msgY = y < 0 ? 600 : y; - //Enable drawing if necessary + // Enable drawing if necessary if (!msgEnabled) { XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); @@ -258,7 +268,7 @@ namespace XPC void Drawing::RemoveWaypoints(Waypoint points[], size_t numPoints) { - //Build a list of indices of waypoints we should delete. + // Build a list of indices of waypoints we should delete. size_t delPoints[WAYPOINT_MAX]; size_t delPointsCur = 0; for (size_t i = 0; i < numPoints; ++i) @@ -276,10 +286,10 @@ namespace XPC } } } - //Sort the indices so that we only have to iterate them once + // Sort the indices so that we only have to iterate them once qsort(delPoints, delPointsCur, sizeof(size_t), cmp); - //Copy the new array on top of the old array + // Copy the new array on top of the old array size_t copyCur = 0; size_t count = delPointsCur; delPointsCur = 0; diff --git a/xpcPlugin/Drawing.h b/xpcPlugin/Drawing.h index 0187810..139c1d2 100644 --- a/xpcPlugin/Drawing.h +++ b/xpcPlugin/Drawing.h @@ -1,7 +1,7 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_DRAWING_H -#define XPC_DRAWING_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_DRAWING_H_ +#define XPCPLUGIN_DRAWING_H_ #include @@ -57,5 +57,4 @@ namespace XPC static void RemoveWaypoints(Waypoint points[], size_t numPoints); }; } - -#endif \ No newline at end of file +#endif diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp index c08de8d..a9898b4 100644 --- a/xpcPlugin/Log.cpp +++ b/xpcPlugin/Log.cpp @@ -1,97 +1,158 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. #include "Log.h" -#include -#include -#include -// Implementation note: I initial wrote this class using C++ iostreams, but I couldn't find any +#include "XPLMUtilities.h" + +#include +#include +#include + +#include +#include +#include + +// Implementation note: I initially wrote this class using C++ iostreams, but I couldn't find any // way to implement FormatLine without adding in a call to sprintf. It therefore seems more -// efficient to me to just use C-style IO and call fprintf directly. +// efficient to me to just use C-style IO and call std::fprintf directly. namespace XPC { + static std::FILE* fd; static void WriteTime(FILE* fd) { - time_t rawtime; - tm* timeinfo; - time(&rawtime); - timeinfo = localtime(&rawtime); + using namespace std::chrono; - char buffer[16] = { 0 }; - // Format is equivalent to [%F %T], but neither of those specifiers is - // supported on Windows as of Visual Studio 13 - strftime(buffer, 16, "[%H:%M:%S] ", timeinfo); - - fprintf(fd, buffer); + system_clock::time_point now = system_clock::now(); + std::time_t now_tt = system_clock::to_time_t(now); + system_clock::time_point now_sec = system_clock::from_time_t(now_tt); + milliseconds ms = duration_cast(now - now_sec); + std::tm * tm = std::localtime(&now_tt); + + std::stringstream ss; + ss << std::setfill('0') + << std::setw(2) << tm->tm_hour << ":" + << std::setw(2) << tm->tm_min << ":" + << std::setw(2) << tm->tm_sec << "." + << std::setw(3) << ms.count() << "|"; + + std::fprintf(fd, ss.str().c_str()); } - void Log::Initialize(std::string version) + static void WriteLevel(FILE* fd, int level) { - FILE* fd = fopen("XPCLog.txt", "w"); + char* str; + switch (level) + { + case LOG_OFF: + str = " OFF|"; + break; + case LOG_FATAL: + str = "FATAL|"; + break; + case LOG_ERROR: + str = "ERROR|"; + break; + case LOG_WARN: + str = " WARN|"; + break; + case LOG_INFO: + str = " INFO|"; + break; + case LOG_DEBUG: + str = "DEBUG|"; + break; + case LOG_TRACE: + str = "TRACE|"; + break; + default: + str = " UNK|"; + break; + } + std::fprintf(fd, str); + } + + void Log::Initialize(const std::string& version) + { + if (LOG_LEVEL == LOG_OFF) + { + return; + } + + // Note: Mode "w" deletes an existing file with the same name. This means that we only + // ever get the log from the last run. This matches the way that X-Plane treats its + // log. + fd = std::fopen("XPCLog.txt", "w"); if (fd != NULL) { - time_t rawtime; - tm* timeinfo; - time(&rawtime); - timeinfo = localtime(&rawtime); + std::time_t rawtime; + std::tm* timeinfo; + std::time(&rawtime); + timeinfo = std::localtime(&rawtime); char timeStr[16] = { 0 }; - // Format is equivalent to %F, but neither of those specifiers is - // supported on Windows as of Visual Studio 13 - strftime(timeStr, 16, "%Y-%m-%d", timeinfo); + std::strftime(timeStr, 16, "%Y-%m-%d", timeinfo); - fprintf(fd, "X-Plane Connect [Version %s]\n", version.c_str()); - fprintf(fd, "Compiled %s %s\n", __DATE__, __TIME__); - fprintf(fd, "Copyright (c) 2013-2015 United States Government as represented by the\n"); - fprintf(fd, "Administrator of the National Aeronautics and Space Administration.\n"); - fprintf(fd, "All Rights Reserved.\n\n"); + std::fprintf(fd, "X-Plane Connect [Version %s]\n", version.c_str()); + std::fprintf(fd, "Compiled %s %s\n", __DATE__, __TIME__); + std::fprintf(fd, "Copyright (c) 2013-2015 United States Government as represented by the\n"); + std::fprintf(fd, "Administrator of the National Aeronautics and Space Administration.\n"); + std::fprintf(fd, "All Rights Reserved.\n\n"); - fprintf(fd, "This file contains debugging information about the X-Plane Connect plugin.\n"); - fprintf(fd, "If you have technical issues with the plugin, please report them by opening\n"); - fprintf(fd, "an issue on GitHub (https://github.com/nasa/XPlaneConnect/issues) or by\n"); - fprintf(fd, "emailing Christopher Teubert (christopher.a.teubert@nasa.gov).\n\n"); + std::fprintf(fd, "This file contains debugging information about the X-Plane Connect plugin.\n"); + std::fprintf(fd, "If you have technical issues with the plugin, please report them by opening\n"); + std::fprintf(fd, "an issue on GitHub (https://github.com/nasa/XPlaneConnect/issues) or by\n"); + std::fprintf(fd, "emailing Christopher Teubert (christopher.a.teubert@nasa.gov).\n\n"); - fprintf(fd, "Log file generated on %s.\n", timeStr); - fclose(fd); + int xpVer; + int xplmVer; + XPLMHostApplicationID hostID; + XPLMGetVersions(&xpVer, &xplmVer, &hostID); + std::fprintf(fd, "X-Plane Version: %d\n", xpVer); + std::fprintf(fd, "Plugin Manager Version: %d\n", xplmVer); + std::fprintf(fd, "Host Application ID: %d\n", hostID); + std::fprintf(fd, "Log file generated on %s.\n", timeStr); + std::fflush(fd); } } - void Log::WriteLine(const std::string& value) + void Log::Close() { - Log::WriteLine(value.c_str()); + if (fd) + { + std::fclose(fd); + } } - void Log::WriteLine(const char* value) + void Log::WriteLine(int level, const std::string& tag, const std::string& value) { - FILE* fd = fopen("XPCLog.txt", "a"); - if (!fd) + if (level > LOG_LEVEL || !fd) { return; } WriteTime(fd); - fprintf(fd, "%s\n", value); - - fclose(fd); + WriteLevel(fd, level); + std::fprintf(fd, "%s|%s\n", tag.c_str(), value.c_str()); + std::fflush(fd); } - void Log::FormatLine(const char* format, ...) + void Log::FormatLine(int level, const std::string& tag, std::string format, ...) { va_list args; - FILE* fd = fopen("XPCLog.txt", "a"); - if (!fd) + if (level > LOG_LEVEL || !fd) { return; } va_start(args, format); WriteTime(fd); - vfprintf(fd, format, args); - fprintf(fd, "\n"); - - fclose(fd); + WriteLevel(fd, level); + std::fprintf(fd, "%s|", tag.c_str()); + std::vfprintf(fd, format.c_str(), args); + std::fprintf(fd, "\n"); + std::fflush(fd); va_end(args); } -} \ No newline at end of file +} diff --git a/xpcPlugin/Log.h b/xpcPlugin/Log.h index a7c941e..018bf6c 100644 --- a/xpcPlugin/Log.h +++ b/xpcPlugin/Log.h @@ -1,21 +1,28 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_LOG_H -#define XPC_LOG_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_LOG_H_ +#define XPCPLUGIN_LOG_H_ #include // LOG_VERBOSITY determines the level of logging throughout the plugin. -// 0: Minimum logging. Only plugin manager events will be logged. -// 1: Critical errors. When an error that prevents correct operation of the -// plugin, attempt to write useful information to the log. Note that since -// XPC runs inside the X-Plane executable, we try very hard no to crash. -// As a result, these log messages may be the only indication of failure. -// 2: All errors. Any time something unexpected happens, log it. -// 3: Significant actions. Any time something happens outside of normal -// command processing, log it. -// 5: Everything. Log nearly every single action the plugin takes. This may -// have a detrimental impact on X-Plane performance. -#define LOG_VERBOSITY 2 +// OFF: No logging at all will be performed. +// FATAL: Critical errors that would normally result in termination of the program. Because XPC +// operates in the X-Plane process, we try to never actually crash. As a result, we this +// level of logging may be the only indication of a problem. +// ERROR: All errors not covered by FATAL +// WARN: Potentially, but not definitely, incorrect behavior +// INFO: Information about normal actions taken by the plugin. +// DEBUG: More verbose information usefull for debugging. +// TRACE: Log all the things! +#define LOG_OFF 0 +#define LOG_FATAL 1 +#define LOG_ERROR 2 +#define LOG_WARN 3 +#define LOG_INFO 4 +#define LOG_DEBUG 5 +#define LOG_TRACE 6 + +#define LOG_LEVEL LOG_TRACE namespace XPC { @@ -23,37 +30,37 @@ namespace XPC /// /// \details Provides functions to write lines to the XPC log file. /// \author Jason Watkins - /// \version 1.0 + /// \version 1.1 /// \since 1.0 /// \date Intial Version: 2015-04-09 - /// \date Last Updated: 2015-04-09 + /// \date Last Updated: 2015-05-11 class Log { public: /// Initializes the logging component by deleting old log files, /// writing header information to the log file. - static void Initialize(std::string header); + static void Initialize(const std::string& header); - /// Writes the C string pointed to by format, followed by a line + /// Closes the log file. + static void Close(); + + /// Writes the string pointed to by format, followed by a line /// terminator to the XPC log file. If format contains format /// specifiers, additional arguments following format will be formatted /// and inserted in the resulting string, replacing their respective /// specifiers. /// /// \param format The format string appropriate for consumption by sprintf. - static void FormatLine(const char* format, ...); + /// + /// \remarks Note that Visual C++ silently fails va_start when the last non-varargs + /// argument is a reference, so we need a value-type format here. + static void FormatLine(int level, const std::string& tag, const std::string format, ...); /// Writes the specified string value, followed by a line terminator /// to the XPC log file. /// /// \param value The value to write. - static void WriteLine(const std::string& value); - - /// Writes the specified C string value, followed by a line terminator - /// to the XPC log file. - /// - /// \param value The value to write. - static void WriteLine(const char* value); + static void WriteLine(int level, const std::string& tag, const std::string& value); }; } #endif diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp index 0fb081a..182a274 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -1,5 +1,5 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. #include "Message.h" #include "Log.h" @@ -11,56 +11,49 @@ namespace XPC { Message::Message() {} - Message Message::ReadFrom(UDPSocket& sock) + Message Message::ReadFrom(const UDPSocket& sock) { Message m; int len = sock.Read(m.buffer, bufferSize, &m.source); m.size = len < 0 ? 0 : len; + if (len > 0) + { + Log::FormatLine(LOG_TRACE, "MESG", "Read message with length %i", len); + } return m; } - - unsigned long Message::GetMagicNumber() + + unsigned long Message::GetMagicNumber() const { - if (size < 4) - { - return 0; - } - return *((unsigned long*)buffer); + unsigned long val = size < 4 ? 0 : *((unsigned long*)buffer); + return val; } - std::string Message::GetHead() + std::string Message::GetHead() const { - if (size < 4) - { - return ""; - } - return std::string((char*)buffer, 4); + std::string val = size < 4 ? "" : std::string((char*)buffer, 4); + return val; } - const unsigned char* Message::GetBuffer() + const unsigned char* Message::GetBuffer() const { - if (size == 0) - { - return NULL; - } - return buffer; + const unsigned char* val = size == 0 ? NULL : buffer; + return val; } - std::size_t Message::GetSize() + std::size_t Message::GetSize() const { return size; } - struct sockaddr Message::GetSource() + struct sockaddr Message::GetSource() const { return source; } - void Message::PrintToLog() + void Message::PrintToLog() const { -#if LOG_VERBOSITY > 4 std::stringstream ss; - ss << "[DEBUG]"; // Dump raw bytes to string ss << std::hex << std::setfill('0'); @@ -68,18 +61,17 @@ namespace XPC { ss << ' ' << std::setw(2) << static_cast(buffer[i]); } - Log::WriteLine(ss.str()); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - ss << std::dec; ss.str(""); - ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; + ss << "Head: " << GetHead() << "(0x" << std::setw(8) << GetMagicNumber() << ")" << std::dec << " Size: " << GetSize(); switch (GetMagicNumber()) // Binary version of head { case 0x4E4EF443: // CONN case 0x54505957: // WYPT case 0x54584554: // TEXT { - Log::WriteLine(ss.str()); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } case 0x4C525443: // CTRL @@ -98,7 +90,7 @@ namespace XPC } ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; - Log::WriteLine(ss.str()); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } case 0x41544144: // DATA @@ -108,45 +100,46 @@ namespace XPC for (int i = 0; i < numCols; ++i) { values[i][0] = buffer[5 + 36 * i]; - memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); + std::memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); } - ss << " (" << numCols << " lines)"; - Log::WriteLine(ss.str()); + ss << " (" << numCols << " lines)"; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); for (int i = 0; i < numCols; ++i) { ss.str(""); - ss << "\t#" << values[i][0]; + ss << " #" << values[i][0]; for (int j = 1; j < 9; ++j) { ss << " " << values[i][j]; - } - Log::WriteLine(ss.str()); + } + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } break; } case 0x46455244: // DREF - { - Log::WriteLine(ss.str()); + { + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); std::string dref((char*)buffer + 6, buffer[5]); - Log::FormatLine("-\tDREF (size %i) = %s", dref.length(), dref.c_str()); + Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str()); ss.str(""); int values = buffer[6 + buffer[5]]; - ss << "\tValues(size " << values << ") ="; + ss << " Values(size " << values << ") ="; for (int i = 0; i < values; ++i) { ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i)); - } - Log::WriteLine(ss.str()); + } + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } case 0x44544547: // GETD - { - Log::WriteLine(ss.str()); + { + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); int cur = 6; for (int i = 0; i < buffer[5]; ++i) { std::string dref((char*)buffer + cur + 1, buffer[cur]); - Log::FormatLine("\t#%i/%i (size:%i) %s", i + 1, buffer[5], dref.length(), dref.c_str()); + Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s", + i + 1, buffer[5], dref.length(), dref.c_str()); cur += 1 + buffer[cur]; } break; @@ -157,28 +150,27 @@ namespace XPC float gear = *((float*)(buffer + 30)); float pos[3]; float orient[3]; - memcpy(pos, buffer + 6, 12); - memcpy(orient, buffer + 18, 12); + std::memcpy(pos, buffer + 6, 12); + std::memcpy(orient, buffer + 18, 12); ss << " AC:" << (int)aircraft; ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; - ss << gear; - Log::WriteLine(ss.str()); + ss << gear; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } case 0x554D4953: // SIMU { - ss << ' ' << (int)buffer[5]; - Log::WriteLine(ss.str()); + ss << ' ' << (int)buffer[5]; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } default: { - ss << " UNKNOWN HEADER "; - Log::WriteLine(ss.str()); + ss << " UNKNOWN HEADER "; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); break; } } -#endif } -} \ No newline at end of file +} diff --git a/xpcPlugin/Message.h b/xpcPlugin/Message.h index 3962dc8..095b77d 100644 --- a/xpcPlugin/Message.h +++ b/xpcPlugin/Message.h @@ -1,7 +1,7 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_MESSAGE_H -#define XPC_MESSAGE_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_MESSAGE_H_ +#define XPCPLUGIN_MESSAGE_H_ #include "UDPSocket.h" @@ -10,10 +10,10 @@ namespace XPC /// Represents a message received from an XPC client. /// /// \author Jason Watkins - /// \version 1.0 + /// \version 1.1 /// \since 1.0 /// \date Intial Version: 2015-04-11 - /// \date Last Updated: 2015-04-11 + /// \date Last Updated: 2015-05-11 class Message { public: @@ -24,25 +24,25 @@ namespace XPC /// \returns A message parsed from the data read from sock. If no /// data was read or an error occurs, returns a message /// with the size set to 0. - static Message ReadFrom(UDPSocket& sock); + static Message ReadFrom(const UDPSocket& sock); /// Gets the message header in binary form. - unsigned long GetMagicNumber(); + unsigned long GetMagicNumber() const; /// Gets the message header. - std::string GetHead(); + std::string GetHead() const; /// Gets the buffer underlying the message. - const unsigned char* GetBuffer(); + const unsigned char* GetBuffer() const; /// Gets the size of the message in bytes. - std::size_t GetSize(); + std::size_t GetSize() const; /// Gets the address this message was read from. - struct sockaddr GetSource(); + struct sockaddr GetSource() const; /// Prints the contents of the message to the XPC log. - void PrintToLog(); + void PrintToLog() const; private: Message(); @@ -53,5 +53,4 @@ namespace XPC struct sockaddr source; }; } - -#endif \ No newline at end of file +#endif diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index 784ebee..d72b8a8 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -1,11 +1,26 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +// +// X-Plane API +// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files(the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Neither the names of the authors nor that of X - Plane or Laminar Research +// may be used to endorse or promote products derived from this software +// without specific prior written permission from the authors or +// Laminar Research, respectively. #include "MessageHandlers.h" #include "DataManager.h" -#include "DataMaps.h" #include "Drawing.h" #include "Log.h" +#include "XPLMUtilities.h" + #include #include @@ -20,6 +35,7 @@ namespace XPC void MessageHandlers::SetSocket(UDPSocket* socket) { + Log::WriteLine(LOG_TRACE, "MSGH", "Setting socket"); MessageHandlers::sock = socket; } @@ -27,6 +43,7 @@ namespace XPC { if (handlers.size() == 0) { + Log::WriteLine(LOG_TRACE, "MSGH", "Initializing handlers"); // Common messages handlers.insert(std::make_pair("CONN", MessageHandlers::HandleConn)); handlers.insert(std::make_pair("CTRL", MessageHandlers::HandleCtrl)); @@ -37,8 +54,7 @@ namespace XPC handlers.insert(std::make_pair("SIMU", MessageHandlers::HandleSimu)); handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText)); handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt)); - // Not implemented messages - handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleUnknown)); + handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleView)); // X-Plane data messages handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData)); @@ -66,16 +82,14 @@ namespace XPC std::string head = msg.GetHead(); if (head == "") { + Log::WriteLine(LOG_WARN, "MSGH", "Warning: HandleMessage called with empty message."); return; // No Message to handle } - msg.PrintToLog(); // Set current connection sockaddr sourceaddr = msg.GetSource(); connectionKey = UDPSocket::GetHost(&sourceaddr); -#if LOG_VERBOSITY > 4 - Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str()); -#endif + Log::FormatLine(LOG_INFO, "MSGH", "Handling message from %s", connectionKey.c_str()); std::map::iterator conn = connections.find(connectionKey); if (conn == connections.end()) // New connection { @@ -87,19 +101,17 @@ namespace XPC connection.addr = sourceaddr; connection.getdCount = 0; connections[connectionKey] = connection; -#if LOG_VERBOSITY > 2 - Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str()); -#endif + Log::FormatLine(LOG_DEBUG, "MSGH", "New connection. ID=%u, Remote=%s", + connection.id, connectionKey.c_str()); } else { connection = (*conn).second; -#if LOG_VERBOSITY > 3 - Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s", + Log::FormatLine(LOG_DEBUG, "MSGH", "Existing connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str()); -#endif } - + + msg.PrintToLog(); // Check if there is a handler for this message type. If so, execute // that handler. Otherwise, execute the unknown message handler. std::map::iterator iter = handlers.find(head); @@ -114,7 +126,7 @@ namespace XPC } } - void MessageHandlers::HandleConn(Message& msg) + void MessageHandlers::HandleConn(const Message& msg) { const unsigned char* buffer = msg.GetBuffer(); @@ -123,23 +135,21 @@ namespace XPC sockaddr* sa = &connection.addr; switch (sa->sa_family) { - case AF_INET: + case AF_INET: // IPV4 address { sockaddr_in* sin = reinterpret_cast(sa); (*sin).sin_port = htons(port); break; } - case AF_INET6: + case AF_INET6: // IPV6 addres { sockaddr_in6* sin = reinterpret_cast(sa); (*sin).sin6_port = htons(port); break; } default: -#if LOG_VERBOSITY > 0 - Log::WriteLine("[CONN] ERROR: Unknown address type."); + Log::WriteLine(LOG_ERROR, "CONN", "ERROR: Unknown address type."); return; -#endif } connections.erase(connectionKey); connectionKey = UDPSocket::GetHost(&connection.addr); @@ -150,31 +160,26 @@ namespace XPC response[5] = connection.id; // Update log -#if LOG_VERBOSITY > 1 - Log::FormatLine("[CONN] ID: %u New destination port: %u", + Log::FormatLine(LOG_TRACE, "CONN", "ID: %u New destination port: %u", connection.id, port); -#endif // Send response sock->SendTo(response, 6, &connection.addr); } - void MessageHandlers::HandleCtrl(Message& msg) + void MessageHandlers::HandleCtrl(const Message& msg) { // Update Log -#if LOG_VERBOSITY > 2 - Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "CTRL", "Message Received (Conn %i)", connection.id); const unsigned char* buffer = msg.GetBuffer(); std::size_t size = msg.GetSize(); - //Legacy packets that don't specify an aircraft number should be 26 bytes long. - //Packets specifying an A/C num should be 27 bytes. - if (size != 26 && size != 27) + // Legacy packets that don't specify an aircraft number should be 26 bytes long. + // Packets specifying an A/C num should be 27 bytes. Packets specifying a speedbrake + // should be 31 bytes. + if (size != 26 && size != 27 && size != 31) { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size); -#endif + Log::FormatLine(LOG_ERROR, "CTRL", "ERROR: Unexpected message length (%i)", size); return; } @@ -182,41 +187,63 @@ namespace XPC float pitch = *((float*)(buffer + 5)); float roll = *((float*)(buffer + 9)); float yaw = *((float*)(buffer + 13)); - float thr = *((float*)(buffer + 17)); + float throttle = *((float*)(buffer + 17)); char gear = buffer[21]; float flaps = *((float*)(buffer + 22)); - unsigned char aircraft = 0; - if (size == 27) + unsigned char aircraftNumber = 0; + if (size >= 27) { - aircraft = buffer[26]; + aircraftNumber = buffer[26]; + } + float spdbrk = DataManager::GetDefaultValue(); + if (size >= 31) + { + spdbrk = *((float*)(buffer + 27)); } - float thrArray[8]; - for (int i = 0; i < 8; ++i) - { - thrArray[i] = thr; - } - DataManager::Set(DREF_YokePitch, pitch, aircraft); - DataManager::Set(DREF_YokeRoll, roll, aircraft); - DataManager::Set(DREF_YokeHeading, yaw, aircraft); - DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft); - DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft); - if (aircraft == 0) + if (!DataManager::IsDefault(pitch)) { - DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", thrArray, 1); + DataManager::Set(DREF_YokePitch, pitch, aircraftNumber); + } + if (!DataManager::IsDefault(roll)) + { + DataManager::Set(DREF_YokeRoll, roll, aircraftNumber); + } + if (!DataManager::IsDefault(yaw)) + { + DataManager::Set(DREF_YokeHeading, yaw, aircraftNumber); + } + if (!DataManager::IsDefault(throttle)) + { + + float throttleArray[8]; + for (int i = 0; i < 8; ++i) + { + throttleArray[i] = throttle; + } + DataManager::Set(DREF_ThrottleSet, throttleArray, 8, aircraftNumber); + DataManager::Set(DREF_ThrottleActual, throttleArray, 8, aircraftNumber); + if (aircraftNumber == 0) + { + DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", throttleArray, 1); + } } if (gear != -1) { - DataManager::SetGear(gear, false, aircraft); + DataManager::SetGear(gear, false, aircraftNumber); } - if (flaps < -999.5 || flaps > -997.5) + if (!DataManager::IsDefault(flaps)) { - DataManager::Set(DREF_FlapSetting, flaps, aircraft); + DataManager::Set(DREF_FlapSetting, flaps, aircraftNumber); + } + if (!DataManager::IsDefault(spdbrk)) + { + DataManager::Set(DREF_SpeedBrakeSet, spdbrk, aircraftNumber); } } - void MessageHandlers::HandleData(Message& msg) + void MessageHandlers::HandleData(const Message& msg) { // Parse data const unsigned char* buffer = msg.GetBuffer(); @@ -224,59 +251,51 @@ namespace XPC std::size_t numCols = (size - 5) / 36; if (numCols > 0) { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "DATA", "Message Received (Conn %i)", connection.id); } else { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_WARN, "DATA", "WARNING: Empty data packet received (Conn %i)", connection.id); return; } if (numCols > 134) // Error. Will overflow values { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[DATA] ERROR: numCols to large."); -#endif + Log::FormatLine(LOG_ERROR, "DATA", "ERROR: numCols to large."); return; } float values[134][9]; for (int i = 0; i < numCols; ++i) { + // 5 byte header + (9 * 4 = 36) bytes per row values[i][0] = buffer[5 + 36 * i]; memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); } // Update log - float savedAlpha = -998; - float savedHPath = -998; + float savedAlpha = DataManager::GetDefaultValue(); + float savedHPath = DataManager::GetDefaultValue(); for (int i = 0; i < numCols; ++i) { unsigned char dataRef = (unsigned char)values[i][0]; if (dataRef >= 134) { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[DATA] ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef); -#endif + Log::FormatLine(LOG_ERROR, "DATA", "ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef); continue; } switch (dataRef) { + // TODO(jason-watkins): This currently overwrites the velocity several times. Should look into making this case smarter somehow. case 3: // Velocity { float theta = DataManager::GetFloat(DREF_Pitch); - float alpha = savedAlpha != -998 ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack); - float hpath = savedHPath != -998 ? savedHPath : DataManager::GetFloat(DREF_HPath); + float alpha = DataManager::IsDefault(savedAlpha) ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack); + float hpath = DataManager::IsDefault(savedHPath) ? savedHPath : DataManager::GetFloat(DREF_HPath); if (alpha != alpha || hpath != hpath) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)"); break; } const float deg2rad = 0.0174532925F; @@ -284,7 +303,7 @@ namespace XPC for (int j = 0; j < 3; ++j) { float v = values[i][ind[j]]; - if (v != -998) + if (!DataManager::IsDefault(v)) { DataManager::Set(DREF_LocalVX, v*cos((theta - alpha)*deg2rad)*sin(hpath*deg2rad)); DataManager::Set(DREF_LocalVY, v*sin((theta - alpha)*deg2rad)); @@ -295,27 +314,25 @@ namespace XPC } case 17: // Orientation { - float orient[3]; - orient[0] = values[i][1]; - orient[1] = values[i][2]; - orient[2] = values[i][3]; - DataManager::SetOrientation(orient); + float orientation[3]; + orientation[0] = values[i][1]; + orientation[1] = values[i][2]; + orientation[2] = values[i][3]; + DataManager::SetOrientation(orientation); break; } case 18: // Alpha, hpath etc. { if (values[i][1] != values[i][1] || values[i][3] != values[i][3]) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)"); break; } - if (values[i][1] != -998) + if (!DataManager::IsDefault(values[i][1])) { savedAlpha = values[i][1]; } - if (values[i][3] != -998) + if (DataManager::IsDefault(values[i][3])) { savedHPath = values[i][3]; } @@ -334,17 +351,15 @@ namespace XPC { if (values[i][1] != values[i][1]) { -#if LOG_VERBOSITY > 0 - Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)"); -#endif + Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)"); break; } - float thr[8]; + float throttle[8]; for (int j = 0; j < 8; ++j) { - thr[j] = values[i][1]; + throttle[j] = values[i][1]; } - DataManager::Set(DREF_ThrottleSet, thr, 8); + DataManager::Set(DREF_ThrottleSet, throttle, 8); break; } default: // Non-Special dataRefs @@ -353,10 +368,8 @@ namespace XPC memcpy(line, values[i] + 1, 8 * sizeof(float)); for (int j = 0; j < 8; ++j) { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[DATA] Setting Dataref %i.%i to %f", dataRef, j, line[j]); -#endif - + Log::FormatLine(LOG_ERROR, "DATA", "Setting Dataref %i.%i to %f", dataRef, j, line[j]); + // TODO(jason-watkins): Why is this a special case? if (dataRef == 14 && j == 0) { DataManager::SetGear(line[0], true); @@ -366,7 +379,7 @@ namespace XPC DREF dref = XPData[dataRef][j]; if (dref == DREF_None) { - // TODO: Send single line instead! + // TODO(jason): Send single line instead! HandleXPlaneData(msg); } else @@ -379,46 +392,59 @@ namespace XPC } } - void MessageHandlers::HandleDref(Message& msg) + void MessageHandlers::HandleDref(const Message& msg) { + Log::FormatLine(LOG_TRACE, "DREF", "Request to set DREF value received (Conn %i)", connection.id); const unsigned char* buffer = msg.GetBuffer(); - unsigned char len = buffer[5]; - std::string dref = std::string((char*)buffer + 6, len); + std::size_t size = msg.GetSize(); + std::size_t pos = 5; + while (pos < size) + { + unsigned char len = buffer[pos++]; + if (pos + len > size) + { + break; + } + std::string dref = std::string((char*)buffer + pos, len); + pos += len; - unsigned char valueCount = buffer[6 + len]; - float* values = (float*)(buffer + 7 + len); + unsigned char valueCount = buffer[pos++]; + if (pos + 4 * valueCount > size) + { + break; + } + float* values = (float*)(buffer + pos); + pos += 4 * valueCount; -#if LOG_VERBOSITY > 1 - Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str()); -#endif - - DataManager::Set(dref, values, valueCount); + DataManager::Set(dref, values, valueCount); + Log::FormatLine(LOG_DEBUG, "DREF", "Set %d values for %s", valueCount, dref.c_str()); + } + if (pos != size) + { + Log::WriteLine(LOG_ERROR, "DREF", "ERROR: Command did not terminate at the expected position."); + } } - void MessageHandlers::HandleGetD(Message& msg) + void MessageHandlers::HandleGetD(const Message& msg) { const unsigned char* buffer = msg.GetBuffer(); unsigned char drefCount = buffer[5]; if (drefCount == 0) // Use last request { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[GETD] DATA Requested: Repeat last request from connection %i (%i data refs)", + Log::FormatLine(LOG_TRACE, "GETD", + "DATA Requested: Repeat last request from connection %i (%i data refs)", connection.id, connection.getdCount); -#endif if (connection.getdCount == 0) // No previous request to use { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[GETD] ERROR: No previous requests from connection %i.", connection.id); -#endif + Log::FormatLine(LOG_ERROR, "GETD", "ERROR: No previous requests from connection %i.", + connection.id); return; } } else // New request { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[GETD] DATA Requested: New Request for connection %i (%i data refs)", + Log::FormatLine(LOG_TRACE, "GETD", "DATA Requested: New Request for connection %i (%i data refs)", connection.id, drefCount); -#endif std::size_t ptr = 6; for (int i = 0; i < drefCount; ++i) { @@ -445,85 +471,96 @@ namespace XPC sock->SendTo(response, cur, &connection.addr); } - void MessageHandlers::HandlePosi(Message& msg) + void MessageHandlers::HandlePosi(const Message& msg) { // Update log -#if LOG_VERBOSITY > 0 - Log::FormatLine("[POSI] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "POSI", "Message Received (Conn %i)", connection.id); const unsigned char* buffer = msg.GetBuffer(); const std::size_t size = msg.GetSize(); if (size < 34) { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[POSI] ERROR: Unexpected size: %i (Expected at least 34)", size); -#endif + Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected at least 34)", size); return; } - char aircraft = buffer[5]; + char aircraftNumber = buffer[5]; float gear = *((float*)(buffer + 30)); float pos[3]; float orient[3]; memcpy(pos, buffer + 6, 12); memcpy(orient, buffer + 18, 12); - if (aircraft > 0) + if (aircraftNumber > 0) { - // Enable AI for the aircraft we are setting + // Enable AI for the aircraftNumber we are setting float ai[20]; std::size_t result = DataManager::GetFloatArray(DREF_PauseAI, ai, 20); if (result == 20) // Only set values if they were retrieved successfully. { - ai[aircraft] = 1; + ai[aircraftNumber] = 1; DataManager::Set(DREF_PauseAI, ai, 0, 20); } } - DataManager::SetPosition(pos, aircraft); - DataManager::SetOrientation(orient, aircraft); + DataManager::SetPosition(pos, aircraftNumber); + DataManager::SetOrientation(orient, aircraftNumber); if (gear != -1) { - DataManager::SetGear(gear, true, aircraft); + DataManager::SetGear(gear, true, aircraftNumber); } } - void MessageHandlers::HandleSimu(Message& msg) + void MessageHandlers::HandleSimu(const Message& msg) { // Update log -#if LOG_VERBOSITY > 0 - Log::FormatLine("[SIMU] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "SIMU", "Message Received (Conn %i)", connection.id); - const unsigned char* buffer = msg.GetBuffer(); - - // Set DREF - int value[20]; - for (int i = 0; i < 20; ++i) + char v = msg.GetBuffer()[5]; + if (v < 0 || v > 2) { - value[i] = buffer[5]; + Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v); + return; } - DataManager::Set(DREF_Pause, value, 20); - -#if LOG_VERBOSITY > 2 - if (buffer[5] == 0) + + int value[20]; + if (v == 2) { - Log::FormatLine("[SIMU] Simulation Resumed (Conn %i)", connection.id); + DataManager::GetIntArray(DREF_Pause, value, 20); + for (int i = 0; i < 20; ++i) + { + value[i] = value[i] ? 0 : 1; + } } else { - Log::FormatLine("[SIMU] Simulation Paused (Conn %i)", connection.id); + for (int i = 0; i < 20; ++i) + { + value[i] = v; + } + } + + // Set DREF + DataManager::Set(DREF_Pause, value, 20); + + switch (v) + { + case 0: + Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed"); + break; + case 1: + Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused"); + break; + case 2: + Log::FormatLine(LOG_INFO, "SIMU", "Simulation switched to %i", value[0]); + break; } -#endif } - void MessageHandlers::HandleText(Message& msg) + void MessageHandlers::HandleText(const Message& msg) { // Update Log -#if LOG_VERBOSITY > 0 - Log::FormatLine("[TEXT] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "TEXT", "Message Received (Conn %i)", connection.id); std::size_t len = msg.GetSize(); const unsigned char* buffer = msg.GetBuffer(); @@ -531,18 +568,14 @@ namespace XPC char text[256] = { 0 }; if (len < 14) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[TEXT] ERROR: Length less than 14 bytes"); -#endif + Log::WriteLine(LOG_ERROR, "TEXT", "ERROR: Length less than 14 bytes"); return; } size_t msgLen = (unsigned char)buffer[13]; if (msgLen == 0) { Drawing::ClearMessage(); -#if LOG_VERBOSITY > 2 - Log::WriteLine("[TEXT] Text cleared"); -#endif + Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text cleared"); } else { @@ -550,18 +583,30 @@ namespace XPC int y = *((int*)(buffer + 9)); strncpy(text, (char*)buffer + 14, msgLen); Drawing::SetMessage(x, y, text); -#if LOG_VERBOSITY > 2 - Log::WriteLine("[TEXT] Text set"); -#endif + Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text set"); } } - void MessageHandlers::HandleWypt(Message& msg) + void MessageHandlers::HandleView(const Message& msg) { // Update Log -#if LOG_VERBOSITY > 0 - Log::FormatLine("[WYPT] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "VIEW", "Message Received(Conn %i)", connection.id); + + const std::size_t size = msg.GetSize(); + if (size != 9) + { + Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9.", size); + return; + } + const unsigned char* buffer = msg.GetBuffer(); + int type = *((int*)(buffer + 5)); + XPLMCommandKeyStroke(type); + } + + void MessageHandlers::HandleWypt(const Message& msg) + { + // Update Log + Log::FormatLine(LOG_TRACE, "WYPT", "Message Received (Conn %i)", connection.id); // Parse data const unsigned char* buffer = msg.GetBuffer(); @@ -578,9 +623,7 @@ namespace XPC } // Perform operation -#if LOG_VERBOSITY > 2 - Log::FormatLine("[WYPT] Performing operation %i", op); -#endif + Log::FormatLine(LOG_INFO, "WYPT", "Performing operation %i", op); switch (op) { case 1: @@ -593,18 +636,14 @@ namespace XPC Drawing::ClearWaypoints(); break; default: -#if LOG_VERBOSITY > 1 - Log::FormatLine("[WYPT] ERROR: %i is not a valid operation.", op); -#endif + Log::FormatLine(LOG_ERROR, "WYPT", "ERROR: %i is not a valid operation.", op); break; } } - void MessageHandlers::HandleXPlaneData(Message& msg) + void MessageHandlers::HandleXPlaneData(const Message& msg) { -#if LOG_VERBOSITY > 1 - Log::WriteLine("[MSGH] Sending raw data to X-Plane"); -#endif + Log::WriteLine(LOG_TRACE, "MSGH", "Sending raw data to X - Plane"); sockaddr_in loopback; loopback.sin_family = AF_INET; loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK); @@ -612,11 +651,8 @@ namespace XPC sock->SendTo(msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback); } - void MessageHandlers::HandleUnknown(Message& msg) + void MessageHandlers::HandleUnknown(const Message& msg) { - // UPDATE LOG -#if LOG_VERBOSITY > 0 - Log::FormatLine("[EXEC] ERROR: Unknown packet type %s", msg.GetHead().c_str()); -#endif + Log::FormatLine(LOG_ERROR, "MSGH", "ERROR: Unknown packet type %s", msg.GetHead().c_str()); } } diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h index 4881dee..13742ee 100644 --- a/xpcPlugin/MessageHandlers.h +++ b/xpcPlugin/MessageHandlers.h @@ -1,7 +1,7 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_MESSAGEHANDLERS_H -#define XPC_MESSAGEHANDLERS_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_MESSAGEHANDLERS_H_ +#define XPCPLUGIN_MESSAGEHANDLERS_H_ #include "Message.h" #include @@ -10,15 +10,15 @@ namespace XPC { /// A function that handles a message. - typedef void(*MessageHandler)(Message&); + typedef void(*MessageHandler)(const Message&); - /// Handles incommming messages and manages connections. + /// Handles incoming messages and manages connections. /// /// \author Jason Watkins - /// \version 1.0 + /// \version 1.1 /// \since 1.0 /// \date Intial Version: 2015-04-12 - /// \date Last Updated: 2015-04-12 + /// \date Last Updated: 2015-05-11 class MessageHandlers { public: @@ -33,21 +33,25 @@ namespace XPC /// \param msg The message to be processed. static void HandleMessage(Message& msg); - /// Sets the socke that message handlers use to send responses. + /// Sets the socket that message handlers use to send responses. static void SetSocket(UDPSocket* socket); private: - static void HandleConn(Message& msg); - static void HandleCtrl(Message& msg); - static void HandleData(Message& msg); - static void HandleDref(Message& msg); - static void HandleGetD(Message& msg); - static void HandlePosi(Message& msg); - static void HandleSimu(Message& msg); - static void HandleText(Message& msg); - static void HandleWypt(Message& msg); - static void HandleXPlaneData(Message& msg); - static void HandleUnknown(Message& msg); + // One handler per message type. Message types are descripbed on the + // wiki at https://github.com/nasa/XPlaneConnect/wiki/Network-Information + static void HandleConn(const Message& msg); + static void HandleCtrl(const Message& msg); + static void HandleData(const Message& msg); + static void HandleDref(const Message& msg); + static void HandleGetD(const Message& msg); + static void HandlePosi(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 HandleXPlaneData(const Message& msg); + static void HandleUnknown(const Message& msg); typedef struct { @@ -64,5 +68,4 @@ namespace XPC static UDPSocket* sock; // Outgoing network socket }; } - -#endif \ No newline at end of file +#endif diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index ed4710d..349f127 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -1,5 +1,5 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. #include "Log.h" #include "UDPSocket.h" @@ -8,41 +8,37 @@ namespace XPC { + const static std::string tag = "SOCK"; + UDPSocket::UDPSocket(unsigned short recvPort) { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[SOCK] Opening socket (port:%d)", recvPort); -#endif + Log::FormatLine(LOG_TRACE, tag, "Opening socket (port:%d)", recvPort); // Setup Port struct sockaddr_in localAddr; localAddr.sin_family = AF_INET; localAddr.sin_addr.s_addr = INADDR_ANY; localAddr.sin_port = htons(recvPort); - - //Create and bind the socket + + // Create and bind the socket #ifdef _WIN32 WSADATA wsa; int startResult = WSAStartup(MAKEWORD(2, 2), &wsa); if (startResult != 0) { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[SOCK] ERROR: WSAStartup failed with error code %i.", startResult); -#endif + Log::FormatLine(LOG_FATAL, tag, "ERROR: WSAStartup failed with error code %i.", startResult); this->sock = ~0; return; } if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { -#if LOG_VERBOSITY > 0 int err = WSAGetLastError(); - Log::FormatLine("[SOCK] ERROR: Failed to open socket. (Error code %i)", err); -#endif + Log::FormatLine(LOG_FATAL, tag, "ERROR: Failed to open socket. (Error code %i)", err); return; } #elif (__APPLE__ || __linux) if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - Log::WriteLine("[SOCK] ERROR: Failed to open socket"); + Log::WriteLine(LOG_FATAL, tag, "ERROR: Failed to open socket"); return; } int optval = 1; @@ -52,39 +48,35 @@ namespace XPC if (bind(this->sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) != 0) { #ifdef _WIN32 -#if LOG_VERBOSITY > 0 int err = WSAGetLastError(); - Log::FormatLine("[SOCK] ERROR: Failed to bind socket. (Error code %i)", err); -#endif + Log::FormatLine(LOG_FATAL, tag, "ERROR: Failed to bind socket. (Error code %i)", err); +#else + Log::WriteLine(LOG_FATAL, tag, "ERROR: Failed to bind socket."); #endif return; } - //Set Timout + // Set Timout int usTimeOut = 500; #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) { -#if LOG_VERBOSITY > 1 int err = WSAGetLastError(); - Log::FormatLine("[SOCK] ERROR: Failed to set timeout. (Error code %i)", err); -#endif + Log::FormatLine(LOG_ERROR, tag, "ERROR: Failed to set timeout. (Error code %i)", err); } #else struct timeval tv; - tv.tv_sec = 0; /* Sec Timeout */ - tv.tv_usec = usTimeOut; // Microsec Timeout + tv.tv_sec = 0; + tv.tv_usec = usTimeOut; setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); #endif } UDPSocket::~UDPSocket() { -#if LOG_VERBOSITY > 1 - Log::FormatLine("[SOCK] Closing socket (%d)", this->sock); -#endif + Log::FormatLine(LOG_TRACE, tag, "Closing socket (%d)", this->sock); #ifdef _WIN32 closesocket(this->sock); #elif (__APPLE__ || __linux) @@ -92,7 +84,7 @@ namespace XPC #endif } - int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr) + int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr) const { socklen_t recvaddrlen = sizeof(*recvAddr); int status = 0; @@ -111,18 +103,16 @@ namespace XPC FD_SET(sock, &stReadFDS); FD_ZERO(&stExceptFDS); FD_SET(sock, &stExceptFDS); - tv.tv_sec = 0; /* Sec Timeout */ - tv.tv_usec = 250; // Microsec Timeout + tv.tv_sec = 0; + tv.tv_usec = 250; // Select Command int result = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv); -#if LOG_VERBOSITY > 1 if (result == SOCKET_ERROR) { int err = WSAGetLastError(); - Log::FormatLine("[SOCK] ERROR: Select failed. (Error code %i)", err); + Log::FormatLine(LOG_ERROR, tag, "ERROR: Select failed. (Error code %i)", err); } -#endif if (result <= 0) // No Data or error { return -1; @@ -137,22 +127,18 @@ namespace XPC return status; } - void UDPSocket::SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) + void UDPSocket::SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) const { if (sendto(sock, (char*)buffer, (int)len, 0, remote, sizeof(*remote)) < 0) { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[SOCK] Send failed. (remote: %s)", GetHost(remote).c_str()); -#endif + Log::FormatLine(LOG_ERROR, tag, "Send failed. (remote: %s)", GetHost(remote).c_str()); } else { -#if LOG_VERBOSITY > 3 - Log::FormatLine("[SOCK] Datagram sent. (remote: %s)", GetHost(remote).c_str()); -#endif + Log::FormatLine(LOG_INFO, tag, "Send failed. (remote: %s)", GetHost(remote).c_str()); } } - + std::string UDPSocket::GetHost(sockaddr* sa) { char ip[INET6_ADDRSTRLEN + 6] = { 0 }; diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h index b6e8e58..5b87439 100644 --- a/xpcPlugin/UDPSocket.h +++ b/xpcPlugin/UDPSocket.h @@ -1,14 +1,14 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. -#ifndef XPC_SOCKET_H -#define XPC_SOCKET_H +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPCPLUGIN_SOCKET_H_ +#define XPCPLUGIN_SOCKET_H_ #include #include #ifdef _WIN32 #include #include -#pragma comment(lib,"ws2_32.lib") //Winsock Library +#pragma comment(lib, "ws2_32.lib") // Winsock Library #elif (__APPLE__ || __linux) #include #include @@ -23,10 +23,10 @@ namespace XPC /// data to XPC clients. /// /// \author Jason Watkins - /// \version 1.0 + /// \version 1.1 /// \since 1.0 /// \date Intial Version: 2015-04-10 - /// \date Last Updated: 2015-04-11 + /// \date Last Updated: 2015-05-11 class UDPSocket { public: @@ -34,7 +34,7 @@ namespace XPC /// specified receive port. /// /// \param recvPort The port on which this instance will receive data. - UDPSocket(unsigned short recvPort); + explicit UDPSocket(unsigned short recvPort); /// Closes the underlying socket for this instance. ~UDPSocket(); @@ -45,17 +45,17 @@ namespace XPC /// \param buffer The array to copy the data into. /// \param size The number of bytes to read. /// \param remoteAddr When at least one byte is read, contains the address - /// of the remote host. + /// of the remote host. /// \returns The number of bytes read, or a negative number if - /// an error occurs. - int Read(unsigned char* buffer, int size, sockaddr* remoteAddr); + /// an error occurs. + int Read(unsigned char* buffer, int size, sockaddr* remoteAddr) const; /// Sends data to the specified remote endpoint. /// /// \param data The data to be sent. /// \param len The number of bytes to send. /// \param remote The destination socket. - void SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote); + void SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) const; /// Gets a string containing the IP address and port contained in the given sockaddr. /// @@ -70,5 +70,4 @@ namespace XPC #endif }; } - -#endif \ No newline at end of file +#endif diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index 3f00db3..c8b4aef 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -1,58 +1,58 @@ -//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the -//National Aeronautics and Space Administration. All Rights Reserved. +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. // -//DISCLAIMERS -// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, -// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT -// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY -// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, -// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN -// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, -// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT -// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING -// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." // -// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES -// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF -// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES -// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING -// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE -// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, -// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE -// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. // -//X-Plane API -//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -//associated documentation files(the "Software"), to deal in the Software without restriction, -//including without limitation the rights to use, copy, modify, merge, publish, distribute, -//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions : -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Neither the names of the authors nor that of X - Plane or Laminar Research -// may be used to endorse or promote products derived from this software -// without specific prior written permission from the authors or -// Laminar Research, respectively. +// X-Plane API +// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +// associated documentation files(the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Neither the names of the authors nor that of X - Plane or Laminar Research +// may be used to endorse or promote products derived from this software +// without specific prior written permission from the authors or +// Laminar Research, respectively. -// X-Plane Connect Plugin +// X-Plane Connect Plugin // -// DESCRIPTION -// XPCPlugin Facilitates Communication to and from the XPlane +// DESCRIPTION +// XPCPlugin Facilitates Communication to and from the XPlane // -// INSTRUCTIONS -// See Readme.md in the root of this repository or the wiki hosted on GitHub at -// https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions, -// and detailed documentation. +// INSTRUCTIONS +// See Readme.md in the root of this repository or the wiki hosted on GitHub at +// https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions, +// and detailed documentation. // -// CONTACT -// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) +// CONTACT +// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) // -// CONTRIBUTORS -// CT: Christopher Teubert (christopher.a.teubert@nasa.gov) -// JW: Jason Watkins (jason.w.watkins@nasa.gov) +// CONTRIBUTORS +// CT: Christopher Teubert (christopher.a.teubert@nasa.gov) +// JW: Jason Watkins (jason.w.watkins@nasa.gov) // XPC Includes #include "DataManager.h" @@ -75,7 +75,8 @@ XPC::UDPSocket* sock = NULL; -double start,lap; +double start; +double lap; static double timeConvert = 0.0; int benchmarkingSwitch = 0; // 1 = time for operations, 2 = time for op + cycle; int cyclesToClear = -1; // Clear message bus every n cycles. -1 == dont clear @@ -93,36 +94,34 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) strcpy(outName, "X-Plane Connect [Version 1.0.1]"); strcpy(outSig, "NASA.XPlaneConnect"); strcpy(outDesc, "X Plane Communications Toolbox\nCopyright (c) 2013-2015 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved."); - + #if (__APPLE__) if ( abs(timeConvert) <= 1e-9 ) // is about 0 { mach_timebase_info_data_t timeBase; - (void)mach_timebase_info( &timeBase ); + (void)mach_timebase_info(&timeBase); timeConvert = (double)timeBase.numer / (double)timeBase.denom / 1000000000.0; } #endif - XPC::Log::Initialize("1.0.1"); - XPC::Log::WriteLine("[EXEC] Plugin Start"); + XPC::Log::Initialize("1.1.0"); + XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Start"); XPC::DataManager::Initialize(); - - float interval = -1; // Call every frame - void* refcon = NULL; // Don't pass anything to the callback directly - XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon); - + return 1; } PLUGIN_API void XPluginStop(void) { - XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL); - XPC::Log::WriteLine("[EXEC] Plugin Shutdown"); + XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Shutdown"); + XPC::Log::Close(); } PLUGIN_API void XPluginDisable(void) { + XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL); + // Close sockets delete sock; sock = NULL; @@ -133,7 +132,7 @@ PLUGIN_API void XPluginDisable(void) // Stop rendering waypoints to screen. XPC::Drawing::ClearWaypoints(); - XPC::Log::WriteLine("[EXEC] Plugin Disabled, sockets closed"); + XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Disabled, sockets closed"); } PLUGIN_API int XPluginEnable(void) @@ -142,15 +141,17 @@ PLUGIN_API int XPluginEnable(void) sock = new XPC::UDPSocket(RECVPORT); XPC::MessageHandlers::SetSocket(sock); - XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened"); + XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Enabled, sockets opened"); if (benchmarkingSwitch > 0) { - XPC::Log::FormatLine("[EXEC] Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch); + XPC::Log::FormatLine(LOG_INFO, "EXEC", "Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch); } -#if LOG_VERBOSITY > 0 - XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY); -#endif + XPC::Log::FormatLine(LOG_INFO, "EXEC", "Debug Logging Enabled (Verbosity: %i)", LOG_LEVEL); + float interval = -1; // Call every frame + void* refcon = NULL; // Don't pass anything to the callback directly + XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon); + return 1; } @@ -172,7 +173,7 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall, counter++; if (benchmarkingSwitch > 1) { - XPC::Log::FormatLine("Cycle time %.6f", inElapsedSinceLastCall); + XPC::Log::FormatLine(LOG_DEBUG, "EXEC", "Cycle time %.6f", inElapsedSinceLastCall); } for (int i = 0; i < OPS_PER_CYCLE; i++) @@ -195,15 +196,15 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall, { #if (__APPLE__) lap = (double)mach_absolute_time( ) * timeConvert; - diff_t = lap-start; - XPC::Log::FormatLine("[BENCH] Runtime %.6f",diff_t); + diff_t = lap - start; + XPC::Log::FormatLine(LOG_INFO, "EXEC", "Runtime %.6f", diff_t); #endif } } if (cyclesToClear != -1 && counter%cyclesToClear == 0) { - XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); + XPC::Log::WriteLine(LOG_DEBUG, "EXEC", "Cleared UDP Buffer"); delete sock; sock = new XPC::UDPSocket(RECVPORT); } diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index 88b4c11..a27a63b 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/mac.xpl b/xpcPlugin/XPlaneConnect/mac.xpl index 0415e6b..da391d2 100755 Binary files a/xpcPlugin/XPlaneConnect/mac.xpl and b/xpcPlugin/XPlaneConnect/mac.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index 30b6838..ce96503 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ diff --git a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj index 4bca25c..4dbd663 100755 --- a/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj +++ b/xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 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 */; }; - BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */; }; BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */; }; BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD311AE041A3007BA7DA /* Log.cpp */; }; BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD331AE041A3007BA7DA /* Message.cpp */; }; @@ -40,8 +39,6 @@ BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XPCPlugin.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataManager.cpp; sourceTree = ""; }; BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = ""; }; - BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataMaps.cpp; sourceTree = ""; }; - BEABAD2E1AE041A3007BA7DA /* DataMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataMaps.h; sourceTree = ""; }; BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drawing.cpp; sourceTree = ""; }; BEABAD301AE041A3007BA7DA /* Drawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Drawing.h; sourceTree = ""; }; BEABAD311AE041A3007BA7DA /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = ""; }; @@ -82,7 +79,6 @@ BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */, BEDC620218EDF1A7005DB364 /* xplaneConnect.c */, BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */, - BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */, BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */, BEABAD311AE041A3007BA7DA /* Log.cpp */, BEABAD331AE041A3007BA7DA /* Message.cpp */, @@ -97,7 +93,6 @@ children = ( BEDC620318EDF1A7005DB364 /* xplaneConnect.h */, BEABAD2C1AE041A3007BA7DA /* DataManager.h */, - BEABAD2E1AE041A3007BA7DA /* DataMaps.h */, BEABAD301AE041A3007BA7DA /* Drawing.h */, BEABAD321AE041A3007BA7DA /* Log.h */, BEABAD341AE041A3007BA7DA /* Message.h */, @@ -192,7 +187,6 @@ BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */, BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */, BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */, - BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */, BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */, BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */, BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */, diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index 73e1302..70c01f1 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -201,7 +201,6 @@ - @@ -210,7 +209,6 @@ - diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters index e921a5d..e62f3d6 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters @@ -30,9 +30,6 @@ Header Files - - Header Files - Header Files @@ -59,9 +56,6 @@ Source Files - - Source Files -