Merge branch 'feature/consistency' into develop

This commit is contained in:
Jason Watkins
2015-04-21 15:58:44 -07:00
51 changed files with 1812 additions and 2172 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +1,255 @@
//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
// //
// xplaneConnect.h //DISCLAIMERS
// // No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
// Created by Chris Teubert on 12/2/13. // 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 xplaneConnect_h
#define xplaneConnect_h
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifndef xplaneConnect_h #include "stdlib.h"
#define xplaneConnect_h #ifdef _WIN32 /* WIN32 SYSTEM */
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#elif (__APPLE__ || __linux)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#ifdef _WIN32 /* WIN32 SYSTEM */ typedef struct
#include <winsock2.h> {
#include <ws2tcpip.h> char open;
#pragma comment(lib,"ws2_32.lib") //Winsock Library unsigned short port;
#elif (__APPLE__ || __linux)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
struct xpcSocket {
short open;
unsigned short port;
// X-Plane IP and Port
char xpIP[16];
unsigned short xpPort;
#ifdef _WIN32 /* WIN32 SYSTEM */
SOCKET sock;
#elif (__APPLE__ || __linux) //OS X/Linux
int sock;
#endif
};
typedef struct // X-Plane IP and Port
{ char xpIP[16];
float pitch; unsigned short xpPort;
float roll;
float yaw;
float throttle;
char gear;
float flaps;
char aircraft;
} xpcCtrl;
typedef struct #ifdef _WIN32
{ SOCKET sock;
double latitude; #else
double longitude; int sock;
double altitude; #endif
} Waypoint; } XPCSocket;
typedef enum typedef enum
{ {
xpc_WYPT_ADD = 1, XPC_WYPT_ADD = 1,
xpc_WYPT_DEL = 2, XPC_WYPT_DEL = 2,
xpc_WYPT_CLR = 3 XPC_WYPT_CLR = 3
} WYPT_OP; } WYPT_OP;
// Low Level UDP Functions
typedef struct /// Opens a new connection to XPC on an OS chosen port.
{ ///
WYPT_OP op; /// \param xpIP A string representing the IP address of the host running X-Plane.
Waypoint points[20]; /// \param xpPort The port of the X-Plane Connect plugin is listening on. Usually 49009.
size_t numPoints; /// \returns An XPCSocket struct representing the newly created connection.
} xpcWypt; XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort);
// Basic Functions
struct xpcSocket openUDP(unsigned short port, const char *xpIP, unsigned short xpPort);
void closeUDP(struct xpcSocket);
short sendUDP(struct xpcSocket recfd, char my_message[], short messageLength);
short readUDP(struct xpcSocket recfd, char *dataRef, struct sockaddr *recvaddr);
// Configuration
short setCONN(struct xpcSocket recfd, unsigned short recPort);
short pauseSim(struct xpcSocket recfd, short);
// UDP DATA
short parseDATA(const char my_message[], short messageLength, float dataRef[][9]);
short readDATA(struct xpcSocket recfd, float dataRef[][9]);
short sendDATA(struct xpcSocket recfd, float dataRef[][9], unsigned short rows);
// Position
short parsePOSI(const char my_message[], float resultArray[], int arraySize, float *gear);
short readPOSI(struct xpcSocket recfd, float resultArray[], int arraySize, float *gear);
short sendPOSI(struct xpcSocket recfd, short ACNum, short numArgs, float valueArray[]);
// Controls
xpcCtrl parseCTRL(const char data[]);
xpcCtrl readCTRL(struct xpcSocket recfd);
short sendCTRL(struct xpcSocket recfd, short numArgs, float valueArray[]);
short sendpCTRL(struct xpcSocket recfd, short numArgs, float valueArray[], char acNum);
// DREF Manipulation
short readDREF(struct xpcSocket recfd, float *resultArray[], short arraySizes[]);
short parseDREF(const char my_message[], char *dataRef, unsigned short *length_of_DREF, float *values, unsigned short *number_of_values);
short sendDREF(struct xpcSocket recfd, const char *dataRef, unsigned short length_of_DREF, float *values, unsigned short number_of_values);
short requestDREF(struct xpcSocket sendfd, struct xpcSocket recfd, char DREFArray[][100], short DREFSizes[], short listLength, float *resultArray[], short arraySizes[]);
int parseGETD(const char my_message[], char *DREFArray[], int DREFSizes[]);
short parseRequest(const char my_message[], float *resultArray[], short arraySizes[]);
short readRequest(struct xpcSocket recfd, float *dataRef[], short arraySizes[], struct sockaddr *recvaddr);
// Waypoints /// Opens a new connection to XPC on the specified port.
xpcWypt parseWYPT(const char data[]); ///
short sendWYPT(struct xpcSocket sendfd, WYPT_OP op, float points[], int numPoints); /// \param xpIP A string representing the IP address of the host running X-Plane.
/// \param xpPort The port of the X-Plane Connect plugin is listening on. Usually 49009.
/// \param port The local port to use when sending and receiving data from XPC.
/// \returns An XPCSocket struct representing the newly created connection.
XPCSocket openUDP(const char *xpIP, unsigned short xpPort, unsigned short port);
// Screen Text /// Closes the specified connection and releases resources associated with it.
short sendTEXT(struct xpcSocket sendfd, char* msg, int x, int y); ///
/// \param sock The socket to close.
void closeUDP(XPCSocket sock);
/// Sends the given data to the X-Plane plugin.
///
/// \param sock The socket to use to send the data.
/// \param buffer A pointer to the data to send.
/// \param len The number of bytes to send.
/// \returns If an error occurs, a negative number. Otehrwise, the number of bytes sent.
int sendUDP(XPCSocket sock, char buffer[], int len);
/// Reads a datagram from the specified socket.
///
/// \param sock The socket to read from.
/// \param buffer A pointer to the location to store the data.
/// \param len The number of bytes to read.
/// \returns If an error occurs, a negative number. Otehrwise, the number of bytes read.
int readUDP(XPCSocket sock, char buffer[], int len);
// Configuration
/// Sets the port on which the socket sends and receives data.
///
/// \param sock A pointer to the socket to change.
/// \param port The new port to use.
/// \returns 0 if successful, otherwise a negative value.
int setCONN(XPCSocket* sock, unsigned short port);
/// Pause or unpause the simulation.
///
/// \param sock The socket to use to send the command.
/// \param pause 0 to unpause the sim; any other value to pause.
/// \returns 0 if successful, otherwise a negative value.
int pauseSim(XPCSocket sock, char pause);
#endif //ifdef _h // X-Plane UDP DATA
/// Reads X-Plane data from the specified socket.
///
/// \details This command is compatible with the X-Plane data API.
/// \param sock The socket to use to send the command.
/// \param dataRef A 2D array of data rows to read into.
/// \param rows The number of rows in dataRef.
/// \returns 0 if successful, otherwise a negative value.
int readDATA(XPCSocket sock, float dataRef[][9], int rows);
/// Sends X-Plane data on the specified socket.
///
/// \details This command is compatible with the X-Plane data API.
/// \param sock The socket to use to send the command.
/// \param dataRef A 2D array of data rows to send.
/// \param rows The number of rows in dataRef.
/// \returns 0 if successful, otherwise a negative value.
int sendDATA(XPCSocket sock, float dataRef[][9], int rows);
// DREF Manipulation
/// Sets the specified dataref to the specified value.
///
/// \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 dref The name of the dataref to set.
/// \param values An array of values representing the data to set.
/// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value.
int setDREF(XPCSocket sock, const char* dref, float values[], int size);
/// Gets the value of the specified dataref.
///
/// \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 dref The name of the dataref to get.
/// \param values The array in which the value of the dataref will be stored.
/// \param size The number of elements in values. The actual number of elements copied in will
/// be set when the function returns.
/// \returns 0 if successful, otherwise a negative value.
int getDREF(XPCSocket sock, const char* dref, float values[], int* size);
/// Gets the value of the specified dataref.
///
/// \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 get.
/// \param values A 2D array in which the values of the datarefs will be stored.
/// \param count The number of datarefs being requested.
/// \param size The number of elements in each row of values. The size of each row will be set
/// to the actual number of elements copied in for that row.
/// \returns 0 if successful, otherwise a negative value.
int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char count, int sizes[]);
// Position
/// Sets the position and orientation of the player aircraft.
///
/// \param sock The socket to use to send the command.
/// \param values An array representing position data about the aircraft. The format of values is
/// [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear]. If less than 7 values are specified,
/// the unspecified values will be left unchanged.
/// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value.
int psendPOSI(XPCSocket sock, float values[], int size);
/// Sets the position and orientation of the specified aircraft.
///
/// \param sock The socket to use to send the command.
/// \param values An array representing position data about the aircraft. The format of values is
/// [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear]. If less than 7 values are specified,
/// the unspecified values will be left unchanged.
/// \param size The number of elements in values.
/// \param ac The aircraft number to set the position of. 0 for the player aircraft.
/// \returns 0 if successful, otherwise a negative value.
int sendPOSI(XPCSocket sock, float values[], int size, char ac);
// Controls
/// Sets the control surfaces of the player aircraft.
///
/// \param sock The socket to use to send the command.
/// \param values An array representing position data about the aircraft. The format of values is
/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are
/// specified, the unspecified values will be left unchanged.
/// \param size The number of elements in values.
/// \returns 0 if successful, otherwise a negative value.
int psendCTRL(XPCSocket sock, float values[], int size);
/// Sets the control surfaces of the specified aircraft.
///
/// \param sock The socket to use to send the command.
/// \param values An array representing position data about the aircraft. The format of values is
/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are
/// specified, the unspecified values will be left unchanged.
/// \param size The number of elements in values.
/// \param ac The aircraft number to set the control surfaces of. 0 for the player aircraft.
/// \returns 0 if successful, otherwise a negative value.
int sendCTRL(XPCSocket sock, float values[], int size, char ac);
// Drawing
/// Sets a string to be printed on the screen in X-Plane.
///
/// \param sock The socket to use to send the command.
/// \param msg The message to print of the screen.
/// \param x The distance in pixels from the left edge of the screen to print the text.
/// \param y The distance in pixels from the bottom edge of the screen to print the top line of text.
/// \returns 0 if successful, otherwise a negative value.
int sendTEXT(XPCSocket sock, char* msg, int x, int y);
/// Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
/// and all points are removed.
///
/// \param sock The socket to use to send the command.
/// \param op The operation to perform. 1=add, 2=remove, 3=clear.
/// \param points An array of values representing points. Each triplet in the array will be
/// interpreted as a (Lat, Lon, Alt) point.
/// \param count The number of points. There should be 3 * count elements in points.
/// \returns 0 if successful, otherwise a negative value.
int sendWYPT(XPCSocket sock, WYPT_OP op, float points[], int count);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif

Binary file not shown.

View File

