Merge branch 'master' into develop

# Conflicts:
#	xpcPlugin/XPlaneConnect/mac.xpl
#	xpcPlugin/xpcPlugin.xcodeproj/project.pbxproj
This commit is contained in:
Chris Teubert
2016-02-29 12:48:30 -08:00
parent 8d5af7e47e
commit cfdd183214
100 changed files with 4224 additions and 1912 deletions

101
C/monitorExample/main.c Normal file
View File

@@ -0,0 +1,101 @@
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#include "../src/xplaneConnect.h"
#include <sys/time.h>
#include "stdio.h"
#ifdef WIN32
HANDLE hStdIn = NULL;
INPUT_RECORD buffer;
int waitForInput()
{
if (hStdIn == NULL)
{
hStdIn = GetStdHandle(STD_INPUT_HANDLE);
}
FlushConsoleInputBuffer(hStdIn);
DWORD result = WaitForSingleObject(hStdIn, 100);
if (result == WAIT_OBJECT_0)
{
DWORD eventsRead;
PeekConsoleInput(hStdIn, &buffer, 1, &eventsRead);
if (eventsRead > 0)
{
return buffer.EventType == KEY_EVENT;
}
}
return FALSE;
}
#else
int fdstdin = 0;
fd_set fds;
struct timeval tv;
int waitForInput()
{
FD_ZERO(&fds);
FD_SET(fdstdin, &fds);
select(1, &fds, NULL, NULL, &tv);
return FD_ISSET(fdstdin, &fds);
}
#endif
int main(void)
{
XPCSocket client = openUDP("127.0.0.1");
const int aircraftNum = 0;
tv.tv_usec = 100 * 1000;
while (1)
{
float posi[7];
int result = getPOSI(client, posi, aircraftNum);
if (result < 0) // Error in getPOSI
{
break;
}
float ctrl[7];
result = getCTRL(client, ctrl, aircraftNum);
if (result < 0) // Error in getCTRL
{
break;
}
printf("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n",
posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]);
// Check if any key has been pressed and break
if (waitForInput())
{
break;
}
}
printf("\n\nPress Any Key to exit...");
getchar();
return 0;
}

View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitorExample", "monitorExample.vcxproj", "{0F45204B-6910-46C7-BECD-273985390F75}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.ActiveCfg = Debug|Win32
{0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.Build.0 = Debug|Win32
{0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.ActiveCfg = Release|Win32
{0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{0F45204B-6910-46C7-BECD-273985390F75}</ProjectGuid>
<RootNamespace>monitorExample</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>../src;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>../src;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\xplaneConnect.c" />
<ClCompile Include="main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\xplaneConnect.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,71 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#include "chrome.h"
#include <stdio.h>
void displayStart(char* version)
{
printf("X-Plane Connect Playback Example [Version %s]\n", version);
printf("(c) 2013-2015 United States Government as represented by the Administrator\n");
printf("of the National Aeronautics and Space Administration. All Rights Reserved.\n");
}
int displayMenu(char* title, char* opts[], size_t count)
{
printf("\n");
printf("+---------------------------------------------- +\n");
printf("| %-42s |\n", title);
printf("+---------------------------------------------- +\n");
for (size_t i = 0; i < count; i++)
{
printf("| %2u. %-40s |\n", i + 1, opts[i]);
}
printf("+---------------------------------------------- +\n");
printf("Please select an option: ");
int opt;
scanf("%d", &opt);
return opt;
}
void displayMsg(char* msg)
{
printf("%s\n", msg);
}
void getString(char* prompt, char buffer[255])
{
printf("%s: ", prompt);
scanf("%255s", buffer);
}
int getInt(char* prompt)
{
printf("%s: ", prompt);
int result;
scanf("%d", &result);
return result;
}

View File

@@ -0,0 +1,40 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#ifndef XPC_PLAYBACKEX_CHROME_H_
#define XPC_PLAYBACKEX_CHROME_H_
#include <stdlib.h>
void displayStart(char* version);
int displayMenu(char* title, char* opts[], size_t count);
void displayMsg(char* msg);
void getString(char* prompt, char buffer[255]);
int getInt(char* prompt);
#endif

View File

@@ -0,0 +1,70 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#include "chrome.h"
#include "playback.h"
int main(void)
{
char* mainOpts[3] =
{
"Record X-Plane",
"Playback File",
"Exit"
};
char path[256] = { 0 };
displayStart("1.2.0.0");
while (1)
{
switch (displayMenu("What would you like to do?", mainOpts, 3))
{
case 1:
{
getString("Enter save file path", path);
int interval = getInt("Enter interval between frames (milliseconds)");
int duration = getInt("Enter duration to record for (seconds)");
record(path, interval, duration);
break;
}
case 2:
{
getString("Enter path to saved playback file", path);
int interval = getInt("Enter interval between frames (milliseconds)");
playback(path, interval);
break;
}
case 3:
displayMsg("Exiting.");
return 0;
default:
displayMsg("Unrecognized option.");
break;
}
}
return 0;
}

View File

@@ -0,0 +1,106 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#include "playback.h"
#include "chrome.h"
#include "xplaneConnect.h"
#include <stdio.h>
#include <time.h>
#ifdef WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
void playbackSleep(int ms)
{
#ifdef WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
void record(char* path, int interval, int duration)
{
FILE* fd = fopen(path, "w");
int count = duration * 1000 / interval;
if (!fd)
{
displayMsg("Unable to open output file.");
return;
}
if (count < 1)
{
displayMsg("Duration is less than one iteration.");
return;
}
displayMsg("Recording...");
XPCSocket sock = openUDP("127.0.0.1");
for (int i = 0; i < count; ++i)
{
float posi[7];
int result = getPOSI(sock, posi, 0);
playbackSleep(interval);
if (result < 0)
{
continue;
}
fprintf(fd, "%f, %f, %f, %f, %f, %f, %f\n", posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6]);
}
closeUDP(sock);
displayMsg("Recording Complete");
}
void playback(char* path, int interval)
{
FILE* fd = fopen(path, "r");
if (!fd)
{
displayMsg("Unable to open output file.");
return;
}
displayMsg("Starting Playback...");
XPCSocket sock = openUDP("127.0.0.1");
float posi[7];
while (!feof(fd) && !ferror(fd))
{
int result = fscanf(fd, "%f, %f, %f, %f, %f, %f, %f\n",
&posi[0], &posi[1], &posi[2], &posi[3], &posi[4], &posi[5], &posi[6]);
playbackSleep(interval);
if (result != 7)
{
continue;
}
sendPOSI(sock, posi, 7, 0);
}
closeUDP(sock);
displayMsg("Playback Complete");
}

View File

@@ -0,0 +1,32 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
#ifndef XPC_PLAYBACKEX_PLAYBACK_H_
#define XPC_PLAYBACKEX_PLAYBACK_H_
void record(char* path, int interval, int duration);
void playback(char* path, int interval);
#endif

View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "playbackExample", "playbackExample.vcxproj", "{40781C32-3EE6-4B3C-A94F-E128BD41E21C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.ActiveCfg = Debug|Win32
{40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.Build.0 = Debug|Win32
{40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.ActiveCfg = Release|Win32
{40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{40781C32-3EE6-4B3C-A94F-E128BD41E21C}</ProjectGuid>
<RootNamespace>playbackExample</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>../../src;../src;$(IncludePath)</IncludePath>
<SourcePath>../../src;../src;$(SourcePath)</SourcePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>../../src;../src;$(IncludePath)</IncludePath>
<SourcePath>../../src;../src;$(SourcePath)</SourcePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="../src/chrome.h" />
<ClInclude Include="../src/playback.h" />
<ClInclude Include="..\..\src\xplaneConnect.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="../src/chrome.c" />
<ClCompile Include="../src/main.c" />
<ClCompile Include="../src/playback.c" />
<ClCompile Include="..\..\src\xplaneConnect.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="../src/chrome.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="../src/playback.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\xplaneConnect.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="../src/chrome.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="../src/main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="../src/playback.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\xplaneConnect.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -117,7 +117,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port)
#else
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 1000;
timeout.tv_usec = 250000;
#endif
if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)
{
@@ -368,32 +368,46 @@ int readDATA(XPCSocket sock, float data[][9], int rows)
/*****************************************************************************/
int sendDREF(XPCSocket sock, const char* dref, float value[], int size)
{
// Setup command
// 5 byte header + max 255 char dref name + max 255 values * 4 bytes per value = 1279
unsigned char buffer[1279] = "DREF";
int drefLen = strnlen(dref, 256);
if (drefLen > 255)
{
printError("setDREF", "dref length is too long. Must be less than 256 characters.");
return -1;
}
if (size > 255)
{
printError("setDREF", "size is too big. Must be less than 256.");
return -2;
}
int len = 7 + drefLen + size * sizeof(float);
// Copy dref to buffer
buffer[5] = (unsigned char)drefLen;
memcpy(buffer + 6, dref, drefLen);
return sendDREFs(sock, &dref, &value, &size, 1);
}
// Copy values to buffer
buffer[6 + drefLen] = (unsigned char)size;
memcpy(buffer + 7 + drefLen, value, size * sizeof(float));
int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], int count)
{
// Setup command
// Max size is technically unlimited.
unsigned char buffer[65536] = "DREF";
int pos = 5;
for (int i = 0; i < count; ++i)
{
int drefLen = strnlen(drefs[i], 256);
if (pos + drefLen + sizes[i] * 4 + 2 > 65536)
{
printError("sendDREF", "About to overrun the send buffer!");
return -4;
}
if (drefLen > 255)
{
printError("sendDREF", "dref %d is too long. Must be less than 256 characters.", i);
return -1;
}
if (sizes[i] > 255)
{
printError("sendDREF", "size %d is too big. Must be less than 256.", i);
return -2;
}
// Copy dref to buffer
buffer[pos++] = (unsigned char)drefLen;
memcpy(buffer + pos, drefs[i], drefLen);
pos += drefLen;
// Copy values to buffer
buffer[pos++] = (unsigned char)sizes[i];
memcpy(buffer + pos, values[i], sizes[i] * sizeof(float));
pos += sizes[i] * sizeof(float);
}
// Send command
if (sendUDP(sock, buffer, len) < 0)
if (sendUDP(sock, buffer, pos) < 0)
{
printError("setDREF", "Failed to send command");
return -3;
@@ -508,6 +522,38 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char
/*****************************************************************************/
/**** POSI functions ****/
/*****************************************************************************/
int getPOSI(XPCSocket sock, float values[7], char ac)
{
// Setup send command
unsigned char buffer[6] = "GETP";
buffer[5] = ac;
// Send command
if (sendUDP(sock, buffer, 6) < 0)
{
printError("getPOSI", "Failed to send command.");
return -1;
}
// Get response
unsigned char readBuffer[34];
int readResult = readUDP(sock, readBuffer, 34);
if (readResult < 0)
{
printError("getPOSI", "Failed to read response.");
return -2;
}
if (readResult != 34)
{
printError("getPOSI", "Unexpected response length.");
return -3;
}
// Copy response into values
memcpy(values, readBuffer + 6, 7 * sizeof(float));
return 0;
}
int sendPOSI(XPCSocket sock, float values[], int size, char ac)
{
// Validate input
@@ -552,6 +598,41 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac)
/*****************************************************************************/
/**** CTRL functions ****/
/*****************************************************************************/
int getCTRL(XPCSocket sock, float values[7], char ac)
{
// Setup send command
unsigned char buffer[6] = "GETC";
buffer[5] = ac;
// Send command
if (sendUDP(sock, buffer, 6) < 0)
{
printError("getCTRL", "Failed to send command.");
return -1;
}
// Get response
unsigned char readBuffer[31];
int readResult = readUDP(sock, readBuffer, 31);
if (readResult < 0)
{
printError("getCTRL", "Failed to read response.");
return -2;
}
if (readResult != 31)
{
printError("getCTRL", "Unexpected response length.");
return -3;
}
// Copy response into values
memcpy(values, readBuffer + 5, 4 * sizeof(float));
values[4] = readBuffer[21];
values[5] = *((float*)(readBuffer + 22));
values[6] = *((float*)(readBuffer + 27));
return 0;
}
int sendCTRL(XPCSocket sock, float values[], int size, char ac)
{
// Validate input
@@ -672,7 +753,7 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count)
memcpy(buffer + 7, points, ptLen);
// Send Command
if (sendUDP(sock, buffer, 40) < 0)
if (sendUDP(sock, buffer, 7 + 12 * count) < 0)
{
printError("sendWYPT", "Failed to send command");
return -2;
@@ -682,3 +763,31 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count)
/*****************************************************************************/
/**** End Drawing functions ****/
/*****************************************************************************/
/*****************************************************************************/
/**** View functions ****/
/*****************************************************************************/
int sendVIEW(XPCSocket sock, VIEW_TYPE view)
{
// Validate Input
if (view < XPC_VIEW_FORWARDS || view > XPC_VIEW_FULLSCREENNOHUD)
{
printError("sendVIEW", "Unrecognized view");
return -1;
}
// Setup Command
char buffer[9] = "VIEW";
*((int*)(buffer + 5)) = view;
// Send Command
if (sendUDP(sock, buffer, 9) < 0)
{
printError("sendVIEW", "Failed to send command");
return -2;
}
return 0;
}
/*****************************************************************************/
/**** End View functions ****/
/*****************************************************************************/

View File

@@ -61,6 +61,23 @@ typedef enum
XPC_WYPT_DEL = 2,
XPC_WYPT_CLR = 3
} WYPT_OP;
typedef enum
{
XPC_VIEW_FORWARDS = 73,
XPC_VIEW_DOWN,
XPC_VIEW_LEFT,
XPC_VIEW_RIGHT,
XPC_VIEW_BACK,
XPC_VIEW_TOWER,
XPC_VIEW_RUNWAY,
XPC_VIEW_CHASE,
XPC_VIEW_FOLLOW,
XPC_VIEW_FOLLOWWITHPANEL,
XPC_VIEW_SPOT,
XPC_VIEW_FULLSCREENWITHHUD,
XPC_VIEW_FULLSCREENNOHUD,
} VIEW_TYPE;
// Low Level UDP Functions
@@ -127,13 +144,27 @@ int sendDATA(XPCSocket sock, float data[][9], int rows);
/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match
/// the size given on that page. XPC currently sends all values as floats regardless of
/// the type described on the wiki. This doesn't cause any data loss for most datarefs.
/// \param sock The socket to use to send the command.
/// \param dref The name of the dataref to set.
/// \param values An array of values representing the data to set.
/// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value.
/// \param sock The socket to use to send the command.
/// \param dref The name of the dataref to set.
/// \param value An array of values representing the data to set.
/// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value.
int sendDREF(XPCSocket sock, const char* dref, float value[], int size);
/// Sets the specified datarefs to the specified values.
///
/// \details dref names and their associated data types can be found on the XPSDK wiki at
/// http://www.xsquawkbox.net/xpsdk/docs/DataRefs.html. The size of values should match
/// the size given on that page. XPC currently sends all values as floats regardless of
/// the type described on the wiki. This doesn't cause any data loss for most datarefs.
/// \param sock The socket to use to send the command.
/// \param drefs The names of the datarefs to set.
/// \param values A multidimensional array containing the values for each dataref to set.
/// \param sizes The number of elements in each array in values
/// \param count The number of datarefs being set.
/// \returns 0 if successful, otherwise a negative value.
int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], int count);
/// Gets the value of the specified dataref.
///
/// \details dref names and their associated data types can be found on the XPSDK wiki at
@@ -165,6 +196,14 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char
// Position
/// Gets the position and orientation of the specified aircraft.
///
/// \param sock The socket used to send the command and receive the response.
/// \param values An array to store the position information returned by the
/// plugin. The format of values is [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear]
/// \returns 0 if successful, otherwise a negative value.
int getPOSI(XPCSocket sock, float values[7], char ac);
/// Sets the position and orientation of the specified aircraft.
///
/// \param sock The socket to use to send the command.
@@ -178,6 +217,16 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac);
// Controls
/// Gets the control surface information for the specified aircraft.
///
/// \param sock The socket used to send the command and receive the response.
/// \param values An array to store the position information returned by the
/// plugin. The format of values is [Elevator, Aileron, Rudder,
/// Throttle, Gear, Flaps, Speed Brakes]
/// \param ac The aircraft to set the control surfaces of. 0 is the main/player aircraft.
/// \returns 0 if successful, otherwise a negative value.
int getCTRL(XPCSocket sock, float values[7], char ac);
/// Sets the control surfaces of the specified aircraft.
///
/// \param sock The socket to use to send the command.
@@ -200,6 +249,13 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac);
/// \returns 0 if successful, otherwise a negative value.
int sendTEXT(XPCSocket sock, char* msg, int x, int y);
/// Sets the camera view in X-Plane.
///
/// \param sock The socket to use to send the command.
/// \param view The view to use.
/// \returns 0 if successful, otherwise a negative value.
int sendVIEW(XPCSocket sock, VIEW_TYPE view);
/// Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
/// and all points are removed.
///

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
ContinuousOperation

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="XPlaneConnect">
<CLASSES>
<root url="jar://$PROJECT_DIR$/libs/XPlaneConnect.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/ContinuousOperation.iml" filepath="$PROJECT_DIR$/ContinuousOperation.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="XPlaneConnect" level="project" />
</component>
</module>

View File

@@ -0,0 +1,47 @@
package gov.nasa.xpc.ex;
import gov.nasa.xpc.XPlaneConnect;
import java.io.IOException;
/**
* An example program demonstrating the use of X-PlaneConnect in a continuous loop.
*
* @author Jason Watkins
* @version 1.0
* @since 2015-06-19
*/
public class Main
{
public static void main(String[] args)
{
try (XPlaneConnect xpc = new XPlaneConnect())
{
int aircraft = 0;
while(true)
{
float[] posi = xpc.getPOSI(aircraft);
float[] ctrl = xpc.getCTRL(aircraft);
System.out.format("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n",
posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]);
try
{
Thread.sleep(100);
}
catch (InterruptedException ex) {}
if(System.in.available() > 0)
{
break;
}
}
}
catch(IOException ex)
{
System.out.println("Error:");
System.out.println(ex.getMessage());
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View File

@@ -0,0 +1 @@
<html>Simple <b>Java</b> application that includes a class with <code>main()</code> method</html>

View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="XPlaneConnect">
<CLASSES>
<root url="jar://$PROJECT_DIR$/../../out/artifacts/XPlaneConnect_jar/XPlaneConnect.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

12
Java/Examples/Playback/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectKey">
<option name="state" value="project://e2804f05-5315-4fc6-a121-c522a6c26470" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Playback.iml" filepath="$PROJECT_DIR$/Playback.iml" />
</modules>
</component>
</project>

View File

@@ -0,0 +1,3 @@
<template>
<input-field default="com.company">IJ_BASE_PACKAGE</input-field>
</template>

6
Java/Examples/Playback/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="XPlaneConnect" level="project" />
</component>
</module>

View File

@@ -0,0 +1,147 @@
package gov.nasa.xpc;
import com.sun.javafx.scene.EnteredExitedHandler;
import java.io.*;
import java.util.Scanner;
public class Main
{
public static void main(String[] args) throws IOException
{
String[] mainOpts = new String[] { "Record X-Plane", "Playback File", "Exit" };
System.out.println("X-Plane Connect Playback Example [Version 1.2.0.0]");
System.out.println("(c) 2013-2015 United States Government as represented by the Administrator");
System.out.println("of the National Aeronautics and Space Administration. All Rights Reserved.");
while(true)
{
int result = displayMenu("What would you like to do?", mainOpts);
switch(result)
{
case 1:
{
String path = getString("Enter a save file path: ");
int interval = getInt("Enter interval between frames (milliseconds): ");
int duration = getInt("Enter duration to record for (seconds): ");
record(path, interval, duration);
break;
}
case 2:
{
String path = getString("Enter path to saved playback file: ");
int interval = getInt("Enter interval between frames (milliseconds): ");
playback(path, interval);
break;
}
case 3:
{
System.out.println("Exiting.");
return;
}
default:
{
System.out.println("Unrecognized menu option.");
break;
}
}
}
}
private static int displayMenu(String title, String[] opts) throws IOException
{
System.out.println();
System.out.println("+---------------------------------------------- +");
System.out.println(String.format("| %1$-42s |", title));
System.out.println("+---------------------------------------------- +");
for(int i = 0; i < opts.length; ++i)
{
System.out.println(String.format("| %1$2d. %2$-40s |", i + 1, opts[i]));
}
System.out.println("+---------------------------------------------- +");
return getInt("Please select an option: ");
}
private static String getString(String prompt) throws IOException
{
System.out.print(prompt);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
return reader.readLine();
}
private static int getInt(String prompt) throws IOException
{
System.out.print(prompt);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String result = reader.readLine();
return Integer.parseInt(result);
}
public static void record(String path, int interval, int duration) throws IOException
{
int count = duration * 1000 / interval;
if (count < 1)
{
throw new IllegalArgumentException("duration is less than one interval.");
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(path)))
{
System.out.println("Recording...");
try (XPlaneConnect xpc = new XPlaneConnect())
{
for (int i = 0; i < count; ++i)
{
float[] posi = xpc.getPOSI(0);
writer.write(String.format("%1$f, %2$f, %3$f, %4$f, %5$f, %6$f, %7$f\n",
posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6]));
try
{
Thread.sleep(interval);
}
catch (InterruptedException ex)
{
}
}
}
System.out.println("Recording Complete");
}
}
public static void playback(String path, int interval) throws IOException
{
try(Scanner reader = new Scanner(new FileReader(path)))
{
reader.useDelimiter("[\\s\\r\\n,]+");
System.out.println("Starting playback...");
try (XPlaneConnect xpc = new XPlaneConnect())
{
while(reader.hasNextLine())
{
float[] posi = new float[7];
for (int i = 0; i < 7; ++i)
{
String s = reader.next();
posi[i] = Float.parseFloat(s);
}
reader.nextLine();
xpc.sendPOSI(posi);
try
{
Thread.sleep(interval);
}
catch (InterruptedException ex)
{
}
}
}
}
}
}

