1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
*.pyc
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
|
||||
316
Python/src/xplaneConnect.py
Normal file
316
Python/src/xplaneConnect.py
Normal file
@@ -0,0 +1,316 @@
|
||||
import socket
|
||||
import struct
|
||||
|
||||
class XPlaneConnect(object):
|
||||
'''XPlaneConnect (XPC) facilitates communication to and from the XPCPlugin.'''
|
||||
socket = None
|
||||
|
||||
# Basic Functions
|
||||
def __init__(self, xpHost = 'localhost', xpPort = 49009, port = 0, timeout = 100):
|
||||
'''Sets up a new connection to an X-Plane Connect plugin running in X-Plane.
|
||||
|
||||
Args:
|
||||
xpHost: The hostname of the machine running X-Plane.
|
||||
xpPort: The port on which the XPC plugin is listening. Usually 49007.
|
||||
port: The port which will be used to send and receive data.
|
||||
timeout: The period (in milliseconds) after which read attempts will fail.
|
||||
'''
|
||||
|
||||
# Validate parameters
|
||||
xpIP = None
|
||||
try:
|
||||
xpIP = socket.gethostbyname(xpHost)
|
||||
except:
|
||||
raise ValueError("Unable to resolve xpHost.")
|
||||
|
||||
if xpPort < 0 or xpPort > 65535:
|
||||
raise ValueError("The specified X-Plane port is not a valid port number.")
|
||||
if port < 0 or port > 65535:
|
||||
raise ValueError("The specified port is not a valid port number.")
|
||||
if timeout < 0:
|
||||
raise ValueError("timeout must be non-negative.")
|
||||
|
||||
# Setup XPlane IP and port
|
||||
self.xpDst = (xpIP, xpPort)
|
||||
|
||||
# Create and bind socket
|
||||
clientAddr = ("0.0.0.0", port)
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
self.socket.bind(clientAddr)
|
||||
timeout /= 1000.0
|
||||
self.socket.settimeout(timeout)
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
'''Closes the specified connection and releases resources associated with it.'''
|
||||
if self.socket is not None:
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
def sendUDP(self, buffer):
|
||||
'''Sends a message over the underlying UDP socket.'''
|
||||
# Preconditions
|
||||
if(len(buffer) == 0):
|
||||
raise ValueError("sendUDP: buffer is empty.")
|
||||
|
||||
self.socket.sendto(buffer, 0, self.xpDst)
|
||||
|
||||
def readUDP(self):
|
||||
'''Reads a message from the underlying UDP socket.'''
|
||||
return self.socket.recv(16384)
|
||||
|
||||
# Configuration
|
||||
def setCONN(self, port):
|
||||
'''Sets the port on which the client sends and receives data.
|
||||
|
||||
Args:
|
||||
port: The new port to use.
|
||||
'''
|
||||
#Validate parameters
|
||||
if port < 0 or port > 65535:
|
||||
raise ValueError("The specified port is not a valid port number.")
|
||||
|
||||
buffer = struct.pack("<4sxH", "CONN", port)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
def pauseSim(self, pause):
|
||||
'''Pauses or un-pauses the physics simulation engine in X-Plane.
|
||||
|
||||
Args:
|
||||
pause: True to pause the simulation; False to resume.
|
||||
'''
|
||||
pause_val = 0
|
||||
if pause:
|
||||
pause_val = 1
|
||||
|
||||
buffer = struct.pack("<4sxB", "SIMU", pause_val)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# X-Plane UDP Data
|
||||
def readDATA(self):
|
||||
'''Reads X-Plane data.
|
||||
|
||||
Returns: A 2 dimensional array containing 0 or more rows of data. Each array
|
||||
in the result will have 9 elements, the first of which is the row number which
|
||||
that array represents data for, and the rest of which are the data elements in
|
||||
that row.
|
||||
'''
|
||||
buffer = self.readUDP();
|
||||
if len(buffer) < 6:
|
||||
return None
|
||||
rows = (len(buffer) - 5) / 36
|
||||
data = []
|
||||
for i in range(rows):
|
||||
data.append(struct.unpack_from("9f", buffer, 5 + 36*i))
|
||||
return data
|
||||
|
||||
def sendDATA(self, data):
|
||||
'''Sends X-Plane data over the underlying UDP socket.
|
||||
|
||||
Args:
|
||||
data: An array of values representing data rows to be set. Each array in `data`
|
||||
should have 9 elements, the first of which is a row number in the range (0-134),
|
||||
and the rest of which are the values to set for that data row.
|
||||
'''
|
||||
if len(data) > 134:
|
||||
raise ValueError("Too many rows in data.")
|
||||
|
||||
buffer = struct.pack("<4sxB", "DATA", len(data))
|
||||
for row in data:
|
||||
if len(row) != 9:
|
||||
raise ValueError("Row does not contain exactly 9 values. <" + str(row) + ">")
|
||||
buffer += struct.pack("<I8f", *row)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Position
|
||||
def sendPOSI(self, values, ac = 0):
|
||||
'''Sets position information on the specified aircraft.
|
||||
|
||||
Args:
|
||||
values: The position values to set. `values` is a array containing up to
|
||||
7 elements. If less than 7 elements are specified or any elment is set to `-998`,
|
||||
those values will not be changed. The elements in `values` corespond to the
|
||||
following:
|
||||
* Latitude (deg)
|
||||
* Longitude (deg)
|
||||
* Altitude (m above MSL)
|
||||
* Roll (deg)
|
||||
* Pitch (deg)
|
||||
* True Heading (deg)
|
||||
* Gear (0=up, 1=down)
|
||||
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft.
|
||||
'''
|
||||
# Preconditions
|
||||
if len(values) < 1 or len(values) > 7:
|
||||
raise ValueError("Must have between 0 and 7 items in values.")
|
||||
if ac < 0 or ac > 20:
|
||||
raise ValueError("Aircraft number must be between 0 and 20.")
|
||||
|
||||
# Pack message
|
||||
buffer = struct.pack("<4sxB", "POSI", ac)
|
||||
for i in range(7):
|
||||
val = -998
|
||||
if i < len(values):
|
||||
val = values[i]
|
||||
buffer += struct.pack("<f", val)
|
||||
|
||||
# Send
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Controls
|
||||
def sendCTRL(self, values, ac = 0):
|
||||
'''Sets control surface information on the specified aircraft.
|
||||
|
||||
Args:
|
||||
values: The control surface values to set. `values` is a array containing up to
|
||||
6 elements. If less than 6 elements are specified or any elment is set to `-998`,
|
||||
those values will not be changed. The elements in `values` corespond to the
|
||||
following:
|
||||
* Latitudinal Stick [-1,1]
|
||||
* Longitudinal Stick [-1,1]
|
||||
* Rudder Pedals [-1, 1]
|
||||
* Throttle [-1, 1]
|
||||
* Gear (0=up, 1=down)
|
||||
* Flaps [0, 1]
|
||||
ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft.
|
||||
'''
|
||||
# Preconditions
|
||||
if len(values) < 1 or len(values) > 6:
|
||||
raise ValueError("Must have between 0 and 6 items in values.")
|
||||
if ac < 0 or ac > 20:
|
||||
raise ValueError("Aircraft number must be between 0 and 20.")
|
||||
|
||||
# Pack message
|
||||
buffer = struct.pack("<4sx", "CTRL")
|
||||
for i in range(6):
|
||||
val = -998
|
||||
if i < len(values):
|
||||
val = values[i]
|
||||
if i == 4:
|
||||
val = -1 if val == -998 else val
|
||||
buffer += struct.pack("B", val)
|
||||
else:
|
||||
buffer += struct.pack("<f", val)
|
||||
buffer += struct.pack("B", ac)
|
||||
|
||||
# Send
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# DREF Manipulation
|
||||
def sendDREF(self, dref, values):
|
||||
'''Sets the specified dataref to the specified value.
|
||||
|
||||
Args:
|
||||
dref: The name of the dataref to set.
|
||||
values: Either a scalar value or a sequence of values.
|
||||
'''
|
||||
# Preconditions
|
||||
if len(dref) == 0 or len(dref) > 255:
|
||||
raise ValueError("dref must be a non-empty string less than 256 characters.")
|
||||
if values == None:
|
||||
raise ValueError("values must be a scalar or sequence of floats.")
|
||||
|
||||
# Pack message
|
||||
fmt = ""
|
||||
buffer = None
|
||||
if hasattr(values, "__len__"):
|
||||
if len(values) > 255:
|
||||
raise ValueError("values must have less than 256 items.")
|
||||
fmt = "<4sxB{0:d}sB{1:d}f".format(len(dref), len(values))
|
||||
buffer = struct.pack(fmt, "DREF", len(dref), dref, len(values), values)
|
||||
else:
|
||||
fmt = "<4sxB{0:d}sBf".format(len(dref))
|
||||
buffer = struct.pack(fmt, "DREF", len(dref), dref, 1, values)
|
||||
|
||||
# Send
|
||||
self.sendUDP(buffer)
|
||||
|
||||
def getDREF(self, dref):
|
||||
'''Gets the value of an X-Plane dataref.
|
||||
|
||||
Args:
|
||||
dref: The name of the dataref to get.
|
||||
|
||||
Returns: A sequence of data representing the values of the requested dataref.
|
||||
'''
|
||||
return self.getDREFs([dref])[0]
|
||||
|
||||
def getDREFs(self, drefs):
|
||||
'''Gets the value of one or more X-Plane datarefs.
|
||||
|
||||
Args:
|
||||
drefs: The names of the datarefs to get.
|
||||
|
||||
Returns: A multidimensional sequence of data representing the values of the requested
|
||||
datarefs.
|
||||
'''
|
||||
# Send request
|
||||
buffer = struct.pack("<4sxB", "GETD", len(drefs))
|
||||
for dref in drefs:
|
||||
fmt = "<B{0:d}s".format(len(dref))
|
||||
buffer += struct.pack(fmt, len(dref), dref)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Read and parse response
|
||||
buffer = self.readUDP()
|
||||
resultCount = struct.unpack_from("B", buffer, 5)[0]
|
||||
offset = 6
|
||||
result = []
|
||||
for i in range(resultCount):
|
||||
rowLen = struct.unpack_from("B", buffer, offset)[0]
|
||||
offset += 1
|
||||
fmt = "<{0:d}f".format(rowLen)
|
||||
row = struct.unpack_from(fmt, buffer, offset)
|
||||
result.append(row)
|
||||
offset += rowLen * 4
|
||||
return result
|
||||
|
||||
# Drawing
|
||||
def sendTEXT(self, msg, x = -1, y = -1):
|
||||
'''Sets a message that X-Plane will display on the screen.
|
||||
|
||||
Args:
|
||||
msg: The string to display on the screen
|
||||
x: The distance in pixels from the left edge of the screen to display the
|
||||
message. A value of -1 indicates that the default horizontal position should
|
||||
be used.
|
||||
y: The distance in pixels from the bottom edge of the screen to display the
|
||||
message. A value of -1 indicates that the default vertical position should be
|
||||
used.
|
||||
'''
|
||||
if y < -1:
|
||||
raise ValueError("y must be greater than or equal to -1.")
|
||||
|
||||
if msg == None:
|
||||
msg = ""
|
||||
|
||||
msgLen = len(msg)
|
||||
buffer = struct.pack("<4sxiiB" + str(msgLen) + "s", "TEXT", x, y, msgLen, msg)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
def sendWYPT(self, op, points):
|
||||
'''Adds, removes, or clears waypoints. Waypoints are three dimensional points on or
|
||||
above the Earth's surface that are represented visually in the simulator. Each
|
||||
point consists of a latitude and longitude expressed in fractional degrees and
|
||||
an altitude expressed as meters above sea level.
|
||||
|
||||
Args:
|
||||
op: The operation to perform. Pass `1` to add waypoints,
|
||||
`2` to remove waypoints, and `3` to clear all waypoints.
|
||||
points: A sequence of floating point values representing latitude, longitude, and
|
||||
altitude triples. The length of this array should always be divisible by 3.
|
||||
'''
|
||||
if op < 1 or op > 3:
|
||||
raise ValueError("Invalid operation specified.")
|
||||
if len(points) % 3 != 0:
|
||||
raise ValueError("Invalid points. Points should be divisible by 3.")
|
||||
if len(points) / 3 > 255:
|
||||
raise ValueError("Too many points. You can only send 255 points at a time.")
|
||||
|
||||
if op == 3:
|
||||
buffer = struct.pack("<4sxBB", "WYPT", 3, 0)
|
||||
else:
|
||||
buffer = struct.pack("<4sxBB" + str(len(points)) + "f", "WYPT", op, len(points), *points)
|
||||
self.sendUDP(buffer)
|
||||
49
Python/xplaneConnect.pyproj
Normal file
49
Python/xplaneConnect.pyproj
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>3c7a940d-17c8-4e91-882f-9bc8b1d2f54b</ProjectGuid>
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>src/XPlaneConnect.py</StartupFile>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>XPlaneConnect</Name>
|
||||
<RootNamespace>XPlaneConnect</RootNamespace>
|
||||
<InterpreterId>{9a7a9026-48c1-4688-9d5d-e5699d47d074}</InterpreterId>
|
||||
<InterpreterVersion>2.7</InterpreterVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="src/XPlaneConnect.py" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\2.7" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<PtvsTargetsFile>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>
|
||||
</PropertyGroup>
|
||||
<Import Condition="Exists($(PtvsTargetsFile))" Project="$(PtvsTargetsFile)" />
|
||||
<Import Condition="!Exists($(PtvsTargetsFile))" Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
<!--<Target Name="CoreCompile" />-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
</Project>
|
||||
24
Python/xplaneConnect.sln
Normal file
24
Python/xplaneConnect.sln
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.31101.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "XPlaneConnect", "XPlaneConnect.pyproj", "{3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}"
|
||||
EndProject
|
||||
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Tests", "..\TestScripts\Python Tests\Tests.pyproj", "{6931EBB2-4E01-4C5A-86B6-668C0E75051B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
298
TestScripts/Python Tests/Tests.py
Normal file
298
TestScripts/Python Tests/Tests.py
Normal file
@@ -0,0 +1,298 @@
|
||||
import random
|
||||
import unittest
|
||||
import imp
|
||||
import time
|
||||
|
||||
xpc = imp.load_source('xplaneConnect', '../../Python/src/xplaneConnect.py')
|
||||
|
||||
class XPCTests(unittest.TestCase):
|
||||
"""Tests the functionality of the XPlaneConnect class."""
|
||||
|
||||
def test_init(self):
|
||||
try:
|
||||
client = xpc.XPlaneConnect()
|
||||
except:
|
||||
self.fail("Default constructor failed.")
|
||||
|
||||
try:
|
||||
client = xpc.XPlaneConnect("I'm not a real host")
|
||||
self.fail("Failed to catch invalid XP host.")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 90001)
|
||||
self.fail("Failed to catch invalid XP port.")
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
client = xpc.XPlaneConnect("127.0.0.1", -1)
|
||||
self.fail("Failed to catch invalid XP port.")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 49009, 90001)
|
||||
self.fail("Failed to catch invalid local port.")
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 49009, -1)
|
||||
self.fail("Failed to catch invalid XP port.")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 49009, 0, -1)
|
||||
self.fail("Failed to catch invalid timeout.")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def test_close(self):
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 49009, 49063)
|
||||
client.close()
|
||||
self.assertIsNone(client.socket)
|
||||
client = xpc.XPlaneConnect("127.0.0.1", 49009, 49063)
|
||||
|
||||
def test_send_read(self):
|
||||
# Init
|
||||
test = "\x00\x01\x02\x03\x05"
|
||||
|
||||
# Setup
|
||||
sender = xpc.XPlaneConnect("127.0.0.1", 49063, 49064)
|
||||
receiver = xpc.XPlaneConnect("127.0.0.1", 49009, 49063)
|
||||
|
||||
# Execution
|
||||
sender.sendUDP(test)
|
||||
buf = receiver.readUDP()
|
||||
|
||||
# Cleanup
|
||||
sender.close()
|
||||
receiver.close()
|
||||
|
||||
# Tests
|
||||
for a, b in zip(test, buf):
|
||||
self.assertEqual(a, b)
|
||||
|
||||
def test_getDREFs(self):
|
||||
# Setup
|
||||
client = xpc.XPlaneConnect()
|
||||
drefs = ["sim/cockpit/switches/gear_handle_status",\
|
||||
"sim/cockpit2/switches/panel_brightness_ratio"]
|
||||
|
||||
# Execution
|
||||
result = client.getDREFs(drefs)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Tests
|
||||
self.assertEqual(2, len(result))
|
||||
self.assertEqual(1, len(result[0]))
|
||||
self.assertEqual(4, len(result[1]))
|
||||
|
||||
def test_sendDREF(self):
|
||||
dref = "sim/cockpit/switches/gear_handle_status"
|
||||
value = None
|
||||
def do_test():
|
||||
# Setup
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.sendDREF(dref, value)
|
||||
result = client.getDREF(dref)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Tests
|
||||
self.assertEqual(1, len(result))
|
||||
self.assertEqual(value, result[0])
|
||||
|
||||
# Test 1
|
||||
value = 1
|
||||
do_test()
|
||||
|
||||
# Test 2
|
||||
value = 0
|
||||
do_test()
|
||||
|
||||
def test_sendDATA(self):
|
||||
# Setup
|
||||
dref = "sim/aircraft/parts/acf_gear_deploy"
|
||||
data = [[ 14, 1, 0, -998, -998, -998, -998, -998, -998 ]]
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.sendDATA(data)
|
||||
result = client.getDREF(dref)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
#Tests
|
||||
self.assertEqual(result[0], data[0][1])
|
||||
|
||||
def test_pauseSim(self):
|
||||
dref = "sim/operation/override/override_planepath"
|
||||
value = None
|
||||
expected = None
|
||||
def do_test():
|
||||
# Setup
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.pauseSim(value)
|
||||
result = client.getDREF(dref)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Test
|
||||
self.assertAlmostEqual(expected, result[0])
|
||||
|
||||
# Test 1
|
||||
value = True
|
||||
expected = 1.0
|
||||
do_test()
|
||||
|
||||
# Test 2
|
||||
value = False
|
||||
expected = 0.0
|
||||
do_test()
|
||||
|
||||
|
||||
def test_sendCTRL(self):
|
||||
# Setup
|
||||
drefs = ["sim/cockpit2/controls/yoke_pitch_ratio",\
|
||||
"sim/cockpit2/controls/yoke_roll_ratio",\
|
||||
"sim/cockpit2/controls/yoke_heading_ratio",\
|
||||
"sim/flightmodel/engine/ENGN_thro",\
|
||||
"sim/cockpit/switches/gear_handle_status",\
|
||||
"sim/flightmodel/controls/flaprqst"]
|
||||
ctrl = []
|
||||
|
||||
def do_test():
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.sendCTRL(ctrl)
|
||||
result = client.getDREFs(drefs)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Tests
|
||||
self.assertEqual(6, len(result))
|
||||
for i in range(6):
|
||||
self.assertAlmostEqual(ctrl[i], result[i][0], 4)
|
||||
|
||||
# Test 1
|
||||
ctrl = [ -1.0, -1.0, -1.0, 0.0, 1.0, 1.0 ]
|
||||
do_test()
|
||||
|
||||
# Test 2
|
||||
ctrl = [ 1.0, 1.0, 1.0, 0.0, 1.0, 0.5 ]
|
||||
do_test()
|
||||
|
||||
# Test 2
|
||||
ctrl = [ 0.0, 0.0, 0.0, 0.8, 1.0, 0.0 ]
|
||||
do_test()
|
||||
|
||||
def test_sendPOSI(self):
|
||||
# Setup
|
||||
drefs = ["sim/flightmodel/position/latitude",\
|
||||
"sim/flightmodel/position/longitude",\
|
||||
"sim/flightmodel/position/elevation",\
|
||||
"sim/flightmodel/position/theta",\
|
||||
"sim/flightmodel/position/phi",\
|
||||
"sim/flightmodel/position/psi",\
|
||||
"sim/cockpit/switches/gear_handle_status"]
|
||||
posi = None
|
||||
def do_test():
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.pauseSim(True)
|
||||
client.sendPOSI(posi)
|
||||
result = client.getDREFs(drefs)
|
||||
client.pauseSim(False)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Tests
|
||||
self.assertEqual(7, len(result))
|
||||
for i in range(7):
|
||||
self.assertAlmostEqual(posi[i], result[i][0], 4)
|
||||
|
||||
# Test 1
|
||||
posi = [ 37.524, -122.06899, 2500, 5, 7, 11, 1 ]
|
||||
do_test()
|
||||
|
||||
# Test 2
|
||||
posi = [ 38, -121.0, 2000, -10, 0, 0, 0 ]
|
||||
do_test()
|
||||
|
||||
def test_sendTEXT(self):
|
||||
# Setup
|
||||
client = xpc.XPlaneConnect()
|
||||
x = 200
|
||||
y = 700
|
||||
msg = "Python sendTEXT test message."
|
||||
|
||||
# Execution
|
||||
client.sendTEXT(msg, x, y)
|
||||
# NOTE: Manually verify that msg appears on the screen in X-Plane
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
def test_sendWYPT(self):
|
||||
# Setup
|
||||
client = xpc.XPlaneConnect()
|
||||
points = [\
|
||||
37.5245, -122.06899, 2500,\
|
||||
37.455397, -122.050037, 2500,\
|
||||
37.469567, -122.051411, 2500,\
|
||||
37.479376, -122.060509, 2300,\
|
||||
37.482237, -122.076130, 2100,\
|
||||
37.474881, -122.087288, 1900,\
|
||||
37.467660, -122.079391, 1700,\
|
||||
37.466298, -122.090549, 1500,\
|
||||
37.362562, -122.039223, 1000,\
|
||||
37.361448, -122.034416, 1000,\
|
||||
37.361994, -122.026348, 1000,\
|
||||
37.365541, -122.022572, 1000,\
|
||||
37.373727, -122.024803, 1000,\
|
||||
37.403869, -122.041283, 50,\
|
||||
37.418544, -122.049222, 6]
|
||||
|
||||
# Execution
|
||||
client.sendPOSI([37.5245, -122.06899, 2500])
|
||||
client.sendWYPT(3, [])
|
||||
client.sendWYPT(1, points)
|
||||
# NOTE: Manually verify that points appear on the screen in X-Plane
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
def test_setCONN(self):
|
||||
# Setup
|
||||
dref = "sim/cockpit/switches/gear_handle_status";
|
||||
client = xpc.XPlaneConnect()
|
||||
|
||||
# Execute
|
||||
client.setCONN(49055)
|
||||
result = client.getDREF(dref)
|
||||
|
||||
# Cleanup
|
||||
client.close()
|
||||
|
||||
# Test
|
||||
self.assertEqual(1, len(result))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
46
TestScripts/Python Tests/Tests.pyproj
Normal file
46
TestScripts/Python Tests/Tests.pyproj
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>6931ebb2-4e01-4c5a-86b6-668c0e75051b</ProjectGuid>
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>Tests.py</StartupFile>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>Tests</Name>
|
||||
<RootNamespace>Tests</RootNamespace>
|
||||
<InterpreterId>{9a7a9026-48c1-4688-9d5d-e5699d47d074}</InterpreterId>
|
||||
<InterpreterVersion>2.7</InterpreterVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Tests.py" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\2.7" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<PtvsTargetsFile>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>
|
||||
</PropertyGroup>
|
||||
<Import Condition="Exists($(PtvsTargetsFile))" Project="$(PtvsTargetsFile)" />
|
||||
<Import Condition="!Exists($(PtvsTargetsFile))" Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
|
||||
<!-- Uncomment the CoreCompile target to enable the Build command in
|
||||
Visual Studio and specify your pre- and post-build commands in
|
||||
the BeforeBuild and AfterBuild targets below. -->
|
||||
<!--<Target Name="CoreCompile" />-->
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user