Created
November 19, 2018 17:52
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 *) ¤t_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 ¤t_state; | |
} | |
void set_float_dataref(char *ref, float val) | |
{ | |
NET_set_float_dataref(client_s, ref, val); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.