Skip to content

Instantly share code, notes, and snippets.

@eburlingame
Created November 19, 2018 17:52
Show Gist options
  • Save eburlingame/30f2453061053180e8215e21b494bc5e to your computer and use it in GitHub Desktop.
Save eburlingame/30f2453061053180e8215e21b494bc5e to your computer and use it in GitHub Desktop.
Basic UDP client to get/set data from XPlane using C on Windows
#include "xplane_client.h"
/*----------------------------------------------
Statics
----------------------------------------------*/
static int client_s;
static sockaddr_in server_addr;
static sim_data_type current_state;
static char is_socket_open;
static void NET_request_dataref(int client, int idx, int freq, char *str)
{
rref_request_type tosend;
memset(&tosend, 0, sizeof(tosend));
strncpy(&tosend.cmd, "RREF", 4);
tosend.cmd[4] = 0;
strncpy(&tosend.str, str, strlen(str) + 1);
tosend.freq = freq;
tosend.idx = idx;
size_t s = sizeof(tosend);
if ( s != 413 )
return;
XINT result = sendto(client, &tosend, sizeof(tosend), 0, (const struct sockaddr*)&server_addr, sizeof(rref_request_type));
if (result < 0)
{
int err = WSAGetLastError();
is_socket_open = 0;
//print("sendto() failed with error code : %d\n", err);
return;
}
}
static void NET_set_int_dataref(int client, char *str, int val)
{
dref_int_struct tosend;
memset(&tosend, 0, sizeof(tosend));
strncpy(&tosend.cmd, "DREF", 4);
strncpy(&tosend.dref_path, str, strlen(str) + 1);
tosend.var = val;
size_t s = sizeof(tosend);
if ( s != 509 )
return;
XINT result = sendto(client, &tosend, sizeof(tosend), 0, (const struct sockaddr*)&server_addr, sizeof(rref_request_type));
if (result < 0)
{
int err = WSAGetLastError();
is_socket_open = 0;
//print("sendto() failed with error code : %d\n", err);
return;
}
}
static void NET_set_float_dataref(int client, char *str, float val)
{
dref_float_struct tosend;
memset(&tosend, 0, sizeof(tosend));
strncpy(&tosend.cmd, "DREF", 4);
strncpy(&tosend.dref_path, str, strlen(str) + 1);
tosend.var = val;
size_t s = sizeof(tosend);
if ( s != 509 )
return;
XINT result = sendto(client, &tosend, sizeof(tosend), 0, (const struct sockaddr*)&server_addr, sizeof(rref_request_type));
if (result < 0)
{
int err = WSAGetLastError();
is_socket_open = 0;
//print("sendto() failed with error code : %d\n", err);
return;
}
}
static void NET_recv(int client, sockaddr_in server_addr)
{
XCHR data_out[net_SIZE_buff];
int i, num_structs;
int retcode;
i = 0;
do {
int addr_len = sizeof(server_addr);
retcode = recvfrom(client, data_out, net_SIZE_buff, 0, (struct sockaddr *)&server_addr, &addr_len);
if (retcode < 0)
{
int err = WSAGetLastError();
//printf("recvfrom() failed with error code : %d\n", err);
return;
}
if (strncmp(data_out, "RREF", 4) == 0)
{
num_structs = (retcode - 5) / sizeof(rref_data_type);
rref_data_type *f = data_out + 5;
for (i = 0; i < num_structs; i += 1)
{
((float *) &current_state)[ f[i].idx ] = f[i].val;
}
}
i++;
} while (retcode > 0); // Repeat until the socket no longer has any data
}
static int setup_socket(int *client_s, sockaddr_in *server_addr)
{
struct timeval read_timeout;
int opt;
u_long iMode = 1; // Non-blocking mode (https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-ioctlsocket)
opt = 1;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 10;
*client_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (client_s < 0)
{
//printf("*** ERROR - socket() failed \n");
is_socket_open = 0;
WSACleanup();
return 1;
}
opt = 1;
if (setsockopt(*client_s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) == SOCKET_ERROR)
{
//printf("\nERROR in setting SO_REUSEADDR : %d \n", WSAGetLastError());
is_socket_open = 0;
closesocket(*client_s);
WSACleanup();
return 1;
}
if (ioctlsocket(*client_s, FIONBIO, &iMode) != NO_ERROR)
{
is_socket_open = 0;
closesocket(*client_s);
WSACleanup();
return 1;
}
// UDP Timeout
if (setsockopt(*client_s, SOL_SOCKET, SO_RCVTIMEO, &read_timeout, sizeof(read_timeout)) == SOCKET_ERROR)
{
//printf("\nERROR in broadcasting ERROR CODE : %d \n", WSAGetLastError());
is_socket_open = 0;
closesocket(*client_s);
WSACleanup();
return 1;
}
// UDP SOCKET Binding
//if (bind(*client_s, (sockaddr *)server_addr, sizeof(sockaddr_in)) == SOCKET_ERROR)
//{
// int err = WSAGetLastError();
// //printf("\nUDP socket binding failed ERROR CODE : %d\n", err);
// is_socket_open = 0;
// closesocket(*client_s);
// WSACleanup();
// return 1;
//}
is_socket_open = 1;
return 0;
}
void xplane_client_open()
{
WORD wVersionRequested = MAKEWORD(1, 1); // Stuff for WSA functions
WSADATA wsaData; // Stuff for WSA functions
if (is_socket_open)
return;
WSAStartup(wVersionRequested, &wsaData);
server_addr.sin_family = AF_INET; // Address family to use
server_addr.sin_port = htons(PORT_NUM); // Port num to use
server_addr.sin_addr.S_un.S_addr = inet_addr(IP_ADDR);
setup_socket(&client_s, &server_addr);
NET_request_dataref(client_s, 0, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/latitude");
NET_request_dataref(client_s, 1, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/longitude");
NET_request_dataref(client_s, 2, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/elevation");
NET_request_dataref(client_s, 3, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/y_agl");
NET_request_dataref(client_s, 4, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/groundspeed");
NET_request_dataref(client_s, 5, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/true_airspeed");
NET_request_dataref(client_s, 6, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/vh_ind");
NET_request_dataref(client_s, 7, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/true_psi");
NET_request_dataref(client_s, 8, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/mag_psi");
NET_request_dataref(client_s, 9, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/R");
NET_request_dataref(client_s, 10, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/hpath");
NET_request_dataref(client_s, 11, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/true_theta");
NET_request_dataref(client_s, 12, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/true_phi");
NET_request_dataref(client_s, 13, DEFAULT_UPDATE_RATE, "sim/cockpit2/gauges/indicators/slip_deg");
NET_request_dataref(client_s, 14, DEFAULT_UPDATE_RATE, "sim/flightmodel/position/magnetic_variation");
}
void xplane_client_poll()
{
int i;
if (!is_socket_open)
{
return;
}
NET_recv(client_s, server_addr);
}
void xplane_client_close()
{
if (closesocket(client_s) < 0)
{
//printf("*** ERROR - closesocket() failed \n");
is_socket_open = 0;
return;
}
WSACleanup();
}
sim_data_type *get_current_sim_state()
{
return &current_state;
}
void set_float_dataref(char *ref, float val)
{
NET_set_float_dataref(client_s, ref, val);
}
// XPlaneC.cpp : Defines the entry point for the console application.
// References
// http://www.nuclearprojects.com/xplane/xplaneref.html
// http://www.nuclearprojects.com/xplane/info.shtml
// https://docs.microsoft.com/en-us/windows/desktop/winsock/windows-sockets-error-codes-2
// https://stackoverflow.com/questions/32471004/udp-client-not-receiving-udp-server-message
#ifndef XPLANE_CLIENT
#define XPLANE_CLIENT
#include <string.h>
#include <windows.h> // Needed for all Winsock stuff
#pragma comment(lib,"ws2_32.lib") // Winsock Library
/*----------------------------------------------
Defines
----------------------------------------------*/
#define PORT_NUM 49000 // Port number used
#define IP_ADDR "127.0.0.1" // IP address of server1 (*** HARDWIRED ***)
#define XCHR char // (character, in local byte - order for the machine you are on)
#define XINT int // (4 - byte int, in local byte - order for the machine you are on)
#define XFLT float // (4 - byte ints and floats, in local byte - order for the machine you are on)
#define XDOB double // (double - precision float, in local byte - order for the machine you are on)
#define strDIM 500
#define vehDIM 10
#define net_SIZE_buff 1024
#define DEFAULT_UPDATE_RATE 20
/*----------------------------------------------
Data Types
----------------------------------------------*/
typedef struct sim_data_type {
float latitude_deg; // 0: sim/flightmodel/position/latitude
float longitude_deg; // 1: sim/flightmodel/position/longitude
float altitude_m_msl; // 2: sim/flightmodel/position/elevation
float altitude_m_agl; // 3: sim/flightmodel/position/y_agl
float ground_speed_mps; // 4: sim/flightmodel/position/groundspeed
float true_airspeed_mps; // 5: sim/flightmodel/position/true_airspeed
float vertical_speed_mps; // 6: sim/flightmodel/position/vh_ind
float true_heading_deg; // 7: sim/flightmodel/position/true_psi
float mag_heading_deg; // 8: sim/flightmodel/position/mag_psi
float true_heading_rate; // 9: sim/flightmodel/position/R
float track_deg; // 10: sim/flightmodel/hpath
float pitch_deg; // 11: sim/flightmodel/position/true_theta
float roll_deg; // 12: sim/flightmodel/position/true_phi
float slip_skid; // 13: sim/cockpit2/gauges/indicators/slip_deg
float mag_variation_deg; // 14: sim/flightmodel/position/magnetic_variation
} sim_data_type;
enum {
XPLN_LATITUDE_DEG,
};
#pragma pack(push,1)
typedef struct rref_request_type {
XCHR cmd[5];
XINT freq;
XINT idx;
XCHR str[400];
} rref_request_type;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct dref_int_struct {
XCHR cmd[5];
XINT var;
XCHR dref_path[500];
} dref_int_struct;
#pragma pack(pop)
#pragma pack(push,1)
typedef struct dref_float_struct {
XCHR cmd[5];
XFLT var;
XCHR dref_path[500];
} dref_float_struct;
#pragma pack(pop)
typedef struct rref_data_type {
XINT idx;
XFLT val;
} rref_data_type;
typedef struct sockaddr_in sockaddr_in;
typedef struct sockaddr sockaddr;
/*----------------------------------------------
API Functions
----------------------------------------------*/
void xplane_client_open();
void xplane_client_poll();
void xplane_client_close();
sim_data_type *get_current_sim_state();
void set_float_dataref(char *ref, float val);
#endif
@RakeshVangaveti
Copy link

hello @eric how do we compile /run this client program before starting XPlane or after ?, i know about sockets in linux ,but im not aware of winsock's , in windows how to run this client
hope i get the reply ...
thanks in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment