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 #else
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 1000; timeout.tv_usec = 250000;
#endif #endif
if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)
{ {
@@ -367,33 +367,47 @@ int readDATA(XPCSocket sock, float data[][9], int rows)
/**** DREF functions ****/ /**** DREF functions ****/
/*****************************************************************************/ /*****************************************************************************/
int sendDREF(XPCSocket sock, const char* dref, float value[], int size) int sendDREF(XPCSocket sock, const char* dref, float value[], int size)
{
return sendDREFs(sock, &dref, &value, &size, 1);
}
int sendDREFs(XPCSocket sock, const char* drefs[], float* values[], int sizes[], int count)
{ {
// Setup command // Setup command
// 5 byte header + max 255 char dref name + max 255 values * 4 bytes per value = 1279 // Max size is technically unlimited.
unsigned char buffer[1279] = "DREF"; unsigned char buffer[65536] = "DREF";
int drefLen = strnlen(dref, 256); int pos = 5;
if (drefLen > 255) for (int i = 0; i < count; ++i)
{ {
printError("setDREF", "dref length is too long. Must be less than 256 characters."); int drefLen = strnlen(drefs[i], 256);
return -1; if (pos + drefLen + sizes[i] * 4 + 2 > 65536)
} {
if (size > 255) printError("sendDREF", "About to overrun the send buffer!");
{ return -4;
printError("setDREF", "size is too big. Must be less than 256."); }
return -2; if (drefLen > 255)
} {
int len = 7 + drefLen + size * sizeof(float); 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 dref to buffer // Copy values to buffer
buffer[5] = (unsigned char)drefLen; buffer[pos++] = (unsigned char)sizes[i];
memcpy(buffer + 6, dref, drefLen); memcpy(buffer + pos, values[i], sizes[i] * sizeof(float));
pos += sizes[i] * sizeof(float);
// Copy values to buffer }
buffer[6 + drefLen] = (unsigned char)size;
memcpy(buffer + 7 + drefLen, value, size * sizeof(float));
// Send command // Send command
if (sendUDP(sock, buffer, len) < 0) if (sendUDP(sock, buffer, pos) < 0)
{ {
printError("setDREF", "Failed to send command"); printError("setDREF", "Failed to send command");
return -3; return -3;
@@ -508,6 +522,38 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char
/*****************************************************************************/ /*****************************************************************************/
/**** POSI functions ****/ /**** 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) int sendPOSI(XPCSocket sock, float values[], int size, char ac)
{ {
// Validate input // Validate input
@@ -552,6 +598,41 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac)
/*****************************************************************************/ /*****************************************************************************/
/**** CTRL functions ****/ /**** 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) int sendCTRL(XPCSocket sock, float values[], int size, char ac)
{ {
// Validate input // Validate input
@@ -672,7 +753,7 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count)
memcpy(buffer + 7, points, ptLen); memcpy(buffer + 7, points, ptLen);
// Send Command // Send Command
if (sendUDP(sock, buffer, 40) < 0) if (sendUDP(sock, buffer, 7 + 12 * count) < 0)
{ {
printError("sendWYPT", "Failed to send command"); printError("sendWYPT", "Failed to send command");
return -2; return -2;
@@ -682,3 +763,31 @@ int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count)
/*****************************************************************************/ /*****************************************************************************/
/**** End Drawing functions ****/ /**** 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

@@ -62,6 +62,23 @@ typedef enum
XPC_WYPT_CLR = 3 XPC_WYPT_CLR = 3
} WYPT_OP; } 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 // Low Level UDP Functions
/// Opens a new connection to XPC on an OS chosen port. /// Opens a new connection to XPC on an OS chosen port.
@@ -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 /// 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 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. /// 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 sock The socket to use to send the command.
/// \param dref The name of the dataref to set. /// \param dref The name of the dataref to set.
/// \param values An array of values representing the data to set. /// \param value An array of values representing the data to set.
/// \param size The number of elements in values. /// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value. /// \returns 0 if successful, otherwise a negative value.
int sendDREF(XPCSocket sock, const char* dref, float value[], int size); int sendDREF(XPCSocket sock, const char* dref, float 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. /// Gets the value of the specified dataref.
/// ///
/// \details dref names and their associated data types can be found on the XPSDK wiki at /// \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 // 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. /// Sets the position and orientation of the specified aircraft.
/// ///
/// \param sock The socket to use to send the command. /// \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 // 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. /// Sets the control surfaces of the specified aircraft.
/// ///
/// \param sock The socket to use to send the command. /// \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. /// \returns 0 if successful, otherwise a negative value.
int sendTEXT(XPCSocket sock, char* msg, int x, int y); 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 /// Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
/// and all points are removed. /// 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. * @throws IOException If the command cannot be sent.
*/ */
public void sendDREF(String dref, float[] value) throws IOException 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 //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(); ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("DREF".getBytes(StandardCharsets.UTF_8)); os.write("DREF".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length os.write(0xFF); //Placeholder for message length
os.write(drefBytes.length); for(int i = 0; i < drefs.length; ++i)
os.write(drefBytes, 0, drefBytes.length); {
os.write(value.length); String dref = drefs[i];
os.write(bb.array()); 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()); 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. * 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>Throttle [-1, 1]</li>
* <li>Gear (0=up, 1=down)</li> * <li>Gear (0=up, 1=down)</li>
* <li>Flaps [0, 1]</li> * <li>Flaps [0, 1]</li>
* <li>Speedbrakes [-0.5, 1.5]</li>
* </ol> * </ol>
* <p> * <p>
* If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To * 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>Throttle [-1, 1]</li>
* <li>Gear (0=up, 1=down)</li> * <li>Gear (0=up, 1=down)</li>
* <li>Flaps [0, 1]</li> * <li>Flaps [0, 1]</li>
* <li>Speedbrakes [-0.5, 1.5]</li>
* </ol> * </ol>
* <p> * <p>
* If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To * 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) 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; cur += 1;
} }
else if (i >= values.length) else if (i >= values.length)
@@ -465,6 +542,44 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray()); 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. * Sets the position of the player ac.
* *
@@ -693,6 +808,26 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray()); 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 * Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
* and all points are removed. * and all points are removed.

View File

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

View File

@@ -1,16 +1,7 @@
function sendDREF( dref, value, socket ) 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
% Inputs % compatibility.
% 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 % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % [CT] Christopher Teubert (SGT, Inc.)
@@ -20,19 +11,8 @@ function sendDREF( dref, value, socket )
import XPlaneConnect.* import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var') if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[setDREF] ERROR: Multiple clients open. You must specify which client to use.'); sendDREFs(dref, value)
if isempty(clients) else
socket = openUDP(); sendDREFs(dref, value, socket)
else
socket = clients(1);
end
end end
%% Validate input
value = single(value);
%%Send command
socket.sendDREF(dref, value);

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('xplaneconnect Example Script-');
disp('Setting up Simulation'); disp('Setting up Simulation');
Socket = openUDP(49005); Socket = openUDP('127.0.0.1', 49009);
%% Ensure connected %% Ensure connected
getDREFs('sim/test/test_float') getDREFs('sim/test/test_float', Socket);
%% Set position of the player aircraft %% Set position of the player aircraft
disp('Setting position'); disp('Setting position');
pauseSim(1); pauseSim(1, Socket);
% Lat Lon Alt Pitch Roll Heading Gear % Lat Lon Alt Pitch Roll Heading Gear
POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1]; 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 % Lat Lon Alt Pitch Roll Heading Gear
POSI = [37.52465, -122.06899, 2500, 0, 20, 0, 1]; 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 %% Set rates
disp('Setting rates'); disp('Setting rates');
% Alpha Velocity PQR % Alpha Velocity PQR
@@ -32,30 +32,30 @@ data = struct('h',[18, 3, 16],...
'd',[0,-999,0,-999,-999,-999,-999,-999;... % Alpha data 'd',[0,-999,0,-999,-999,-999,-999,-999;... % Alpha data
130,130,130,130,-999,-999,-999,-999;... % Velocity data 130,130,130,130,-999,-999,-999,-999;... % Velocity data
0,0,0,-999,-999,-999,-999,-999]); % PQR data 0,0,0,-999,-999,-999,-999,-999]); % PQR data
sendDATA(data); sendDATA(data, Socket);
%% Set CTRL %% Set CTRL
% Throttle % Throttle
CTRL = [0,0,0,0.8]; CTRL = [0,0,0,0.8,0,0];
sendCTRL(CTRL); sendCTRL(CTRL, 0, Socket);
pause(5); pause(5);
pauseSim(0); pauseSim(0, Socket);
pause(5) % Run sim for 5 seconds pause(5) % Run sim for 5 seconds
%% Pause sim %% Pause sim
disp('Pausing simulation'); disp('Pausing simulation');
pauseSim(1); pauseSim(1, Socket);
pause(5); pause(5);
disp('Unpausing simulation'); disp('Unpausing simulation');
pauseSim(0); pauseSim(0, Socket);
pause(10) % Run sim for 10 seconds pause(10) % Run sim for 10 seconds
%% Use DREF to raise landing gear %% Use DREF to raise landing gear
disp('Raising gear'); disp('Raising gear');
gearDREF = 'sim/cockpit/switches/gear_handle_status'; gearDREF = 'sim/cockpit/switches/gear_handle_status';
sendDREF(gearDREF, 0); sendDREF(gearDREF, 0, Socket);
pause(10) % Run sim for 10 seconds pause(10) % Run sim for 10 seconds
%% Confirm gear and paus status by reading DREFs %% Confirm gear and paus status by reading DREFs
disp('Checking gear'); disp('Checking gear');
pauseDREF = 'sim/operation/override/override_planepath'; pauseDREF = 'sim/operation/override/override_planepath';
result = requestDREF({gearDREF, pauseDREF}); result = getDREFs({gearDREF, pauseDREF}, Socket);
if result{1} == 0 if result{1} == 0
disp('Gear stowed'); disp('Gear stowed');
else 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,9 +79,21 @@ class XPlaneConnect(object):
if port < 0 or port > 65535: if port < 0 or port > 65535:
raise ValueError("The specified port is not a valid port number.") raise ValueError("The specified port is not a valid port number.")
#Send command
buffer = struct.pack("<4sxH", "CONN", port) buffer = struct.pack("<4sxH", "CONN", port)
self.sendUDP(buffer) 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): def pauseSim(self, pause):
'''Pauses or un-pauses the physics simulation engine in X-Plane. '''Pauses or un-pauses the physics simulation engine in X-Plane.
@@ -132,6 +144,28 @@ class XPlaneConnect(object):
self.sendUDP(buffer) 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): def sendPOSI(self, values, ac = 0):
'''Sets position information on the specified aircraft. '''Sets position information on the specified aircraft.
@@ -143,8 +177,8 @@ class XPlaneConnect(object):
* Latitude (deg) * Latitude (deg)
* Longitude (deg) * Longitude (deg)
* Altitude (m above MSL) * Altitude (m above MSL)
* Roll (deg)
* Pitch (deg) * Pitch (deg)
* Roll (deg)
* True Heading (deg) * True Heading (deg)
* Gear (0=up, 1=down) * Gear (0=up, 1=down)
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft. 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) self.sendUDP(buffer)
# Controls # 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): def sendCTRL(self, values, ac = 0):
'''Sets control surface information on the specified aircraft. '''Sets control surface information on the specified aircraft.
@@ -213,26 +270,40 @@ class XPlaneConnect(object):
'''Sets the specified dataref to the specified value. '''Sets the specified dataref to the specified value.
Args: 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. values: Either a scalar value or a sequence of values.
''' '''
# Preconditions self.sendDREFs([dref], [values])
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.")
# Pack message def sendDREFs(self, drefs, values):
fmt = "" '''Sets the specified datarefs to the specified values.
buffer = None
if hasattr(values, "__len__"): Args:
if len(values) > 255: drefs: A list of names of the datarefs to set.
raise ValueError("values must have less than 256 items.") values: A list of scalar or vector values to set.
fmt = "<4sxB{0:d}sB{1:d}f".format(len(dref), len(values)) '''
buffer = struct.pack(fmt, "DREF", len(dref), dref, len(values), values) if len(drefs) != len(values):
else: raise ValueError("drefs and values must have the same number of elements.")
fmt = "<4sxB{0:d}sBf".format(len(dref))
buffer = struct.pack(fmt, "DREF", len(dref), dref, 1, values) 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
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 # Send
self.sendUDP(buffer) self.sendUDP(buffer)
@@ -300,6 +371,23 @@ class XPlaneConnect(object):
buffer = struct.pack("<4sxiiB" + str(msgLen) + "s", "TEXT", x, y, msgLen, msg) buffer = struct.pack("<4sxiiB" + str(msgLen) + "s", "TEXT", x, y, msgLen, msg)
self.sendUDP(buffer) 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): def sendWYPT(self, op, points):
'''Adds, removes, or clears waypoints. Waypoints are three dimensional points on or '''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 above the Earth's surface that are represented visually in the simulator. Each
@@ -324,3 +412,18 @@ class XPlaneConnect(object):
else: else:
buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points) buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points)
self.sendUDP(buffer) 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> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>3c7a940d-17c8-4e91-882f-9bc8b1d2f54b</ProjectGuid> <ProjectGuid>3c7a940d-17c8-4e91-882f-9bc8b1d2f54b</ProjectGuid>
<ProjectHome>.</ProjectHome> <ProjectHome>.</ProjectHome>
<StartupFile>src\example.py</StartupFile> <StartupFile>src\basicExample.py</StartupFile>
<SearchPath> <SearchPath>
</SearchPath> </SearchPath>
<WorkingDirectory>.</WorkingDirectory> <WorkingDirectory>.</WorkingDirectory>
@@ -24,8 +24,14 @@
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging> <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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\xpc.py" />
<Compile Include="src\example.py"> <Compile Include="src\basicExample.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
</ItemGroup> </ItemGroup>