@@ -44,8 +44,7 @@ import java.util.Arrays;
public class XPlaneConnect implements AutoCloseable public class XPlaneConnect implements AutoCloseable
{ {
private static int clientNum; private static int clientNum;
private DatagramSocket outSocket; private DatagramSocket socket;
private DatagramSocket inSocket;
private InetAddress xplaneAddr; private InetAddress xplaneAddr;
private int xplanePort; private int xplanePort;
@@ -54,7 +53,7 @@ public class XPlaneConnect implements AutoCloseable
* *
* @return The incoming port number. * @return The incoming port number.
*/ */
public int getRecvPort() { return inSocket.getLocalPort(); } public int getRecvPort() { return socket.getLocalPort(); }
/** /**
* Gets the port on which the client sends data to X-Plane. * Gets the port on which the client sends data to X-Plane.
@@ -116,62 +115,55 @@ public class XPlaneConnect implements AutoCloseable
*/ */
public XPlaneConnect(int timeout) throws SocketException public XPlaneConnect(int timeout) throws SocketException
{ {
this.inSocket = new DatagramSocket(49008); this.socket = new DatagramSocket(0);
this.outSocket = new DatagramSocket(50000 + ++clientNum);
this.xplaneAddr = InetAddress.getLoopbackAddress(); this.xplaneAddr = InetAddress.getLoopbackAddress();
this.xplanePort = 49009; this.xplanePort = 49009;
this.inSocket.setSoTimeout(timeout); this.socket.setSoTimeout(timeout);
} }
/** /**
* Initializes a new instance of the {@code XPlaneConnect} class using the specified ports and X-Plane host. * Initializes a new instance of the {@code XPlaneConnect} class using the specified ports and X-Plane host.
* *
* @param port The port on which the X-Plane Connect plugin is sending data. * @param xpHost The network host on which X-Plane is running.
* @param xplaneHost The network host on which X-Plane is running. * @param xpPort The port on which the X-Plane Connect plugin is listening.
* @param xplanePort The port on which the X-Plane Connect plugin is listening. * @param port The local port to use when sending and receiving data from XPC.
* @throws java.net.SocketException If this instance is unable to bind to the specified port. * @throws java.net.SocketException If this instance is unable to bind to the specified port.
* @throws java.net.UnknownHostException If the specified hostname can not be resolved. * @throws java.net.UnknownHostException If the specified hostname can not be resolved.
*/ */
public XPlaneConnect(int port, String xplaneHost, int xplanePort) public XPlaneConnect(String xpHost, int xpPort, int port)
throws java.net.SocketException, java.net.UnknownHostException throws java.net.SocketException, java.net.UnknownHostException
{ {
this(port, xplaneHost, xplanePort, 100); this(xpHost, xpPort, port, 100);
} }
/** /**
* Initializes a new instance of the {@code XPlaneConnect} class using the specified ports, hostname, and timeout. * Initializes a new instance of the {@code XPlaneConnect} class using the specified ports, hostname, and timeout.
* *
* @param port The port on which the X-Plane Connect plugin is sending data. * @param xpHost The network host on which X-Plane is running.
* @param xplaneHost The network host on which X-Plane is running. * @param xpPort The port on which the X-Plane Connect plugin is listening.
* @param xplanePort The port on which the X-Plane Connect plugin is listening. * @param port The port on which the X-Plane Connect plugin is sending data.
* @param timeout The time, in milliseconds, after which read attempts will timeout. * @param timeout The time, in milliseconds, after which read attempts will timeout.
* @throws java.net.SocketException If this instance is unable to bind to the specified port. * @throws java.net.SocketException If this instance is unable to bind to the specified port.
* @throws java.net.UnknownHostException If the specified hostname can not be resolved. * @throws java.net.UnknownHostException If the specified hostname can not be resolved.
*/ */
public XPlaneConnect(int port, String xplaneHost, int xplanePort, int timeout) public XPlaneConnect(String xpHost, int xpPort, int port, int timeout)
throws java.net.SocketException, java.net.UnknownHostException throws java.net.SocketException, java.net.UnknownHostException
{ {
this.inSocket = new DatagramSocket(port); this.socket = new DatagramSocket(port);
this.outSocket = new DatagramSocket(50000 + ++clientNum); this.xplaneAddr = InetAddress.getByName(xpHost);
this.xplaneAddr = InetAddress.getByName(xplaneHost); this.xplanePort = xpPort;
this.xplanePort = xplanePort; this.socket.setSoTimeout(timeout);
this.inSocket.setSoTimeout(timeout);
} }
/** /**
* Closes the underlying inSocket. * Closes the underlying socket.
*/ */
public void close() public void close()
{ {
if(inSocket != null) if(socket != null)
{ {
inSocket.close(); socket.close();
inSocket = null; socket = null;
}
if(outSocket != null)
{
outSocket.close();
outSocket = null;
} }
} }
@@ -181,13 +173,13 @@ public class XPlaneConnect implements AutoCloseable
* @return The data send from X-Plane. * @return The data send from X-Plane.
* @throws IOException If the read operation fails * @throws IOException If the read operation fails
*/ */
private byte[] readUDP() throws IOException //TODO: Store data in a class level buffer to account for partial messages private byte[] readUDP() throws IOException
{ {
byte[] buffer = new byte[2048]; byte[] buffer = new byte[2048];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try try
{ {
inSocket.receive(packet); socket.receive(packet);
return Arrays.copyOf(buffer, buffer[4]); return Arrays.copyOf(buffer, buffer[4]);
} }
catch (SocketTimeoutException ex) catch (SocketTimeoutException ex)
@@ -197,22 +189,15 @@ public class XPlaneConnect implements AutoCloseable
} }
/** /**
* Send the given data to the X-Plane plugin. This method automatically sets the length byte before sending, * Send the given data to the X-Plane plugin.
* overwriting any value previously stored in @{code buffer[4]}.
* *
* @param buffer The data to send. * @param buffer The data to send.
* @throws IOException If the send operation fails. * @throws IOException If the send operation fails.
*/ */
private void sendUDP(byte[] buffer) throws IOException private void sendUDP(byte[] buffer) throws IOException
{ {
if(buffer.length < 5 || buffer.length > 255)
{
throw new IllegalArgumentException("buffer must be between 5 and 255 bytes long.");
}
buffer[4] = (byte)buffer.length;
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, xplaneAddr, xplanePort); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, xplaneAddr, xplanePort);
outSocket.send(packet); socket.send(packet);
} }
/** /**
@@ -236,9 +221,9 @@ public class XPlaneConnect implements AutoCloseable
* @return A byte array representing data dependent on the dref requested. * @return A byte array representing data dependent on the dref requested.
* @throws IOException If either the request or the response fails. * @throws IOException If either the request or the response fails.
*/ */
public float[] requestDREF(String dref) throws IOException public float[] getDREF(String dref) throws IOException
{ {
return requestDREFs(new String[]{dref})[0]; return getDREFs(new String[] {dref})[0];
} }
/** /**
@@ -248,7 +233,7 @@ public class XPlaneConnect implements AutoCloseable
* @return A multidimensional array representing the data for each requested dref. * @return A multidimensional array representing the data for each requested dref.
* @throws IOException If either the request or the response fails. * @throws IOException If either the request or the response fails.
*/ */
public float[][] requestDREFs(String[] drefs) throws IOException public float[][] getDREFs(String[] drefs) throws IOException
{ {
//Preconditions //Preconditions
if(drefs == null || drefs.length == 0) if(drefs == null || drefs.length == 0)
@@ -321,19 +306,19 @@ public class XPlaneConnect implements AutoCloseable
throw new IOException("No response received."); throw new IOException("No response received.");
} }
public void sendDREF(String dref, float value) throws IOException public void setDREF(String dref, float value) throws IOException
{ {
sendDREF(dref, new float[] {value}); setDREF(dref, new float[] {value});
} }
/** /**
* Sends a command to X-Plane that sets the given DREF. * Sends a command to X-Plane that sets the given DREF.
* *
* @param dref The name of the DREF to set. * @param dref The name of the X-Plane dataref to set.
* @param value An array of floating point values whose structure depends on the dref specified. * @param value An array of floating point values whose structure depends on the dref specified.
* @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 setDREF(String dref, float[] value) throws IOException
{ {
//Preconditions //Preconditions
if(dref == null) if(dref == null)
@@ -559,6 +544,36 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray()); sendUDP(os.toByteArray());
} }
/**
* Reads X-Plane data
*
* @return The data read.
* @throws IOException If the read operation fails.
*/
public float[][] readData() throws IOException
{
byte[] buffer = readUDP();
ByteBuffer bb = ByteBuffer.wrap(buffer);
int cur = 5;
int len = bb.get(cur++);
float[][] result = new float[bb.get(len)][9];
for(int i = 0; i < len; ++i)
{
for(int j = 0; j < 9; ++j)
{
result[i][j] = bb.getFloat(cur);
cur += 4;
}
}
return result;
}
/**
* Sends data to X-Plane
*
* @param data The data to send.
* @throws IOException If the command cannot be sent.
*/
public void sendDATA(float[][] data) throws IOException public void sendDATA(float[][] data) throws IOException
{ {
//Preconditions //Preconditions
@@ -594,6 +609,36 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray()); sendUDP(os.toByteArray());
} }
/**
* Selects what data X-Plane will export over UDP.
*
* @param rows The row numbers to select.
* @throws IOException If the command cannot be sent.
*/
public void selectDATA(int[] rows) throws IOException
{
//Preconditions
if(rows == null || rows.length == 0)
{
throw new IllegalArgumentException("rows must be a non-null, non-empty array.");
}
//Convert data to bytes
ByteBuffer bb = ByteBuffer.allocate(4 * rows.length);
bb.order(ByteOrder.LITTLE_ENDIAN);
for(int i = 0; i < rows.length; ++i)
{
bb.putInt(i * 4, rows[i]);
}
//Build and send message
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("DSEL".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length
os.write(bb.array());
sendUDP(os.toByteArray());
}
/** /**
* Sets a message to be displayed on the screen in X-Plane at the default screen location. * Sets a message to be displayed on the screen in X-Plane at the default screen location.
* *
@@ -643,6 +688,15 @@ public class XPlaneConnect implements AutoCloseable
sendUDP(os.toByteArray()); sendUDP(os.toByteArray());
} }
/**
* Adds, removes, or clears a set of waypoints. If the command is clear, the points are ignored
* and all points are removed.
*
* @param op The operation to perform.
* @param points An array of values representing points. Each triplet in the array will be
* interpreted as a (Lat, Lon, Alt) point.
* @throws IOException If the command cannot be sent.
*/
public void sendWYPT(WaypointOp op, float[] points) throws IOException public void sendWYPT(WaypointOp op, float[] points) throws IOException
{ {
//Preconditions //Preconditions
@@ -672,12 +726,12 @@ public class XPlaneConnect implements AutoCloseable
/** /**
* Sets the port on which the client will receive data from X-Plane. * Sets the port on which the client will receive data from X-Plane.
* *
* @param recvPort The new incoming port number. * @param port The new incoming port number.
* @throws IOException If the command cannnot be sent. * @throws IOException If the command cannnot be sent.
*/ */
public void setCONN(int recvPort) throws IOException public void setCONN(int port) throws IOException
{ {
if(recvPort < 0 || recvPort >= 0xFFFF) if(port < 0 || port >= 0xFFFF)
{ {
throw new IllegalArgumentException("Invalid port (must be non-negative and less than 65536)."); throw new IllegalArgumentException("Invalid port (must be non-negative and less than 65536).");
} }
@@ -685,14 +739,14 @@ public class XPlaneConnect implements AutoCloseable
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write("CONN".getBytes(StandardCharsets.UTF_8)); os.write("CONN".getBytes(StandardCharsets.UTF_8));
os.write(0xFF); //Placeholder for message length os.write(0xFF); //Placeholder for message length
os.write((byte) recvPort); os.write((byte) port);
os.write((byte) (recvPort >> 8)); os.write((byte) (port >> 8));
sendUDP(os.toByteArray()); sendUDP(os.toByteArray());
int soTimeout = inSocket.getSoTimeout(); int soTimeout = socket.getSoTimeout();
inSocket.close(); socket.close();
inSocket = new DatagramSocket(recvPort); socket = new DatagramSocket(port);
inSocket.setSoTimeout(soTimeout); socket.setSoTimeout(soTimeout);
readUDP(); // Try to read response readUDP(); // Try to read response
} }
} }

Binary file not shown.

View File

@@ -1,33 +0,0 @@
function socket = clearUDPBuffer(socket,varargin)
% clearUDPBuffer Script that clears an UDP Socket Buffer by closing and
% reopening the socket
%
% Inputs
% Socket: UDP Socket to be cleared
%
% Outputs
% Socket: UDP Socket
%
% Use
% 1. import XPlaneConnect.*;
% 2. Socket = openUDP(49005);
% 3. Socket = clearUDPBuffer(Socket);
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.*
%% Close and reopen socket
if ~socket.isClosed()
port = socket.getLocalPort(); %get port of socket
socket.close; %Close Socket
socket = openUDP(port,varargin); %open new socket
end
end

View File

@@ -1,28 +1,27 @@
function [socket] = closeUDP(socket) function closeUDP(socket)
% closeUDP Script that closes a UDP Socket % closeUDP Closes the specified connection and releases resources associated with it.
% %
% Inputs % Inputs
% Socket: UDP Socket to be closed % socket: An XPC client to close
%
% Outputs
% Socket: Closed Socket
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. Socket = openUDP(49005); % 2. socket = openUDP();
% 3. Status = closeUDP(Socket); % 3. closeUDP(socket);
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov % christopher.a.teubert@nasa.gov
% % [JW] Jason Watkins
% To Do % jason.w.watkins@nasa.gov
% 1. Verify Input
%
% BEGIN CODE
%% Close the socket
assert(isequal(class(socket), 'gov.nasa.xpc.XPlaneConnect'),...
'[closeUDP] ERROR: socket was not an XPC client.');
socket.close; socket.close;
assert(isequal(socket.isClosed(),1),'closeUDP: Error- Could not close socket'); %% Track open clients
global clients;
%TODO: Remove stale clients
end end

View File

@@ -0,0 +1,38 @@
function result = requestDREFs( drefs, socket )
%requestDREF request the value of a specific DataRef from X-Plane over UDP
%
%Inputs
% drefs: Cell Array of DataRefs to be requested
% socket (optional): The client to use when sending the command.
%Outputs
% result: cell array of resulting data.
%
%Use
% 1. import XPlaneConnect.*;
% 2. socket = opendUDP();
% 3. drefs = {'sim/cockpit2/controls/yoke_heading_ratio','sim/cockpit2/controls/yoke_roll_ratio'};
% 4. result = requestDREF(drefs, socket);
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
% [JW] Jason Watkins
% jason.w.watkins@nasa.gov
import XPlaneConnect.*
%% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[getDREFs] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% Send command
result = socket.getDREFs(drefs);
end

View File

@@ -1,41 +1,41 @@
function [socket] = openUDP(port, varargin) function [ socket ] = openUDP(varargin)
%openUDP Script that opens an UDP Socket %openUDP Initializes a new instance of the XPC client and returns it.
% %
%Inputs %Inputs
% port: UDP Port for socket % xpHost: The network host on which X-Plane is running.
% xpPort: The port on which the X-Plane Connect plugin is listening.
% port: The local port to use when sending and receiving data from XPC.
% timeout (optional): Optional parameter for time to UDP timeout (in ms) % timeout (optional): Optional parameter for time to UDP timeout (in ms)
%Outputs %Outputs
% Socket: UDP Socket % socket: An instance of the XPC client.
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. Socket = openUDP(49005); %Open socket at port 49005 with timeout=0.1 sec % 2. socket = openUDP(); % Open a socket using the default settings
% or % or
% 2. Socket = openUDP(49005); %Open socket at port 49005 with timeout=0.2 sec % 2. Socket = openUDP('127.0.0.1', 49008, 49005); % Open a socket to connect to X-Plane on the local machine and receive data on port 49005
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov % christopher.a.teubert@nasa.gov
% % [JW] Jason Watkins
% To Do % jason.w.watkins@nasa.gov
% 1. Verify Input
%
% BEGIN CODE
import java.net.DatagramSocket %% Handle input
p = inputParser;
addOptional(p,'xpHost','127.0.0.1',@ischar);
addOptional(p,'xpPort',49009,@isnumeric);
addOptional(p,'port',0,@isnumeric);
parse(p,varargin{:});
%% create socket %% Create client
socket = DatagramSocket(port); [folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP'));
socket.setSoTimeout(100); javaaddpath([folder, '\XPlaneConnect.jar']);
socket.setReceiveBufferSize(2000); import gov.nasa.xpc.*;
socket = XPlaneConnect(p.Results.xpHost, p.Results.xpPort, p.Results.port);
%% interpret input %% Track open clients
if ~isempty(varargin) global clients;
if isnumeric(varargin(1)) clients = [clients, socket];
socket.setSoTimeout(varargin(1));
end
end
assert(isequal(socket.isClosed(),0),'openUDP: Error- Could not open port'); end
end

View File

@@ -1,13 +1,9 @@
function status = pauseSim( pause, varargin ) function pauseSim( pause, socket )
%pauseSim pause Simulation %pauseSim Pauses or unpauses X-Plane.
% %
%Inputs %Inputs
% Pause: binary value 0=run, 1=pause % pause: binary value 0=run, 1=pause
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin
%
%Outputs
% status: If there was an error. Status<0 means there was an error.
% %
%Use %Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
@@ -16,29 +12,26 @@ function status = pauseSim( pause, varargin )
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov % christopher.a.teubert@nasa.gov
% % [JW] Jason Watkins
% To Do % jason.w.watkins@nasa.gov
% 1. Verify Input
%
%BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input %% Get client
% Optional parameters global clients;
p = inputParser; if ~exist('socket', 'var')
addRequired(p,'pause',@isnumeric); assert(isequal(length(clients) < 2, 1), '[pauseSim] ERROR: Multiple clients open. You must specify which client to use.');
addOptional(p,'IP','127.0.0.1',@ischar); if isempty(clients)
addOptional(p,'port',49009,@isnumeric); socket = openUDP();
parse(p,pause,varargin{:}); else
socket = clients(1);
end
end
% Check format of input-TODO %% Validate input
pause = logical(pause);
%% BODY %% Send command
message = zeros(1,6); socket.pauseSim(pause);
message(1:4) = 'SIMU'-0;
message(6) = uint8(p.Results.pause);
status = sendUDP(message, p.Results.IP, p.Results.port);
end end

View File

@@ -1,9 +1,8 @@
function [ sensor ] = readDATA( socket ) function [ result ] = readDATA( socket )
% readDATA Reads UDP Socket and interprets data % readDATA Reads UDP Socket and interprets data
% %
% Inputs % Inputs
% location: Either an opened UDP Socket or integer port number % socket (optional): The client to read from.
%
% Outputs % Outputs
% If data is X-Plane data format: % If data is X-Plane data format:
% data: Matlab X-Plane DATA Structure % data: Matlab X-Plane DATA Structure
@@ -30,62 +29,30 @@ function [ sensor ] = readDATA( socket )
% NOTE: sending in a port number instead of an opened socket clears the UDP buffer. This only works if the data is being sent at a fast rate. Sending in a UDP Socket reads the first packet in the buffer. % NOTE: sending in a port number instead of an opened socket clears the UDP buffer. This only works if the data is being sent at a fast rate. Sending in a UDP Socket reads the first packet in the buffer.
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % Christopher Teubert (SGT, Inc.) <christopher.a.teubert@nasa.gov>
% christopher.a.teubert@nasa.gov % Jason Watkins <jason.w.watkins@nasa.gov>
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Read UDP Socket %% Get client
[ sensor.raw] = readUDP(socket); global clients;
if ~exist('socket', 'var')
%% Interpret Input assert(istrue(length(clients) < 2), '[readDATA] ERROR: Multiple clients open. You must specify which client to use.');
bits = size(sensor.raw); if isempty(clients)
if sensor.raw ~= -998 %If the signal exists socket = openUDP();
header = char(sensor.raw(1:4)'); else
if strcmp(header,'DATA') %DATA signal type socket = clients(1);
Values = floor((bits-5)/36);
sensor.d = [];
sensor.h = zeros(Values(1),1);
for i=1:Values(1)
sensor.h(i) = sensor.raw(6+(i-1)*36);
sensor.d = [sensor.d; typecast(uint8(sensor.raw(10+(i-1)*36:5+i*36))','single')];
end
elseif strcmp(header,'STRU') %STRU signal type
a = 6;
while a<length(sensor.raw)
strdim = sensor.raw(a);
if strdim == 0
break
end
fieldName = char(sensor.raw(a+1:a+strdim)');
a = a+strdim+1;
dim1 = sensor.raw(a);
dim2 = sensor.raw(a+1);
if dim1 == 0 %String
value = char(sensor.raw(a+2:a+1+dim2))';
a = a + dim2 + 2;
else
value = [];
for i=1:dim1
value(i,:) = typecast(uint8(sensor.raw(a+2+(i-1)*dim2*4:a+1+i*dim2*4))','single');
end
a = a + dim1*dim2*4+2;
end
sensor.(fieldName) = value;
end
elseif strcmp(header,'OTHR')
sensor.d = sensor.raw(6:end);
else %Other signal type
sensor.d = sensor.raw;
end
else %No Signal
sensor.d = -998;
end end
end end
%% Get data
result.raw = socket.readDATA();
%% Format data
rows = (length(result.raw) - 5) / 9;
result.h = zeroes(rows);
for i=1:rows
j = 6 + (i - 1) * 9;
result.h(i) = result.rows(j);
result.d = [result.d; result.rows((j+1):(j+9))];
end

View File

@@ -1,83 +0,0 @@
function [ data ] = readUDP( varargin )
%readUDP Read Array from UDP Socket
%
% Inputs
% input: Either an opened UDP Socket or integer port number
%
% Outputs
% data: UDP uint8 Array. Equal to -998 in the case of an error
%
% Use
% 1. import XPlaneConnect.*;
% 2. socket = openUDP(49005);
% 3. data = readUDP(socket);
% 4. status = closeUDP(socket);
%
% or
%
% 1. import XPlaneConnect.*;
% 2. data = readUDP(49005);
%
% NOTE: sending in a port number instead of an opened socket clears the
%UDP buffer. This only works if the data is being sent at a fast rate.
%Sending in a UDP Socket reads the first packet in the buffer.
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.*
import java.net.DatagramPacket
bits = 2000;
%% Interpret Input
global udpReadPort;
if isempty(varargin)
if isempty(udpReadPort)
udpReadPort = 49008;
end
socket = openUDP(udpReadPort);
ownSocket = 1;
else
socket = varargin{1};
ownSocket = 0;
if isnumeric(varargin{1})
socket=openUDP(varargin{1});
ownSocket = 1;
end
end
%% Try reading packet
try
packet = DatagramPacket(zeros(1,bits,'int8'),bits);
socket.receive(packet)
data = packet.getData;
data = int16(data);
data(data(:)<0) = uint8(data(data(:)<0) + 256); %fix signed issue
size = int16(data(5)); %size of data stream
%% trim trailing data
for i=1:floor(length(data)/256)+1
if data(size+1:end)==0
break
end
size = size + 256;
end
data = data(1:size);
catch err %Read Unsuccessful
data = -998;
end
%% Close Port (if opened in code)
if ownSocket
socket.close()
end
end

View File

@@ -1,69 +0,0 @@
function result = requestDREF( DREFArray, varargin )
%requestDREF request the value of a specific DataRef from X-Plane over UDP
%
%Inputs
% DREFArray: Cell Array of DataRefs to be requested
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine)
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin
%
%Outputs
% result: cell array of resulting data where
%
%Use
% 1. import XPlaneConnect.*;
% 2. DREFArray = {'sim/cockpit2/controls/yoke_heading_ratio','sim/cockpit2/controls/yoke_roll_ratio'};
% 3. result = requestDREF( DREFArray, '172.0.100.54' );
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
%BEGIN CODE
import XPlaneConnect.*
message = zeros(1,6);
len = 7;
status = -1; %#ok<NASGU> % no data
%% Handle Input
p = inputParser;
addRequired(p,'DREFArray');
addOptional(p,'IP','127.0.0.1',@ischar);
addOptional(p,'port',49009,@isnumeric);
parse(p,DREFArray,varargin{:});
%% BODY
% Header
message(1:4) = 'GETD'-0;
message(6) = length(p.Results.DREFArray);
% DREFS
for i=1:length(p.Results.DREFArray)
message(len) = length(p.Results.DREFArray{i});
message(len+1:len+message(len)) = p.Results.DREFArray{i};
len = len+1+message(len);
end
% Send UDP
status = sendUDP(message, p.Results.IP, p.Results.port);
% Look for response
for i=1:40
data = readUDP();
if length(data) > 1 % Received Data
status = 0;
counter = 7;
nArrays = data(6);
result = cell(nArrays,1);
for j=1:nArrays
sizeArray = data(counter);
result{j} = typecast(uint8(data(counter+1:counter+sizeArray*4))','single');
counter = counter + 1 + sizeArray * 4;
end
break;
else
result = cell(0,1);
end
end
end

View File

@@ -1,13 +1,9 @@
function [ status ] = selectDATA( index, varargin ) function selectDATA( rows, socket )
% selectDATA Choose specific X-Plane parameters to be send over UDP % selectDATA Choose specific X-Plane parameters to be send over UDP
% %
% Inputs % Inputs
% index: An array of the values that to be sent. Corresponds to the numbers on the XPlane Output screen % rows: An array of the values that to be sent. Corresponds to the numbers on the XPlane Output screen
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
%
% Outputs
% status: If there was an error. Status<0 means there was an error.
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
@@ -15,29 +11,24 @@ function [ status ] = selectDATA( index, varargin )
% 3. status = selectDATA( values, '127.0.0.1', 49005 ); % send to localhose port 49005 % 3. status = selectDATA( values, '127.0.0.1', 49005 ); % send to localhose port 49005
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % Christopher Teubert (SGT, Inc.) <christopher.a.teubert@nasa.gov>
% christopher.a.teubert@nasa.gov % Jason Watkins <jason.w.watkins@nasa.gov>
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input %% Get client
p = inputParser; global clients;
addRequired(p,'index'); if ~exist('socket', 'var')
addOptional(p,'IP','127.0.0.1',@ischar); assert(istrue(length(clients) < 2), '[selectDATA] ERROR: Multiple clients open. You must specify which client to use.');
addOptional(p,'port',49009,@isnumeric); if isempty(clients)
parse(p,index,varargin{:}); socket = openUDP();
else
%% BODY socket = clients(1);
dataString = ['DSEL'-0,0];
for i=1:length(index)
dataString = [dataString, p.Results.index(i), 0, 0, 0];
end end
status = sendUDP(dataStream, p.Results.IP, p.Results.port);
end end
%% Validate input
rows = int32(rows);
%% Send command
socket.selectDATA(rows);

View File

@@ -1,5 +1,5 @@
function [ status ] = sendCTRL( ctrl, acft, IP, port ) function sendCTRL( ctrl, ac, socket )
% sendCTRL Send X-Plane Aircraft Control Commands over UDP % sendCTRL Sends command to X-Plane setting control surfaces on the specified aircraft.
% %
% Inputs % Inputs
% ctrl: control array where the elements are as follows: % ctrl: control array where the elements are as follows:
@@ -9,56 +9,45 @@ function [ status ] = sendCTRL( ctrl, acft, IP, port )
% 4. Throttle [-1, 1] % 4. Throttle [-1, 1]
% 5. Gear (0=up, 1=down) % 5. Gear (0=up, 1=down)
% 6. Flaps [0, 1] % 6. Flaps [0, 1]
% acft (optional): Aircraft # (default is 0; 0 = own aircraft) % ac (optional): The aircraft to set. 0 for the player aircraft.
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
% %
% Outputs % Outputs
% status: If there was an error. Status<0 means there was an error. % status: If there was an error. Status<0 means there was an error.
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. #Send the data array to port 49009 on the computer at IP address 172.0.100.54. % 2. socket = openUDP();
% 3. status = sendCTRL([0, 0, 0, 0.8, 0, 1], 1, '172.0.100.54',49009); % 3. status = sendCTRL([0, 0, 0, 0.8, 0, 1], 0, socket); % Set throttle and flaps on the player aircraft.
% %
% Note: send the value -998 to not overwrite that parameter. That is, if -998 is sent, the parameter will stay at the current X-Plane value % Note: send the value -998 to not overwrite that parameter. That is, if
% -998 is sent, the parameter will stay at the current X-Plane value.
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % Christopher Teubert (SGT, Inc.) <christopher.a.teubert@nasa.gov>
% christopher.a.teubert@nasa.gov % Jason Watkins <jason.w.watkins@nasa.gov>
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input %% Get client
% Optional parameters global clients;
if ~exist('IP','var'), IP = '127.0.0.1'; end if ~exist('socket', 'var')
if ~exist('port','var'), port = 49009; end assert(isequal(length(clients) < 2, 1), '[sendCTRL] ERROR: Multiple clients open. You must specify which client to use.');
if ~exist('acft','var'), acft = 0; end if isempty(clients)
socket = openUDP();
% Check format of input-TODO else
socket = clients(1);
%%BODY
header = ['CTRL'-0,0];
dataStream = header; %TODO-ADD ACFT
% Deal with position update
control = [-998.5, -998.5, -998.5, -998.5, -998.5, -998.5];
for i=1:min(length(ctrl),length(control))
control(i) = ctrl(i);
end end
dataStream = [dataStream, typecast(single(control(1:4)),'uint8')];
dataStream = [dataStream, uint8(control(5))];
dataStream = [dataStream, typecast(single(control(6)),'uint8')];
dataStream = [dataStream, uint8(acft)];
% Send DATA
status = sendUDP(dataStream, IP, port);
end end
%% Validate input
ctrl = single(ctrl);
if ~exist('ac', 'var')
ac = 0;
end
ac = logical(ac);
%% Send command
socket.sendCTRL(ctrl, ac);
end

View File

@@ -1,15 +1,11 @@
function [ status ] = sendDATA(data, varargin) function sendDATA(data, socket)
% sendDATA Send X-Plane formatted DATA over UDP % sendDATA Send X-Plane formatted DATA over UDP
% %
% Inputs % Inputs
% data: X-Plane formatted data. Is a matlab structure with the following fields: % data: X-Plane formatted data. Is a matlab structure with the following fields:
% .h: Header array containing header numbers corresponding to values in the X-Plane UDP Data screen. % .h: Header array containing header numbers corresponding to values in the X-Plane UDP Data screen.
% .d: 2-D Data array. Each row contains eight values corresponding to the header value (.h(1) corresponds to .d(1,:)) % .d: 2-D Data array. Each row contains eight values corresponding to the header value (.h(1) corresponds to .d(1,:))
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent . Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp interface.
%
% Outputs
% status: 0: no error, <0: error
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
@@ -29,29 +25,23 @@ function [ status ] = sendDATA(data, varargin)
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input %% Get client
p = inputParser; global clients;
addRequired(p,'data'); if ~exist('socket', 'var')
addOptional(p,'IP','127.0.0.1',@ischar); assert(isequal(length(clients) < 2, 1), '[sendDATA] ERROR: Multiple clients open. You must specify which client to use.');
addOptional(p,'port',49009,@isnumeric); if isempty(clients)
parse(p,data,varargin{:}); socket = openUDP();
%% BODY
header = ['DATA'-0,0];
dataStream = header;
for i=1:length(p.Results.data.h)
dataStream = [dataStream, p.Results.data.h(i), 0, 0, 0, typecast(single(p.Results.data.d(i,1:8)),'uint8')]; %#ok<AGROW>
end
% Send DATA
if length(dataStream) > 5
status = sendUDP(dataStream, p.Results.IP, p.Results.port);
else else
disp('Warning in sendDATA: Sending empty dataStream') socket = clients(1);
status = -2;
end end
end end
%% Validate input
javaData = [];
for i = 1:length(data.h);
javaData = [javaData; data.h(i) data.d(i, 1:8)];
end
%% Send command
socket.sendDATA(javaData);

View File

@@ -1,58 +0,0 @@
function status = sendDREF( dataRef, Value, varargin )
% sendDREF Send a command to change any DataRef in X-Plane over UDP. This requires the X-Plane Connect Plugin to be running
%
% Inputs
% dataRef: The X-Plane Dataref that will be chaged
% Value: The value that the above dataref is set to
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine)
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
%
% Outputs
% status: If there was an error. status<0 means there was an error.
%
% Use
% 1. import XPlaneConnect.*
% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear
% 3. Value = 0;
% 4. status = sendDREF(dataRef, Value);
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.*
status = 0;
%% Handle Input
p = inputParser;
addRequired(p,'dataRef');
addRequired(p,'Value');
addOptional(p,'IP','127.0.0.1',@ischar);
addOptional(p,'port',49009,@isnumeric);
parse(p,dataRef, Value ,varargin{:});
%% BODY
dataStream = zeros(1,7+length(p.Results.dataRef)+length(p.Results.Value)*4);
% Build Header
dataStream(1:4) = 'DREF'-0;
% Add DREF
dataStream(6) = uint8(length(p.Results.dataRef));
len = 6+length(p.Results.dataRef);
dataStream(7:len)=p.Results.dataRef-0;
% Add Value
dataStream(len+1) =uint8(length(p.Results.Value));
dataStream(len+2:end)=typecast(single(p.Results.Value),'uint8');
% Send DREF
if length(dataStream) > 5
status = sendUDP(dataStream, p.Results.IP, p.Results.port);
end
end

View File

@@ -1,5 +1,5 @@
function [ status ] = sendPOSI( posi, varargin ) function sendPOSI( posi, ac, socket )
% sendPOSI Send X-Plane Aircraft Position over UDP % sendPOSI Sets the position of the specified aircraft.
% %
% Inputs % Inputs
% posi: Position array where the elements are as follows: % posi: Position array where the elements are as follows:
@@ -10,52 +10,43 @@ function [ status ] = sendPOSI( posi, varargin )
% 5. Pitch (deg) % 5. Pitch (deg)
% 6. True Heading (deg) % 6. True Heading (deg)
% 7. Gear (0=up, 1=down) % 7. Gear (0=up, 1=down)
% acft (optional): Aircraft # (default is 0; 0 = own aircraft) % acft (optional): The aircraft to set. 0 for the player aircraft.
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
%
% Outputs
% status: If there was an error. Status<0 means there was an error.
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. #Send the data array to port 49009 on the computer at IP address 172.0.100.54. % 2. sendPOSI([37.5242422, -122.06899, 2500, 0, 0, 0, 1], 1);
% 3. status = sendPOSI([37.5242422, -122.06899, 2500, 0, 0, 0, 1], 1, '172.0.100.54');
% %
% Note: send the value -998 to not overwrite that parameter. That is, if -998 is sent, the parameter will stay at the current X-Plane value % Note: send the value -998 to not overwrite that parameter. That is, if
% -998 is sent, the parameter will stay at the current X-Plane value.
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov % christopher.a.teubert@nasa.gov
% % [JW] Jason Watkins
% To Do % jason.w.watkins@nasa.gov
%
% BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input %% Get client
p = inputParser; global clients;
addRequired(p,'posi'); if ~exist('socket', 'var')
addOptional(p,'acft',0,@isnumeric); assert(isequal(length(clients) < 2, 1), '[sendPOSI] ERROR: Multiple clients open. You must specify which client to use.');
addOptional(p,'IP','127.0.0.1',@ischar); if isempty(clients)
addOptional(p,'port',49009,@isnumeric); socket = openUDP();
parse(p,posi,varargin{:}); else
socket = clients(1);
%% BODY
header = ['POSI'-0,0];
dataStream = [header, p.Results.acft];
% Deal with position update
position = [37.4185718,-121.935565,500,0,0,0, 0];
for i=1:min(length(position),length(p.Results.posi))
position(i) = p.Results.posi(i);
end end
dataStream = [dataStream, typecast(single(position),'uint8')];
% Send POSI
status = sendUDP(dataStream, p.Results.IP, p.Results.port);
end end
%% Validate input
posi = single(posi);
if ~exist('ac', 'var')
ac = 0;
end
ac = logical(ac);
%% Send command
socket.sendPOSI(posi, ac);
end

View File

@@ -1,57 +0,0 @@
function [ status ] = sendSTRU( STRU, varargin )
%sendSTRU Send a MATLAB structure over UDP
%
%Inputs
% stru: A MATLAB structure
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine)
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin
%
%Outputs
% status: If there was an error. Status<0 means there was an error.
%
%Use
% 1. import XPlaneConnect.*;
% 2. data = struct('a',[1:20],'b',1.853,'name','Example Structure');
% 3. #Send the data structure to port 49005 on the computer at IP address 172.0.100.54
% 4. status = sendUDP( data, '172.0.100.54', 49005 );
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
% To Do
%
%BEGIN CODE
import XPlaneConnect.*
%% Handle Input
p = inputParser;
addRequired(p,'STRU');
addOptional(p,'IP','127.0.0.1',@ischar);
addOptional(p,'port',49009,@isnumeric);
parse(p,STRU,varargin{:});
%% Form Data Array representing the structure
DATA = ['STRU'-0,0]; %array header
fieldName = fieldnames(p.Results.STRU); %all Struct fields
for i=1:length(fieldName) %for each field
field = getfield(p.Results.STRU,fieldName{i}); %get field
if ischar(field) %String
dim1 = 0; %Indicates string
dim2 = length(field); %length
data = field-0; %data
else %Numeric
dim1 = size(field,1); %Array Dim1
dim2 = size(field,2); %Array Dim2
data = typecast(single(reshape(field',1,dim1*dim2)),'uint8'); %Data
end
DATA = [DATA,length(fieldName{i}),fieldName{i}-0,dim1,dim2,data]; %add to array
end
%% Send Array
status = sendUDP(DATA, p.Results.IP, p.Results.port);
end

View File

@@ -1,4 +1,4 @@
function [ status ] = sendTEXT( msg, varargin ) function sendTEXT( msg, x, y, socket )
% sendTEXT Sends a string to be displayed in X-Plane. % sendTEXT Sends a string to be displayed in X-Plane.
% %
% Inputs % Inputs
@@ -13,35 +13,35 @@ function [ status ] = sendTEXT( msg, varargin )
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. #Set a message to be displayed near the top middle of the screen. % 2. % Set a message to be displayed near the top middle of the screen.
% 3. status = sendTEXT('Some text', 512, 600); % 3. status = sendTEXT('Some text', 512, 600);
% %
% Contributors % Contributors
% Jason Watkins % Jason Watkins (jason.w.watkins@nasa.gov)
% jason.w.watkins@nasa.gov
%
% To Do
%
% BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input
p = inputParser;
addRequired(p,'msg');
addOptional(p,'x',-1,@isnumeric);
addOptional(p,'y',-1,@isnumeric);
addOptional(p,'IP','127.0.0.1',@ischar);
addOptional(p,'port',49009,@isnumeric);
parse(p,msg,varargin{:});
%% Body %% Get client
header = ['TEXT'-0,0]; global clients;
dataStream = [header,... if ~exist('socket', 'var')
typecast(uint32(p.Results.x), 'uint8'),... assert(isequal(length(clients) < 2, 1), '[sendTEXT] ERROR: Multiple clients open. You must specify which client to use.');
typecast(uint32(p.Results.y), 'uint8'),... if isempty(clients)
uint8(length(msg)), msg-0]; socket = openUDP();
else
% Send TEXT socket = clients(1);
status = sendUDP(dataStream, p.Results.IP, p.Results.port); end
end end
%% Validate input
if ~exist('x', 'var')
x = -1;
end
if ~exist('y', 'var')
y = -1;
end
x = int32(x);
y = int32(y);
%% Send command
socket.sendTEXT(msg, x, y);
end

View File

@@ -1,49 +0,0 @@
function [ status ] = sendUDP( data, IP, port )
%sendUDP Send an one dimensional array of type uint8 data over an UDP connection using a java DatagramSocket
%
%Inputs
% Data: 1-D array of type uint8 data to be sent
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine)
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin
%
%Outputs
% status: If there was an error. Status<0 means there was an error.
%
%Use
% 1. import XPlaneConnect.*;
% 2. data = uint8([1:20]);
% 3. #Send the data array to port 49005 on the computer at IP address 172.0.100.54.
% 4. status = sendUDP( data, '172.0.100.54', 49005 );
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
%
% To Do
% 1. Verify Input
%
%BEGIN CODE
import java.net.DatagramSocket
import java.net.DatagramPacket
import java.net.InetAddress
data(5) = length(data);
status = 0;
%% Send array
persistent socket
if isempty(socket)
try
socket = DatagramSocket;
catch err
status = 1;
disp(err)
end
end
IP = InetAddress.getByName(IP);
packet = DatagramPacket(data, length(data), IP, port); %create packet
socket.send(packet);
end

View File

@@ -1,53 +1,55 @@
function [ status ] = sendWYPT( op, points, varargin ) function sendWYPT( op, points, socket )
% sendWYPT Adds, removes, or clears a set of waypoints to be rendered in % sendWYPT Adds, removes, or clears a set of waypoints to be rendered in
% the simulator. % the simulator.
% %
% Inputs % Inputs
% msg: The string to be displayed % op: The operation to perform. 1=add, 2=remove, 3=clear.
% x (optional): The distance from the left edge of the screen to display the message. % points: An array of values representing points. Each triplet in the
% y (optional): The distance from the bottom edge of the screen to display the message. % array will be interpreted as a (Lat, Lon, Alt) point.
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
%
% Outputs
% status: 0 if successful, otherwise a negative value.
% %
% Use % Use
% 1. import XPlaneConnect.*; % 1. import XPlaneConnect.*;
% 2. #Set a message to be displayed near the top middle of the screen. % 2. %Set a message to be displayed near the top middle of the screen.
% 3. status = sendTEXT('Some text', 512, 600); % 3. status = sendTEXT('Some text', 512, 600);
% %
% Contributors % Contributors
% Jason Watkins % Jason Watkins (jason.w.watkins@nasa.gov)
% jason.w.watkins@nasa.gov
% %
% To Do % To Do
% %
% BEGIN CODE % BEGIN CODE
import XPlaneConnect.* import XPlaneConnect.*
%% Handle Input
p = inputParser;
addRequired(p,'op');
addRequired(p,'points');
addOptional(p,'IP','127.0.0.1',@ischar);
addOptional(p,'port',49009,@isnumeric);
parse(p,op,points,varargin{:});
%% Validate Input %% Get client
len = uint32(length(points)); global clients;
assert(op > 0 && op < 4); if ~exist('socket', 'var')
assert(mod(len, 3) == 0); assert(isequal(length(clients) < 2, 1), '[sendWYPT] ERROR: Multiple clients open. You must specify which client to use.');
assert(len / 3 < 20); if isempty(clients)
socket = openUDP();
%% Body else
header = ['WYPT'-0,0]; socket = clients(1);
dataStream = [header,... end
uint8(op),...
uint8(len / 3),...
typecast(single(points), 'uint8')];
% Send TEXT
status = sendUDP(dataStream, p.Results.IP, p.Results.port);
end end
%% Get WaypointOp class
[folder, ~, ~] = fileparts(which('XPlaneConnect.openUDP'));
javaaddpath([folder, '\XPlaneConnect.jar']);
import gov.nasa.xpc.*;
%% Validate input
len = uint32(length(points));
assert(op > 0 && op < 4);
wyptOp = WaypointOp.Add;
if isequal(op, 2)
wyptOp = WaypointOp.Del;
elseif isequal(op, 3)
wyptOp = WaypointOp.Clr;
end
assert(mod(len, 3) == 0);
%% Send command
socket.sendWYPT(wyptOp, points);
end

View File

@@ -1,51 +1,36 @@
function status = setConn( recvPort, IP, port ) function setConn(port, socket)
% setConn Send a command to set up the port where you will receive data on % setConn Send a command to set up the port where you will receive data on
% this computer. % this computer.
% %
% Inputs % Inputs
% Receiving Port: Port that data will be sent to in the future for this connection % port: Port that data will be sent to in the future for this connection.
% IP Address (optional): IP Address of the machine that will receive the data as a character array. Default is '127.0.0.1' (local machine) % socket (optional): The client to use when sending the command.
% port (optional): Port on the receiving machine where the data will be sent. Default is 49009 (XPlaneConnect). In general use 49009 to send to the plugin and 49005 to send to the x-plane udp
%
% Outputs
% status: If there was an error. status<0 means there was an error.
% %
% Use % Use
% 1. import XPlaneConnect.* % 1. import XPlaneConnect.*
% 2. status = setConn(49011); % 2. status = setConn(49011);
% %
% Contributors % Contributors
% [CT] Christopher Teubert (SGT, Inc.) % Christopher Teubert (SGT, Inc.) <christopher.a.teubert@nasa.gov>
% christopher.a.teubert@nasa.gov % Jason Watkins <jason.w.watkins@nasa.gov>
%
% To Do
% 1. Verify Input
%
% BEGIN CODE
import XPlaneConnect.*
status = 0; import XPlaneConnect.*
message = zeros(1,7);
%% Handle Input
% Optional parameters
if ~exist('IP','var'), IP = '127.0.0.1'; end
if ~exist('port','var'), port = 49009; end
% Check format of input-TODO %% Get client
global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[setCONN] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
socket = openUDP();
else
socket = clients(1);
end
end
%% BODY %% Validate input
% Header port = int32(port);
message(1:4) = 'CONN'-0;
%% Send command
% RecvPort socket.setCONN(port);
message(6:7) = typecast(uint16(recvPort),'uint8');
% Send
sendUDP(message,IP,port);
global udpReadPort;
udpReadPort = recvPort;
readUDP(); % Read and discard CONF message
end end

View File

@@ -0,0 +1,38 @@
function setDREF( dref, value, socket )
% sendDREF Sends a command to X-Plane that sets the given DREF.
%
% Inputs
% dref: The name of the X-Plane dataref to set.
% Value: An array of floating point values whose structure depends on the dref specified.
% socket (optional): The client to use when sending the command.
%
% Use
% 1. import XPlaneConnect.*
% 2. dataRef = 'sim/aircraft/parts/acf_gear_deploy'; // Landing Gear
% 3. Value = 0;
% 4. status = setDREF(dataRef, Value);
%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
% [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
value = single(value);
%%Send command
socket.setDREF(dref, value);

View File

@@ -22,6 +22,9 @@
<ClCompile Include="..\..\C\src\xplaneConnect.c" /> <ClCompile Include="..\..\C\src\xplaneConnect.c" />
<ClCompile Include="..\C Tests\main.c" /> <ClCompile Include="..\C Tests\main.c" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h" />
</ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{BC701AF4-552C-4C9D-82A1-B352542783A4}</ProjectGuid> <ProjectGuid>{BC701AF4-552C-4C9D-82A1-B352542783A4}</ProjectGuid>
<RootNamespace>CTests</RootNamespace> <RootNamespace>CTests</RootNamespace>

View File

@@ -22,4 +22,9 @@
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project> </Project>

View File

@@ -16,12 +16,10 @@
int testFailed = 0; int testFailed = 0;
int testPassed = 0; int testPassed = 0;
void runTest(short (*f)()) void runTest(int (*test)(), char* name)
{ {
short result; int result = test(); // Run Test
printf("Test %i: %s - ", testPassed + testFailed + 1, name);
printf("Test %i: ",testPassed+testFailed+1);
result = (*f)(); // Run Test
if (result == 0) if (result == 0)
{ {
printf("PASSED\n"); printf("PASSED\n");
@@ -34,63 +32,54 @@ void runTest(short (*f)())
} }
} }
short openTest() // openUDP Test int openTest() // openUDP Test
{ {
printf("openUDP - "); XPCSocket sock = openUDP("127.0.0.1", 49009, 49062);
struct xpcSocket sendPort = openUDP( 49062, "127.0.0.1", 49009 ); closeUDP(sock);
return 0; return 0;
} }
short closeTest() // closeUDP test int closeTest() // closeUDP test
{ {
printf("closeUDP - "); XPCSocket sendPort = openUDP("127.0.0.1", 49009, 49063);
struct xpcSocket sendPort = openUDP( 49063, "127.0.0.1", 49009 );
closeUDP(sendPort); closeUDP(sendPort);
sendPort = openUDP(49063, "127.0.0.1", 49009); sendPort = openUDP("127.0.0.1", 49009, 49063);
closeUDP(sendPort); closeUDP(sendPort);
return 0; return 0;
} }
short sendReadTest() // send/read Test int sendReadTest() // send/read Test
{ {
printf("send/readUDP - ");
// Initialization // Initialization
int i; // Iterator
char test[] = {0, 1, 2, 3, 5}; char test[] = {0, 1, 2, 3, 5};
char buf[5] = {0}; char buf[5] = {0};
struct xpcSocket sendPort, recvPort; XPCSocket outSock = openUDP("127.0.0.1", 49063, 49064);
XPCSocket inSock = openUDP("127.0.0.1", 49009, 49063);
// Setup
sendPort = openUDP( 49064, "127.0.0.1", 49063 );
recvPort = openUDP( 49063, "127.0.0.1", 49009 );
// Execution // Execution
sendUDP( sendPort, test, sizeof(test) ); sendUDP(outSock, test, sizeof(test));
readUDP( recvPort, buf, NULL ); // Test readUDP(inSock, buf, sizeof(buf), NULL);
// Close // Close
closeUDP(sendPort); closeUDP(outSock);
closeUDP(recvPort); closeUDP(inSock);
// Tests // Tests
for (i=0; i<4; i++) for (int i = 0; i < sizeof(buf); i++)
{ {
if (test[i] != buf[i]) // Not received correctly if (test[i] != buf[i]) // Not received correctly
{ {
return -1; return -1;
} }
} }
return 0; return 0;
} }
short sendTEXTTest() int sendTEXTTest()
{ {
printf("sendTEXT - ");
// Setup // Setup
struct xpcSocket sendPort = openUDP(49064, "127.0.0.1", 49009); XPCSocket sendPort = openUDP("127.0.0.1", 49009, 49064);
int x = 100; int x = 100;
int y = 700; int y = 700;
char* msg = "XPlaneConnect test message. Now with 100% fewer new lines!"; char* msg = "XPlaneConnect test message. Now with 100% fewer new lines!";
@@ -104,262 +93,177 @@ short sendTEXTTest()
return 0; return 0;
} }
short requestDREFTest() // Request DREF Test (Required for next tests) int requestDREFTest() // Request DREF Test (Required for next tests)
{ {
printf("requestDREF - ");
// Initialization // Initialization
int i; // Iterator char* drefs[100] =
char DREFArray[100][100]; {
float *recDATA[100]; "sim/cockpit/switches/gear_handle_status",
short DREFSizes[100]; "sim/cockpit2/switches/panel_brightness_ratio"
struct xpcSocket sendPort, recvPort; };
short result = 0; float* data[100];
int sizes[100];
XPCSocket sock = openUDP("127.0.0.1", 49009, 49064);
// Setup // Setup
for (i = 0; i < 100; i++) { for (int i = 0; i < 100; ++i)
recDATA[i] = (float *) malloc(40*sizeof(float)); {
memset(DREFArray[i],0,100); data[i] = (float*)malloc(40 * sizeof(float));
} sizes[i] = 40;
sendPort = openUDP( 49064, "127.0.0.1", 49009 ); }
recvPort = openUDP( 49008, "127.0.0.1", 49009 );
strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status");
strcpy(DREFArray[1],"sim/cockpit2/switches/panel_brightness_ratio");
for (i=0;i<2;i++) {
DREFSizes[i] = (int) strlen(DREFArray[i]);
}
// Execution // Execution
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 2, recDATA, DREFSizes); int result = getDREFs(sock, drefs, data, 2, sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Tests // Tests
if ( result < 0)// Request 2 values if ( result < 0)// Request 2 values
{ {
return -1; return -1;
} }
if (DREFSizes[0] != 1 || DREFSizes[1] != 4) if (sizes[0] != 1 || sizes[1] != 4)
{ {
return -2; return -2;
} }
return 0; return 0;
} }
short sendDREFTest() // sendDREF test int sendDREFTest() // sendDREF test
{ {
printf("sendDREF - ");
// Initialization // Initialization
int i; // Iterator char* drefs[100] =
char DREFArray[100][100]; {
float *recDATA[100]; "sim/cockpit/switches/gear_handle_status"
short DREFSizes[100]; };
float value = 0.0; float* data[100];
struct xpcSocket sendPort, recvPort; int sizes[100];
short result = 0; XPCSocket sock = openUDP("127.0.0.1", 49009, 49066);
float value = 1.0F;
// Setup
for (i = 0; i < 100; i++) { // Setup
recDATA[i] = (float *) malloc(40*sizeof(float)); for (int i = 0; i < 100; ++i)
memset(DREFArray[i],0,100); {
} data[i] = (float*)malloc(40 * sizeof(float));
sendPort = openUDP( 49066, "127.0.0.1", 49009 ); sizes[i] = 40;
recvPort = openUDP( 49008, "127.0.0.1", 49009 ); }
strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status");
for (i=0;i<1;i++) {
DREFSizes[i] = (int) strlen(DREFArray[i]);
}
// Execution // Execution
sendDREF(sendPort, DREFArray[0], DREFSizes[0], &value, 1); setDREF(sock, drefs[0], &value, 1);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); int result = getDREFs(sock, drefs, data, 1, sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Tests // Tests
if (result < 0)// Request 1 value if (result < 0)// Request 1 value
{ {
return -1; return -1;
} }
if (DREFSizes[0] != 1) if (sizes[0] != 1)
{ {
return -2; return -2;
} }
if (*recDATA[0] != value) if (data[0][0] != value)
{ {
return -3; return -3;
} }
return 0; return 0;
} }
short sendDATATest() // sendDATA test int sendDATATest() // sendDATA test
{ {
printf("sendData - ");
// Initialize // Initialize
int i,j; // Iterator int i,j; // Iterator
char DREFArray[100][100]; char* drefs[100] =
float data[4][9] = {0}; {
float *recDATA[100]; "sim/aircraft/parts/acf_gear_deploy"
short DREFSizes[100]; };
struct xpcSocket sendPort, recvPort; float* data[100];
short result = 0; int sizes[100];
float DATA[4][9];
// Setup XPCSocket sock = openUDP("127.0.0.1", 49009, 49066);
for (i = 0; i < 100; i++) {
recDATA[i] = (float *) malloc(40*sizeof(float)); // Setup
memset(DREFArray[i],0,100); for (int i = 0; i < 100; ++i)
} {
sendPort = openUDP( 49066, "127.0.0.1", 49009 ); data[i] = (float*)malloc(40 * sizeof(float));
recvPort = openUDP( 49008, "127.0.0.1", 49009 ); sizes[i] = 40;
strcpy(DREFArray[0],"sim/aircraft/parts/acf_gear_deploy"); }
for (i=0;i<1;i++) { for (i = 0; i < 4; i++)
DREFSizes[i] = (int) strlen(DREFArray[i]); {
} for (j = 0; j < 9; j++)
for (i=0;i<4;i++) { // Set array to -999 {
for (j=0;j<9;j++) data[i][j] = -999; data[i][j] = -998;
} }
}
data[0][0] = 14; // Gear data[0][0] = 14; // Gear
data[0][1] = 1; data[0][1] = 1;
data[0][2] = 0; data[0][2] = 0;
// Execution // Execution
sendDATA(sendPort, data, 1); // Gear sendDATA(sock, DATA, 1);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); // Test int result = getDREFs(sock, drefs, data, 1, sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Tests // Tests
if ( result < 0 )// Request 1 value if ( result < 0 )// Request 1 value
{ {
return -1; return -1;
} }
if (DREFSizes[0] != 10) if (sizes[0] != 10)
{ {
return -2; return -2;
} }
if (*recDATA[0] != data[0][1]) if (*data[0] != data[0][1])
{ {
return -3; return -3;
} }
return 0; return 0;
} }
short sendCTRLTest() // sendCTRL test int psendCTRLTest() // sendCTRL test
{ {
printf("sendCTRL - ");
// Initialize // Initialize
int i; // Iterator char* drefs[100] =
char DREFArray[100][100]; {
float CTRL[6] = { 0.0 }; "sim/cockpit2/controls/yoke_pitch_ratio",
float *recDATA[100]; "sim/cockpit2/controls/yoke_roll_ratio",
short DREFSizes[100]; "sim/cockpit2/controls/yoke_heading_ratio",
struct xpcSocket sendPort, recvPort; "sim/flightmodel/engine/ENGN_thro",
short result; "sim/cockpit/switches/gear_handle_status",
"sim/flightmodel/controls/flaprqst"
};
float* data[100];
int sizes[100];
float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F };
XPCSocket sock = openUDP("127.0.0.1", 49009, 49066);
// Setup // Setup
for (i = 0; i < 100; i++) { for (int i = 0; i < 100; i++)
recDATA[i] = (float *)malloc(40 * sizeof(float)); {
memset(DREFArray[i], 0, 100); data[i] = (float*)malloc(40 * sizeof(float));
sizes[i] = 40;
} }
sendPort = openUDP(49066, "127.0.0.1", 49009);
recvPort = openUDP(49008, "127.0.0.1", 49009);
strcpy(DREFArray[0], "sim/cockpit2/controls/yoke_pitch_ratio");
strcpy(DREFArray[1], "sim/cockpit2/controls/yoke_roll_ratio");
strcpy(DREFArray[2], "sim/cockpit2/controls/yoke_heading_ratio");
strcpy(DREFArray[3], "sim/flightmodel/engine/ENGN_thro");
strcpy(DREFArray[4], "sim/cockpit/switches/gear_handle_status");
strcpy(DREFArray[5], "sim/flightmodel/controls/flaprqst");
for (i = 0; i < 100; i++) {
DREFSizes[i] = (int)strlen(DREFArray[i]);
}
CTRL[3] = 0.8; // Throttle
CTRL[4] = 1; // Gear
CTRL[5] = 0.5; // Flaps
// Execute // Execute
sendCTRL(sendPort, 6, CTRL); psendCTRL(sock, CTRL, 6);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 6, recDATA, DREFSizes); // Test int result = getDREFs(sock, drefs, data, 6, sizes);
// Close // Close socket
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Tests // Tests
if (result < 0)// Request 1 value if (result < 0)// Request 1 value
{ {
return -6; return -6;
} }
for (i = 0; i<6; i++) for (int i = 0; i < 6; i++)
{ {
if (fabs(recDATA[i][0] - CTRL[i])>1e-4) if (fabs(data[i][0] - CTRL[i]) > 1e-4)
{
return -i - 1;
}
}
return 0;
}
short sendpCTRLTest()
{
printf("sendNonPlayerCTRL - ");
// Initialize
int i; // Iterator
char DREFArray[100][100];
float CTRL[6] = { 0.0 };
float *recDATA[100];
short DREFSizes[100];
struct xpcSocket sendPort, recvPort;
short result;
// Setup
for (i = 0; i < 100; i++)
{
recDATA[i] = (float *)malloc(40 * sizeof(float));
memset(DREFArray[i], 0, 100);
}
sendPort = openUDP(49066, "127.0.0.1", 49009);
recvPort = openUDP(49008, "127.0.0.1", 49009);
strcpy(DREFArray[0], "sim/multiplayer/position/plane1_yolk_pitch");
strcpy(DREFArray[1], "sim/multiplayer/position/plane1_yolk_roll");
strcpy(DREFArray[2], "sim/multiplayer/position/plane1_yolk_yaw");
strcpy(DREFArray[3], "sim/multiplayer/position/plane1_throttle");
strcpy(DREFArray[4], "sim/multiplayer/position/plane1_gear_deploy");
strcpy(DREFArray[5], "sim/multiplayer/position/plane1_flap_ratio");
for (i = 0; i < 100; i++)
{
DREFSizes[i] = (int)strlen(DREFArray[i]);
}
CTRL[3] = 0.8; // Throttle
CTRL[4] = 1; // Gear
CTRL[5] = 0.5; // Flaps
// Execute
sendpCTRL(sendPort, 6, CTRL, 1);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 9, recDATA, DREFSizes); // Test
// Close
closeUDP(sendPort);
closeUDP(recvPort);
// Tests
if (result < 0)// Request 1 value
{
return -6;
}
for (i = 0; i<6; i++)
{
if (fabs(recDATA[i][0] - CTRL[i])>1e-4)
{ {
return -i - 1; return -i - 1;
} }
@@ -368,51 +272,85 @@ short sendpCTRLTest()
return 0; return 0;
} }
short sendPOSITest() // sendPOSI test int sendCTRLTest()
{
// Initialize
char* drefs[100] =
{
"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",
};
float* data[100];
int sizes[100];
float CTRL[6] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F };
XPCSocket sock = openUDP("127.0.0.1", 49009, 49066);
// Setup
for (int i = 0; i < 100; i++)
{
data[i] = (float*)malloc(40 * sizeof(float));
sizes[i] = 40;
}
// Execute
sendCTRL(sock, CTRL, 6, 1);
int result = getDREFs(sock, drefs, data, 6, sizes);
// Close socket
closeUDP(sock);
// Tests
if (result < 0)// Request 1 value
{
return -6;
}
for (int i = 0; i < 6; i++)
{
if (fabs(data[i][0] - CTRL[i]) > 1e-4)
{
return -i - 1;
}
}
return 0;
}
int sendPOSITest() // sendPOSI test
{ {
printf("sendPOSI - ");
// Initialization // Initialization
int i; // Iterator int i; // Iterator
char DREFArray[100][100]; char* drefs[100] =
float POSI[8] = {0.0}; {
float *recDATA[100]; "sim/flightmodel/position/latitude",
short DREFSizes[100]; "sim/flightmodel/position/longitude",
struct xpcSocket sendPort, recvPort; "sim/flightmodel/position/y_agl",
short result; "sim/flightmodel/position/phi",
"sim/flightmodel/position/theta",
"sim/flightmodel/position/psi",
"sim/cockpit/switches/gear_handle_status"
};
float* data[100];
int sizes[100];
float POSI[8] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
XPCSocket sock = openUDP("127.0.0.1", 49009, 49063);
// Setup // Setup
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++)
recDATA[i] = (float *) malloc(40*sizeof(float)); {
memset(DREFArray[i],0,100); data[i] = (float*)malloc(40 * sizeof(float));
sizes[i] = 40;
} }
sendPort = openUDP( 49063, "127.0.0.1", 49009 );
recvPort = openUDP( 49008, "127.0.0.1", 49009 );
strcpy(DREFArray[0],"sim/flightmodel/position/latitude");
strcpy(DREFArray[1],"sim/flightmodel/position/longitude");
strcpy(DREFArray[2],"sim/flightmodel/position/y_agl");
strcpy(DREFArray[3],"sim/flightmodel/position/phi");
strcpy(DREFArray[4],"sim/flightmodel/position/theta");
strcpy(DREFArray[5],"sim/flightmodel/position/psi");
strcpy(DREFArray[6],"sim/cockpit/switches/gear_handle_status");
for (i=0;i<7;i++) {
DREFSizes[i] = (int) strlen(DREFArray[i]);
}
POSI[0] = 37.524; // Lat
POSI[1] = -122.06899; // Lon
POSI[2] = 2500; // Alt
POSI[3] = 0; // Pitch
POSI[4] = 0; // Roll
POSI[5] = 0; // Heading
POSI[6] = 1; // Gear
// Execution // Execution
sendPOSI( sendPort, 0, 7, POSI ); sendPOSI(sock, POSI, 7, 0);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 7, recDATA, DREFSizes); // Test int result = getDREFs(sock, drefs, data, 7, sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Tests // Tests
if ( result < 0 )// Request 1 value if ( result < 0 )// Request 1 value
@@ -421,114 +359,104 @@ short sendPOSITest() // sendPOSI test
} }
for (i=0;i<7-1;i++) for (i=0;i<7-1;i++)
{ {
if (i==2) if (i == 2)
{ {
continue; continue;
} }
if (fabs(recDATA[i][0]-POSI[i])>1e-4) if (fabs(data[i][0] - POSI[i]) > 1e-4)
{ {
return -i; return -i;
} }
} }
return 0; return 0;
} }
short sendWYPTTest() int sendWYPTTest()
{ {
printf("sendWYPT - ");
// Setup // Setup
struct xpcSocket sendPort = openUDP(49064, "127.0.0.1", 49009); XPCSocket sock = openUDP("127.0.0.1", 49009, 49064);
float points[] = float points[] =
{ {
37.5245, -122.06899, 2500, 37.5245F, -122.06899F, 2500,
37.455397, -122.050037, 2500, 37.455397F, -122.050037F, 2500,
37.469567, -122.051411, 2500, 37.469567F, -122.051411F, 2500,
37.479376, -122.060509, 2300, 37.479376F, -122.060509F, 2300,
37.482237, -122.076130, 2100, 37.482237F, -122.076130F, 2100,
37.474881, -122.087288, 1900, 37.474881F, -122.087288F, 1900,
37.467660, -122.079391, 1700, 37.467660F, -122.079391F, 1700,
37.466298, -122.090549, 1500, 37.466298F, -122.090549F, 1500,
37.362562, -122.039223, 1000, 37.362562F, -122.039223F, 1000,
37.361448, -122.034416, 1000, 37.361448F, -122.034416F, 1000,
37.361994, -122.026348, 1000, 37.361994F, -122.026348F, 1000,
37.365541, -122.022572, 1000, 37.365541F, -122.022572F, 1000,
37.373727, -122.024803, 1000, 37.373727F, -122.024803F, 1000,
37.403869, -122.041283, 50, 37.403869F, -122.041283F, 50,
37.418544, -122.049222, 6 37.418544F, -122.049222F, 6
}; };
// Test // Test
sendWYPT(sendPort, xpc_WYPT_ADD, points, 15); sendWYPT(sock, XPC_WYPT_ADD, points, 15);
// NOTE: Visually ensure waypoints are added in the sim
// Cleanup // Cleanup
closeUDP(sendPort); closeUDP(sock);
return 0; return 0;
} }
short pauseTest() // pauseSim test int pauseTest() // pauseSim test
{ {
printf("pauseSim - ");
// Initialize // Initialize
int i; // Iterator int i; // Iterator
char DREFArray[100][100]; char* drefs[100] =
float *recDATA[100]; {
short DREFSizes[100],RECSizes[100]; "sim/operation/override/override_planepath"
struct xpcSocket sendPort, recvPort; };
short result; float* data[100];
int sizes[100];
XPCSocket sock = openUDP("127.0.0.1", 49009, 49064);
// Setup // Setup
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++)
recDATA[i] = (float *) malloc(40*sizeof(float)); {
memset(DREFArray[i],0,100); data[i] = (float*)malloc(40 * sizeof(float));
} sizes[i] = 40;
// Setup
sendPort = openUDP( 49064, "127.0.0.1", 49009 );
recvPort = openUDP( 49008, "127.0.0.1", 49009 );
strcpy(DREFArray[0],"sim/operation/override/override_planepath");
for (i=0;i<1;i++) {
DREFSizes[i] = (int) strlen(DREFArray[i]);
} }
// Execute // Execute
pauseSim(sendPort, 1); pauseSim(sock, 1);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, RECSizes); // Test int result = getDREF(sock, drefs[0], data[0], sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Test // Test
if (result < 0) { if (result < 0)
{
return -1; return -1;
} }
if (recDATA[0][0] != 1) if (data[0][0] != 1)
{ {
return -2; return -2;
} }
// Reopen // Reopen
sendPort = openUDP( 49064, "127.0.0.1", 49009 ); sock = openUDP("127.0.0.1", 49009, 49064);
recvPort = openUDP( 49008, "127.0.0.1", 49009 );
// Execute 2 // Execute 2
pauseSim(sendPort, 0); pauseSim(sock, 0);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, RECSizes); // Test result = getDREF(sock, drefs[0], data[0], sizes);
// Close 2 // Close 2
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Test 2 // Test 2
if (result < 0) if (result < 0)
{ {
return -3; return -3;
} }
if (recDATA[0][0] != 0) if (data[0][0] != 0)
{ {
return -4; return -4;
} }
@@ -536,48 +464,39 @@ short pauseTest() // pauseSim test
return 0; return 0;
} }
short connTest() // setConn test int connTest() // setConn test
{ {
printf("setConn - ");
// Initialize // Initialize
int i; // Iterator char* drefs[100] =
char DREFArray[100][100]; {
float *recDATA[100]; "sim/cockpit/switches/gear_handle_status"
short DREFSizes[100]; };
struct xpcSocket sendPort, recvPort; float* data[100];
short result = 0; int sizes[100];
XPCSocket sock = openUDP("127.0.0.1", 49009, 49067);
#if (__APPLE__ || __linux) #if (__APPLE__ || __linux)
usleep(0); usleep(0);
#endif #endif
// Setup // Setup
sendPort = openUDP( 49067, "127.0.0.1", 49009 ); for (int i = 0; i < 100; ++i)
recvPort = openUDP( 49055, "127.0.0.1", 49009 ); {
strcpy(DREFArray[0],"sim/cockpit/switches/gear_handle_status"); data[i] = (float*)malloc(40 * sizeof(float));
for (i=0;i<1;i++) { sizes[i] = 40;
DREFSizes[i] = (int) strlen(DREFArray[i]); }
}
// Execution // Execution
setCONN(sendPort, 49055); setCONN(&sock, 49055);
result = requestDREF(sendPort, recvPort, DREFArray, DREFSizes, 1, recDATA, DREFSizes); // Test int result = getDREF(sock, drefs[0], data[0], sizes);
// Close // Close
closeUDP(sendPort); closeUDP(sock);
closeUDP(recvPort);
// Test // Test
if ( result < 0 )// No data received if ( result < 0 )// No data received
{ {
return -1; return -1;
} }
// Set up for next test
sendPort = openUDP( 49067, "127.0.0.1", 49009 );
setCONN(sendPort, 49008);
closeUDP(sendPort);
return 0; return 0;
} }
@@ -593,19 +512,19 @@ int main(int argc, const char * argv[])
printf("(Linux) \n"); printf("(Linux) \n");
#endif #endif
runTest(openTest); runTest(openTest, "open");
runTest(closeTest); runTest(closeTest, "close");
runTest(sendReadTest); runTest(sendReadTest, "send/read");
runTest(sendTEXTTest); runTest(sendTEXTTest, "TEXT");
runTest(requestDREFTest); runTest(requestDREFTest, "GETD");
runTest(sendDREFTest); runTest(sendDREFTest, "DREF");
runTest(sendDATATest); runTest(sendDATATest, "DATA");
runTest(sendCTRLTest); runTest(sendCTRLTest, "CTRL");
runTest(sendpCTRLTest); runTest(psendCTRLTest, "CTRL (player)");
runTest(sendPOSITest); runTest(sendPOSITest, "POSI");
runTest(sendWYPTTest); runTest(sendWYPTTest, "WYPT");
runTest(pauseTest); runTest(pauseTest, "SIMU");
runTest(connTest); runTest(connTest, "CONN");
printf( "----------------\nTest Summary\n\tFailed: %i\n\tPassed: %i\n", testFailed, testPassed ); printf( "----------------\nTest Summary\n\tFailed: %i\n\tPassed: %i\n", testFailed, testPassed );

