Version 1.3-rc.1 (#138)
* Minor improvements to the handling of landing gear by the SetGear and HandlePOSI functions * sendPOSI command change (double for lat/lon/h) (#111) * Updated POSI to use doubles for lat/lon/alt, as step size for floats was unacceptably large at high longitudes. * Updated Java library for MATLAB, updated Java project, and updated Windows plugin binaries * Rolled back Java version to 1.7 for MATLAB compatibility * Update MessageHandlers.cpp * Update DataManager.cpp * Added pause functionality for individual a/c Adds new cases such that: 0: Unpauses all a/c 1: Pauses all a/c 2: Switches case for all a/c 100:119: Pauses a/c 0:19 200:219: Unpauses a/c 0:19 Updates log messages. Keeps the 0,1,2 arguments as they previously were. * Finished individual pause functionality * Update DataManager.cpp * Updated flags to allow for individual pause commands * Individual pause command documentation * Updated flags to allow for individual pause commands * Adding individual pause to documentation * Updated flags to allow for individual pause commands * Requested changes, cleaning * Update CMakeLists.txt Include "-fno-stack-protector" for linking and compiling for systems that do not have this have this flag as a default. * Enabling AI after setting position (#118) Previously, the code enabled the AI before setting the position of a/c, which negated its purpose. * Updated XPlane SDK to version 2.1.3 * Resolve function ambiguity for std::abs on mac Fixes #126 * Update copyright notice * Update Windows binaries * Update Linux binaries * Update version numbers * fix osx abs ambiguity (#142) * fix indexing error (#143) * Runway camera location control (#144) * update ignore * basics working * set cam pos remotely * log cam position * keep default behaviour, if short view message is received Compatibility with existing software * all to tabs * rename variable * option to use camera direction fields * Added UDP multicast for plugin discovery (#153) * Added Timer * Added UDPSocket::GetAddr * Added MessageHandlers::SendBeacon() * PoC of starting a timer on PluginEnable * C++11 * Added Timer to xcode project * C++11 in cmake * added Timer to cmake * use function pointer in Timer * moved Timer to namespace + wait for thread to join when stopping the timer * Windows: changed uint to unisgned short * Windows: Added Timer.h/cpp to project * GetAddr static * Send xplane and plugin version with BECN * SendBeacon with params * fixed file copyrights * Include functional to fix Linux compile error * review fixes * Send plugin receive port in BECN * review fixes * Fixed tabs vs spaces indentations (#157) * Java client BECN implementation (#155) * Added MS Azure Dev Ops CI integration (#162) * Do not build for i386 on macOS Use ARCHS_STANDARD to avoid the error “The i386 architecture is deprecated. You should update your ARCHS build setting to remove the i386 architecture.” * Fixed missing include The Visual Studio solution was not compiling * Fixed isnan ambigious refernce Ambigious reference when compiling on travis https://stackoverflow.com/questions/33770374/why-is-isnan-ambiguous-and-how-to-avoid-it * Set MSVC warning level to 3 Too many warnings were generated when building a Release build making Travis job to fail because of too much output * Use default toolset for Visual Studio AppVeyor recommends to set the default toolset * #include <cstdint> * Use MSVC ToolsVersion="14.0" # Conflicts: # xpcPlugin/xpcPlugin/xpcPlugin.vcxproj * Removed binaries from repository # Conflicts: # xpcPlugin/XPlaneConnect/64/win.xpl # xpcPlugin/XPlaneConnect/win.xpl * Added ms azure ci xcode linux linux + macos linux + macos removed branch filter win tests win tests Test all platfroms artifacts tests * output all binaries to ‘XPlaneConnect’ All platforms produce a binary in xpcPlugin/XPlaneConnect/ xpcPlugin/XPlaneConnect/64/ * Added ms azure GH deploy deploy stage GH release test trigger tags * Clean up yml file - Added variables - Added job decriptions * Ignore script to ignore .exp files + fixed output path * Renamed ms azure GH connection * Update service connection for azure pipeline * Added Python3 compatible xpc client. (#156) * Update Azure Pipelines service connection
This commit is contained in:
72
Python3/src/basicExample.py
Normal file
72
Python3/src/basicExample.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from time import sleep
|
||||
import xpc
|
||||
|
||||
def ex():
|
||||
print "X-Plane Connect example script"
|
||||
print "Setting up simulation"
|
||||
with xpc.XPlaneConnect() as client:
|
||||
# Verify connection
|
||||
try:
|
||||
# If X-Plane does not respond to the request, a timeout error
|
||||
# will be raised.
|
||||
client.getDREF("sim/test/test_float")
|
||||
except:
|
||||
print "Error establishing connection to X-Plane."
|
||||
print "Exiting..."
|
||||
return
|
||||
|
||||
# Set position of the player aircraft
|
||||
print "Setting position"
|
||||
# Lat Lon Alt Pitch Roll Yaw Gear
|
||||
posi = [37.524, -122.06899, 2500, 0, 0, 0, 1]
|
||||
client.sendPOSI(posi)
|
||||
|
||||
# Set position of a non-player aircraft
|
||||
print "Setting NPC position"
|
||||
# Lat Lon Alt Pitch Roll Yaw Gear
|
||||
posi = [37.52465, -122.06899, 2500, 0, 20, 0, 1]
|
||||
client.sendPOSI(posi, 1)
|
||||
|
||||
# Set angle of attack, velocity, and orientation using the DATA command
|
||||
print "Setting orientation"
|
||||
data = [\
|
||||
[18, 0, -998, 0, -998, -998, -998, -998, -998],\
|
||||
[ 3, 130, 130, 130, 130, -998, -998, -998, -998],\
|
||||
[16, 0, 0, 0, -998, -998, -998, -998, -998]\
|
||||
]
|
||||
client.sendDATA(data)
|
||||
|
||||
# Set control surfaces and throttle of the player aircraft using sendCTRL
|
||||
print "Setting controls"
|
||||
ctrl = [0.0, 0.0, 0.0, 0.8]
|
||||
client.sendCTRL(ctrl)
|
||||
|
||||
# Pause the sim
|
||||
print "Pausing"
|
||||
client.pauseSim(True)
|
||||
sleep(2)
|
||||
|
||||
# Toggle pause state to resume
|
||||
print "Resuming"
|
||||
client.pauseSim(False)
|
||||
|
||||
# Stow landing gear using a dataref
|
||||
print "Stowing gear"
|
||||
gear_dref = "sim/cockpit/switches/gear_handle_status"
|
||||
client.sendDREF(gear_dref, 0)
|
||||
|
||||
# Let the sim run for a bit.
|
||||
sleep(4)
|
||||
|
||||
# Make sure gear was stowed successfully
|
||||
gear_status = client.getDREF(gear_dref)
|
||||
if gear_status[0] == 0:
|
||||
print "Gear stowed"
|
||||
else:
|
||||
print "Error stowing gear"
|
||||
|
||||
print "End of Python client example"
|
||||
raw_input("Press any key to exit...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
ex()
|
||||
16
Python3/src/monitorExample.py
Normal file
16
Python3/src/monitorExample.py
Normal 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()
|
||||
82
Python3/src/playbackExample.py
Normal file
82
Python3/src/playbackExample.py
Normal 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()
|
||||
437
Python3/src/xpc.py
Normal file
437
Python3/src/xpc.py
Normal file
@@ -0,0 +1,437 @@
|
||||
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()
|
||||
|
||||
# Define __enter__ and __exit__ to support the `with` construct.
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
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.")
|
||||
|
||||
#Send command
|
||||
buffer = struct.pack(b"<4sxH", b"CONN", port)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
#Rebind socket
|
||||
clientAddr = ("0.0.0.0", port)
|
||||
timeout = self.socket.gettimeout()
|
||||
self.socket.close()
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
self.socket.bind(clientAddr)
|
||||
self.socket.settimeout(timeout)
|
||||
|
||||
#Read response
|
||||
buffer = self.socket.recv(1024)
|
||||
|
||||
def pauseSim(self, pause):
|
||||
"""Pauses or un-pauses the physics simulation engine in X-Plane.
|
||||
|
||||
Args:
|
||||
pause: True to pause the simulation; False to resume.
|
||||
"""
|
||||
pause = int(pause)
|
||||
if pause < 0 or pause > 2:
|
||||
raise ValueError("Invalid argument for pause command.")
|
||||
|
||||
buffer = struct.pack(b"<4sxB", b"SIMU", pause)
|
||||
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(b"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(b"<4sx", b"DATA")
|
||||
for row in data:
|
||||
if len(row) != 9:
|
||||
raise ValueError("Row does not contain exactly 9 values. <" + str(row) + ">")
|
||||
buffer += struct.pack(b"<I8f", *row)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Position
|
||||
def getPOSI(self, ac=0):
|
||||
"""Gets position information for the specified aircraft.
|
||||
|
||||
Args:
|
||||
ac: The aircraft to get the position of. 0 is the main/player aircraft.
|
||||
"""
|
||||
# Send request
|
||||
buffer = struct.pack(b"<4sxB", b"GETP", ac)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Read response
|
||||
resultBuf = self.readUDP()
|
||||
if len(resultBuf) != 34:
|
||||
raise ValueError("Unexpected response length.")
|
||||
|
||||
result = struct.unpack(b"<4sxBfffffff", resultBuf)
|
||||
if result[0] != b"POSI":
|
||||
raise ValueError("Unexpected header: " + result[0])
|
||||
|
||||
# Drop the header & ac from the return value
|
||||
return result[2:]
|
||||
|
||||
def sendPOSI(self, values, ac=0):
|
||||
"""Sets position information on the specified aircraft.
|
||||
|
||||
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)
|
||||
* Pitch (deg)
|
||||
* Roll (deg)
|
||||
* True Heading (deg)
|
||||
* Gear (0=up, 1=down)
|
||||
ac: The aircraft to set the position 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(b"<4sxB", b"POSI", ac)
|
||||
for i in range(7):
|
||||
val = -998
|
||||
if i < len(values):
|
||||
val = values[i]
|
||||
buffer += struct.pack(b"<f", val)
|
||||
|
||||
# Send
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Controls
|
||||
def getCTRL(self, ac=0):
|
||||
"""Gets the control surface information for the specified aircraft.
|
||||
|
||||
Args:
|
||||
ac: The aircraft to get the control surfaces of. 0 is the main/player aircraft.
|
||||
"""
|
||||
# Send request
|
||||
buffer = struct.pack(b"<4sxB", b"GETC", ac)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Read response
|
||||
resultBuf = self.readUDP()
|
||||
if len(resultBuf) != 31:
|
||||
raise ValueError("Unexpected response length.")
|
||||
|
||||
result = struct.unpack(b"<4sxffffbfBf", resultBuf)
|
||||
if result[0] != b"CTRL":
|
||||
raise ValueError("Unexpected header: " + result[0])
|
||||
|
||||
# Drop the header from the return value
|
||||
result =result[1:7] + result[8:]
|
||||
return result
|
||||
|
||||
def sendCTRL(self, values, ac=0):
|
||||
"""Sets control surface information on the specified aircraft.
|
||||
|
||||
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]
|
||||
* Speedbrakes [-0.5, 1.5]
|
||||
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 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(b"<4sx", b"CTRL")
|
||||
for i in range(6):
|
||||
val = -998
|
||||
if i < len(values):
|
||||
val = values[i]
|
||||
if i == 4:
|
||||
val = -1 if (abs(val + 998) < 1e-4) else val
|
||||
buffer += struct.pack(b"b", val)
|
||||
else:
|
||||
buffer += struct.pack(b"<f", val)
|
||||
|
||||
buffer += struct.pack(b"B", ac)
|
||||
if len(values) == 7:
|
||||
buffer += struct.pack(b"<f", values[6])
|
||||
|
||||
# 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 datarefs to set.
|
||||
values: Either a scalar value or a sequence of values.
|
||||
"""
|
||||
self.sendDREFs([dref], [values])
|
||||
|
||||
def sendDREFs(self, drefs, values):
|
||||
"""Sets the specified datarefs to the specified values.
|
||||
|
||||
Args:
|
||||
drefs: A list of names of the datarefs to set.
|
||||
values: A list of scalar or vector values to set.
|
||||
"""
|
||||
if len(drefs) != len(values):
|
||||
raise ValueError("drefs and values must have the same number of elements.")
|
||||
|
||||
buffer = struct.pack(b"<4sx", b"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 is 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.encode(), len(dref), dref.encode(), len(value), value)
|
||||
else:
|
||||
fmt = "<B{0:d}sBf".format(len(dref))
|
||||
buffer += struct.pack(fmt.encode(), len(dref), dref.encode(), 1, value)
|
||||
|
||||
# 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(b"<4sxB", b"GETD", len(drefs))
|
||||
for dref in drefs:
|
||||
fmt = "<B{0:d}s".format(len(dref))
|
||||
buffer += struct.pack(fmt.encode(), len(dref), dref.encode())
|
||||
self.sendUDP(buffer)
|
||||
|
||||
# Read and parse response
|
||||
buffer = self.readUDP()
|
||||
resultCount = struct.unpack_from(b"B", buffer, 5)[0]
|
||||
offset = 6
|
||||
result = []
|
||||
for i in range(resultCount):
|
||||
rowLen = struct.unpack_from(b"B", buffer, offset)[0]
|
||||
offset += 1
|
||||
fmt = "<{0:d}f".format(rowLen)
|
||||
row = struct.unpack_from(fmt.encode(), 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)
|
||||
|
||||
# TODO: Multiple byte conversions
|
||||
buffer = struct.pack(b"<4sxiiB" + (str(msgLen) + "s").encode(), b"TEXT", x, y, msgLen, msg.encode())
|
||||
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(b"<4sxi", b"VIEW", view)
|
||||
|
||||
# Send message
|
||||
self.sendUDP(buffer)
|
||||
|
||||
def sendWYPT(self, op, points):
|
||||
"""Adds, removes, or clears waypoints. Waypoints are three dimensional points on or
|
||||
above the Earth's surface that are represented visually in the simulator. Each
|
||||
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(b"<4sxBB", b"WYPT", 3, 0)
|
||||
else:
|
||||
buffer = struct.pack(("<4sxBB" + str(len(points)) + "f").encode(), b"WYPT", op, len(points), *points)
|
||||
self.sendUDP(buffer)
|
||||
|
||||
|
||||
class ViewType(object):
|
||||
Forwards = 73
|
||||
Down = 74
|
||||
Left = 75
|
||||
Right = 76
|
||||
Back = 77
|
||||
Tower = 78
|
||||
Runway = 79
|
||||
Chase = 80
|
||||
Follow = 81
|
||||
FollowWithPanel = 82
|
||||
Spot = 83
|
||||
FullscreenWithHud = 84
|
||||
FullscreenNoHud = 85
|
||||
58
Python3/xplaneConnect.pyproj
Normal file
58
Python3/xplaneConnect.pyproj
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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\basicExample.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\playbackExample.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="src\monitorExample.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="src\xpc.py" />
|
||||
<Compile Include="src\basicExample.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</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
Python3/xplaneConnect.sln
Normal file
24
Python3/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
|
||||
Reference in New Issue
Block a user