View File

@@ -2,7 +2,7 @@
The X-Plane Connect (XPC) Toolbox is an open source research tool used to 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 interact with the commercial flight simulator software X-Plane. XPC allows users
to control aircraft and receive state information from aircraft simulated in 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 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 algorithms, simulate an active airspace, or generate out-the-window visuals for
in-house flight simulation software. Possible applications include active control in-house flight simulation software. Possible applications include active control

View File

@@ -21,9 +21,20 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\C\src\xplaneConnect.c" /> <ClCompile Include="..\..\C\src\xplaneConnect.c" />
<ClCompile Include="..\C Tests\main.c" /> <ClCompile Include="..\C Tests\main.c" />
<ClCompile Include="..\C Tests\Test.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h" /> <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> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{BC701AF4-552C-4C9D-82A1-B352542783A4}</ProjectGuid> <ProjectGuid>{BC701AF4-552C-4C9D-82A1-B352542783A4}</ProjectGuid>

View File

@@ -21,10 +21,40 @@
<ClCompile Include="..\..\C\src\xplaneConnect.c"> <ClCompile Include="..\..\C\src\xplaneConnect.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\C Tests\Test.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h"> <ClInclude Include="..\..\C\src\xplaneConnect.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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> </ItemGroup>
</Project> </Project>

View File