View File

@@ -31,7 +31,7 @@ public class XPlaneConnectTest
{ {
fail(); fail();
} }
try(XPlaneConnect xpc = new XPlaneConnect(49007, "127.0.0.1", 49009)) try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49007))
{ {
assertNotNull(xpc); assertNotNull(xpc);
} }
@@ -39,7 +39,7 @@ public class XPlaneConnectTest
{ {
fail(); fail();
} }
try(XPlaneConnect xpc = new XPlaneConnect(49007, "127.0.0.1", 49009, 200)) try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49007, 200))
{ {
assertNotNull(xpc); assertNotNull(xpc);
} }
@@ -50,9 +50,9 @@ public class XPlaneConnectTest
} }
@Test @Test
public void testGetRecvPort() throws SocketException public void testGetRecvPort() throws Exception
{ {
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49008))
{ {
assertEquals(49008, xpc.getRecvPort()); assertEquals(49008, xpc.getRecvPort());
} }
@@ -87,11 +87,11 @@ public class XPlaneConnectTest
} }
@Test @Test
public void constructorTest_SocketAlreadyBound() throws SocketException public void constructorTest_SocketAlreadyBound() throws Exception
{ {
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect("127.0.0.1", 49009, 49008))
{ {
try(XPlaneConnect xpc2 = new XPlaneConnect()) try(XPlaneConnect xpc2 = new XPlaneConnect("127.0.0.1", 49009, 49008))
{ {
fail(); fail();
} }
@@ -106,7 +106,7 @@ public class XPlaneConnectTest
@Test(expected = UnknownHostException.class) @Test(expected = UnknownHostException.class)
public void constructorTest_InvalidHost() throws UnknownHostException public void constructorTest_InvalidHost() throws UnknownHostException
{ {
try(XPlaneConnect xpc = new XPlaneConnect(49007, "notarealhost", 49009)) try(XPlaneConnect xpc = new XPlaneConnect("notarealhost", 49009, 49007))
{ {
} }
@@ -123,7 +123,7 @@ public class XPlaneConnectTest
String dref = "sim/cockpit/switches/gear_handle_status"; String dref = "sim/cockpit/switches/gear_handle_status";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
float[] result = xpc.requestDREF(dref); float[] result = xpc.getDREF(dref);
assertEquals(1, result.length); assertEquals(1, result.length);
} }
} }
@@ -138,7 +138,7 @@ public class XPlaneConnectTest
}; };
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
float[][] result = xpc.requestDREFs(drefs); float[][] result = xpc.getDREFs(drefs);
assertEquals(2, result.length); assertEquals(2, result.length);
assertEquals(1, result[0].length); assertEquals(1, result[0].length);
assertEquals(4, result[1].length); assertEquals(4, result[1].length);
@@ -150,7 +150,7 @@ public class XPlaneConnectTest
{ {
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.requestDREFs(null); xpc.getDREFs(null);
fail(); fail();
} }
} }
@@ -161,7 +161,7 @@ public class XPlaneConnectTest
String[] drefs = new String[0]; String[] drefs = new String[0];
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.requestDREFs(drefs); xpc.getDREFs(drefs);
fail(); fail();
} }
} }
@@ -172,7 +172,7 @@ public class XPlaneConnectTest
String[] drefs = new String[300]; String[] drefs = new String[300];
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.requestDREFs(drefs); xpc.getDREFs(drefs);
fail(); fail();
} }
} }
@@ -184,7 +184,7 @@ public class XPlaneConnectTest
String[] drefs = new String[]{longDREF}; String[] drefs = new String[]{longDREF};
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.requestDREFs(drefs); xpc.getDREFs(drefs);
fail(); fail();
} }
} }
@@ -195,7 +195,7 @@ public class XPlaneConnectTest
String dref = ""; String dref = "";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.requestDREF(dref); xpc.getDREF(dref);
fail(); fail();
} }
} }
@@ -207,12 +207,12 @@ public class XPlaneConnectTest
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.pauseSim(true); xpc.pauseSim(true);
float[] result = xpc.requestDREF(dref); float[] result = xpc.getDREF(dref);
//assertEquals(1, result.length); //TODO: Why is this result 20 elements long in Java? (It's only one in MATLAB) //assertEquals(1, result.length); //TODO: Why is this result 20 elements long in Java? (It's only one in MATLAB)
assertEquals(1, result[0], 1e-4); assertEquals(1, result[0], 1e-4);
xpc.pauseSim(false); xpc.pauseSim(false);
result = xpc.requestDREF(dref); result = xpc.getDREF(dref);
//assertEquals(1, result.length); //assertEquals(1, result.length);
assertEquals(0, result[0], 1e-4); assertEquals(0, result[0], 1e-4);
} }
@@ -305,12 +305,12 @@ public class XPlaneConnectTest
String dref = "sim/cockpit/switches/gear_handle_status"; String dref = "sim/cockpit/switches/gear_handle_status";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
float gearHandle = xpc.requestDREF(dref)[0]; float gearHandle = xpc.getDREF(dref)[0];
float value = gearHandle > 0.5 ? 0 : 1; float value = gearHandle > 0.5 ? 0 : 1;
xpc.sendDREF(dref, value); xpc.setDREF(dref, value);
float result = xpc.requestDREF(dref)[0]; float result = xpc.getDREF(dref)[0];
assertEquals(value, result, 1e-4); assertEquals(value, result, 1e-4);
} }
} }
@@ -320,7 +320,7 @@ public class XPlaneConnectTest
{ {
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(null, 0); xpc.setDREF(null, 0);
fail(); fail();
} }
} }
@@ -332,7 +332,7 @@ public class XPlaneConnectTest
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(dref, null); xpc.setDREF(dref, null);
fail(); fail();
} }
} }
@@ -343,19 +343,22 @@ public class XPlaneConnectTest
String dref = "sim/cockpit/switches/gear_handle_status"; String dref = "sim/cockpit/switches/gear_handle_status";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(dref, new float[0]); xpc.setDREF(dref, new float[0]);
fail(); fail();
} }
} }
@Test(expected = IllegalArgumentException.class) @Test
public void testSendDREF_MessageTooLong() throws IOException public void testSendDREF_MessageTooLong() throws IOException
{ {
// NOTE: This test originally ensured that the client restricted messages to 255 bytes in length.
// This restriction is no longer necessary, however removing it uncovered a bug that caused
// the plugin to do a bad memcpy and crash. This test is left here to ensure that X-Plane
// does not crash when given erroneously large value arrays.
String dref = "sim/cockpit/switches/gear_handle_status"; String dref = "sim/cockpit/switches/gear_handle_status";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(dref, new float[200]); xpc.setDREF(dref, new float[200]);
fail();
} }
} }
@@ -365,7 +368,7 @@ public class XPlaneConnectTest
String dref = "sim/cockpit/switches/i/am/a/very/long/fake/dref/that/is/over/255/characters/./which/means/that/my/length/cant/be/encoded/in/the/single/byte/allocated/by/the/message/format/,/so/i/should/cause/an/exception/instead/./i/am/still/not/long/enough/./almost/there"; String dref = "sim/cockpit/switches/i/am/a/very/long/fake/dref/that/is/over/255/characters/./which/means/that/my/length/cant/be/encoded/in/the/single/byte/allocated/by/the/message/format/,/so/i/should/cause/an/exception/instead/./i/am/still/not/long/enough/./almost/there";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(dref, 0); xpc.setDREF(dref, 0);
fail(); fail();
} }
} }
@@ -376,7 +379,7 @@ public class XPlaneConnectTest
String dref = ""; String dref = "";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDREF(dref, 0); xpc.setDREF(dref, 0);
fail(); fail();
} }
} }
@@ -396,7 +399,7 @@ public class XPlaneConnectTest
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendCTRL(ctrl); xpc.sendCTRL(ctrl);
float[][] result = xpc.requestDREFs(drefs); float[][] result = xpc.getDREFs(drefs);
if(result.length < ctrl.length) if(result.length < ctrl.length)
{ {
fail(); fail();
@@ -425,8 +428,8 @@ public class XPlaneConnectTest
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendCTRL(ctrl, 1); xpc.sendCTRL(ctrl, 1);
float[][] result1 = xpc.requestDREFs(drefs1); float[][] result1 = xpc.getDREFs(drefs1);
float[][] result2 = xpc.requestDREFs(drefs2); float[][] result2 = xpc.getDREFs(drefs2);
if(result1.length != 4 || result2.length != 2) if(result1.length != 4 || result2.length != 2)
{ {
fail(); fail();
@@ -480,7 +483,7 @@ public class XPlaneConnectTest
xpc.sendPOSI(posi); xpc.sendPOSI(posi);
//TODO: It seems that these calls are a bit too fast. The dref request often gets stale data, causing the test to fail incorrectly. //TODO: It seems that these calls are a bit too fast. The dref request often gets stale data, causing the test to fail incorrectly.
try {Thread.sleep(100);}catch(InterruptedException ex){} try {Thread.sleep(100);}catch(InterruptedException ex){}
float[][] result = xpc.requestDREFs(drefs); float[][] result = xpc.getDREFs(drefs);
xpc.pauseSim(false); xpc.pauseSim(false);
if(result.length < posi.length) if(result.length < posi.length)
{ {
@@ -543,7 +546,7 @@ public class XPlaneConnectTest
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
xpc.sendDATA(data); xpc.sendDATA(data);
float[] result = xpc.requestDREF(dref); float[] result = xpc.getDREF(dref);
assertEquals(data[0][1], result[0], 1e-4); assertEquals(data[0][1], result[0], 1e-4);
} }
} }
@@ -575,15 +578,9 @@ public class XPlaneConnectTest
String dref = "sim/cockpit/switches/gear_handle_status"; String dref = "sim/cockpit/switches/gear_handle_status";
try(XPlaneConnect xpc = new XPlaneConnect()) try(XPlaneConnect xpc = new XPlaneConnect())
{ {
int p = xpc.getRecvPort();
xpc.setCONN(49055); xpc.setCONN(49055);
assertEquals(49055, xpc.getRecvPort()); assertEquals(49055, xpc.getRecvPort());
float[] result = xpc.requestDREF(dref); float[] result = xpc.getDREF(dref);
assertEquals(1, result.length);
xpc.setCONN(p);
assertEquals(p, xpc.getRecvPort());
result = xpc.requestDREF(dref);
assertEquals(1, result.length); assertEquals(1, result.length);
} }
} }