60
Java/src/ViewType.java Normal file
View File

@@ -0,0 +1,60 @@
//NOTICES:
// Copyright <20> 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE
// SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT
// SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO
// THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN ENDORSEMENT BY
// GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE,
// SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE.
// FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY
// SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES,
// EXPENSES OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR
// RESULTING FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD
// HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY
// PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER
// SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
package gov.nasa.xpc;
/**
* Represents a camera view in X-Plane
*
* @author Jason Watkins
* @version 1.1
* @since 2015-05-08
*/
public enum ViewType
{
Forwards(73),
Down(74),
Left(75),
Right(76),
Back(77),
Tower(78),
Runway(79),
Chase(80),
Follow(81),
FollowWithPanel(82),
Spot(83),
FullscreenWithHud(84),
FullscreenNoHud(85);
private final int value;
private ViewType(int value)
{
this.value = value;
}
public int getValue()
{
return value;
}
}

View File

@@ -331,46 +331,114 @@ public class XPlaneConnect implements AutoCloseable
* @throws IOException If the command cannot be sent.
*/
public void sendDREF(String dref, float[] value) throws IOException
{
sendDREFs(new String[] {dref}, new float[][] {value});
}
/**
* Sends a command to X-Plane that sets the given DREF.
*
* @param drefs The names of the X-Plane datarefs to set.
* @param values A sequence of arrays of floating point values whose structure depends on the drefs specified.
* @throws IOException If the command cannot be sent.
*/
public void sendDREFs(String[] drefs, float[][] values) throws IOException
{
//Preconditions
if(dref == null)
if(drefs == null || drefs.length == 0)
{
throw new IllegalArgumentException("dref must be a valid string.");
throw new IllegalArgumentException(("drefs must be non-empty."));
}
if(value == null || value.length == 0)
if(values == null || values.length != drefs.length)
{
throw new IllegalArgumentException("value must be non-null and should contain at least one value.");
throw new IllegalArgumentException("values must be of the same size as drefs.");
}
//Convert drefs to bytes.
byte[] drefBytes = dref.getBytes(StandardCharsets.UTF_8);
if(drefBytes.length == 0)
{
throw new IllegalArgumentException("DREF is an empty string!");
}
if(drefBytes.length > 255)
{
throw new IllegalArgumentException("dref must be less than 255 bytes in UTF-8. Are you sure this is a valid dref?");
}
ByteBuffer bb = ByteBuffer.allocate(4 * value.length);
bb.order(ByteOrder.LITTLE_ENDIAN);
for(int i = 0; i < value.length; ++i)
{
bb.putFloat(i * 4, value[i]);
}
//Build and send message
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("DREF".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length
os.write(drefBytes.length);
os.write(drefBytes, 0, drefBytes.length);
os.write(value.length);
os.write(bb.array());
for(int i = 0; i < drefs.length; ++i)
{
String dref = drefs[i];
float[] value = values[i];
if (dref == null)
{
throw new IllegalArgumentException("dref must be a valid string.");
}
if (value == null || value.length == 0)
{
throw new IllegalArgumentException("value must be non-null and should contain at least one value.");
}
//Convert drefs to bytes.
byte[] drefBytes = dref.getBytes(StandardCharsets.UTF_8);
if (drefBytes.length == 0)
{
throw new IllegalArgumentException("DREF is an empty string!");
}
if (drefBytes.length > 255)
{
throw new IllegalArgumentException("dref must be less than 255 bytes in UTF-8. Are you sure this is a valid dref?");
}
ByteBuffer bb = ByteBuffer.allocate(4 * value.length);
bb.order(ByteOrder.LITTLE_ENDIAN);
for (int j = 0; j < value.length; ++j)
{
bb.putFloat(j * 4, value[j]);
}
//Build and send message
os.write(drefBytes.length);
os.write(drefBytes, 0, drefBytes.length);
os.write(value.length);
os.write(bb.array());
}
sendUDP(os.toByteArray());
}
/**
* Gets the control surface information for the specified airplane.
*
* @param ac The aircraft to get control surface information for.
* @return An array containing control surface data in the same format as {@code sendCTRL}.
* @throws IOException If the command cannot be sent or a response cannot be read.
*/
public float[] getCTRL(int ac) throws IOException
{
// Send request
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("GETC".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length
os.write(ac);
sendUDP(os.toByteArray());
// Read response
byte[] data = readUDP();
if(data.length == 0)
{
throw new IOException("No response received.");
}
if(data.length < 31)
{
throw new IOException("Response too short");
}
// Parse response
float[] result = new float[7];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.order(ByteOrder.LITTLE_ENDIAN);
result[0] = bb.getFloat(5);
result[1] = bb.getFloat(9);
result[2] = bb.getFloat(13);
result[3] = bb.getFloat(17);
result[4] = bb.get(21);
result[5] = bb.getFloat(22);
result[6] = bb.getFloat(27);
return result;
}
/**
* Sends command to X-Plane setting control surfaces on the player ac.
*
@@ -382,6 +450,7 @@ public class XPlaneConnect implements AutoCloseable
* <li>Throttle [-1, 1]</li>
* <li>Gear (0=up, 1=down)</li>
* <li>Flaps [0, 1]</li>
* <li>Speedbrakes [-0.5, 1.5]</li>
* </ol>
* <p>
* If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To
@@ -406,6 +475,7 @@ public class XPlaneConnect implements AutoCloseable
* <li>Throttle [-1, 1]</li>
* <li>Gear (0=up, 1=down)</li>
* <li>Flaps [0, 1]</li>
* <li>Speedbrakes [-0.5, 1.5]</li>
* </ol>
* <p>
* If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To
@@ -440,7 +510,14 @@ public class XPlaneConnect implements AutoCloseable
{
if(i == 4)
{
bb.put(cur, (byte) values[i]);
if(i >= values.length)
{
bb.put(cur, (byte)-1);
}
else
{
bb.put(cur, (byte)values[i]);
}
cur += 1;
}
else if (i >= values.length)
@@ -465,6 +542,44 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray());
}
/**
* Gets position information for the specified airplane.
*
* @param ac The aircraft to get position information for.
* @return An array containing control surface data in the same format as {@code sendPOSI}.
* @throws IOException If the command cannot be sent or a response cannot be read.
*/
public float[] getPOSI(int ac) throws IOException
{
// Send request
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("GETP".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length
os.write(ac);
sendUDP(os.toByteArray());
// Read response
byte[] data = readUDP();
if(data.length == 0)
{
throw new IOException("No response received.");
}
if(data.length < 34)
{
throw new IOException("Response too short");
}
// Parse response
float[] result = new float[7];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.order(ByteOrder.LITTLE_ENDIAN);
for(int i = 0; i < 7; ++i)
{
result[i] = bb.getFloat(6 + 4 * i);
}
return result;
}
/**
* Sets the position of the player ac.
*
@@ -693,6 +808,26 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray());
}
/**
* Sets the camera view in X-Plane.
*
* @param view The view to use.
* @throws IOException If the command cannot be sent.
*/
public void sendVIEW(ViewType view) throws IOException
{
ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(view.getValue());
//Build and send message
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("VIEW".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length
os.write(bb.array());
sendUDP(os.toByteArray());
}
/**
* Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
* and all points are removed.

View File

@@ -22,6 +22,10 @@ socket.close;
%% Track open clients
global clients;
%TODO: Remove stale clients
for i = 1:length(clients)
if socket == clients(i)
clients = [clients(1:i-1) clients(i+1:length(clients))];
end
end
end

View File

@@ -0,0 +1,29 @@
function ctrl = getCTRL(ac, socket)
% getCTRL Gets control surface information for the specified aircraft
%
% Inputs
% ac: The aircraft number to get control surface data for.
% Outputs
% posi: An array of values matching the the format used by sendCTRL
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[getCTRL] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
ac = int32(ac);
%% Send command
ctrl = socket.getCTRL(ac);
end

View File

@@ -0,0 +1,29 @@
function posi = getPOSI(ac, socket)
% getPOSI Gets position information for the specified aircraft
%
% Inputs
% ac: The aircraft number to get position data for.
% Outputs
% posi: An array of values matching the the format used by sendPOSI
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[getPOSI] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
ac = int32(ac);
%% Send command
posi = socket.getPOSI(ac);
end

View File

@@ -26,6 +26,7 @@ p = inputParser;
addOptional(p,'xpHost','127.0.0.1',@ischar);
addOptional(p,'xpPort',49009,@isnumeric);
addOptional(p,'port',0,@isnumeric);
addOptional(p,'timeout',100,@isnumeric);
parse(p,varargin{:});
%% Create client
@@ -33,7 +34,7 @@ if ~exist('gov.nasa.xpc.XPlaneConnect', 'class')
[folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP'));
javaaddpath(fullfile(folder, 'XPlaneConnect.jar'));
end
socket = gov.nasa.xpc.XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port);
socket = gov.nasa.xpc.XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port, p.Results.timeout);
%% Track open clients
global clients;

View File

@@ -1,17 +1,8 @@
function sendDREF( dref, value, socket )
% sendDREF Sends a command to X-Plane that sets the given DREF.
% sendDREFs Sends a command to X-Plane that sets the given DREF. This
% function is now an alias to sendDREFs. It is kept only for backwards
% compatibility.
%
% Inputs
% dref: The name of the X-Plane dataref to set.
% Value: An array of floating point values whose structure depends on the dref specified.
% socket (optional): The client to use when sending the command.
%
% Use
% 1. import XPlaneConnect.*
% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear
% 3. Value = 0;
% 4. status = setDREF(dataRef, Value);
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
@@ -20,19 +11,8 @@ function sendDREF( dref, value, socket )
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
value = single(value);
%%Send command
socket.sendDREF(dref, value);
sendDREFs(dref, value)
else
sendDREFs(dref, value, socket)
end

View File

@@ -0,0 +1,37 @@
function sendDREFs( drefs, values, socket )
% sendDREFs Sends a command to X-Plane that sets the given DREF(s).
%
% Inputs
% dref: The name or names of the X-Plane dataref(s) to set.
% Value: An array or an multidimensional array of floating point values
% whose structure depends on the dref specified.
% socket (optional): The client to use when sending the command.
%
% Use
% 1. import XPlaneConnect.*
% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear
% 3. Value = 0;
% 4. status = setDREF(dataRef, Value);
%
% Contributors
% [JW] Jason Watkins
% jason.w.watkins@nasa.gov
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
values = single(values);
%%Send command
socket.sendDREFs(drefs, values);

View File

@@ -0,0 +1,35 @@
function sendVIEW(view, socket)
% sendVIEW Sets the camera
%
%Inputs
% view: The view to use.
% socket (optional): The client to use when sending the command.
%
%Use
% 1. import XPlaneConnect.*;
% 2. sendView(Forwards);
%
% Contributors
% [JW] Jason Watkins <jason.w.watkins@nasa.gov>
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[pauseSim] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
%% Send command
socket.sendVIEW(view)
end

View File

@@ -10,21 +10,21 @@ import XPlaneConnect.*
disp('xplaneconnect Example Script-');
disp('Setting up Simulation');
Socket = openUDP(49005);
Socket = openUDP('127.0.0.1', 49009);
%% Ensure connected
getDREFs('sim/test/test_float')
getDREFs('sim/test/test_float', Socket);
%% Set position of the player aircraft
disp('Setting position');
pauseSim(1);
pauseSim(1, Socket);
% Lat Lon Alt Pitch Roll Heading Gear
POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1];
sendPOSI(POSI); % Set own aircraft position
sendPOSI(POSI, 0, Socket); % Set own aircraft position
% Lat Lon Alt Pitch Roll Heading Gear
POSI = [37.52465, -122.06899, 2500, 0, 20, 0, 1];
sendPOSI(POSI, 1); % Place another aircraft just north of us
sendPOSI(POSI, 1, Socket); % Place another aircraft just north of us
%% Set rates
disp('Setting rates');
% Alpha Velocity PQR
@@ -32,30 +32,30 @@ data = struct('h',[18, 3, 16],...
'd',[0,-999,0,-999,-999,-999,-999,-999;... % Alpha data
130,130,130,130,-999,-999,-999,-999;... % Velocity data
0,0,0,-999,-999,-999,-999,-999]); % PQR data
sendDATA(data);
sendDATA(data, Socket);
%% Set CTRL
% Throttle
CTRL = [0,0,0,0.8];
sendCTRL(CTRL);
CTRL = [0,0,0,0.8,0,0];
sendCTRL(CTRL, 0, Socket);
pause(5);
pauseSim(0);
pauseSim(0, Socket);
pause(5) % Run sim for 5 seconds
%% Pause sim
disp('Pausing simulation');
pauseSim(1);
pauseSim(1, Socket);
pause(5);
disp('Unpausing simulation');
pauseSim(0);
pauseSim(0, Socket);
pause(10) % Run sim for 10 seconds
%% Use DREF to raise landing gear
disp('Raising gear');
gearDREF = 'sim/cockpit/switches/gear_handle_status';
sendDREF(gearDREF, 0);
sendDREF(gearDREF, 0, Socket);
pause(10) % Run sim for 10 seconds
%% Confirm gear and paus status by reading DREFs
disp('Checking gear');
pauseDREF = 'sim/operation/override/override_planepath';
result = requestDREF({gearDREF, pauseDREF});
result = getDREFs({gearDREF, pauseDREF}, Socket);
if result{1} == 0
disp('Gear stowed');
else

View File

@@ -0,0 +1,19 @@
%% X-Plane Connect MATLAB Example Script
% This script demonstrates how to read and write data to the XPC plugin.
% Before running this script, ensure that the XPC plugin is installed and
% X-Plane is running.
%% Import XPC
clear all
addpath('../')
import XPlaneConnect.*
Socket = openUDP();
while 1
posi = getPOSI(0, Socket);
ctrl = getCTRL(0, Socket);
fprintf('Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n', ...
posi(1), posi(2), posi(3), ctrl(2), ctrl(1), ctrl(3));
pause(0.1);
end
closeUDP(Socket);

View File

@@ -0,0 +1,100 @@
37.184181, -120.793640, 3904.040771, 1.856917, 0.001644, 116.058609, 0.000000
37.184143, -120.793541, 3903.825928, 2.172139, -0.034512, 115.875587, 0.000000
37.184090, -120.793404, 3903.511719, 2.425995, -0.111958, 115.577911, 0.000000
37.184040, -120.793282, 3903.230225, 2.497653, -0.205506, 115.289268, 0.000000
37.183994, -120.793167, 3902.961914, 2.478850, -0.315117, 115.006279, 0.000000
37.183949, -120.793053, 3902.683838, 2.412536, -0.447890, 114.716370, 0.000000
37.183910, -120.792953, 3902.452881, 2.344441, -0.570747, 114.485756, 0.000000
37.183857, -120.792824, 3902.137939, 2.253826, -0.752984, 114.194984, 0.000000
37.183807, -120.792702, 3901.830566, 2.178489, -0.942979, 113.943779, 0.000000
37.183765, -120.792595, 3901.561279, 2.125190, -1.116157, 113.754135, 0.000000
37.183735, -120.792511, 3901.342773, 2.089507, -1.260052, 113.621834, 0.000000
37.183693, -120.792404, 3901.058350, 2.051378, -1.449701, 113.479713, 0.000000
37.183628, -120.792244, 3900.620361, 2.004393, -1.742637, 113.320374, 0.000000
37.183571, -120.792107, 3900.232178, 1.969860, -1.999084, 113.234406, 0.000000
37.183529, -120.791992, 3899.922852, 1.945357, -2.199157, 113.199463, 0.000000
37.183487, -120.791885, 3899.607666, 1.922479, -2.397843, 113.190887, 0.000000
37.183441, -120.791771, 3899.279297, 1.738008, -2.604320, 113.214371, 0.000000
37.183395, -120.791649, 3898.942383, 1.172052, -2.819112, 113.280800, 0.000000
37.183342, -120.791512, 3898.525879, 0.437769, -3.059577, 113.392952, 0.000000
37.183292, -120.791389, 3898.116943, -0.110597, -3.260861, 113.510010, 0.000000
37.183247, -120.791275, 3897.675293, -0.458939, -3.399007, 113.635101, 0.000000
37.183220, -120.791206, 3897.409668, -0.495934, -3.425584, 113.706985, 0.000000
37.183182, -120.791107, 3896.976318, -0.338542, -3.312644, 113.832870, 0.000000
37.183128, -120.790962, 3896.313965, 0.088305, -2.812056, 114.069130, 0.000000
37.183083, -120.790848, 3895.734863, 0.355302, -2.143086, 114.327766, 0.000000
37.183033, -120.790718, 3895.112061, 0.345563, -1.171917, 114.668144, 0.000000
37.182987, -120.790604, 3894.519043, 0.034761, -0.025642, 115.037415, 0.000000
37.182941, -120.790489, 3893.926270, -0.549108, 1.265883, 115.421654, 0.000000
37.182888, -120.790344, 3893.151611, -1.627703, 3.039703, 115.896240, 0.000000
37.182831, -120.790199, 3892.313477, -2.879907, 4.868656, 116.325722, 0.000000
37.182785, -120.790077, 3891.557129, -3.883288, 6.350969, 116.633751, 0.000000
37.182743, -120.789970, 3890.766602, -4.752331, 7.729563, 116.888329, 0.000000
37.182705, -120.789871, 3889.997314, -5.439554, 8.871016, 117.077042, 0.000000
37.182659, -120.789749, 3888.965820, -6.164141, 10.014505, 117.239494, 0.000000
37.182610, -120.789627, 3887.832031, -6.734039, 10.811956, 117.319382, 0.000000
37.182560, -120.789505, 3886.558594, -7.134449, 11.245357, 117.313339, 0.000000
37.182514, -120.789383, 3885.197510, -7.320403, 11.326477, 117.228592, 0.000000
37.182465, -120.789261, 3883.700439, -7.264509, 11.086659, 117.071564, 0.000000
37.182415, -120.789131, 3882.124023, -7.020517, 10.579235, 116.843575, 0.000000
37.182365, -120.789009, 3880.477295, -6.698184, 9.864036, 116.538239, 0.000000
37.182316, -120.788887, 3878.771973, -6.407937, 8.986709, 116.154274, 0.000000
37.182266, -120.788765, 3877.028076, -6.217397, 7.992366, 115.705994, 0.000000
37.182217, -120.788643, 3875.249023, -6.173934, 6.988136, 115.220467, 0.000000
37.182167, -120.788521, 3873.432617, -6.285208, 6.050294, 114.729034, 0.000000
37.182117, -120.788399, 3871.583496, -6.453388, 5.213163, 114.269440, 0.000000
37.182068, -120.788269, 3869.689209, -6.484870, 4.480176, 113.873062, 0.000000
37.182014, -120.788147, 3867.766357, -6.310386, 3.856612, 113.555290, 0.000000
37.181965, -120.788025, 3865.820801, -6.021461, 3.327790, 113.311180, 0.000000
37.181915, -120.787903, 3863.870361, -5.728759, 2.874094, 113.135651, 0.000000
37.181866, -120.787781, 3861.910156, -5.480271, 2.475115, 113.025513, 0.000000
37.181816, -120.787651, 3859.973145, -5.302026, 2.122235, 112.979225, 0.000000
37.181767, -120.787529, 3858.042969, -5.182843, 1.808723, 112.993736, 0.000000
37.181717, -120.787407, 3856.119629, -5.038956, 1.552066, 113.070557, 0.000000
37.181667, -120.787285, 3854.215088, -4.828547, 1.368155, 113.206085, 0.000000
37.181614, -120.787148, 3852.185059, -4.515563, 1.254129, 113.410904, 0.000000
37.181557, -120.787010, 3850.030762, -4.132499, 1.210060, 113.681358, 0.000000
37.181507, -120.786888, 3848.263916, -3.831450, 1.225745, 113.935280, 0.000000
37.181458, -120.786766, 3846.475342, -3.528734, 1.284648, 114.214294, 0.000000
37.181408, -120.786644, 3844.712158, -3.243377, 1.382594, 114.502975, 0.000000
37.181358, -120.786522, 3842.987549, -2.982575, 1.515415, 114.790489, 0.000000
37.181309, -120.786392, 3841.302246, -2.688687, 1.679037, 115.070442, 0.000000
37.181259, -120.786270, 3839.659424, -2.305070, 1.869125, 115.338135, 0.000000
37.181210, -120.786148, 3838.070068, -1.827937, 2.080883, 115.588036, 0.000000
37.181156, -120.786018, 3836.515137, -1.326015, 2.315055, 115.817902, 0.000000
37.181107, -120.785889, 3835.020508, -0.858468, 2.566000, 116.019691, 0.000000
37.181053, -120.785767, 3833.605713, -0.446646, 2.827466, 116.188957, 0.000000
37.181004, -120.785637, 3832.263672, -0.082390, 3.097450, 116.326546, 0.000000
37.180950, -120.785515, 3830.996094, 0.248427, 3.372406, 116.433670, 0.000000
37.180901, -120.785385, 3829.802734, 0.561189, 3.648955, 116.513596, 0.000000
37.180847, -120.785263, 3828.686279, 0.901348, 3.909596, 116.570610, 0.000000
37.180798, -120.785133, 3827.642090, 1.422296, 4.101938, 116.610832, 0.000000
37.180744, -120.785011, 3826.675537, 2.124010, 4.216800, 116.636208, 0.000000
37.180691, -120.784889, 3825.800049, 2.895841, 4.268193, 116.644135, 0.000000
37.180641, -120.784767, 3825.024658, 3.647615, 4.264566, 116.632553, 0.000000
37.180588, -120.784637, 3824.355469, 4.389325, 4.198403, 116.604164, 0.000000
37.180538, -120.784523, 3823.797119, 5.116096, 4.063403, 116.559959, 0.000000
37.180489, -120.784401, 3823.351562, 5.802851, 3.858843, 116.500069, 0.000000
37.180435, -120.784279, 3823.021240, 6.434232, 3.583184, 116.425621, 0.000000
37.180386, -120.784164, 3822.803223, 7.040646, 3.237821, 116.340439, 0.000000
37.180336, -120.784042, 3822.697021, 7.629384, 2.839028, 116.249237, 0.000000
37.180286, -120.783928, 3822.700195, 8.181519, 2.404881, 116.155312, 0.000000
37.180244, -120.783821, 3822.798828, 8.596326, 1.981372, 116.066658, 0.000000
37.180183, -120.783684, 3823.074219, 9.020256, 1.372200, 115.947289, 0.000000
37.180126, -120.783554, 3823.453369, 9.331639, 0.798377, 115.847244, 0.000000
37.180073, -120.783424, 3823.932129, 9.642418, 0.224538, 115.759720, 0.000000
37.180019, -120.783302, 3824.521729, 9.962214, -0.365530, 115.680557, 0.000000
37.179970, -120.783188, 3825.146240, 10.216949, -0.901261, 115.617035, 0.000000
37.179928, -120.783081, 3825.777100, 10.420901, -1.378152, 115.566948, 0.000000
37.179871, -120.782951, 3826.665283, 10.683296, -1.973540, 115.510689, 0.000000
37.179817, -120.782829, 3827.599854, 10.932059, -2.526971, 115.463028, 0.000000
37.179764, -120.782700, 3828.676758, 11.182608, -3.091355, 115.418022, 0.000000
37.179710, -120.782570, 3829.791748, 11.405788, -3.608584, 115.379250, 0.000000
37.179657, -120.782448, 3830.954102, 11.541166, -4.084742, 115.349564, 0.000000
37.179604, -120.782326, 3832.230469, 11.431418, -4.526593, 115.343117, 0.000000
37.179554, -120.782204, 3833.525391, 11.053594, -4.891589, 115.367775, 0.000000
37.179501, -120.782082, 3834.855957, 10.596283, -5.199400, 115.407677, 0.000000
37.179451, -120.781960, 3836.201660, 10.188977, -5.458726, 115.448441, 0.000000
37.179401, -120.781837, 3837.573486, 9.879080, -5.681745, 115.481895, 0.000000
37.179348, -120.781715, 3838.972656, 9.667299, -5.869191, 115.505310, 0.000000
37.179298, -120.781593, 3840.342529, 9.507944, -5.992330, 115.524872, 0.000000

View File

@@ -0,0 +1,36 @@
%% X-Plane Connect MATLAB Playback Example Script
% This script demonstrates how to playback recorded data from X-Plane.
% (See Record.m)
% Before running this script, ensure that the XPC plugin is installed and
% X-Plane is running.
%% Import XPC
addpath('../')
import XPlaneConnect.*
%% Setup
% Create variables and open connection to X-Plane
path = 'MyRecording.txt'; % File containing stored data
interval = 0.1; % Time between snapshots in seconds
duration = 10;
Socket = openUDP(); % Open connection to X-Plane
fd = fopen(path, 'r'); % Open file
disp('X-Plane Connect Playback Example Script');
fprintf('Playing back ''%s'' in %fs increments.\n', path, interval);
%% Start Playback
count = floor(duration / interval);
pauseSim(1);
for i = 1:count
line = fgetl(fd);
posi = sscanf(line, '%f, %f, %f, %f, %f, %f, %f\n');
sendPOSI(posi);
pause(interval);
end
%% Close connection and file
pauseSim(0);
closeUDP(Socket);
fclose(fd);
disp('Playback complete.');

View File

@@ -0,0 +1,34 @@
%% X-Plane Connect MATLAB Recording Example Script
% This script demonstrates how to record data from X-Plane that can later
% be played back. (See Playback.m)
% Before running this script, ensure that the XPC plugin is installed and
% X-Plane is running.
%% Import XPC
addpath('../')
import XPlaneConnect.*
%% Setup
% Create variables and open connection to X-Plane
path = 'MyRecording.txt'; % File to save the data in
interval = 0.1; % Time between snapshots in seconds
duration = 10; % Time to record for in seconds
Socket = openUDP(); % Open connection to X-Plane
fd = fopen(path, 'w'); % Open file
disp('X-Plane Connect Recording Example Script');
fprintf('Recording to ''%s'' for %f seconds in %fs increments.\n', path, duration, interval);
%% Start Recording
count = floor(duration / interval);
for i = 1:count
posi = getPOSI(0, Socket);
fprintf(fd, '%f, %f, %f, %f, %f, %f, %f\n', ...
posi(1), posi(2), posi(3), posi(4), posi(5), posi(6), posi(7));
pause(interval);
end
%% Close connection and file
closeUDP(Socket);
fclose(fd);
disp('Recording complete.');

View File

@@ -0,0 +1,16 @@
import sys
import xpc
def monitor():
with xpc.XPlaneConnect() as client:
while True:
posi = client.getPOSI();
ctrl = client.getCTRL();
print "Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n"\
% (posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2])
if __name__ == "__main__":
monitor()

View File

@@ -0,0 +1,82 @@
from time import sleep
import xpc
def record(path, interval = 0.1, duration = 60):
try:
fd = open(path, "w")
except:
print "Unable to open file."
return
count = int(duration / interval)
if count < 1:
print "duration is less than a single frame."
return
with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client:
print "Recording..."
for i in range(0, count):
try:
posi = client.getPOSI()
fd.write("{0}, {1}, {2}, {3}, {4}, {5}, {6}\n".format(*posi))
except:
print "Error reading position"
continue
sleep(interval);
print "Recording Complete"
fd.close()
def playback(path, interval):
try:
fd = open(path, "r")
except:
print "Unable to open file."
return
with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client:
print "Starting Playback..."
for line in fd:
try:
posi = [ float(x) for x in line.split(',') ]
posi = client.sendPOSI(posi)
except:
print "Error sending position"
continue
sleep(interval);
print "Playback Complete"
fd.close()
def printMenu(title, opts):
print "\n+---------------------------------------------- +"
print "| {0:42} |\n".format(title)
print "+---------------------------------------------- +"
for i in range(0,len(opts)):
print "| {0:2}. {1:40} |".format(i + 1, opts[i])
print "+---------------------------------------------- +"
return int(raw_input("Please select and option: "))
def ex():
print "X-Plane Connect Playback Example [Version 1.2.0]"
print "(c) 2013-2015 United States Government as represented by the Administrator"
print "of the National Aeronautics and Space Administration. All Rights Reserved."
mainOpts = [ "Record X-Plane", "Playback File", "Exit" ]
while True:
opt = printMenu("What would you like to do?", mainOpts)
if opt == 1:
path = raw_input("Enter save file path: ")
interval = float(raw_input("Enter interval between frames (seconds): "))
duration = float(raw_input("Enter duration to record for (seconds): "))
record(path, interval, duration)
elif opt == 2:
path = raw_input("Enter save file path: ")
interval = float(raw_input("Enter interval between frames (seconds): "))
playback(path, interval)
elif opt == 3:
return;
else:
print "Unrecognized option."
if __name__ == "__main__":
ex()

View File

@@ -79,8 +79,20 @@ class XPlaneConnect(object):
if port < 0 or port > 65535:
raise ValueError("The specified port is not a valid port number.")
#Send command
buffer = struct.pack("<4sxH", "CONN", port)
self.sendUDP(buffer)
#Rebind socket
clientAddr = ("0.0.0.0", port)
timeout = self.socket.gettimeout();
self.socket.close();
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.socket.bind(clientAddr)
self.socket.settimeout(timeout)
#Read response
buffer = self.socket.recv(1024)
def pauseSim(self, pause):
'''Pauses or un-pauses the physics simulation engine in X-Plane.
@@ -131,7 +143,29 @@ class XPlaneConnect(object):
buffer += struct.pack("<I8f", *row)
self.sendUDP(buffer)
# Position
# Position
def getPOSI(self, ac = 0):
'''Gets position information for the specified aircraft.
Args:
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft.
'''
# Send request
buffer = struct.pack("<4sxB", "GETP", ac)
self.sendUDP(buffer)
# Read response
resultBuf = self.readUDP()
if len(resultBuf) != 34:
raise ValueError("Unexpected response length.")
result = struct.unpack("<4sxBfffffff", resultBuf)
if result[0] != "POSI":
raise ValueError("Unexpected header: " + result[0])
# Drop the header & ac from the return value
return result[2:]
def sendPOSI(self, values, ac = 0):
'''Sets position information on the specified aircraft.
@@ -143,8 +177,8 @@ class XPlaneConnect(object):
* Latitude (deg)
* Longitude (deg)
* Altitude (m above MSL)
* Roll (deg)
* Pitch (deg)
* Roll (deg)
* True Heading (deg)
* Gear (0=up, 1=down)
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft.
@@ -167,6 +201,29 @@ class XPlaneConnect(object):
self.sendUDP(buffer)
# Controls
def getCTRL(self, ac = 0):
'''Gets the control surface information for the specified aircraft.
Args:
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft.
'''
# Send request
buffer = struct.pack("<4sxB", "GETC", ac)
self.sendUDP(buffer)
# Read response
resultBuf = self.readUDP()
if len(resultBuf) != 31:
raise ValueError("Unexpected response length.")
result = struct.unpack("<4sxffffbfBf", resultBuf)
if result[0] != "CTRL":
raise ValueError("Unexpected header: " + result[0])
# Drop the header from the return value
result =result[1:7] + result[8:]
return result
def sendCTRL(self, values, ac = 0):
'''Sets control surface information on the specified aircraft.
@@ -208,31 +265,45 @@ class XPlaneConnect(object):
# Send
self.sendUDP(buffer)
# DREF Manipulation
# DREF Manipulation
def sendDREF(self, dref, values):
'''Sets the specified dataref to the specified value.
Args:
dref: The name of the dataref to set.
dref: The name of the datarefs to set.
values: Either a scalar value or a sequence of values.
'''
# Preconditions
if len(dref) == 0 or len(dref) > 255:
raise ValueError("dref must be a non-empty string less than 256 characters.")
if values == None:
raise ValueError("values must be a scalar or sequence of floats.")
self.sendDREFs([dref], [values])
def sendDREFs(self, drefs, values):
'''Sets the specified datarefs to the specified values.
Args:
drefs: A list of names of the datarefs to set.
values: A list of scalar or vector values to set.
'''
if len(drefs) != len(values):
raise ValueError("drefs and values must have the same number of elements.")
buffer = struct.pack("<4sx", "DREF")
for i in range(len(drefs)):
dref = drefs[i]
value = values[i]
# Preconditions
if len(dref) == 0 or len(dref) > 255:
raise ValueError("dref must be a non-empty string less than 256 characters.")
if value == None:
raise ValueError("value must be a scalar or sequence of floats.")
# Pack message
fmt = ""
buffer = None
if hasattr(values, "__len__"):
if len(values) > 255:
raise ValueError("values must have less than 256 items.")
fmt = "<4sxB{0:d}sB{1:d}f".format(len(dref), len(values))
buffer = struct.pack(fmt, "DREF", len(dref), dref, len(values), values)
else:
fmt = "<4sxB{0:d}sBf".format(len(dref))
buffer = struct.pack(fmt, "DREF", len(dref), dref, 1, values)
# Pack message
if hasattr(value, "__len__"):
if len(value) > 255:
raise ValueError("value must have less than 256 items.")
fmt = "<B{0:d}sB{1:d}f".format(len(dref), len(value))
buffer += struct.pack(fmt, len(dref), dref, len(value), value)
else:
fmt = "<B{0:d}sBf".format(len(dref))
buffer += struct.pack(fmt, len(dref), dref, 1, value)
# Send
self.sendUDP(buffer)
@@ -300,6 +371,23 @@ class XPlaneConnect(object):
buffer = struct.pack("<4sxiiB" + str(msgLen) + "s", "TEXT", x, y, msgLen, msg)
self.sendUDP(buffer)
def sendVIEW(self, view):
'''Sets the camera view in X-Plane
Args:
view: The view to use. The ViewType class provides named constants
for known views.
'''
# Preconditions
if view < ViewType.Forwards or view > ViewType.FullscreenNoHud:
raise ValueError("Unknown view command.")
# Pack buffer
buffer = struct.pack("<4sxi", "VIEW", view)
# Send message
self.sendUDP(buffer)
def sendWYPT(self, op, points):
'''Adds, removes, or clears waypoints. Waypoints are three dimensional points on or
above the Earth's surface that are represented visually in the simulator. Each
@@ -324,3 +412,18 @@ class XPlaneConnect(object):
else:
buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points)
self.sendUDP(buffer)
class ViewType(object):
Forwards = 73
Down = 74
Left = 75
Right = 76
Back = 77
Tower = 78
Runway = 79
Chase = 80
Follow = 81
FollowWithPanel = 82
Spot = 83
FullscreenWithHud = 84
FullscreenNoHud = 85

View File

@@ -5,7 +5,7 @@
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>3c7a940d-17c8-4e91-882f-9bc8b1d2f54b</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>src\example.py</StartupFile>
<StartupFile>src\basicExample.py</StartupFile>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
@@ -24,8 +24,14 @@
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="src\playbackExample.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\monitorExample.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\xpc.py" />
<Compile Include="src\example.py">
<Compile Include="src\basicExample.py">
<SubType>Code</SubType>
</Compile>
</ItemGroup>

View File

@@ -2,7 +2,7 @@
The X-Plane Connect (XPC) Toolbox is an open source research tool used to
interact with the commercial flight simulator software X-Plane. XPC allows users
to control aircraft and receive state information from aircraft simulated in
X-Plane using functions written in C, C++, Java, or MATLAB in real time over the
X-Plane using functions written in C, C++, Java, MATLAB, or Python in real time over the
network. This research tool has been used to visualize flight paths, test control
algorithms, simulate an active airspace, or generate out-the-window visuals for
in-house flight simulation software. Possible applications include active control

View File

@@ -21,9 +21,20 @@
<ItemGroup>
<ClCompile Include="..\..\C\src\xplaneConnect.c" />
<ClCompile Include="..\C Tests\main.c" />
<ClCompile Include="..\C Tests\Test.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h" />
<ClInclude Include="..\C Tests\CtrlTests.h" />
<ClInclude Include="..\C Tests\DataTests.h" />
<ClInclude Include="..\C Tests\DrefTests.h" />
<ClInclude Include="..\C Tests\PosiTests.h" />
<ClInclude Include="..\C Tests\SimuTests.h" />
<ClInclude Include="..\C Tests\Test.h" />
<ClInclude Include="..\C Tests\TextTests.h" />
<ClInclude Include="..\C Tests\UDPTests.h" />
<ClInclude Include="..\C Tests\ViewTests.h" />
<ClInclude Include="..\C Tests\WyptTests.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{BC701AF4-552C-4C9D-82A1-B352542783A4}</ProjectGuid>

View File

@@ -21,10 +21,40 @@
<ClCompile Include="..\..\C\src\xplaneConnect.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\C Tests\Test.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\UDPTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\DrefTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\TextTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\SimuTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\CtrlTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\DataTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\PosiTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\WyptTests.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\C Tests\Test.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */ = {isa = PBXBuildFile; fileRef = BE7CF62B1B0CFA34008B1E07 /* Test.c */; };
BEB0F5071A28F9A3001975A6 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F5061A28F9A3001975A6 /* main.c */; };
BEB0F5091A28F9A3001975A6 /* C_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = BEB0F5081A28F9A3001975A6 /* C_Tests.1 */; };
BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F50F1A28F9D5001975A6 /* xplaneConnect.c */; };
@@ -27,6 +28,17 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CtrlTests.h; sourceTree = "<group>"; };
BE7CF6271B0CFA34008B1E07 /* DataTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataTests.h; sourceTree = "<group>"; };
BE7CF6281B0CFA34008B1E07 /* DrefTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrefTests.h; sourceTree = "<group>"; };
BE7CF6291B0CFA34008B1E07 /* PosiTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PosiTests.h; sourceTree = "<group>"; };
BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimuTests.h; sourceTree = "<group>"; };
BE7CF62B1B0CFA34008B1E07 /* Test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Test.c; sourceTree = "<group>"; };
BE7CF62C1B0CFA34008B1E07 /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = "<group>"; };
BE7CF62D1B0CFA34008B1E07 /* TextTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTests.h; sourceTree = "<group>"; };
BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UDPTests.h; sourceTree = "<group>"; };
BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewTests.h; sourceTree = "<group>"; };
BE7CF6301B0CFA34008B1E07 /* WyptTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WyptTests.h; sourceTree = "<group>"; };
BEB0F5031A28F9A3001975A6 /* C Tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "C Tests"; sourceTree = BUILT_PRODUCTS_DIR; };
BEB0F5061A28F9A3001975A6 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
BEB0F5081A28F9A3001975A6 /* C_Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = C_Tests.1; sourceTree = "<group>"; };
@@ -66,6 +78,17 @@
BEB0F5051A28F9A3001975A6 /* C Tests */ = {
isa = PBXGroup;
children = (
BE7CF6261B0CFA34008B1E07 /* CtrlTests.h */,
BE7CF6271B0CFA34008B1E07 /* DataTests.h */,
BE7CF6281B0CFA34008B1E07 /* DrefTests.h */,
BE7CF6291B0CFA34008B1E07 /* PosiTests.h */,
BE7CF62A1B0CFA34008B1E07 /* SimuTests.h */,
BE7CF62B1B0CFA34008B1E07 /* Test.c */,
BE7CF62C1B0CFA34008B1E07 /* Test.h */,
BE7CF62D1B0CFA34008B1E07 /* TextTests.h */,
BE7CF62E1B0CFA34008B1E07 /* UDPTests.h */,
BE7CF62F1B0CFA34008B1E07 /* ViewTests.h */,
BE7CF6301B0CFA34008B1E07 /* WyptTests.h */,
BEB0F5061A28F9A3001975A6 /* main.c */,
BEB0F5081A28F9A3001975A6 /* C_Tests.1 */,
);
@@ -126,6 +149,7 @@
BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */,
BEB0F5121A28F9D5001975A6 /* xplaneConnect.h in Sources */,
BEB0F5071A28F9A3001975A6 /* main.c in Sources */,
BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,188 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef CTRLTESTS_H
#define CTRLTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int doCTRLTest(char* drefs[7], float values[], int size, int ac, float expected[7])
{
float* data[7];
int sizes[7];
for (int i = 0; i < 7; ++i)
{
data[i] = (float*)malloc(sizeof(float) * 16);
sizes[i] = 16;
}
// Execute command
XPCSocket sock = openUDP(IP);
int result = sendCTRL(sock, values, size, ac);
if (result >= 0)
{
result = getDREFs(sock, drefs, data, 7, sizes);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test values
float actual[7];
for (int i = 0; i < 7; ++i)
{
actual[i] = data[i][0];
}
return compareArray(expected, actual, 7);
}
int doGETCTest(float values[7], int ac, float expected[7])
{
// Execute Test
float actual[7];
XPCSocket sock = openUDP(IP);
int result = sendCTRL(sock, values, 7, ac);
if (result >= 0)
{
result = getCTRL(sock, actual, ac);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test values
for (int i = 0; i < 7; ++i)
{
if (fabs(expected[i] - actual[i]) > 1e-4)
{
return -10 - i;
}
}
return 0;
}
int basicCTRLTest(char** drefs, int ac)
{
// Set control surfaces to known state.
float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F };
float expected[7] = { 0.0F, 0.0F, 0.0F, NAN, NAN, NAN, NAN };
int result = doCTRLTest(drefs, CTRL, 3, ac, expected);
if (result < 0)
{
return -10000 + result;
}
// Test control surfaces and set other values to known state.
expected[0] = CTRL[0] = 0.2F;
expected[1] = CTRL[1] = 0.1F;
expected[2] = CTRL[2] = 0.1F;
expected[3] = 0.8F;
expected[4] = 1.0F;
expected[5] = 0.5F;
result = doCTRLTest(drefs, CTRL, 6, ac, expected);
if (result < 0)
{
return -20000 + result;
}
// Test other values and verify control surfaces unchanged.
CTRL[0] = -998;
CTRL[1] = -998;
CTRL[2] = -998;
expected[3] = CTRL[3] = 0.9F;
expected[4] = CTRL[4] = 0.0F;
expected[5] = CTRL[5] = 0.75F;
result = doCTRLTest(drefs, CTRL, 6, ac, expected);
if (result < 0)
{
return -30000 + result;
}
return 0;
}
int testCTRL_Player()
{
char* drefs[] =
{
"sim/cockpit2/controls/yoke_pitch_ratio",
"sim/cockpit2/controls/yoke_roll_ratio",
"sim/cockpit2/controls/yoke_heading_ratio",
"sim/flightmodel/engine/ENGN_thro",
"sim/cockpit/switches/gear_handle_status",
"sim/flightmodel/controls/flaprqst",
"sim/flightmodel/controls/sbrkrqst"
};
return basicCTRLTest(drefs, 0);
}
int testCTRL_NonPlayer()
{
char* drefs[] =
{
"sim/multiplayer/position/plane1_yolk_pitch",
"sim/multiplayer/position/plane1_yolk_roll",
"sim/multiplayer/position/plane1_yolk_yaw",
"sim/multiplayer/position/plane1_throttle",
"sim/multiplayer/position/plane1_gear_deploy",
"sim/multiplayer/position/plane1_flap_ratio",
"sim/multiplayer/position/plane1_sbrkrqst"
};
return basicCTRLTest(drefs, 1);
}
int testCTRL_Speedbrakes()
{
char* drefs[] =
{
"sim/cockpit2/controls/yoke_pitch_ratio",
"sim/cockpit2/controls/yoke_roll_ratio",
"sim/cockpit2/controls/yoke_heading_ratio",
"sim/flightmodel/engine/ENGN_thro",
"sim/cockpit/switches/gear_handle_status",
"sim/flightmodel/controls/flaprqst",
"sim/flightmodel/controls/sbrkrqst"
};
// Arm
float CTRL[7] = { -998, -998, -998, -998, -998, -998, -0.5F };
float expected[7] = { NAN, NAN, NAN, NAN, NAN, NAN, -0.5F };
int result = doCTRLTest(drefs, CTRL, 7, 0, expected);
if (result < 0)
{
return -10000 + result;
}
// Set to full
expected[6] = CTRL[6] = 1.5F;
result = doCTRLTest(drefs, CTRL, 7, 0, expected);
if (result < 0)
{
return -20000 + result;
}
// Retract
expected[6] = CTRL[6] = 0.0F;
result = doCTRLTest(drefs, CTRL, 7, 0, expected);
if (result < 0)
{
return -30000 + result;
}
return 0;
}
int testGETC()
{
float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F };
return doGETCTest(CTRL, 0, CTRL);
}
int testGETC_NonPlayer()
{
float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F };
return doGETCTest(CTRL, 2, CTRL);
}
#endif

