diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index 4215038..521abc5 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -693,5 +693,6 @@ public class XPlaneConnect implements AutoCloseable inSocket.close(); inSocket = new DatagramSocket(recvPort); inSocket.setSoTimeout(soTimeout); + readUDP(); // Try to read response } } diff --git a/MATLAB/+XPlaneConnect/setConn.m b/MATLAB/+XPlaneConnect/setConn.m index ad757a4..39c2fbf 100644 --- a/MATLAB/+XPlaneConnect/setConn.m +++ b/MATLAB/+XPlaneConnect/setConn.m @@ -46,5 +46,6 @@ function status = setConn( recvPort, IP, port ) global udpReadPort; udpReadPort = recvPort; + readUDP(); % Read and discard CONF message end \ No newline at end of file diff --git a/README.md b/README.md index fbe40ce..8769654 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,44 @@ -XPlaneConnect -============= +#X-Plane Connect +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 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 of an XPlane simulation, flight visualization, recording states during a flight, or interacting with a mission over UDP. -The X-Plane Communications Toolbox (XPC) 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 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. +###Architecture +XPC includes an X-Plane plugin (xpcPlugin) and clients written in several languages that interact with the plugin. + +####Quick Start +To get started using X-Plane Connect, do the following. + +1. Purchase and install X-Plane 9 or 10. +2. Copy the X-Plane Plugin folder (xpcPlugin/XPlaneConnect) to the plugin directory ([X-Plane Directory]/Resources/plugins/) +3. Write some code using one of the clients to manipulate X-Plane data. + +Each client is located in a top-level directory of the repository named for the client's language. The client directories generally include a 'src' folder containing the client source code, and an 'Examples' folder containing sample code demonstrating how to use the client. + +####Additional Information +For detailed information about XPC and how to use the XPC clients, refer to the [XPC Wiki](https://github.com/nasa/XPlaneConnect/wiki). + +####Capabilities +The XPC Toolbox allows the user to manipulate the internal state of X-Plane by reading and setting DataRefs, a complete list of which can be found on the [X-Plane SDK wiki](http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html). + +In addition, several convenience functions are provided, which allow the user to efficiently execute common commands. These functions include the ability to set the position and control surfaces of both player and multiplayer aircraft. In addition, the pause function allows users to easily pause and un-pause X-Plane's physics simulation engine. + +###Compatibility +XPC has been tested with the following software versions: +* Windows: Vista, 7, & 8 +* Mac OSX: 10.8-10.10 +* X-Plane: 9 & 10 + +###Notices +Copyright ©2013-2015 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. + +No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." + +Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +####X-Plane API +Copyright (c) 2008, Sandy Barbour and Ben Supnik All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Neither the names of the authors nor that of X-Plane or Laminar Research may be used to endorse or promote products derived from this software without specific prior written permission from the authors or Laminar Research, respectively. + +X-Plane API SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Readme.txt b/Readme.txt deleted file mode 100644 index f906e0d..0000000 --- a/Readme.txt +++ /dev/null @@ -1,60 +0,0 @@ -X-Plane Connect (XPC) Toolbox - -Description -The X-Plane Connect (XPC) Toolbox facilitates communication with X-Plane. This toolbox allows for the real-time manipulation of X-Plane variables. Possible applications include active control of an XPlane simulation, flight visualization, recording states during a flight, or interacting with a mission over UDP. - -Architecture -XPC includes a plugin (xpcPlugin) which is to be copied into the X-Plane Plugin Directory ([X-Plane Directory]/Resources/Plugin/), and the xpcScripts-A series of functions for communication with X-Plane. - -xpcPlugin (Directory: xpcPlugin/) - -xpcScripts - C: (Directory: C/src/, Example: C/xpcExample/) - MATLAB: (Directory: Matlab/+XPlaneConnect/, Example: MATLAB/xpcExample/) - java: (Directory: java/src/, Example: java/xpcExample/) - python: (future-Not complete yet): (Directory: python/xpc/, Example: python/xpcExample/) - -Instructions: - 1. Purchase/Install X-Plane - 2. Copy the X-Plane Plugin folder (xpcPlugin/XPlaneConnect) to the plugin directory ([X-Plane Directory]/Resources/plugins/) - 3. Write code using the xpcScrips to manipulate X-Plane - -Capabilities: - Set Aircraft Position (own or other aircraft): Use sendPOSI() - Control Aircraft (own or other aircraft): Use sendCTRL() - Set any internal X-Plane dataref: Use sendDREF() - Get the value of any X-Plane dataref: Use requestDREF() - Pause Simulation: Use pauseSim() - -Compatability: - Windows: - Tested on Windows Vista and Windows 7 - Mac OSX - Tested on OS X 10.8-10.10 - X-Plane - Tested with X-Plane 9 & 10 - -Notices: - -Copyright ©2013-2014 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. - -Disclaimers - -No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." - -Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. -X-Plane API -Copyright (c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Neither the names of the authors nor that of X-Plane or Laminar Research - may be used to endorse or promote products derived from this software - without specific prior written permission from the authors or - Laminar Research, respectively. - -X-Plane API SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Contributors: - CT: Chris Teubert (christopher.a.teubert@nasa.gov) diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp new file mode 100644 index 0000000..86d3d80 --- /dev/null +++ b/xpcPlugin/DataManager.cpp @@ -0,0 +1,551 @@ +//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 "DataManager.h" +#include "Log.h" + +#include "XPLMDataAccess.h" +#include "XPLMGraphics.h" + +#include +#include + +namespace XPC +{ + static std::unordered_map drefs; + static std::unordered_map mdrefs[20]; + static std::unordered_map sdrefs; + + void DataManager::Initialize() + { + drefs = std::unordered_map( + { + { DREF::None, XPLMFindDataRef("sim/test/test_float") }, + + { DREF::Pause, XPLMFindDataRef("sim/operation/override/override_planepath") }, + { DREF::PauseAI, XPLMFindDataRef("sim/operation/override/override_plane_ai_autopilot") }, + + { DREF::TotalRuntime, XPLMFindDataRef("sim/time/total_running_time_sec") }, + { DREF::TotalFlighttime, XPLMFindDataRef("sim/time/total_flight_time_sec") }, + { DREF::TimerElapsedtime, XPLMFindDataRef("sim/time/timer_elapsed_time_sec") }, + + { DREF::IndicatedAirspeed, XPLMFindDataRef("sim/flightmodel/position/indicated_airspeed") }, + { DREF::TrueAirspeed, XPLMFindDataRef("sim/flightmodel/position/true_airspeed") }, + { DREF::GroundSpeed, XPLMFindDataRef("sim/flightmodel/position/groundspeed") }, + + { DREF::MachNumber, XPLMFindDataRef("sim/flightmodel/misc/machno") }, + { DREF::GForceNormal, XPLMFindDataRef("sim/flightmodel2/misc/gforce_normal") }, + { DREF::GForceAxial, XPLMFindDataRef("sim/flightmodel2/misc/gforce_axial") }, + { DREF::GForceSide, XPLMFindDataRef("sim/flightmodel2/misc/gforce_side") }, + + { DREF::BarometerSealevelInHg, XPLMFindDataRef("sim/weather/barometer_sealevel_inhg") }, + { DREF::TemperaturSealevelC, XPLMFindDataRef("sim/weather/temperature_sealevel_c") }, + { DREF::WindSpeedKts, XPLMFindDataRef("sim/cockpit2/gauges/indicators/wind_speed_kts") }, + + { DREF::YokePitch, XPLMFindDataRef("sim/joystick/yoke_pitch_ratio") }, + { DREF::YokeRoll, XPLMFindDataRef("sim/joystick/yoke_roll_ratio") }, + { DREF::YokeHeading, XPLMFindDataRef("sim/joystick/yoke_heading_ratio") }, + + { DREF::Elevator, XPLMFindDataRef("sim/cockpit2/controls/yoke_pitch_ratio") }, + { DREF::Aileron, XPLMFindDataRef("sim/cockpit2/controls/yoke_roll_ratio") }, + { DREF::Rudder, XPLMFindDataRef("sim/cockpit2/controls/yoke_heading_ratio") }, + + { DREF::FlapSetting, XPLMFindDataRef("sim/flightmodel/controls/flaprqst") }, + { DREF::FlapActual, XPLMFindDataRef("sim/flightmodel/controls/flaprat") }, + + { DREF::GearDeploy, XPLMFindDataRef("sim/aircraft/parts/acf_gear_deploy") }, + { DREF::GearHandle, XPLMFindDataRef("sim/cockpit/switches/gear_handle_status") }, + { DREF::BrakeParking, XPLMFindDataRef("sim/flightmodel/controls/parkbrakel") }, + { DREF::BrakeLeft, XPLMFindDataRef("sim/cockpit2/controls/left_brake_ratio") }, + { DREF::BrakeRight, XPLMFindDataRef("sim/cockpit2/controls/right_brake_ratio") }, + + { DREF::M, XPLMFindDataRef("sim/flightmodel/position/M") }, + { DREF::L, XPLMFindDataRef("sim/flightmodel/position/L") }, + { DREF::N, XPLMFindDataRef("sim/flightmodel/position/N") }, + + { DREF::QRad, XPLMFindDataRef("sim/flightmodel/position/Qrad") }, + { DREF::PRad, XPLMFindDataRef("sim/flightmodel/position/Prad") }, + { DREF::RRad, XPLMFindDataRef("sim/flightmodel/position/Rrad") }, + { DREF::Q, XPLMFindDataRef("sim/flightmodel/position/Q") }, + { DREF::P, XPLMFindDataRef("sim/flightmodel/position/P") }, + { DREF::R, XPLMFindDataRef("sim/flightmodel/position/R") }, + + { DREF::Pitch, XPLMFindDataRef("sim/flightmodel/position/theta") }, + { DREF::Roll, XPLMFindDataRef("sim/flightmodel/position/phi") }, + { DREF::HeadingTrue, XPLMFindDataRef("sim/flightmodel/position/psi") }, + { DREF::HeadingMag, XPLMFindDataRef("sim/flightmodel/position/magpsi") }, + { DREF::Quaternion, XPLMFindDataRef("sim/flightmodel/position/q") }, + + { DREF::AngleOfAttack, XPLMFindDataRef("sim/flightmodel/position/alpha") }, + { DREF::Sideslip, XPLMFindDataRef("sim/cockpit2/gauges/indicators/sideslip_degrees") }, + { DREF::HPath, XPLMFindDataRef("sim/flightmodel/position/hpath") }, + { DREF::VPath, XPLMFindDataRef("sim/flightmodel/position/vpath") }, + + { DREF::VPath, XPLMFindDataRef("sim/flightmodel/position/magnetic_variation") }, + + { DREF::Latitude, XPLMFindDataRef("sim/flightmodel/position/latitude") }, + { DREF::Longitude, XPLMFindDataRef("sim/flightmodel/position/longitude") }, + { DREF::AGL, XPLMFindDataRef("sim/flightmodel/position/y_agl") }, + + { DREF::LocalX, XPLMFindDataRef("sim/flightmodel/position/local_x") }, + { DREF::LocalY, XPLMFindDataRef("sim/flightmodel/position/local_y") }, + { DREF::LocalZ, XPLMFindDataRef("sim/flightmodel/position/local_z") }, + { DREF::LocalVX, XPLMFindDataRef("sim/flightmodel/position/local_vx") }, + { DREF::LocalVY, XPLMFindDataRef("sim/flightmodel/position/local_vy") }, + { DREF::LocalVZ, XPLMFindDataRef("sim/flightmodel/position/local_vz") }, + + { DREF::ThrottleSet, XPLMFindDataRef("sim/flightmodel/engine/ENGN_thro") }, + { DREF::ThrottleActual, XPLMFindDataRef("sim/flightmodel2/engines/throttle_used_ratio") }, + + { DREF::MP1Lat, XPLMFindDataRef("sim/multiplayer/position/plane1_lat") }, + { DREF::MP2Lat, XPLMFindDataRef("sim/multiplayer/position/plane2_lat") }, + { DREF::MP3Lat, XPLMFindDataRef("sim/multiplayer/position/plane3_lat") }, + { DREF::MP4Lat, XPLMFindDataRef("sim/multiplayer/position/plane4_lat") }, + { DREF::MP5Lat, XPLMFindDataRef("sim/multiplayer/position/plane5_lat") }, + { DREF::MP6Lat, XPLMFindDataRef("sim/multiplayer/position/plane6_lat") }, + { DREF::MP7Lat, XPLMFindDataRef("sim/multiplayer/position/plane7_lat") }, + + { DREF::MP1Lon, XPLMFindDataRef("sim/multiplayer/position/plane1_lon") }, + { DREF::MP2Lon, XPLMFindDataRef("sim/multiplayer/position/plane2_lon") }, + { DREF::MP3Lon, XPLMFindDataRef("sim/multiplayer/position/plane3_lon") }, + { DREF::MP4Lon, XPLMFindDataRef("sim/multiplayer/position/plane4_lon") }, + { DREF::MP5Lon, XPLMFindDataRef("sim/multiplayer/position/plane5_lon") }, + { DREF::MP6Lon, XPLMFindDataRef("sim/multiplayer/position/plane6_lon") }, + { DREF::MP7Lon, XPLMFindDataRef("sim/multiplayer/position/plane7_lon") }, + + { DREF::MP1Alt, XPLMFindDataRef("sim/multiplayer/position/plane1_el") }, + { DREF::MP2Alt, XPLMFindDataRef("sim/multiplayer/position/plane2_el") }, + { DREF::MP3Alt, XPLMFindDataRef("sim/multiplayer/position/plane3_el") }, + { DREF::MP4Alt, XPLMFindDataRef("sim/multiplayer/position/plane4_el") }, + { DREF::MP5Alt, XPLMFindDataRef("sim/multiplayer/position/plane5_el") }, + { DREF::MP6Alt, XPLMFindDataRef("sim/multiplayer/position/plane6_el") }, + { DREF::MP7Alt, XPLMFindDataRef("sim/multiplayer/position/plane7_el") }, + }); + + char multi[256]; + for (int i = 1; i < 20; i++) + { + sprintf(multi, "sim/multiplayer/position/plane%i_x", i); // X + mdrefs[i][DREF::LocalX] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_y", i); // Y + mdrefs[i][DREF::LocalY] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_z", i); // Z + mdrefs[i][DREF::LocalZ] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_the", i); // Theta (Pitch) + mdrefs[i][DREF::Pitch] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_phi", i); // Phi (Roll) + mdrefs[i][DREF::Roll] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_psi", i); // Psi (Heading-True) + mdrefs[i][DREF::HeadingTrue] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i); // Landing Gear + 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); + 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); + mdrefs[i][DREF::Spoiler] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_speedbrake_ratio", i); + mdrefs[i][DREF::BrakeSpeed] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_slat_ratio", i); + mdrefs[i][DREF::Slats] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_wing_sweep", i); + mdrefs[i][DREF::Sweep] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i); + mdrefs[i][DREF::ThrottleActual] = XPLMFindDataRef(multi); + 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); + mdrefs[i][DREF::YokeRoll] = XPLMFindDataRef(multi); + sprintf(multi, "sim/multiplayer/position/plane%i_yolk_yaw", i); + mdrefs[i][DREF::YokeHeading] = XPLMFindDataRef(multi); + } + } + + int DataManager::Get(std::string dref, float values[], int size) + { + XPLMDataRef& xdref = sdrefs[dref]; + if (xdref == nullptr) + { + xdref = XPLMFindDataRef(dref.c_str()); + } + if (!xdref) + { + // DREF does not exist +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DMAN] ERROR: invalid DREF"); +#endif + 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 + switch (dataType) + { + case 1: // Integer + { + values[0] = (float)XPLMGetDatai(xdref); +#if LOG_VERBOSITY > 4 + Log::FormatLine(" value was %i", (int)values[0]); +#endif + return 1; + } + case 4: // Double + { + values[0] = (float)XPLMGetDatad(xdref); +#if LOG_VERBOSITY > 4 + Log::FormatLine(" value was %f", values[0]); +#endif + return 1; + } + case 8: // Float Array + { + int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + XPLMGetDatavf(xdref, values, 0, drefSize); +#if LOG_VERBOSITY > 4 + Log::FormatLine(" value count was %i", drefSize); +#endif + return drefSize; + } + case 16: // Integer Array + { + int iValues[64]; + int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + drefSize = min(drefSize, 64); + XPLMGetDatavi(xdref, iValues, 0, drefSize); + for (int i = 0; i < drefSize; ++i) + { + values[i] = (float)iValues[i]; + } +#if LOG_VERBOSITY > 4 + Log::FormatLine(" value count was %i", drefSize); +#endif + return drefSize; + } + default: // Assume float + { + values[0] = XPLMGetDataf(xdref); +#if LOG_VERBOSITY > 4 + Log::FormatLine(" value was %f", values[0]); +#endif + return 1; + } + } + } + + double DataManager::GetDouble(DREF dref, std::uint8_t aircraft) + { + 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 + return value; + } + + float DataManager::GetFloat(DREF dref, std::uint8_t aircraft) + { + 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 + return value; + } + + int DataManager::GetInt(DREF dref, std::uint8_t aircraft) + { + 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 + return value; + } + + int DataManager::GetFloatArray(DREF dref, float values[], int size, std::uint8_t aircraft) + { + 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 + return resultSize; + } + + int DataManager::GetIntArray(DREF dref, int values[], int size, std::uint8_t aircraft) + { + 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 + return resultSize; + } + + void DataManager::Set(DREF dref, double value, std::uint8_t 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 + XPLMSetDatad(xdref, value); + } + + void DataManager::Set(DREF dref, float value, std::uint8_t 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 + XPLMSetDataf(xdref, value); + } + + void DataManager::Set(DREF dref, int value, std::uint8_t 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 + XPLMSetDatai(xdref, value); + } + + void DataManager::Set(DREF dref, float values[], int size, std::uint8_t 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 + int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + XPLMSetDatavf(xdref, values, 0, drefSize); + } + + void DataManager::Set(DREF dref, int values[], int size, std::uint8_t 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 + int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + XPLMSetDatavi(xdref, values, 0, drefSize); + } + + void DataManager::Set(std::string dref, float values[], int size) + { + XPLMDataRef& xdref = sdrefs[dref]; + if (xdref == nullptr) + { + xdref = XPLMFindDataRef(dref.c_str()); + } +#if LOG_VERBOSITY > 4 + Log::FormatLine("[DMAN] Setting DREF %s (x:%X) (%i values)", dref.c_str(), xdref, size); +#endif + if (!xdref) + { + // DREF does not exist +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DMAN] ERROR: invalid DREF"); +#endif + return; + } + if (std::isnan(values[0])) + { +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DMAN] ERROR: Value must be a number (NaN received)"); +#endif + return; + } + + XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); + switch (dataType) + { + case 1: // Integer + { + XPLMSetDatai(xdref, (int)values[0]); + break; + } + case 4: // Double + { + XPLMSetDatad(xdref, values[0]); + break; + } + case 8: // Float Array + { + int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + XPLMSetDatavf(xdref, values, 0, drefSize); + break; + } + case 16: // Integer Array + { + int iValues[64]; + int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); + drefSize = min(drefSize, size); + drefSize = min(drefSize, 64); + for (int i = 0; i < drefSize; ++i) + { + iValues[i] = (int)values[i]; + } + XPLMSetDatavi(xdref, iValues, 0, drefSize); + break; + } + default: // Assume float + { + XPLMSetDataf(xdref, values[0]); + break; + } + } + } + + void DataManager::SetGear(float gear, bool immediate, std::uint8_t aircraft) + { +#if LOG_VERBOSITY > 3 + Log::FormatLine("[DMAN] Setting gear (value:%f, immediate:%i) for aircraft %i", gear, immediate, aircraft); +#endif + float gearArray[10]; + + if ((gear < -8.5f && gear > 9.5f) || (gear < -997.9f && gear > -999.1f)) + { + return; + } + if (std::isnan(gear) || gear < 0 || gear > 1) + { + Log::WriteLine("[GEAR] ERROR: Value must be 0 or 1"); + return; + } + + for (int i = 0; i < 10; i++) + { + gearArray[i] = gear; + } + + if (aircraft == 0) // Own aircraft + { + Set(DREF::GearHandle, (int)gear); + if (immediate) + { + Set(DREF::GearDeploy, gearArray, 10); + } + } + else // Multiplayer aircraft + { + XPLMSetDatavf(mdrefs[aircraft][DREF::GearDeploy], gearArray, 0, 10); + } + } + + void DataManager::SetPosition(float pos[3], std::uint8_t aircraft) + { +#if LOG_VERBOSITY > 3 + Log::FormatLine("[DMAN] Setting position (%f, %f, %f) for aircraft %i", pos[0], pos[1], pos[2], aircraft); +#endif + if (std::isnan(pos[0] + pos[1] + pos[2])) + { +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DMAN] ERROR: Position must be a number (NaN received)"); +#endif + return; + } + + double local[3] = { 0 }; + XPLMWorldToLocal(pos[0], pos[1], pos[2], &local[0], &local[1], &local[2]); + if (aircraft == 0) // Player aircraft + { + // If the sim is paused, setting global position won't update the + // local position, so set them just in case. + Set(DREF::LocalX, local[0]); + Set(DREF::LocalY, local[1]); + Set(DREF::LocalZ, local[2]); + // If the sim is unpaused, this will override the above settings. + Set(DREF::Latitude, (double)pos[0]); + Set(DREF::Longitude, (double)pos[1]); + Set(DREF::AGL, (double)pos[2]); + } + else // Multiplayer + { + XPLMSetDatad(mdrefs[aircraft][DREF::LocalX], local[0]); + XPLMSetDatad(mdrefs[aircraft][DREF::LocalY], local[1]); + XPLMSetDatad(mdrefs[aircraft][DREF::LocalZ], local[2]); + } + } + + void DataManager::SetOrientation(float orient[3], std::uint8_t aircraft) + { +#if LOG_VERBOSITY > 3 + Log::FormatLine("[DMAN] Setting orientation (%f, %f, %f) for aircraft %i", + orient[0], orient[1], orient[2], aircraft); +#endif + if (std::isnan(orient[0] + orient[1] + orient[2])) + { +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DMAN] ERROR: Orientation must be a number (NaN received)"); +#endif + return; + } + + if (aircraft == 0) // Player aircraft + { + // If the sim is paused, setting the quaternion won't update these values, + // so we set them here just in case. + Set(DREF::Pitch, orient[0]); + Set(DREF::Roll, orient[1]); + Set(DREF::HeadingTrue, orient[2]); + + // Convert to Quaternions + // from: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane + // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf + float q[4] = { 0 }; + 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]); + + // If the sim is un-paused, this will overwrite the pitch/roll/yaw + // values set above. + Set(DREF::Quaternion, q, 4); + } + else // Multiplayer + { + XPLMSetDataf(mdrefs[aircraft][DREF::Pitch], orient[0]); + XPLMSetDataf(mdrefs[aircraft][DREF::Roll], orient[1]); + XPLMSetDataf(mdrefs[aircraft][DREF::HeadingTrue], orient[2]); + } + } + + void DataManager::SetFlaps(float value) + { + if (std::isnan(value)) + { + Log::WriteLine("[FLAP] ERROR: Value must be a number (NaN received)"); + return; + } + value = max(value, 0); + value = min(value, 1); + + Set(DREF::FlapSetting, value); + Set(DREF::FlapActual, value); + } +} \ No newline at end of file diff --git a/xpcPlugin/DataManager.h b/xpcPlugin/DataManager.h new file mode 100644 index 0000000..85eb018 --- /dev/null +++ b/xpcPlugin/DataManager.h @@ -0,0 +1,411 @@ +//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 + +#include +#include + +namespace XPC +{ + /// Represents named datarefs used by X-Plane Connect + enum class DREF + { + None = 0, + + Pause, + PauseAI, + + // Times + TotalRuntime = 100, + TotalFlighttime, + TimerElapsedtime, + + // Velocities + IndicatedAirspeed = 300, + TrueAirspeed, + GroundSpeed, + + // Mach, VVI, G-loads + MachNumber = 400, + GForceNormal, + GForceAxial, + GForceSide, + + // Atmosphere: Weather + BarometerSealevelInHg = 500, + TemperaturSealevelC, + WindSpeedKts, + + // Joystick + YokePitch = 800, + YokeRoll, + YokeHeading, + + // Control Surfaces + Elevator = 1100, + Aileron, + Rudder, + + // Flaps + FlapSetting = 1300, + FlapActual, + + // Gear & Brakes + GearDeploy = 1400, + GearHandle, + BrakeParking, + BrakeLeft, + BrakeRight, + + // MNR (Angular Moments) + M = 1500, + L, + N, + + //PQR (Angular Velocities) + QRad = 1600, + PRad, + RRad, + Q, + P, + R, + + // Orientation: pitch, roll, yaw, heading + Pitch = 1700, + Roll, + HeadingTrue, + HeadingMag, + Quaternion, + + // Orientation: alpha beta hpath vpath slip + AngleOfAttack = 1800, + Sideslip, + HPath, + VPath, + + MagneticVariation = 1901, + + // Global Position + Latitude = 2000, + Longitude, + AGL, + + // Local Postion & Velocity + LocalX = 2100, + LocalY, + LocalZ, + LocalVX, + LocalVY, + LocalVZ, + + ThrottleSet = 2200, + ThrottleActual = 2300, + + // Multiplayer Aircraft + FlapActual2, + Spoiler, + BrakeSpeed, + Sweep, + Slats, + + // Mulitplayer positon + MP1Lat, + MP2Lat, + MP3Lat, + MP4Lat, + MP5Lat, + MP6Lat, + MP7Lat, + + MP1Lon, + MP2Lon, + MP3Lon, + MP4Lon, + MP5Lon, + MP6Lon, + MP7Lon, + + MP1Alt, + MP2Alt, + MP3Alt, + MP4Alt, + MP5Alt, + MP6Alt, + MP7Alt, + }; + + /// Marshals data between the plugin and X-Plane. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-13 + /// \date Last Updated: 2015-04-13 + class DataManager + { + public: + /// Initializes the internal data used by DataManager to translate DREF values + /// into X-Plane internal data. + static void Initialize(); + + /// Gets a dataref based on its name. + /// + /// \param dref The name of the dref to get. + /// \param values An array in which the result of the operation will be stored. + /// \param size The size of the values array. + /// \returns The number of elements placed in the values array. For scalar + /// drefs, this will be one. For array drefs, this will be the + /// lesser of the size and the number of elements in the dref. + /// + /// \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 + /// the other methods in this class. + /// + /// \remarks For simplicity, this method is provided with only one output type. + /// For most integer and double drefs, this is unlikely to cause issues. + /// However, for drefs where the entire integer range may be used, or + /// 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); + + /// Gets the value of a double dataref. + /// + /// \param dref The dataref to get. + /// \param aircraft The aircraft number for which to get the data. + /// \returns The value of the dref specified if that dref has the type + /// double; otherwise an undefined value. + /// + /// \remarks This method does not verify the type of the dref requested. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the value returned is determined by the X-Plane + /// plugin manager, and is considered undefined by the plugin. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static double GetDouble(DREF dref, std::uint8_t aircraft = 0); + + /// Gets the value of a float dataref. + /// + /// \param dref The dataref to get. + /// \param aircraft The aircraft number for which to get the data. + /// \returns The value of the dref specified if that dref has the type + /// float; otherwise an undefined value. + /// + /// \remarks This method does not verify the type of the dref requested. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the value returned is determined by the X-Plane + /// plugin manager, and is considered undefined by the plugin. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static float GetFloat(DREF dref, std::uint8_t aircraft = 0); + + /// Gets the value of an integer dataref. + /// + /// \param dref The dataref to get. + /// \param aircraft The aircraft number for which to get the data. + /// \returns The value of the dref specified if that dref has the type + /// int; otherwise an undefined value. + /// + /// \remarks This method does not verify the type of the dref requested. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the value returned is determined by the X-Plane + /// plugin manager, and is considered undefined by the plugin. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static int GetInt(DREF dref, std::uint8_t aircraft = 0); + + /// Gets the value of a float array dataref. + /// + /// \param dref The dataref to get. + /// \param values An array in which the result of the operation will be stored. + /// \param size The size of the values array. + /// \param aircraft The aircraft number for which to get the data. + /// \returns The number of elements placed in the values array. This will + /// be the lesser of the size and the number of elements in the dref. + /// + /// \remarks This method does not verify the type of the dref requested. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the value returned is determined by the X-Plane + /// plugin manager, and is considered undefined by the plugin. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static int GetFloatArray(DREF dref, float values[], int size, std::uint8_t aircraft = 0); + + + /// Gets the value of an int array dataref. + /// + /// \param dref The dataref to get. + /// \param values An array in which the result of the operation will be stored. + /// \param size The size of the values array. + /// \param aircraft The aircraft number for which to get the data. + /// \returns The number of elements placed in the values array. This will + /// be the lesser of the size and the number of elements in the dref. + /// + /// \remarks This method does not verify the type of the dref requested. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the value returned is determined by the X-Plane + /// plugin manager, and is considered undefined by the plugin. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static int GetIntArray(DREF dref, int values[], int size, std::uint8_t aircraft = 0); + + /// Sets the value of a dataref + /// + /// \param dref The name of the dref to set. + /// \param values The value to set the dref to. + /// \param size The number of items stored in values. + /// + /// \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 + /// the other methods in this class. + /// + /// \remarks For simplicity, this method is provided with only one input type. + /// For most integer and double drefs, this is unlikely to cause issues. + /// However, for drefs where the entire integer range may be used, or + /// 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); + + /// Sets the value of a double dataref. + /// + /// \param dref The dataref to set. + /// \param value The value to set the dref to. + /// \param aircraft The aircraft number for which to get the data. + /// + /// \remarks This method does not verify the type of the dref being set. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the operation will fail silently. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static void Set(DREF dref, double value, std::uint8_t aircraft = 0); + + /// Sets the value of a float dataref. + /// + /// \param dref The dataref to set. + /// \param value The value to set the dref to. + /// \param aircraft The aircraft number for which to get the data. + /// + /// \remarks This method does not verify the type of the dref being set. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the operation will fail silently. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static void Set(DREF dref, float value, std::uint8_t aircraft = 0); + + /// Sets the value of an integer dataref. + /// + /// \param dref The dataref to set. + /// \param value The value to set the dref to. + /// \param aircraft The aircraft number for which to get the data. + /// + /// \remarks This method does not verify the type of the dref being set. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the operation will fail silently. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static void Set(DREF dref, int value, std::uint8_t aircraft = 0); + + /// Sets the value of a float array dataref. + /// + /// \param dref The dataref to set. + /// \param values The value to set the dref to. + /// \param size The number of items stored in values. + /// \param aircraft The aircraft number for which to get the data. + /// + /// \remarks This method does not verify the type of the dref being set. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the operation will fail silently. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static void Set(DREF dref, float values[], int size, std::uint8_t aircraft = 0); + + /// Sets the value of an integer array dataref. + /// + /// \param dref The dataref to set. + /// \param values The value to set the dref to. + /// \param size The number of items stored in values. + /// \param aircraft The aircraft number for which to get the data. + /// + /// \remarks This method does not verify the type of the dref being set. It is + /// the responsibility of the caller to check the X-Plane documentation + /// and call the appropriate overload of this method. If the wrong + /// overload is called, the operation will fail silently. + /// + /// \remarks Although any combination of dref and aircraft may be passed to this + /// method, most drefs are not valid for multiplayer aircraft. All drefs + /// that are not prefixed with 'MP' are valid for the player aircraft. + /// 'MP' drefs should be called with aircraft 0. + static void Set(DREF dref, int values[], int size, std::uint8_t aircraft = 0); + + /// Sets the landing gear for the specified airplane. + /// + /// \param gear The value to set the gear to. 0 for gear down, 1 for gear up. + /// \param immediate Whether the gear should be forced to the specified position. + /// If immediate is false, only the gear handle dref will be set. + /// \param aircraft The aircraft to set the landing gear status on. + static void SetGear(float gear, bool immediate, std::uint8_t aircraft = 0); + + /// Sets the position of the specified aircraft on the Earth. + /// + /// \param pos An array containing latitude, longitude and altitude in + /// fractional degrees and meters above sea level. + /// \param aircraft The aircraft to set the position of. + static void SetPosition(float pos[3], std::uint8_t aircraft = 0); + + /// Sets the orientation of the specified aircraft. + /// + /// \param orient An array containing the pitch, roll, and yaw orientations + /// to set, all in fractional degrees. + /// \param aircraft The aircraft to set the orientation of. + static void SetOrientation(float orient[3], std::uint8_t aircraft = 0); + + /// Sets flaps on the the player aircraft. + /// + /// \param value The flaps settings. Should be between 0.0 (no flaps) and 1.0 (full flaps). + static void SetFlaps(float value); + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/DataMaps.cpp b/xpcPlugin/DataMaps.cpp new file mode 100644 index 0000000..b477f18 --- /dev/null +++ b/xpcPlugin/DataMaps.cpp @@ -0,0 +1,80 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "DataManager.h" + +namespace XPC +{ + DREF XPData[134][8] + { + {}, // Row 0: Frame Rates + { // Row 1: Times + DREF::None, DREF::TotalRuntime, DREF::TotalFlighttime, DREF::TimerElapsedtime, + }, + {}, // Row 2: Sim stats + { // Row 3: Velocities + DREF::IndicatedAirspeed, DREF::None, DREF::TrueAirspeed, DREF::GroundSpeed + }, + { // Row 4: Mach, VVI, G-Loads + DREF::MachNumber, DREF::None, DREF::None, DREF::None, + DREF::GForceNormal, DREF::GForceAxial, DREF::GForceSide + }, + { // Row 5: Atmosphere: Weather + DREF::BarometerSealevelInHg, DREF::TemperaturSealevelC, DREF::None, DREF::WindSpeedKts + }, + {}, // Row 6: Atmosphere: Aircraft + {}, // Row 7: System Pressures + { // Row 8: Joystick + DREF::YokePitch, DREF::YokeRoll, DREF::YokeHeading + }, + {}, // Row 9: Other Flight Controls + {}, // Row 10: Art stab ail/elv/rud + { // Row 11: Control Surfaces + DREF::Elevator, DREF::Aileron, DREF::Rudder + }, + {}, // Row 12: Wing Sweep/Trust Vec + { // Row 13: trip/flap/slat/s-brakes + DREF::None, DREF::None, DREF::None, DREF::FlapSetting, DREF::FlapActual + }, + { // Row 14: Gear, Braks + DREF::GearDeploy, DREF::BrakeParking, DREF::BrakeLeft, DREF::BrakeRight, + DREF::None, DREF::None, DREF::None, + DREF::GearHandle + }, + { // Row 15: MNR (Angular Moments) + DREF::M, DREF::L, DREF::N + }, + { // Row 16: PQR (Angular Velocities) + DREF::QRad, DREF::PRad, DREF::RRad, DREF::Q, DREF::P, DREF::R + }, + { // Row 17: Orientation: pitch, roll, yaw, heading + DREF::Pitch, DREF::Roll, DREF::HeadingTrue, DREF::HeadingMag, DREF::Quaternion + }, + { // Row 18: Orientation: alpha beta hpath vpath slip + DREF::AngleOfAttack, DREF::Sideslip, DREF::HPath, DREF::VPath, DREF::Sideslip + }, + { // Row 19: Mag Compass + DREF::HeadingMag, DREF::MagneticVariation + }, + { // Row 20: Global Position + DREF::Latitude, DREF::Longitude, DREF::AGL + }, + { // Row 21: Local Position, Velocity + DREF::LocalX, DREF::LocalY, DREF::LocalZ, DREF::LocalVX, DREF::LocalVY, DREF::LocalVZ + }, + { // Row 22: All Planes: Lat + DREF::Latitude, DREF::MP1Lat, DREF::MP2Lat, DREF::MP3Lat, DREF::MP4Lat, DREF::MP5Lat, DREF::MP6Lat, DREF::MP7Lat + }, + { // Row 23: All Planes: Lon + DREF::Longitude, DREF::MP1Lon, DREF::MP2Lon, DREF::MP3Lon, DREF::MP4Lon, DREF::MP5Lon, DREF::MP6Lon, DREF::MP7Lon + }, + { // Row 24: All Planes: Alt + DREF::AGL, DREF::MP1Alt, DREF::MP2Alt, DREF::MP3Alt, DREF::MP4Alt, DREF::MP5Alt, DREF::MP6Alt, DREF::MP7Alt + }, + { // Row 25: Throttle Command + DREF::ThrottleSet + }, + { // Row 26: Throttle Actual + DREF::ThrottleActual + } + }; +} \ No newline at end of file diff --git a/xpcPlugin/DataMaps.h b/xpcPlugin/DataMaps.h new file mode 100644 index 0000000..ef600b7 --- /dev/null +++ b/xpcPlugin/DataMaps.h @@ -0,0 +1,13 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#ifndef XPC_DATAMAPS_H +#define XPC_DATAMAPS_H +#include "DataManager.h" + +namespace XPC +{ + /// Maps X-Plane dataref lines to XPC DREF values. + extern DREF XPData[134][8]; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/Drawing.cpp b/xpcPlugin/Drawing.cpp new file mode 100644 index 0000000..6cb3ff6 --- /dev/null +++ b/xpcPlugin/Drawing.cpp @@ -0,0 +1,302 @@ +//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 "Drawing.h" + +#include "XPLMDisplay.h" +#include "XPLMGraphics.h" +#include "XPLMDataAccess.h" + +#include +#include +//OpenGL includes +#if IBM +#include +#endif +#if __GNUC__ +#include +#else +#include +#endif + +namespace XPC +{ + //Internal Structures + typedef struct + { + double x; + double y; + double z; + } LocalPoint; + + //Internal Memory + static const size_t MSG_MAX = 1024; + static const size_t MSG_LINE_MAX = MSG_MAX / 16; + static bool msgEnabled = false; + static int msgX = -1; + static int msgY = -1; + static char msgVal[MSG_MAX] = { 0 }; + static size_t newLineCount = 0; + static size_t newLines[MSG_LINE_MAX] = { 0 }; + static float rgb[3] = { 0.25F, 1.0F, 0.25F }; + + static const size_t WAYPOINT_MAX = 128; + static bool routeEnabled = false; + static size_t numWaypoints = 0; + static Waypoint waypoints[WAYPOINT_MAX]; + static LocalPoint localPoints[WAYPOINT_MAX]; + + XPLMDataRef planeXref; + XPLMDataRef planeYref; + XPLMDataRef planeZref; + + //Internal Functions + static int cmp(const void * a, const void * b) + { + std::size_t sa = *(size_t*)a; + std::size_t sb = *(size_t*)b; + if (sa > sb) + { + return 1; + } + if (sb > sa) + { + return -1; + } + return 0; + } + + 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 + const float TAN = 0.00436335F; + float h = d * TAN; + + glBegin(GL_QUAD_STRIP); + //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 + glVertex3f(x - h, y - h, z + h); + glVertex3f(x + h, y - h, z + h); + //Bottom + glVertex3f(x - h, y - h, z - h); + glVertex3f(x + h, y - h, z - h); + //Back + glVertex3f(x - h, y + h, z - h); + glVertex3f(x + h, y + h, z - h); + + glEnd(); + glBegin(GL_QUADS); + //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 + 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); + + glEnd(); + } + + static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) + { + XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic); + int y = msgY - 16; + for (size_t i = 0; i < newLineCount; ++i) + { + XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic); + y -= 16; + } + return 1; + } + + static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) + { + float px = XPLMGetDataf(planeXref); + float py = XPLMGetDataf(planeYref); + float pz = XPLMGetDataf(planeZref); + + Waypoint* g; + LocalPoint* l; + //Convert to local + for (size_t i = 0; i < numWaypoints; ++i) + { + g = &waypoints[i]; + l = &localPoints[i]; + XPLMWorldToLocal(g->latitude, g->longitude, g->altitude, + &l->x, &l->y, &l->z); + } + + + //Draw posts + glColor3f(1.0F, 1.0F, 1.0F); + glBegin(GL_LINES); + for (size_t i = 0; i < numWaypoints; ++i) + { + l = &localPoints[i]; + glVertex3f((float)l->x, (float)l->y, (float)l->z); + glVertex3f((float)l->x, -1000.0F, (float)l->z); + } + glEnd(); + + //Draw route + glColor3f(1.0F, 0.0F, 0.0F); + glBegin(GL_LINE_STRIP); + for (size_t i = 0; i < numWaypoints; ++i) + { + l = &localPoints[i]; + glVertex3f((float)l->x, (float)l->y, (float)l->z); + } + glEnd(); + + //Draw markers + glColor3f(1.0F, 1.0F, 1.0F); + for (size_t i = 0; i < numWaypoints; ++i) + { + l = &localPoints[i]; + float xoff = (float)l->x - px; + float yoff = (float)l->y - py; + float zoff = (float)l->z - pz; + float d = sqrtf(xoff*xoff + yoff*yoff + zoff*zoff); + gl_drawCube((float)l->x, (float)l->y, (float)l->z, d); + } + return 1; + } + + //Public Functions + void Drawing::ClearMessage() + { + XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); + msgEnabled = false; + } + + void Drawing::SetMessage(int x, int y, char* msg) + { + //Determine size of message and clear instead if the message string + //is empty. + size_t len = strnlen(msg, MSG_MAX); + if (len == 0) + { + ClearMessage(); + return; + } + + //Set the message, location, and mark new lines. + strncpy(msgVal, msg, len); + newLineCount = 0; + for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i) + { + if (msgVal[i] == '\n' || msgVal[i] == '\r') + { + msgVal[i] = 0; + newLines[newLineCount++] = i + 1; + } + } + msgX = x < 0 ? 10 : x; + msgY = y < 0 ? 600 : y; + + //Enable drawing if necessary + if (!msgEnabled) + { + XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_LastCockpit, 0, NULL); + msgEnabled = true; + } + } + + void Drawing::ClearWaypoints() + { + numWaypoints = 0; + if (routeEnabled) + { + XPLMUnregisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL); + } + return; + } + + void Drawing::AddWaypoints(Waypoint points[], size_t numPoints) + { + if (numWaypoints + numPoints > WAYPOINT_MAX) + { + numPoints = WAYPOINT_MAX - numWaypoints; + } + size_t finalNumWaypoints = numPoints + numWaypoints; + for (size_t i = 0; i < numPoints; ++i) + { + waypoints[numWaypoints + i] = points[i]; + } + numWaypoints = finalNumWaypoints; + + if (!routeEnabled) + { + XPLMRegisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL); + } + if (!planeXref) + { + planeXref = XPLMFindDataRef("sim/flightmodel/position/local_x"); + planeYref = XPLMFindDataRef("sim/flightmodel/position/local_y"); + planeZref = XPLMFindDataRef("sim/flightmodel/position/local_z"); + } + } + + void Drawing::RemoveWaypoints(Waypoint points[], size_t numPoints) + { + //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) + { + Waypoint p = points[i]; + for (size_t j = 0; j < numWaypoints; ++j) + { + Waypoint q = waypoints[j]; + if (p.latitude == q.latitude && + p.longitude == q.longitude && + p.altitude == q.altitude) + { + delPoints[delPointsCur++] = j; + break; + } + } + } + //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 + size_t copyCur = 0; + size_t count = delPointsCur; + delPointsCur = 0; + for (size_t i = 0; i < numWaypoints; ++i) + { + if (i == delPoints[delPointsCur]) + { + ++delPointsCur; + continue; + } + waypoints[copyCur++] = waypoints[i]; + } + numWaypoints -= count; + if (numWaypoints == 0) + { + ClearWaypoints(); + } + } +} \ No newline at end of file diff --git a/xpcPlugin/Drawing.h b/xpcPlugin/Drawing.h new file mode 100644 index 0000000..dbc57e4 --- /dev/null +++ b/xpcPlugin/Drawing.h @@ -0,0 +1,56 @@ +//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 + +#include "xplaneConnect.h" + +#include + +namespace XPC +{ + /// Handles tasks that involve drawing to the screen in X-Plane. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-10 + /// \date Last Updated: 2015-04-10 + class Drawing + { + public: + /// Clears the current message on the screen if any and unregisters the + /// draw callback for message drawing. + static void ClearMessage(); + + /// Sets the message to be drawn on the screen. + /// + /// \param x The x coordinate of the message relative to the left edge + /// of the screen. + /// \param y The y coordinate of the message relative to the bottom + /// edge of the screen + /// \param msg A C string containing the message to display. The message + /// value is copied into a local buffer. + static void SetMessage(int x, int y, char* msg); + + /// Adds the given waypoints to the list of waypoints to draw. + /// + /// \param points A pointer to an array of waypoints. + /// \param numPoints The number of points in the array. + static void AddWaypoints(Waypoint points[], size_t numPoints); + + /// Removes all waypoints and unregisters the callback for waypoint + /// drawing. + static void ClearWaypoints(); + + /// Removes the given waypoints from the list of waypoints to draw. + /// If all waypoints are removed as a result of this action, unregisters + /// the callback for waypoint drawing. + /// + /// \param points A pointer to an array of waypoints. + /// \param numPoints The number of points in the array. + static void RemoveWaypoints(Waypoint points[], size_t numPoints); + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp new file mode 100644 index 0000000..22c6a18 --- /dev/null +++ b/xpcPlugin/Log.cpp @@ -0,0 +1,97 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "Log.h" +#include +#include +#include + +// Implementation note: I initial wrote this class using C++ iostreams, but I couldn't find any +// way to implement FormatLine without adding in a call to sprintf. It therefore seems more +// efficient to me to just use C-style IO and call fprintf directly. +namespace XPC +{ + static void WriteTime(FILE* fd) + { + 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); + } + + void Log::Initialize(std::string version) + { + FILE* fd = fopen("XPCLog.txt", "w"); + if (fd != NULL) + { + time_t rawtime; + tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + + char timeStr[16] = { 0 }; + // Format is equivalent to %F, but neither of those specifiers is + // supported on Windows as of Visual Studio 13 + strftime(timeStr, 16, "%Y-%m-%d", timeinfo); + + fprintf(fd, "X-Plane Connect [Version %s]\n", version.c_str()); + fprintf(fd, "Compiled %s %s\n", __DATE__, __TIME__); + fprintf(fd, "Copyright (c) 2013-2015 United States Government as represented by the\n"); + fprintf(fd, "Administrator of the National Aeronautics and Space Administration.\n"); + fprintf(fd, "All Rights Reserved.\n\n"); + + fprintf(fd, "This file contains debugging information about the X-Plane Connect plugin.\n"); + fprintf(fd, "If you have technical issues with the plugin, please report them by opening\n"); + fprintf(fd, "an issue on GitHub (https://github.com/nasa/XPlaneConnect/issues) or by\n"); + fprintf(fd, "emailing Christopher Teubert (christopher.a.teubert@nasa.gov).\n\n"); + + fprintf(fd, "Log file generated on %s.\n", timeStr); + fclose(fd); + } + } + + void Log::WriteLine(const std::string& value) + { + Log::WriteLine(value.c_str()); + } + + void Log::WriteLine(const char* value) + { + FILE* fd = fopen("XPCLog.txt", "a"); + if (!fd) + { + return; + } + + WriteTime(fd); + fprintf(fd, "%s\n", value); + + fclose(fd); + } + + void Log::FormatLine(const char* format, ...) + { + va_list args; + va_start(args, format); + + FILE* fd = fopen("XPCLog.txt", "a"); + if (!fd) + { + return; + } + + WriteTime(fd); + vfprintf(fd, format, args); + fprintf(fd, "\n"); + + fclose(fd); + + va_end(args); + } +} \ No newline at end of file diff --git a/xpcPlugin/Log.h b/xpcPlugin/Log.h new file mode 100644 index 0000000..136e103 --- /dev/null +++ b/xpcPlugin/Log.h @@ -0,0 +1,59 @@ +//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 +#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. +// 4: Everything. Log nearly every single action the plugin takes. This may +// have a detrimental impact on X-Plane performance. +#define LOG_VERBOSITY 2 + +namespace XPC +{ + /// Handles logging for the plugin. + /// + /// \details Provides functions to write lines to the XPC log file. + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-09 + /// \date Last Updated: 2015-04-09 + 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); + + /// Writes the C 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, ...); + + /// 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); + }; +} +#endif diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp new file mode 100644 index 0000000..3b05f21 --- /dev/null +++ b/xpcPlugin/Message.cpp @@ -0,0 +1,166 @@ +//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 "xplaneConnect.h" + +#include +#include +#include + +namespace XPC +{ + Message::Message() {} + + Message Message::ReadFrom(UDPSocket& sock) + { + Message m; + int len = sock.Read(m.buffer, bufferSize, &m.source); + m.size = len < 0 ? 0 : len; + return m; + } + + std::uint32_t Message::GetMagicNumber() + { + if (size < 4) + { + return 0; + } + return *((std::uint32_t*)buffer); + } + + std::string Message::GetHead() + { + if (size < 4) + { + return ""; + } + return std::string((char*)buffer, 4); + } + + const std::uint8_t* Message::GetBuffer() + { + if (size == 0) + { + return nullptr; + } + return buffer; + } + + std::size_t Message::GetSize() + { + return size; + } + + struct sockaddr Message::GetSource() + { + return source; + } + + void Message::PrintToLog() + { +#if LOG_VERBOSITY > 2 + std::stringstream ss; + ss << "[DEBUG]"; + + // Dump raw bytes to string + ss << std::hex << std::setfill('0'); + for (int i = 0; i < size; ++i) + { + ss << ' ' << std::setw(2) << static_cast(buffer[i]); + } + Log::WriteLine(ss.str()); + + ss.str(""); + ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; + switch (GetMagicNumber()) // Binary version of head + { + case 0x4E4EF443: // CONN + case 0x54505957: // WYPT + case 0x54584554: // TEXT + { + Log::WriteLine(ss.str()); + break; + } + case 0x4C525443: // CTRL + { + xpcCtrl ctrl = parseCTRL((char*)buffer); + ss << " (" << ctrl.pitch << " " << ctrl.roll << " " << ctrl.yaw << ") "; + ss << ctrl.throttle << " " << ctrl.gear << " " << ctrl.flaps; + Log::WriteLine(ss.str()); + break; + } + case 0x41544144: // DATA + { + float dataRef[30][9]; + short numCols = parseDATA((char*)buffer, size, dataRef); + ss << " (" << numCols << " lines)"; + Log::WriteLine(ss.str()); + for (int i = 0; i < numCols; ++i) + { + ss.str(""); + ss << "\t#" << dataRef[i][0]; + for (int j = 1; j < 9; ++j) + { + ss << " " << dataRef[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()); + ss.str(""); + int values = buffer[6 + buffer[5]]; + ss << "\tValues(size " << values << ") ="; + for (int i = 0; i < values; ++i) + { + ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i)); + } + Log::WriteLine(ss.str()); + break; + } + case 0x44544547: // GETD + { + Log::WriteLine(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()); + cur += 1 + buffer[cur]; + } + break; + } + case 0x49534F50: // POSI + { + float position[6] = { 0.0 }; + short aircraft = 0; + float gear = -1.0; + aircraft = parsePOSI((char*)buffer, position, 6, &gear); + ss << ' ' << aircraft; + ss << " (" << position[0] << ' ' << position[1] << ' ' << position[2] << ") ("; + ss << position[3] << ' ' << position[4] << ' ' << position[5] << ") "; + ss << gear; + Log::WriteLine(ss.str()); + break; + } + case 0x554D4953: // SIMU + { + ss << ' ' << (int)buffer[5]; + Log::WriteLine(ss.str()); + break; + } + default: + { + ss << " UNKNOWN HEADER "; + Log::WriteLine(ss.str()); + break; + } + } +#endif + } +} \ No newline at end of file diff --git a/xpcPlugin/Message.h b/xpcPlugin/Message.h new file mode 100644 index 0000000..9bcfe49 --- /dev/null +++ b/xpcPlugin/Message.h @@ -0,0 +1,57 @@ +//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 + +#include "UDPSocket.h" + +namespace XPC +{ + /// Represents a message received from an XPC client. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-11 + /// \date Last Updated: 2015-04-11 + class Message + { + public: + /// Reads a datagram from the specified socket and interprets it as a + /// message. + /// + /// \param sock The socket to read from. + /// \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); + + /// Gets the message header in binary form. + std::uint32_t GetMagicNumber(); + + /// Gets the message header. + std::string GetHead(); + + /// Gets the buffer underlying the message. + const std::uint8_t* GetBuffer(); + + /// Gets the size of the message in bytes. + std::size_t GetSize(); + + /// Gets the address this message was read from. + struct sockaddr GetSource(); + + /// Prints the contents of the message to the XPC log. + void PrintToLog(); + + private: + Message(); + + static const std::size_t bufferSize = 4096; + std::uint8_t buffer[bufferSize]; + std::size_t size; + struct sockaddr source; + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp new file mode 100644 index 0000000..ac8c35a --- /dev/null +++ b/xpcPlugin/MessageHandlers.cpp @@ -0,0 +1,634 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +#include "MessageHandlers.h" +#include "DataManager.h" +#include "DataMaps.h" +#include "Drawing.h" +#include "Log.h" + +namespace XPC +{ + std::unordered_map MessageHandlers::connections; + std::unordered_map MessageHandlers::handlers( + { + // Common messages + { "CONN", MessageHandlers::HandleConn }, + { "CTRL", MessageHandlers::HandleCtrl }, + { "DATA", MessageHandlers::HandleData }, + { "DREF", MessageHandlers::HandleDref }, + { "GETD", MessageHandlers::HandleGetD }, + { "POSI", MessageHandlers::HandlePosi }, + { "SIMU", MessageHandlers::HandleSimu }, + { "TEXT", MessageHandlers::HandleText }, + { "WYPT", MessageHandlers::HandleWypt }, + // Not implemented messages + { "VIEW", MessageHandlers::HandleUnknown }, + // X-Plane data messages + { "DSEL", MessageHandlers::HandleXPlaneData }, + { "USEL", MessageHandlers::HandleXPlaneData }, + { "DCOC", MessageHandlers::HandleXPlaneData }, + { "UCOC", MessageHandlers::HandleXPlaneData }, + { "MOUS", MessageHandlers::HandleXPlaneData }, + { "CHAR", MessageHandlers::HandleXPlaneData }, + { "MENU", MessageHandlers::HandleXPlaneData }, + { "SOUN", MessageHandlers::HandleXPlaneData }, + { "FAIL", MessageHandlers::HandleXPlaneData }, + { "RECO", MessageHandlers::HandleXPlaneData }, + { "PAPT", MessageHandlers::HandleXPlaneData }, + { "VEHN", MessageHandlers::HandleXPlaneData }, + { "VEH1", MessageHandlers::HandleXPlaneData }, + { "VEHA", MessageHandlers::HandleXPlaneData }, + { "GSET", MessageHandlers::HandleXPlaneData }, + { "OBJN", MessageHandlers::HandleXPlaneData }, + { "OBJL", MessageHandlers::HandleXPlaneData }, + { "GSET", MessageHandlers::HandleXPlaneData }, + { "ISET", MessageHandlers::HandleXPlaneData }, + { "BOAT", MessageHandlers::HandleXPlaneData }, + }); + + std::string MessageHandlers::connectionKey; + MessageHandlers::ConnectionInfo MessageHandlers::connection; + UDPSocket* MessageHandlers::sock; + + void MessageHandlers::SetSocket(UDPSocket* socket) + { + MessageHandlers::sock = socket; + } + + static std::string getIP(sockaddr* sa) + { + char ip[INET6_ADDRSTRLEN + 6] = { 0 }; + switch (sa->sa_family) + { + case AF_INET: + { + sockaddr_in* sin = reinterpret_cast(sa); + inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN); + break; + } + case AF_INET6: + { + sockaddr_in6* sin = reinterpret_cast(sa); + inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN); + break; + } + default: + return "UNKNOWN"; + } + return std::string(ip); + } + + static std::uint16_t getPort(sockaddr* sa) + { + switch (sa->sa_family) + { + case AF_INET: + { + sockaddr_in *sin = reinterpret_cast(sa); + return ntohs((*sin).sin_port); + } + case AF_INET6: + { + sockaddr_in6 *sin = reinterpret_cast(sa); + return ntohs((*sin).sin6_port); + } + default: + return ~0; + } + } + + void MessageHandlers::HandleMessage(Message& msg) + { + // Make sure we really have a message to handle. + std::string head = msg.GetHead(); + if (head == "") + { + return; + } + msg.PrintToLog(); + + // Set current connection + sockaddr sourceaddr = msg.GetSource(); + std::string ip = getIP(&sourceaddr); + std::uint16_t port = getPort(&sourceaddr); + connectionKey = ip + ":" + std::to_string(port); +#if LOG_VERBOSITY > 4 + Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str()); +#endif + auto conn = connections.find(connectionKey); + if (conn == connections.end()) // New connection + { + connection = MessageHandlers::ConnectionInfo + { + // If this is a new connection, that means we just added an elment + // to connections. As long as we never remove elements, the size of + // connections will serve as a unique id. + connections.size(), + ip, + 49008, // By default, send information to the client on this port. + port, + 0 + }; + connections[connectionKey] = connection; +#if LOG_VERBOSITY > 4 + Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s:%u", connection.id, ip.c_str(), port); +#endif + } + else + { + connection = (*conn).second; +#if LOG_VERBOSITY > 4 + Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s:%u", + connection.id, connection.ip.c_str(), connection.srcPort); +#endif + } + + // Check if there is a handler for this message type. If so, execute + // that handler. Othewwise, execute the unknown message handler. + auto iter = handlers.find(head); + if (iter != handlers.end()) + { + MessageHandler handler = (*iter).second; + handler(msg); + } + else + { + MessageHandlers::HandleUnknown(msg); + } + } + + void MessageHandlers::HandleConn(Message& msg) + { + const std::uint8_t* buffer = msg.GetBuffer(); + + // Store new port + connection.dstPort = *((std::uint16_t*)(buffer + 5)); + connections[connectionKey] = connection; + + // Create response + std::uint8_t response[6] = "CONF"; + response[5] = connection.id; + + // Update log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[CONN] ID: %u New destination port: %u", + connection.id, connection.dstPort); +#endif + + // Send response + sock->SendTo(response, 6, connection.ip, connection.dstPort); + } + + void MessageHandlers::HandleCtrl(Message& msg) + { + // Update Log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id); +#endif + + const std::uint8_t* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + //Legacy packets that don't specify an aircraft number should be 26 bytes long. + //Packets specifying an A/C num should be 27 bytes. + if (size != 26 && size != 27) + { +#if LOG_VERBOSITY > 1 + Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size); +#endif + return; + } + + // Parse message data + float pitch = *((float*)(buffer + 5)); + float roll = *((float*)(buffer + 9)); + float yaw = *((float*)(buffer + 13)); + float thr = *((float*)(buffer + 17)); + std::int8_t gear = buffer[21]; + float flaps = *((float*)(buffer + 22)); + std::uint8_t aircraft = 0; + if (size == 27) + { + aircraft = buffer[26]; + } + + float thrArray[8]; + for (int i = 0; i < 8; ++i) + { + thrArray[i] = thr; + } + + DataManager::Set(DREF::YokePitch, pitch, aircraft); + DataManager::Set(DREF::YokeRoll, roll, aircraft); + DataManager::Set(DREF::YokeHeading, yaw, aircraft); + DataManager::Set(XPC::DREF::ThrottleSet, thrArray, 8, aircraft); + DataManager::Set(XPC::DREF::ThrottleActual, thrArray, 8, aircraft); + if (aircraft == 0) + { + DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", thrArray, 1); + } + if (gear != -1) + { + DataManager::SetGear(gear, false, aircraft); + } + if (flaps < -999.5 || flaps > -997.5) + { + DataManager::Set(DREF::FlapSetting, flaps, aircraft); + } + } + + void MessageHandlers::HandleData(Message& msg) + { + // Parse data + const std::uint8_t* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + std::size_t numCols = (size - 5) / 36; + if (numCols > 0) + { +#if LOG_VERBOSITY > 3 + Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id); +#endif + } + if (numCols > 32) // Error. Will overflow values + { +#if LOG_VERBOSITY > 2 + Log::FormatLine("[DATA] ERROR numCols to large."); +#endif + return; + } + else + { +#if LOG_VERBOSITY > 2 + Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id); +#endif + return; + } + float values[32][9]; + for (int i = 0; i < numCols; ++i) + { + values[i][0] = buffer[5 + 36 * i]; + memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); + } + + // Update log + + float savedAlpha = -998; + float savedHPath = -998; + for (int i = 0; i < numCols; ++i) + { + std::uint8_t dataRef = (std::uint8_t)values[i][0]; + if (dataRef > 134) + { + Log::FormatLine("[DATA] ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef); + } + + switch (dataRef) + { + 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); + if (alpha != alpha || hpath != hpath) + { +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)"); +#endif + break; + } + const float deg2rad = (float) 0.0174532925; + int ind[3] = { 1, 3, 4 }; + for (int j = 0; j < 3; ++j) + { + float v = values[i][ind[j]]; + if (v != -998) + { + DataManager::Set(DREF::LocalVX, v*cos((theta - alpha)*deg2rad)*sin(hpath*deg2rad)); + DataManager::Set(DREF::LocalVY, v*sin((theta - alpha)*deg2rad)); + DataManager::Set(DREF::LocalVZ, -v*cos((theta - alpha)*deg2rad)*cos(hpath*deg2rad)); + } + } + break; + } + case 17: // Orientation + { + float orient[3] + { + values[i][1] == -998 ? DataManager::GetFloat(DREF::Pitch) : values[i][1], + values[i][2] == -998 ? DataManager::GetFloat(DREF::Roll) : values[i][2], + values[i][3] == -998 ? DataManager::GetFloat(DREF::HeadingTrue) : values[i][3] + }; + DataManager::SetOrientation(orient); + 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 + break; + } + if (values[i][1] != -998) + { + savedAlpha = values[i][1]; + } + if (values[i][3] != -998) + { + savedHPath = values[i][3]; + } + break; + } + case 20: // Position + { + float pos[3] + { + values[i][2] == -998 ? DataManager::GetFloat(DREF::Latitude) : values[i][2], + values[i][3] == -998 ? DataManager::GetFloat(DREF::Longitude) : values[i][3], + values[i][4] == -998 ? DataManager::GetFloat(DREF::AGL) : values[i][4] + }; + DataManager::SetPosition(pos); + break; + } + case 25: // Throttle + { + if (values[i][1] != values[i][1]) + { +#if LOG_VERBOSITY > 0 + Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)"); +#endif + break; + } + float thr[8]; + for (int j = 0; j < 8; ++j) + { + thr[j] = values[i][1]; + } + DataManager::Set(DREF::ThrottleSet, thr, 8); + break; + } + default: // Non-Special dataRefs + { + float line[8]; + memcpy(line, values[i] + 1, 8 * sizeof(float)); + for (int j = 0; j < 8; ++j) + { +#if LOG_VERBOSITY > 1 + Log::FormatLine("Setting Dataref %i.%i to %f", dataRef, j, line[j]); +#endif + + if (dataRef == 14 && j == 0) + { + DataManager::SetGear(line[0], true); + continue; + } + + XPC::DREF dref = XPC::XPData[dataRef][j]; + if (dref == XPC::DREF::None) + { + HandleXPlaneData(msg); + } + else + { + XPC::DataManager::Set(dref, line, 8); + } + } + } + } + } + } + + void MessageHandlers::HandleDref(Message& msg) + { + const std::uint8_t* buffer = msg.GetBuffer(); + std::uint8_t len = buffer[5]; + std::string dref = std::string((char*)buffer + 6, len); + + std::uint8_t valueCount = buffer[6 + len]; + float values[40]; + memcpy(values, buffer + len + 7, valueCount * sizeof(float)); + +#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); + } + + void MessageHandlers::HandleGetD(Message& msg) + { + const std::uint8_t* buffer = msg.GetBuffer(); + std::uint8_t 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)", + 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 + return; + } + } + else // New request + { +#if LOG_VERBOSITY > 0 + Log::FormatLine("[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) + { + std::uint8_t len = buffer[ptr]; + connection.getdRequest[i] = std::string((char*)buffer + 1 + ptr, len); + ptr += 1 + len; + } + connection.getdCount = drefCount; + connections[connectionKey] = connection; + } + + std::uint8_t response[4096] = "RESP"; + response[5] = drefCount; + std::size_t cur = 6; + for (int i = 0; i < drefCount; ++i) + { + float values[255]; + int count = DataManager::Get(connection.getdRequest[i], values, 255); + response[cur++] = count; + memcpy(response + cur, values, count * sizeof(float)); + cur += count * sizeof(float); + } + + sock->SendTo(response, cur, connection.ip, connection.dstPort); + } + + void MessageHandlers::HandlePosi(Message& msg) + { + // Update log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[POSI] Message Received (Conn %i)", connection.id); +#endif + + const std::uint8_t* 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 + return; + } + + std::int8_t aircraft = 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) + { + // Enable AI for the aircraft 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; + DataManager::Set(DREF::PauseAI, ai, 0, 20); + } + } + + DataManager::SetPosition(pos, aircraft); + DataManager::SetOrientation(orient, aircraft); + if (gear != -1) + { + DataManager::SetGear(gear, true, aircraft); + } + } + + void MessageHandlers::HandleSimu(Message& msg) + { + // Update log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[SIMU] Message Received (Conn %i)", connection.id); +#endif + + const std::uint8_t* buffer = msg.GetBuffer(); + + // Set DREF + int value = buffer[5]; + DataManager::Set(DREF::Pause, &value, 1); + +#if LOG_VERBOSITY > 2 + if (buffer[5] == 0) + { + Log::FormatLine("[SIMU] Simulation Resumed (Conn %i)", connection.id); + } + else + { + Log::FormatLine("[SIMU] Simulation Paused (Conn %i)", connection.id); + } +#endif + } + + void MessageHandlers::HandleText(Message& msg) + { + // Update Log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[TEXT] Message Received (Conn %i)", connection.id); +#endif + + std::size_t len = msg.GetSize(); + const std::uint8_t* buffer = msg.GetBuffer(); + + char text[256] = { 0 }; + if (len < 14) + { +#if LOG_VERBOSITY > 1 + Log::WriteLine("[TEXT] ERROR: Length less than 14 bytes"); +#endif + return; + } + size_t msgLen = (unsigned char)buffer[13]; + if (msgLen == 0) + { + Drawing::ClearMessage(); +#if LOG_VERBOSITY > 2 + Log::WriteLine("[TEXT] Text cleared"); +#endif + } + else + { + int x = *((int*)(buffer + 5)); + 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 + } + } + + void MessageHandlers::HandleWypt(Message& msg) + { + // Update Log +#if LOG_VERBOSITY > 0 + Log::FormatLine("[WYPT] Message Received (Conn %i)", connection.id); +#endif + + // Parse data + const std::uint8_t* buffer = msg.GetBuffer(); + std::uint8_t op = buffer[5]; + std::uint8_t count = buffer[6]; + Waypoint points[255]; + const std::uint8_t* ptr = buffer + 7; + for (size_t i = 0; i < count; ++i) + { + points[i].latitude = *((float*)ptr); + points[i].longitude = *((float*)(ptr + 4)); + points[i].altitude = *((float*)(ptr + 8)); + ptr += 12; + } + + // Perform operation +#if LOG_VERBOSITY > 2 + Log::FormatLine("[WYPT] Performing operation %i", op); +#endif + switch (op) + { + case xpc_WYPT_ADD: + Drawing::AddWaypoints(points, count); + break; + case xpc_WYPT_DEL: + Drawing::RemoveWaypoints(points, count); + break; + case xpc_WYPT_CLR: + Drawing::ClearWaypoints(); + break; + default: +#if LOG_VERBOSITY > 1 + Log::FormatLine("[WYPT] ERROR: %i is not a valid operation.", op); +#endif + break; + } + } + + void MessageHandlers::HandleXPlaneData(Message& msg) + { +#if LOG_VERBOSITY > 1 + Log::WriteLine("[MSGH] Sending raw data to X-Plane"); +#endif + sock->SendTo((std::uint8_t*)msg.GetBuffer(), msg.GetSize(), "127.0.0.1", 49000); + } + + void MessageHandlers::HandleUnknown(Message& msg) + { + // UPDATE LOG +#if LOG_VERBOSITY > 0 + XPC::Log::FormatLine("[EXEC] ERROR: Unknown packet type %s", msg.GetHead().c_str()); +#endif + } +} \ No newline at end of file diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h new file mode 100644 index 0000000..7db08e2 --- /dev/null +++ b/xpcPlugin/MessageHandlers.h @@ -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. +#ifndef XPC_MESSAGEHANDLERS_H +#define XPC_MESSAGEHANDLERS_H +#include "Message.h" +#include "UDPSocket.h" + +#include +#include + +namespace XPC +{ + /// A function that handles a message. + typedef void(*MessageHandler)(Message&); + + /// Handles incommming messages and manages connections. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-12 + /// \date Last Updated: 2015-04-12 + class MessageHandlers + { + public: + /// The first stop for all messages to the plugin after they are read from the + /// socket. + /// + /// \details After a message is read, HandleMessage analyzes the sender's network address + /// to determine whether the sender is a new client. It then either loads + /// connection details for an existing client, or creates a new connection record + /// for new clients. Finally, the message handler checks the message type and + /// dispatches the message to the appropriate handler. + /// \param msg The message to be processed. + static void HandleMessage(Message& msg); + + /// Sets the socke 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); + + typedef struct + { + std::uint8_t id; + std::string ip; + std::uint16_t dstPort; + std::uint16_t srcPort; + std::uint8_t getdCount; + std::string getdRequest[255]; + } ConnectionInfo; + + static std::unordered_map connections; + static std::unordered_map handlers; + static std::string connectionKey; // The current connection ip:port string + static ConnectionInfo connection; // The current connection record + static UDPSocket* sock; // Outgoing network socket + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp new file mode 100644 index 0000000..9795205 --- /dev/null +++ b/xpcPlugin/UDPSocket.cpp @@ -0,0 +1,171 @@ +//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" + +namespace XPC +{ + UDPSocket::UDPSocket(std::uint16_t recvPort) + { +#if LOG_VERBOSITY > 1 + Log::FormatLine("[SOCK] Opening socket (port:%d)", recvPort); +#endif + // 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 +#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 + 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 + return; + } +#elif (__APPLE__ || __linux) + if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + Log::WriteLine("[SOCK] ERROR: Failed to open socket"); + return; + } + int optval = 1; + setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + setsockopt(theSocket.sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); +#endif + if (bind(this->sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) != 0) + { +#if LOG_VERBOSITY > 0 + int err = WSAGetLastError(); + Log::FormatLine("[SOCK] ERROR: Failed to bind socket. (Error code %i)", err); +#endif + return; + } + + //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 + } +#else + struct timeval tv; + tv.tv_sec = 0; /* Sec Timeout */ + tv.tv_usec = usTimeOut; // Microsec Timeout + 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 +#ifdef _WIN32 + closesocket(this->sock); +#elif (__APPLE__ || __linux) + close(this->sock); +#endif + } + + int UDPSocket::Read(std::uint8_t* dst, int maxLen, sockaddr* recvAddr) + { + socklen_t recvaddrlen = sizeof(*recvAddr); + int status = 0; + +#ifdef _WIN32 + // Windows readUDP needs the select command- minimum timeout is 1ms. + // Without this playback becomes choppy + + // Definitions + FD_SET stReadFDS; + FD_SET stExceptFDS; + timeval tv; + + // Setup for Select + FD_ZERO(&stReadFDS); + FD_SET(sock, &stReadFDS); + FD_ZERO(&stExceptFDS); + FD_SET(sock, &stExceptFDS); + tv.tv_sec = 0; /* Sec Timeout */ + tv.tv_usec = 250; // Microsec Timeout + + // 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); + } +#endif + if (result <= 0) // No Data or error + { + return -1; + } + + // If no error: Read Data + status = recvfrom(sock, (char*)dst, maxLen, 0, recvAddr, &recvaddrlen); +#else + // For apple or linux-just read - will timeout in 0.5 ms + status = (int)recvfrom(recfd.sock, dataRef, 5000, 0, recvaddr, &recvaddrlen); +#endif + return status; + } + + // TODO: Add IPV6 support + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::string remoteHost, std::uint16_t remotePort) + { +#if LOG_VERBOSITY > 4 + Log::FormatLine("[SOCK] Sending message to %s:%u (length=%u)", remoteHost.c_str(), remotePort, len); +#endif + if (remoteHost == "localhost") + { + remoteHost = "127.0.0.1"; + } + std::uint32_t ip; + inet_pton(AF_INET, remoteHost.c_str(), &ip); + SendTo(buffer, len, ip, remotePort); + } + + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::uint32_t remoteIP, std::uint16_t remotePort) + { + struct sockaddr_in remoteAddr; + remoteAddr.sin_family = AF_INET; + remoteAddr.sin_port = htons(remotePort); + remoteAddr.sin_addr.s_addr = remoteIP; + +#ifdef _WIN32 + const char on = 1; +#else + int on = 1; +#endif + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + + buffer[4] = (std::uint8_t)len; + if (sendto(sock, (char*)buffer, (int)len, 0, (const struct sockaddr *) &remoteAddr, sizeof(remoteAddr)) < 0) + { +#if LOG_VERBOSITY > 0 + Log::FormatLine("[SOCK] Send failed. (remote: %s:%d)", remoteAddr, remotePort); +#endif + } + } +} \ No newline at end of file diff --git a/xpcPlugin/UDPSocket.h b/xpcPlugin/UDPSocket.h new file mode 100644 index 0000000..69d7410 --- /dev/null +++ b/xpcPlugin/UDPSocket.h @@ -0,0 +1,79 @@ +//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 + +#include +#include +#include +#ifdef _WIN32 +#include +#include +#pragma comment(lib,"ws2_32.lib") //Winsock Library +#elif (__APPLE__ || __linux) +#include +#include +#include +#include +#endif + + +namespace XPC +{ + /// Represents a UDP datagram socket used for reading data from and sending + /// data to XPC clients. + /// + /// \author Jason Watkins + /// \version 1.0 + /// \since 1.0 + /// \date Intial Version: 2015-04-10 + /// \date Last Updated: 2015-04-11 + class UDPSocket + { + public: + /// Initializes a new instance of the XPCSocket class bound to the + /// specified receive port. + /// + /// \param recvPort The port on which this instance will receive data. + UDPSocket(std::uint16_t recvPort); + + /// Closes the underlying socket for this instance. + ~UDPSocket(); + + /// Reads the specified number of bytes into the data buffer and stores + /// the remote endpoint. + /// + /// \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. + /// \returns The number of bytes read, or a negative number if + /// an error occurs. + int Read(std::uint8_t* buffer, int size, sockaddr* remoteAddr); + + /// Sends data to the specified remote endpoint. + /// + /// \param data The data to be sent. + /// \param len The number of bytes to send. + /// \param remoteHost The hostname of the destination client. + /// \param remotePort The port of the destination client. + void SendTo(std::uint8_t* buffer, std::size_t len, std::string remoteHost, std::uint16_t remotePort); + + /// Sends data to the specified remote endpoint. + /// + /// \param data The data to be sent. + /// \param len The number of bytes to send. + /// \param remoteHost The hostname of the destination client. + /// \param remotePort The port of the destination client. + void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, std::uint32_t remoteIP, std::uint16_t remotePort); + + private: +#ifdef _WIN32 + SOCKET sock; +#else + int sock; +#endif + }; +} + +#endif \ No newline at end of file diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index bc8dd29..726754e 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -1,158 +1,101 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. // -// XPCPlugin Beta +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. +// +//X-Plane 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 // // DESCRIPTION // XPCPlugin Facilitates Communication to and from the XPlane // -// REQUIREMENTS -// 1. X-Plane Version 9.0 or newer (untested on previous versions) -// 2. XPCPlugin.xpl-must be placed in [X-Plane Directory]/Resources/plugins -// 3. OS X 10.8 or newer (untested on previous versions/Windows) -// // INSTRUCTIONS -// 1. Move xpcPlugin.xpl into [X-Plane Directory]/Resources/plugins -// -// COMMAND TYPES -// DATA: Works like UDP Commands -// SIMU: Pauses/Unpauses Simulation -// CONN: Set Returned Port Number -// DREF: Sets value to specific DREF (see http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html) -// GETD: Requests specific DREF -// POSI: Set Position -// CTRL: Set Control Variables -// WYPT: Add/Edit a Waypoint (PLANNED) -// VIEW: Set Simulation View (PLANNED) -// TEXT: Add Text to Screen (PLANNED) -// -// NOTICES: -// Copyright ã 2013-2014 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. -// -// DISCLAIMERS -// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." -// -// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. -// -// X-Plane API -// Copyright (c) 2008, Sandy Barbour and Ben Supnik All rights reserved. -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// * Neither the names of the authors nor that of X-Plane or Laminar Research may be used to endorse or promote products derived from this software without specific prior written permission from the authors or Laminar Research, respectively. -// -// X-Plane API SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// See Readme.md in the root of this repository or the wiki hosted on GitHub at +// https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions, +// and detailed documentation. // // CONTACT // For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) // // CONTRIBUTORS // CT: Christopher Teubert (christopher.a.teubert@nasa.gov) -// -// TO DO -// 1. Handle hitting maxcon -// 2. POSI: Alpha/Beta -// 3. Error Handling -// 4. CTRL: add SpeedBrakes -// 5. SIMU: -1 = switch -// 6. SENDBUF: Add message -// 7. Ability to add text to screen -// 8. Bound checking -// -// BEGIN CODE +// JW: Jason Watkins (jason.w.watkins@nasa.gov) -#define _WINSOCKAPI_ +// XPC Includes +#include "DataManager.h" +#include "Drawing.h" +#include "Log.h" +#include "Message.h" +#include "MessageHandlers.h" +#include "UDPSocket.h" -#include -#include -#include -#include - -//#include "XPLMPlanes.h" +// XPLM Includes #include "XPLMProcessing.h" -#include "XPLMGraphics.h" -#include "xpcDrawing.h" -#include "xpcPluginTools.h" -#ifdef _WIN32 /* WIN32 SYSTEM */ -#include -#elif (__APPLE__) +// System Includes +#ifdef __APPLE__ #include #endif -#define MAXCONN 50 // Maximum number of dedicated connections #define RECVPORT 49009 // Port that the plugin receives commands on #define SENDPORT 49097 // Port that the plugin sends on #define OPS_PER_CYCLE 20 // Max Number of operations per cycle -static XPLMDataRef XPLMSwitch; // for turning on/off simulation -struct xpcSocket recSocket; -struct xpcSocket sendSocket; +XPC::UDPSocket* recvSocket = nullptr; +XPC::UDPSocket* sendSocket = nullptr; -short number_of_connections = 0; -short current_connection = -1; +double start,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 +int counter = 0; -double start,lap; -static double timeConvert = 0.0; -int benchmarkingSwitch = 0; // 1 = time for operations, 2 = time for op + cycle; -int debugSwitch = 2; -int cyclesToClear = -1; // Clear message bus every n cycles. -1 == dont clear -int counter = 0; - -struct connectionHistory -{ - short connectionID; - - char IP[16]; - unsigned short recPort; - unsigned short fromPort; - short requestLength; - XPLMDataRef XPLMRequestedDRefs[100]; -}; - -connectionHistory connectionList[MAXCONN]; - -PLUGIN_API int XPluginStart(char * outName, char * outSig, char * outDesc); -static float MyFlightLoopCallback(float, float, int inCounter, void * inRefcon); +PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc); PLUGIN_API void XPluginStop(void); PLUGIN_API void XPluginDisable(void); PLUGIN_API int XPluginEnable(void); -PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFromWho, int inMessage, void * inParam); +PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFromWho, int inMessage, void* inParam); +static float XPCFlightLoopCallback(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, int inCounter, void* inRefcon); -int handleSIMU(char buf[]); -int handleCONN(char buf[]); -int handlePOSI(char buf[]); -int handleCTRL(char buf[]); -int handleWYPT(char buf[], int len); -int handleGETD(char *buf); -int handleDREF(char *buf); -int handleVIEW(); -int handleDATA(char *buf, int buflen); -int handleTEXT(char *buf, int len); -short handleInput(struct XPCMessage * theMessage); - -char setPOSI(short aircraft, float pos[3]); -char setORIENT(short aircraft, float orient[3]); -char setDREF(XPLMDataRef theDREF, float floatarray[], short arrayStart, short arraySize); -char setGEAR(short aircraft, float gear, char posi); -char setFLAP(float flap); -void sendBUF(char buf[], int buflen); - -PLUGIN_API int XPluginStart( char * outName, - char * outSig, - char * outDesc) +PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) { - static FILE *logFile; // Log File - strcpy(outName, "xplaneConnect"); - strcpy(outSig, "NASA.xplaneConnect"); - strcpy(outDesc, "X Plane Communications Toolbox"); - - logFile = fopen("xpcLog.txt","w"); - if (logFile != NULL) // If opened correctly - { - fprintf(logFile,"\n"); - fclose(logFile); - } + strcpy(outName, "X-Plane Connect [Version 0.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."); - updateLog("[EXEC] xpcPlugin Start", 22); - #if (__APPLE__) if ( abs(timeConvert) <= 1e-9 ) // is about 0 { @@ -163,91 +106,81 @@ PLUGIN_API int XPluginStart( char * outName, 1000000000.0; } #endif + XPC::Log::Initialize("0.2.0"); + XPC::Log::WriteLine("[EXEC] Plugin Start"); + XPC::DataManager::Initialize(); - // Build the DataRef Array - buildXPLMDataRefs(); - - //On/Off Switch for simulation - XPLMSwitch = XPLMFindDataRef("sim/operation/override/override_planepath"); - - XPLMRegisterFlightLoopCallback( - MyFlightLoopCallback, /* Callback */ - -1, /* Interval (s)*/ - NULL); /* refcon not used. */ + 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) { - char logmsg[100] = "[EXEC] xpcPlugin Shutdown"; - XPLMUnregisterFlightLoopCallback(MyFlightLoopCallback, NULL); - updateLog(logmsg,strlen(logmsg)); + XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL); + XPC::Log::WriteLine("[EXEC] Plugin Shutdown"); } PLUGIN_API void XPluginDisable(void) { - char logmsg[100] = "[EXEC] xpcPlugin Disabled, sockets closed"; - closeUDP(recSocket); - closeUDP(sendSocket); - updateLog(logmsg,strlen(logmsg)); + // Close sockets + delete recvSocket; + delete sendSocket; + recvSocket = nullptr; + sendSocket = nullptr; - XPCClearMessage(); + // Stop rendering messages to screen. + XPC::Drawing::ClearMessage(); + + // Stop rendering waypoints to screen. + XPC::Drawing::ClearWaypoints(); + + XPC::Log::WriteLine("[EXEC] Plugin Disabled, sockets closed"); } PLUGIN_API int XPluginEnable(void) { - char logmsg[100] = "[EXEC] xpcPlugin Enabled, sockets opened"; - char IP[16] = "127.0.0.1"; - recSocket = openUDP(RECVPORT, IP, 49009); - sendSocket = openUDP(SENDPORT, IP, 49099); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - - if (benchmarkingSwitch>0) + // Open sockets + recvSocket = new XPC::UDPSocket(RECVPORT); + sendSocket = new XPC::UDPSocket(SENDPORT); + XPC::MessageHandlers::SetSocket(sendSocket); + + XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened"); + if (benchmarkingSwitch > 0) { - sprintf(logmsg,"[EXEC] Benchmarking Enabled (Verbosity: %i)",benchmarkingSwitch); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - } - - if (debugSwitch>0) - { - sprintf(logmsg,"[EXEC] Debug Enabled (Verbosity: %i)",debugSwitch); - updateLog(logmsg,strlen(logmsg)); + XPC::Log::FormatLine("[EXEC] Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch); } +#if LOG_VERBOSITY > 0 + XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY); +#endif return 1; } -PLUGIN_API void XPluginReceiveMessage( XPLMPluginID inFromWho, - int inMessage, - void * inParam) +PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFromWho, int inMessage, void* inParam) { + // XPC doesn't have anything useful to say to other plugins, so simply ignore + // any messages received. } -float MyFlightLoopCallback( float inElapsedSinceLastCall, - float inElapsedTimeSinceLastFlightLoop, - int inCounter, - void * inRefcon) +float XPCFlightLoopCallback(float inElapsedSinceLastCall, + float inElapsedTimeSinceLastFlightLoop, + int inCounter, + void* inRefcon) { - int i; - short result; - char logmsg[100] = {0}; #if (__APPLE__) double diff_t; #endif - XPCMessage theMessage; - + counter++; if (benchmarkingSwitch > 1) { - sprintf(logmsg,"Cycle time %.6f",inElapsedSinceLastCall); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); + XPC::Log::FormatLine("Cycle time %.6f", inElapsedSinceLastCall); } - - for (i=0;i 0) { @@ -255,921 +188,29 @@ float MyFlightLoopCallback( float inElapsedSinceLastCall, start = (double)mach_absolute_time( ) * timeConvert; #endif } - readMessage(&recSocket, &theMessage); - result = handleInput(&theMessage); + + XPC::Message msg = XPC::Message::ReadFrom(*recvSocket); + if (msg.GetHead() == "") + { + break; + } + XPC::MessageHandlers::HandleMessage(msg); if (benchmarkingSwitch > 0) { #if (__APPLE__) lap = (double)mach_absolute_time( ) * timeConvert; diff_t = lap-start; - sprintf(logmsg,"Runtime %.6f",diff_t); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); + XPC::Log::FormatLine("[BENCH] Runtime %.6f",diff_t); #endif } - - if (result<=0) break; // No Signal } - - if (cyclesToClear != -1) + + if (cyclesToClear != -1 && counter%cyclesToClear == 0) { - if (counter%cyclesToClear==0) - { - strcpy(logmsg,"[EXEC] Cleared UDP Buffer"); - updateLog(logmsg,strlen(logmsg)); - char IP[16] = "127.0.0.1"; - closeUDP(recSocket); - recSocket = openUDP(RECVPORT, IP, 49009); - - } + XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); + delete recvSocket; + recvSocket = new XPC::UDPSocket(RECVPORT); } return -1; -} - -short handleInput(struct XPCMessage * theMessage) -{ - int i; - char logmsg[100] = {0}; - char IP[16] = {0}; - unsigned short port = 0; - - current_connection = -1; - - if (theMessage->msglen > 0) // If message received - { - if (debugSwitch>0) - { -#ifdef _WIN32 -#else - printBufferToLog(*theMessage); -#endif - } - - // Check for existing connection - port = getIP(theMessage->recvaddr,IP); - for (i=0; i 0) - { - current_connection=i; - break; - } - } - - if (current_connection == -1) //SETUP NEW CONNECTION - { - if (number_of_connections>=MAXCONN) // Handle hitting MAXCONN (COME UP WITH A BETTER SOLUTION) - { - updateLog("[EXEC] Hit maximum number of connections-Removing old ones",58); - number_of_connections = 0; - } - - memcpy(connectionList[number_of_connections].IP,IP,16); - connectionList[number_of_connections].recPort = 49008; - connectionList[number_of_connections].fromPort = port; - current_connection = number_of_connections; - number_of_connections ++; - - // Log Connection - sprintf(logmsg,"[EXEC] New Connection [%i]. IP=%s, port=%i",number_of_connections,IP, port); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - } - - if (strncmp(theMessage->head,"CONN",4)==0) // Header = CONN (Connection) - { - handleCONN(theMessage->msg); - } - else if (strncmp(theMessage->head,"SIMU",4)==0) // Header = SIMU - { - handleSIMU(theMessage->msg); - } - else if (strncmp(theMessage->head,"POSI",4)==0) // Header = POSI (Position) - { - handlePOSI(theMessage->msg); - } - else if (strncmp(theMessage->head,"CTRL",4)==0) // Header = CTRL (Control) - { - handleCTRL(theMessage->msg); - } - else if (strncmp(theMessage->head,"WYPT",4)==0) // Header = WYPT (Waypoint Draw) - { - handleWYPT(theMessage->msg, theMessage->msglen); - } - else if (strncmp(theMessage->head,"GETD",4)==0) // Header = GETD (Data Request) - { - handleGETD(theMessage->msg); - } - else if (strncmp(theMessage->head,"DREF",4)==0) // Header = DREF (By Data Ref) (this is slower than DATA) - { - handleDREF(theMessage->msg); - } - else if (strncmp(theMessage->head,"VIEW",4)==0) // Header = VIEW (Change View) - { - handleVIEW(); - } - else if (strncmp(theMessage->head,"DATA",4)==0) // Header = DATA (UDP Data) - { - handleDATA(theMessage->msg, theMessage->msglen); - } - else if (strncmp(theMessage->head, "TEXT", 4) == 0) // Header = TEXT (Screen message) - { - handleTEXT(theMessage->msg, theMessage->msglen); - } - else if ((strncmp(theMessage->head,"DSEL",4)==0) || (strncmp(theMessage->head,"USEL",4)==0)) // Header = DSEL/USEL (Select UDP Send) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"DCOC",4)==0) || (strncmp(theMessage->head,"UCOC",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"MOUS",4)==0) || (strncmp(theMessage->head,"CHAR",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if (strncmp(theMessage->head,"MENU",4)==0) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if (strncmp(theMessage->head,"SOUN",4)==0) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"FAIL",4)==0) || (strncmp(theMessage->head,"RECO",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if (strncmp(theMessage->head,"PAPT",4)==0) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"VEHN",4)==0) || (strncmp(theMessage->head,"VEH1",4)==0) || (strncmp(theMessage->head,"VEHA",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"OBJN",4)==0) || (strncmp(theMessage->head,"OBJL",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if ((strncmp(theMessage->head,"GSET",4)==0) || (strncmp(theMessage->head,"ISET",4)==0)) - { - sendBUF(theMessage->msg,theMessage->msglen); // Send to UDP - } - else if (strncmp(theMessage->head, "BOAT", 4) == 0) - { - sendBUF(theMessage->msg, theMessage->msglen); // Send to UDP - } - else - { //unrecognized header - sprintf(logmsg,"[EXEC] ERROR: Command %s not recognized",theMessage->head); - updateLog(logmsg, strlen(logmsg)); - } - current_connection = -1; - } // end if (buflen > 0) - - return theMessage->msglen; -} - -void sendBUF( char buf[], int buflen) -{ - char IP[16] = "127.0.0.1"; - memcpy(sendSocket.xpIP,IP,16); - sendSocket.xpPort = 49000; - sendUDP(sendSocket,buf,buflen); -} - -int handleCONN(char buf[]) -{ - char logmsg[100] = {0}; - char the_message[7]; - char header[5]; - - strncpy(header,"CONF",4); - memcpy(&the_message,&header,4); - the_message[4] = (char) current_connection; - - // COPY PORT - memcpy(&(connectionList[current_connection].recPort),&buf[5],sizeof(connectionList[current_connection].recPort)); - - if (connectionList[current_connection].recPort <= 1) // Is not valid port - { - connectionList[current_connection].recPort = 49008; - return updateLog("[CONN] ERROR: Port Number must be a number (NaN received)",51); - } - - - // UPDATE LOG - sprintf(logmsg,"[CONN] Update Connection %i- Sending to port: %i",current_connection+1,connectionList[current_connection].recPort); - updateLog(logmsg,strlen(logmsg)); - - // SEND CONFIRMATION - // TODO: Ivestigate why sending confirmation causes crashes on Windows 8 - //memcpy(sendSocket.xpIP,connectionList[current_connection].IP, sizeof(connectionList[current_connection].IP)); - //sendSocket.xpPort = connectionList[current_connection].recPort; - //sendUDP(sendSocket, the_message, 5); - - return 0; -} - -int handleSIMU(char buf[]) -{ - int SIMUArray[1] = {buf[5]}; - char logmsg[100] = {0}; - - if (SIMUArray[0] != SIMUArray[0]) // Is NaN - { - return updateLog("[SIMU] ERROR: Value must be a number (NaN received)",51); - } - - XPLMSetDatavi(XPLMSwitch, SIMUArray, 0, 1); - - if (buf[5] == 0) - { - sprintf(logmsg,"[SIMU] Simulation Resumed (Conn %i)", current_connection+1); - updateLog(logmsg,strlen(logmsg)); - } - else - { - sprintf(logmsg,"[SIMU] Simulation Paused (Conn %i)", current_connection+1); - updateLog(logmsg,strlen(logmsg)); - } - - return 0; -} - -int handleTEXT(char *buf, int len) -{ - char msg[256] = { 0 }; - if (len < 14) - { - updateLog("[TEXT] ERROR: Length less than 14 bytes", 39); - return -1; - } - size_t msgLen = (unsigned char)buf[13]; - if (msgLen == 0) - { - XPCClearMessage(); - updateLog("[TEXT] Text cleared", 19); - } - else - { - int x = *((int*)(buf + 5)); - int y = *((int*)(buf + 9)); - strncpy(msg, buf + 14, msgLen); - XPCSetMessage(x, y, msg); - updateLog("[TEXT] Text set", 15); - } - return 0; -} - -char setDREF(XPLMDataRef theDREF, float floatarray[], short arrayStart, short arraySize) -{ - XPLMDataTypeID dataType; - short i=arrayStart; - - if ((floatarray[i]<-997.5 && floatarray[i]>-999.5)) - { - return 0; // Do not change - } - - if (theDREF == XPLMDataRefs[0][0]) - { - return -1; - } - - if (theDREF) // VALID POINTER - { - if (floatarray[i] != floatarray[i]) // Is NaN - { - goto NANMessage; - } - - dataType = XPLMGetDataRefTypes(theDREF); - switch (dataType) - { - {case 1: //Integer - XPLMSetDatai(theDREF,(int) floatarray[i]); - break;} - - {case 4: //Double - XPLMSetDatad(theDREF,(double) floatarray[i]); - break;} - - {case 8: //Float Array - fmini(XPLMGetDatavf(theDREF,NULL,0,8),arraySize); //find size of array - if ( floatarray[0] != floatarray[0] ) // NaN Check - { - goto NANMessage; - } - - XPLMSetDatavf(theDREF,floatarray,arrayStart,arraySize); - break;} - - {case 16: //Integer Array - int intArray[20]; - short length; - length = fmini(XPLMGetDatavi(theDREF,NULL,0,8),arraySize); //find size of array - for (i=arrayStart; i < arrayStart+length; i++) - { - intArray[i] = (int) floatarray[i]; - } - XPLMSetDatavi(theDREF,intArray,0,length); - break;} - - {default: //Float - XPLMSetDataf(theDREF,floatarray[i]); - break;} - } - } - else - return updateLog("[DREF] ERROR: invalid DREF",26); - - return 0; - -NANMessage: - return updateLog("[DREF] ERROR: Value must be a number (NaN received)",51); -} - -char setGEAR(short aircraft, float gear, char posi) -{ - int i; - float gearArray[8]; - - if ((gear < - 8.5f && gear > 9.5f) || (gear < -997.9f && gear > -999.1f)) - { - return -1; // Don't change command - } - - if ( ( gear != gear ) || ( gear < -1.f ) || ( gear > 1.f ) ) // NaN & Positive test - { - return updateLog("[GEAR] ERROR: Value must be 0 or 1",51); - } - - for (i=0;i<8;i++) - { - gearArray[i] = gear; - } - - if (!aircraft) - { // Own Aircraft - setDREF(XPLMDataRefs[14][7], gearArray, 0, 8); - } - else - { // Multiplayer - setDREF(multiplayer[aircraft][6], gearArray, 0, 8); - } - - if (posi) - { - XPLMSetDatavf(XPLMDataRefs[14][0], gearArray, 0, 1); - } - - return 0; -} - -char setPOSI(short aircraft, float pos[3]) -{ - double local[3] = {0}; - int i; - float tPos = pos[0] + pos[1] + pos[2]; - - if (tPos != tPos) // Is NaN - { - return updateLog("[POSI] ERROR: Position must be a number (NaN received)",51); - } - - XPLMWorldToLocal(pos[0],pos[1],pos[2],&local[0],&local[1],&local[2]); - if (aircraft <= 0) - { // Main aircraft - for (i=0; i<3; i++) - { - XPLMSetDatad(XPLMDataRefs[21][i],local[i]); - XPLMSetDatad(XPLMDataRefs[20][i], pos[i]); - } - } - else - { // Multiplayer - for (i=0; i<3; i++) - { - XPLMSetDatad(multiplayer[aircraft][i], local[i]); - } - } - - return 0; -} - -char setORIENT(short aircraft, float orient[3]) -{ - int i; - float q[4] = {0}; - float pi = (float) 0.00872664625997; // 1/2 rad - float tOrient = orient[0] + orient[1] + orient[2]; - - if ( tOrient != tOrient ) // Is NaN - { - return updateLog("[ORIENT] ERROR: Orientation must be a number (NaN received)",53); - } - - if ( aircraft <= 0 ) // Main aircraft - { - XPLMSetDataf(XPLMDataRefs[17][0],orient[0]); - XPLMSetDataf(XPLMDataRefs[17][1],orient[1]); - XPLMSetDataf(XPLMDataRefs[17][2],orient[2]); - - //Convert to Quartonians (from: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane), http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf pA2) - orient[2] = pi * orient[2]; // 1/2 raidians - orient[0] = pi * orient[0]; // 1/2 raidians - orient[1] = pi * orient[1]; // 1/2 raidians - 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]); - - XPLMSetDatavf(XPLMDataRefs[17][4],q,0,4); - } - else - { - for (i=0; i<3; i++) - { - XPLMSetDataf(multiplayer[aircraft][i+3],orient[i]); - } - } - - return 0; -} - -char setFLAP(float flap) -{ - float floatArray[1] = {flap}; - setDREF(XPLMDataRefs[13][3],floatArray,0,1); - setDREF(XPLMDataRefs[13][4],floatArray,0,1); - - return 0; -} - -int handlePOSI(char buf[]) -{ - char logmsg[100]; - float position[8] = {0.0}; - float pos[3],orient[3]; - short aircraft = 0; - float gear = -1.0; - int autopilot[20] = {0}; - - // UPDATE LOG - sprintf(logmsg,"[POSI] Message Received (Conn %i)", current_connection+1); - updateLog(logmsg, strlen(logmsg)); - - aircraft = fmini(parsePOSI(buf,position,6, &gear),19); - - //ADD AIRCRAFT HANDLING- (-1 = single aircraft for player) - if (aircraft > 0) // Multiplayer aircraft - { - XPLMGetDatavi(AIswitch, autopilot, 0, 20); - autopilot[aircraft] = 1; - XPLMSetDatavi(AIswitch, autopilot, 0, 20); - } - - // Position - memcpy(pos,position,3*sizeof(float)); - setPOSI(aircraft, pos); - - // Orientation - memcpy(orient,&position[3],3*sizeof(float)); - setORIENT(aircraft, orient); - - //Landing Gear - if (gear != -1) - { - setGEAR(aircraft, gear, 1); - } - - return 0; -} - -int handleCTRL(char buf[]) -{ - char logmsg[100]; - xpcCtrl ctrl; - float thr[8] = { 0 }; - short i; - - // UPDATE LOG - sprintf(logmsg,"[CTRL] Message Received (Conn %i)", current_connection+1); - updateLog(logmsg, strlen(logmsg)); - - ctrl = parseCTRL(buf); - if (ctrl.aircraft < 0) //parseCTRL failed - { - return 1; - } - if (ctrl.aircraft > 19) //Can only handle 19 non-player aircraft right now - { - return 2; - } - if (ctrl.aircraft == 0) //player aircraft - { - // SET CONTROLS - XPLMSetDataf(XPLMDataRefs[11][0], ctrl.pitch); - XPLMSetDataf(XPLMDataRefs[11][1], ctrl.roll); - XPLMSetDataf(XPLMDataRefs[11][2], ctrl.yaw); - - // SET Throttle - for (i = 0; i<8; i++) - { - thr[i] = ctrl.throttle; - } - XPLMSetDatavf(XPLMDataRefs[25][0], thr, 0, 8); - XPLMSetDatavf(XPLMDataRefs[26][0], thr, 0, 8); - setDREF(XPLMFindDataRef("sim/flightmodel/engine/ENGN_thro_override"), thr, 0, 1); - - // SET Gear/Flaps - if (ctrl.gear != -1) - { - setGEAR(0, ctrl.gear, 0); // Gear - } - if (ctrl.flaps < -999.5 || ctrl.flaps > -997.5) // Flaps - { - XPLMSetDataf(XPLMDataRefs[13][3], ctrl.flaps); - } - } - else //non-player aircraft - { - // SET CONTROLS - XPLMSetDataf(multiplayer[ctrl.aircraft][14], ctrl.pitch); - XPLMSetDataf(multiplayer[ctrl.aircraft][15], ctrl.roll); - XPLMSetDataf(multiplayer[ctrl.aircraft][16], ctrl.yaw); - - // SET Throttle - for (i = 0; i<8; i++) - { - thr[i] = ctrl.throttle; - } - XPLMSetDatavf(multiplayer[ctrl.aircraft][13], thr, 0, 8); - - // SET Gear/Flaps - if (ctrl.gear != -1) - { - float gear[10]; - for (int i = 0; i < 10; ++i) - { - gear[i] = ctrl.gear; - } - XPLMSetDatavf(multiplayer[ctrl.aircraft][6], gear, 0, 10); - } - if (ctrl.flaps < -999.5 || ctrl.flaps > -997.5) // Flaps - { - XPLMSetDataf(multiplayer[ctrl.aircraft][7], ctrl.flaps); - XPLMSetDataf(multiplayer[ctrl.aircraft][8], ctrl.flaps); - } - } - return 0; -} - -int handleWYPT(char buf[], int len) -{ - // UPDATE LOG - char logmsg[100]; - sprintf(logmsg,"[WYPT] Message Received (Conn %i)", current_connection+1); - updateLog(logmsg, strlen(logmsg)); - - xpcWypt wypt = parseWYPT(buf); - if (wypt.op < 0) - { - sprintf(logmsg, "[WYPT] Failed to parse command. ERR:%i", wypt.op); - updateLog(logmsg, strlen(logmsg)); - return -1; - } - else - { - sprintf(logmsg, "[WYPT] Performing operation %i", wypt.op); - updateLog(logmsg, strlen(logmsg)); - } - - switch (wypt.op) - { - case xpc_WYPT_ADD: - XPCAddWaypoints(wypt.points, wypt.numPoints); - break; - case xpc_WYPT_DEL: - XPCRemoveWaypoints(wypt.points, wypt.numPoints); - break; - case xpc_WYPT_CLR: - XPCClearWaypoints(); - break; - default: //If parseWYPT is doing its job, we shouldn't ever hit this. - return -2; - } - return 0; -} - -int handleGETD(char buf[]) -{ - int length,i,k; - XPLMDataTypeID dataType; - char logmsg[400] = {0}; - int listLength = buf[5]; - char the_message[5000]; - char header[5] = {0}; - int count = 6; - float values[100] = {-998}; - int intArray[100] = {-998}; - int DREFSizes[100] = {0}; - char *DREFArray[100]; - - strncpy(header,"RESP",4); - memcpy(&the_message,&header,4); - - if (listLength == 0) // USE LAST REQUEST - { - sprintf(logmsg,"[GETD] DATA Requested- repeat last request from connection %i (%i data refs)",current_connection+1,connectionList[current_connection].requestLength); - - if (connectionList[current_connection].requestLength < 0) // No past requests - { - updateLog(logmsg, strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - sprintf(logmsg,"[GETD] ERROR- No previous requests from connection %i.",current_connection+1); - return updateLog(logmsg, strlen(logmsg)); - } - } - else if (listLength > 0) // NEW REQUEST - { - connectionList[current_connection].requestLength = (short) listLength; - - for (int i = 0; i < connectionList[current_connection].requestLength; i++) - { - DREFArray[i] = (char *) malloc(100); - memset(DREFArray[i],0,100); - } - - parseGETD(buf,DREFArray,DREFSizes); - sprintf(logmsg,"[GETD] DATA Requested- New Request for connection %i [%i]:",current_connection+1,listLength); - } - else - { - return -1; - } - - // Update Log - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - - for (i=0;i 0) - { - connectionList[current_connection].XPLMRequestedDRefs[i] = XPLMFindDataRef(DREFArray[i]); - } - - if (connectionList[current_connection].XPLMRequestedDRefs[i]) //Valid Pointer - { - dataType = XPLMGetDataRefTypes(connectionList[current_connection].XPLMRequestedDRefs[i]); - switch (dataType) - { - {case 1: //Integer - length = 1; - values[0] = (float) XPLMGetDatai(connectionList[current_connection].XPLMRequestedDRefs[i]); - break;} - - {case 4: //Double - length = 1; - values[0] = (float) XPLMGetDatad(connectionList[current_connection].XPLMRequestedDRefs[i]); - break;} - - {case 8: //Float Array - length = XPLMGetDatavf(connectionList[current_connection].XPLMRequestedDRefs[i],NULL,0,8); //find size of array - XPLMGetDatavf(connectionList[current_connection].XPLMRequestedDRefs[i],values,0,fminl(length,100)); - break;} - - {case 16: //Integer Array - length = XPLMGetDatavi(connectionList[current_connection].XPLMRequestedDRefs[i],NULL,0,8); //find size of array - XPLMGetDatavi(connectionList[current_connection].XPLMRequestedDRefs[i],intArray,0,fminl(length,100)); - for (k=0; k < length; k++) { - values[k]=(float) intArray[k]; - } - break;} - - {default: //Float - length = 1; - values[0] = XPLMGetDataf(connectionList[current_connection].XPLMRequestedDRefs[i]); - break;} - } - the_message[count] = length; - memcpy(&the_message[count+1],&values,length*sizeof(float)); - count += 1 + length*sizeof(float); - } - else - { - sprintf(logmsg,"%s-ERROR: invalid DREF",DREFArray[i]); - updateLog(logmsg, strlen(logmsg)); - } - } - the_message[5] = (char) connectionList[current_connection].requestLength; - - memcpy(sendSocket.xpIP,connectionList[current_connection].IP, sizeof(connectionList[current_connection].IP)); - sendSocket.xpPort = connectionList[current_connection].recPort; - - if (count > 6) - { - sendUDP(sendSocket, the_message, count); - } - - return 0; -} - -int handleDREF(char buf[]) -{ - char logmsg[400]={0}; - - char DREF[100] = {0}; - unsigned short lenDREF = 0; - unsigned short lenVALUE = 0; - float values[40] = {0.0}; - XPLMDataRef theDREF; - - parseDREF(buf, DREF, &lenDREF,values,&lenVALUE); - - // Input Varification - if (lenDREF <= 0) - { - return 1; - } - - // Handle DREF - sprintf(logmsg,"[DREF] Request to set DREF value received (Conn %i): %s",current_connection+1,DREF); - updateLog(logmsg,strlen(logmsg)); - - theDREF = XPLMFindDataRef(DREF); - setDREF(theDREF, values, 0, lenVALUE); - - return 0; -} - -int handleVIEW() -{ - char logmsg[100]; - - // UPDATE LOG - sprintf(logmsg,"[VIEW] Message Received (Conn %i)- VIEW FEATURE UNDER CONSTRUCTION",current_connection+1); - updateLog(logmsg, strlen(logmsg)); - - return 0; -} - -int handleDATA(char buf[], int buflen) -{ - int i,j,lines; - char logmsg[100] = {0}; - float floatArray[10] = {0}; - float recValues[20][9] = {0}; - const float deg2rad = (float) 0.0174532925; - float savedAlpha=-998; - float savedHPath=-998; - - lines = parseDATA(buf, buflen, recValues); //Parse Data - if (lines > 0) - { - // UPDATE LOG - sprintf(logmsg,"[DATA] Message Received (Conn %i)",current_connection+1); - updateLog(logmsg, strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - } - else - { - // UPDATE LOG - sprintf(logmsg,"[DATA] WARNING: Empty data packet received (Conn %i)",current_connection+1); - return updateLog(logmsg, strlen(logmsg)); - } - - for (i = 0; i134 ) - { // DREF Check 1: This ensures that the received dataRef is in the range of 0-134 - sprintf(logmsg,"[DATA] ERROR: DataRef # must be between 0-134 (Rec: %hi)",dataRef); - updateLog(logmsg,strlen(logmsg)); - memset(logmsg,0,strlen(logmsg)); - continue; - } - - switch (dataRef) - { - {case 3: // Velocity - // Velocity has to be broken into individual components in X-Plane World Coordinate System where x=East, y=Up. - //Note: Add Sideslip - - float theta, alpha, hpath,v; - int ind[3] = {1,3,4}; - theta = XPLMGetDataf(XPLMDataRefs[17][0]); //Theta - - if (savedAlpha != -998) - alpha = savedAlpha; - else - alpha = XPLMGetDataf(XPLMDataRefs[18][0]); //Alpha - - if (savedHPath != -998) - hpath = savedHPath; - else - hpath = XPLMGetDataf(XPLMDataRefs[18][2]); //Velocity Heading - - if ( ( hpath != hpath ) && ( alpha != alpha ) && ( theta != theta ) ) // NaN Check - { - updateLog("[DATA] ERROR: Value must be a number (NaN received)",51); - break; - } - - for (j=0;j 0) - { - char theString[100] = {0}; - sprintf(theString,"Setting Dataref %i.%i to %f",dataRef,j,floatArray[j]); - updateLog(theString, strlen(theString)); - } - - if (dataRef==14 && j==0) - { - setGEAR(0, recValues[i][j+1], 1); // Landing Gear - continue; - } - - // Set DATAREF - if (setDREF(XPLMDataRefs[dataRef][j],floatArray,j,8) == -1) - sendBUF(buf,buflen); - } //End for j=1:8 - break;} - } // End switch(dataRef) - }// End for(all lines) - - return 0; -} // End handleDATA +} \ No newline at end of file diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index 9f42109..3438995 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index 0e18c52..b94dc15 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ diff --git a/xpcPlugin/xpcDrawing.cpp b/xpcPlugin/xpcDrawing.cpp deleted file mode 100644 index d770d27..0000000 --- a/xpcPlugin/xpcDrawing.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include "xpcDrawing.h" -#include "XPLMDisplay.h" -#include "XPLMGraphics.h" -#include "XPLMDataAccess.h" -#include -#include -#include -//OpenGL includes -#if IBM -#include -#endif -#if __GNUC__ -#include -#else -#include -#endif - -//Internal Structures -typedef struct -{ - double x; - double y; - double z; -} LocalPoint; - -//Internal Memory -static const size_t MSG_MAX = 1024; -static const size_t MSG_LINE_MAX = MSG_MAX / 16; -static bool msgEnabled = false; -static int msgX = -1; -static int msgY = -1; -static char msgVal[MSG_MAX] = { 0 }; -static size_t newLineCount = 0; -static size_t newLines[MSG_LINE_MAX] = { 0 }; -static float rgb[3] = { 0.25F, 1.0F, 0.25F }; - -static const size_t WAYPOINT_MAX = 128; -static bool routeEnabled = false; -static size_t numWaypoints = 0; -static Waypoint waypoints[WAYPOINT_MAX]; -static LocalPoint localPoints[WAYPOINT_MAX]; - -XPLMDataRef planeXref; -XPLMDataRef planeYref; -XPLMDataRef planeZref; - -//Internal Functions -int cmp(const void * a, const void * b) -{ - return (*(size_t*)a - *(size_t*)b); -} - -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 - const float TAN = 0.00436335082070156648652885284203; - float h = d * TAN; - - glBegin(GL_QUAD_STRIP); - //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 - glVertex3f(x - h, y - h, z + h); - glVertex3f(x + h, y - h, z + h); - //Bottom - glVertex3f(x - h, y - h, z - h); - glVertex3f(x + h, y - h, z - h); - //Back - glVertex3f(x - h, y + h, z - h); - glVertex3f(x + h, y + h, z - h); - - glEnd(); - glBegin(GL_QUADS); - //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 - 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); - - glEnd(); -} - -static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) -{ - XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic); - int y = msgY - 16; - for (size_t i = 0; i < newLineCount; ++i) - { - XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic); - y -= 16; - } - return 1; -} - -static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) -{ - float px = XPLMGetDataf(planeXref); - float py = XPLMGetDataf(planeYref); - float pz = XPLMGetDataf(planeZref); - - Waypoint* g; - LocalPoint* l; - //Convert to local - for (size_t i = 0; i < numWaypoints; ++i) - { - g = &waypoints[i]; - l = &localPoints[i]; - XPLMWorldToLocal(g->latitude, g->longitude, g->altitude, - &l->x, &l->y, &l->z); - } - - - //Draw posts - glColor3f(1.0F, 1.0F, 1.0F); - glBegin(GL_LINES); - for (size_t i = 0; i < numWaypoints; ++i) - { - l = &localPoints[i]; - glVertex3f((float)l->x, (float)l->y, (float)l->z); - glVertex3f((float)l->x, -1000.0F, (float)l->z); - } - glEnd(); - - //Draw route - glColor3f(1.0F, 0.0F, 0.0F); - glBegin(GL_LINE_STRIP); - for (size_t i = 0; i < numWaypoints; ++i) - { - l = &localPoints[i]; - glVertex3f((float)l->x, (float)l->y, (float)l->z); - } - glEnd(); - - //Draw markers - glColor3f(1.0F, 1.0F, 1.0F); - for (size_t i = 0; i < numWaypoints; ++i) - { - l = &localPoints[i]; - float xoff = (float)l->x - px; - float yoff = (float)l->y - py; - float zoff = (float)l->z - pz; - float d = sqrtf(xoff*xoff + yoff*yoff + zoff*zoff); - gl_drawCube((float)l->x, (float)l->y, (float)l->z, d); - } - return 1; -} - -//Public Functions -void XPCClearMessage() -{ - XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); - msgEnabled = false; -} - -void XPCSetMessage(int x, int y, char* msg) -{ - //Determine size of message and clear instead if the message string - //is empty. - size_t len = strnlen(msg, MSG_MAX); - if (len == 0) - { - XPCClearMessage(); - return; - } - - //Set the message, location, and mark new lines. - strncpy(msgVal, msg, len); - newLineCount = 0; - for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i) - { - if (msgVal[i] == '\n' || msgVal[i] == '\r') - { - msgVal[i] = 0; - newLines[newLineCount++] = i + 1; - } - } - msgX = x < 0 ? 10 : x; - msgY = y < 0 ? 600 : y; - - //Enable drawing if necessary - if (!msgEnabled) - { - XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_LastCockpit, 0, NULL); - msgEnabled = true; - } -} - -void XPCClearWaypoints() -{ - numWaypoints = 0; - if (routeEnabled) - { - XPLMUnregisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL); - } - return; -} - -void XPCAddWaypoints(Waypoint points[], size_t numPoints) -{ - if (numWaypoints + numPoints > WAYPOINT_MAX) - { - numPoints = WAYPOINT_MAX - numWaypoints; - } - size_t finalNumWaypoints = numPoints + numWaypoints; - for (size_t i = 0; i < numPoints; ++i) - { - waypoints[numWaypoints + i] = points[i]; - } - numWaypoints = finalNumWaypoints; - - if (!routeEnabled) - { - XPLMRegisterDrawCallback(RouteDrawCallback, xplm_Phase_Objects, 0, NULL); - } - if (!planeXref) - { - planeXref = XPLMFindDataRef("sim/flightmodel/position/local_x"); - planeYref = XPLMFindDataRef("sim/flightmodel/position/local_y"); - planeZref = XPLMFindDataRef("sim/flightmodel/position/local_z"); - } -} - -void XPCRemoveWaypoints(Waypoint points[], size_t numPoints) -{ - //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) - { - Waypoint p = points[i]; - for (size_t j = 0; j < numWaypoints; ++j) - { - Waypoint q = waypoints[j]; - if (p.latitude == q.latitude && - p.longitude == q.longitude && - p.altitude == q.altitude) - { - delPoints[delPointsCur++] = j; - break; - } - } - } - //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 - size_t copyCur = 0; - size_t count = delPointsCur; - delPointsCur = 0; - for (size_t i = 0; i < numWaypoints; ++i) - { - if (i == delPoints[delPointsCur]) - { - ++delPointsCur; - continue; - } - waypoints[copyCur++] = waypoints[i]; - } - numWaypoints -= count; -} \ No newline at end of file diff --git a/xpcPlugin/xpcDrawing.h b/xpcPlugin/xpcDrawing.h deleted file mode 100644 index 4d79275..0000000 --- a/xpcPlugin/xpcDrawing.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef xpcDrawing_h -#define xpcDrawing_h - -#include -#include "xplaneConnect.h" - -void XPCClearMessage(); - -void XPCSetMessage(int x, int y, char* msg); - -void XPCClearWaypoints(); - -void XPCAddWaypoints(Waypoint points[], size_t numPoints); - -void XPCRemoveWaypoints(Waypoint points[], size_t numPoints); - -#endif diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj index 2b495b4..3ed8c71 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj @@ -59,14 +59,15 @@ NotUsing Level3 Disabled - IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;%(PreprocessorDefinitions) + IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) OldStyle true false - 4996 + + false - MultiThreaded + MultiThreadedDebug Windows @@ -79,7 +80,7 @@ NotUsing Level3 Disabled - IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;%(PreprocessorDefinitions) + IBM=1;XPLM200;_DEBUG;_WINDOWS;_USRDLL;XPCPLUGIN_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) OldStyle @@ -89,7 +90,7 @@ false 4996 false - MultiThreaded + MultiThreadedDebug Windows @@ -99,14 +100,24 @@ - - + + + + + + + - + + + + + + + - diff --git a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters index 0082526..21d7598 100644 --- a/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters +++ b/xpcPlugin/xpcPlugin/xpcPlugin.vcxproj.filters @@ -18,10 +18,25 @@ Header Files - + Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -32,10 +47,25 @@ Source Files - + Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files diff --git a/xpcPlugin/xpcPluginTools.cpp b/xpcPlugin/xpcPluginTools.cpp deleted file mode 100644 index 53228c7..0000000 --- a/xpcPlugin/xpcPluginTools.cpp +++ /dev/null @@ -1,492 +0,0 @@ -// xpcPluginTools functions to support xpcPlugin -// -// FUNCTIONS -// buildXPLMDataRefs -// fmini -// almostequal -// updateLog -// handleDREFSIM -// getIP -// printBuffertoLog -// test -// testi -// -// CONTACT -// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) -// -// CONTRIBUTORS -// CT: Christopher Teubert (christopher.a.teubert@nasa.gov) -// -// TO DO: -// 1. Support sending -1 length to updateLog for it to calculate intself (look for /0) -// 2. Have printbuffertolog run parse function -// 3. Builddatarefs: Fill out & test options -// -// BEGIN CODE - -#include -#include -#include -#include -#include -#include "xpcPluginTools.h" - -XPLMDataRef XPLMDataRefs[134][8]; -XPLMDataRef multiplayer[20][17]; -XPLMDataRef AIswitch; - -void readMessage(struct xpcSocket * recSocket, struct XPCMessage * pMessage) -{ - pMessage->msglen = readUDP( *recSocket, pMessage->msg, &(pMessage->recvaddr) ); - - if ( pMessage->msglen <= 0 ) // No Message - { - pMessage->msg[0] = 0; - } - else - { - strncpy( pMessage->head, pMessage->msg, 4 ); - } - - return; -} - -void buildXPLMDataRefs() -{ - int i, j; - char multi[50] = {0}; - - for (i=0; i<134; i++) - { - for (j=0; j<8; j++) - { - XPLMDataRefs[i][j] = XPLMFindDataRef("sim/test/test_float"); - } - } - - /* Prefetch the sim variables we will use. */ - //Row 0: Frame Rates - - //Row 1: Times - XPLMDataRefs[1][1] = XPLMFindDataRef("sim/time/total_running_time_sec"); - XPLMDataRefs[1][2] = XPLMFindDataRef("sim/time/total_flight_time_sec"); - XPLMDataRefs[1][3] = XPLMFindDataRef("sim/time/timer_elapsed_time_sec"); - - //cockpit2/clock_time/zulu_time_hours - //cockpit2/clock_time/zulu_time_minutes - //cockpit2/clock_time/zulu_time_seconds - - //cockpit2/clock_time/local_time_hours - - //cockpit2/clock_time/hobbs_time_hours - - //Row 2: Sim Stats - - //Row 3: Velocities - //READ ONLY - XPLMDataRefs[3][0] = XPLMFindDataRef("sim/flightmodel/position/indicated_airspeed"); //Vind knots indicated airspeed TEST - - //Vind knots equivilent airspeed (considering compressible flow) - XPLMDataRefs[3][2] = XPLMFindDataRef("sim/flightmodel/position/true_airspeed"); //Vtrue knots true airspeed - XPLMDataRefs[3][3] = XPLMFindDataRef("sim/flightmodel/position/groundspeed"); //Vtrue Knots true ground speed - - //XPLMDataRefs[3][5] = Vind mph - //XPLMDataRefs[3][6] = Vtrue mphas - //XPLMDataRefs[3][7] = Vtre mphgs - - //Row 4: Mach, VVI, G-loads - XPLMDataRefs[4][0] = XPLMFindDataRef("sim/flightmodel/misc/machno"); // Mach Number - XPLMDataRefs[4][4] = XPLMFindDataRef("sim/flightmodel2/misc/gforce_normal"); - XPLMDataRefs[4][5] = XPLMFindDataRef("sim/flightmodel2/misc/gforce_axial"); - XPLMDataRefs[4][6] = XPLMFindDataRef("sim/flightmodel2/misc/gforce_side"); - - //Row 5: Atmosphere: Weather - XPLMDataRefs[5][0] = XPLMFindDataRef("sim/weather/barometer_sealevel_inhg"); - XPLMDataRefs[5][1] = XPLMFindDataRef("sim/weather/temperature_sealevel_c"); - XPLMDataRefs[5][3] = XPLMFindDataRef("sim/cockpit2/gauges/indicators/wind_speed_kts"); - - //Row 6: Atmosphere: Aircraft - - //Row 7: System Pressures - - //Row 8: Joystick - XPLMDataRefs[8][0] = XPLMFindDataRef("sim/joystick/yoke_pitch_ratio"); - XPLMDataRefs[8][1] = XPLMFindDataRef("sim/joystick/yoke_roll_ratio"); - XPLMDataRefs[8][2] = XPLMFindDataRef("sim/joystick/yoke_heading_ratio"); - - //Row 9: Other Flight Controls - - //Row 10: Art stab ail/elv/rud - - //Row 11: Control Surfaces - XPLMDataRefs[11][0] = XPLMFindDataRef("sim/cockpit2/controls/yoke_pitch_ratio"); //Elevator Position - XPLMDataRefs[11][1] = XPLMFindDataRef("sim/cockpit2/controls/yoke_roll_ratio"); //Aileron Position - XPLMDataRefs[11][2] = XPLMFindDataRef("sim/cockpit2/controls/yoke_heading_ratio"); //Rudder Position - XPLMDataRefs[11][3] = NULL; - XPLMDataRefs[11][4] = NULL; - XPLMDataRefs[11][5] = NULL; - XPLMDataRefs[11][6] = NULL; - XPLMDataRefs[11][7] = NULL; - - //Row 12: Wing Sweep/Trust Vec - - //Row 13: trip/flap/slat/s-brakes - XPLMDataRefs[13][3] = XPLMFindDataRef("sim/flightmodel/controls/flaprqst"); - XPLMDataRefs[13][4] = XPLMFindDataRef("sim/flightmodel/controls/flaprat");// should be equal to flap2rat - - //Row 14: Gear, Brakes - XPLMDataRefs[14][0] = XPLMFindDataRef("sim/aircraft/parts/acf_gear_deploy"); //Landing Gear-SPECIAL (Float[10]) - XPLMDataRefs[14][1] = XPLMFindDataRef("sim/flightmodel/controls/parkbrake"); //Parking Brake - XPLMDataRefs[14][2] = XPLMFindDataRef("sim/cockpit2/controls/left_brake_ratio"); - XPLMDataRefs[14][3] = XPLMFindDataRef("sim/cockpit2/controls/right_brake_ratio"); - - XPLMDataRefs[14][7] = XPLMFindDataRef("sim/cockpit/switches/gear_handle_status"); //Landing Gear Handle - - //Row 15: MNR (Angular Moments) - //READ ONLY - XPLMDataRefs[15][0] = XPLMFindDataRef("sim/flightmodel/position/M"); - XPLMDataRefs[15][1] = XPLMFindDataRef("sim/flightmodel/position/L"); - XPLMDataRefs[15][2] = XPLMFindDataRef("sim/flightmodel/position/N"); - - //Row 16: PQR (Angular Velocities) - XPLMDataRefs[16][0] = XPLMFindDataRef("sim/flightmodel/position/Qrad"); - XPLMDataRefs[16][1] = XPLMFindDataRef("sim/flightmodel/position/Prad"); - XPLMDataRefs[16][2] = XPLMFindDataRef("sim/flightmodel/position/Rrad"); - XPLMDataRefs[16][3] = XPLMFindDataRef("sim/flightmodel/position/Q"); - XPLMDataRefs[16][4] = XPLMFindDataRef("sim/flightmodel/position/P"); - XPLMDataRefs[16][5] = XPLMFindDataRef("sim/flightmodel/position/R"); - - //Row 17: Orientation: pitch, roll, yaw, heading - XPLMDataRefs[17][0] = XPLMFindDataRef("sim/flightmodel/position/theta"); //Pitch - XPLMDataRefs[17][1] = XPLMFindDataRef("sim/flightmodel/position/phi"); //roll - XPLMDataRefs[17][2] = XPLMFindDataRef("sim/flightmodel/position/psi"); //true heading (where nose is pointing) - - //READ ONLY (always) - XPLMDataRefs[17][3] = XPLMFindDataRef("sim/flightmodel/position/magpsi"); //magnetic heading - - //WRITABLE - XPLMDataRefs[17][4] = XPLMFindDataRef("sim/flightmodel/position/q"); //Quartonian - - //Row 18: Orientation: alpha beta hpath vpath slip - XPLMDataRefs[18][0] = XPLMFindDataRef("sim/flightmodel/position/alpha"); //Angle of Attack - XPLMDataRefs[18][1] = XPLMFindDataRef("sim/cockpit2/gauges/indicators/sideslip_degrees"); //fix to beta - XPLMDataRefs[18][2] = XPLMFindDataRef("sim/flightmodel/position/hpath"); //Heading the aircraft flies (velocity vector) TEST - XPLMDataRefs[18][3] = XPLMFindDataRef("sim/flightmodel/position/vpath"); //VPath TEST - XPLMDataRefs[18][7] = XPLMFindDataRef("sim/cockpit2/gauges/indicators/sideslip_degrees"); - - //Row 19: Mag Compass - XPLMDataRefs[19][0] = XPLMFindDataRef("sim/flightmodel/position/magpsi"); - - //READ ONLY - XPLMDataRefs[19][1] = XPLMFindDataRef("sim/flightmodel/position/magnetic_variation"); - - //Row 20: Global Position - //READ ONLY - XPLMDataRefs[20][0] = XPLMFindDataRef("sim/flightmodel/position/latitude"); // Read Only - XPLMDataRefs[20][1] = XPLMFindDataRef("sim/flightmodel/position/longitude"); // Read Only - XPLMDataRefs[20][3] = XPLMFindDataRef("sim/flightmodel/position/y_agl"); // Read Only - - //Row 21: Local Position, Velocity - XPLMDataRefs[21][0] = XPLMFindDataRef("sim/flightmodel/position/local_x"); - XPLMDataRefs[21][1] = XPLMFindDataRef("sim/flightmodel/position/local_y"); - XPLMDataRefs[21][2] = XPLMFindDataRef("sim/flightmodel/position/local_z"); - XPLMDataRefs[21][3] = XPLMFindDataRef("sim/flightmodel/position/local_vx"); - XPLMDataRefs[21][4] = XPLMFindDataRef("sim/flightmodel/position/local_vy"); - XPLMDataRefs[21][5] = XPLMFindDataRef("sim/flightmodel/position/local_vz"); - - //Row 22: All Planes: Lat (READ ONLY) - XPLMDataRefs[22][0] = XPLMDataRefs[20][0]; - XPLMDataRefs[22][1] = XPLMFindDataRef("sim/multiplayer/position/plane1_lat"); - XPLMDataRefs[22][2] = XPLMFindDataRef("sim/multiplayer/position/plane2_lat"); - XPLMDataRefs[22][3] = XPLMFindDataRef("sim/multiplayer/position/plane3_lat"); - XPLMDataRefs[22][4] = XPLMFindDataRef("sim/multiplayer/position/plane4_lat"); - XPLMDataRefs[22][5] = XPLMFindDataRef("sim/multiplayer/position/plane5_lat"); - XPLMDataRefs[22][6] = XPLMFindDataRef("sim/multiplayer/position/plane6_lat"); - XPLMDataRefs[22][7] = XPLMFindDataRef("sim/multiplayer/position/plane7_lat"); - - //Row 23: All Planes: Lon - XPLMDataRefs[23][0] = XPLMDataRefs[20][1]; - XPLMDataRefs[23][1] = XPLMFindDataRef("sim/multiplayer/position/plane1_lon"); - XPLMDataRefs[23][2] = XPLMFindDataRef("sim/multiplayer/position/plane2_lon"); - XPLMDataRefs[23][3] = XPLMFindDataRef("sim/multiplayer/position/plane3_lon"); - XPLMDataRefs[23][4] = XPLMFindDataRef("sim/multiplayer/position/plane4_lon"); - XPLMDataRefs[23][5] = XPLMFindDataRef("sim/multiplayer/position/plane5_lon"); - XPLMDataRefs[23][6] = XPLMFindDataRef("sim/multiplayer/position/plane6_lon"); - XPLMDataRefs[23][7] = XPLMFindDataRef("sim/multiplayer/position/plane7_lon"); - - //Row 24: All Planes: Alt - XPLMDataRefs[24][0] = XPLMDataRefs[20][2]; - XPLMDataRefs[24][1] = XPLMFindDataRef("sim/multiplayer/position/plane1_el"); - XPLMDataRefs[24][2] = XPLMFindDataRef("sim/multiplayer/position/plane2_el"); - XPLMDataRefs[24][3] = XPLMFindDataRef("sim/multiplayer/position/plane3_el"); - XPLMDataRefs[24][4] = XPLMFindDataRef("sim/multiplayer/position/plane4_el"); - XPLMDataRefs[24][5] = XPLMFindDataRef("sim/multiplayer/position/plane5_el"); - XPLMDataRefs[24][6] = XPLMFindDataRef("sim/multiplayer/position/plane6_el"); - XPLMDataRefs[24][7] = XPLMFindDataRef("sim/multiplayer/position/plane7_el"); - - //Row 25: Throttle Command - XPLMDataRefs[25][0] = XPLMFindDataRef("sim/flightmodel/engine/ENGN_thro"); //Throttle (array 8) - - //Row 26: Throttle Actual - XPLMDataRefs[26][0] = XPLMFindDataRef("sim/flightmodel2/engines/throttle_used_ratio"); // Trottle Actual (array 8) (Read Only) - - //Row 27: - - - // Multiplayer - for ( i = 1; i < 20; i++ ) - { - sprintf(multi, "sim/multiplayer/position/plane%i_x", i); // X - multiplayer[i][0] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_y", i); // Y - multiplayer[i][1] = XPLMFindDataRef(multi); - sprintf(multi,"sim/multiplayer/position/plane%i_z", i); // Z - multiplayer[i][2] = XPLMFindDataRef(multi); - sprintf(multi,"sim/multiplayer/position/plane%i_the", i); // Theta (Pitch) - multiplayer[i][3] = XPLMFindDataRef(multi); - sprintf(multi,"sim/multiplayer/position/plane%i_phi", i); // Phi (Roll) - multiplayer[i][4] = XPLMFindDataRef(multi); - sprintf(multi,"sim/multiplayer/position/plane%i_psi", i); // Psi (Heading-True) - multiplayer[i][5] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i); // Landing Gear - multiplayer[i][6] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio", i); - multiplayer[i][7] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio2", i); - multiplayer[i][8] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); - multiplayer[i][9] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_speedbrake_ratio", i); - multiplayer[i][10] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_slat_ratio", i); - multiplayer[i][11] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_wing_sweep", i); - multiplayer[i][12] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i); - multiplayer[i][13] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_yolk_pitch", i); - multiplayer[i][14] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i); - multiplayer[i][15] = XPLMFindDataRef(multi); - sprintf(multi, "sim/multiplayer/position/plane%i_yolk_yaw", i); - multiplayer[i][16] = XPLMFindDataRef(multi); - } - AIswitch = XPLMFindDataRef("sim/operation/override/override_plane_ai_autopilot"); -} - -int fmini(int a, int b) -{ - // Returns the minimum value of two integers - return ((((a)-(b))&0x80000000) >> 31)? (a) : (b); -} - -int almostequal(float arg1, float arg2, float tol) -{ - // Compares arg1 and arg 2. If they are within tol returns true - return (abs(arg1-arg2)>8, (int) ((*sendaddr).sin_addr.s_addr&0xFF0000)>>16, (int) ((*sendaddr).sin_addr.s_addr&0xFF000000)>>24); - - return ntohs((*sendaddr).sin_port); -} - -// DEBUGGING TOOLS -// -------------------------------- -int printBufferToLog(struct XPCMessage & msg) -{ - // Prints the entire buffer to the log (for debugging) - char logmsg[350] = "[DEBUG]"; - int i; - - for (i=0;i -#include "xplaneConnect.h" -#include "XPLMDataAccess.h" - - extern XPLMDataRef XPLMDataRefs[134][8]; - extern XPLMDataRef multiplayer[20][17]; - extern XPLMDataRef AIswitch; - - struct XPCMessage - { - short connectionID; - char head[5]; - char msg[5000]; - int msglen; - struct sockaddr recvaddr; - }; - - void readMessage(struct xpcSocket * recSocket, struct XPCMessage * pMessage); - - void buildXPLMDataRefs(void); - - int almostequal(float arg1, float arg2, float tol); - - int test(const char *buffer); - - int test(int buffer); - - int updateLog(const char *buffer, int length); - - unsigned short getIP(struct sockaddr recvaddr, char *IP); - - int printBufferToLog(struct XPCMessage & msg); - - int fmini(int a, int b); - - -#endif