View File

@@ -1,6 +1,7 @@
function CTRLTest( ) function CTRLTest( )
%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
addpath('../../MATLAB') addpath('../../MATLAB')
import XPlaneConnect.* import XPlaneConnect.*
@@ -14,14 +15,15 @@ THROT = rand();
CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0]; CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0];
sendCTRL(CTRL); sendCTRL(CTRL);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),6),'CTRLTest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result),6),'CTRLTest: getDREFs unsucessful-wrong number of elements returned');
assert(isequal(length(result{4}),8),'CTRLTest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); assert(isequal(length(result{4}),8),'CTRLTest: getDREFs unsucessful- element 1 incorrect size (should be size 8)');
for i=1:length(CTRL)-1 for i=1:length(CTRL)-1
assert(abs(result{i}(1)-CTRL(i))<1e-4,['CTRLTest: DATA set unsucessful-',num2str(i)]); assert(abs(result{i}(1)-CTRL(i))<1e-4,['CTRLTest: DATA set unsucessful-',num2str(i)]);
end end
%% Test NPC aircraft
DREFS = {'sim/multiplayer/position/plane1_yolk_pitch',... DREFS = {'sim/multiplayer/position/plane1_yolk_pitch',...
'sim/multiplayer/position/plane1_yolk_roll',... 'sim/multiplayer/position/plane1_yolk_roll',...
'sim/multiplayer/position/plane1_yolk_yaw',... 'sim/multiplayer/position/plane1_yolk_yaw',...
@@ -32,7 +34,7 @@ THROT = rand();
CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0]; CTRL = [0.0, 0.0, 1.0, THROT, 0.0, 1.0];
sendCTRL(CTRL, 1); sendCTRL(CTRL, 1);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),6),'CTRLTest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result),6),'CTRLTest: requestDREF unsucessful-wrong number of elements returned');
assert(isequal(length(result{4}),8),'CTRLTest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); assert(isequal(length(result{4}),8),'CTRLTest: requestDREF unsucessful- element 1 incorrect size (should be size 8)');

View File

@@ -10,11 +10,10 @@ value = rand();
data = struct('h',25,'d',[value,-998,-998,-998,-998,-998,-998,-998]); data = struct('h',25,'d',[value,-998,-998,-998,-998,-998,-998,-998]);
sendDATA(data); sendDATA(data);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),1),'DATATest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result),8),'DATATest: getDREFs unsucessful-wrong number of elements returned');
assert(isequal(length(result{1}),8),'DATATest: requestDREF unsucessful- element 1 incorrect size (should be size 8)'); assert(abs(result(1)-value)<1e-4,'DATATest: DATA set unsucessful');
assert(abs(result{1}(1)-value)<1e-4,'DATATest: DATA set unsucessful');
end end