View File

@@ -0,0 +1,62 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef DATATESTS_H
#define DATATESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int testDATA()
{
// Initialize
int i, j; // Iterator
char* drefs[100] =
{
"sim/aircraft/parts/acf_gear_deploy"
};
float* data[100]; // array for result of getDREFs
int sizes[100];
float DATA[4][9]; // Array for sendDATA
XPCSocket sock = openUDP(IP);
// Setup
for (int i = 0; i < 100; ++i)
{
data[i] = (float*)malloc(40 * sizeof(float));
sizes[i] = 40;
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < 9; j++)
{
data[i][j] = -998;
}
}
DATA[0][0] = 14; // Gear
DATA[0][1] = 1;
DATA[0][2] = 0;
// Execution
sendDATA(sock, DATA, 1);
int result = getDREFs(sock, drefs, data, 1, sizes);
// Close
closeUDP(sock);
// Tests
if (result < 0)// Request 1 value
{
return -1;
}
if (sizes[0] != 10)
{
return -2;
}
if (*data[0] != data[0][1])
{
return -3;
}
return 0;
}
#endif

View File

@@ -0,0 +1,247 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef DREFTESTS_H
#define DREFTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int doGETDTest(char* drefs[], float* expected[], int count, int sizes[])
{
// Setup memory
int* asizes = (int*)malloc(sizeof(int) * count);
float** actual = (float**)malloc(sizeof(float*) * count);
for (int i = 0; i < count; ++i)
{
asizes[i] = sizes[i];
actual[i] = (float*)malloc(sizeof(float) * sizes[i]);
}
// Execute command
XPCSocket sock = openUDP(IP);
int result = getDREFs(sock, drefs, actual, count, asizes);
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test sizes and values
result = compareArrays(expected, sizes, actual, asizes, count);
for (int i = 0; i < count; ++i)
{
free(actual[i]);
}
free(actual);
free(asizes);
return result;
}
int doDREFTest(char* drefs[], float* values[], float* expected[], int count, int sizes[])
{
// Setup memory
int* asizes = (int*)malloc(sizeof(int) * count);
float** actual = (float**)malloc(sizeof(float*) * count);
for (int i = 0; i < count; ++i)
{
asizes[i] = sizes[i];
actual[i] = (float*)malloc(sizeof(float) * sizes[i]);
}
// Execute command
XPCSocket sock = openUDP(IP);
int result = sendDREFs(sock, drefs, values, sizes, count);
if (result >= 0)
{
result = getDREFs(sock, drefs, actual, count, asizes);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test sizes and values
result = compareArrays(expected, sizes, actual, asizes, count);
for (int i = 0; i < count; ++i)
{
free(actual[i]);
}
free(actual);
free(asizes);
return result;
}
int testGETD_Basic()
{
char* drefs[] =
{
"sim/cockpit/switches/gear_handle_status", //int
"sim/cockpit/autopilot/altitude", //float
"sim/aircraft/prop/acf_prop_type", //int[8]
"sim/cockpit2/switches/panel_brightness_ratio", //float[4]
"sim/aircraft/view/acf_tailnum", //byte[40]
"sim/flightmodel/position/elevation" //double
};
int sizes[] = { 1, 1, 8, 4, 40, 1 };
float* expected[6];
for (int i = 0; i < 6; ++i)
{
expected[i] = (float*)malloc(sizeof(float) * sizes[i]);
for (int j = 0; j < sizes[i]; ++j)
{
expected[i][j] = NAN;
}
}
return doGETDTest(drefs, expected, 6, sizes);
}
int testGETD_TestFloat()
{
char* dref = "sim/test/test_float";
int size = 1;
float* expected[1];
expected[0] = (float*)malloc(sizeof(float));
expected[0][0] = 0.0F;
int result = doGETDTest(&dref, &expected, 1, &size);
free(expected[0]);
return result;
}
int testGETD_Types()
{
char* drefs[] =
{
"sim/cockpit/switches/gear_handle_status", //int
"sim/cockpit/autopilot/altitude", //float
"sim/aircraft/prop/acf_prop_type", //int[8]
"sim/cockpit2/switches/panel_brightness_ratio", //float[4]
"sim/aircraft/view/acf_tailnum", //byte[40]
"sim/flightmodel/position/elevation" //double
};
int sizes[] = { 1, 1, 8, 4, 40, 1 };
float* data[6];
for (int i = 0; i < 6; ++i)
{
data[i] = (float*)malloc(sizeof(float) * sizes[i]);
}
// Execute command
XPCSocket sock = openUDP(IP);
int result = getDREFs(sock, drefs, data, 6, sizes);
closeUDP(sock);
// Tests
if (result < 0)
{
return -1;
}
// Verify sizes
if (sizes[0] != 1 || sizes[1] != 1 || sizes[2] != 8
|| sizes[3] != 4 || sizes[4] != 40 || sizes[5] != 1)
{
return -2;
}
// Verify integer drefs are integers
if ((float)((int)data[0][0]) != data[0][0])
{
return -3;
}
for (int i = 0; i < 8; ++i)
{
if ((float)((int)data[2][i]) != data[2][i])
{
return -3;
}
}
for (int i = 0; i < 40; ++i)
{
if ((float)((char)data[4][i]) != data[4][i])
{
return -3;
}
}
// Verify tail number has at least one valid character
if (data[4][0] <= 0 || data[4][0] > 127)
{
return -4;
}
return 0;
}
int testDREF()
{
char* drefs[] =
{
"sim/cockpit/switches/gear_handle_status", //int
"sim/cockpit/autopilot/altitude", //float
"sim/aircraft/prop/acf_prop_type", //int[8]
"sim/cockpit2/switches/panel_brightness_ratio", //float[4]
"sim/aircraft/view/acf_tailnum", //byte[40]
"sim/flightmodel/position/elevation" //double - Read only
};
float* values[6];
float* expected[6];
int sizes[6];
// Setup
sizes[0] = 1;
values[0] = (float*)malloc(sizes[0] * sizeof(float));
expected[0] = values[0];
values[0][0] = 1;
sizes[1] = 1;
values[1] = (float*)malloc(sizes[1] * sizeof(float));
expected[1] = values[1];
values[1][0] = 4000.0F;
sizes[2] = 8;
values[2] = (float*)malloc(sizes[2] * sizeof(float));
expected[2] = values[2];
for (int i = 0; i < 8; ++i)
{
values[2][i] = 0;
}
sizes[3] = 4;
values[3] = (float*)malloc(sizes[3] * sizeof(float));
expected[3] = values[3];
for (int i = 0; i < 4; ++i)
{
values[3][i] = 0.25F;
}
sizes[4] = 40;
values[4] = (float*)malloc(sizes[4] * sizeof(float));
expected[4] = (float*)malloc(sizes[4] * sizeof(float));
memset(values[4], 0, sizes[4] * sizeof(float));
values[4][0] = 78.0F; //N
values[4][1] = 55.0F; //7
values[4][2] = 52.0F; //4
values[4][3] = 56.0F; //8
values[4][4] = 53.0F; //5
values[4][5] = 89.0F; //Y
expected[4][0] = 78.0F; //N
expected[4][1] = 55.0F; //7
expected[4][2] = 52.0F; //4
expected[4][3] = 56.0F; //8
expected[4][4] = 53.0F; //5
expected[4][5] = 89.0F; //Y
for (int i = 6; i < sizes[4]; ++i)
{
expected[4][i] = NAN;
}
sizes[5] = 1;
values[5] = (float*)malloc(sizes[5] * sizeof(float));
expected[5] = (float*)malloc(sizes[5] * sizeof(float));
values[5][0] = 5000.0F;
expected[5][0] = NAN;
return doDREFTest(drefs, values, expected, 6, sizes);
}
#endif

View File

@@ -0,0 +1,158 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef POSITESTS_H
#define POSITESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int doPOSITest(char* drefs[7], float values[], int size, int ac, float expected[7])
{
float* data[7];
int sizes[7];
for (int i = 0; i < 7; ++i)
{
data[i] = (float*)malloc(sizeof(float) * 10);
sizes[i] = 10;
}
// Execute command
XPCSocket sock = openUDP(IP);
pauseSim(sock, 1);
int result = sendPOSI(sock, values, size, ac);
if (result >= 0)
{
result = getDREFs(sock, drefs, data, 7, sizes);
}
pauseSim(sock, 0);
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test values
float actual[7];
for (int i = 0; i < 7; ++i)
{
actual[i] = data[i][0];
}
return compareArray(expected, actual, 7);
}
int doGETPTest(float values[7], int ac, float expected[7])
{
// Execute Test
float actual[7];
XPCSocket sock = openUDP(IP);
int result = sendPOSI(sock, values, 7, ac);
if (result >= 0)
{
result = getPOSI(sock, actual, ac);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
// Test values
for (int i = 0; i < 7; ++i)
{
if (fabs(expected[i] - actual[i]) > 1e-4)
{
return -10 - i;
}
}
return 0;
}
int basicPOSITest(char** drefs, int ac)
{
// Set psoition and initial orientation
float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
float expected[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
int result = doPOSITest(drefs, POSI, 7, ac, expected);
if (result < 0)
{
return -10000 + result;
}
// Set orientation
POSI[0] = -998.0F;
POSI[1] = -998.0F;
POSI[2] = -998.0F;
POSI[3] = 5.0F;
POSI[4] = -5.0F;
POSI[5] = 10.0F;
POSI[6] = 0;
float *loc[3];
for (int i = 0; i < 3; ++i)
{
loc[i] = (float*)malloc(sizeof(float));
}
int sizes[3] = { 1, 1, 1 };
XPCSocket sock = openUDP(IP);
pauseSim(sock, 1);
getDREFs(sock, drefs, loc, 3, sizes);
closeUDP(sock);
expected[0] = loc[0][0];
expected[1] = loc[1][0];
expected[2] = loc[2][0];
expected[3] = 5.0F;
expected[4] = -5.0F;
expected[5] = 10.0F;
expected[6] = 0.0F;
result = doPOSITest(drefs, POSI, 7, ac, expected);
if (result < 0)
{
return -20000 + result;
}
return 0;
}
int testPOSI_Player()
{
char* drefs[] =
{
"sim/flightmodel/position/latitude",
"sim/flightmodel/position/longitude",
"sim/flightmodel/position/elevation",
"sim/flightmodel/position/theta",
"sim/flightmodel/position/phi",
"sim/flightmodel/position/psi",
"sim/cockpit/switches/gear_handle_status"
};
return basicPOSITest(drefs, 0);
}
int testPOSI_NonPlayer()
{
char* drefs[] =
{
"sim/multiplayer/position/plane1_lat",
"sim/multiplayer/position/plane1_lon",
"sim/multiplayer/position/plane1_el",
"sim/multiplayer/position/plane1_the",
"sim/multiplayer/position/plane1_phi",
"sim/multiplayer/position/plane1_psi",
"sim/multiplayer/position/plane1_gear_deploy"
};
return basicPOSITest(drefs, 1);
}
int testGetPOSI_Player()
{
float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
return doGETPTest(POSI, 0, POSI);
}
int testGetPOSI_NonPlayer()
{
float POSI[7] = { 37.624F, -122.06899F, 1500, 0, 0, 0, 1 };
return doGETPTest(POSI, 3, POSI);
}
#endif

View File

@@ -0,0 +1,81 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef SIMUTESTS_H
#define SIMIUTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int doSIMUTest(int value, float expected)
{
int size = 20;
float actual[20];
char* dref = "sim/operation/override/override_planepath";
XPCSocket sock = openUDP(IP);
int result = pauseSim(sock, value);
if (result >= 0)
{
result = getDREF(sock, dref, &actual, &size);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
for (int i = 0; i < 20; ++i)
{
if (!feq(actual[i], expected) && !isnan(expected))
{
return -100 - i;
}
}
return 0;
}
int testSIMU_Basic()
{
int result = doSIMUTest(0, 0);
if (result < 0)
{
return -1;
}
result = doSIMUTest(1, 1);
if (result < 0)
{
return -2;
}
result = doSIMUTest(0, 0);
if (result < 0)
{
return -3;
}
return 0;
}
int testSIMU_Toggle()
{
int result = doSIMUTest(0, 0);
if (result < 0)
{
return -1;
}
result = doSIMUTest(2, 1);
if (result < 0)
{
return -1;
}
result = doSIMUTest(2, 0);
if (result < 0)
{
return -3;
}
return 0;
}
#endif

View File

@@ -0,0 +1,51 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#include "Test.h"
int testFailed = 0;
int testPassed = 0;
void runTest(int(*test)(), char* name)
{
printf("Running test %s... ", name);
int result = test(); // Run Test
if (result == 0)
{
printf("PASSED\n");
testPassed++;
}
else
{
printf("Test %s - FAILED\n\tError: %i\n", name, result);
testFailed++;
}
}
int compareFloat(float expected, float actual)
{
return feq(expected, actual) || isnan(expected) ? 0 : -1;
}
int compareArray(float expected[], float actual[], int size)
{
return compareArrays(&expected, &size, &actual, &size, 1);
}
int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[], int count)
{
for (int i = 0; i < count; ++i)
{
if (esizes[i] != asizes[i])
{
return -100 - i;
}
for (int j = 0; j < esizes[i]; ++j)
{
if (!feq(actual[i][j], expected[i][j]) && !isnan(expected[i][j]))
{
return -1000 - i * 100 - j;
}
}
}
return 0;
}

View File

@@ -0,0 +1,25 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef TESTRUNNER_H
#define TESTRUNNER_H
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#define feq(x, y) (fabs(x - y) < 1e-4)
#define IP "127.0.0.1"
extern int testFailed;
extern int testPassed;
void runTest(int(*test)(), char* name);
int compareFloat(float expected, float actual);
int compareArray(float expected[], float actual[], int size);
int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[], int count);
#endif

View File

@@ -0,0 +1,32 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef TEXTTESTS_H
#define TEXTTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int testTEXT()
{
// Setup
XPCSocket sendPort = openUDP(IP);
int x = 100;
int y = 700;
char* msg = "This is an X-Plane Connect test message.\nThis should be a new line.\r\nThat will be parsed as two line breaks.";
// Test
sendTEXT(sendPort, msg, x, y);
// NOTE: Manually verify that msg appears on the screen in X-Plane!
sendTEXT(sendPort, "Another test message", x, y);
// NOTE: Manually verify that msg appears on the screen and that no part of the previous
// message is visible.
sendTEXT(sendPort, NULL, -1, -1);
// Cleanup
closeUDP(sendPort);
return 0;
}
#endif

View File

@@ -0,0 +1,55 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef UDPTESTS_H
#define UDPTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int testOpen()
{
XPCSocket sock = openUDP("localhost");
int result = strncmp(sock.xpIP, "127.0.0.1", 16);
closeUDP(sock);
return result;
}
int testClose()
{
XPCSocket sendPort = aopenUDP(IP, 49009, 49063);
closeUDP(sendPort);
sendPort = aopenUDP(IP, 49009, 49063);
closeUDP(sendPort);
return 0;
}
int testCONN()
{
// Initialize
char* drefs[] =
{
"sim/cockpit/switches/gear_handle_status"
};
float data[1];
int size = 1;
XPCSocket sock = openUDP(IP);
#if (__APPLE__ || __linux)
usleep(0);
#endif
// Execution
setCONN(&sock, 49055);
int result = getDREF(sock, drefs[0], data, &size);
// Close
closeUDP(sock);
// Test
if (result < 0)// No data received
{
return -1;
}
return 0;
}
#endif

View File

@@ -0,0 +1,73 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef VIEWTESTS_H
#define VIEWTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
typedef enum
{
XPC_VDREF_FORWARDS = 1000,
XPC_VDREF_DOWN4 = 1001,
XPC_VDREF_DOWN8 = 1002,
XPC_VDREF_LEFT45 = 1004,
XPC_VDREF_RIGHT45 = 1005,
XPC_VDREF_LEFT90 = 1006,
XPC_VDREF_RIGHT90 = 1007,
XPC_VDREF_LEFT135 = 1008,
XPC_VDREF_RIGHT135 = 1009,
XPC_VDREF_BACKWARD = 1010,
XPC_VDREF_LEFTUP = 1011,
XPC_VDREF_RIGHTUP = 1012,
XPC_VDREF_AIRPORTBEACONTOWER = 1014,
XPC_VDREF_ONRUNWAY = 1015,
XPC_VDREF_CHASE = 1017,
XPC_VDREF_FOLLOW = 1018,
XPC_VDREF_FOLLOWWITHPANEL = 1019,
XPC_VDREF_SPOT = 1020,
XPC_VDREF_SPOTMOVING = 1021,
XPC_VDREF_FULLSCREENWITHHUD = 1023,
XPC_VDREF_FULLSCREENNOHUD = 1024,
XPC_VDREF_STRAIGHTDOWN = 1025,
XPC_VDREF_3DCOCKPIT = 1026
} VIEW_DREF;
int doViewTest(VIEW_TYPE viewCommand, VIEW_DREF viewResult)
{
// Setup
char* dref = "sim/graphics/view/view_type";
float value;
int size = 1;
// Execute command
XPCSocket sock = openUDP(IP);
int result = sendVIEW(sock, viewCommand);
if (result >= 0)
{
result = getDREF(sock, dref, &value, &size);
}
closeUDP(sock);
if (result < 0)
{
return -1;
}
if ((int)value != viewResult)
{
return -2;
}
return 0;
}
int testView()
{
int result = doViewTest(XPC_VIEW_FORWARDS, XPC_VDREF_FORWARDS);
if (result < 0)
{
return -1;
}
return doViewTest(XPC_VIEW_CHASE, XPC_VDREF_CHASE);
}
#endif

View File

@@ -0,0 +1,42 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef WYPTTESTS_H
#define WYPTTESTS_H
#include "Test.h"
#include "xplaneConnect.h"
int testWYPT()
{
// Setup
XPCSocket sock = openUDP(IP);
float points[] =
{
37.5245F, -122.06899F, 2500,
37.455397F, -122.050037F, 2500,
37.469567F, -122.051411F, 2500,
37.479376F, -122.060509F, 2300,
37.482237F, -122.076130F, 2100,
37.474881F, -122.087288F, 1900,
37.467660F, -122.079391F, 1700,
37.466298F, -122.090549F, 1500,
37.362562F, -122.039223F, 1000,
37.361448F, -122.034416F, 1000,
37.361994F, -122.026348F, 1000,
37.365541F, -122.022572F, 1000,
37.373727F, -122.024803F, 1000,
37.403869F, -122.041283F, 50,
37.418544F, -122.049222F, 6
};
// Test
sendWYPT(sock, XPC_WYPT_CLR, NULL, 0);
sendWYPT(sock, XPC_WYPT_ADD, points, 15);
// NOTE: Visually ensure waypoints are added in the sim
// Cleanup
closeUDP(sock);
return 0;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
package gov.nasa.xpc.test;
import gov.nasa.xpc.ViewType;
import gov.nasa.xpc.WaypointOp;
import gov.nasa.xpc.XPlaneConnect;
@@ -333,6 +334,33 @@ public class XPlaneConnectTest
}
}
@Test
public void testSendDREFs() throws IOException
{
String[] drefs =
{
"sim/cockpit/switches/gear_handle_status",
"sim/cockpit/autopilot/altitude"
};
try(XPlaneConnect xpc = new XPlaneConnect())
{
float[][] values = {{1}, {2000}};
xpc.sendDREFs(drefs, values);
float[][] result = xpc.getDREFs(drefs);
assertEquals(values[0][0], result[0][0], 1e-4);
assertEquals(values[1][0], result[1][0], 1e-4);
values[0][0] = 0;
values[1][0] = 4000;
xpc.sendDREFs(drefs, values);
result = xpc.getDREFs(drefs);
assertEquals(values[0][0], result[0][0], 1e-4);
assertEquals(values[1][0], result[1][0], 1e-4);
}
}
@Test(expected = IllegalArgumentException.class)
public void testSendDREF_NullDREF() throws IOException
{
@@ -650,4 +678,51 @@ public class XPlaneConnectTest
fail();
}
}
@Test
public void testSendView() throws IOException
{
String dref = "sim/graphics/view/view_type";
float fwd = 1000;
float chase = 1017;
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.sendVIEW(ViewType.Forwards);
float result = xpc.getDREF(dref)[0];
assertEquals(fwd, result, 1e-4);
xpc.sendVIEW(ViewType.Chase);
result = xpc.getDREF(dref)[0];
assertEquals(chase, result, 1e-4);
}
}
@Test
public void testGetPOSI() throws IOException
{
float[] values = { 37.524F, -122.06899F, 2500.0F, 45.0F, -45.0F, 15.0F, 1.0F };
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.pauseSim(true);
xpc.sendPOSI(values);
float[] actual = xpc.getPOSI(0);
assertArrayEquals(values, actual, 1e-4F);
}
}
@Test
public void testGetCTRL() throws IOException
{
float[] values = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F };
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.sendCTRL(values);
float[] actual = xpc.getCTRL(0);
assertArrayEquals(values, actual, 1e-4F);
}
}
}