@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; BEB0F5071A28F9A3001975A6 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F5061A28F9A3001975A6 /* main.c */; };
BEB0F5091A28F9A3001975A6 /* C_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = BEB0F5081A28F9A3001975A6 /* C_Tests.1 */; }; BEB0F5091A28F9A3001975A6 /* C_Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = BEB0F5081A28F9A3001975A6 /* C_Tests.1 */; };
BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F50F1A28F9D5001975A6 /* xplaneConnect.c */; }; BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */ = {isa = PBXBuildFile; fileRef = BEB0F50F1A28F9D5001975A6 /* xplaneConnect.c */; };
@@ -27,6 +28,17 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference 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; }; 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>"; }; 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>"; }; BEB0F5081A28F9A3001975A6 /* C_Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = C_Tests.1; sourceTree = "<group>"; };
@@ -66,6 +78,17 @@
BEB0F5051A28F9A3001975A6 /* C Tests */ = { BEB0F5051A28F9A3001975A6 /* C Tests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( 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 */, BEB0F5061A28F9A3001975A6 /* main.c */,
BEB0F5081A28F9A3001975A6 /* C_Tests.1 */, BEB0F5081A28F9A3001975A6 /* C_Tests.1 */,
); );
@@ -126,6 +149,7 @@
BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */, BEB0F5111A28F9D5001975A6 /* xplaneConnect.c in Sources */,
BEB0F5121A28F9D5001975A6 /* xplaneConnect.h in Sources */, BEB0F5121A28F9D5001975A6 /* xplaneConnect.h in Sources */,
BEB0F5071A28F9A3001975A6 /* main.c in Sources */, BEB0F5071A28F9A3001975A6 /* main.c in Sources */,
BE7CF6311B0CFA34008B1E07 /* Test.c in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; 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; package gov.nasa.xpc.test;
import gov.nasa.xpc.ViewType;
import gov.nasa.xpc.WaypointOp; import gov.nasa.xpc.WaypointOp;
import gov.nasa.xpc.XPlaneConnect; 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) @Test(expected = IllegalArgumentException.class)
public void testSendDREF_NullDREF() throws IOException public void testSendDREF_NullDREF() throws IOException
{ {
@@ -650,4 +678,51 @@ public class XPlaneConnectTest
fail(); 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 %CTRLTest Summary of this function goes here
% Detailed explanation goes here % Detailed explanation goes here
%% Test player aircraft %% Test player aircraft

View File

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

View File

@@ -3,7 +3,7 @@ import unittest
import imp import imp
import time import time
import xpc xpc = imp.load_source('xpc', '../../Python/src/xpc.py')
class XPCTests(unittest.TestCase): class XPCTests(unittest.TestCase):
"""Tests the functionality of the XPlaneConnect class.""" """Tests the functionality of the XPlaneConnect class."""
@@ -117,6 +117,37 @@ class XPCTests(unittest.TestCase):
value = 0 value = 0
do_test() 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): def test_sendDATA(self):
# Setup # Setup
dref = "sim/aircraft/parts/acf_gear_deploy" dref = "sim/aircraft/parts/acf_gear_deploy"
@@ -171,6 +202,29 @@ class XPCTests(unittest.TestCase):
expected = 0.0 expected = 0.0
do_test() 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): def test_sendCTRL(self):
# Setup # Setup
@@ -239,6 +293,31 @@ class XPCTests(unittest.TestCase):
ctrl[6] = 0.0 ctrl[6] = 0.0
do_test() 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): def test_sendPOSI(self):
# Setup # Setup
drefs = ["sim/flightmodel/position/latitude",\ drefs = ["sim/flightmodel/position/latitude",\
@@ -288,6 +367,22 @@ class XPCTests(unittest.TestCase):
# Cleanup # Cleanup
client.close() 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): def test_sendWYPT(self):
# Setup # Setup
client = xpc.XPlaneConnect() client = xpc.XPlaneConnect()

View File

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

View File

@@ -1,25 +1,26 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
// //
//X-Plane API // X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. // 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 // 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, // associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, // 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 // 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 : // furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice, // * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research // * 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 // may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or // without specific prior written permission from the authors or
// Laminar Research, respectively. // Laminar Research, respectively.
#include "DataManager.h" #include "DataManager.h"
#include "Log.h" #include "Log.h"
#include "XPLMDataAccess.h" #include "XPLMDataAccess.h"
#include "XPLMGraphics.h" #include "XPLMGraphics.h"
#include <algorithm>
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#include <map> #include <map>
@@ -28,14 +29,17 @@ namespace XPC
{ {
using namespace std; using namespace std;
const size_t PLANE_COUNT = 20;
static map<DREF, XPLMDataRef> drefs; static map<DREF, XPLMDataRef> drefs;
static map<DREF, XPLMDataRef> mdrefs[20]; static map<DREF, XPLMDataRef> mdrefs[PLANE_COUNT];
static map<string, XPLMDataRef> sdrefs; static map<string, XPLMDataRef> sdrefs;
DREF XPData[134][8] = { DREF_None }; DREF XPData[134][8] = { DREF_None };
void DataManager::Initialize() 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_None, XPLMFindDataRef("sim/test/test_float")));
drefs.insert(make_pair(DREF_Pause, XPLMFindDataRef("sim/operation/override/override_planepath"))); 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"))); drefs.insert(make_pair(DREF_MP7Alt, XPLMFindDataRef("sim/multiplayer/position/plane7_el")));
char multi[256]; 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); sprintf(multi, "sim/multiplayer/position/plane%i_x", i);
mdrefs[i][DREF_LocalX] = XPLMFindDataRef(multi); mdrefs[i][DREF_LocalX] = XPLMFindDataRef(multi);
@@ -165,8 +169,8 @@ namespace XPC
sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i); sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i);
mdrefs[i][DREF_GearDeploy] = XPLMFindDataRef(multi); mdrefs[i][DREF_GearDeploy] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio", i); 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_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); sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio2", i);
mdrefs[i][DREF_FlapActual2] = XPLMFindDataRef(multi); mdrefs[i][DREF_FlapActual2] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i);
@@ -179,6 +183,7 @@ namespace XPC
mdrefs[i][DREF_Sweep] = XPLMFindDataRef(multi); mdrefs[i][DREF_Sweep] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i); sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i);
mdrefs[i][DREF_ThrottleActual] = XPLMFindDataRef(multi); 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); sprintf(multi, "sim/multiplayer/position/plane%i_yolk_pitch", i);
mdrefs[i][DREF_YokePitch] = XPLMFindDataRef(multi); mdrefs[i][DREF_YokePitch] = XPLMFindDataRef(multi);
sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i); sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i);
@@ -299,8 +304,9 @@ namespace XPC
XPData[26][0] = DREF_ThrottleActual; 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]; XPLMDataRef& xdref = sdrefs[dref];
if (xdref == NULL) if (xdref == NULL)
{ {
@@ -308,24 +314,18 @@ namespace XPC
} }
if (!xdref) // DREF does not exist if (!xdref) // DREF does not exist
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str());
Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str());
#endif
return 0; return 0;
} }
XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
Log::FormatLine("[DMAN] Get DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
#endif
// XPLMDataTypeID is a bit flag, so it may contain more than one of the // 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. // following types. We prefer types as close to float as possible.
if ((dataType & 2) == 2) // Float if ((dataType & 2) == 2) // Float
{ {
values[0] = XPLMGetDataf(xdref); values[0] = XPLMGetDataf(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
return 1; return 1;
} }
if ((dataType & 8) == 8) // Float array if ((dataType & 8) == 8) // Float array
@@ -333,99 +333,80 @@ namespace XPC
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0);
if (drefSize > size) if (drefSize > size)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
drefSize = size; drefSize = size;
} }
XPLMGetDatavf(xdref, values, 0, drefSize); XPLMGetDatavf(xdref, values, 0, drefSize);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
return drefSize; return drefSize;
} }
if ((dataType & 4) == 4) // Double if ((dataType & 4) == 4) // Double
{ {
values[0] = (float)XPLMGetDatad(xdref); values[0] = (float)XPLMGetDatad(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
return 1; return 1;
} }
if ((dataType & 1) == 1) // Integer if ((dataType & 1) == 1) // Integer
{ {
values[0] = (float)XPLMGetDatai(xdref); int iValue = XPLMGetDatai(xdref);
#if LOG_VERBOSITY > 4 values[0] = (float)iValue;
Log::FormatLine("[DMAN] -- value was %i", (int)values[0]); Log::FormatLine(LOG_INFO, "DMAN", " -- Real value was %i, cast to %f", iValue, values[0]);
#endif
return 1; return 1;
} }
if ((dataType & 16) == 16) // Integer array 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); int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0);
if (drefSize > size) if (drefSize > size)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
drefSize = size; drefSize = size;
} }
if (drefSize > 200) if (drefSize > TMP_SIZE)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer");
Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer"); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE);
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize); drefSize = TMP_SIZE;
#endif
drefSize = 200;
} }
XPLMGetDatavi(xdref, iValues, 0, drefSize); XPLMGetDatavi(xdref, iValues, 0, drefSize);
for (int i = 0; i < drefSize; ++i) for (int i = 0; i < drefSize; ++i)
{ {
values[i] = (float)iValues[i]; values[i] = (float)iValues[i];
} }
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
return drefSize; return drefSize;
} }
if ((dataType & 32) == 32) // Byte array 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); int drefSize = XPLMGetDatab(xdref, NULL, 0, 0);
if (drefSize > size) if (drefSize > size)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than available space");
Log::WriteLine("[DMAN] WARN: dref size is larger than available space"); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Available size : %i", drefSize, size);
Log::FormatLine(" Actual dref size: %i, Available size: %i", drefSize, size);
#endif
drefSize = size; drefSize = size;
} }
if (drefSize > 1024) if (drefSize > TMP_SIZE)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than temp buffer");
Log::WriteLine("[DMAN] WARN: dref size is larger than temp buffer"); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %u", drefSize, TMP_SIZE);
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize); drefSize = TMP_SIZE;
#endif
drefSize = 1024;
} }
XPLMGetDatab(xdref, bValues, 0, drefSize); XPLMGetDatab(xdref, bValues, 0, drefSize);
for (int i = 0; i < drefSize; ++i) for (int i = 0; i < drefSize; ++i)
{ {
values[i] = (float)bValues[i]; values[i] = (float)bValues[i];
} }
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
return drefSize; return drefSize;
} }
// No match // No match
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unrecognized data type.");
Log::WriteLine("[DMAN] ERROR: Unrecognized data type.");
#endif
return 0; return 0;
} }
@@ -433,9 +414,8 @@ namespace XPC
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
double value = XPLMGetDatad(xdref); double value = XPLMGetDatad(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i",
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
return value; return value;
} }
@@ -443,9 +423,8 @@ namespace XPC
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
float value = XPLMGetDataf(xdref); float value = XPLMGetDataf(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %f for a/c %i",
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %f for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
return value; return value;
} }
@@ -453,9 +432,8 @@ namespace XPC
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int value = XPLMGetDatai(xdref); int value = XPLMGetDatai(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result %i for a/c %i",
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result %i for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
return value; return value;
} }
@@ -463,9 +441,8 @@ namespace XPC
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int resultSize = XPLMGetDatavf(xdref, values, 0, size); int resultSize = XPLMGetDatavf(xdref, values, 0, size);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i",
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft); dref, xdref, resultSize, aircraft);
#endif
return resultSize; return resultSize;
} }
@@ -473,46 +450,46 @@ namespace XPC
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
int resultSize = XPLMGetDatavi(xdref, values, 0, size); int resultSize = XPLMGetDatavi(xdref, values, 0, size);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Get DREF %i (x:%X) result size %i for a/c %i",
Log::FormatLine("[DMAN] Get DREF %i (x:%X) result size %i for a/c %i", dref, xdref, resultSize, aircraft); dref, xdref, resultSize, aircraft);
#endif
return resultSize; return resultSize;
} }
void DataManager::Set(DREF dref, double value, char aircraft) void DataManager::Set(DREF dref, double value, char aircraft)
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i",
Log::FormatLine("[DMAN] Setting DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
XPLMSetDatad(xdref, value); XPLMSetDatad(xdref, value);
} }
void DataManager::Set(DREF dref, float value, char aircraft) void DataManager::Set(DREF dref, float value, char aircraft)
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %f for a/c %i",
Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %f for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
XPLMSetDataf(xdref, value); XPLMSetDataf(xdref, value);
} }
void DataManager::Set(DREF dref, int value, char aircraft) void DataManager::Set(DREF dref, int value, char aircraft)
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) to %i for a/c %i",
Log::FormatLine("[DMAN] Set DREF %i (x:%X) to %i for a/c %i", dref, xdref, value, aircraft); dref, xdref, value, aircraft);
#endif
XPLMSetDatai(xdref, value); XPLMSetDatai(xdref, value);
} }
void DataManager::Set(DREF dref, float values[], int size, char aircraft) void DataManager::Set(DREF dref, float values[], int size, char aircraft)
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i",
Log::FormatLine("[DMAN] Set DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft); dref, xdref, size, aircraft);
#endif
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); 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); drefSize = min(drefSize, size);
XPLMSetDatavf(xdref, values, 0, drefSize); XPLMSetDatavf(xdref, values, 0, drefSize);
} }
@@ -520,15 +497,19 @@ namespace XPC
void DataManager::Set(DREF dref, int values[], int size, char aircraft) void DataManager::Set(DREF dref, int values[], int size, char aircraft)
{ {
const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref]; const XPLMDataRef& xdref = aircraft == 0 ? drefs[dref] : mdrefs[aircraft][dref];
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %i (x:%X) (%i values) for a/c %i",
Log::FormatLine("[DMAN] Setting DREF %i (x:%X) (%i values) for a/c %i", dref, xdref, size, aircraft); dref, xdref, size, aircraft);
#endif
int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0); 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); drefSize = min(drefSize, size);
XPLMSetDatavi(xdref, values, 0, drefSize); 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]; XPLMDataRef& xdref = sdrefs[dref];
if (xdref == NULL) if (xdref == NULL)
@@ -538,149 +519,121 @@ namespace XPC
if (!xdref) if (!xdref)
{ {
// DREF does not exist // DREF does not exist
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str());
Log::FormatLine("[DMAN] ERROR: invalid DREF %s", dref.c_str());
#endif
return; return;
} }
if (isnan(values[0])) if (isnan(values[0]))
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Value must be a number (NaN received)");
Log::WriteLine("[DMAN] ERROR: Value must be a number (NaN received)");
#endif
return; return;
} }
XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
Log::FormatLine("[DMAN] Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType);
#endif
if ((dataType & 2) == 2) // Float if ((dataType & 2) == 2) // Float
{ {
XPLMSetDataf(xdref, values[0]); XPLMSetDataf(xdref, values[0]);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
} }
else if ((dataType & 8) == 8) // Float Array else if ((dataType & 8) == 8) // Float Array
{ {
int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0); int drefSize = XPLMGetDatavf(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize) if (size > drefSize)
{ {
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
} }
#endif
drefSize = min(drefSize, size); drefSize = min(drefSize, size);
XPLMSetDatavf(xdref, values, 0, drefSize); XPLMSetDatavf(xdref, values, 0, drefSize);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
} }
else if ((dataType & 4) == 4) // Double else if ((dataType & 4) == 4) // Double
{ {
XPLMSetDatad(xdref, values[0]); XPLMSetDatad(xdref, values[0]);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value was %f", values[0]);
Log::FormatLine("[DMAN] -- value was %f", values[0]);
#endif
} }
else if ((dataType & 1) == 1) // Integer else if ((dataType & 1) == 1) // Integer
{ {
XPLMSetDatai(xdref, (int)values[0]); XPLMSetDatai(xdref, (int)values[0]);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value was %i", (int)values[0]);
Log::FormatLine("[DMAN] -- value was %i", (int)values[0]);
#endif
} }
else if ((dataType & 16) == 16) // Integer Array 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); int drefSize = XPLMGetDatavi(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize) if (size > drefSize)
{ {
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
} }
#endif
drefSize = min(drefSize, size); drefSize = min(drefSize, size);
if (drefSize > 200) if (drefSize > TMP_SIZE)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size");
Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size."); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i",
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 200", drefSize); drefSize, TMP_SIZE);
#endif drefSize = TMP_SIZE;
drefSize = 200;
} }
for (int i = 0; i < drefSize; ++i) for (int i = 0; i < drefSize; ++i)
{ {
iValues[i] = (int)values[i]; iValues[i] = (int)values[i];
} }
XPLMSetDatavi(xdref, iValues, 0, drefSize); XPLMSetDatavi(xdref, iValues, 0, drefSize);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
} }
else if ((dataType & 32) == 32) // Byte Array 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); int drefSize = XPLMGetDatab(xdref, NULL, 0, 0);
#if LOG_VERBOSITY > 1
if (size > drefSize) if (size > drefSize)
{ {
Log::WriteLine("[DMAN] WARN: Provided size is larger than actual dref size"); Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger than actual dref size");
Log::FormatLine(" Actual dref size: %i, Provided buffer size: %i", drefSize, size); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Provided buffer size : %i",
drefSize, size);
} }
#endif
drefSize = min(drefSize, size); drefSize = min(drefSize, size);
if (drefSize > 1024) if (drefSize > TMP_SIZE)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_WARN, "DMAN", "Warning: dref size is larger temp buffer size");
Log::WriteLine("[DMAN] WARN: drefSize larger than temp buffer size."); Log::FormatLine(LOG_DEBUG, "DMAN", "Actual dref size : %i, Temp buffer size: %i",
Log::FormatLine(" Actual dref size: %i, Temp buffer size: 1024", drefSize); drefSize, TMP_SIZE);
#endif drefSize = TMP_SIZE;
drefSize = 1024;
} }
for (int i = 0; i < drefSize; ++i) for (int i = 0; i < drefSize; ++i)
{ {
bValues[i] = (char)values[i]; bValues[i] = (char)values[i];
} }
XPLMSetDatab(xdref, bValues, 0, drefSize); XPLMSetDatab(xdref, bValues, 0, drefSize);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "DMAN", " -- value count was %i", drefSize);
Log::FormatLine("[DMAN] -- value count was %i", drefSize);
#endif
} }
else else
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Unknown type.");
Log::WriteLine("[DMAN] ERROR: Unknown type.");
#endif
} }
#if LOG_VERBOSITY > 1
if (!XPLMCanWriteDataRef(xdref)) 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) void DataManager::SetGear(float gear, bool immediate, char aircraft)
{ {
#if LOG_VERBOSITY > 3 Log::FormatLine(LOG_INFO, "DMAN", "Setting gear (value:%f, immediate:%i) for aircraft %i",
Log::FormatLine("[DMAN] Setting gear (value:%f, immediate:%i) for aircraft %i", gear, immediate, aircraft); gear, immediate, aircraft);
#endif
if (isnan(gear) || gear < 0 || gear > 1) 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; return;
} }
if (isnan(gear) || gear < 0 || gear > 1)
if ((gear < -8.5 && gear > -9.5) || (gear < -997.9 && gear > -999.1))
{ {
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Gear value must be 0 or 1");
return; return;
} }
@@ -706,26 +659,23 @@ namespace XPC
void DataManager::SetPosition(float pos[3], char aircraft) void DataManager::SetPosition(float pos[3], char aircraft)
{ {
#if LOG_VERBOSITY > 3 Log::FormatLine(LOG_INFO, "DMAN", "Setting position (%f, %f, %f) for aircraft %i",
Log::FormatLine("[DMAN] Setting position (%f, %f, %f) for aircraft %i", pos[0], pos[1], pos[2], aircraft); pos[0], pos[1], pos[2], aircraft);
#endif
if (isnan(pos[0] + pos[1] + pos[2])) if (isnan(pos[0] + pos[1] + pos[2]))
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Position must be a number (NaN received)");
Log::WriteLine("[DMAN] ERROR: Position must be a number (NaN received)");
#endif
return; return;
} }
if (pos[0] < -997.9 && pos[0] > -999.1) if (IsDefault(pos[0]))
{ {
pos[0] = (float)GetDouble(DREF_Latitude, aircraft); 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); 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); pos[2] = (float)GetDouble(DREF_Elevation, aircraft);
} }
@@ -738,7 +688,6 @@ namespace XPC
Set(DREF_LocalY, local[1], aircraft); Set(DREF_LocalY, local[1], aircraft);
Set(DREF_LocalZ, local[2], aircraft); Set(DREF_LocalZ, local[2], aircraft);
// If the sim is unpaused, this will override the above settings. // 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_Latitude, (double)pos[0], aircraft);
Set(DREF_Longitude, (double)pos[1], aircraft); Set(DREF_Longitude, (double)pos[1], aircraft);
Set(DREF_Elevation, (double)pos[2], aircraft); Set(DREF_Elevation, (double)pos[2], aircraft);
@@ -746,27 +695,23 @@ namespace XPC
void DataManager::SetOrientation(float orient[3], char aircraft) void DataManager::SetOrientation(float orient[3], char aircraft)
{ {
#if LOG_VERBOSITY > 3 Log::FormatLine(LOG_INFO, "DMAN", "Setting orientation (%f, %f, %f) for aircraft %i",
Log::FormatLine("[DMAN] Setting orientation (%f, %f, %f) for aircraft %i",
orient[0], orient[1], orient[2], aircraft); orient[0], orient[1], orient[2], aircraft);
#endif
if (isnan(orient[0] + orient[1] + orient[2])) if (isnan(orient[0] + orient[1] + orient[2]))
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Orientation must be a number (NaN received)");
Log::WriteLine("[DMAN] ERROR: Orientation must be a number (NaN received)");
#endif
return; return;
} }
if (orient[0] < -997.9 && orient[0] > -999.1) if (IsDefault(orient[0]))
{ {
orient[0] = GetFloat(DREF_Pitch, aircraft); 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); 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); orient[2] = GetFloat(DREF_HeadingTrue, aircraft);
} }
@@ -782,16 +727,16 @@ namespace XPC
{ {
// Convert to Quaternions // Convert to Quaternions
// from: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane // 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 q[4];
float halfRad = 0.00872664625997F; float halfRad = 0.00872664625997F;
orient[2] = halfRad * orient[2]; float theta = halfRad * orient[0];
orient[0] = halfRad * orient[0]; float phi = halfRad * orient[1];
orient[1] = halfRad * orient[1]; float psi = halfRad * orient[2];
q[0] = cos(orient[2]) * cos(orient[0]) * cos(orient[1]) + sin(orient[2]) * sin(orient[0]) * sin(orient[1]); q[0] = cos(phi) * cos(theta) * cos(psi) + sin(phi) * sin(theta) * sin(psi);
q[1] = cos(orient[2]) * cos(orient[0]) * sin(orient[1]) - sin(orient[2]) * sin(orient[0]) * cos(orient[1]); q[1] = sin(phi) * cos(theta) * cos(psi) - cos(phi) * sin(theta) * sin(psi);
q[2] = cos(orient[2]) * sin(orient[0]) * cos(orient[1]) + sin(orient[2]) * cos(orient[0]) * sin(orient[1]); q[2] = cos(phi) * sin(theta) * cos(psi) + sin(phi) * cos(theta) * sin(psi);
q[3] = sin(orient[2]) * cos(orient[0]) * cos(orient[1]) - cos(orient[2]) * sin(orient[0]) * sin(orient[1]); 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 // If the sim is un-paused, this will overwrite the pitch/roll/yaw
// values set above. // values set above.
@@ -801,14 +746,14 @@ namespace XPC
void DataManager::SetFlaps(float value) void DataManager::SetFlaps(float value)
{ {
Log::FormatLine(LOG_INFO, "DMAN", "Setting flaps (value:%f)", value);
if (isnan(value)) if (isnan(value))
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Flap value must be a number (NaN received)");
Log::WriteLine("[DMAN] ERROR: Flap value must be a number (NaN received)");
#endif
return; return;
} }
if (value < -997.9 && value > -999.1) if (IsDefault(value))
{ {
return; return;
} }
@@ -819,4 +764,14 @@ namespace XPC
Set(DREF_FlapSetting, value); Set(DREF_FlapSetting, value);
Set(DREF_FlapActual, 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 // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_DATAMANAGER_H #ifndef XPCPLUGIN_DATAMANAGER_H_
#define XPC_DATAMANAGER_H #define XPCPLUGIN_DATAMANAGER_H_
#include <string> #include <string>
@@ -62,7 +62,7 @@ namespace XPC
DREF_L, DREF_L,
DREF_N, DREF_N,
//PQR (Angular Velocities) // PQR (Angular Velocities)
DREF_QRad = 1600, DREF_QRad = 1600,
DREF_PRad, DREF_PRad,
DREF_RRad, DREF_RRad,
@@ -139,13 +139,13 @@ namespace XPC
/// Maps X-Plane dataref lines to XPC DREF values. /// Maps X-Plane dataref lines to XPC DREF values.
extern DREF XPData[134][8]; 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 /// \author Jason Watkins
/// \version 1.0.1 /// \version 1.1
/// \since 1.0.0 /// \since 1.0.0
/// \date Intial Version: 2015-04-13 /// \date Intial Version: 2015-04-13
/// \date Last Updated: 2015-04-29 /// \date Last Updated: 2015-05-14
class DataManager class DataManager
{ {
public: public:
@@ -165,7 +165,7 @@ namespace XPC
/// \remarks The first time this method is called for a given dataref, it must /// \remarks The first time this method is called for a given dataref, it must
/// perform a relatively expensive lookup operation to translate the /// perform a relatively expensive lookup operation to translate the
/// given string into an X-Plane internal pointer. This value is cached, /// 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. /// the other methods in this class.
/// ///
/// \remarks For simplicity, this method is provided with only one output type. /// \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 /// doubles where high precision is required, using this method may result
/// in a loss of precision. In that case, consider using one of the /// in a loss of precision. In that case, consider using one of the
/// strongly typed methods instead. /// 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. /// Gets the value of a double dataref.
/// ///
@@ -294,7 +294,7 @@ namespace XPC
/// doubles where high precision is required, using this method may result /// doubles where high precision is required, using this method may result
/// in a loss of precision. In that case, consider using one of the /// in a loss of precision. In that case, consider using one of the
/// strongly typed methods instead. /// 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. /// 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). /// \param value The flaps settings. Should be between 0.0 (no flaps) and 1.0 (full flaps).
static void SetFlaps(float value); 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 #endif

View File

@@ -1,19 +1,19 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
// //
//X-Plane API // X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. // 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 // 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, // associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, // 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 // 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 : // furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice, // * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research // * 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 // may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or // without specific prior written permission from the authors or
// Laminar Research, respectively. // Laminar Research, respectively.
#include "Drawing.h" #include "Drawing.h"
#include "XPLMDisplay.h" #include "XPLMDisplay.h"
@@ -23,7 +23,7 @@
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <cstring> #include <cstring>
//OpenGL includes // OpenGL includes
#if IBM #if IBM
#include <windows.h> #include <windows.h>
#endif #endif
@@ -31,11 +31,11 @@
# include <OpenGL/gl.h> # include <OpenGL/gl.h>
#else #else
# include <GL/gl.h> # include <GL/gl.h>
#endif/*__APPLE__*/ #endif
namespace XPC namespace XPC
{ {
//Internal Structures // Internal Structures
typedef struct typedef struct
{ {
double x; double x;
@@ -43,7 +43,7 @@ namespace XPC
double z; double z;
} LocalPoint; } LocalPoint;
//Internal Memory // Internal Memory
static const size_t MSG_MAX = 1024; static const size_t MSG_MAX = 1024;
static const size_t MSG_LINE_MAX = MSG_MAX / 16; static const size_t MSG_LINE_MAX = MSG_MAX / 16;
static bool msgEnabled = false; static bool msgEnabled = false;
@@ -64,7 +64,9 @@ namespace XPC
XPLMDataRef planeYref; XPLMDataRef planeYref;
XPLMDataRef planeZref; 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) static int cmp(const void * a, const void * b)
{ {
std::size_t sa = *(size_t*)a; std::size_t sa = *(size_t*)a;
@@ -80,36 +82,42 @@ namespace XPC
return 0; 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) 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; const float TAN = 0.00436335F;
float h = d * TAN; float h = d * TAN;
glBegin(GL_QUAD_STRIP); 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);
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);
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);
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);
glVertex3f(x + h, y + h, z - h); glVertex3f(x + h, y + h, z - h);
glEnd(); glEnd();
glBegin(GL_QUADS); 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);
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); 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(); glEnd();
} }
/// Draws the string set by the TEXT command.
static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) static int MessageDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{ {
const int LINE_HEIGHT = 16;
XPLMDrawString(rgb, msgX, msgY, msgVal, NULL, xplmFont_Basic); 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) for (size_t i = 0; i < newLineCount; ++i)
{ {
XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic); XPLMDrawString(rgb, msgX, y, msgVal + newLines[i], NULL, xplmFont_Basic);
y -= 16; y -= LINE_HEIGHT;
} }
return 1; return 1;
} }
/// Draws waypoints.
static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon) static int RouteDrawCallback(XPLMDrawingPhase inPhase, int inIsBefore, void * inRefcon)
{ {
float px = XPLMGetDataf(planeXref); float px = XPLMGetDataf(planeXref);
float py = XPLMGetDataf(planeYref); float py = XPLMGetDataf(planeYref);
float pz = XPLMGetDataf(planeZref); float pz = XPLMGetDataf(planeZref);
//Convert to local // Convert to local
for (size_t i = 0; i < numWaypoints; ++i) for (size_t i = 0; i < numWaypoints; ++i)
{ {
Waypoint* g = &waypoints[i]; Waypoint* g = &waypoints[i];
@@ -146,7 +157,7 @@ namespace XPC
} }
//Draw posts // Draw posts
glColor3f(1.0F, 1.0F, 1.0F); glColor3f(1.0F, 1.0F, 1.0F);
glBegin(GL_LINES); glBegin(GL_LINES);
for (size_t i = 0; i < numWaypoints; ++i) for (size_t i = 0; i < numWaypoints; ++i)
@@ -157,7 +168,7 @@ namespace XPC
} }
glEnd(); glEnd();
//Draw route // Draw route
glColor3f(1.0F, 0.0F, 0.0F); glColor3f(1.0F, 0.0F, 0.0F);
glBegin(GL_LINE_STRIP); glBegin(GL_LINE_STRIP);
for (size_t i = 0; i < numWaypoints; ++i) for (size_t i = 0; i < numWaypoints; ++i)
@@ -167,7 +178,7 @@ namespace XPC
} }
glEnd(); glEnd();
//Draw markers // Draw markers
glColor3f(1.0F, 1.0F, 1.0F); glColor3f(1.0F, 1.0F, 1.0F);
for (size_t i = 0; i < numWaypoints; ++i) for (size_t i = 0; i < numWaypoints; ++i)
{ {
@@ -181,7 +192,7 @@ namespace XPC
return 1; return 1;
} }
//Public Functions // Public Functions
void Drawing::ClearMessage() void Drawing::ClearMessage()
{ {
XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); XPLMUnregisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
@@ -190,8 +201,7 @@ namespace XPC
void Drawing::SetMessage(int x, int y, char* msg) void Drawing::SetMessage(int x, int y, char* msg)
{ {
//Determine size of message and clear instead if the message string // Determine the size of the message and clear it if it is empty.
//is empty.
size_t len = strnlen(msg, MSG_MAX - 1); size_t len = strnlen(msg, MSG_MAX - 1);
if (len == 0) if (len == 0)
{ {
@@ -199,7 +209,7 @@ namespace XPC
return; return;
} }
//Set the message, location, and mark new lines. // Set the message, location, and mark new lines.
strncpy(msgVal, msg, len + 1); strncpy(msgVal, msg, len + 1);
newLineCount = 0; newLineCount = 0;
for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i) for (size_t i = 0; i < len && newLineCount < MSG_LINE_MAX; ++i)
@@ -213,7 +223,7 @@ namespace XPC
msgX = x < 0 ? 10 : x; msgX = x < 0 ? 10 : x;
msgY = y < 0 ? 600 : y; msgY = y < 0 ? 600 : y;
//Enable drawing if necessary // Enable drawing if necessary
if (!msgEnabled) if (!msgEnabled)
{ {
XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL); XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
@@ -258,7 +268,7 @@ namespace XPC
void Drawing::RemoveWaypoints(Waypoint points[], size_t numPoints) 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 delPoints[WAYPOINT_MAX];
size_t delPointsCur = 0; size_t delPointsCur = 0;
for (size_t i = 0; i < numPoints; ++i) 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); 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 copyCur = 0;
size_t count = delPointsCur; size_t count = delPointsCur;
delPointsCur = 0; delPointsCur = 0;

View File

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

View File

@@ -1,17 +1,20 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
#include "Log.h" #include "Log.h"
#include "XPLMUtilities.h" #include "XPLMUtilities.h"
#include <chrono>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#ifndef LIN
#include <chrono>
#endif
#include <iomanip> #include <iomanip>
#include <sstream> #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 // 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. // efficient to me to just use C-style IO and call std::fprintf directly.
namespace XPC namespace XPC
@@ -19,6 +22,20 @@ namespace XPC
static std::FILE* fd; static std::FILE* fd;
static void WriteTime(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; using namespace std::chrono;
system_clock::time_point now = system_clock::now(); system_clock::time_point now = system_clock::now();
@@ -28,17 +45,59 @@ namespace XPC
std::tm * tm = std::localtime(&now_tt); std::tm * tm = std::localtime(&now_tt);
std::stringstream ss; std::stringstream ss;
ss << std::setfill('0') << "[" ss << std::setfill('0')
<< std::setw(2) << tm->tm_hour << ":" << std::setw(2) << tm->tm_hour << ":"
<< std::setw(2) << tm->tm_min << ":" << std::setw(2) << tm->tm_min << ":"
<< std::setw(2) << tm->tm_sec << "." << std::setw(2) << tm->tm_sec << "."
<< std::setw(3) << ms.count() << "]"; << std::setw(3) << ms.count() << "|";
std::fprintf(fd, ss.str().c_str()); 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"); fd = std::fopen("XPCLog.txt", "w");
if (fd != NULL) 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()); if (level > LOG_LEVEL || !fd)
}
void Log::WriteLine(const char* value)
{
if (!fd)
{ {
return; return;
} }
WriteTime(fd); 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); std::fflush(fd);
} }
void Log::FormatLine(const char* format, ...) void Log::FormatLine(int level, const std::string& tag, std::string format, ...)
{ {
va_list args; va_list args;
if (!fd) if (level > LOG_LEVEL || !fd)
{ {
return; return;
} }
va_start(args, format); va_start(args, format);
WriteTime(fd); 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::fprintf(fd, "\n");
std::fflush(fd); std::fflush(fd);

View File

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

View File

@@ -1,8 +1,10 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
#include "Message.h" #include "Message.h"
#include "Log.h" #include "Log.h"
#include <cstring>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <string> #include <string>
@@ -11,79 +13,62 @@ namespace XPC
{ {
Message::Message() {} Message::Message() {}
Message Message::ReadFrom(UDPSocket& sock) Message Message::ReadFrom(const UDPSocket& sock)
{ {
Message m; Message m;
int len = sock.Read(m.buffer, bufferSize, &m.source); int len = sock.Read(m.buffer, bufferSize, &m.source);
m.size = len < 0 ? 0 : len; m.size = len < 0 ? 0 : len;
if (len > 0)
{
Log::FormatLine(LOG_TRACE, "MESG", "Read message with length %i", len);
}
return m; return m;
} }
unsigned long Message::GetMagicNumber() std::string Message::GetHead() const
{ {
if (size < 4) std::string val = size < 4 ? "" : std::string((char*)buffer, 4);
{ return val;
return 0;
}
return *((unsigned long*)buffer);
} }
std::string Message::GetHead() const unsigned char* Message::GetBuffer() const
{ {
if (size < 4) const unsigned char* val = size == 0 ? NULL : buffer;
{ return val;
return "";
}
return std::string((char*)buffer, 4);
} }
const unsigned char* Message::GetBuffer() std::size_t Message::GetSize() const
{
if (size == 0)
{
return NULL;
}
return buffer;
}
std::size_t Message::GetSize()
{ {
return size; return size;
} }
struct sockaddr Message::GetSource() struct sockaddr Message::GetSource() const
{ {
return source; return source;
} }
void Message::PrintToLog() void Message::PrintToLog() const
{ {
#if LOG_VERBOSITY > 4 using namespace std;
std::stringstream ss; stringstream ss;
ss << "[DEBUG]";
// Dump raw bytes to string // Dump raw bytes to string
ss << std::hex << std::setfill('0'); ss << std::hex << setfill('0');
for (int i = 0; i < size; ++i) 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.str("");
ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; ss << "Head: " << head << std::dec << " Size: " << GetSize();
switch (GetMagicNumber()) // Binary version of head 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 // Parse message data
float pitch = *((float*)(buffer + 5)); float pitch = *((float*)(buffer + 5));
float roll = *((float*)(buffer + 9)); float roll = *((float*)(buffer + 9));
@@ -96,89 +81,91 @@ namespace XPC
{ {
aircraft = buffer[26]; aircraft = buffer[26];
} }
ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")";
ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps;
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
break; }
} else if (head == "DATA")
case 0x41544144: // DATA
{ {
std::size_t numCols = (size - 5) / 36; size_t numCols = (size - 5) / 36;
float values[32][9]; float values[32][9];
for (int i = 0; i < numCols; ++i) for (int i = 0; i < numCols; ++i)
{ {
values[i][0] = buffer[5 + 36 * 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)"; ss << " (" << numCols << " lines)";
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
for (int i = 0; i < numCols; ++i) for (int i = 0; i < numCols; ++i)
{ {
ss.str(""); ss.str("");
ss << "\t#" << values[i][0]; ss << " #" << values[i][0];
for (int j = 1; j < 9; ++j) for (int j = 1; j < 9; ++j)
{ {
ss << " " << values[i][j]; ss << " " << values[i][j];
} }
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
} }
break; }
} else if (head == "DREF")
case 0x46455244: // DREF {
{ Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
Log::WriteLine(ss.str()); string dref((char*)buffer + 6, buffer[5]);
std::string dref((char*)buffer + 6, buffer[5]); Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str());
Log::FormatLine("-\tDREF (size %i) = %s", dref.length(), dref.c_str());
ss.str(""); ss.str("");
int values = buffer[6 + buffer[5]]; int values = buffer[6 + buffer[5]];
ss << "\tValues(size " << values << ") ="; ss << " Values(size " << values << ") =";
for (int i = 0; i < values; ++i) for (int i = 0; i < values; ++i)
{ {
ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i)); ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i));
} }
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
break;
} }
case 0x44544547: // GETD else if (head == "GETC" || head == "GETP")
{ {
Log::WriteLine(ss.str()); 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; int cur = 6;
for (int i = 0; i < buffer[5]; ++i) for (int i = 0; i < buffer[5]; ++i)
{ {
std::string dref((char*)buffer + cur + 1, buffer[cur]); string dref((char*)buffer + cur + 1, buffer[cur]);
Log::FormatLine("\t#%i/%i (size:%i) %s", i + 1, buffer[5], dref.length(), dref.c_str()); Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s",
i + 1, buffer[5], dref.length(), dref.c_str());
cur += 1 + buffer[cur]; cur += 1 + buffer[cur];
} }
break;
} }
case 0x49534F50: // POSI else if (head == "POSI")
{ {
char aircraft = buffer[5]; char aircraft = buffer[5];
float gear = *((float*)(buffer + 30)); float gear = *((float*)(buffer + 30));
float pos[3]; float pos[3];
float orient[3]; float orient[3];
std::memcpy(pos, buffer + 6, 12); memcpy(pos, buffer + 6, 12);
std::memcpy(orient, buffer + 18, 12); memcpy(orient, buffer + 18, 12);
ss << " AC:" << (int)aircraft; ss << " AC:" << (int)aircraft;
ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:("; ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:(";
ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:";
ss << gear; ss << gear;
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
break;
} }
case 0x554D4953: // SIMU else if (head == "SIMU")
{ {
ss << ' ' << (int)buffer[5]; ss << ' ' << (int)buffer[5];
Log::WriteLine(ss.str()); Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
break;
}
default:
{
ss << " UNKNOWN HEADER ";
Log::WriteLine(ss.str());
break;
}
} }
#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 // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_MESSAGE_H #ifndef XPCPLUGIN_MESSAGE_H_
#define XPC_MESSAGE_H #define XPCPLUGIN_MESSAGE_H_
#include "UDPSocket.h" #include "UDPSocket.h"
@@ -10,10 +10,10 @@ namespace XPC
/// Represents a message received from an XPC client. /// Represents a message received from an XPC client.
/// ///
/// \author Jason Watkins /// \author Jason Watkins
/// \version 1.0 /// \version 1.1
/// \since 1.0 /// \since 1.0
/// \date Intial Version: 2015-04-11 /// \date Intial Version: 2015-04-11
/// \date Last Updated: 2015-04-11 /// \date Last Updated: 2015-05-11
class Message class Message
{ {
public: public:
@@ -24,25 +24,25 @@ namespace XPC
/// \returns A message parsed from the data read from sock. If no /// \returns A message parsed from the data read from sock. If no
/// data was read or an error occurs, returns a message /// data was read or an error occurs, returns a message
/// with the size set to 0. /// with the size set to 0.
static Message ReadFrom(UDPSocket& sock); static Message ReadFrom(const UDPSocket& sock);
/// Gets the message header in binary form. /// Gets the message header in binary form.
unsigned long GetMagicNumber(); unsigned long GetMagicNumber() const;
/// Gets the message header. /// Gets the message header.
std::string GetHead(); std::string GetHead() const;
/// Gets the buffer underlying the message. /// Gets the buffer underlying the message.
const unsigned char* GetBuffer(); const unsigned char* GetBuffer() const;
/// Gets the size of the message in bytes. /// 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. /// 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. /// Prints the contents of the message to the XPC log.
void PrintToLog(); void PrintToLog() const;
private: private:
Message(); Message();

View File

@@ -1,10 +1,26 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // 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 "MessageHandlers.h"
#include "DataManager.h" #include "DataManager.h"
#include "Drawing.h" #include "Drawing.h"
#include "Log.h" #include "Log.h"
#include "XPLMUtilities.h"
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
@@ -19,6 +35,7 @@ namespace XPC
void MessageHandlers::SetSocket(UDPSocket* socket) void MessageHandlers::SetSocket(UDPSocket* socket)
{ {
Log::WriteLine(LOG_TRACE, "MSGH", "Setting socket");
MessageHandlers::sock = socket; MessageHandlers::sock = socket;
} }
@@ -26,6 +43,7 @@ namespace XPC
{ {
if (handlers.size() == 0) if (handlers.size() == 0)
{ {
Log::WriteLine(LOG_TRACE, "MSGH", "Initializing handlers");
// Common messages // Common messages
handlers.insert(std::make_pair("CONN", MessageHandlers::HandleConn)); handlers.insert(std::make_pair("CONN", MessageHandlers::HandleConn));
handlers.insert(std::make_pair("CTRL", MessageHandlers::HandleCtrl)); 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("SIMU", MessageHandlers::HandleSimu));
handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText)); handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText));
handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt)); handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt));
// Not implemented messages handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleView));
handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleUnknown)); handlers.insert(std::make_pair("GETC", MessageHandlers::HandleGetC));
handlers.insert(std::make_pair("GETP", MessageHandlers::HandleGetP));
// X-Plane data messages // X-Plane data messages
handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData));
handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData));
@@ -65,16 +84,14 @@ namespace XPC
std::string head = msg.GetHead(); std::string head = msg.GetHead();
if (head == "") if (head == "")
{ {
Log::WriteLine(LOG_WARN, "MSGH", "Warning: HandleMessage called with empty message.");
return; // No Message to handle return; // No Message to handle
} }
msg.PrintToLog();
// Set current connection // Set current connection
sockaddr sourceaddr = msg.GetSource(); sockaddr sourceaddr = msg.GetSource();
connectionKey = UDPSocket::GetHost(&sourceaddr); connectionKey = UDPSocket::GetHost(&sourceaddr);
#if LOG_VERBOSITY > 4 Log::FormatLine(LOG_INFO, "MSGH", "Handling message from %s", connectionKey.c_str());
Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str());
#endif
std::map<std::string, ConnectionInfo>::iterator conn = connections.find(connectionKey); std::map<std::string, ConnectionInfo>::iterator conn = connections.find(connectionKey);
if (conn == connections.end()) // New connection if (conn == connections.end()) // New connection
{ {
@@ -86,19 +103,17 @@ namespace XPC
connection.addr = sourceaddr; connection.addr = sourceaddr;
connection.getdCount = 0; connection.getdCount = 0;
connections[connectionKey] = connection; connections[connectionKey] = connection;
#if LOG_VERBOSITY > 2 Log::FormatLine(LOG_DEBUG, "MSGH", "New connection. ID=%u, Remote=%s",
Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str()); connection.id, connectionKey.c_str());
#endif
} }
else else
{ {
connection = (*conn).second; connection = (*conn).second;
#if LOG_VERBOSITY > 3 Log::FormatLine(LOG_DEBUG, "MSGH", "Existing connection. ID=%u, Remote=%s",
Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s",
connection.id, connectionKey.c_str()); connection.id, connectionKey.c_str());
#endif
} }
msg.PrintToLog();
// Check if there is a handler for this message type. If so, execute // Check if there is a handler for this message type. If so, execute
// that handler. Otherwise, execute the unknown message handler. // that handler. Otherwise, execute the unknown message handler.
std::map<std::string, MessageHandler>::iterator iter = handlers.find(head); 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(); const unsigned char* buffer = msg.GetBuffer();
@@ -122,23 +137,21 @@ namespace XPC
sockaddr* sa = &connection.addr; sockaddr* sa = &connection.addr;
switch (sa->sa_family) switch (sa->sa_family)
{ {
case AF_INET: case AF_INET: // IPV4 address
{ {
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa); sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa);
(*sin).sin_port = htons(port); (*sin).sin_port = htons(port);
break; break;
} }
case AF_INET6: case AF_INET6: // IPV6 addres
{ {
sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa); sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa);
(*sin).sin6_port = htons(port); (*sin).sin6_port = htons(port);
break; break;
} }
default: default:
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "CONN", "ERROR: Unknown address type.");
Log::WriteLine("[CONN] ERROR: Unknown address type.");
return; return;
#endif
} }
connections.erase(connectionKey); connections.erase(connectionKey);
connectionKey = UDPSocket::GetHost(&connection.addr); connectionKey = UDPSocket::GetHost(&connection.addr);
@@ -149,31 +162,26 @@ namespace XPC
response[5] = connection.id; response[5] = connection.id;
// Update log // Update log
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_TRACE, "CONN", "ID: %u New destination port: %u",
Log::FormatLine("[CONN] ID: %u New destination port: %u",
connection.id, port); connection.id, port);
#endif
// Send response // Send response
sock->SendTo(response, 6, &connection.addr); sock->SendTo(response, 6, &connection.addr);
} }
void MessageHandlers::HandleCtrl(Message& msg) void MessageHandlers::HandleCtrl(const Message& msg)
{ {
// Update Log // Update Log
#if LOG_VERBOSITY > 2 Log::FormatLine(LOG_TRACE, "CTRL", "Message Received (Conn %i)", connection.id);
Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id);
#endif
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
std::size_t size = msg.GetSize(); std::size_t size = msg.GetSize();
//Legacy packets that don't specify an aircraft number should be 26 bytes long. // 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 an A/C num should be 27 bytes. Packets specifying a speedbrake
// should be 31 bytes.
if (size != 26 && size != 27 && size != 31) if (size != 26 && size != 27 && size != 31)
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "CTRL", "ERROR: Unexpected message length (%i)", size);
Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size);
#endif
return; return;
} }
@@ -181,63 +189,63 @@ namespace XPC
float pitch = *((float*)(buffer + 5)); float pitch = *((float*)(buffer + 5));
float roll = *((float*)(buffer + 9)); float roll = *((float*)(buffer + 9));
float yaw = *((float*)(buffer + 13)); float yaw = *((float*)(buffer + 13));
float thr = *((float*)(buffer + 17)); float throttle = *((float*)(buffer + 17));
char gear = buffer[21]; char gear = buffer[21];
float flaps = *((float*)(buffer + 22)); float flaps = *((float*)(buffer + 22));
unsigned char aircraft = 0; unsigned char aircraftNumber = 0;
if (size >= 27) if (size >= 27)
{ {
aircraft = buffer[26]; aircraftNumber = buffer[26];
} }
float spdbrk = -998; float spdbrk = DataManager::GetDefaultValue();
if (size >= 31) if (size >= 31)
{ {
spdbrk = *((float*)(buffer + 27)); 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) for (int i = 0; i < 8; ++i)
{ {
thrArray[i] = thr; throttleArray[i] = throttle;
} }
DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft); DataManager::Set(DREF_ThrottleSet, throttleArray, 8, aircraftNumber);
DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft); DataManager::Set(DREF_ThrottleActual, throttleArray, 8, aircraftNumber);
if (aircraft == 0) 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) 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 // Parse data
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
@@ -245,59 +253,51 @@ namespace XPC
std::size_t numCols = (size - 5) / 36; std::size_t numCols = (size - 5) / 36;
if (numCols > 0) if (numCols > 0)
{ {
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_TRACE, "DATA", "Message Received (Conn %i)", connection.id);
Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id);
#endif
} }
else else
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_WARN, "DATA", "WARNING: Empty data packet received (Conn %i)", connection.id);
Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id);
#endif
return; return;
} }
if (numCols > 134) // Error. Will overflow values if (numCols > 134) // Error. Will overflow values
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "DATA", "ERROR: numCols to large.");
Log::FormatLine("[DATA] ERROR: numCols to large.");
#endif
return; return;
} }
float values[134][9]; float values[134][9];
for (int i = 0; i < numCols; ++i) for (int i = 0; i < numCols; ++i)
{ {
// 5 byte header + (9 * 4 = 36) bytes per row
values[i][0] = buffer[5 + 36 * i]; values[i][0] = buffer[5 + 36 * i];
memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float)); memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float));
} }
// Update log // Update log
float savedAlpha = -998; float savedAlpha = DataManager::GetDefaultValue();
float savedHPath = -998; float savedHPath = DataManager::GetDefaultValue();
for (int i = 0; i < numCols; ++i) for (int i = 0; i < numCols; ++i)
{ {
unsigned char dataRef = (unsigned char)values[i][0]; unsigned char dataRef = (unsigned char)values[i][0];
if (dataRef >= 134) if (dataRef >= 134)
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "DATA", "ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef);
Log::FormatLine("[DATA] ERROR: DataRef # must be between 0 - 134 (Received: %hi)", (int)dataRef);
#endif
continue; continue;
} }
switch (dataRef) switch (dataRef)
{ {
// TODO(jason-watkins): This currently overwrites the velocity several times. Should look into making this case smarter somehow.
case 3: // Velocity case 3: // Velocity
{ {
float theta = DataManager::GetFloat(DREF_Pitch); float theta = DataManager::GetFloat(DREF_Pitch);
float alpha = savedAlpha != -998 ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack); float alpha = DataManager::IsDefault(savedAlpha) ? savedAlpha : DataManager::GetFloat(DREF_AngleOfAttack);
float hpath = savedHPath != -998 ? savedHPath : DataManager::GetFloat(DREF_HPath); float hpath = DataManager::IsDefault(savedHPath) ? savedHPath : DataManager::GetFloat(DREF_HPath);
if (alpha != alpha || hpath != hpath) if (alpha != alpha || hpath != hpath)
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
break; break;
} }
const float deg2rad = 0.0174532925F; const float deg2rad = 0.0174532925F;
@@ -305,7 +305,7 @@ namespace XPC
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
{ {
float v = values[i][ind[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_LocalVX, v*cos((theta - alpha)*deg2rad)*sin(hpath*deg2rad));
DataManager::Set(DREF_LocalVY, v*sin((theta - alpha)*deg2rad)); DataManager::Set(DREF_LocalVY, v*sin((theta - alpha)*deg2rad));
@@ -316,27 +316,25 @@ namespace XPC
} }
case 17: // Orientation case 17: // Orientation
{ {
float orient[3]; float orientation[3];
orient[0] = values[i][1]; orientation[0] = values[i][1];
orient[1] = values[i][2]; orientation[1] = values[i][2];
orient[2] = values[i][3]; orientation[2] = values[i][3];
DataManager::SetOrientation(orient); DataManager::SetOrientation(orientation);
break; break;
} }
case 18: // Alpha, hpath etc. case 18: // Alpha, hpath etc.
{ {
if (values[i][1] != values[i][1] || values[i][3] != values[i][3]) if (values[i][1] != values[i][1] || values[i][3] != values[i][3])
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
break; break;
} }
if (values[i][1] != -998) if (!DataManager::IsDefault(values[i][1]))
{ {
savedAlpha = values[i][1]; savedAlpha = values[i][1];
} }
if (values[i][3] != -998) if (DataManager::IsDefault(values[i][3]))
{ {
savedHPath = values[i][3]; savedHPath = values[i][3];
} }
@@ -355,17 +353,15 @@ namespace XPC
{ {
if (values[i][1] != values[i][1]) if (values[i][1] != values[i][1])
{ {
#if LOG_VERBOSITY > 0 Log::WriteLine(LOG_ERROR, "DATA", "ERROR: Value must be a number (NaN received)");
Log::WriteLine("[DATA] ERROR: Value must be a number (NaN received)");
#endif
break; break;
} }
float thr[8]; float throttle[8];
for (int j = 0; j < 8; ++j) 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; break;
} }
default: // Non-Special dataRefs default: // Non-Special dataRefs
@@ -374,10 +370,8 @@ namespace XPC
memcpy(line, values[i] + 1, 8 * sizeof(float)); memcpy(line, values[i] + 1, 8 * sizeof(float));
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_ERROR, "DATA", "Setting Dataref %i.%i to %f", dataRef, j, line[j]);
Log::FormatLine("[DATA] Setting Dataref %i.%i to %f", dataRef, j, line[j]); // TODO(jason-watkins): Why is this a special case?
#endif
if (dataRef == 14 && j == 0) if (dataRef == 14 && j == 0)
{ {
DataManager::SetGear(line[0], true); DataManager::SetGear(line[0], true);
@@ -387,7 +381,7 @@ namespace XPC
DREF dref = XPData[dataRef][j]; DREF dref = XPData[dataRef][j];
if (dref == DREF_None) if (dref == DREF_None)
{ {
// TODO: Send single line instead! // TODO(jason): Send single line instead!
HandleXPlaneData(msg); HandleXPlaneData(msg);
} }
else 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(); const unsigned char* buffer = msg.GetBuffer();
unsigned char len = buffer[5]; std::size_t size = msg.GetSize();
std::string dref = std::string((char*)buffer + 6, len); 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]; unsigned char valueCount = buffer[pos++];
float* values = (float*)(buffer + 7 + len); if (pos + 4 * valueCount > size)
{
break;
}
float* values = (float*)(buffer + pos);
pos += 4 * valueCount;
#if LOG_VERBOSITY > 1 DataManager::Set(dref, values, valueCount);
Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str()); Log::FormatLine(LOG_DEBUG, "DREF", "Set %d values for %s", valueCount, dref.c_str());
#endif }
if (pos != size)
DataManager::Set(dref, values, valueCount); {
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(); const unsigned char* buffer = msg.GetBuffer();
unsigned char drefCount = buffer[5]; unsigned char drefCount = buffer[5];
if (drefCount == 0) // Use last request if (drefCount == 0) // Use last request
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_TRACE, "GETD",
Log::FormatLine("[GETD] DATA Requested: Repeat last request from connection %i (%i data refs)", "DATA Requested: Repeat last request from connection %i (%i data refs)",
connection.id, connection.getdCount); connection.id, connection.getdCount);
#endif
if (connection.getdCount == 0) // No previous request to use if (connection.getdCount == 0) // No previous request to use
{ {
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_ERROR, "GETD", "ERROR: No previous requests from connection %i.",
Log::FormatLine("[GETD] ERROR: No previous requests from connection %i.", connection.id); connection.id);
#endif
return; return;
} }
} }
else // New request else // New request
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_TRACE, "GETD", "DATA Requested: New Request for connection %i (%i data refs)",
Log::FormatLine("[GETD] DATA Requested: New Request for connection %i (%i data refs)",
connection.id, drefCount); connection.id, drefCount);
#endif
std::size_t ptr = 6; std::size_t ptr = 6;
for (int i = 0; i < drefCount; ++i) for (int i = 0; i < drefCount; ++i)
{ {
@@ -466,64 +510,84 @@ namespace XPC
sock->SendTo(response, cur, &connection.addr); 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 // Update log
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_TRACE, "POSI", "Message Received (Conn %i)", connection.id);
Log::FormatLine("[POSI] Message Received (Conn %i)", connection.id);
#endif
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
const std::size_t size = msg.GetSize(); const std::size_t size = msg.GetSize();
if (size < 34) if (size < 34)
{ {
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected at least 34)", size);
Log::FormatLine("[POSI] ERROR: Unexpected size: %i (Expected at least 34)", size);
#endif
return; return;
} }
char aircraft = buffer[5]; char aircraftNumber = buffer[5];
float gear = *((float*)(buffer + 30)); float gear = *((float*)(buffer + 30));
float pos[3]; float pos[3];
float orient[3]; float orient[3];
memcpy(pos, buffer + 6, 12); memcpy(pos, buffer + 6, 12);
memcpy(orient, buffer + 18, 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]; float ai[20];
std::size_t result = DataManager::GetFloatArray(DREF_PauseAI, ai, 20); std::size_t result = DataManager::GetFloatArray(DREF_PauseAI, ai, 20);
if (result == 20) // Only set values if they were retrieved successfully. 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::Set(DREF_PauseAI, ai, 0, 20);
} }
} }
DataManager::SetPosition(pos, aircraft); DataManager::SetPosition(pos, aircraftNumber);
DataManager::SetOrientation(orient, aircraft); DataManager::SetOrientation(orient, aircraftNumber);
if (gear != -1) 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 // Update log
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_TRACE, "SIMU", "Message Received (Conn %i)", connection.id);
Log::FormatLine("[SIMU] Message Received (Conn %i)", connection.id);
#endif
char v = msg.GetBuffer()[5]; char v = msg.GetBuffer()[5];
if (v < 0 || v > 2) if (v < 0 || v > 2)
{ {
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v);
Log::FormatLine("[SIMU] ERROR: Invalid argument: %i", v);
return; return;
#endif
} }
int value[20]; int value[20];
@@ -546,28 +610,24 @@ namespace XPC
// Set DREF // Set DREF
DataManager::Set(DREF_Pause, value, 20); DataManager::Set(DREF_Pause, value, 20);
#if LOG_VERBOSITY > 2
switch (v) switch (v)
{ {
case 0: case 0:
Log::WriteLine("[SIMU] Simulation Resumed"); Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed");
break; break;
case 1: case 1:
Log::WriteLine("[SIMU] Simulation Paused"); Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused");
break; break;
case 2: case 2:
Log::WriteLine("[SIMU] Simulation switched."); Log::FormatLine(LOG_INFO, "SIMU", "Simulation switched to %i", value[0]);
break; break;
} }
#endif
} }
void MessageHandlers::HandleText(Message& msg) void MessageHandlers::HandleText(const Message& msg)
{ {
// Update Log // Update Log
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_TRACE, "TEXT", "Message Received (Conn %i)", connection.id);
Log::FormatLine("[TEXT] Message Received (Conn %i)", connection.id);
#endif
std::size_t len = msg.GetSize(); std::size_t len = msg.GetSize();
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
@@ -575,18 +635,14 @@ namespace XPC
char text[256] = { 0 }; char text[256] = { 0 };
if (len < 14) if (len < 14)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_ERROR, "TEXT", "ERROR: Length less than 14 bytes");
Log::WriteLine("[TEXT] ERROR: Length less than 14 bytes");
#endif
return; return;
} }
size_t msgLen = (unsigned char)buffer[13]; size_t msgLen = (unsigned char)buffer[13];
if (msgLen == 0) if (msgLen == 0)
{ {
Drawing::ClearMessage(); Drawing::ClearMessage();
#if LOG_VERBOSITY > 2 Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text cleared");
Log::WriteLine("[TEXT] Text cleared");
#endif
} }
else else
{ {
@@ -594,18 +650,30 @@ namespace XPC
int y = *((int*)(buffer + 9)); int y = *((int*)(buffer + 9));
strncpy(text, (char*)buffer + 14, msgLen); strncpy(text, (char*)buffer + 14, msgLen);
Drawing::SetMessage(x, y, text); Drawing::SetMessage(x, y, text);
#if LOG_VERBOSITY > 2 Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text set");
Log::WriteLine("[TEXT] Text set");
#endif
} }
} }
void MessageHandlers::HandleWypt(Message& msg) void MessageHandlers::HandleView(const Message& msg)
{ {
// Update Log // Update Log
#if LOG_VERBOSITY > 0 Log::FormatLine(LOG_TRACE, "VIEW", "Message Received(Conn %i)", connection.id);
Log::FormatLine("[WYPT] Message Received (Conn %i)", connection.id);
#endif 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 // Parse data
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
@@ -622,9 +690,7 @@ namespace XPC
} }
// Perform operation // Perform operation
#if LOG_VERBOSITY > 2 Log::FormatLine(LOG_INFO, "WYPT", "Performing operation %i", op);
Log::FormatLine("[WYPT] Performing operation %i", op);
#endif
switch (op) switch (op)
{ {
case 1: case 1:
@@ -637,18 +703,14 @@ namespace XPC
Drawing::ClearWaypoints(); Drawing::ClearWaypoints();
break; break;
default: default:
#if LOG_VERBOSITY > 1 Log::FormatLine(LOG_ERROR, "WYPT", "ERROR: %i is not a valid operation.", op);
Log::FormatLine("[WYPT] ERROR: %i is not a valid operation.", op);
#endif
break; break;
} }
} }
void MessageHandlers::HandleXPlaneData(Message& msg) void MessageHandlers::HandleXPlaneData(const Message& msg)
{ {
#if LOG_VERBOSITY > 1 Log::WriteLine(LOG_TRACE, "MSGH", "Sending raw data to X - Plane");
Log::WriteLine("[MSGH] Sending raw data to X-Plane");
#endif
sockaddr_in loopback; sockaddr_in loopback;
loopback.sin_family = AF_INET; loopback.sin_family = AF_INET;
loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK); loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -656,11 +718,8 @@ namespace XPC
sock->SendTo(msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback); sock->SendTo(msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback);
} }
void MessageHandlers::HandleUnknown(Message& msg) void MessageHandlers::HandleUnknown(const Message& msg)
{ {
// UPDATE LOG Log::FormatLine(LOG_ERROR, "MSGH", "ERROR: Unknown packet type %s", msg.GetHead().c_str());
#if LOG_VERBOSITY > 0
Log::FormatLine("[EXEC] ERROR: Unknown packet type %s", msg.GetHead().c_str());
#endif
} }
} }

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPC_SOCKET_H #ifndef XPCPLUGIN_SOCKET_H_
#define XPC_SOCKET_H #define XPCPLUGIN_SOCKET_H_
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library #pragma comment(lib, "ws2_32.lib") // Winsock Library
#elif (__APPLE__ || __linux) #elif (__APPLE__ || __linux)
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -23,10 +23,10 @@ namespace XPC
/// data to XPC clients. /// data to XPC clients.
/// ///
/// \author Jason Watkins /// \author Jason Watkins
/// \version 1.0 /// \version 1.1
/// \since 1.0 /// \since 1.0
/// \date Intial Version: 2015-04-10 /// \date Intial Version: 2015-04-10
/// \date Last Updated: 2015-04-11 /// \date Last Updated: 2015-05-11
class UDPSocket class UDPSocket
{ {
public: public:
@@ -34,7 +34,7 @@ namespace XPC
/// specified receive port. /// specified receive port.
/// ///
/// \param recvPort The port on which this instance will receive data. /// \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. /// Closes the underlying socket for this instance.
~UDPSocket(); ~UDPSocket();
@@ -45,17 +45,17 @@ namespace XPC
/// \param buffer The array to copy the data into. /// \param buffer The array to copy the data into.
/// \param size The number of bytes to read. /// \param size The number of bytes to read.
/// \param remoteAddr When at least one byte is read, contains the address /// \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 /// \returns The number of bytes read, or a negative number if
/// an error occurs. /// an error occurs.
int Read(unsigned char* buffer, int size, sockaddr* remoteAddr); int Read(unsigned char* buffer, int size, sockaddr* remoteAddr) const;
/// Sends data to the specified remote endpoint. /// Sends data to the specified remote endpoint.
/// ///
/// \param data The data to be sent. /// \param data The data to be sent.
/// \param len The number of bytes to send. /// \param len The number of bytes to send.
/// \param remote The destination socket. /// \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. /// 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 // Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved. // National Aeronautics and Space Administration. All Rights Reserved.
// //
//DISCLAIMERS // DISCLAIMERS
// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, // 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 // EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF // THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY // 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, // 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 // 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, // 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 // HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT
// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING // SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." // 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 // 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 // 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 // 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 // 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 // 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, // 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 // TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE
// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. // IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT.
// //
//X-Plane API // X-Plane API
//Copyright(c) 2008, Sandy Barbour and Ben Supnik All rights reserved. // 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 // 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, // associated documentation files(the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, // 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 // 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 : // furnished to do so, subject to the following conditions :
// * Redistributions of source code must retain the above copyright notice, // * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// * Neither the names of the authors nor that of X - Plane or Laminar Research // * 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 // may be used to endorse or promote products derived from this software
// without specific prior written permission from the authors or // without specific prior written permission from the authors or
// Laminar Research, respectively. // Laminar Research, respectively.
// X-Plane Connect Plugin // X-Plane Connect Plugin
// //
// DESCRIPTION // DESCRIPTION
// XPCPlugin Facilitates Communication to and from the XPlane // XPCPlugin Facilitates Communication to and from the XPlane
// //
// INSTRUCTIONS // INSTRUCTIONS
// See Readme.md in the root of this repository or the wiki hosted on GitHub at // 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, // https://github.com/nasa/XPlaneConnect/wiki for requirements, installation instructions,
// and detailed documentation. // and detailed documentation.
// //
// CONTACT // CONTACT
// For questions email Christopher Teubert (christopher.a.teubert@nasa.gov) // For questions email Christopher Teubert (christopher.a.teubert@nasa.gov)
// //
// CONTRIBUTORS // CONTRIBUTORS
// CT: Christopher Teubert (christopher.a.teubert@nasa.gov) // CT: Christopher Teubert (christopher.a.teubert@nasa.gov)
// JW: Jason Watkins (jason.w.watkins@nasa.gov) // JW: Jason Watkins (jason.w.watkins@nasa.gov)
// XPC Includes // XPC Includes
#include "DataManager.h" #include "DataManager.h"
@@ -75,7 +75,8 @@
XPC::UDPSocket* sock = NULL; XPC::UDPSocket* sock = NULL;
double start,lap; double start;
double lap;
static double timeConvert = 0.0; static double timeConvert = 0.0;
int benchmarkingSwitch = 0; // 1 = time for operations, 2 = time for op + cycle; int benchmarkingSwitch = 0; // 1 = time for operations, 2 = time for op + cycle;
int cyclesToClear = -1; // Clear message bus every n cycles. -1 == dont clear int cyclesToClear = -1; // Clear message bus every n cycles. -1 == dont clear
@@ -90,7 +91,7 @@ static float XPCFlightLoopCallback(float inElapsedSinceLastCall, float inElapsed
PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) 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(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."); 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.");
@@ -98,32 +99,29 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc)
if ( abs(timeConvert) <= 1e-9 ) // is about 0 if ( abs(timeConvert) <= 1e-9 ) // is about 0
{ {
mach_timebase_info_data_t timeBase; mach_timebase_info_data_t timeBase;
(void)mach_timebase_info( &timeBase ); (void)mach_timebase_info(&timeBase);
timeConvert = (double)timeBase.numer / timeConvert = (double)timeBase.numer /
(double)timeBase.denom / (double)timeBase.denom /
1000000000.0; 1000000000.0;
} }
#endif #endif
XPC::Log::Initialize("1.0.1"); XPC::Log::Initialize("1.1.1");
XPC::Log::WriteLine("[EXEC] Plugin Start"); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Start");
XPC::DataManager::Initialize(); 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; return 1;
} }
PLUGIN_API void XPluginStop(void) PLUGIN_API void XPluginStop(void)
{ {
XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Shutdown");
XPC::Log::WriteLine("[EXEC] Plugin Shutdown");
XPC::Log::Close(); XPC::Log::Close();
} }
PLUGIN_API void XPluginDisable(void) PLUGIN_API void XPluginDisable(void)
{ {
XPLMUnregisterFlightLoopCallback(XPCFlightLoopCallback, NULL);
// Close sockets // Close sockets
delete sock; delete sock;
sock = NULL; sock = NULL;
@@ -134,7 +132,7 @@ PLUGIN_API void XPluginDisable(void)
// Stop rendering waypoints to screen. // Stop rendering waypoints to screen.
XPC::Drawing::ClearWaypoints(); 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) PLUGIN_API int XPluginEnable(void)
@@ -143,14 +141,16 @@ PLUGIN_API int XPluginEnable(void)
sock = new XPC::UDPSocket(RECVPORT); sock = new XPC::UDPSocket(RECVPORT);
XPC::MessageHandlers::SetSocket(sock); 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) 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(LOG_INFO, "EXEC", "Debug Logging Enabled (Verbosity: %i)", LOG_LEVEL);
XPC::Log::FormatLine("[EXEC] Debug Logging Enabled (Verbosity: %i)", LOG_VERBOSITY);
#endif float interval = -1; // Call every frame
void* refcon = NULL; // Don't pass anything to the callback directly
XPLMRegisterFlightLoopCallback(XPCFlightLoopCallback, interval, refcon);
return 1; return 1;
} }
@@ -173,7 +173,7 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
counter++; counter++;
if (benchmarkingSwitch > 1) 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++) for (int i = 0; i < OPS_PER_CYCLE; i++)
@@ -196,15 +196,15 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
{ {
#if (__APPLE__) #if (__APPLE__)
lap = (double)mach_absolute_time( ) * timeConvert; lap = (double)mach_absolute_time( ) * timeConvert;
diff_t = lap-start; diff_t = lap - start;
XPC::Log::FormatLine("[BENCH] Runtime %.6f",diff_t); XPC::Log::FormatLine(LOG_INFO, "EXEC", "Runtime %.6f", diff_t);
#endif #endif
} }
} }
if (cyclesToClear != -1 && counter%cyclesToClear == 0) if (cyclesToClear != -1 && counter%cyclesToClear == 0)
{ {
XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); XPC::Log::WriteLine(LOG_DEBUG, "EXEC", "Cleared UDP Buffer");
delete sock; delete sock;
sock = new XPC::UDPSocket(RECVPORT); 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 */; }; BE37D960187C8B0F0033B082 /* XPCPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */; };
BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; }; BE8361EF18C5591C00E9C923 /* mac.xpl in CopyFiles */ = {isa = PBXBuildFile; fileRef = D607B19909A556E400699BC3 /* mac.xpl */; };
BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */; }; 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 */; }; BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */; };
BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD311AE041A3007BA7DA /* Log.cpp */; }; BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD311AE041A3007BA7DA /* Log.cpp */; };
BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BEABAD331AE041A3007BA7DA /* Message.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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; BEABAD311AE041A3007BA7DA /* Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Log.cpp; sourceTree = "<group>"; };
@@ -82,7 +79,6 @@
BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */, BE37D95E187C8B0F0033B082 /* XPCPlugin.cpp */,
BEDC620218EDF1A7005DB364 /* xplaneConnect.c */, BEDC620218EDF1A7005DB364 /* xplaneConnect.c */,
BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */, BEABAD2B1AE041A3007BA7DA /* DataManager.cpp */,
BEABAD2D1AE041A3007BA7DA /* DataMaps.cpp */,
BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */, BEABAD2F1AE041A3007BA7DA /* Drawing.cpp */,
BEABAD311AE041A3007BA7DA /* Log.cpp */, BEABAD311AE041A3007BA7DA /* Log.cpp */,
BEABAD331AE041A3007BA7DA /* Message.cpp */, BEABAD331AE041A3007BA7DA /* Message.cpp */,
@@ -97,7 +93,6 @@
children = ( children = (
BEDC620318EDF1A7005DB364 /* xplaneConnect.h */, BEDC620318EDF1A7005DB364 /* xplaneConnect.h */,
BEABAD2C1AE041A3007BA7DA /* DataManager.h */, BEABAD2C1AE041A3007BA7DA /* DataManager.h */,
BEABAD2E1AE041A3007BA7DA /* DataMaps.h */,
BEABAD301AE041A3007BA7DA /* Drawing.h */, BEABAD301AE041A3007BA7DA /* Drawing.h */,
BEABAD321AE041A3007BA7DA /* Log.h */, BEABAD321AE041A3007BA7DA /* Log.h */,
BEABAD341AE041A3007BA7DA /* Message.h */, BEABAD341AE041A3007BA7DA /* Message.h */,
@@ -192,7 +187,6 @@
BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */, BEABAD3A1AE041A3007BA7DA /* Log.cpp in Sources */,
BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */, BEABAD3B1AE041A3007BA7DA /* Message.cpp in Sources */,
BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */, BEABAD3C1AE041A3007BA7DA /* MessageHandlers.cpp in Sources */,
BEABAD381AE041A3007BA7DA /* DataMaps.cpp in Sources */,
BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */, BEDC620418EDF1A7005DB364 /* xplaneConnect.c in Sources */,
BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */, BEABAD371AE041A3007BA7DA /* DataManager.cpp in Sources */,
BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */, BEABAD391AE041A3007BA7DA /* Drawing.cpp in Sources */,
@@ -259,7 +253,7 @@
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++98"; CLANG_CXX_LANGUAGE_STANDARD = "c++98";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CONFIGURATION_BUILD_DIR = ./Mac; CONFIGURATION_BUILD_DIR = ./XPlaneConnect;
DYLIB_COMPATIBILITY_VERSION = ""; DYLIB_COMPATIBILITY_VERSION = "";
DYLIB_CURRENT_VERSION = ""; DYLIB_CURRENT_VERSION = "";
EXECUTABLE_EXTENSION = xpl; EXECUTABLE_EXTENSION = xpl;
@@ -371,7 +365,11 @@
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/xplaneConnect-asdjuezcjkhojuewbyxhyhabxfwc/Build/Products/Debug", "$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/xplaneConnect-asdjuezcjkhojuewbyxhyhabxfwc/Build/Products/Debug",
); );
MACH_O_TYPE = mh_bundle; MACH_O_TYPE = mh_bundle;
<<<<<<< HEAD
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
=======
ONLY_ACTIVE_ARCH = NO;
>>>>>>> master
PRODUCT_NAME = mac; PRODUCT_NAME = mac;
SDKROOT = macosx; SDKROOT = macosx;
STRIP_INSTALLED_PRODUCT = YES; STRIP_INSTALLED_PRODUCT = YES;