View File

@@ -14,14 +14,14 @@ DREFS = {'sim/flightmodel/position/latitude', ...
POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1]; % Gear POSI = [37.524, -122.06899, 2500, 0, 0, 0, 1]; % Gear
sendPOSI(POSI); sendPOSI(POSI);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),7),'POSITest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result),7),'POSITest: getDREFs unsucessful-wrong number of elements returned');
for i=1:length(POSI) for i=1:length(POSI)
if i==3 if i==3
continue continue
end end
assert(abs(result{i}(1)-POSI(i))<1e-4,['POSITest: DATA set unsucessful-',num2str(i)]); assert(abs(result(i)-POSI(i))<1e-4,['POSITest: DATA set unsucessful-',num2str(i)]);
end end
end end

View File

@@ -1,4 +1,4 @@
function requestDREFTest( ) function getDREFsTest( )
%SENDREADTEST Summary of this function goes here %SENDREADTEST Summary of this function goes here
% Detailed explanation goes here % Detailed explanation goes here
addpath('../../MATLAB') addpath('../../MATLAB')
@@ -7,7 +7,7 @@ import XPlaneConnect.*
DREFS = {'sim/cockpit/switches/gear_handle_status',... DREFS = {'sim/cockpit/switches/gear_handle_status',...
'sim/cockpit2/switches/panel_brightness_ratio'}; 'sim/cockpit2/switches/panel_brightness_ratio'};
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),2)); assert(isequal(length(result),2));
assert(isequal(length(result{1}),1)); assert(isequal(length(result{1}),1));