View File

@@ -0,0 +1,15 @@
function getCTRLTest()
%GETCTRLTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
values = [10.0, 5.0, -5.0, 0.8, 1.0, 0.5, -1.5];
sendCTRL(values, 0);
actual = getCTRL(0);
assert(isequal(length(actual), length(values)));
for i = 1:length(actual)
assert(abs(actual(i) - values(i)) <1e-4)
end
end

View File

@@ -0,0 +1,17 @@
function getPOSITest()
%GETCTRLTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ];
pauseSim(1);
sendPOSI(values, 0);
actual = getPOSI(0);
pauseSim(0);
assert(isequal(length(actual), length(values)));
for i = 1:length(actual)
assert(abs(actual(i) - values(i)) <1e-4)
end
end

View File

@@ -1,4 +1,4 @@
function CTRLTest( )
function sendCTRLTest( )
%CTRLTest Summary of this function goes here
% Detailed explanation goes here
%% Test player aircraft

View File

@@ -1,4 +1,4 @@
function POSITest( )
function sendPOSITest( )
%POSITest Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')

View File

@@ -0,0 +1,26 @@
function sendVIEWTest()
%% Setup
addpath('../../MATLAB')
import XPlaneConnect.*
if ~exist('gov.nasa.xpc.ViewType', 'class')
[folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP'));
javaaddpath(fullfile(folder, 'XPlaneConnect.jar'));
end
dref = 'sim/graphics/view/view_type';
fwd = 1000;
chase = 1017;
%% Excecute
sendVIEW(gov.nasa.xpc.ViewType.Forwards);
result = getDREFs(dref);
assert(isequal(result, fwd))
sendVIEW(gov.nasa.xpc.ViewType.Chase);
result = getDREFs(dref);
assert(isequal(result, chase))
end

View File

@@ -1,3 +1,6 @@
addpath('../../MATLAB')
import XPlaneConnect.*
testsPassed=0;
testsFailed=0;
if ismac()
@@ -15,12 +18,16 @@ theTests = {{@openCloseTest, 'Open/Close Test', 0},...
{@getDREFsTest,'Request DREF Test', 0},...
{@sendDREFTest,'Send DREF Test', 0},...
{@DATATest,'DATA Test', 0},...
{@CTRLTest,'CTRL Test', 0},...
{@POSITest,'POSI Test', 0},...
{@sendCTRLTest,'sendCTRL Test', 0},...
{@getCTRLTest,'getCTRL Test', 0},...
{@sendPOSITest,'sendPOSI Test', 0},...
{@getPOSITest,'getPOSI Test', 0},...
{@sendWYPTTest,'WYPT Test', 0},...
{@sendVIEWTest,'VIEW Test', 0},...
{@pauseTest,'Pause Test', 0},...
{@setConnTest, 'setConn Test', 0}};
socket = openUDP();
for i=1:length(theTests)
fprintf(['Test ',num2str(i),': ',theTests{i}{2},' - ']);
try
@@ -35,6 +42,7 @@ for i=1:length(theTests)
testsFailed = testsFailed + 1;
end
end
closeUDP(socket);
disp('Results Summary:');
fprintf('Passed: %i\tFailed: %i\n',testsPassed, testsFailed);

View File

@@ -3,7 +3,7 @@ import unittest
import imp
import time
import xpc
xpc = imp.load_source('xpc', '../../Python/src/xpc.py')
class XPCTests(unittest.TestCase):
"""Tests the functionality of the XPlaneConnect class."""
@@ -117,6 +117,37 @@ class XPCTests(unittest.TestCase):
value = 0
do_test()
def test_sendDREFs(self):
drefs = [\
"sim/cockpit/switches/gear_handle_status",\
"sim/cockpit/autopilot/altitude"]
values = None
def do_test():
# Setup
client = xpc.XPlaneConnect()
# Execute
client.sendDREFs(drefs, values)
result = client.getDREFs(drefs)
# Cleanup
client.close()
# Tests
self.assertEqual(2, len(result))
self.assertEqual(1, len(result[0]))
self.assertEqual(values[0], result[0][0])
self.assertEqual(1, len(result[1]))
self.assertEqual(values[1], result[1][0])
# Test 1
values = [1, 2000]
do_test()
# Test 2
values = [0, 4000]
do_test()
def test_sendDATA(self):
# Setup
dref = "sim/aircraft/parts/acf_gear_deploy"
@@ -171,6 +202,29 @@ class XPCTests(unittest.TestCase):
expected = 0.0
do_test()
def test_getCTRL(self):
values = None
ac = 0
expected = None
def do_test():
with xpc.XPlaneConnect() as client:
# Execute
client.sendCTRL(values, ac)
result = client.getCTRL(ac)
# Test
self.assertEqual(len(result), len(expected))
for a, e in zip(result, expected):
self.assertAlmostEqual(a, e, 4)
values = [0.0, 0.0, 0.0, 0.8, 1.0, 0.5, -1.5]
expected = values
ac = 0
do_test()
ac = 3
do_test()
def test_sendCTRL(self):
# Setup
@@ -239,6 +293,31 @@ class XPCTests(unittest.TestCase):
ctrl[6] = 0.0
do_test()
def test_getPOSI(self):
values = None
ac = 0
expected = None
def do_test():
with xpc.XPlaneConnect() as client:
# Execute
client.pauseSim(True)
client.sendPOSI(values, ac)
result = client.getPOSI(ac)
client.pauseSim(False)
# Test
self.assertEqual(len(result), len(expected))
for a, e in zip(result, expected):
self.assertAlmostEqual(a, e, 4)
values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ]
expected = values
ac = 0
do_test()
ac = 3
do_test()
def test_sendPOSI(self):
# Setup
drefs = ["sim/flightmodel/position/latitude",\
@@ -288,6 +367,22 @@ class XPCTests(unittest.TestCase):
# Cleanup
client.close()
def test_sendView(self):
# Setup
dref = "sim/graphics/view/view_type"
fwd = 1000
chase = 1017
#Execution
with xpc.XPlaneConnect() as client:
client.sendVIEW(xpc.ViewType.Forwards)
result = client.getDREF(dref)
self.assertAlmostEqual(fwd, result[0], 1e-4)
client.sendVIEW(xpc.ViewType.Chase)
result = client.getDREF(dref)
self.assertAlmostEqual(chase, result[0], 1e-4)
def test_sendWYPT(self):
# Setup
client = xpc.XPlaneConnect()

View File

@@ -13,7 +13,6 @@ SET(CMAKE_CXX_COMPILER g++)
add_library(xpc64 SHARED XPCPlugin.cpp
DataManager.cpp
DataMaps.cpp
Drawing.cpp
Log.cpp
Message.cpp
@@ -24,7 +23,6 @@ set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-shared
add_library(xpc32 SHARED XPCPlugin.cpp
DataManager.cpp
DataMaps.cpp
Drawing.cpp
Log.cpp
Message.cpp

View File

@@ -1,25 +1,26 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
//X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
//associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute,
//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
// X-Plane API
// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files(the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
#include "DataManager.h"
#include "Log.h"
#include "XPLMDataAccess.h"
#include "XPLMGraphics.h"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
@@ -28,14 +29,17 @@ namespace XPC
{
using namespace std;
const size_t PLANE_COUNT = 20;
static map<DREF, XPLMDataRef> drefs;
static map<DREF, XPLMDataRef> mdrefs[20];
static map<DREF, XPLMDataRef> mdrefs[PLANE_COUNT];
static map<string, XPLMDataRef> sdrefs;
DREF XPData[134][8] = { DREF_None };
void DataManager::Initialize()
{
Log::WriteLine(LOG_TRACE, "DMAN", "Initializing drefs");
drefs.insert(make_pair(DREF_None, XPLMFindDataRef("sim/test/test_float")));
drefs.insert(make_pair(DREF_Pause, XPLMFindDataRef("sim/operation/override/override_planepath")));
@@ -142,7 +146,7 @@ namespace XPC
drefs.insert(make_pair(DREF_MP7Alt, XPLMFindDataRef("sim/multiplayer/position/plane7_el")));
char multi[256];
for (int i = 1; i < 20; i++)
for (int i = 1; i < PLANE_COUNT; i++)
{
sprintf(multi, "sim/multiplayer/position/plane%i_x", i);
mdrefs[i][DREF_LocalX] = XPLMFindDataRef(multi);
@@ -165,8 +169,8 @@ namespace XPC
sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i);
mdrefs[i][DREF_GearDeploy] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio", i);
mdrefs[i][DREF_FlapSetting] = XPLMFindDataRef(multi); // Can't set the actual flap setting on npc aircraft
mdrefs[i][DREF_FlapActual] = XPLMFindDataRef(multi);
mdrefs[i][DREF_FlapSetting] = mdrefs[i][DREF_FlapActual]; // Can't set the actual flap setting on npc aircraft
sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio2", i);
mdrefs[i][DREF_FlapActual2] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i);
@@ -179,6 +183,7 @@ namespace XPC
mdrefs[i][DREF_Sweep] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i);
mdrefs[i][DREF_ThrottleActual] = XPLMFindDataRef(multi);
mdrefs[i][DREF_ThrottleSet] = mdrefs[i][DREF_ThrottleActual]; // No throttle set for multiplayer planes.
sprintf(multi, "sim/multiplayer/position/plane%i_yolk_pitch", i);
mdrefs[i][DREF_YokePitch] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i);
@@ -299,8 +304,9 @@ namespace XPC
XPData[26][0] = DREF_ThrottleActual;
}
int DataManager::Get(string dref, float values[], int size)
int DataManager::Get(const string& dref, float values[], int size)
{
Log::WriteLine(LOG_TRACE, "DMAN", "Entered Get(string, float*, int)");
XPLMDataRef& xdref = sdrefs[dref];
if (xdref == NULL)
{
@@ -308,24 +314,18 @@ namespace XPC
}
if (!xdref) // DREF does not exist
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str());
#endif
Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str());
return 0;
}
XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
// XPLMDataTypeID is a bit flag, so it may contain more than one of the
// following types. We prefer types as close to float as possible.
if ((dataType & 2) == 2) // Float
{
values[0] = XPLMGetDataf(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
return 1;
}
if ((dataType & 8) == 8) // Float array
@@ -333,99 +333,80 @@ namespace XPC
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0);
if (drefSize > size)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: dref size is larger than available space");
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
drefSize = size;
}
XPLMGetDatavf(xdref, values, 0, drefSize);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
return drefSize;
}
if ((dataType & 4) == 4) // Double
{
values[0] = (float)XPLMGetDatad(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
return 1;
}
if ((dataType & 1) == 1) // Integer
{
values[0] = (float)XPLMGetDatai(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %i", (int)values[0]);
#endif
int iValue = XPLMGetDatai(xdref);
values[0] = (float)iValue;
Log::FormatLine(LOG_INFO, "DMAN", " -- Real value was %i, cast to %f", iValue, values[0]);
return 1;
}
if ((dataType & 16) == 16) // Integer array
{
int iValues[200];
const std::size_t TMP_SIZE = 200;
int iValues[TMP_SIZE];
int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0);
if (drefSize > size)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: dref size is larger than available space");
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
drefSize = size;
}
if (drefSize > 200)
if (drefSize > TMP_SIZE)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer");
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize);
#endif
drefSize = 200;
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE);
drefSize = TMP_SIZE;
}
XPLMGetDatavi(xdref, iValues, 0, drefSize);
for (int i = 0; i < drefSize; ++i)
{
values[i] = (float)iValues[i];
}
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
return drefSize;
}
if ((dataType & 32) == 32) // Byte array
{
char bValues[1024];
const std::size_t TMP_SIZE = 1024;
char bValues[TMP_SIZE];
int drefSize = XPLMGetDatab(xdref, NULL, 0, 0);
if (drefSize > size)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: dref size is larger than available space");
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
drefSize = size;
}
if (drefSize > 1024)
if (drefSize > TMP_SIZE)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer");
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize);
#endif
drefSize = 1024;
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE);
drefSize = TMP_SIZE;
}
XPLMGetDatab(xdref, bValues, 0, drefSize);
for (int i = 0; i < drefSize; ++i)
{
values[i] = (float)bValues[i];
}
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
return drefSize;
}
// No match
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Unrecognized data type.");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unrecognized data type.");
return 0;
}
@@ -433,9 +414,8 @@ namespace XPC
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
double value = XPLMGetDatad(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i",
dref, xdref, value, aircraft);
return value;
}
@@ -443,9 +423,8 @@ namespace XPC
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
float value = XPLMGetDataf(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i",
dref, xdref, value, aircraft);
return value;
}
@@ -453,9 +432,8 @@ namespace XPC
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int value = XPLMGetDatai(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %i for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %i for a/c %i",
dref, xdref, value, aircraft);
return value;
}
@@ -463,9 +441,8 @@ namespace XPC
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int resultSize = XPLMGetDatavf(xdref, values, 0, size);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i",
dref, xdref, resultSize, aircraft);
return resultSize;
}
@@ -473,62 +450,66 @@ namespace XPC
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int resultSize = XPLMGetDatavi(xdref, values, 0, size);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i",
dref, xdref, resultSize, aircraft);
return resultSize;
}
void DataManager::Set(DREF dref, double value, char aircraft)
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Setting DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i",
dref, xdref, value, aircraft);
XPLMSetDatad(xdref, value);
}
void DataManager::Set(DREF dref, float value, char aircraft)
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i",
dref, xdref, value, aircraft);
XPLMSetDataf(xdref, value);
}
void DataManager::Set(DREF dref, int value, char aircraft)
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %i for a/c %i", dref, xdref, value, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %i for a/c %i",
dref, xdref, value, aircraft);
XPLMSetDatai(xdref, value);
}
void DataManager::Set(DREF dref, float values[], int size, char aircraft)
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Set DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i",
dref, xdref, size, aircraft);
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0);
if (drefSize < size)
{
Log::FormatLine(LOG_WARN, "DMAN", "Warning: Too many values when setting DREF %i. Expected %i, got %i",
dref, drefSize, size);
}
drefSize = min(drefSize, size);
XPLMSetDatavf(xdref, values, 0, drefSize);
}
void DataManager::Set(DREF dref, int values[], int size, char aircraft)
{
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Setting DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i",
dref, xdref, size, aircraft);
int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0);
if (drefSize < size)
{
Log::FormatLine(LOG_WARN, "DMAN", "Warning: Too many values when setting DREF %i. Expected %i, got %i",
dref, drefSize, size);
}
drefSize = min(drefSize, size);
XPLMSetDatavi(xdref, values, 0, drefSize);
}
void DataManager::Set(string dref, float values[], int size)
void DataManager::Set(const string& dref, float values[], int size)
{
XPLMDataRef& xdref = sdrefs[dref];
if (xdref == NULL)
@@ -538,149 +519,121 @@ namespace XPC
if (!xdref)
{
// DREF does not exist
#if LOG_VERBOSITY > 0
Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str());
#endif
Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str());
return;
}
if (isnan(values[0]))
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Value must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Value must be a number (NaN received)");
return;
}
XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
if ((dataType & 2) == 2) // Float
{
XPLMSetDataf(xdref, values[0]);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
}
else if ((dataType & 8) == 8) // Float Array
{
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize)
{
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size);
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
}
#endif
drefSize = min(drefSize, size);
XPLMSetDatavf(xdref, values, 0, drefSize);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
}
else if ((dataType & 4) == 4) // Double
{
XPLMSetDatad(xdref, values[0]);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
}
else if ((dataType & 1) == 1) // Integer
{
XPLMSetDatai(xdref, (int)values[0]);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value was %i", (int)values[0]);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value was %i", (int)values[0]);
}
else if ((dataType & 16) == 16) // Integer Array
{
int iValues[200];
const std::size_t TMP_SIZE = 200;
int iValues[TMP_SIZE];
int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize)
{
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size);
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
}
#endif
drefSize = min(drefSize, size);
if (drefSize > 200)
if (drefSize > TMP_SIZE)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size.");
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize);
#endif
drefSize = 200;
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i",
drefSize, TMP_SIZE);
drefSize = TMP_SIZE;
}
for (int i = 0; i < drefSize; ++i)
{
iValues[i] = (int)values[i];
}
XPLMSetDatavi(xdref, iValues, 0, drefSize);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
}
else if ((dataType & 32) == 32) // Byte Array
{
char bValues[1024];
const std::size_t TMP_SIZE = 1024;
char bValues[TMP_SIZE];
int drefSize = XPLMGetDatab(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize)
{
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size);
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
}
#endif
drefSize = min(drefSize, size);
if (drefSize > 1024)
if (drefSize > TMP_SIZE)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size.");
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize);
#endif
drefSize = 1024;
Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size");
Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i",
drefSize, TMP_SIZE);
drefSize = TMP_SIZE;
}
for (int i = 0; i < drefSize; ++i)
{
bValues[i] = (char)values[i];
}
XPLMSetDatab(xdref, bValues, 0, drefSize);
#if LOG_VERBOSITY > 4
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
}
else
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Unknown type.");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unknown type.");
}
#if LOG_VERBOSITY > 1
if (!XPLMCanWriteDataRef(xdref))
{
Log::WriteLine("[DMAN] WARN: dref is not writable. The write operation probably failed.");
Log::WriteLine(LOG_WARN, "DMAN", "WARN: dref is not writable. The write operation probably failed.");
}
#endif
}
void DataManager::SetGear(float gear, bool immediate, char aircraft)
{
#if LOG_VERBOSITY > 3
Log::FormatLine("[DMAN] Setting gear (value:%f, immediate:%i) for aircraft %i", gear, immediate, aircraft);
#endif
if (isnan(gear) || gear < 0 || gear > 1)
Log::FormatLine(LOG_INFO, "DMAN", "Setting gear (value:%f, immediate:%i) for aircraft %i",
gear, immediate, aircraft);
if ((gear < -8.5 && gear > -9.5) || IsDefault(gear))
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[GEAR] ERROR: Value must be 0 or 1");
#endif
return;
}
if ((gear < -8.5 && gear > -9.5) || (gear < -997.9 && gear > -999.1))
if (isnan(gear) || gear < 0 || gear > 1)
{
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Gear value must be 0 or 1");
return;
}
@@ -706,26 +659,23 @@ namespace XPC
void DataManager::SetPosition(float pos[3], char aircraft)
{
#if LOG_VERBOSITY > 3
Log::FormatLine("[DMAN] Setting position (%f, %f, %f) for aircraft %i", pos[0], pos[1], pos[2], aircraft);
#endif
Log::FormatLine(LOG_INFO, "DMAN", "Setting position (%f, %f, %f) for aircraft %i",
pos[0], pos[1], pos[2], aircraft);
if (isnan(pos[0] + pos[1] + pos[2]))
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Position must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Position must be a number (NaN received)");
return;
}
if (pos[0] < -997.9 && pos[0] > -999.1)
if (IsDefault(pos[0]))
{
pos[0] = (float)GetDouble(DREF_Latitude, aircraft);
}
if (pos[1] < -997.9 && pos[1] > -999.1)
if (IsDefault(pos[1]))
{
pos[1] = (float)GetDouble(DREF_Longitude, aircraft);
}
if (pos[2] < -997.9 && pos[2] > -999.1)
if (IsDefault(pos[2]))
{
pos[2] = (float)GetDouble(DREF_Elevation, aircraft);
}
@@ -738,7 +688,6 @@ namespace XPC
Set(DREF_LocalY, local[1], aircraft);
Set(DREF_LocalZ, local[2], aircraft);
// If the sim is unpaused, this will override the above settings.
// TODO: Are these setable when paused? Are these necessary?
Set(DREF_Latitude, (double)pos[0], aircraft);
Set(DREF_Longitude, (double)pos[1], aircraft);
Set(DREF_Elevation, (double)pos[2], aircraft);
@@ -746,27 +695,23 @@ namespace XPC
void DataManager::SetOrientation(float orient[3], char aircraft)
{
#if LOG_VERBOSITY > 3
Log::FormatLine("[DMAN] Setting orientation (%f, %f, %f) for aircraft %i",
Log::FormatLine(LOG_INFO, "DMAN", "Setting orientation (%f, %f, %f) for aircraft %i",
orient[0], orient[1], orient[2], aircraft);
#endif
if (isnan(orient[0] + orient[1] + orient[2]))
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Orientation must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Orientation must be a number (NaN received)");
return;
}
if (orient[0] < -997.9 && orient[0] > -999.1)
if (IsDefault(orient[0]))
{
orient[0] = GetFloat(DREF_Pitch, aircraft);
}
if (orient[1] < -997.9 && orient[1] > -999.1)
if (IsDefault(orient[1]))
{
orient[1] = GetFloat(DREF_Roll, aircraft);
}
if (orient[2] < -997.9 && orient[2] > -999.1)
if (IsDefault(orient[2]))
{
orient[2] = GetFloat(DREF_HeadingTrue, aircraft);
}
@@ -782,16 +727,16 @@ namespace XPC
{
// Convert to Quaternions
// from: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane
// http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf
// http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf
float q[4];
float halfRad = 0.00872664625997F;
orient[2] = halfRad * orient[2];
orient[0] = halfRad * orient[0];
orient[1] = halfRad * orient[1];
q[0] = cos(orient[2]) * cos(orient[0]) * cos(orient[1]) + sin(orient[2]) * sin(orient[0]) * sin(orient[1]);
q[1] = cos(orient[2]) * cos(orient[0]) * sin(orient[1]) - sin(orient[2]) * sin(orient[0]) * cos(orient[1]);
q[2] = cos(orient[2]) * sin(orient[0]) * cos(orient[1]) + sin(orient[2]) * cos(orient[0]) * sin(orient[1]);
q[3] = sin(orient[2]) * cos(orient[0]) * cos(orient[1]) - cos(orient[2]) * sin(orient[0]) * sin(orient[1]);
float theta = halfRad * orient[0];
float phi = halfRad * orient[1];
float psi = halfRad * orient[2];
q[0] = cos(phi) * cos(theta) * cos(psi) + sin(phi) * sin(theta) * sin(psi);
q[1] = sin(phi) * cos(theta) * cos(psi) - cos(phi) * sin(theta) * sin(psi);
q[2] = cos(phi) * sin(theta) * cos(psi) + sin(phi) * cos(theta) * sin(psi);
q[3] = cos(phi) * cos(theta) * sin(psi) - sin(phi) * sin(theta) * cos(psi);
// If the sim is un-paused, this will overwrite the pitch/roll/yaw
// values set above.
@@ -801,14 +746,14 @@ namespace XPC
void DataManager::SetFlaps(float value)
{
Log::FormatLine(LOG_INFO, "DMAN", "Setting flaps (value:%f)", value);
if (isnan(value))
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DMAN] ERROR: Flap value must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Flap value must be a number (NaN received)");
return;
}
if (value < -997.9 && value > -999.1)
if (IsDefault(value))
{
return;
}
@@ -819,4 +764,14 @@ namespace XPC
Set(DREF_FlapSetting, value);
Set(DREF_FlapActual, value);
}
float DataManager::GetDefaultValue()
{
return -998.0F;
}
bool DataManager::IsDefault(float value)
{
return value < -997.9 && value > -999.1;
}
}

