diff --git a/C/monitorExample/main.c b/C/monitorExample/main.c new file mode 100644 index 0000000..b9f7c43 --- /dev/null +++ b/C/monitorExample/main.c @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +// +// DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "../src/xplaneConnect.h" +#include +#include "stdio.h" + +#ifdef WIN32 +HANDLE hStdIn = NULL; +INPUT_RECORD buffer; +int waitForInput() +{ + if (hStdIn == NULL) + { + hStdIn = GetStdHandle(STD_INPUT_HANDLE); + } + FlushConsoleInputBuffer(hStdIn); + DWORD result = WaitForSingleObject(hStdIn, 100); + if (result == WAIT_OBJECT_0) + { + DWORD eventsRead; + PeekConsoleInput(hStdIn, &buffer, 1, &eventsRead); + if (eventsRead > 0) + { + return buffer.EventType == KEY_EVENT; + } + } + return FALSE; +} +#else +int fdstdin = 0; +fd_set fds; +struct timeval tv; + +int waitForInput() +{ + + FD_ZERO(&fds); + FD_SET(fdstdin, &fds); + select(1, &fds, NULL, NULL, &tv); + return FD_ISSET(fdstdin, &fds); +} +#endif + +int main(void) +{ + XPCSocket client = openUDP("127.0.0.1"); + const int aircraftNum = 0; + tv.tv_usec = 100 * 1000; + while (1) + { + float posi[7]; + int result = getPOSI(client, posi, aircraftNum); + if (result < 0) // Error in getPOSI + { + break; + } + + float ctrl[7]; + result = getCTRL(client, ctrl, aircraftNum); + if (result < 0) // Error in getCTRL + { + break; + } + + printf("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n", + posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]); + + // Check if any key has been pressed and break + if (waitForInput()) + { + break; + } + } + + + printf("\n\nPress Any Key to exit..."); + getchar(); + return 0; +} \ No newline at end of file diff --git a/C/monitorExample/monitorExample.sln b/C/monitorExample/monitorExample.sln new file mode 100644 index 0000000..11435f7 --- /dev/null +++ b/C/monitorExample/monitorExample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitorExample", "monitorExample.vcxproj", "{0F45204B-6910-46C7-BECD-273985390F75}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.Build.0 = Debug|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.ActiveCfg = Release|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/C/monitorExample/monitorExample.vcxproj b/C/monitorExample/monitorExample.vcxproj new file mode 100644 index 0000000..3be8e30 --- /dev/null +++ b/C/monitorExample/monitorExample.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0F45204B-6910-46C7-BECD-273985390F75} + monitorExample + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + ../src;$(IncludePath) + + + ../src;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/C/monitorExample/monitorExample.vcxproj.filters b/C/monitorExample/monitorExample.vcxproj.filters new file mode 100644 index 0000000..50a53c3 --- /dev/null +++ b/C/monitorExample/monitorExample.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/C/playbackExample/src/chrome.c b/C/playbackExample/src/chrome.c new file mode 100644 index 0000000..652de96 --- /dev/null +++ b/C/playbackExample/src/chrome.c @@ -0,0 +1,71 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "chrome.h" + +#include + +void displayStart(char* version) +{ + printf("X-Plane Connect Playback Example [Version %s]\n", version); + printf("(c) 2013-2015 United States Government as represented by the Administrator\n"); + printf("of the National Aeronautics and Space Administration. All Rights Reserved.\n"); +} + +int displayMenu(char* title, char* opts[], size_t count) +{ + printf("\n"); + printf("+---------------------------------------------- +\n"); + printf("| %-42s |\n", title); + printf("+---------------------------------------------- +\n"); + for (size_t i = 0; i < count; i++) + { + printf("| %2u. %-40s |\n", i + 1, opts[i]); + } + printf("+---------------------------------------------- +\n"); + printf("Please select an option: "); + + int opt; + scanf("%d", &opt); + return opt; +} + +void displayMsg(char* msg) +{ + printf("%s\n", msg); +} + +void getString(char* prompt, char buffer[255]) +{ + printf("%s: ", prompt); + scanf("%255s", buffer); +} + +int getInt(char* prompt) +{ + printf("%s: ", prompt); + int result; + scanf("%d", &result); + return result; +} \ No newline at end of file diff --git a/C/playbackExample/src/chrome.h b/C/playbackExample/src/chrome.h new file mode 100644 index 0000000..d0da842 --- /dev/null +++ b/C/playbackExample/src/chrome.h @@ -0,0 +1,40 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#ifndef XPC_PLAYBACKEX_CHROME_H_ +#define XPC_PLAYBACKEX_CHROME_H_ + +#include + +void displayStart(char* version); + +int displayMenu(char* title, char* opts[], size_t count); + +void displayMsg(char* msg); + +void getString(char* prompt, char buffer[255]); + +int getInt(char* prompt); + +#endif \ No newline at end of file diff --git a/C/playbackExample/src/main.c b/C/playbackExample/src/main.c new file mode 100644 index 0000000..c796edd --- /dev/null +++ b/C/playbackExample/src/main.c @@ -0,0 +1,70 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "chrome.h" +#include "playback.h" + +int main(void) +{ + char* mainOpts[3] = + { + "Record X-Plane", + "Playback File", + "Exit" + }; + char path[256] = { 0 }; + + displayStart("1.2.0.0"); + while (1) + { + switch (displayMenu("What would you like to do?", mainOpts, 3)) + { + case 1: + { + getString("Enter save file path", path); + int interval = getInt("Enter interval between frames (milliseconds)"); + int duration = getInt("Enter duration to record for (seconds)"); + + record(path, interval, duration); + break; + } + case 2: + { + getString("Enter path to saved playback file", path); + int interval = getInt("Enter interval between frames (milliseconds)"); + + playback(path, interval); + break; + } + case 3: + displayMsg("Exiting."); + return 0; + default: + displayMsg("Unrecognized option."); + break; + } + } + + return 0; +} \ No newline at end of file diff --git a/C/playbackExample/src/playback.c b/C/playbackExample/src/playback.c new file mode 100644 index 0000000..75ad523 --- /dev/null +++ b/C/playbackExample/src/playback.c @@ -0,0 +1,106 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "playback.h" + +#include "chrome.h" + +#include "xplaneConnect.h" + +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +void playbackSleep(int ms) +{ +#ifdef WIN32 + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + +void record(char* path, int interval, int duration) +{ + FILE* fd = fopen(path, "w"); + int count = duration * 1000 / interval; + if (!fd) + { + displayMsg("Unable to open output file."); + return; + } + if (count < 1) + { + displayMsg("Duration is less than one iteration."); + return; + } + displayMsg("Recording..."); + + XPCSocket sock = openUDP("127.0.0.1"); + for (int i = 0; i < count; ++i) + { + float posi[7]; + int result = getPOSI(sock, posi, 0); + playbackSleep(interval); + if (result < 0) + { + continue; + } + fprintf(fd, "%f, %f, %f, %f, %f, %f, %f\n", posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6]); + } + closeUDP(sock); + displayMsg("Recording Complete"); +} + +void playback(char* path, int interval) +{ + FILE* fd = fopen(path, "r"); + if (!fd) + { + displayMsg("Unable to open output file."); + return; + } + displayMsg("Starting Playback..."); + + XPCSocket sock = openUDP("127.0.0.1"); + float posi[7]; + while (!feof(fd) && !ferror(fd)) + { + int result = fscanf(fd, "%f, %f, %f, %f, %f, %f, %f\n", + &posi[0], &posi[1], &posi[2], &posi[3], &posi[4], &posi[5], &posi[6]); + playbackSleep(interval); + if (result != 7) + { + continue; + } + sendPOSI(sock, posi, 7, 0); + } + closeUDP(sock); + displayMsg("Playback Complete"); +} \ No newline at end of file diff --git a/C/playbackExample/src/playback.h b/C/playbackExample/src/playback.h new file mode 100644 index 0000000..e54a3b1 --- /dev/null +++ b/C/playbackExample/src/playback.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. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#ifndef XPC_PLAYBACKEX_PLAYBACK_H_ +#define XPC_PLAYBACKEX_PLAYBACK_H_ + +void record(char* path, int interval, int duration); + +void playback(char* path, int interval); + +#endif \ No newline at end of file diff --git a/C/playbackExample/win/playbackExample.sln b/C/playbackExample/win/playbackExample.sln new file mode 100644 index 0000000..18a4c81 --- /dev/null +++ b/C/playbackExample/win/playbackExample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "playbackExample", "playbackExample.vcxproj", "{40781C32-3EE6-4B3C-A94F-E128BD41E21C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.ActiveCfg = Debug|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.Build.0 = Debug|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.ActiveCfg = Release|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/C/playbackExample/win/playbackExample.vcxproj b/C/playbackExample/win/playbackExample.vcxproj new file mode 100644 index 0000000..a8adf8a --- /dev/null +++ b/C/playbackExample/win/playbackExample.vcxproj @@ -0,0 +1,89 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {40781C32-3EE6-4B3C-A94F-E128BD41E21C} + playbackExample + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + ../../src;../src;$(IncludePath) + ../../src;../src;$(SourcePath) + + + ../../src;../src;$(IncludePath) + ../../src;../src;$(SourcePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/C/playbackExample/win/playbackExample.vcxproj.filters b/C/playbackExample/win/playbackExample.vcxproj.filters new file mode 100644 index 0000000..6a83f74 --- /dev/null +++ b/C/playbackExample/win/playbackExample.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index 24234e7..7153321 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -117,7 +117,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port) #else struct timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = 1000; + timeout.tv_usec = 250000; #endif if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { @@ -368,32 +368,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; @@ -508,6 +522,38 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char /*****************************************************************************/ /**** POSI functions ****/ /*****************************************************************************/ +int getPOSI(XPCSocket sock, float values[7], char ac) +{ + // Setup send command + unsigned char buffer[6] = "GETP"; + buffer[5] = ac; + + // Send command + if (sendUDP(sock, buffer, 6) < 0) + { + printError("getPOSI", "Failed to send command."); + return -1; + } + + // Get response + unsigned char readBuffer[34]; + int readResult = readUDP(sock, readBuffer, 34); + if (readResult < 0) + { + printError("getPOSI", "Failed to read response."); + return -2; + } + if (readResult != 34) + { + printError("getPOSI", "Unexpected response length."); + return -3; + } + + // Copy response into values + memcpy(values, readBuffer + 6, 7 * sizeof(float)); + return 0; +} + int sendPOSI(XPCSocket sock, float values[], int size, char ac) { // Validate input @@ -552,6 +598,41 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac) /*****************************************************************************/ /**** CTRL functions ****/ /*****************************************************************************/ +int getCTRL(XPCSocket sock, float values[7], char ac) +{ + // Setup send command + unsigned char buffer[6] = "GETC"; + buffer[5] = ac; + + // Send command + if (sendUDP(sock, buffer, 6) < 0) + { + printError("getCTRL", "Failed to send command."); + return -1; + } + + // Get response + unsigned char readBuffer[31]; + int readResult = readUDP(sock, readBuffer, 31); + if (readResult < 0) + { + printError("getCTRL", "Failed to read response."); + return -2; + } + if (readResult != 31) + { + printError("getCTRL", "Unexpected response length."); + return -3; + } + + // Copy response into values + memcpy(values, readBuffer + 5, 4 * sizeof(float)); + values[4] = readBuffer[21]; + values[5] = *((float*)(readBuffer + 22)); + values[6] = *((float*)(readBuffer + 27)); + return 0; +} + int sendCTRL(XPCSocket sock, float values[], int size, char ac) { // Validate input @@ -672,7 +753,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; @@ -682,3 +763,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 abcfd1f..161178a 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 @@ -165,6 +196,14 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char // Position +/// Gets the position and orientation of the specified aircraft. +/// +/// \param sock The socket used to send the command and receive the response. +/// \param values An array to store the position information returned by the +/// plugin. The format of values is [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear] +/// \returns 0 if successful, otherwise a negative value. +int getPOSI(XPCSocket sock, float values[7], char ac); + /// Sets the position and orientation of the specified aircraft. /// /// \param sock The socket to use to send the command. @@ -178,6 +217,16 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac); // Controls +/// Gets the control surface information for the specified aircraft. +/// +/// \param sock The socket used to send the command and receive the response. +/// \param values An array to store the position information returned by the +/// plugin. The format of values is [Elevator, Aileron, Rudder, +/// Throttle, Gear, Flaps, Speed Brakes] +/// \param ac The aircraft to set the control surfaces of. 0 is the main/player aircraft. +/// \returns 0 if successful, otherwise a negative value. +int getCTRL(XPCSocket sock, float values[7], char ac); + /// Sets the control surfaces of the specified aircraft. /// /// \param sock The socket to use to send the command. @@ -200,6 +249,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/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/ContinuousOperation/.idea/.name b/Java/Examples/ContinuousOperation/.idea/.name new file mode 100644 index 0000000..9ac7aa6 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/.name @@ -0,0 +1 @@ +ContinuousOperation \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/compiler.xml b/Java/Examples/ContinuousOperation/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml b/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml b/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml new file mode 100644 index 0000000..da48fd9 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/misc.xml b/Java/Examples/ContinuousOperation/.idea/misc.xml new file mode 100644 index 0000000..a61f7bc --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/misc.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/modules.xml b/Java/Examples/ContinuousOperation/.idea/modules.xml new file mode 100644 index 0000000..0a1c2fe --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/vcs.xml b/Java/Examples/ContinuousOperation/.idea/vcs.xml new file mode 100644 index 0000000..6564d52 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/ContinuousOperation.iml b/Java/Examples/ContinuousOperation/ContinuousOperation.iml new file mode 100644 index 0000000..6c4f0ab --- /dev/null +++ b/Java/Examples/ContinuousOperation/ContinuousOperation.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar b/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar new file mode 100644 index 0000000..eba5283 Binary files /dev/null and b/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar differ diff --git a/Java/Examples/ContinuousOperation/src/Main.java b/Java/Examples/ContinuousOperation/src/Main.java new file mode 100644 index 0000000..61a14eb --- /dev/null +++ b/Java/Examples/ContinuousOperation/src/Main.java @@ -0,0 +1,47 @@ +package gov.nasa.xpc.ex; + +import gov.nasa.xpc.XPlaneConnect; + +import java.io.IOException; + +/** + * An example program demonstrating the use of X-PlaneConnect in a continuous loop. + * + * @author Jason Watkins + * @version 1.0 + * @since 2015-06-19 + */ +public class Main +{ + public static void main(String[] args) + { + try (XPlaneConnect xpc = new XPlaneConnect()) + { + int aircraft = 0; + while(true) + { + float[] posi = xpc.getPOSI(aircraft); + float[] ctrl = xpc.getCTRL(aircraft); + + System.out.format("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n", + posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]); + + try + { + Thread.sleep(100); + } + catch (InterruptedException ex) {} + + if(System.in.available() > 0) + { + break; + } + } + } + catch(IOException ex) + { + System.out.println("Error:"); + System.out.println(ex.getMessage()); + } + } +} diff --git a/Java/Examples/Playback/.idea/compiler.xml b/Java/Examples/Playback/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/Java/Examples/Playback/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/copyright/profiles_settings.xml b/Java/Examples/Playback/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/Java/Examples/Playback/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/description.html b/Java/Examples/Playback/.idea/description.html new file mode 100644 index 0000000..db5f129 --- /dev/null +++ b/Java/Examples/Playback/.idea/description.html @@ -0,0 +1 @@ +Simple Java application that includes a class with main() method \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml b/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml new file mode 100644 index 0000000..f0cda69 --- /dev/null +++ b/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/misc.xml b/Java/Examples/Playback/.idea/misc.xml new file mode 100644 index 0000000..5a65e9a --- /dev/null +++ b/Java/Examples/Playback/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/modules.xml b/Java/Examples/Playback/.idea/modules.xml new file mode 100644 index 0000000..7ba8a90 --- /dev/null +++ b/Java/Examples/Playback/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/project-template.xml b/Java/Examples/Playback/.idea/project-template.xml new file mode 100644 index 0000000..1f08b88 --- /dev/null +++ b/Java/Examples/Playback/.idea/project-template.xml @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/vcs.xml b/Java/Examples/Playback/.idea/vcs.xml new file mode 100644 index 0000000..6564d52 --- /dev/null +++ b/Java/Examples/Playback/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/Playback.iml b/Java/Examples/Playback/Playback.iml new file mode 100644 index 0000000..6c4f0ab --- /dev/null +++ b/Java/Examples/Playback/Playback.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/src/gov/nasa/xpc/Main.java b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java new file mode 100644 index 0000000..0e0b376 --- /dev/null +++ b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java @@ -0,0 +1,147 @@ +package gov.nasa.xpc; + +import com.sun.javafx.scene.EnteredExitedHandler; + +import java.io.*; +import java.util.Scanner; + +public class Main +{ + + public static void main(String[] args) throws IOException + { + String[] mainOpts = new String[] { "Record X-Plane", "Playback File", "Exit" }; + + System.out.println("X-Plane Connect Playback Example [Version 1.2.0.0]"); + System.out.println("(c) 2013-2015 United States Government as represented by the Administrator"); + System.out.println("of the National Aeronautics and Space Administration. All Rights Reserved."); + while(true) + { + int result = displayMenu("What would you like to do?", mainOpts); + switch(result) + { + case 1: + { + String path = getString("Enter a save file path: "); + int interval = getInt("Enter interval between frames (milliseconds): "); + int duration = getInt("Enter duration to record for (seconds): "); + + record(path, interval, duration); + break; + } + case 2: + { + String path = getString("Enter path to saved playback file: "); + int interval = getInt("Enter interval between frames (milliseconds): "); + + playback(path, interval); + break; + } + case 3: + { + System.out.println("Exiting."); + return; + } + default: + { + System.out.println("Unrecognized menu option."); + break; + } + } + } + } + + private static int displayMenu(String title, String[] opts) throws IOException + { + System.out.println(); + System.out.println("+---------------------------------------------- +"); + System.out.println(String.format("| %1$-42s |", title)); + System.out.println("+---------------------------------------------- +"); + for(int i = 0; i < opts.length; ++i) + { + System.out.println(String.format("| %1$2d. %2$-40s |", i + 1, opts[i])); + + } + System.out.println("+---------------------------------------------- +"); + return getInt("Please select an option: "); + } + + private static String getString(String prompt) throws IOException + { + System.out.print(prompt); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + return reader.readLine(); + } + + private static int getInt(String prompt) throws IOException + { + System.out.print(prompt); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String result = reader.readLine(); + + return Integer.parseInt(result); + } + + public static void record(String path, int interval, int duration) throws IOException + { + int count = duration * 1000 / interval; + if (count < 1) + { + throw new IllegalArgumentException("duration is less than one interval."); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) + { + System.out.println("Recording..."); + try (XPlaneConnect xpc = new XPlaneConnect()) + { + for (int i = 0; i < count; ++i) + { + float[] posi = xpc.getPOSI(0); + writer.write(String.format("%1$f, %2$f, %3$f, %4$f, %5$f, %6$f, %7$f\n", + posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6])); + try + { + Thread.sleep(interval); + } + catch (InterruptedException ex) + { + } + } + } + System.out.println("Recording Complete"); + } + } + + public static void playback(String path, int interval) throws IOException + { + try(Scanner reader = new Scanner(new FileReader(path))) + { + reader.useDelimiter("[\\s\\r\\n,]+"); + System.out.println("Starting playback..."); + try (XPlaneConnect xpc = new XPlaneConnect()) + { + while(reader.hasNextLine()) + { + float[] posi = new float[7]; + for (int i = 0; i < 7; ++i) + { + String s = reader.next(); + posi[i] = Float.parseFloat(s); + } + reader.nextLine(); + xpc.sendPOSI(posi); + try + { + Thread.sleep(interval); + } + catch (InterruptedException ex) + { + } + } + } + } + } +} 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 511613b..f98816e 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -331,46 +331,114 @@ 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()); } + /** + * Gets the control surface information for the specified airplane. + * + * @param ac The aircraft to get control surface information for. + * @return An array containing control surface data in the same format as {@code sendCTRL}. + * @throws IOException If the command cannot be sent or a response cannot be read. + */ + public float[] getCTRL(int ac) throws IOException + { + // Send request + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("GETC".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(ac); + sendUDP(os.toByteArray()); + + // Read response + byte[] data = readUDP(); + if(data.length == 0) + { + throw new IOException("No response received."); + } + if(data.length < 31) + { + throw new IOException("Response too short"); + } + + // Parse response + float[] result = new float[7]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.order(ByteOrder.LITTLE_ENDIAN); + result[0] = bb.getFloat(5); + result[1] = bb.getFloat(9); + result[2] = bb.getFloat(13); + result[3] = bb.getFloat(17); + result[4] = bb.get(21); + result[5] = bb.getFloat(22); + result[6] = bb.getFloat(27); + return result; + } + /** * Sends command to X-Plane setting control surfaces on the player ac. * @@ -382,6 +450,7 @@ public class XPlaneConnect implements AutoCloseable *
  • Throttle [-1, 1]
  • *
  • Gear (0=up, 1=down)
  • *
  • Flaps [0, 1]
  • + *
  • Speedbrakes [-0.5, 1.5]
  • * *

    * If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To @@ -406,6 +475,7 @@ public class XPlaneConnect implements AutoCloseable *

  • Throttle [-1, 1]
  • *
  • Gear (0=up, 1=down)
  • *
  • Flaps [0, 1]
  • + *
  • Speedbrakes [-0.5, 1.5]
  • * *

    * If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To @@ -440,7 +510,14 @@ public class XPlaneConnect implements AutoCloseable { if(i == 4) { - bb.put(cur, (byte) values[i]); + if(i >= values.length) + { + bb.put(cur, (byte)-1); + } + else + { + bb.put(cur, (byte)values[i]); + } cur += 1; } else if (i >= values.length) @@ -465,6 +542,44 @@ public class XPlaneConnect implements AutoCloseable sendUDP(os.toByteArray()); } + /** + * Gets position information for the specified airplane. + * + * @param ac The aircraft to get position information for. + * @return An array containing control surface data in the same format as {@code sendPOSI}. + * @throws IOException If the command cannot be sent or a response cannot be read. + */ + public float[] getPOSI(int ac) throws IOException + { + // Send request + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("GETP".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(ac); + sendUDP(os.toByteArray()); + + // Read response + byte[] data = readUDP(); + if(data.length == 0) + { + throw new IOException("No response received."); + } + if(data.length < 34) + { + throw new IOException("Response too short"); + } + + // Parse response + float[] result = new float[7]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.order(ByteOrder.LITTLE_ENDIAN); + for(int i = 0; i < 7; ++i) + { + result[i] = bb.getFloat(6 + 4 * i); + } + return result; + } + /** * Sets the position of the player ac. * @@ -693,6 +808,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 755bb29..f9d9e0b 100644 Binary files a/MATLAB/+XPlaneConnect/XPlaneConnect.jar and b/MATLAB/+XPlaneConnect/XPlaneConnect.jar differ diff --git a/MATLAB/+XPlaneConnect/closeUDP.m b/MATLAB/+XPlaneConnect/closeUDP.m index f49dd62..4941ae1 100644 --- a/MATLAB/+XPlaneConnect/closeUDP.m +++ b/MATLAB/+XPlaneConnect/closeUDP.m @@ -22,6 +22,10 @@ socket.close; %% Track open clients global clients; -%TODO: Remove stale clients +for i = 1:length(clients) + if socket == clients(i) + clients = [clients(1:i-1) clients(i+1:length(clients))]; + end +end end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/getCTRL.m b/MATLAB/+XPlaneConnect/getCTRL.m new file mode 100644 index 0000000..820afca --- /dev/null +++ b/MATLAB/+XPlaneConnect/getCTRL.m @@ -0,0 +1,29 @@ +function ctrl = getCTRL(ac, socket) +% getCTRL Gets control surface information for the specified aircraft +% +% Inputs +% ac: The aircraft number to get control surface data for. +% Outputs +% posi: An array of values matching the the format used by sendCTRL + + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[getCTRL] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +ac = int32(ac); + +%% Send command +ctrl = socket.getCTRL(ac); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/getPOSI.m b/MATLAB/+XPlaneConnect/getPOSI.m new file mode 100644 index 0000000..35b8d5a --- /dev/null +++ b/MATLAB/+XPlaneConnect/getPOSI.m @@ -0,0 +1,29 @@ +function posi = getPOSI(ac, socket) +% getPOSI Gets position information for the specified aircraft +% +% Inputs +% ac: The aircraft number to get position data for. +% Outputs +% posi: An array of values matching the the format used by sendPOSI + + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[getPOSI] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +ac = int32(ac); + +%% Send command +posi = socket.getPOSI(ac); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/openUDP.m b/MATLAB/+XPlaneConnect/openUDP.m index c38e087..2961598 100644 --- a/MATLAB/+XPlaneConnect/openUDP.m +++ b/MATLAB/+XPlaneConnect/openUDP.m @@ -26,6 +26,7 @@ p = inputParser; addOptional(p,'xpHost','127.0.0.1',@ischar); addOptional(p,'xpPort',49009,@isnumeric); addOptional(p,'port',0,@isnumeric); +addOptional(p,'timeout',100,@isnumeric); parse(p,varargin{:}); %% Create client @@ -33,7 +34,7 @@ if ~exist('gov.nasa.xpc.XPlaneConnect', 'class') [folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP')); javaaddpath(fullfile(folder, 'XPlaneConnect.jar')); end -socket = gov.nasa.xpc.XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port); +socket = gov.nasa.xpc.XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port, p.Results.timeout); %% Track open clients global clients; 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 f25e848..03e1cef 100644 --- a/MATLAB/Example/Example.m +++ b/MATLAB/Example/Example.m @@ -10,21 +10,21 @@ import XPlaneConnect.* disp('xplaneconnect Example Script-'); disp('Setting up Simulation'); -Socket = openUDP(49005); +Socket = openUDP('127.0.0.1', 49009); %% Ensure connected -getDREFs('sim/test/test_float') +getDREFs('sim/test/test_float', Socket); %% Set position of the player aircraft disp('Setting position'); - pauseSim(1); + pauseSim(1, Socket); % Lat Lon Alt Pitch Roll Heading Gear POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1]; -sendPOSI(POSI); % Set own aircraft position +sendPOSI(POSI, 0, Socket); % Set own aircraft position % Lat Lon Alt Pitch Roll Heading Gear POSI = [37.52465, -122.06899, 2500, 0, 20, 0, 1]; -sendPOSI(POSI, 1); % Place another aircraft just north of us +sendPOSI(POSI, 1, Socket); % Place another aircraft just north of us %% Set rates disp('Setting rates'); % Alpha Velocity PQR @@ -32,30 +32,30 @@ data = struct('h',[18, 3, 16],... 'd',[0,-999,0,-999,-999,-999,-999,-999;... % Alpha data 130,130,130,130,-999,-999,-999,-999;... % Velocity data 0,0,0,-999,-999,-999,-999,-999]); % PQR data -sendDATA(data); +sendDATA(data, Socket); %% Set CTRL % Throttle - CTRL = [0,0,0,0.8]; - sendCTRL(CTRL); + CTRL = [0,0,0,0.8,0,0]; + sendCTRL(CTRL, 0, Socket); pause(5); - pauseSim(0); + pauseSim(0, Socket); pause(5) % Run sim for 5 seconds %% Pause sim disp('Pausing simulation'); - pauseSim(1); + pauseSim(1, Socket); pause(5); disp('Unpausing simulation'); - pauseSim(0); + pauseSim(0, Socket); pause(10) % Run sim for 10 seconds %% Use DREF to raise landing gear disp('Raising gear'); gearDREF = 'sim/cockpit/switches/gear_handle_status'; - sendDREF(gearDREF, 0); + sendDREF(gearDREF, 0, Socket); pause(10) % Run sim for 10 seconds %% Confirm gear and paus status by reading DREFs disp('Checking gear'); pauseDREF = 'sim/operation/override/override_planepath'; - result = requestDREF({gearDREF, pauseDREF}); + result = getDREFs({gearDREF, pauseDREF}, Socket); if result{1} == 0 disp('Gear stowed'); else diff --git a/MATLAB/MonitorExample/MonitorExample.m b/MATLAB/MonitorExample/MonitorExample.m new file mode 100644 index 0000000..ffbdc14 --- /dev/null +++ b/MATLAB/MonitorExample/MonitorExample.m @@ -0,0 +1,19 @@ +%% X-Plane Connect MATLAB Example Script +% This script demonstrates how to read and write data to the XPC plugin. +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +clear all +addpath('../') +import XPlaneConnect.* + +Socket = openUDP(); +while 1 + posi = getPOSI(0, Socket); + ctrl = getCTRL(0, Socket); + + fprintf('Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n', ... + posi(1), posi(2), posi(3), ctrl(2), ctrl(1), ctrl(3)); + pause(0.1); +end +closeUDP(Socket); \ No newline at end of file diff --git a/MATLAB/PlaybackExample/MyRecording.txt b/MATLAB/PlaybackExample/MyRecording.txt new file mode 100644 index 0000000..60c546a --- /dev/null +++ b/MATLAB/PlaybackExample/MyRecording.txt @@ -0,0 +1,100 @@ +37.184181, -120.793640, 3904.040771, 1.856917, 0.001644, 116.058609, 0.000000 +37.184143, -120.793541, 3903.825928, 2.172139, -0.034512, 115.875587, 0.000000 +37.184090, -120.793404, 3903.511719, 2.425995, -0.111958, 115.577911, 0.000000 +37.184040, -120.793282, 3903.230225, 2.497653, -0.205506, 115.289268, 0.000000 +37.183994, -120.793167, 3902.961914, 2.478850, -0.315117, 115.006279, 0.000000 +37.183949, -120.793053, 3902.683838, 2.412536, -0.447890, 114.716370, 0.000000 +37.183910, -120.792953, 3902.452881, 2.344441, -0.570747, 114.485756, 0.000000 +37.183857, -120.792824, 3902.137939, 2.253826, -0.752984, 114.194984, 0.000000 +37.183807, -120.792702, 3901.830566, 2.178489, -0.942979, 113.943779, 0.000000 +37.183765, -120.792595, 3901.561279, 2.125190, -1.116157, 113.754135, 0.000000 +37.183735, -120.792511, 3901.342773, 2.089507, -1.260052, 113.621834, 0.000000 +37.183693, -120.792404, 3901.058350, 2.051378, -1.449701, 113.479713, 0.000000 +37.183628, -120.792244, 3900.620361, 2.004393, -1.742637, 113.320374, 0.000000 +37.183571, -120.792107, 3900.232178, 1.969860, -1.999084, 113.234406, 0.000000 +37.183529, -120.791992, 3899.922852, 1.945357, -2.199157, 113.199463, 0.000000 +37.183487, -120.791885, 3899.607666, 1.922479, -2.397843, 113.190887, 0.000000 +37.183441, -120.791771, 3899.279297, 1.738008, -2.604320, 113.214371, 0.000000 +37.183395, -120.791649, 3898.942383, 1.172052, -2.819112, 113.280800, 0.000000 +37.183342, -120.791512, 3898.525879, 0.437769, -3.059577, 113.392952, 0.000000 +37.183292, -120.791389, 3898.116943, -0.110597, -3.260861, 113.510010, 0.000000 +37.183247, -120.791275, 3897.675293, -0.458939, -3.399007, 113.635101, 0.000000 +37.183220, -120.791206, 3897.409668, -0.495934, -3.425584, 113.706985, 0.000000 +37.183182, -120.791107, 3896.976318, -0.338542, -3.312644, 113.832870, 0.000000 +37.183128, -120.790962, 3896.313965, 0.088305, -2.812056, 114.069130, 0.000000 +37.183083, -120.790848, 3895.734863, 0.355302, -2.143086, 114.327766, 0.000000 +37.183033, -120.790718, 3895.112061, 0.345563, -1.171917, 114.668144, 0.000000 +37.182987, -120.790604, 3894.519043, 0.034761, -0.025642, 115.037415, 0.000000 +37.182941, -120.790489, 3893.926270, -0.549108, 1.265883, 115.421654, 0.000000 +37.182888, -120.790344, 3893.151611, -1.627703, 3.039703, 115.896240, 0.000000 +37.182831, -120.790199, 3892.313477, -2.879907, 4.868656, 116.325722, 0.000000 +37.182785, -120.790077, 3891.557129, -3.883288, 6.350969, 116.633751, 0.000000 +37.182743, -120.789970, 3890.766602, -4.752331, 7.729563, 116.888329, 0.000000 +37.182705, -120.789871, 3889.997314, -5.439554, 8.871016, 117.077042, 0.000000 +37.182659, -120.789749, 3888.965820, -6.164141, 10.014505, 117.239494, 0.000000 +37.182610, -120.789627, 3887.832031, -6.734039, 10.811956, 117.319382, 0.000000 +37.182560, -120.789505, 3886.558594, -7.134449, 11.245357, 117.313339, 0.000000 +37.182514, -120.789383, 3885.197510, -7.320403, 11.326477, 117.228592, 0.000000 +37.182465, -120.789261, 3883.700439, -7.264509, 11.086659, 117.071564, 0.000000 +37.182415, -120.789131, 3882.124023, -7.020517, 10.579235, 116.843575, 0.000000 +37.182365, -120.789009, 3880.477295, -6.698184, 9.864036, 116.538239, 0.000000 +37.182316, -120.788887, 3878.771973, -6.407937, 8.986709, 116.154274, 0.000000 +37.182266, -120.788765, 3877.028076, -6.217397, 7.992366, 115.705994, 0.000000 +37.182217, -120.788643, 3875.249023, -6.173934, 6.988136, 115.220467, 0.000000 +37.182167, -120.788521, 3873.432617, -6.285208, 6.050294, 114.729034, 0.000000 +37.182117, -120.788399, 3871.583496, -6.453388, 5.213163, 114.269440, 0.000000 +37.182068, -120.788269, 3869.689209, -6.484870, 4.480176, 113.873062, 0.000000 +37.182014, -120.788147, 3867.766357, -6.310386, 3.856612, 113.555290, 0.000000 +37.181965, -120.788025, 3865.820801, -6.021461, 3.327790, 113.311180, 0.000000 +37.181915, -120.787903, 3863.870361, -5.728759, 2.874094, 113.135651, 0.000000 +37.181866, -120.787781, 3861.910156, -5.480271, 2.475115, 113.025513, 0.000000 +37.181816, -120.787651, 3859.973145, -5.302026, 2.122235, 112.979225, 0.000000 +37.181767, -120.787529, 3858.042969, -5.182843, 1.808723, 112.993736, 0.000000 +37.181717, -120.787407, 3856.119629, -5.038956, 1.552066, 113.070557, 0.000000 +37.181667, -120.787285, 3854.215088, -4.828547, 1.368155, 113.206085, 0.000000 +37.181614, -120.787148, 3852.185059, -4.515563, 1.254129, 113.410904, 0.000000 +37.181557, -120.787010, 3850.030762, -4.132499, 1.210060, 113.681358, 0.000000 +37.181507, -120.786888, 3848.263916, -3.831450, 1.225745, 113.935280, 0.000000 +37.181458, -120.786766, 3846.475342, -3.528734, 1.284648, 114.214294, 0.000000 +37.181408, -120.786644, 3844.712158, -3.243377, 1.382594, 114.502975, 0.000000 +37.181358, -120.786522, 3842.987549, -2.982575, 1.515415, 114.790489, 0.000000 +37.181309, -120.786392, 3841.302246, -2.688687, 1.679037, 115.070442, 0.000000 +37.181259, -120.786270, 3839.659424, -2.305070, 1.869125, 115.338135, 0.000000 +37.181210, -120.786148, 3838.070068, -1.827937, 2.080883, 115.588036, 0.000000 +37.181156, -120.786018, 3836.515137, -1.326015, 2.315055, 115.817902, 0.000000 +37.181107, -120.785889, 3835.020508, -0.858468, 2.566000, 116.019691, 0.000000 +37.181053, -120.785767, 3833.605713, -0.446646, 2.827466, 116.188957, 0.000000 +37.181004, -120.785637, 3832.263672, -0.082390, 3.097450, 116.326546, 0.000000 +37.180950, -120.785515, 3830.996094, 0.248427, 3.372406, 116.433670, 0.000000 +37.180901, -120.785385, 3829.802734, 0.561189, 3.648955, 116.513596, 0.000000 +37.180847, -120.785263, 3828.686279, 0.901348, 3.909596, 116.570610, 0.000000 +37.180798, -120.785133, 3827.642090, 1.422296, 4.101938, 116.610832, 0.000000 +37.180744, -120.785011, 3826.675537, 2.124010, 4.216800, 116.636208, 0.000000 +37.180691, -120.784889, 3825.800049, 2.895841, 4.268193, 116.644135, 0.000000 +37.180641, -120.784767, 3825.024658, 3.647615, 4.264566, 116.632553, 0.000000 +37.180588, -120.784637, 3824.355469, 4.389325, 4.198403, 116.604164, 0.000000 +37.180538, -120.784523, 3823.797119, 5.116096, 4.063403, 116.559959, 0.000000 +37.180489, -120.784401, 3823.351562, 5.802851, 3.858843, 116.500069, 0.000000 +37.180435, -120.784279, 3823.021240, 6.434232, 3.583184, 116.425621, 0.000000 +37.180386, -120.784164, 3822.803223, 7.040646, 3.237821, 116.340439, 0.000000 +37.180336, -120.784042, 3822.697021, 7.629384, 2.839028, 116.249237, 0.000000 +37.180286, -120.783928, 3822.700195, 8.181519, 2.404881, 116.155312, 0.000000 +37.180244, -120.783821, 3822.798828, 8.596326, 1.981372, 116.066658, 0.000000 +37.180183, -120.783684, 3823.074219, 9.020256, 1.372200, 115.947289, 0.000000 +37.180126, -120.783554, 3823.453369, 9.331639, 0.798377, 115.847244, 0.000000 +37.180073, -120.783424, 3823.932129, 9.642418, 0.224538, 115.759720, 0.000000 +37.180019, -120.783302, 3824.521729, 9.962214, -0.365530, 115.680557, 0.000000 +37.179970, -120.783188, 3825.146240, 10.216949, -0.901261, 115.617035, 0.000000 +37.179928, -120.783081, 3825.777100, 10.420901, -1.378152, 115.566948, 0.000000 +37.179871, -120.782951, 3826.665283, 10.683296, -1.973540, 115.510689, 0.000000 +37.179817, -120.782829, 3827.599854, 10.932059, -2.526971, 115.463028, 0.000000 +37.179764, -120.782700, 3828.676758, 11.182608, -3.091355, 115.418022, 0.000000 +37.179710, -120.782570, 3829.791748, 11.405788, -3.608584, 115.379250, 0.000000 +37.179657, -120.782448, 3830.954102, 11.541166, -4.084742, 115.349564, 0.000000 +37.179604, -120.782326, 3832.230469, 11.431418, -4.526593, 115.343117, 0.000000 +37.179554, -120.782204, 3833.525391, 11.053594, -4.891589, 115.367775, 0.000000 +37.179501, -120.782082, 3834.855957, 10.596283, -5.199400, 115.407677, 0.000000 +37.179451, -120.781960, 3836.201660, 10.188977, -5.458726, 115.448441, 0.000000 +37.179401, -120.781837, 3837.573486, 9.879080, -5.681745, 115.481895, 0.000000 +37.179348, -120.781715, 3838.972656, 9.667299, -5.869191, 115.505310, 0.000000 +37.179298, -120.781593, 3840.342529, 9.507944, -5.992330, 115.524872, 0.000000 diff --git a/MATLAB/PlaybackExample/Playback.m b/MATLAB/PlaybackExample/Playback.m new file mode 100644 index 0000000..f5ea2a5 --- /dev/null +++ b/MATLAB/PlaybackExample/Playback.m @@ -0,0 +1,36 @@ +%% X-Plane Connect MATLAB Playback Example Script +% This script demonstrates how to playback recorded data from X-Plane. +% (See Record.m) +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +addpath('../') +import XPlaneConnect.* + +%% Setup +% Create variables and open connection to X-Plane +path = 'MyRecording.txt'; % File containing stored data +interval = 0.1; % Time between snapshots in seconds +duration = 10; +Socket = openUDP(); % Open connection to X-Plane +fd = fopen(path, 'r'); % Open file + +disp('X-Plane Connect Playback Example Script'); +fprintf('Playing back ''%s'' in %fs increments.\n', path, interval); + +%% Start Playback +count = floor(duration / interval); +pauseSim(1); +for i = 1:count + line = fgetl(fd); + posi = sscanf(line, '%f, %f, %f, %f, %f, %f, %f\n'); + sendPOSI(posi); + pause(interval); +end + +%% Close connection and file +pauseSim(0); +closeUDP(Socket); +fclose(fd); + +disp('Playback complete.'); \ No newline at end of file diff --git a/MATLAB/PlaybackExample/Record.m b/MATLAB/PlaybackExample/Record.m new file mode 100644 index 0000000..8f0d0b0 --- /dev/null +++ b/MATLAB/PlaybackExample/Record.m @@ -0,0 +1,34 @@ +%% X-Plane Connect MATLAB Recording Example Script +% This script demonstrates how to record data from X-Plane that can later +% be played back. (See Playback.m) +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +addpath('../') +import XPlaneConnect.* + +%% Setup +% Create variables and open connection to X-Plane +path = 'MyRecording.txt'; % File to save the data in +interval = 0.1; % Time between snapshots in seconds +duration = 10; % Time to record for in seconds +Socket = openUDP(); % Open connection to X-Plane +fd = fopen(path, 'w'); % Open file + +disp('X-Plane Connect Recording Example Script'); +fprintf('Recording to ''%s'' for %f seconds in %fs increments.\n', path, duration, interval); + +%% Start Recording +count = floor(duration / interval); +for i = 1:count + posi = getPOSI(0, Socket); + fprintf(fd, '%f, %f, %f, %f, %f, %f, %f\n', ... + posi(1), posi(2), posi(3), posi(4), posi(5), posi(6), posi(7)); + pause(interval); +end + +%% Close connection and file +closeUDP(Socket); +fclose(fd); + +disp('Recording complete.'); \ No newline at end of file diff --git a/Python/src/example.py b/Python/src/basicExample.py similarity index 100% rename from Python/src/example.py rename to Python/src/basicExample.py diff --git a/Python/src/monitorExample.py b/Python/src/monitorExample.py new file mode 100644 index 0000000..c4cdb48 --- /dev/null +++ b/Python/src/monitorExample.py @@ -0,0 +1,16 @@ +import sys + +import xpc + +def monitor(): + with xpc.XPlaneConnect() as client: + while True: + posi = client.getPOSI(); + ctrl = client.getCTRL(); + + print "Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n"\ + % (posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]) + + +if __name__ == "__main__": + monitor() \ No newline at end of file diff --git a/Python/src/playbackExample.py b/Python/src/playbackExample.py new file mode 100644 index 0000000..dfa4f52 --- /dev/null +++ b/Python/src/playbackExample.py @@ -0,0 +1,82 @@ +from time import sleep +import xpc + +def record(path, interval = 0.1, duration = 60): + try: + fd = open(path, "w") + except: + print "Unable to open file." + return + + count = int(duration / interval) + if count < 1: + print "duration is less than a single frame." + return + + with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client: + print "Recording..." + for i in range(0, count): + try: + posi = client.getPOSI() + fd.write("{0}, {1}, {2}, {3}, {4}, {5}, {6}\n".format(*posi)) + except: + print "Error reading position" + continue + sleep(interval); + print "Recording Complete" + fd.close() + +def playback(path, interval): + try: + fd = open(path, "r") + except: + print "Unable to open file." + return + + with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client: + print "Starting Playback..." + for line in fd: + try: + posi = [ float(x) for x in line.split(',') ] + posi = client.sendPOSI(posi) + except: + print "Error sending position" + continue + sleep(interval); + print "Playback Complete" + fd.close() + +def printMenu(title, opts): + print "\n+---------------------------------------------- +" + print "| {0:42} |\n".format(title) + print "+---------------------------------------------- +" + for i in range(0,len(opts)): + print "| {0:2}. {1:40} |".format(i + 1, opts[i]) + print "+---------------------------------------------- +" + return int(raw_input("Please select and option: ")) + +def ex(): + print "X-Plane Connect Playback Example [Version 1.2.0]" + print "(c) 2013-2015 United States Government as represented by the Administrator" + print "of the National Aeronautics and Space Administration. All Rights Reserved." + + mainOpts = [ "Record X-Plane", "Playback File", "Exit" ] + + while True: + opt = printMenu("What would you like to do?", mainOpts) + if opt == 1: + path = raw_input("Enter save file path: ") + interval = float(raw_input("Enter interval between frames (seconds): ")) + duration = float(raw_input("Enter duration to record for (seconds): ")) + record(path, interval, duration) + elif opt == 2: + path = raw_input("Enter save file path: ") + interval = float(raw_input("Enter interval between frames (seconds): ")) + playback(path, interval) + elif opt == 3: + return; + else: + print "Unrecognized option." + +if __name__ == "__main__": + ex() \ No newline at end of file diff --git a/Python/src/xpc.py b/Python/src/xpc.py index 13d3bbd..e2b5c5e 100644 --- a/Python/src/xpc.py +++ b/Python/src/xpc.py @@ -79,8 +79,20 @@ class XPlaneConnect(object): 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. @@ -131,7 +143,29 @@ class XPlaneConnect(object): buffer += struct.pack(" 255: - raise ValueError("dref must be a non-empty string less than 256 characters.") - if values == None: - raise ValueError("values must be a scalar or sequence of floats.") + self.sendDREFs([dref], [values]) + + def sendDREFs(self, drefs, values): + '''Sets the specified datarefs to the specified values. + + Args: + drefs: A list of names of the datarefs to set. + values: A list of scalar or vector values to set. + ''' + if len(drefs) != len(values): + raise ValueError("drefs and values must have the same number of elements.") + + buffer = struct.pack("<4sx", "DREF") + for i in range(len(drefs)): + dref = drefs[i] + value = values[i] + # Preconditions + if len(dref) == 0 or len(dref) > 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 - fmt = "" - buffer = None - if hasattr(values, "__len__"): - if len(values) > 255: - raise ValueError("values must have less than 256 items.") - fmt = "<4sxB{0:d}sB{1:d}f".format(len(dref), len(values)) - buffer = struct.pack(fmt, "DREF", len(dref), dref, len(values), values) - else: - fmt = "<4sxB{0:d}sBf".format(len(dref)) - buffer = struct.pack(fmt, "DREF", len(dref), dref, 1, values) + # 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 @@ -324,3 +412,18 @@ class XPlaneConnect(object): 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 index c288a9c..446a9ff 100644 --- a/Python/xplaneConnect.pyproj +++ b/Python/xplaneConnect.pyproj @@ -5,7 +5,7 @@ 2.0 3c7a940d-17c8-4e91-882f-9bc8b1d2f54b . - src\example.py + src\basicExample.py . @@ -24,8 +24,14 @@ false + + Code + + + Code + - + Code diff --git a/README.md b/README.md index 0065c56..774afc9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The X-Plane Connect (XPC) Toolbox is an open source research tool used to interact with the commercial flight simulator software X-Plane. XPC allows users to control aircraft and receive state information from aircraft simulated in -X-Plane using functions written in C, C++, Java, or MATLAB in real time over the +X-Plane using functions written in C, C++, Java, MATLAB, or Python in real time over the network. This research tool has been used to visualize flight paths, test control algorithms, simulate an active airspace, or generate out-the-window visuals for in-house flight simulation software. Possible applications include active control 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..7b9e445 --- /dev/null +++ b/TestScripts/C Tests/CtrlTests.h @@ -0,0 +1,188 @@ +//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 doGETCTest(float values[7], int ac, float expected[7]) +{ + // Execute Test + float actual[7]; + XPCSocket sock = openUDP(IP); + int result = sendCTRL(sock, values, 7, ac); + if (result >= 0) + { + result = getCTRL(sock, actual, ac); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + for (int i = 0; i < 7; ++i) + { + if (fabs(expected[i] - actual[i]) > 1e-4) + { + return -10 - i; + } + } + return 0; +} + +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; +} + +int testGETC() +{ + float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + return doGETCTest(CTRL, 0, CTRL); +} + +int testGETC_NonPlayer() +{ + float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + return doGETCTest(CTRL, 2, CTRL); +} +#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..104e517 --- /dev/null +++ b/TestScripts/C Tests/PosiTests.h @@ -0,0 +1,158 @@ +//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 doGETPTest(float values[7], int ac, float expected[7]) +{ + // Execute Test + float actual[7]; + XPCSocket sock = openUDP(IP); + int result = sendPOSI(sock, values, 7, ac); + if (result >= 0) + { + result = getPOSI(sock, actual, ac); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + for (int i = 0; i < 7; ++i) + { + if (fabs(expected[i] - actual[i]) > 1e-4) + { + return -10 - i; + } + } + return 0; +} + +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); +} + +int testGetPOSI_Player() +{ + float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; + return doGETPTest(POSI, 0, POSI); +} + +int testGetPOSI_NonPlayer() +{ + float POSI[7] = { 37.624F, -122.06899F, 1500, 0, 0, 0, 1 }; + return doGETPTest(POSI, 3, POSI); +} + +#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 5f84eb8..26428eb 100644 --- a/TestScripts/C Tests/main.c +++ b/TestScripts/C Tests/main.c @@ -1,1029 +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 3 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - float expected[] = { data[0][0], data[1][0], data[2][0], CTRL[3], CTRL[4], CTRL[5] }; - 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] - expected[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 3 - // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps - float expected[] = { data[0][0], data[1][0], data[2][0], CTRL[3], CTRL[4], CTRL[5] }; - 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] - expected[i]) > 1e-2) - { - return -i - 31; - } - } - - return 0; -} - - -int sendCTRLspeedbrakeTest() // sendCTRL test -{ - // Initialize - char* dref = "sim/flightmodel/controls/sbrkrqst"; - float data; - int size = 1; - float CTRL[7] = { -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -0.5F }; - XPCSocket sock = openUDP(IP); - - // Execute 1 - // Arm speedbrakes - sendCTRL(sock, CTRL, 7, 0); - int result = getDREF(sock, dref, &data, &size); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -1; - } - if (fabs(data - CTRL[6]) > 1e-4) - { - return -11; - } - - sock = openUDP(IP); - // Execute 2 - // Set full speedbrakes - CTRL[6] = 1.0F; - sendCTRL(sock, CTRL, 7, 0); - result = getDREF(sock, dref, &data, &size); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -2; - } - if (fabs(data - CTRL[6]) > 1e-4) - { - return -21; - } - - sock = openUDP(IP); - // Execute 3 - // Retract speedbrakes - CTRL[6] = 0.0F; - sendCTRL(sock, CTRL, 7, 0); - result = getDREF(sock, dref, &data, &size); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -3; - } - if (fabs(data - CTRL[6]) > 1e-4) - { - return -31; - } - - sock = openUDP(IP); - // Execute 4 - // Verify -998 does not change value. - CTRL[6] = -998.0F; - sendCTRL(sock, CTRL, 7, 0); - result = getDREF(sock, dref, &data, &size); - - // Close socket - closeUDP(sock); - - // Tests - if (result < 0) - { - return -4; - } - if (data> 1e-4) - { - return -41; - } - - 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 3 - 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] = - { - "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 -{ - // Setup - // 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* dref = "sim/operation/override/override_planepath"; - int size = 20; - float data[20]; - XPCSocket sock = openUDP(IP); - - // Execute - pauseSim(sock, 0); - result = getDREF(sock, dref, data, &size); - - // Test - if (result < 0) - { - result = -1; - } - else if (data[0] != 0) - { - result = -2; - } - - if (result == 0) - { - // Execute 2 - pauseSim(sock, 2); - result = getDREF(sock, dref, data, &size); - - // Test 2 - if (result < 0) - { - result = -3; - } - else if (data[0] != 1) - { - result = -4; - } - } - - if (result == 0) - { - // Execute 3 - pauseSim(sock, 0); - result = getDREF(sock, dref, data, &size); - - // Test 2 - if (result < 0) - { - result = -5; - } - else if (data[0] != 0) - { - result = -6; - } - } - - // 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[]) { @@ -1035,24 +21,46 @@ 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(sendCTRLspeedbrakeTest, "CTRL (speedbrake)"); - 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)"); + runTest(testGETC, "GETC (player)"); + runTest(testGETC_NonPlayer, "GETC (Non-player)"); + // POSI + runTest(testPOSI_Player, "POSI (player)"); + runTest(testPOSI_NonPlayer, "POSI (non-player)"); + runTest(testGetPOSI_Player, "GETP (player)"); + runTest(testGetPOSI_NonPlayer, "GETP (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 216a3d7..2e94bb5 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; @@ -333,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 { @@ -650,4 +678,51 @@ 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); + } + + } + + @Test + public void testGetPOSI() throws IOException + { + float[] values = { 37.524F, -122.06899F, 2500.0F, 45.0F, -45.0F, 15.0F, 1.0F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.pauseSim(true); + xpc.sendPOSI(values); + float[] actual = xpc.getPOSI(0); + + assertArrayEquals(values, actual, 1e-4F); + } + } + + @Test + public void testGetCTRL() throws IOException + { + float[] values = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.sendCTRL(values); + float[] actual = xpc.getCTRL(0); + + assertArrayEquals(values, actual, 1e-4F); + } + } } \ No newline at end of file diff --git a/TestScripts/MATLAB Tests/getCTRLTest.m b/TestScripts/MATLAB Tests/getCTRLTest.m new file mode 100644 index 0000000..3dc74f8 --- /dev/null +++ b/TestScripts/MATLAB Tests/getCTRLTest.m @@ -0,0 +1,15 @@ +function getCTRLTest() +%GETCTRLTEST Summary of this function goes here +% Detailed explanation goes here +addpath('../../MATLAB') +import XPlaneConnect.* + +values = [10.0, 5.0, -5.0, 0.8, 1.0, 0.5, -1.5]; +sendCTRL(values, 0); +actual = getCTRL(0); + +assert(isequal(length(actual), length(values))); +for i = 1:length(actual) + assert(abs(actual(i) - values(i)) <1e-4) +end +end diff --git a/TestScripts/MATLAB Tests/getPOSITest.m b/TestScripts/MATLAB Tests/getPOSITest.m new file mode 100644 index 0000000..c80b79a --- /dev/null +++ b/TestScripts/MATLAB Tests/getPOSITest.m @@ -0,0 +1,17 @@ +function getPOSITest() +%GETCTRLTEST Summary of this function goes here +% Detailed explanation goes here +addpath('../../MATLAB') +import XPlaneConnect.* + +values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ]; +pauseSim(1); +sendPOSI(values, 0); +actual = getPOSI(0); +pauseSim(0); + +assert(isequal(length(actual), length(values))); +for i = 1:length(actual) + assert(abs(actual(i) - values(i)) <1e-4) +end +end diff --git a/TestScripts/MATLAB Tests/CTRLTest.m b/TestScripts/MATLAB Tests/sendCTRLTest.m similarity index 98% rename from TestScripts/MATLAB Tests/CTRLTest.m rename to TestScripts/MATLAB Tests/sendCTRLTest.m index 7b8a9bc..0569917 100644 --- a/TestScripts/MATLAB Tests/CTRLTest.m +++ b/TestScripts/MATLAB Tests/sendCTRLTest.m @@ -1,4 +1,4 @@ -function CTRLTest( ) +function sendCTRLTest( ) %CTRLTest Summary of this function goes here % Detailed explanation goes here %% Test player aircraft diff --git a/TestScripts/MATLAB Tests/POSITest.m b/TestScripts/MATLAB Tests/sendPOSITest.m similarity index 96% rename from TestScripts/MATLAB Tests/POSITest.m rename to TestScripts/MATLAB Tests/sendPOSITest.m index 16b6c8d..42a6e15 100644 --- a/TestScripts/MATLAB Tests/POSITest.m +++ b/TestScripts/MATLAB Tests/sendPOSITest.m @@ -1,4 +1,4 @@ -function POSITest( ) +function sendPOSITest( ) %POSITest Summary of this function goes here % Detailed explanation goes here addpath('../../MATLAB') 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..2ef6d5f 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() @@ -15,12 +18,16 @@ theTests = {{@openCloseTest, 'Open/Close Test', 0},... {@getDREFsTest,'Request DREF Test', 0},... {@sendDREFTest,'Send DREF Test', 0},... {@DATATest,'DATA Test', 0},... - {@CTRLTest,'CTRL Test', 0},... - {@POSITest,'POSI Test', 0},... + {@sendCTRLTest,'sendCTRL Test', 0},... + {@getCTRLTest,'getCTRL Test', 0},... + {@sendPOSITest,'sendPOSI Test', 0},... + {@getPOSITest,'getPOSI 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 +42,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 index ac6b1b6..486803d 100644 --- a/TestScripts/Python Tests/Tests.py +++ b/TestScripts/Python Tests/Tests.py @@ -3,7 +3,7 @@ import unittest import imp import time -import xpc +xpc = imp.load_source('xpc', '../../Python/src/xpc.py') class XPCTests(unittest.TestCase): """Tests the functionality of the XPlaneConnect class.""" @@ -117,6 +117,37 @@ class XPCTests(unittest.TestCase): 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" @@ -171,6 +202,29 @@ class XPCTests(unittest.TestCase): expected = 0.0 do_test() + def test_getCTRL(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.sendCTRL(values, ac) + result = client.getCTRL(ac) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [0.0, 0.0, 0.0, 0.8, 1.0, 0.5, -1.5] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + def test_sendCTRL(self): # Setup @@ -239,6 +293,31 @@ class XPCTests(unittest.TestCase): ctrl[6] = 0.0 do_test() + def test_getPOSI(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.pauseSim(True) + client.sendPOSI(values, ac) + result = client.getPOSI(ac) + client.pauseSim(False) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + def test_sendPOSI(self): # Setup drefs = ["sim/flightmodel/position/latitude",\ @@ -288,6 +367,22 @@ class XPCTests(unittest.TestCase): # 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() diff --git a/xpcPlugin/CMakeLists.txt b/xpcPlugin/CMakeLists.txt index ca964b5..d07ae27 100644 --- a/xpcPlugin/CMakeLists.txt +++ b/xpcPlugin/CMakeLists.txt @@ -13,7 +13,6 @@ SET(CMAKE_CXX_COMPILER g++) add_library(xpc64 SHARED XPCPlugin.cpp DataManager.cpp - DataMaps.cpp Drawing.cpp Log.cpp Message.cpp @@ -24,7 +23,6 @@ set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-shared add_library(xpc32 SHARED XPCPlugin.cpp DataManager.cpp - DataMaps.cpp Drawing.cpp Log.cpp Message.cpp diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 178ac47..4571006 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -1,25 +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 "Log.h" #include "XPLMDataAccess.h" #include "XPLMGraphics.h" +#include #include #include #include @@ -28,14 +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"))); @@ -142,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); @@ -165,8 +169,8 @@ namespace XPC sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i); mdrefs[i][DREF_GearDeploy] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio", i); - mdrefs[i][DREF_FlapSetting] = XPLMFindDataRef(multi); // Can't set the actual flap setting on npc aircraft mdrefs[i][DREF_FlapActual] = XPLMFindDataRef(multi); + mdrefs[i][DREF_FlapSetting] = mdrefs[i][DREF_FlapActual]; // Can't set the actual flap setting on npc aircraft sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio2", i); mdrefs[i][DREF_FlapActual2] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); @@ -179,6 +183,7 @@ namespace XPC mdrefs[i][DREF_Sweep] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i); mdrefs[i][DREF_ThrottleActual] = XPLMFindDataRef(multi); + mdrefs[i][DREF_ThrottleSet] = mdrefs[i][DREF_ThrottleActual]; // No throttle set for multiplayer planes. sprintf(multi, "sim/multiplayer/position/plane%i_yolk_pitch", i); mdrefs[i][DREF_YokePitch] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i); @@ -299,8 +304,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) { @@ -308,24 +314,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 @@ -333,99 +333,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; } @@ -433,9 +414,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; } @@ -443,9 +423,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; } @@ -453,9 +432,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; } @@ -463,9 +441,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; } @@ -473,62 +450,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) @@ -538,149 +519,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", dref.c_str(), 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; } @@ -706,26 +659,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); } @@ -738,7 +688,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); @@ -746,27 +695,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); } @@ -782,16 +727,16 @@ 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]; - orient[0] = halfRad * orient[0]; - orient[1] = halfRad * orient[1]; - q[0] = cos(orient[2]) * cos(orient[0]) * cos(orient[1]) + sin(orient[2]) * sin(orient[0]) * sin(orient[1]); - q[1] = cos(orient[2]) * cos(orient[0]) * sin(orient[1]) - sin(orient[2]) * sin(orient[0]) * cos(orient[1]); - q[2] = cos(orient[2]) * sin(orient[0]) * cos(orient[1]) + sin(orient[2]) * cos(orient[0]) * sin(orient[1]); - q[3] = sin(orient[2]) * cos(orient[0]) * cos(orient[1]) - cos(orient[2]) * sin(orient[0]) * sin(orient[1]); + float theta = halfRad * orient[0]; + float phi = halfRad * orient[1]; + float psi = halfRad * orient[2]; + q[0] = cos(phi) * cos(theta) * cos(psi) + sin(phi) * sin(theta) * sin(psi); + q[1] = sin(phi) * cos(theta) * cos(psi) - cos(phi) * sin(theta) * sin(psi); + q[2] = cos(phi) * sin(theta) * cos(psi) + sin(phi) * cos(theta) * sin(psi); + q[3] = cos(phi) * cos(theta) * sin(psi) - sin(phi) * sin(theta) * cos(psi); // If the sim is un-paused, this will overwrite the pitch/roll/yaw // values set above. @@ -801,14 +746,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; } @@ -819,4 +764,14 @@ namespace XPC Set(DREF_FlapSetting, value); Set(DREF_FlapActual, value); } + + 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 9eab1e8..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, @@ -139,13 +139,13 @@ namespace XPC /// Maps X-Plane dataref lines to XPC DREF values. extern DREF XPData[134][8]; - /// Marshals data between the plugin and X-Plane. + /// 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: @@ -165,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. @@ -174,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. /// @@ -294,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. /// @@ -409,6 +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 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 d24b2bf..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 diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp index 6afc271..7b849dd 100644 --- a/xpcPlugin/Log.cpp +++ b/xpcPlugin/Log.cpp @@ -1,17 +1,20 @@ -//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 "XPLMUtilities.h" -#include #include #include #include + +#ifndef LIN +#include +#endif #include #include -// Implementation note: I initial wrote this class using C++ iostreams, but I couldn't find any +// 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 std::fprintf directly. namespace XPC @@ -19,6 +22,20 @@ namespace XPC static std::FILE* fd; static void WriteTime(FILE* fd) { +#ifdef LIN + // Can't provide high resolution logging on Linux because C++11 doesn't work with X-Plane. + time_t rawtime; + tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + + 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); +#else using namespace std::chrono; system_clock::time_point now = system_clock::now(); @@ -28,17 +45,59 @@ namespace XPC std::tm * tm = std::localtime(&now_tt); std::stringstream ss; - ss << std::setfill('0') << "[" + 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::setw(3) << ms.count() << "|"; + std::fprintf(fd, ss.str().c_str()); +#endif } - void Log::Initialize(std::string version) + static void WriteLevel(FILE* fd, int level) { + const 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) { @@ -81,35 +140,33 @@ namespace XPC } } - void Log::WriteLine(const std::string& value) + void Log::WriteLine(int level, const std::string& tag, const std::string& value) { - Log::WriteLine(value.c_str()); - } - - void Log::WriteLine(const char* value) - { - if (!fd) + if (level > LOG_LEVEL || !fd) { return; } WriteTime(fd); - std::fprintf(fd, "%s\n", value); + 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; - if (!fd) + if (level > LOG_LEVEL || !fd) { return; } va_start(args, format); WriteTime(fd); - std::vfprintf(fd, format, args); + WriteLevel(fd, level); + std::fprintf(fd, "%s|", tag.c_str()); + std::vfprintf(fd, format.c_str(), args); std::fprintf(fd, "\n"); std::fflush(fd); diff --git a/xpcPlugin/Log.h b/xpcPlugin/Log.h index 84d75c0..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,40 +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); /// Closes the log file. static void Close(); - /// Writes the C string pointed to by format, followed by a line + /// 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 3221a8f..3563a09 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -1,8 +1,10 @@ -//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" +#include + #include #include #include @@ -11,79 +13,62 @@ 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() + + std::string Message::GetHead() const { - if (size < 4) - { - return 0; - } - return *((unsigned long*)buffer); + std::string val = size < 4 ? "" : std::string((char*)buffer, 4); + return val; } - std::string Message::GetHead() + const unsigned char* Message::GetBuffer() const { - if (size < 4) - { - return ""; - } - return std::string((char*)buffer, 4); + const unsigned char* val = size == 0 ? NULL : buffer; + return val; } - const unsigned char* Message::GetBuffer() - { - if (size == 0) - { - return NULL; - } - return buffer; - } - - 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]"; + using namespace std; + stringstream ss; // Dump raw bytes to string - ss << std::hex << std::setfill('0'); + ss << std::hex << setfill('0'); for (int i = 0; i < size; ++i) { - ss << ' ' << std::setw(2) << static_cast(buffer[i]); + ss << ' ' << setw(2) << static_cast(buffer[i]); } - Log::WriteLine(ss.str()); + Log::WriteLine(LOG_TRACE, "DBUG", ss.str()); - ss << std::dec; + std::string head = GetHead(); ss.str(""); - ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; - switch (GetMagicNumber()) // Binary version of head + ss << "Head: " << head << std::dec << " Size: " << GetSize(); + if (head == "CONN" || head == "WYPT" || head == "TEXT") + { + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else if (head == "CTRL") { - case 0x4E4EF443: // CONN - case 0x54505957: // WYPT - case 0x54584554: // TEXT - { - Log::WriteLine(ss.str()); - break; - } - case 0x4C525443: // CTRL - { // Parse message data float pitch = *((float*)(buffer + 5)); float roll = *((float*)(buffer + 9)); @@ -96,89 +81,91 @@ namespace XPC { aircraft = buffer[26]; } - ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; - ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; - Log::WriteLine(ss.str()); - break; - } - case 0x41544144: // DATA + ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; + ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else if (head == "DATA") { - std::size_t numCols = (size - 5) / 36; + size_t numCols = (size - 5) / 36; float values[32][9]; for (int i = 0; i < numCols; ++i) { values[i][0] = buffer[5 + 36 * i]; - std::memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); + memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); } - ss << " (" << numCols << " lines)"; - Log::WriteLine(ss.str()); - for (int i = 0; i < numCols; ++i) - { - ss.str(""); - ss << "\t#" << values[i][0]; - for (int j = 1; j < 9; ++j) - { + ss << " (" << numCols << " lines)"; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + for (int i = 0; i < numCols; ++i) + { + ss.str(""); + ss << " #" << values[i][0]; + for (int j = 1; j < 9; ++j) + { ss << " " << values[i][j]; - } - Log::WriteLine(ss.str()); - } - break; - } - case 0x46455244: // DREF - { - Log::WriteLine(ss.str()); - std::string dref((char*)buffer + 6, buffer[5]); - Log::FormatLine("-\tDREF (size %i) = %s", dref.length(), dref.c_str()); + } + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + } + else if (head == "DREF") + { + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + string dref((char*)buffer + 6, buffer[5]); + 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()); - break; + } + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } - case 0x44544547: // GETD - { - Log::WriteLine(ss.str()); + else if (head == "GETC" || head == "GETP") + { + ss << " Aircraft:" << (int)buffer[5]; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else if (head == "GETD") + { + 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()); + string dref((char*)buffer + cur + 1, buffer[cur]); + Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s", + i + 1, buffer[5], dref.length(), dref.c_str()); cur += 1 + buffer[cur]; } - break; } - case 0x49534F50: // POSI + else if (head == "POSI") { char aircraft = buffer[5]; float gear = *((float*)(buffer + 30)); float pos[3]; float orient[3]; - std::memcpy(pos, buffer + 6, 12); - std::memcpy(orient, buffer + 18, 12); + memcpy(pos, buffer + 6, 12); + memcpy(orient, buffer + 18, 12); ss << " AC:" << (int)aircraft; ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; - ss << gear; - Log::WriteLine(ss.str()); - break; + ss << gear; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } - case 0x554D4953: // SIMU + else if (head == "SIMU") { - ss << ' ' << (int)buffer[5]; - Log::WriteLine(ss.str()); - break; - } - default: - { - ss << " UNKNOWN HEADER "; - Log::WriteLine(ss.str()); - break; - } + ss << ' ' << (int)buffer[5]; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); } -#endif + else if (head == "VIEW") + { + ss << "Type:" << *((unsigned long*)(buffer + 5)); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else + { + ss << " UNKNOWN HEADER "; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } } } diff --git a/xpcPlugin/Message.h b/xpcPlugin/Message.h index b44b41d..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(); diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index 17e5cb0..d94be09 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -1,10 +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 "Drawing.h" #include "Log.h" +#include "XPLMUtilities.h" + #include #include @@ -19,6 +35,7 @@ namespace XPC void MessageHandlers::SetSocket(UDPSocket* socket) { + Log::WriteLine(LOG_TRACE, "MSGH", "Setting socket"); MessageHandlers::sock = socket; } @@ -26,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)); @@ -36,8 +54,9 @@ 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)); + handlers.insert(std::make_pair("GETC", MessageHandlers::HandleGetC)); + handlers.insert(std::make_pair("GETP", MessageHandlers::HandleGetP)); // X-Plane data messages handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData)); @@ -65,16 +84,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 { @@ -86,19 +103,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); @@ -113,7 +128,7 @@ namespace XPC } } - void MessageHandlers::HandleConn(Message& msg) + void MessageHandlers::HandleConn(const Message& msg) { const unsigned char* buffer = msg.GetBuffer(); @@ -122,23 +137,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); @@ -149,31 +162,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. + // 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; } @@ -181,63 +189,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; + unsigned char aircraftNumber = 0; if (size >= 27) { - aircraft = buffer[26]; + aircraftNumber = buffer[26]; } - float spdbrk = -998; + float spdbrk = DataManager::GetDefaultValue(); if (size >= 31) { spdbrk = *((float*)(buffer + 27)); } - if (pitch < -999.5 || pitch > -997.5) + if (!DataManager::IsDefault(pitch)) { - DataManager::Set(DREF_YokePitch, pitch, aircraft); + DataManager::Set(DREF_YokePitch, pitch, aircraftNumber); } - if (roll < -999.5 || roll > -997.5) + if (!DataManager::IsDefault(roll)) { - DataManager::Set(DREF_YokeRoll, roll, aircraft); + DataManager::Set(DREF_YokeRoll, roll, aircraftNumber); } - if (yaw < -999.5 || yaw > -997.5) + if (!DataManager::IsDefault(yaw)) { - DataManager::Set(DREF_YokeHeading, yaw, aircraft); + DataManager::Set(DREF_YokeHeading, yaw, aircraftNumber); } - if (thr < -999.5 || thr > -997.5) + if (!DataManager::IsDefault(throttle)) { - float thrArray[8]; + float throttleArray[8]; for (int i = 0; i < 8; ++i) { - thrArray[i] = thr; + throttleArray[i] = throttle; } - DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft); - DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft); - if (aircraft == 0) + 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", thrArray, 1); + 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 (spdbrk < -999.5 || spdbrk > -997.5) + if (!DataManager::IsDefault(spdbrk)) { - DataManager::Set(DREF_SpeedBrakeSet, spdbrk, aircraft); + 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(); @@ -245,59 +253,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; @@ -305,7 +305,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)); @@ -316,27 +316,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]; } @@ -355,17 +353,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 @@ -374,10 +370,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); @@ -387,7 +381,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 @@ -400,46 +394,96 @@ 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::HandleGetC(const Message& msg) + { + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + if (size != 6) + { + Log::FormatLine(LOG_ERROR, "GCTL", "Unexpected message length: %u", size); + return; + } + unsigned char aircraft = buffer[5]; + // TODO(jason-watkins): Get proper printf specifier for unsigned char + Log::FormatLine(LOG_TRACE, "GCTL", "Getting control information for aircraft %u", aircraft); + + float throttle[8]; + unsigned char response[31] = "CTRL"; + *((float*)(response + 5)) = DataManager::GetFloat(DREF_Elevator, aircraft); + *((float*)(response + 9)) = DataManager::GetFloat(DREF_Aileron, aircraft); + *((float*)(response + 13)) = DataManager::GetFloat(DREF_Rudder, aircraft); + DataManager::GetFloatArray(DREF_ThrottleSet, throttle, 8, aircraft); + *((float*)(response + 17)) = throttle[0]; + if (aircraft == 0) + { + response[21] = (char)DataManager::GetInt(DREF_GearHandle, aircraft); + } + else + { + float mpGear[10]; + DataManager::GetFloatArray(DREF_GearDeploy, mpGear, 10, aircraft); + response[21] = mpGear[0] > 0.5 ? 1 : 0; + } + *((float*)(response + 22)) = DataManager::GetFloat(DREF_FlapSetting, aircraft); + response[26] = aircraft; + *((float*)(response + 27)) = DataManager::GetFloat(DREF_SpeedBrakeSet, aircraft); + + sock->SendTo(response, 31, &connection.addr); + } + + 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) { @@ -466,64 +510,84 @@ namespace XPC sock->SendTo(response, cur, &connection.addr); } - void MessageHandlers::HandlePosi(Message& msg) + void MessageHandlers::HandleGetP(const Message& msg) + { + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + if (size != 6) + { + Log::FormatLine(LOG_ERROR, "GPOS", "Unexpected message length: %u", size); + return; + } + unsigned char aircraft = buffer[5]; + Log::FormatLine(LOG_TRACE, "GPOS", "Getting position information for aircraft %u", aircraft); + + unsigned char response[34] = "POSI"; + response[5] = (char)DataManager::GetInt(DREF_GearHandle, aircraft); + *((float*)(response + 6)) = (float)DataManager::GetDouble(DREF_Latitude, aircraft); + *((float*)(response + 10)) = (float)DataManager::GetDouble(DREF_Longitude, aircraft); + *((float*)(response + 14)) = (float)DataManager::GetDouble(DREF_Elevation, aircraft); + *((float*)(response + 18)) = DataManager::GetFloat(DREF_Pitch, aircraft); + *((float*)(response + 22)) = DataManager::GetFloat(DREF_Roll, aircraft); + *((float*)(response + 26)) = DataManager::GetFloat(DREF_HeadingTrue, aircraft); + + float gear[10]; + DataManager::GetFloatArray(DREF_GearDeploy, gear, 10, aircraft); + *((float*)(response + 30)) = gear[0]; + + sock->SendTo(response, 34, &connection.addr); + } + + 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 > 1 - Log::FormatLine("[SIMU] Message Received (Conn %i)", connection.id); -#endif + Log::FormatLine(LOG_TRACE, "SIMU", "Message Received (Conn %i)", connection.id); char v = msg.GetBuffer()[5]; if (v < 0 || v > 2) { -#if LOG_VERBOSITY > 0 - Log::FormatLine("[SIMU] ERROR: Invalid argument: %i", v); + Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v); return; -#endif } int value[20]; @@ -546,28 +610,24 @@ namespace XPC // Set DREF DataManager::Set(DREF_Pause, value, 20); -#if LOG_VERBOSITY > 2 switch (v) { case 0: - Log::WriteLine("[SIMU] Simulation Resumed"); + Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed"); break; case 1: - Log::WriteLine("[SIMU] Simulation Paused"); + Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused"); break; case 2: - Log::WriteLine("[SIMU] Simulation switched."); + 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(); @@ -575,18 +635,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 { @@ -594,18 +650,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(); @@ -622,9 +690,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: @@ -637,18 +703,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); @@ -656,11 +718,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 b7cfd25..d7f317d 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,27 @@ 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 HandleGetC(const Message& msg); + static void HandleGetD(const Message& msg); + static void HandleGetP(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 { diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index ed4710d..210cc56 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 succeeded. (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 184f6e5..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. /// diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index cd25d96..f94b98a 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 @@ -90,40 +91,37 @@ static float XPCFlightLoopCallback(float inElapsedSinceLastCall, float inElapsed PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) { - strcpy(outName, "X-Plane Connect [Version 1.0.1]"); + strcpy(outName, "X-Plane Connect [Version 1.2.0]"); 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.1"); + 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; @@ -134,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) @@ -143,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; } @@ -173,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++) @@ -196,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/lin.xpl b/xpcPlugin/XPlaneConnect/64/lin.xpl index 991d757..476986c 100755 Binary files a/xpcPlugin/XPlaneConnect/64/lin.xpl and b/xpcPlugin/XPlaneConnect/64/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index e171bde..1328419 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/lin.xpl b/xpcPlugin/XPlaneConnect/lin.xpl index e4dd997..a1780c7 100755 Binary files a/xpcPlugin/XPlaneConnect/lin.xpl and b/xpcPlugin/XPlaneConnect/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index 395ca48..0813198 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 a7c37d5..d752984 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 */, @@ -259,7 +253,7 @@ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "c++98"; CLANG_CXX_LIBRARY = "libc++"; - CONFIGURATION_BUILD_DIR = ./Mac; + CONFIGURATION_BUILD_DIR = ./XPlaneConnect; DYLIB_COMPATIBILITY_VERSION = ""; DYLIB_CURRENT_VERSION = ""; EXECUTABLE_EXTENSION = xpl; @@ -371,7 +365,11 @@ "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/xplaneConnect-asdjuezcjkhojuewbyxhyhabxfwc/Build/Products/Debug", ); MACH_O_TYPE = mh_bundle; +<<<<<<< HEAD ONLY_ACTIVE_ARCH = YES; +======= + ONLY_ACTIVE_ARCH = NO; +>>>>>>> master PRODUCT_NAME = mac; SDKROOT = macosx; STRIP_INSTALLED_PRODUCT = YES;