View File

@@ -4,13 +4,10 @@ function openCloseTest()
addpath('../../MATLAB') addpath('../../MATLAB')
import XPlaneConnect.* import XPlaneConnect.*
socket = openUDP( 49007 ); socket = openUDP();
closeUDP(socket);
closeUDP( socket ); socket = openUDP();
assert(isequal(socket.isClosed(),1),'openCloseTest: socket is still open'); closeUDP(socket);
socket = openUDP( 49007 );
closeUDP( socket );
end end

View File

@@ -9,17 +9,15 @@ DREFS = {'sim/operation/override/override_planepath'};
value = 1; % Pause value = 1; % Pause
pauseSim(value); pauseSim(value);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),1),'pauseTest: requestDREF unsucessful-wrong number of elements returned'); assert(abs(result(1)-value)<1e-4,'pauseTest: Pause Unsuccessful');
assert(abs(result{1}(1)-value)<1e-4,'pauseTest: Pause Unsuccessful');
value = 0; % Resume value = 0; % Resume
pauseSim(value); pauseSim(value);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),1),'pauseTest: requestDREF unsucessful-wrong number of elements returned'); assert(abs(result(1)-value)<1e-4,'pauseTest: Pause Unsuccessful');
assert(abs(result{1}(1)-value)<1e-4,'pauseTest: Pause Unsuccessful');
end end