View File

@@ -1,7 +1,7 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_DATAMANAGER_H
#define XPC_DATAMANAGER_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_DATAMANAGER_H_
#define XPCPLUGIN_DATAMANAGER_H_
#include <string>
@@ -62,7 +62,7 @@ namespace XPC
DREF_L,
DREF_N,
//PQR (Angular Velocities)
// PQR (Angular Velocities)
DREF_QRad = 1600,
DREF_PRad,
DREF_RRad,
@@ -139,13 +139,13 @@ namespace XPC
/// Maps X-Plane dataref lines to XPC DREF values.
extern DREF XPData[134][8];
/// Marshals data between the plugin and X-Plane.
/// Contains methods to martial data between the plugin and X-Plane.
///
/// \author Jason Watkins
/// \version 1.0.1
/// \version 1.1
/// \since 1.0.0
/// \date Intial Version: 2015-04-13
/// \date Last Updated: 2015-04-29
/// \date Last Updated: 2015-05-14
class DataManager
{
public:
@@ -165,7 +165,7 @@ namespace XPC
/// \remarks The first time this method is called for a given dataref, it must
/// perform a relatively expensive lookup operation to translate the
/// given string into an X-Plane internal pointer. This value is cached,
/// so subsequent calls will incure minimal extra overhead compared to
/// so subsequent calls will incur minimal extra overhead compared to
/// the other methods in this class.
///
/// \remarks For simplicity, this method is provided with only one output type.
@@ -174,7 +174,7 @@ namespace XPC
/// doubles where high precision is required, using this method may result
/// in a loss of precision. In that case, consider using one of the
/// strongly typed methods instead.
static int Get(std::string dref, float values[], int size);
static int Get(const std::string& dref, float values[], int size);
/// Gets the value of a double dataref.
///
@@ -294,7 +294,7 @@ namespace XPC
/// doubles where high precision is required, using this method may result
/// in a loss of precision. In that case, consider using one of the
/// strongly typed methods instead.
static void Set(std::string dref, float values[], int size);
static void Set(const std::string& dref, float values[], int size);
/// Sets the value of a double dataref.
///
@@ -409,6 +409,15 @@ namespace XPC
///
/// \param value The flaps settings. Should be between 0.0 (no flaps) and 1.0 (full flaps).
static void SetFlaps(float value);
/// Gets a default value that indicates that a dataref should not be changed.
static float GetDefaultValue();
/// Checks whether the given value should be treated as a default value.
///
/// \param value The value to check.
/// \returns true if value is a default value; otherwise false.
static bool IsDefault(float value);
};
}
#endif

View File

@@ -1,19 +1,19 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
//X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
//associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute,
//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
// X-Plane API
// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files(the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
#include "Drawing.h"
#include "XPLMDisplay.h"
@@ -23,7 +23,7 @@
#include <cmath>
#include <string>
#include <cstring>
//OpenGL includes
// OpenGL includes
#if IBM
#include <windows.h>
#endif
@@ -31,11 +31,11 @@
# include <OpenGL/gl.h>
#else
# include <GL/gl.h>
#endif/*__APPLE__*/
#endif
namespace XPC
{
//Internal Structures
// Internal Structures
typedef struct
{
double x;
@@ -43,7 +43,7 @@ namespace XPC
double z;
} LocalPoint;
//Internal Memory
// Internal Memory
static const size_t MSG_MAX = 1024;
static const size_t MSG_LINE_MAX = MSG_MAX / 16;
static bool msgEnabled = false;
@@ -64,7 +64,9 @@ namespace XPC
XPLMDataRef planeYref;
XPLMDataRef planeZref;
//Internal Functions
// Internal Functions
/// Comparse two size_t integers. Used by qsort in RemoveWaypoints.
static int cmp(const void * a, const void * b)
{
std::size_t sa = *(size_t*)a;
@@ -80,36 +82,42 @@ namespace XPC
return 0;
}
/// Draws a cube centered at the specified OpenGL world coordinates.
///
/// \param x The X coordinate.
/// \param y The Y coordinate.
/// \param z The Z coordinate.
/// \param d The distance from the player airplane to the center of the cube.
static void gl_drawCube(float x, float y, float z, float d)
{
//tan(0.25) degrees. Should scale all markers to appear about the same size
// tan(0.25) degrees. Should scale all markers to appear about the same size
const float TAN = 0.00436335F;
float h = d * TAN;
glBegin(GL_QUAD_STRIP);
//Top
// Top
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x + h, y + h, z + h);
//Front
// Front
glVertex3f(x - h, y - h, z + h);
glVertex3f(x + h, y - h, z + h);
//Bottom
// Bottom
glVertex3f(x - h, y - h, z - h);
glVertex3f(x + h, y - h, z - h);
//Back
// Back
glVertex3f(x - h, y + h, z - h);
glVertex3f(x + h, y + h, z - h);
glEnd();
glBegin(GL_QUADS);
//Left
// Left
glVertex3f(x - h, y + h, z - h);
glVertex3f(x - h, y + h, z + h);
glVertex3f(x - h, y - h, z + h);
glVertex3f(x - h, y - h, z - h);
//Right
// Right
glVertex3f(x + h, y + h, z + h);
glVertex3f(x + h, y + h, z - h);
glVertex3f(x + h, y - h, z - h);
@@ -118,25 +126,28 @@ namespace XPC
glEnd();
}
/// Draws the string set by the TEXT command.
static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
const int LINE_HEIGHT = 16;
XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic);
int y = msgY - 16;
int y = msgY - LINE_HEIGHT;
for (size_t i = 0; i < newLineCount; ++i)
{
XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic);
y -= 16;
y -= LINE_HEIGHT;
}
return 1;
}
/// Draws waypoints.
static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{
float px = XPLMGetDataf(planeXref);
float py = XPLMGetDataf(planeYref);
float pz = XPLMGetDataf(planeZref);
//Convert to local
// Convert to local
for (size_t i = 0; i < numWaypoints; ++i)
{
Waypoint* g = &waypoints[i];
@@ -146,7 +157,7 @@ namespace XPC
}
//Draw posts
// Draw posts
glColor3f(1.0F, 1.0F, 1.0F);
glBegin(GL_LINES);
for (size_t i = 0; i < numWaypoints; ++i)
@@ -157,7 +168,7 @@ namespace XPC
}
glEnd();
//Draw route
// Draw route
glColor3f(1.0F, 0.0F, 0.0F);
glBegin(GL_LINE_STRIP);
for (size_t i = 0; i < numWaypoints; ++i)
@@ -167,7 +178,7 @@ namespace XPC
}
glEnd();
//Draw markers
// Draw markers
glColor3f(1.0F, 1.0F, 1.0F);
for (size_t i = 0; i < numWaypoints; ++i)
{
@@ -181,7 +192,7 @@ namespace XPC
return 1;
}
//Public Functions
// Public Functions
void Drawing::ClearMessage()
{
XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
@@ -190,8 +201,7 @@ namespace XPC
void Drawing::SetMessage(int x, int y, char* msg)
{
//Determine size of message and clear instead if the message string
//is empty.
// Determine the size of the message and clear it if it is empty.
size_t len = strnlen(msg, MSG_MAX - 1);
if (len == 0)
{
@@ -199,7 +209,7 @@ namespace XPC
return;
}
//Set the message, location, and mark new lines.
// Set the message, location, and mark new lines.
strncpy(msgVal, msg, len + 1);
newLineCount = 0;
for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i)
@@ -213,7 +223,7 @@ namespace XPC
msgX = x < 0 ? 10 : x;
msgY = y < 0 ? 600 : y;
//Enable drawing if necessary
// Enable drawing if necessary
if (!msgEnabled)
{
XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
@@ -258,7 +268,7 @@ namespace XPC
void Drawing::RemoveWaypoints(Waypoint points[], size_t numPoints)
{
//Build a list of indices of waypoints we should delete.
// Build a list of indices of waypoints we should delete.
size_t delPoints[WAYPOINT_MAX];
size_t delPointsCur = 0;
for (size_t i = 0; i < numPoints; ++i)
@@ -276,10 +286,10 @@ namespace XPC
}
}
}
//Sort the indices so that we only have to iterate them once
// Sort the indices so that we only have to iterate them once
qsort(delPoints, delPointsCur, sizeof(size_t), cmp);
//Copy the new array on top of the old array
// Copy the new array on top of the old array
size_t copyCur = 0;
size_t count = delPointsCur;
delPointsCur = 0;

View File

@@ -1,7 +1,7 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_DRAWING_H
#define XPC_DRAWING_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_DRAWING_H_
#define XPCPLUGIN_DRAWING_H_
#include <cstdlib>

View File