View File

@@ -1,19 +0,0 @@
function sendDREFTest( )
%SENDREADTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
DREFS = {'sim/cockpit/switches/gear_handle_status'};
value = randi([0 10]);
sendDREF(DREFS{1},value);
result = requestDREF(DREFS);
assert(isequal(length(result),1),'sendDREFTest: requestDREF unsucessful-wrong number of elements returned');
assert(isequal(length(result{1}),1),'sendDREFTest: requestDREF unsucessful- element 1 incorrect size (should be size 1)');
assert(isequal(result{1},value),'sendDREFTest: DREF set unsucessful');
end

View File

@@ -1,18 +0,0 @@
function sendReadTest( )
%SENDREADTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
recvPortNum = 49074;
recvPort = openUDP(49074);
sendUDP('test2'-0,'127.0.0.1',49074);
result = readUDP(recvPort);
closeUDP(recvPort);
assert(~isequal(result,-998), 'no data received');
assert(all(result(1:4)'=='test'-0));
end

View File

@@ -8,7 +8,7 @@ import XPlaneConnect.*
DREFS = {'sim/cockpit/switches/gear_handle_status'}; DREFS = {'sim/cockpit/switches/gear_handle_status'};
setConn(49055); setConn(49055);
result = requestDREF(DREFS); result = getDREFs(DREFS);
assert(isequal(length(result),1),'setConnTest: requestDREF unsucessful-wrong number of elements returned'); assert(isequal(length(result),1),'setConnTest: requestDREF unsucessful-wrong number of elements returned');

View File

@@ -0,0 +1,18 @@
function setDREFTest( )
%SENDREADTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
DREFS = {'sim/cockpit/switches/gear_handle_status'};
value = randi([0 10]);
setDREF(DREFS{1},value);
result = getDREFs(DREFS);
assert(isequal(length(result),1),'setDREFTest: requestDREF unsucessful-wrong number of elements returned');
assert(isequal(result(1),value),'setDREFTest: DREF set unsucessful');
end

View File

@@ -1,19 +0,0 @@
function struTest( )
%STRUTEST Summary of this function goes here
% Detailed explanation goes here
addpath('../../MATLAB')
import XPlaneConnect.*
recvNum = 49076;
recvPort = openUDP(recvNum);
testSTRU = struct('h',15,'d',[12, 34, 55, 99, 1, 0],'test','theTest');
sendSTRU(testSTRU,'127.0.0.1',recvNum);
resultSTRU = readDATA(recvPort);
closeUDP(recvPort);
resultSTRU = rmfield(resultSTRU,'raw');
assert(isequal(testSTRU,resultSTRU),'struTest: Error-structs are not equal');
end

View File

@@ -11,16 +11,14 @@ end
disp(['XPC Tests-MATLAB (', os, ')']); disp(['XPC Tests-MATLAB (', os, ')']);
theTests = {{@openCloseTest, 'Open/Close Test', 0},... theTests = {{@openCloseTest, 'Open/Close Test', 0},...
{@sendReadTest,'Send/Read Test', 0},...
{@sendTEXTTest,'TEXT Test', 0},... {@sendTEXTTest,'TEXT Test', 0},...
{@requestDREFTest,'Request DREF Test', 0},... {@getDREFsTest,'Request DREF Test', 0},...
{@sendDREFTest,'Send DREF Test', 0},... {@setDREFTest,'Send DREF Test', 0},...
{@DATATest,'DATA Test', 0},... {@DATATest,'DATA Test', 0},...
{@CTRLTest,'CTRL Test', 0},... {@CTRLTest,'CTRL Test', 0},...
{@POSITest,'POSI Test', 0},... {@POSITest,'POSI Test', 0},...
{@sendWYPTTest,'WYPT Test', 0},... {@sendWYPTTest,'WYPT Test', 0},...
{@pauseTest,'Pause Test', 0},... {@pauseTest,'Pause Test', 0},...
{@struTest,'Struct Test', 0},...
{@setConnTest, 'setConn Test', 0}}; {@setConnTest, 'setConn Test', 0}};
for i=1:length(theTests) for i=1:length(theTests)

View File

@@ -3,12 +3,17 @@
#ifndef XPC_DRAWING_H #ifndef XPC_DRAWING_H
#define XPC_DRAWING_H #define XPC_DRAWING_H
#include "xplaneConnect.h"
#include <cstdlib> #include <cstdlib>
namespace XPC namespace XPC
{ {
typedef struct
{
double latitude;
double longitude;
double altitude;
} Waypoint;
/// Handles tasks that involve drawing to the screen in X-Plane. /// Handles tasks that involve drawing to the screen in X-Plane.
/// ///
/// \author Jason Watkins /// \author Jason Watkins

View File

@@ -2,7 +2,6 @@
//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 "xplaneConnect.h"
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
@@ -71,6 +70,7 @@ namespace XPC
} }
Log::WriteLine(ss.str()); Log::WriteLine(ss.str());
ss << std::dec;
ss.str(""); ss.str("");
ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")"; ss << "[" << GetHead() << "-DEBUG] (" << GetSize() << ")";
switch (GetMagicNumber()) // Binary version of head switch (GetMagicNumber()) // Binary version of head
@@ -84,25 +84,41 @@ namespace XPC
} }
case 0x4C525443: // CTRL case 0x4C525443: // CTRL
{ {
xpcCtrl ctrl = parseCTRL((char*)buffer); // Parse message data
ss << " (" << ctrl.pitch << " " << ctrl.roll << " " << ctrl.yaw << ") "; float pitch = *((float*)(buffer + 5));
ss << ctrl.throttle << " " << ctrl.gear << " " << ctrl.flaps; float roll = *((float*)(buffer + 9));
float yaw = *((float*)(buffer + 13));
float thr = *((float*)(buffer + 17));
char gear = buffer[21];
float flaps = *((float*)(buffer + 22));
std::uint8_t aircraft = 0;
if (size == 27)
{
aircraft = buffer[26];
}
ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")";
ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps;
Log::WriteLine(ss.str()); Log::WriteLine(ss.str());
break; break;
} }
case 0x41544144: // DATA case 0x41544144: // DATA
{ {
float dataRef[30][9]; std::size_t numCols = (size - 5) / 36;
short numCols = parseDATA((char*)buffer, size, dataRef); float values[32][9];
for (int i = 0; i < numCols; ++i)
{
values[i][0] = buffer[5 + 36 * i];
memcpy(values[i] + 1, buffer + 9 + 36 * i, 9 * sizeof(float));
}
ss << " (" << numCols << " lines)"; ss << " (" << numCols << " lines)";
Log::WriteLine(ss.str()); Log::WriteLine(ss.str());
for (int i = 0; i < numCols; ++i) for (int i = 0; i < numCols; ++i)
{ {
ss.str(""); ss.str("");
ss << "\t#" << dataRef[i][0]; ss << "\t#" << values[i][0];
for (int j = 1; j < 9; ++j) for (int j = 1; j < 9; ++j)
{ {
ss << " " << dataRef[i][j]; ss << " " << values[i][j];
} }
Log::WriteLine(ss.str()); Log::WriteLine(ss.str());
} }
@@ -136,14 +152,16 @@ namespace XPC
break; break;
} }
case 0x49534F50: // POSI case 0x49534F50: // POSI
{ {
float position[6] = { 0.0 }; char aircraft = buffer[5];
short aircraft = 0; float gear = *((float*)(buffer + 30));
float gear = -1.0; float pos[3];
aircraft = parsePOSI((char*)buffer, position, 6, &gear); float orient[3];
ss << ' ' << aircraft; memcpy(pos, buffer + 6, 12);
ss << " (" << position[0] << ' ' << position[1] << ' ' << position[2] << ") ("; memcpy(orient, buffer + 18, 12);
ss << position[3] << ' ' << position[4] << ' ' << position[5] << ") "; ss << " AC:" << (int)aircraft;
ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:(";
ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:";
ss << gear; ss << gear;
Log::WriteLine(ss.str()); Log::WriteLine(ss.str());
break; break;

View File

@@ -55,48 +55,6 @@ namespace XPC
MessageHandlers::sock = socket; MessageHandlers::sock = socket;
} }
static std::string getIP(sockaddr* sa)
{
char ip[INET6_ADDRSTRLEN + 6] = { 0 };
switch (sa->sa_family)
{
case AF_INET:
{
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa);
inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN);
break;
}
case AF_INET6:
{
sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa);
inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN);
break;
}
default:
return "UNKNOWN";
}
return std::string(ip);
}
static unsigned short getPort(sockaddr* sa)
{
switch (sa->sa_family)
{
case AF_INET:
{
sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sa);
return ntohs((*sin).sin_port);
}
case AF_INET6:
{
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(sa);
return ntohs((*sin).sin6_port);
}
default:
return ~0;
}
}
void MessageHandlers::HandleMessage(Message& msg) void MessageHandlers::HandleMessage(Message& msg)
{ {
// Make sure we really have a message to handle. // Make sure we really have a message to handle.
@@ -109,9 +67,7 @@ namespace XPC
// Set current connection // Set current connection
sockaddr sourceaddr = msg.GetSource(); sockaddr sourceaddr = msg.GetSource();
std::string ip = getIP(&sourceaddr); connectionKey = UDPSocket::GetHost(&sourceaddr);
unsigned short port = getPort(&sourceaddr);
connectionKey = ip + ":" + std::to_string(port);
#if LOG_VERBOSITY > 4 #if LOG_VERBOSITY > 4
Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str()); Log::FormatLine("[MSGH] Handling message from %s", connectionKey.c_str());
#endif #endif
@@ -124,22 +80,20 @@ namespace XPC
// to connections. As long as we never remove elements, the size of // to connections. As long as we never remove elements, the size of
// connections will serve as a unique id. // connections will serve as a unique id.
static_cast<unsigned char>(connections.size()), static_cast<unsigned char>(connections.size()),
ip, sourceaddr,
49008, // By default, send information to the client on this port.
port,
0 0
}; };
connections[connectionKey] = connection; connections[connectionKey] = connection;
#if LOG_VERBOSITY > 4 #if LOG_VERBOSITY > 2
Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s:%u", connection.id, ip.c_str(), port); Log::FormatLine("[MSGH] New connection. ID=%u, Remote=%s", connection.id, connectionKey.c_str());
#endif #endif
} }
else else
{ {
connection = (*conn).second; connection = (*conn).second;
#if LOG_VERBOSITY > 4 #if LOG_VERBOSITY > 3
Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s:%u", Log::FormatLine("[MSGH] Existing connection. ID=%u, Remote=%s",
connection.id, connection.ip.c_str(), connection.srcPort); connection.id, connectionKey.c_str());
#endif #endif
} }
@@ -162,7 +116,30 @@ namespace XPC
const unsigned char* buffer = msg.GetBuffer(); const unsigned char* buffer = msg.GetBuffer();
// Store new port // Store new port
connection.dstPort = *((unsigned short*)(buffer + 5)); std::uint16_t port = *((std::uint16_t*)(buffer + 5));
sockaddr* sa = &connection.addr;
switch (sa->sa_family)
{
case AF_INET:
{
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa);
(*sin).sin_port = htons(port);
break;
}
case AF_INET6:
{
sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa);
(*sin).sin6_port = htons(port);
break;
}
default:
#if LOG_VERBOSITY > 0
Log::WriteLine("[CONN] ERROR: Unknown address type.");
return;
#endif
}
connections.erase(connectionKey);
connectionKey = UDPSocket::GetHost(&connection.addr);
connections[connectionKey] = connection; connections[connectionKey] = connection;
// Create response // Create response
@@ -170,19 +147,19 @@ namespace XPC
response[5] = connection.id; response[5] = connection.id;
// Update log // Update log
#if LOG_VERBOSITY > 0 #if LOG_VERBOSITY > 1
Log::FormatLine("[CONN] ID: %u New destination port: %u", Log::FormatLine("[CONN] ID: %u New destination port: %u",
connection.id, connection.dstPort); connection.id, port);
#endif #endif
// Send response // Send response
sock->SendTo(response, 6, connection.ip, connection.dstPort); sock->SendTo(response, 6, &connection.addr);
} }
void MessageHandlers::HandleCtrl(Message& msg) void MessageHandlers::HandleCtrl(Message& msg)
{ {
// Update Log // Update Log
#if LOG_VERBOSITY > 0 #if LOG_VERBOSITY > 2
Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id); Log::FormatLine("[CTRL] Message Received (Conn %i)", connection.id);
#endif #endif
@@ -192,7 +169,7 @@ namespace XPC
//Packets specifying an A/C num should be 27 bytes. //Packets specifying an A/C num should be 27 bytes.
if (size != 26 && size != 27) if (size != 26 && size != 27)
{ {
#if LOG_VERBOSITY > 1 #if LOG_VERBOSITY > 0
Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size); Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size);
#endif #endif
return; return;
@@ -244,20 +221,20 @@ 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 > 3 #if LOG_VERBOSITY > 2
Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id); Log::FormatLine("[DATA] Message Received (Conn %i)", connection.id);
#endif #endif
} }
if (numCols > 32) // Error. Will overflow values if (numCols > 32) // Error. Will overflow values
{ {
#if LOG_VERBOSITY > 2 #if LOG_VERBOSITY > 0
Log::FormatLine("[DATA] ERROR numCols to large."); Log::FormatLine("[DATA] ERROR numCols to large.");
#endif #endif
return; return;
} }
else else
{ {
#if LOG_VERBOSITY > 2 #if LOG_VERBOSITY > 1
Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id); Log::FormatLine("[DATA] WARNING: Empty data packet received (Conn %i)", connection.id);
#endif #endif
return; return;
@@ -405,8 +382,7 @@ namespace XPC
std::string dref = std::string((char*)buffer + 6, len); std::string dref = std::string((char*)buffer + 6, len);
unsigned char valueCount = buffer[6 + len]; unsigned char valueCount = buffer[6 + len];
float values[40]; float* values = (float*)(buffer + 7 + len);
memcpy(values, buffer + len + 7, valueCount * sizeof(float));
#if LOG_VERBOSITY > 1 #if LOG_VERBOSITY > 1
Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str()); Log::FormatLine("[DREF] Request to set DREF value received (Conn %i): %s", connection.id, dref.c_str());
@@ -462,7 +438,7 @@ namespace XPC
cur += count * sizeof(float); cur += count * sizeof(float);
} }
sock->SendTo(response, cur, connection.ip, connection.dstPort); sock->SendTo(response, cur, &connection.addr);
} }
void MessageHandlers::HandlePosi(Message& msg) void MessageHandlers::HandlePosi(Message& msg)
@@ -599,13 +575,13 @@ namespace XPC
#endif #endif
switch (op) switch (op)
{ {
case xpc_WYPT_ADD: case 1:
Drawing::AddWaypoints(points, count); Drawing::AddWaypoints(points, count);
break; break;
case xpc_WYPT_DEL: case 2:
Drawing::RemoveWaypoints(points, count); Drawing::RemoveWaypoints(points, count);
break; break;
case xpc_WYPT_CLR: case 3:
Drawing::ClearWaypoints(); Drawing::ClearWaypoints();
break; break;
default: default:
@@ -621,7 +597,11 @@ namespace XPC
#if LOG_VERBOSITY > 1 #if LOG_VERBOSITY > 1
Log::WriteLine("[MSGH] Sending raw data to X-Plane"); Log::WriteLine("[MSGH] Sending raw data to X-Plane");
#endif #endif
sock->SendTo((unsigned char*)msg.GetBuffer(), msg.GetSize(), "127.0.0.1", 49000); sockaddr_in loopback;
loopback.sin_family = AF_INET;
loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
loopback.sin_port = htons(49000);
sock->SendTo((std::uint8_t*)msg.GetBuffer(), msg.GetSize(), (sockaddr*)&loopback);
} }
void MessageHandlers::HandleUnknown(Message& msg) void MessageHandlers::HandleUnknown(Message& msg)