@@ -1,17 +1,20 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#include "Log.h"
#include "XPLMUtilities.h"
#include <chrono>
#include <cstdarg>
#include <cstdio>
#include <ctime>
#ifndef LIN
#include <chrono>
#endif
#include <iomanip>
#include <sstream>
// Implementation note: I initial wrote this class using C++ iostreams, but I couldn't find any
// Implementation note: I initially wrote this class using C++ iostreams, but I couldn't find any
// way to implement FormatLine without adding in a call to sprintf. It therefore seems more
// efficient to me to just use C-style IO and call std::fprintf directly.
namespace XPC
@@ -19,6 +22,20 @@ namespace XPC
static std::FILE* fd;
static void WriteTime(FILE* fd)
{
#ifdef LIN
// Can't provide high resolution logging on Linux because C++11 doesn't work with X-Plane.
time_t rawtime;
tm* timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
char buffer[16] = { 0 };
// Format is equivalent to [%F %T], but neither of those specifiers is
// supported on Windows as of Visual Studio 13
strftime(buffer, 16, "[%H:%M:%S] ", timeinfo);
fprintf(fd, buffer);
#else
using namespace std::chrono;
system_clock::time_point now = system_clock::now();
@@ -28,17 +45,59 @@ namespace XPC
std::tm * tm = std::localtime(&now_tt);
std::stringstream ss;
ss << std::setfill('0') << "["
ss << std::setfill('0')
<< std::setw(2) << tm->tm_hour << ":"
<< std::setw(2) << tm->tm_min << ":"
<< std::setw(2) << tm->tm_sec << "."
<< std::setw(3) << ms.count() << "]";
<< std::setw(3) << ms.count() << "|";
std::fprintf(fd, ss.str().c_str());
#endif
}
void Log::Initialize(std::string version)
static void WriteLevel(FILE* fd, int level)
{
const char* str;
switch (level)
{
case LOG_OFF:
str = " OFF|";
break;
case LOG_FATAL:
str = "FATAL|";
break;
case LOG_ERROR:
str = "ERROR|";
break;
case LOG_WARN:
str = " WARN|";
break;
case LOG_INFO:
str = " INFO|";
break;
case LOG_DEBUG:
str = "DEBUG|";
break;
case LOG_TRACE:
str = "TRACE|";
break;
default:
str = " UNK|";
break;
}
std::fprintf(fd, str);
}
void Log::Initialize(const std::string& version)
{
if (LOG_LEVEL == LOG_OFF)
{
return;
}
// Note: Mode "w" deletes an existing file with the same name. This means that we only
// ever get the log from the last run. This matches the way that X-Plane treats its
// log.
fd = std::fopen("XPCLog.txt", "w");
if (fd != NULL)
{
@@ -81,35 +140,33 @@ namespace XPC
}
}
void Log::WriteLine(const std::string& value)
void Log::WriteLine(int level, const std::string& tag, const std::string& value)
{
Log::WriteLine(value.c_str());
}
void Log::WriteLine(const char* value)
{
if (!fd)
if (level > LOG_LEVEL || !fd)
{
return;
}
WriteTime(fd);
std::fprintf(fd, "%s\n", value);
WriteLevel(fd, level);
std::fprintf(fd, "%s|%s\n", tag.c_str(), value.c_str());
std::fflush(fd);
}
void Log::FormatLine(const char* format, ...)
void Log::FormatLine(int level, const std::string& tag, std::string format, ...)
{
va_list args;
if (!fd)
if (level > LOG_LEVEL || !fd)
{
return;
}
va_start(args, format);
WriteTime(fd);
std::vfprintf(fd, format, args);
WriteLevel(fd, level);
std::fprintf(fd, "%s|", tag.c_str());
std::vfprintf(fd, format.c_str(), args);
std::fprintf(fd, "\n");
std::fflush(fd);

View File

@@ -1,21 +1,28 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_LOG_H
#define XPC_LOG_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_LOG_H_
#define XPCPLUGIN_LOG_H_
#include <string>
// LOG_VERBOSITY determines the level of logging throughout the plugin.
// 0: Minimum logging. Only plugin manager events will be logged.
// 1: Critical errors. When an error that prevents correct operation of the
// plugin, attempt to write useful information to the log. Note that since
// XPC runs inside the X-Plane executable, we try very hard no to crash.
// As a result, these log messages may be the only indication of failure.
// 2: All errors. Any time something unexpected happens, log it.
// 3: Significant actions. Any time something happens outside of normal
// command processing, log it.
// 5: Everything. Log nearly every single action the plugin takes. This may
// have a detrimental impact on X-Plane performance.
#define LOG_VERBOSITY 2
// OFF: No logging at all will be performed.
// FATAL: Critical errors that would normally result in termination of the program. Because XPC
// operates in the X-Plane process, we try to never actually crash. As a result, we this
// level of logging may be the only indication of a problem.
// ERROR: All errors not covered by FATAL
// WARN: Potentially, but not definitely, incorrect behavior
// INFO: Information about normal actions taken by the plugin.
// DEBUG: More verbose information usefull for debugging.
// TRACE: Log all the things!
#define LOG_OFF 0
#define LOG_FATAL 1
#define LOG_ERROR 2
#define LOG_WARN 3
#define LOG_INFO 4
#define LOG_DEBUG 5
#define LOG_TRACE 6
#define LOG_LEVEL LOG_TRACE
namespace XPC
{
@@ -23,40 +30,37 @@ namespace XPC
///
/// \details Provides functions to write lines to the XPC log file.
/// \author Jason Watkins
/// \version 1.0
/// \version 1.1
/// \since 1.0
/// \date Intial Version: 2015-04-09
/// \date Last Updated: 2015-04-09
/// \date Last Updated: 2015-05-11
class Log
{
public:
/// Initializes the logging component by deleting old log files,
/// writing header information to the log file.
static void Initialize(std::string header);
static void Initialize(const std::string& header);
/// Closes the log file.
static void Close();
/// Writes the C string pointed to by format, followed by a line
/// Writes the string pointed to by format, followed by a line
/// terminator to the XPC log file. If format contains format
/// specifiers, additional arguments following format will be formatted
/// and inserted in the resulting string, replacing their respective
/// specifiers.
///
/// \param format The format string appropriate for consumption by sprintf.
static void FormatLine(const char* format, ...);
///
/// \remarks Note that Visual C++ silently fails va_start when the last non-varargs
/// argument is a reference, so we need a value-type format here.
static void FormatLine(int level, const std::string& tag, const std::string format, ...);
/// Writes the specified string value, followed by a line terminator
/// to the XPC log file.
///
/// \param value The value to write.
static void WriteLine(const std::string& value);
/// Writes the specified C string value, followed by a line terminator
/// to the XPC log file.
///
/// \param value The value to write.
static void WriteLine(const char* value);
static void WriteLine(int level, const std::string& tag, const std::string& value);
};
}
#endif

View File

@@ -1,8 +1,10 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#include "Message.h"
#include "Log.h"
#include <cstring>
#include <iomanip>
#include <sstream>
#include <string>
@@ -11,79 +13,62 @@ namespace XPC
{
Message::Message() {}
Message Message::ReadFrom(UDPSocket& sock)
Message Message::ReadFrom(const UDPSocket& sock)
{
Message m;
int len = sock.Read(m.buffer, bufferSize, &m.source);
m.size = len < 0 ? 0 : len;
if (len > 0)
{
Log::FormatLine(LOG_TRACE, "MESG", "Read message with length %i", len);
}
return m;
}
unsigned long Message::GetMagicNumber()
std::string Message::GetHead() const
{
if (size < 4)
{
return 0;
}
return *((unsigned long*)buffer);
std::string val = size < 4 ? "" : std::string((char*)buffer, 4);
return val;
}
std::string Message::GetHead()
const unsigned char* Message::GetBuffer() const
{
if (size < 4)
{
return "";
}
return std::string((char*)buffer, 4);
const unsigned char* val = size == 0 ? NULL : buffer;
return val;
}
const unsigned char* Message::GetBuffer()
{
if (size == 0)
{
return NULL;
}
return buffer;
}
std::size_t Message::GetSize()
std::size_t Message::GetSize() const
{
return size;
}
struct sockaddr Message::GetSource()
struct sockaddr Message::GetSource() const
{
return source;
}
void Message::PrintToLog()
void Message::PrintToLog() const
{
#if LOG_VERBOSITY > 4
std::stringstream ss;
ss << "[DEBUG]";
using namespace std;
stringstream ss;
// Dump raw bytes to string
ss << std::hex << std::setfill('0');
ss << std::hex << setfill('0');
for (int i = 0; i < size; ++i)
{
ss << ' ' << std::setw(2) << static_cast<unsigned>(buffer[i]);
ss << ' ' << setw(2) << static_cast<unsigned>(buffer[i]);
}
Log::WriteLine(ss.str());
Log::WriteLine(LOG_TRACE, "DBUG", ss.str());
ss << std::dec;
std::string head = GetHead();
ss.str("");
ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")";
switch (GetMagicNumber()) // Binary version of head
ss << "Head: " << head << std::dec << " Size: " << GetSize();
if (head == "CONN" || head == "WYPT" || head == "TEXT")
{
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
else if (head == "CTRL")
{
case 0x4E4EF443: // CONN
case 0x54505957: // WYPT
case 0x54584554: // TEXT
{
Log::WriteLine(ss.str());
break;
}
case 0x4C525443: // CTRL
{
// Parse message data
float pitch = *((float*)(buffer + 5));
float roll = *((float*)(buffer + 9));
@@ -96,89 +81,91 @@ namespace XPC
{
aircraft = buffer[26];
}
ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")";
ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps;
Log::WriteLine(ss.str());
break;
}
case 0x41544144: // DATA
ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")";
ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps;
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
else if (head == "DATA")
{
std::size_t numCols = (size - 5) / 36;
size_t numCols = (size - 5) / 36;
float values[32][9];
for (int i = 0; i < numCols; ++i)
{
values[i][0] = buffer[5 + 36 * i];
std::memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float));
memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float));
}
ss << " (" << numCols << " lines)";
Log::WriteLine(ss.str());
for (int i = 0; i < numCols; ++i)
{
ss.str("");
ss << "\t#" << values[i][0];
for (int j = 1; j < 9; ++j)
{
ss << " (" << numCols << " lines)";
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
for (int i = 0; i < numCols; ++i)
{
ss.str("");
ss << " #" << values[i][0];
for (int j = 1; j < 9; ++j)
{
ss << " " << values[i][j];
}
Log::WriteLine(ss.str());
}
break;
}
case 0x46455244: // DREF
{
Log::WriteLine(ss.str());
std::string dref((char*)buffer + 6, buffer[5]);
Log::FormatLine("-\tDREF (size %i) = %s", dref.length(), dref.c_str());
}
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
}
else if (head == "DREF")
{
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
string dref((char*)buffer + 6, buffer[5]);
Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str());
ss.str("");
int values = buffer[6 + buffer[5]];
ss << "\tValues(size " << values << ") =";
ss << " Values(size " << values << ") =";
for (int i = 0; i < values; ++i)
{
ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i));
}
Log::WriteLine(ss.str());
break;
}
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
case 0x44544547: // GETD
{
Log::WriteLine(ss.str());
else if (head == "GETC" || head == "GETP")
{
ss << " Aircraft:" << (int)buffer[5];
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
else if (head == "GETD")
{
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
int cur = 6;
for (int i = 0; i < buffer[5]; ++i)
{
std::string dref((char*)buffer + cur + 1, buffer[cur]);
Log::FormatLine("\t#%i/%i (size:%i) %s", i + 1, buffer[5], dref.length(), dref.c_str());
string dref((char*)buffer + cur + 1, buffer[cur]);
Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s",
i + 1, buffer[5], dref.length(), dref.c_str());
cur += 1 + buffer[cur];
}
break;
}
case 0x49534F50: // POSI
else if (head == "POSI")
{
char aircraft = buffer[5];
float gear = *((float*)(buffer + 30));
float pos[3];
float orient[3];
std::memcpy(pos, buffer + 6, 12);
std::memcpy(orient, buffer + 18, 12);
memcpy(pos, buffer + 6, 12);
memcpy(orient, buffer + 18, 12);
ss << " AC:" << (int)aircraft;
ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:(";
ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:";
ss << gear;
Log::WriteLine(ss.str());
break;
ss << gear;
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
case 0x554D4953: // SIMU
else if (head == "SIMU")
{
ss << ' ' << (int)buffer[5];
Log::WriteLine(ss.str());
break;
}
default:
{
ss << " UNKNOWN HEADER ";
Log::WriteLine(ss.str());
break;
}
ss << ' ' << (int)buffer[5];
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
#endif
else if (head == "VIEW")
{
ss << "Type:" << *((unsigned long*)(buffer + 5));
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
else
{
ss << " UNKNOWN HEADER ";
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
}
}

View File

@@ -1,7 +1,7 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_MESSAGE_H
#define XPC_MESSAGE_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_MESSAGE_H_
#define XPCPLUGIN_MESSAGE_H_
#include "UDPSocket.h"
@@ -10,10 +10,10 @@ namespace XPC
/// Represents a message received from an XPC client.
///
/// \author Jason Watkins
/// \version 1.0
/// \version 1.1
/// \since 1.0
/// \date Intial Version: 2015-04-11
/// \date Last Updated: 2015-04-11
/// \date Last Updated: 2015-05-11
class Message
{
public:
@@ -24,25 +24,25 @@ namespace XPC
/// \returns A message parsed from the data read from sock. If no
/// data was read or an error occurs, returns a message
/// with the size set to 0.
static Message ReadFrom(UDPSocket& sock);
static Message ReadFrom(const UDPSocket& sock);
/// Gets the message header in binary form.
unsigned long GetMagicNumber();
unsigned long GetMagicNumber() const;
/// Gets the message header.
std::string GetHead();
std::string GetHead() const;
/// Gets the buffer underlying the message.
const unsigned char* GetBuffer();
const unsigned char* GetBuffer() const;
/// Gets the size of the message in bytes.
std::size_t GetSize();
std::size_t GetSize() const;
/// Gets the address this message was read from.
struct sockaddr GetSource();
struct sockaddr GetSource() const;
/// Prints the contents of the message to the XPC log.
void PrintToLog();
void PrintToLog() const;
private:
Message();

View File

@@ -1,10 +1,26 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// X-Plane API
// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files(the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
#include "MessageHandlers.h"
#include "DataManager.h"
#include "Drawing.h"
#include "Log.h"
#include "XPLMUtilities.h"
#include <cmath>
#include <cstring>
@@ -19,6 +35,7 @@ namespace XPC
void MessageHandlers::SetSocket(UDPSocket* socket)
{
Log::WriteLine(LOG_TRACE, "MSGH", "Setting socket");
MessageHandlers::sock = socket;
}
@@ -26,6 +43,7 @@ namespace XPC
{
if (handlers.size() == 0)
{
Log::WriteLine(LOG_TRACE, "MSGH", "Initializing handlers");
// Common messages
handlers.insert(std::make_pair("CONN", MessageHandlers::HandleConn));
handlers.insert(std::make_pair("CTRL", MessageHandlers::HandleCtrl));
@@ -36,8 +54,9 @@ namespace XPC
handlers.insert(std::make_pair("SIMU", MessageHandlers::HandleSimu));
handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText));
handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt));
// Not implemented messages
handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleUnknown));
handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleView));
handlers.insert(std::make_pair("GETC", MessageHandlers::HandleGetC));
handlers.insert(std::make_pair("GETP", MessageHandlers::HandleGetP));
// X-Plane data messages
handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData));
handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData));
@@ -65,16 +84,14 @@ namespace XPC
std::string head = msg.GetHead();
if (head == "")
{
Log::WriteLine(LOG_WARN, "MSGH", "Warning: HandleMessage called with empty message.");
return; // No Message to handle
}
msg.PrintToLog();
// Set current connection
sockaddr sourceaddr = msg.GetSource();
connectionKey = UDPSocket::GetHost(&sourceaddr);
#if LOG_VERBOSITY > 4
Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str());
#endif
Log::FormatLine(LOG_INFO, "MSGH", "Handling message from %s", connectionKey.c_str());
std::map<std::string, ConnectionInfo>::iterator conn = connections.find(connectionKey);
if (conn == connections.end()) // New connection
{
@@ -86,19 +103,17 @@ namespace XPC
connection.addr = sourceaddr;
connection.getdCount = 0;
connections[connectionKey] = connection;
#if LOG_VERBOSITY > 2
Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str());
#endif
Log::FormatLine(LOG_DEBUG, "MSGH", "New connection. ID=%u, Remote=%s",
connection.id, connectionKey.c_str());
}
else
{
connection = (*conn).second;
#if LOG_VERBOSITY > 3
Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s",
Log::FormatLine(LOG_DEBUG, "MSGH", "Existing connection. ID=%u, Remote=%s",
connection.id, connectionKey.c_str());
#endif
}
msg.PrintToLog();
// Check if there is a handler for this message type. If so, execute
// that handler. Otherwise, execute the unknown message handler.
std::map<std::string, MessageHandler>::iterator iter = handlers.find(head);
@@ -113,7 +128,7 @@ namespace XPC
}
}
void MessageHandlers::HandleConn(Message& msg)
void MessageHandlers::HandleConn(const Message& msg)
{
const unsigned char* buffer = msg.GetBuffer();
@@ -122,23 +137,21 @@ namespace XPC
sockaddr* sa = &connection.addr;
switch (sa->sa_family)
{
case AF_INET:
case AF_INET: // IPV4 address
{
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa);
(*sin).sin_port = htons(port);
break;
}
case AF_INET6:
case AF_INET6: // IPV6 addres
{
sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa);
(*sin).sin6_port = htons(port);
break;
}
default:
#if LOG_VERBOSITY > 0
Log::WriteLine("[CONN] ERROR: Unknown address type.");
Log::WriteLine(LOG_ERROR, "CONN", "ERROR: Unknown address type.");
return;
#endif
}
connections.erase(connectionKey);
connectionKey = UDPSocket::GetHost(&connection.addr);
@@ -149,31 +162,26 @@ namespace XPC
response[5] = connection.id;
// Update log
#if LOG_VERBOSITY > 1
Log::FormatLine("[CONN] ID: %u New destination port: %u",
Log::FormatLine(LOG_TRACE, "CONN", "ID: %u New destination port: %u",
connection.id, port);
#endif
// Send response
sock->SendTo(response, 6, &connection.addr);
}
void MessageHandlers::HandleCtrl(Message& msg)
void MessageHandlers::HandleCtrl(const Message& msg)
{
// Update Log
#if LOG_VERBOSITY > 2
Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "CTRL", "Message Received (Conn %i)", connection.id);
const unsigned char* buffer = msg.GetBuffer();
std::size_t size = msg.GetSize();
//Legacy packets that don't specify an aircraft number should be 26 bytes long.
//Packets specifying an A/C num should be 27 bytes.
// Legacy packets that don't specify an aircraft number should be 26 bytes long.
// Packets specifying an A/C num should be 27 bytes. Packets specifying a speedbrake
// should be 31 bytes.
if (size != 26 && size != 27 && size != 31)
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size);
#endif
Log::FormatLine(LOG_ERROR, "CTRL", "ERROR: Unexpected message length (%i)", size);
return;
}
@@ -181,63 +189,63 @@ namespace XPC
float pitch = *((float*)(buffer + 5));
float roll = *((float*)(buffer + 9));
float yaw = *((float*)(buffer + 13));
float thr = *((float*)(buffer + 17));
float throttle = *((float*)(buffer + 17));
char gear = buffer[21];
float flaps = *((float*)(buffer + 22));
unsigned char aircraft = 0;
unsigned char aircraftNumber = 0;
if (size >= 27)
{
aircraft = buffer[26];
aircraftNumber = buffer[26];
}
float spdbrk = -998;
float spdbrk = DataManager::GetDefaultValue();
if (size >= 31)
{
spdbrk = *((float*)(buffer + 27));
}
if (pitch < -999.5 || pitch > -997.5)
if (!DataManager::IsDefault(pitch))
{
DataManager::Set(DREF_YokePitch, pitch, aircraft);
DataManager::Set(DREF_YokePitch, pitch, aircraftNumber);
}
if (roll < -999.5 || roll > -997.5)
if (!DataManager::IsDefault(roll))
{
DataManager::Set(DREF_YokeRoll, roll, aircraft);
DataManager::Set(DREF_YokeRoll, roll, aircraftNumber);
}
if (yaw < -999.5 || yaw > -997.5)
if (!DataManager::IsDefault(yaw))
{
DataManager::Set(DREF_YokeHeading, yaw, aircraft);
DataManager::Set(DREF_YokeHeading, yaw, aircraftNumber);
}
if (thr < -999.5 || thr > -997.5)
if (!DataManager::IsDefault(throttle))
{
float thrArray[8];
float throttleArray[8];
for (int i = 0; i < 8; ++i)
{
thrArray[i] = thr;
throttleArray[i] = throttle;
}
DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft);
DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft);
if (aircraft == 0)
DataManager::Set(DREF_ThrottleSet, throttleArray, 8, aircraftNumber);
DataManager::Set(DREF_ThrottleActual, throttleArray, 8, aircraftNumber);
if (aircraftNumber == 0)
{
DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", thrArray, 1);
DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", throttleArray, 1);
}
}
if (gear != -1)
{
DataManager::SetGear(gear, false, aircraft);
DataManager::SetGear(gear, false, aircraftNumber);
}
if (flaps < -999.5 || flaps > -997.5)
if (!DataManager::IsDefault(flaps))
{
DataManager::Set(DREF_FlapSetting, flaps, aircraft);
DataManager::Set(DREF_FlapSetting, flaps, aircraftNumber);
}
if (spdbrk < -999.5 || spdbrk > -997.5)
if (!DataManager::IsDefault(spdbrk))
{
DataManager::Set(DREF_SpeedBrakeSet, spdbrk, aircraft);
DataManager::Set(DREF_SpeedBrakeSet, spdbrk, aircraftNumber);
}
}
void MessageHandlers::HandleData(Message& msg)
void MessageHandlers::HandleData(const Message& msg)
{
// Parse data
const unsigned char* buffer = msg.GetBuffer();
@@ -245,59 +253,51 @@ namespace XPC
std::size_t numCols = (size - 5) / 36;
if (numCols > 0)
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "DATA", "Message Received (Conn %i)", connection.id);
}
else
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_WARN, "DATA", "WARNING: Empty data packet received (Conn %i)", connection.id);
return;
}
if (numCols > 134) // Error. Will overflow values
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[DATA] ERROR: numCols to large.");
#endif
Log::FormatLine(LOG_ERROR, "DATA", "ERROR: numCols to large.");
return;
}
float values[134][9];
for (int i = 0; i < numCols; ++i)
{
// 5 byte header + (9 * 4 = 36) bytes per row
values[i][0] = buffer[5 + 36 * i];
memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float));
}
// Update log
float savedAlpha = -998;
float savedHPath = -998;
float savedAlpha = DataManager::GetDefaultValue();
float savedHPath = DataManager::GetDefaultValue();
for (int i = 0; i < numCols; ++i)
{
unsigned char dataRef = (unsigned char)values[i][0];
if (dataRef >= 134)
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[DATA] ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef);
#endif
Log::FormatLine(LOG_ERROR, "DATA", "ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef);
continue;
}
switch (dataRef)
{
// TODO(jason-watkins): This currently overwrites the velocity several times. Should look into making this case smarter somehow.
case 3: // Velocity
{
float theta = DataManager::GetFloat(DREF_Pitch);
float alpha = savedAlpha != -998 ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack);
float hpath = savedHPath != -998 ? savedHPath : DataManager::GetFloat(DREF_HPath);
float alpha = DataManager::IsDefault(savedAlpha) ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack);
float hpath = DataManager::IsDefault(savedHPath) ? savedHPath : DataManager::GetFloat(DREF_HPath);
if (alpha != alpha || hpath != hpath)
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
break;
}
const float deg2rad = 0.0174532925F;
@@ -305,7 +305,7 @@ namespace XPC
for (int j = 0; j < 3; ++j)
{
float v = values[i][ind[j]];
if (v != -998)
if (!DataManager::IsDefault(v))
{
DataManager::Set(DREF_LocalVX, v*cos((theta - alpha)*deg2rad)*sin(hpath*deg2rad));
DataManager::Set(DREF_LocalVY, v*sin((theta - alpha)*deg2rad));
@@ -316,27 +316,25 @@ namespace XPC
}
case 17: // Orientation
{
float orient[3];
orient[0] = values[i][1];
orient[1] = values[i][2];
orient[2] = values[i][3];
DataManager::SetOrientation(orient);
float orientation[3];
orientation[0] = values[i][1];
orientation[1] = values[i][2];
orientation[2] = values[i][3];
DataManager::SetOrientation(orientation);
break;
}
case 18: // Alpha, hpath etc.
{
if (values[i][1] != values[i][1] || values[i][3] != values[i][3])
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
break;
}
if (values[i][1] != -998)
if (!DataManager::IsDefault(values[i][1]))
{
savedAlpha = values[i][1];
}
if (values[i][3] != -998)
if (DataManager::IsDefault(values[i][3]))
{
savedHPath = values[i][3];
}
@@ -355,17 +353,15 @@ namespace XPC
{
if (values[i][1] != values[i][1])
{
#if LOG_VERBOSITY > 0
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
break;
}
float thr[8];
float throttle[8];
for (int j = 0; j < 8; ++j)
{
thr[j] = values[i][1];
throttle[j] = values[i][1];
}
DataManager::Set(DREF_ThrottleSet, thr, 8);
DataManager::Set(DREF_ThrottleSet, throttle, 8);
break;
}
default: // Non-Special dataRefs
@@ -374,10 +370,8 @@ namespace XPC
memcpy(line, values[i] + 1, 8 * sizeof(float));
for (int j = 0; j < 8; ++j)
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[DATA] Setting Dataref %i.%i to %f", dataRef, j, line[j]);
#endif
Log::FormatLine(LOG_ERROR, "DATA", "Setting Dataref %i.%i to %f", dataRef, j, line[j]);
// TODO(jason-watkins): Why is this a special case?
if (dataRef == 14 && j == 0)
{
DataManager::SetGear(line[0], true);
@@ -387,7 +381,7 @@ namespace XPC
DREF dref = XPData[dataRef][j];
if (dref == DREF_None)
{
// TODO: Send single line instead!
// TODO(jason): Send single line instead!
HandleXPlaneData(msg);
}
else
@@ -400,46 +394,96 @@ namespace XPC
}
}
void MessageHandlers::HandleDref(Message& msg)
void MessageHandlers::HandleDref(const Message& msg)
{
Log::FormatLine(LOG_TRACE, "DREF", "Request to set DREF value received (Conn %i)", connection.id);
const unsigned char* buffer = msg.GetBuffer();
unsigned char len = buffer[5];
std::string dref = std::string((char*)buffer + 6, len);
std::size_t size = msg.GetSize();
std::size_t pos = 5;
while (pos < size)
{
unsigned char len = buffer[pos++];
if (pos + len > size)
{
break;
}
std::string dref = std::string((char*)buffer + pos, len);
pos += len;
unsigned char valueCount = buffer[6 + len];
float* values = (float*)(buffer + 7 + len);
unsigned char valueCount = buffer[pos++];
if (pos + 4 * valueCount > size)
{
break;
}
float* values = (float*)(buffer + pos);
pos += 4 * valueCount;
#if LOG_VERBOSITY > 1
Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str());
#endif
DataManager::Set(dref, values, valueCount);
DataManager::Set(dref, values, valueCount);
Log::FormatLine(LOG_DEBUG, "DREF", "Set %d values for %s", valueCount, dref.c_str());
}
if (pos != size)
{
Log::WriteLine(LOG_ERROR, "DREF", "ERROR: Command did not terminate at the expected position.");
}
}
void MessageHandlers::HandleGetD(Message& msg)
void MessageHandlers::HandleGetC(const Message& msg)
{
const unsigned char* buffer = msg.GetBuffer();
std::size_t size = msg.GetSize();
if (size != 6)
{
Log::FormatLine(LOG_ERROR, "GCTL", "Unexpected message length: %u", size);
return;
}
unsigned char aircraft = buffer[5];
// TODO(jason-watkins): Get proper printf specifier for unsigned char
Log::FormatLine(LOG_TRACE, "GCTL", "Getting control information for aircraft %u", aircraft);
float throttle[8];
unsigned char response[31] = "CTRL";
*((float*)(response + 5)) = DataManager::GetFloat(DREF_Elevator, aircraft);
*((float*)(response + 9)) = DataManager::GetFloat(DREF_Aileron, aircraft);
*((float*)(response + 13)) = DataManager::GetFloat(DREF_Rudder, aircraft);
DataManager::GetFloatArray(DREF_ThrottleSet, throttle, 8, aircraft);
*((float*)(response + 17)) = throttle[0];
if (aircraft == 0)
{
response[21] = (char)DataManager::GetInt(DREF_GearHandle, aircraft);
}
else
{
float mpGear[10];
DataManager::GetFloatArray(DREF_GearDeploy, mpGear, 10, aircraft);
response[21] = mpGear[0] > 0.5 ? 1 : 0;
}
*((float*)(response + 22)) = DataManager::GetFloat(DREF_FlapSetting, aircraft);
response[26] = aircraft;
*((float*)(response + 27)) = DataManager::GetFloat(DREF_SpeedBrakeSet, aircraft);
sock->SendTo(response, 31, &connection.addr);
}
void MessageHandlers::HandleGetD(const Message& msg)
{
const unsigned char* buffer = msg.GetBuffer();
unsigned char drefCount = buffer[5];
if (drefCount == 0) // Use last request
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[GETD] DATA Requested: Repeat last request from connection %i (%i data refs)",
Log::FormatLine(LOG_TRACE, "GETD",
"DATA Requested: Repeat last request from connection %i (%i data refs)",
connection.id, connection.getdCount);
#endif
if (connection.getdCount == 0) // No previous request to use
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[GETD] ERROR: No previous requests from connection %i.", connection.id);
#endif
Log::FormatLine(LOG_ERROR, "GETD", "ERROR: No previous requests from connection %i.",
connection.id);
return;
}
}
else // New request
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[GETD] DATA Requested: New Request for connection %i (%i data refs)",
Log::FormatLine(LOG_TRACE, "GETD", "DATA Requested: New Request for connection %i (%i data refs)",
connection.id, drefCount);
#endif
std::size_t ptr = 6;
for (int i = 0; i < drefCount; ++i)
{
@@ -466,64 +510,84 @@ namespace XPC
sock->SendTo(response, cur, &connection.addr);
}
void MessageHandlers::HandlePosi(Message& msg)
void MessageHandlers::HandleGetP(const Message& msg)
{
const unsigned char* buffer = msg.GetBuffer();
std::size_t size = msg.GetSize();
if (size != 6)
{
Log::FormatLine(LOG_ERROR, "GPOS", "Unexpected message length: %u", size);
return;
}
unsigned char aircraft = buffer[5];
Log::FormatLine(LOG_TRACE, "GPOS", "Getting position information for aircraft %u", aircraft);
unsigned char response[34] = "POSI";
response[5] = (char)DataManager::GetInt(DREF_GearHandle, aircraft);
*((float*)(response + 6)) = (float)DataManager::GetDouble(DREF_Latitude, aircraft);
*((float*)(response + 10)) = (float)DataManager::GetDouble(DREF_Longitude, aircraft);
*((float*)(response + 14)) = (float)DataManager::GetDouble(DREF_Elevation, aircraft);
*((float*)(response + 18)) = DataManager::GetFloat(DREF_Pitch, aircraft);
*((float*)(response + 22)) = DataManager::GetFloat(DREF_Roll, aircraft);
*((float*)(response + 26)) = DataManager::GetFloat(DREF_HeadingTrue, aircraft);
float gear[10];
DataManager::GetFloatArray(DREF_GearDeploy, gear, 10, aircraft);
*((float*)(response + 30)) = gear[0];
sock->SendTo(response, 34, &connection.addr);
}
void MessageHandlers::HandlePosi(const Message& msg)
{
// Update log
#if LOG_VERBOSITY > 0
Log::FormatLine("[POSI] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "POSI", "Message Received (Conn %i)", connection.id);
const unsigned char* buffer = msg.GetBuffer();
const std::size_t size = msg.GetSize();
if (size < 34)
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[POSI] ERROR: Unexpected size: %i (Expected at least 34)", size);
#endif
Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected at least 34)", size);
return;
}
char aircraft = buffer[5];
char aircraftNumber = buffer[5];
float gear = *((float*)(buffer + 30));
float pos[3];
float orient[3];
memcpy(pos, buffer + 6, 12);
memcpy(orient, buffer + 18, 12);
if (aircraft > 0)
if (aircraftNumber > 0)
{
// Enable AI for the aircraft we are setting
// Enable AI for the aircraftNumber we are setting
float ai[20];
std::size_t result = DataManager::GetFloatArray(DREF_PauseAI, ai, 20);
if (result == 20) // Only set values if they were retrieved successfully.
{
ai[aircraft] = 1;
ai[aircraftNumber] = 1;
DataManager::Set(DREF_PauseAI, ai, 0, 20);
}
}
DataManager::SetPosition(pos, aircraft);
DataManager::SetOrientation(orient, aircraft);
DataManager::SetPosition(pos, aircraftNumber);
DataManager::SetOrientation(orient, aircraftNumber);
if (gear != -1)
{
DataManager::SetGear(gear, true, aircraft);
DataManager::SetGear(gear, true, aircraftNumber);
}
}
void MessageHandlers::HandleSimu(Message& msg)
void MessageHandlers::HandleSimu(const Message& msg)
{
// Update log
#if LOG_VERBOSITY > 1
Log::FormatLine("[SIMU] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "SIMU", "Message Received (Conn %i)", connection.id);
char v = msg.GetBuffer()[5];
if (v < 0 || v > 2)
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[SIMU] ERROR: Invalid argument: %i", v);
Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v);
return;
#endif
}
int value[20];
@@ -546,28 +610,24 @@ namespace XPC
// Set DREF
DataManager::Set(DREF_Pause, value, 20);
#if LOG_VERBOSITY > 2
switch (v)
{
case 0:
Log::WriteLine("[SIMU] Simulation Resumed");
Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed");
break;
case 1:
Log::WriteLine("[SIMU] Simulation Paused");
Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused");
break;
case 2:
Log::WriteLine("[SIMU] Simulation switched.");
Log::FormatLine(LOG_INFO, "SIMU", "Simulation switched to %i", value[0]);
break;
}
#endif
}
void MessageHandlers::HandleText(Message& msg)
void MessageHandlers::HandleText(const Message& msg)
{
// Update Log
#if LOG_VERBOSITY > 0
Log::FormatLine("[TEXT] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "TEXT", "Message Received (Conn %i)", connection.id);
std::size_t len = msg.GetSize();
const unsigned char* buffer = msg.GetBuffer();
@@ -575,18 +635,14 @@ namespace XPC
char text[256] = { 0 };
if (len < 14)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[TEXT] ERROR: Length less than 14 bytes");
#endif
Log::WriteLine(LOG_ERROR, "TEXT", "ERROR: Length less than 14 bytes");
return;
}
size_t msgLen = (unsigned char)buffer[13];
if (msgLen == 0)
{
Drawing::ClearMessage();
#if LOG_VERBOSITY > 2
Log::WriteLine("[TEXT] Text cleared");
#endif
Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text cleared");
}
else
{
@@ -594,18 +650,30 @@ namespace XPC
int y = *((int*)(buffer + 9));
strncpy(text, (char*)buffer + 14, msgLen);
Drawing::SetMessage(x, y, text);
#if LOG_VERBOSITY > 2
Log::WriteLine("[TEXT] Text set");
#endif
Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text set");
}
}
void MessageHandlers::HandleWypt(Message& msg)
void MessageHandlers::HandleView(const Message& msg)
{
// Update Log
#if LOG_VERBOSITY > 0
Log::FormatLine("[WYPT] Message Received (Conn %i)", connection.id);
#endif
Log::FormatLine(LOG_TRACE, "VIEW", "Message Received(Conn %i)", connection.id);
const std::size_t size = msg.GetSize();
if (size != 9)
{
Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9.", size);
return;
}
const unsigned char* buffer = msg.GetBuffer();
int type = *((int*)(buffer + 5));
XPLMCommandKeyStroke(type);
}
void MessageHandlers::HandleWypt(const Message& msg)
{
// Update Log
Log::FormatLine(LOG_TRACE, "WYPT", "Message Received (Conn %i)", connection.id);
// Parse data
const unsigned char* buffer = msg.GetBuffer();
@@ -622,9 +690,7 @@ namespace XPC
}
// Perform operation
#if LOG_VERBOSITY > 2
Log::FormatLine("[WYPT] Performing operation %i", op);
#endif
Log::FormatLine(LOG_INFO, "WYPT", "Performing operation %i", op);
switch (op)
{
case 1:
@@ -637,18 +703,14 @@ namespace XPC
Drawing::ClearWaypoints();
break;
default:
#if LOG_VERBOSITY > 1
Log::FormatLine("[WYPT] ERROR: %i is not a valid operation.", op);
#endif
Log::FormatLine(LOG_ERROR, "WYPT", "ERROR: %i is not a valid operation.", op);
break;
}
}
void MessageHandlers::HandleXPlaneData(Message& msg)
void MessageHandlers::HandleXPlaneData(const Message& msg)
{
#if LOG_VERBOSITY > 1
Log::WriteLine("[MSGH] Sending raw data to X-Plane");
#endif
Log::WriteLine(LOG_TRACE, "MSGH", "Sending raw data to X - Plane");
sockaddr_in loopback;
loopback.sin_family = AF_INET;
loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -656,11 +718,8 @@ namespace XPC
sock->SendTo(msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback);
}
void MessageHandlers::HandleUnknown(Message& msg)
void MessageHandlers::HandleUnknown(const Message& msg)
{
// UPDATE LOG
#if LOG_VERBOSITY > 0
Log::FormatLine("[EXEC] ERROR: Unknown packet type %s", msg.GetHead().c_str());
#endif
Log::FormatLine(LOG_ERROR, "MSGH", "ERROR: Unknown packet type %s", msg.GetHead().c_str());
}
}

View File

@@ -1,7 +1,7 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_MESSAGEHANDLERS_H
#define XPC_MESSAGEHANDLERS_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_MESSAGEHANDLERS_H_
#define XPCPLUGIN_MESSAGEHANDLERS_H_
#include "Message.h"
#include <string>
@@ -10,15 +10,15 @@
namespace XPC
{
/// A function that handles a message.
typedef void(*MessageHandler)(Message&);
typedef void(*MessageHandler)(const Message&);
/// Handles incommming messages and manages connections.
/// Handles incoming messages and manages connections.
///
/// \author Jason Watkins
/// \version 1.0
/// \version 1.1
/// \since 1.0
/// \date Intial Version: 2015-04-12
/// \date Last Updated: 2015-04-12
/// \date Last Updated: 2015-05-11
class MessageHandlers
{
public:
@@ -33,21 +33,27 @@ namespace XPC
/// \param msg The message to be processed.
static void HandleMessage(Message& msg);
/// Sets the socke that message handlers use to send responses.
/// Sets the socket that message handlers use to send responses.
static void SetSocket(UDPSocket* socket);
private:
static void HandleConn(Message& msg);
static void HandleCtrl(Message& msg);
static void HandleData(Message& msg);
static void HandleDref(Message& msg);
static void HandleGetD(Message& msg);
static void HandlePosi(Message& msg);
static void HandleSimu(Message& msg);
static void HandleText(Message& msg);
static void HandleWypt(Message& msg);
static void HandleXPlaneData(Message& msg);
static void HandleUnknown(Message& msg);
// One handler per message type. Message types are descripbed on the
// wiki at https://github.com/nasa/XPlaneConnect/wiki/Network-Information
static void HandleConn(const Message& msg);
static void HandleCtrl(const Message& msg);
static void HandleData(const Message& msg);
static void HandleDref(const Message& msg);
static void HandleGetC(const Message& msg);
static void HandleGetD(const Message& msg);
static void HandleGetP(const Message& msg);
static void HandlePosi(const Message& msg);
static void HandleSimu(const Message& msg);
static void HandleText(const Message& msg);
static void HandleWypt(const Message& msg);
static void HandleView(const Message& msg);
static void HandleXPlaneData(const Message& msg);
static void HandleUnknown(const Message& msg);
typedef struct
{

View File

@@ -1,5 +1,5 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#include "Log.h"
#include "UDPSocket.h"
@@ -8,41 +8,37 @@
namespace XPC
{
const static std::string tag = "SOCK";
UDPSocket::UDPSocket(unsigned short recvPort)
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[SOCK] Opening socket (port:%d)", recvPort);
#endif
Log::FormatLine(LOG_TRACE, tag, "Opening socket (port:%d)", recvPort);
// Setup Port
struct sockaddr_in localAddr;
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = INADDR_ANY;
localAddr.sin_port = htons(recvPort);
//Create and bind the socket
// Create and bind the socket
#ifdef _WIN32
WSADATA wsa;
int startResult = WSAStartup(MAKEWORD(2, 2), &wsa);
if (startResult != 0)
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[SOCK] ERROR: WSAStartup failed with error code %i.", startResult);
#endif
Log::FormatLine(LOG_FATAL, tag, "ERROR: WSAStartup failed with error code %i.", startResult);
this->sock = ~0;
return;
}
if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
#if LOG_VERBOSITY > 0
int err = WSAGetLastError();
Log::FormatLine("[SOCK] ERROR: Failed to open socket. (Error code %i)", err);
#endif
Log::FormatLine(LOG_FATAL, tag, "ERROR: Failed to open socket. (Error code %i)", err);
return;
}
#elif (__APPLE__ || __linux)
if ((this->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
Log::WriteLine("[SOCK] ERROR: Failed to open socket");
Log::WriteLine(LOG_FATAL, tag, "ERROR: Failed to open socket");
return;
}
int optval = 1;
@@ -52,39 +48,35 @@ namespace XPC
if (bind(this->sock, (struct sockaddr*)&localAddr, sizeof(localAddr)) != 0)
{
#ifdef _WIN32
#if LOG_VERBOSITY > 0
int err = WSAGetLastError();
Log::FormatLine("[SOCK] ERROR: Failed to bind socket. (Error code %i)", err);
#endif
Log::FormatLine(LOG_FATAL, tag, "ERROR: Failed to bind socket. (Error code %i)", err);
#else
Log::WriteLine(LOG_FATAL, tag, "ERROR: Failed to bind socket.");
#endif
return;
}
//Set Timout
// Set Timout
int usTimeOut = 500;
#ifdef _WIN32
DWORD msTimeOutWin = 1; // Minimum socket timeout in Windows is 1ms
if(setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&msTimeOutWin, sizeof(msTimeOutWin)) != 0)
{
#if LOG_VERBOSITY > 1
int err = WSAGetLastError();
Log::FormatLine("[SOCK] ERROR: Failed to set timeout. (Error code %i)", err);
#endif
Log::FormatLine(LOG_ERROR, tag, "ERROR: Failed to set timeout. (Error code %i)", err);
}
#else
struct timeval tv;
tv.tv_sec = 0; /* Sec Timeout */
tv.tv_usec = usTimeOut; // Microsec Timeout
tv.tv_sec = 0;
tv.tv_usec = usTimeOut;
setsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
#endif
}
UDPSocket::~UDPSocket()
{
#if LOG_VERBOSITY > 1
Log::FormatLine("[SOCK] Closing socket (%d)", this->sock);
#endif
Log::FormatLine(LOG_TRACE, tag, "Closing socket (%d)", this->sock);
#ifdef _WIN32
closesocket(this->sock);
#elif (__APPLE__ || __linux)
@@ -92,7 +84,7 @@ namespace XPC
#endif
}
int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr)
int UDPSocket::Read(unsigned char* dst, int maxLen, sockaddr* recvAddr) const
{
socklen_t recvaddrlen = sizeof(*recvAddr);
int status = 0;
@@ -111,18 +103,16 @@ namespace XPC
FD_SET(sock, &stReadFDS);
FD_ZERO(&stExceptFDS);
FD_SET(sock, &stExceptFDS);
tv.tv_sec = 0; /* Sec Timeout */
tv.tv_usec = 250; // Microsec Timeout
tv.tv_sec = 0;
tv.tv_usec = 250;
// Select Command
int result = select(-1, &stReadFDS, (FD_SET *)0, &stExceptFDS, &tv);
#if LOG_VERBOSITY > 1
if (result == SOCKET_ERROR)
{
int err = WSAGetLastError();
Log::FormatLine("[SOCK] ERROR: Select failed. (Error code %i)", err);
Log::FormatLine(LOG_ERROR, tag, "ERROR: Select failed. (Error code %i)", err);
}
#endif
if (result <= 0) // No Data or error
{
return -1;
@@ -137,22 +127,18 @@ namespace XPC
return status;
}
void UDPSocket::SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote)
void UDPSocket::SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) const
{
if (sendto(sock, (char*)buffer, (int)len, 0, remote, sizeof(*remote)) < 0)
{
#if LOG_VERBOSITY > 0
Log::FormatLine("[SOCK] Send failed. (remote: %s)", GetHost(remote).c_str());
#endif
Log::FormatLine(LOG_ERROR, tag, "Send failed. (remote: %s)", GetHost(remote).c_str());
}
else
{
#if LOG_VERBOSITY > 3
Log::FormatLine("[SOCK] Datagram sent. (remote: %s)", GetHost(remote).c_str());
#endif
Log::FormatLine(LOG_INFO, tag, "Send succeeded. (remote: %s)", GetHost(remote).c_str());
}
}
std::string UDPSocket::GetHost(sockaddr* sa)
{
char ip[INET6_ADDRSTRLEN + 6] = { 0 };

View File

@@ -1,14 +1,14 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_SOCKET_H
#define XPC_SOCKET_H
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_SOCKET_H_
#define XPCPLUGIN_SOCKET_H_
#include <cstdlib>
#include <string>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#pragma comment(lib, "ws2_32.lib") // Winsock Library
#elif (__APPLE__ || __linux)
#include <sys/socket.h>
#include <netinet/in.h>
@@ -23,10 +23,10 @@ namespace XPC
/// data to XPC clients.
///
/// \author Jason Watkins
/// \version 1.0
/// \version 1.1
/// \since 1.0
/// \date Intial Version: 2015-04-10
/// \date Last Updated: 2015-04-11
/// \date Last Updated: 2015-05-11
class UDPSocket
{
public:
@@ -34,7 +34,7 @@ namespace XPC
/// specified receive port.
///
/// \param recvPort The port on which this instance will receive data.
UDPSocket(unsigned short recvPort);
explicit UDPSocket(unsigned short recvPort);
/// Closes the underlying socket for this instance.
~UDPSocket();
@@ -45,17 +45,17 @@ namespace XPC
/// \param buffer The array to copy the data into.
/// \param size The number of bytes to read.
/// \param remoteAddr When at least one byte is read, contains the address
/// of the remote host.
/// of the remote host.
/// \returns The number of bytes read, or a negative number if
/// an error occurs.
int Read(unsigned char* buffer, int size, sockaddr* remoteAddr);
/// an error occurs.
int Read(unsigned char* buffer, int size, sockaddr* remoteAddr) const;
/// Sends data to the specified remote endpoint.
///
/// \param data The data to be sent.
/// \param len The number of bytes to send.
/// \param remote The destination socket.
void SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote);
void SendTo(const unsigned char* buffer, std::size_t len, sockaddr* remote) const;
/// Gets a string containing the IP address and port contained in the given sockaddr.
///

View File

@@ -1,58 +1,58 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
//DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
// DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY
// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED,
// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS,
// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
//
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES
// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF
// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES
// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING
// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT,
// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
//
//X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
//associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute,
//sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
// X-Plane API
// Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files(the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research
// may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or
// Laminar Research, respectively.
// X-Plane Connect Plugin
// X-Plane Connect Plugin
//
// DESCRIPTION
// XPCPlugin Facilitates Communication to and from the XPlane
// DESCRIPTION
// XPCPlugin Facilitates Communication to and from the XPlane
//
// INSTRUCTIONS
// See Readme.md in the root of this repository or the wiki hosted on GitHub at
// https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions,
// and detailed documentation.
// INSTRUCTIONS
// See Readme.md in the root of this repository or the wiki hosted on GitHub at
// https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions,
// and detailed documentation.
//
// CONTACT
// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov)
// CONTACT
// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov)
//
// CONTRIBUTORS
// CT: Christopher Teubert (christopher.a.teubert@nasa.gov)
// JW: Jason Watkins (jason.w.watkins@nasa.gov)
// CONTRIBUTORS
// CT: Christopher Teubert (christopher.a.teubert@nasa.gov)
// JW: Jason Watkins (jason.w.watkins@nasa.gov)
// XPC Includes
#include "DataManager.h"
@@ -75,7 +75,8 @@
XPC::UDPSocket* sock = NULL;
double start,lap;
double start;
double lap;
static double timeConvert = 0.0;
int benchmarkingSwitch = 0; // 1 = time for operations, 2 = time for op + cycle;
int cyclesToClear = -1; // Clear message bus every n cycles. -1 == dont clear
@@ -90,40 +91,37 @@ static float XPCFlightLoopCallback(float inElapsedSinceLastCall, float inElapsed
PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc)
{
strcpy(outName, "X-Plane Connect [Version 1.0.1]");
strcpy(outName, "X-Plane Connect [Version 1.2.0]");
strcpy(outSig, "NASA.XPlaneConnect");
strcpy(outDesc, "X Plane Communications Toolbox\nCopyright (c) 2013-2015 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved.");
#if (__APPLE__)
if ( abs(timeConvert) <= 1e-9 ) // is about 0
{
mach_timebase_info_data_t timeBase;
(void)mach_timebase_info( &timeBase );
(void)mach_timebase_info(&timeBase);
timeConvert = (double)timeBase.numer /
(double)timeBase.denom /
1000000000.0;
}
#endif
XPC::Log::Initialize("1.0.1");
XPC::Log::WriteLine("[EXEC] Plugin Start");
XPC::Log::Initialize("1.1.1");
XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Start");
XPC::DataManager::Initialize();
float interval = -1; // Call every frame
void* refcon = NULL; // Don't pass anything to the callback directly
XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon);
return 1;
}
PLUGIN_API void XPluginStop(void)
{
XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL);
XPC::Log::WriteLine("[EXEC] Plugin Shutdown");
XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Shutdown");
XPC::Log::Close();
}
PLUGIN_API void XPluginDisable(void)
{
XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL);
// Close sockets
delete sock;
sock = NULL;
@@ -134,7 +132,7 @@ PLUGIN_API void XPluginDisable(void)
// Stop rendering waypoints to screen.
XPC::Drawing::ClearWaypoints();
XPC::Log::WriteLine("[EXEC] Plugin Disabled, sockets closed");
XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Disabled, sockets closed");
}
PLUGIN_API int XPluginEnable(void)
@@ -143,15 +141,17 @@ PLUGIN_API int XPluginEnable(void)
sock = new XPC::UDPSocket(RECVPORT);
XPC::MessageHandlers::SetSocket(sock);
XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened");
XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Enabled, sockets opened");
if (benchmarkingSwitch > 0)
{
XPC::Log::FormatLine("[EXEC] Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch);
XPC::Log::FormatLine(LOG_INFO, "EXEC", "Benchmarking Enabled (Verbosity: %i)", benchmarkingSwitch);
}
#if LOG_VERBOSITY > 0
XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY);
#endif
XPC::Log::FormatLine(LOG_INFO, "EXEC", "Debug Logging Enabled (Verbosity: %i)", LOG_LEVEL);
float interval = -1; // Call every frame
void* refcon = NULL; // Don't pass anything to the callback directly
XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon);
return 1;
}
@@ -173,7 +173,7 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
counter++;
if (benchmarkingSwitch > 1)
{
XPC::Log::FormatLine("Cycle time %.6f", inElapsedSinceLastCall);
XPC::Log::FormatLine(LOG_DEBUG, "EXEC", "Cycle time %.6f", inElapsedSinceLastCall);
}
for (int i = 0; i < OPS_PER_CYCLE; i++)
@@ -196,15 +196,15 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
{
#if (__APPLE__)
lap = (double)mach_absolute_time( ) * timeConvert;
diff_t = lap-start;
XPC::Log::FormatLine("[BENCH] Runtime %.6f",diff_t);
diff_t = lap - start;
XPC::Log::FormatLine(LOG_INFO, "EXEC", "Runtime %.6f", diff_t);
#endif
}
}
if (cyclesToClear != -1 && counter%cyclesToClear == 0)
{
XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer");
XPC::Log::WriteLine(LOG_DEBUG, "EXEC", "Cleared UDP Buffer");
delete sock;
sock = new XPC::UDPSocket(RECVPORT);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -10,7 +10,6 @@
BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */; };
BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; };
BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */; };
BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */; };
BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */; };
BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD311AE041A3007BA7DA /* Log.cpp */; };
BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD331AE041A3007BA7DA /* Message.cpp */; };
@@ -40,8 +39,6 @@
BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XPCPlugin.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataManager.cpp; sourceTree = "<group>"; };
BEABAD2C1AE041A3007BA7DA /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataManager.h; sourceTree = "<group>"; };
BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataMaps.cpp; sourceTree = "<group>"; };
BEABAD2E1AE041A3007BA7DA /* DataMaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataMaps.h; sourceTree = "<group>"; };
BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drawing.cpp; sourceTree = "<group>"; };
BEABAD301AE041A3007BA7DA /* Drawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Drawing.h; sourceTree = "<group>"; };
BEABAD311AE041A3007BA7DA /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = "<group>"; };
@@ -82,7 +79,6 @@
BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */,
BEDC620218EDF1A7005DB364 /* xplaneConnect.c */,
BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */,
BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */,
BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */,
BEABAD311AE041A3007BA7DA /* Log.cpp */,
BEABAD331AE041A3007BA7DA /* Message.cpp */,
@@ -97,7 +93,6 @@
children = (
BEDC620318EDF1A7005DB364 /* xplaneConnect.h */,
BEABAD2C1AE041A3007BA7DA /* DataManager.h */,
BEABAD2E1AE041A3007BA7DA /* DataMaps.h */,
BEABAD301AE041A3007BA7DA /* Drawing.h */,
BEABAD321AE041A3007BA7DA /* Log.h */,
BEABAD341AE041A3007BA7DA /* Message.h */,
@@ -192,7 +187,6 @@
BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */,
BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */,
BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */,
BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */,
BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */,
BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */,
BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */,
@@ -259,7 +253,7 @@
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++98";
CLANG_CXX_LIBRARY = "libc++";
CONFIGURATION_BUILD_DIR = ./Mac;
CONFIGURATION_BUILD_DIR = ./XPlaneConnect;
DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = "";
EXECUTABLE_EXTENSION = xpl;
@@ -371,7 +365,11 @@
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/xplaneConnect-asdjuezcjkhojuewbyxhyhabxfwc/Build/Products/Debug",
);
MACH_O_TYPE = mh_bundle;
<<<<<<< HEAD
ONLY_ACTIVE_ARCH = YES;
=======
ONLY_ACTIVE_ARCH = NO;
>>>>>>> master
PRODUCT_NAME = mac;
SDKROOT = macosx;
STRIP_INSTALLED_PRODUCT = YES;