View File

@@ -52,9 +52,7 @@ namespace XPC
typedef struct typedef struct
{ {
unsigned char id; unsigned char id;
std::string ip; sockaddr addr;
unsigned short dstPort;
unsigned short srcPort;
unsigned char getdCount; unsigned char getdCount;
std::string getdRequest[255]; std::string getdRequest[255];
} ConnectionInfo; } ConnectionInfo;

View File

@@ -133,41 +133,49 @@ namespace XPC
return status; return status;
} }
// TODO: Add IPV6 support void UDPSocket::SendTo(std::uint8_t* buffer, std::size_t len, sockaddr* remote)
void UDPSocket::SendTo(unsigned char* buffer, std::size_t len, std::string remoteHost, unsigned short remotePort)
{ {
#if LOG_VERBOSITY > 4 buffer[4] = (std::uint8_t)len;
Log::FormatLine("[SOCK] Sending message to %s:%u (length=%u)", remoteHost.c_str(), remotePort, len); if (sendto(sock, (char*)buffer, (int)len, 0, remote, sizeof(*remote)) < 0)
#endif
if (remoteHost == "localhost")
{
remoteHost = "127.0.0.1";
}
unsigned long ip;
inet_pton(AF_INET, remoteHost.c_str(), &ip);
SendTo(buffer, len, ip, remotePort);
}
void UDPSocket::SendTo(unsigned char* buffer, std::size_t len, unsigned long remoteIP, unsigned short remotePort)
{
struct sockaddr_in remoteAddr;
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(remotePort);
remoteAddr.sin_addr.s_addr = remoteIP;
#ifdef _WIN32
const char on = 1;
#else
int on = 1;
#endif
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
buffer[4] = (unsigned char)len;
if (sendto(sock, (char*)buffer, (int)len, 0, (const struct sockaddr *) &remoteAddr, sizeof(remoteAddr)) < 0)
{ {
#if LOG_VERBOSITY > 0 #if LOG_VERBOSITY > 0
Log::FormatLine("[SOCK] Send failed. (remote: %s:%d)", remoteAddr, remotePort); Log::FormatLine("[SOCK] Send failed. (remote: %s)", GetHost(remote).c_str());
#endif
}
else
{
#if LOG_VERBOSITY > 3
Log::FormatLine("[SOCK] Datagram sent. (remote: %s)", GetHost(remote).c_str());
#endif #endif
} }
} }
std::string UDPSocket::GetHost(sockaddr* sa)
{
char ip[INET6_ADDRSTRLEN + 6] = { 0 };
switch (sa->sa_family)
{
case AF_INET:
{
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(sa);
inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN);
int len = strnlen(ip, INET6_ADDRSTRLEN);
ip[len++] = ':';
sprintf(ip + len, "%d", ntohs((*sin).sin_port));
break;
}
case AF_INET6:
{
sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(sa);
inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN);
int len = strnlen(ip, INET6_ADDRSTRLEN);
ip[len++] = ':';
sprintf(ip + len, "%d", ntohs((*sin).sin6_port));
break;
}
default:
return "UNKNOWN";
}
return std::string(ip);
}
} }

View File

@@ -52,20 +52,16 @@ namespace XPC
/// 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 remoteHost The hostname of the destination client. /// \param remote The destination socket.
/// \param remotePort The port of the destination client. void SendTo(std::uint8_t* buffer, std::size_t len, sockaddr* remote);
void SendTo(unsigned char* buffer, std::size_t len, std::string remoteHost, unsigned short remotePort);
/// Sends data to the specified remote endpoint. /// Gets a string containing the IP address and port contained in the given sockaddr.
/// ///
/// \param data The data to be sent. /// \param addr The socket address to parse.
/// \param len The number of bytes to send. /// \returns A string representation of the socket address.
/// \param remoteHost The hostname of the destination client. static std::string GetHost(sockaddr* addr);
/// \param remotePort The port of the destination client.
void SendTo(unsigned char* buffer, std::size_t len, unsigned long remoteIP, unsigned short remotePort);
private: private:
#ifdef _WIN32 #ifdef _WIN32
SOCKET sock; SOCKET sock;

View File

@@ -70,11 +70,9 @@
#endif #endif
#define RECVPORT 49009 // Port that the plugin receives commands on #define RECVPORT 49009 // Port that the plugin receives commands on
#define SENDPORT 49097 // Port that the plugin sends on
#define OPS_PER_CYCLE 20 // Max Number of operations per cycle #define OPS_PER_CYCLE 20 // Max Number of operations per cycle
XPC::UDPSocket* recvSocket = nullptr; XPC::UDPSocket* sock = nullptr;
XPC::UDPSocket* sendSocket = nullptr;
double start,lap; double start,lap;
static double timeConvert = 0.0; static double timeConvert = 0.0;
@@ -125,10 +123,8 @@ PLUGIN_API void XPluginStop(void)
PLUGIN_API void XPluginDisable(void) PLUGIN_API void XPluginDisable(void)
{ {
// Close sockets // Close sockets
delete recvSocket; delete sock;
delete sendSocket; sock = nullptr;
recvSocket = nullptr;
sendSocket = nullptr;
// Stop rendering messages to screen. // Stop rendering messages to screen.
XPC::Drawing::ClearMessage(); XPC::Drawing::ClearMessage();
@@ -142,9 +138,8 @@ PLUGIN_API void XPluginDisable(void)
PLUGIN_API int XPluginEnable(void) PLUGIN_API int XPluginEnable(void)
{ {
// Open sockets // Open sockets
recvSocket = new XPC::UDPSocket(RECVPORT); sock = new XPC::UDPSocket(RECVPORT);
sendSocket = new XPC::UDPSocket(SENDPORT); XPC::MessageHandlers::SetSocket(sock);
XPC::MessageHandlers::SetSocket(sendSocket);
XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened"); XPC::Log::WriteLine("[EXEC] Plugin Enabled, sockets opened");
if (benchmarkingSwitch > 0) if (benchmarkingSwitch > 0)
@@ -188,7 +183,7 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
#endif #endif
} }
XPC::Message msg = XPC::Message::ReadFrom(*recvSocket); XPC::Message msg = XPC::Message::ReadFrom(*sock);
if (msg.GetHead() == "") if (msg.GetHead() == "")
{ {
break; break;
@@ -208,8 +203,8 @@ float XPCFlightLoopCallback(float inElapsedSinceLastCall,
if (cyclesToClear != -1 && counter%cyclesToClear == 0) if (cyclesToClear != -1 && counter%cyclesToClear == 0)
{ {
XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer"); XPC::Log::WriteLine("[EXEC] Cleared UDP Buffer");
delete recvSocket; delete sock;
recvSocket = new XPC::UDPSocket(RECVPORT); sock = new XPC::UDPSocket(RECVPORT);
} }
return -1; return -1;
} }

Binary file not shown.

Binary file not shown.

View File

@@ -42,7 +42,7 @@
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<OutDir>..\XPlaneConnect\</OutDir> <OutDir>..\XPlaneConnect\</OutDir>
<TargetExt>.xpl</TargetExt> <TargetExt>.xpl</TargetExt>
<IncludePath>..\xpcPlugin\SDK\CHeaders\XPLM;..\SDK\CHeaders\XPLM;..\..\C\src;$(IncludePath)</IncludePath> <IncludePath>..\SDK\CHeaders\XPLM;$(IncludePath)</IncludePath>
<LibraryPath>..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath)</LibraryPath> <LibraryPath>..\xpcPlugin\SDK\Libraries\Win;$(LibraryPath)</LibraryPath>
<TargetName>win</TargetName> <TargetName>win</TargetName>
</PropertyGroup> </PropertyGroup>
@@ -99,7 +99,6 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h" />
<ClInclude Include="..\DataManager.h" /> <ClInclude Include="..\DataManager.h" />
<ClInclude Include="..\DataMaps.h" /> <ClInclude Include="..\DataMaps.h" />
<ClInclude Include="..\Drawing.h" /> <ClInclude Include="..\Drawing.h" />
@@ -109,7 +108,6 @@
<ClInclude Include="..\UDPSocket.h" /> <ClInclude Include="..\UDPSocket.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\C\src\xplaneConnect.c" />
<ClCompile Include="..\DataManager.cpp" /> <ClCompile Include="..\DataManager.cpp" />
<ClCompile Include="..\DataMaps.cpp" /> <ClCompile Include="..\DataMaps.cpp" />
<ClCompile Include="..\Drawing.cpp" /> <ClCompile Include="..\Drawing.cpp" />

View File

@@ -15,9 +15,6 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\C\src\xplaneConnect.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\Log.h"> <ClInclude Include="..\Log.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@@ -41,9 +38,6 @@
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\C\src\xplaneConnect.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\XPCPlugin.cpp"> <ClCompile Include="..\XPCPlugin.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>