Você está na página 1de 19

N0800 ---------------

Follow How to fly


This Plane AI Aircrafts
SimConnect Using
Tutorial Waypoints
For Flight in FSX SDK
Simulator X ---------------
N0800 Tutorial program. The console output of the program shows what we are doing.
What is this tutorial for? The tutorial is to understand how we send nearby waypoints What do you need to complete this tutorial? MS FSX SDK, which is on the CD
to a second aircraft, to fly it smoothly towards the waypoints. We’ll also drop a parachute of “MS FSX Professional”. A compiler, which can be MS Visual Studio C++, C# or VB,
at the location of the waypoints, to visualize where is the second aircraft flying to. In the which is available for free at Microsoft.
picture below, our aircraft is on top and the second aircraft is controlled by our SimConnect
SimConnect Overview

SimConnect is a gateway that allows external programs to interact with FSX. void C A LL B A C K
P ro g ra m S ta rt
SimConnect is part of FSX SDK. So you need to install the SDK if you want to use M yD isp atch P ro c
(S IM C O N N E C T_ R E C V * pD ata)
SimConnect. It displays itself as a server and the external programs are clients. C ontinue = true
SimConnect allows the client to receive information about FSX simulation. The s w itc h(pD ata->dw ID )

client may ask details of the current state of the simulation, for instance which objects, S im C onnect_C a llD isp atc h {
c a se S IM C O N N EC T_R E C V_ ID _ E XC E P T IO N :
like aircrafts, are currently simulated; or what are the parameters, like the altitude, of (M yD is patc hP ro c) …
bre a k;

these objects. c a se S IM C O N N EC T_R E C V_ ID _ E V EN T :



bre a k;
S leep()
An asynchronous request / reply mechanism is used to obtain data. The client c a se S IM C O N N EC T_R E C V_ ID _ E V EN T _O BJ E C T _ AD D R EM O V E :

usually sends a request to SimConnect, SimConnect replies to this request later. The client bre a k;

c a se S IM C O N N EC T_R E C V_ ID _ S IM O BJ EC T_ D AT A :
hasn’t to wait for the reply, it can perform other tasks in the meantime. When SimConnect C ontinue = = true

bre a k;

is ready to reply, it sends an event message to the client, using a messaging system. c a se …

The client needs to receive all event messages, and dispatch them according
P ro g ra m E n d R o u tin e E n d
to their nature. To do that it monitors continuously for new SimConnect messages thru
an endless loop. When a message is available, the loop transfers the control to a callback
routine designed to handle these messages in a specific way.
The callback procedure is the core of the client design. It identifies the type of
message received, and execute appropriate calls so that the application is aware of replies not. The connection between the server and the clients relies on IP streams and pipes,
to previous requests. Basically a SimConnect client application is a program that sends but these details are mostly transparent to the clients programmers. The SimConnect
requests to SimConnect and manage replies in an asynchronous way in the callback API manages the communication when data transfers are needed. Clients may ask other
procedure. clients to be notified of what they send to SimConnect. The FSX simulation engine by itself
The client may also “subscribe for events notification”. It informs SimConnect is also a SimConnect client. All messages inbound and outbound are processed by the
of its interest in being informed about changes, e.g. the simulation being paused, or server based on priorities that may be assigned by clients. The final order for processing
an object being removed from the simulation. When such event occurs, SimConnect messages of identical priority is managed by SimConnect.
just sends an event message to be received and processed by the client in the callback
procedure.
SimConnect Documentation
SimConnect allows the client to change the status of the simulation. The client
can move objects, create new ones, control cameras, modify FSX menus, display dialog The main documentation for SimConnect use is the Help file included in FSX SDK.
boxes, etc. To do that, the client sends data, aimed to some object in the simulation. Unfortunately this documentation is disappointing, it is not at all aimed to start
SimConnect receives these data asynchronously too. It processes them when possible, and programming SimConnect applications, it is just the raw documentation of the
according to some priority set by the client. SimConnect API, in alphabetical order of the functions. In addition, it is definetely C++
SimConnect manages multiple clients. Clients may reside on the same computer or oriented, and leaves the C# or VB programmer with additional difficulties related to calling
the C API from a non C application. Additional mechanisms are needed to check / convert SimConnect in FSX. This is what you’ll find on MSDN.
/ validate data types being sent to SimConnect by the client (MS calls that “marshalling”). If you start programming for SimConnect, bear in mind you are tackling new frontiers on
Let’s see with this C portion of code: your own. No “beam me up, Scotty!”. So... how do you start programming SimConnect
struct Struct1 clients?
{
char* title;
double latitude;
double longitude;
IDE and programming language
double altitude;
}; For the IDE, you’ll probably end up with Visual Studio, which comes for free in
In C# you’ll have to write your code this way: its Express edition (well... you’re pestered to register after a month, so you’ll certainly pay
some price in the end). I don’t know about using Eclipse, which I like definitely more than
[StructLayout(LayoutKind.Sequential,
CharSet = CharSet.Ansi, Pack = 1)]
VSE. I started with VSE to avoid additional difficulties to code the examples found in the
struct Struct1 documentation.
{
Which language to use for coding SimConnect clients? You may use C/C++ or
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String title; MS CLI managed languages (C# , VB, VJ). If you are efficient with C/C++, you’ll be safer
public double latitude; than with the others. There is also an independent implementation of the SimConnect API
public double longitude; in Java (jSimConnect) with maybe the same drawbacks than MS managed code, but it
public double altitude; could be worth a try.
};
For Kaspersky customers who want to install Visual Studio. MS has an article
In the SDK documentation you’ll find a small set of examples in C# and VB. However all about incompatibility between KAV and VS and suggests installing with KAV inactive. I
the API is described using C approach. So be prepared to face additional challenges if you had not this problem, however, after I installed VSE, KAV started blocking web pages with
have or want to use compilers producing “managed code” for the .NET framework. embedded scripts. KAV is now complaining to be unable to load VBScript.dll. Kaspersky
When it comes to searching the internet for answers not found in the SDK documentation, support is leading nowhere. Once this problem existed with leftovers after NAV uninstall,
you’ll also feel that there is a very limited number of urls reported by your prefered search so the only suggestion they provide is to uninstall NAV properly whatever your problem
engine. Most of them point to MSDN. Many of those pages will discuss something not is. My KAV licence is expiring in a month, so I’ll live with that until then and uninstall KAV
working as it should, or will ask for explanations about how things should be done. You (properly).
may end thinking this is just emphasizing the fact that there are so many functions in
the API, and so little overview on how to use them. And no SimConnect Primer available
so far. All things considered, you got FSX SDK in FSX professional for a mere 10 bucks
What you need to code a SimConnect Client (with C++)
compared to the standard version. That’s OK!
You need SimConnect.h and SimConnect.lib. The overview of the “SimConnect
By the way, after MS realized Flight Simulator was a valuable platform for simulation SDK Reference” (the SDK help file) explains how to to set up your IDE correctly.
of any kind, and could be a gold mine in the serious companies sectors, they started
working on the generic simulation engine “Enterprise Simulation Platform”. ESP has now For this tutorial, all the code is in the same source file. The project is created from the
several versions, but for the time being the documentation related to ESP can be used for Win32 Console Application template that comes with VS. The output of the printf calls will
be displayed in the DOS-like window. This is a nice way to debug asynchronous calls.
Of course, you need to install the FSX SDK if you didn’t install it with FSX initially. simulation. Such events may need to be reported by the server to all or some of its clients
By creating a SimConnect.ini file in your My Documents\Flight Simulator X (5).
Files folder you can also enable a debug window in FSX. This is briefly explained in the
On his side, the user (8) is controlling FSX. If a client wants to be informed when some
help file. You can copy here the default ini file found in the SDK folder. No need to create user’s event occurs, it may “subscribe” to it. Such events detected by the simulation engine
one from scratch. (9) will be transferred to the server and then to the client.
At this time, you now have the blue part of this diagram (1, 2 and 3): If FSX is terminated by the user (or for some other reason), a message will be sent by the
2 server to the clients.
C++
S ource In the end the client has to close the connection with the server to free any allocated
resources.
3 1 We mentionned the server informing (calling back) the client of two events: the
S im C o nnect V isual connection process success and FSX being terminated. Technically this is a call of the
A P I H eader C++
C om pile T im e client’s callback procedure, with a parameter being a pointer to a message structure. All
messages are declared this way: SIMCONNECT_RECV* pData.
R un T im e
SIMCONNECT_RECV definition:
4 F S X U ser
struct SIMCONNECT_RECV
8 {
DWORD dwSize; // record size
DWORD dwVersion; // interface version
5 7
DWORD dwID; // see SIMCONNECT_RECV_ID
FSX
S im C onnect S im C onnect };
S im ulation
A pplication (C lient) S erv er
E ngine dwID is an integer identifying the kind of message being received. For a feedback after
10 9 a connection request it will be 2, for FSX quitting it will be 3. We won’t manipulate these
6
values directly. Instead we’ll use the enumerated values provided in SimConnect.h:
S im C o nnect
A P I Library // Receive data types
enum SIMCONNECT_RECV_ID {
SIMCONNECT_RECV_ID_NULL,
The SimConnect API header (3) contains all the declarations needed to work with the SIMCONNECT_RECV_ID_EXCEPTION,
API (6). After you compile your code and link it (4) with the API library, you can run the SIMCONNECT_RECV_ID_OPEN,
executable client (5). This is how it will work: SIMCONNECT_RECV_ID_QUIT,
SIMCONNECT_RECV_ID_EVENT,
(5) the client opens a connection with the server (7). When the connection request has SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE,
been processed successfully, the server post a message (10) to the client to inform it. The SIMCONNECT_RECV_ID_EVENT_FILENAME,
client can now send requests and data to the server. SIMCONNECT_RECV_ID_EVENT_FRAME,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
To answer such request the server (7) may need to talk with FSX (9). ...
SIMCONNECT_RECV_ID_EVENT_RACE_LAP,
Conversely FSX (9) will have to inform the server (7) about events occuring in the
}; Message loop
Depending on the SIMCONNECT_RECV_ID value, we’ll need to cast the intial
SIMCONNECT_RECV* pData to the actual structure it points. For example, if this is an Actually let’s construct the first bricks of our masterpiece ;-) starting with the constructor
“OPEN” message identified by pData->dwID == SIMCONNECT_RECV_ID_OPEN, then of our main class (FollowThisPlane). As you recall, we need to have an endless loop
pData actually points to a structure declared this way: that wait for incoming message posted by the server, and transfer them to a callback
struct SIMCONNECT_RECV_OPEN : public SIMCONNECT_RECV procedure. We’ll execute this loop just after we have requested to open a connection with
{ the server:
char szApplicationName[256];
HANDLE hSimConnect = NULL;
DWORD dwApplicationVersionMajor;
bool keep_going = false;
DWORD dwApplicationVersionMinor;
// Constructor. Establish a connection with SimConnect
DWORD dwApplicationBuildMajor;
void FollowThisPlane()
DWORD dwApplicationBuildMinor;
{
DWORD dwSimConnectVersionMajor;
// Connect to FSX
DWORD dwSimConnectVersionMinor;
Connect();
DWORD dwSimConnectBuildMajor;
if (hSimConnect != NULL)
DWORD dwSimConnectBuildMinor;
{
DWORD dwReserved1;
// Connected. Loop until FSX exits
DWORD dwReserved2;
keep_going = true;
};
while(keep_going)
This structure inherits from SIMCONNECT_RECV. {
SimConnect_CallDispatch(hSimConnect,
When we receive this message, we have plenty of data that can be accessed with the MyDispatchProc, NULL);
pointer after a cast: Sleep(1);
}
SIMCONNECT_RECV_OPEN* pOpen = (SIMCONNECT_RECV_OPEN*)pData;
// FSX exited. Close the connection with SimConnect
pOpen->szApplicationName will be the name of the application. Sure this is not of Disconnect();
a critical interest in this tutorial, but you see the principle. }
else
Regarding the structure returned after we receive a SIMCONNECT_RECV_ID_QUIT {
message, it’s exactly the same than the basic one: // Not able to connect. Not retrying in this version
}
struct SIMCONNECT_RECV_QUIT : public SIMCONNECT_RECV // (the program now quits)
{
}
};

No field is added.
The call Connect(); will initiate a connection, but the outcome of this request will
An answer to a request for the altitude of our aircraft will be more interesting. arrive asynchronously later. We’ll have a look at what we do within this call. When
So we’ll move on and discuss our real example. Since every client must open a connection Connect() returns, the variable hSimConnect will have been positionned by
to SimConnect, let’s see how we do that and how we handle the reply from the server. SimConnect when requesting to open a connection. In case we were not able to talk
to SimConnect, this variable will remain NULL. In this case, our constructor will return
and the programm will quit. It can happen if FSX is not active when we run our little printf(“\nError %d”, hr);
}
SimConnect client.
else
In case we were able to post our request to open a connection, we’ll set keep_going to {
true and enter an infinite loop. In fact we’ll need to have another part of the program // Connection to FSX initialized
printf(
resetting this flag to false at some point, so that we can exit the loop and terminate the “\nConnection request being processed (%d)”,
program. hSimConnect);
// (When connection process completes, an
Until then, in this loop we’ll call SimConnect_CallDispatch(hSimConnect,
// OPEN message will be received)
MyDispatchProc, NULL) and then Sleep(1). SimConnect_CallDispatch }
just looks for the next SimConnect message waiting in the connection queue associated }
with hSimConnect and instructs SimConnect to call another portion of our code }
(MyDispatchProc) to handle this message.
Bear in mind that this function excepted, a SimConnect client is completely asynchronous.
Receiving the Open event from SimConnect
The client waits for a message to arrive, and at the same time performs other tasks, likely
in relation with the messages already received. This means the client must have different
threads working in parallel.The constructor’s thread is likely to be waiting most of the Using SimConnect_Open() we requested SimConnect to open a connection with our
time. SimConnect will call MyDispatchProc() in a new thread. client. The server already returned the handle for this connection (in hSimConnect),
however the connection is not yet finalized. SimConnect has to complete the request and
When SimConnect_CallDispatch() returns, we just Sleep() for 1 ms to ensure then to send an event of type SIMCONNECT_RECV_ID_OPEN. This event will ultimately
other processes can run. be transferred to our callback procedure which needs to take care of it. Let’s see what we
do in this procedure:
Opening a connection with SimConnect server // SimConnect sends a message to this client, dispatch it.
void CALLBACK MyDispatchProc(
SIMCONNECT_RECV* pData, DWORD cbData, void *pContext)
We mentionned the call to Connect() included in our messages loop. Here is the code {
we write in this function: switch(pData->dwID)
case SIMCONNECT_RECV_ID_OPEN:
// Try to connect to FSX
// the connection process is complete,
void Connect()
Process_Connected();
{
break;
HRESULT hr;
}
if (hSimConnect == NULL)
{ When the callback procedure is called by SimConnect, pData points to the message.
printf(“\nRequesting a SimConnect connection”); However, as we have already seen, this message contains only information about the
hr = SimConnect_Open(&hSimConnect,
“Follow This Plane”, NULL, 0, 0, 0);
application and server, useless for this tutorial. Thus we ignore the message data, and
if (hr != S_OK) call another function Process_Connected() that must take care of the connection
{ completion. All messages will be processed using the callback procedure above. We’ll add
// FSX may be inactive more case as soon as we’ll need to process other types of messages. All OPEN messages
will be processed the same way: we’ll call Process_Connected(). We bind the ID with the list of data and units. Then when we want the server to return
the data, we only provide the ID. The server has the definition somewhere in its tables
associated with our client.
Processing the connection completion // Unique IDs of data definitions hosted by SimConnect
static enum DATA {
When entering Process_Connected() called by the callback procedure, we now know DATA_ACFT_POSITION,
we are connected to SimConnected, so to FSX for all practical purposes. Our scenario for ....
this client is to have another aircraft controlled by FSX AI following our aircraft. The first };
thing is to create this aircraft, but when creating an object, we need to provide a position We’ll add IDs to this enumeration when we’ll need more. Note that we could just use
(latitude, longitude and altitude). In our case we’ll create it near our own aircraft. So we plain numbers instead of enum, but this would be a little bit difficult to remember them.
need to know were we are. By the way, this program assumes we have already taken off Now let’s explain to SimConnect which data we are looking for:
(because we don’t want to add the code required to takeoff the AI aircraft).
// prepare data definitions
We’ll request our position. “Our” means “user’s”. All objects in the simulation are identified void Prepare_Data()
by a unique number. The user’s aircraft has a constant ID defined in SimConnect.h: {
// set up the data definitions
SIMCONNECT_OBJECT_ID_USER. There is no particular API function to request a
printf(“\nSending Data definitions.”);
position, this falls into requesting a data about an object, and happens to be performed SimConnect_AddToDataDefinition(hSimConnect,
by calling SimConnect_RequestDataOnSimObject(). This function is used with a DATA_ACFT_POSITION, “Plane Altitude”, “feet”);
pre-definition of the data we want. In our case we want 3 data: latitude, longitude and SimConnect_AddToDataDefinition(hSimConnect,
altitude. What we need to do is to register our set of data into the server. MS calls that a DATA_ACFT_POSITION, “Plane Latitude”, “degrees”);
SimConnect_AddToDataDefinition(hSimConnect,
“data_define”. This definition will also tell the server which units we want to use for our DATA_ACFT_POSITION, “Plane Longitude”, “degrees”);
data (an altitude may be returned in feet or in meters for instance) SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, “Heading Indicator”, “degrees”);
Our routine to process the connection completion looks like this:
SimConnect_AddToDataDefinition(hSimConnect,
// The connection process is completed DATA_ACFT_POSITION, “Airspeed True”, “knots”);
void Process_Connected() }
{
printf(“\nConnected.”); We call 5 times SimConnect_AddToDataDefinition(). Each time we provide
// prepare data definitions these two first parameters: hSimConnect and DATA_ACFT_POSITION. The first one
Prepare_Data(); is to identify our connection instance to SimConnect. Data definition are of course kept
// request the initial position of the user’s aircraft
Request_User_Position(SIMCONNECT_OBJECT_ID_USER);
isolated from definitions of other connections. The second parameter tells SimConnect
// (the position will be received later) which definition we are taking about.
} The two last parameters are to be read as a pair “data name”-”unit to use”. You can see
A call to define our set of data to be returned, another call to send the data request. we are also requesting our heading and our speed, we’ll need them too when creating
the AI aircraft, because we will want it to move at some speed (not free falling) and in the
Let’s go to the details of the data definition. What we see is that we need to maintain a same direction than us. We’ll also provide an attitude, but it will be “straight and level”
list of “data-define” identifiers we’ll use when talking with the server. whatever our attitude.
Now let’s see how to request these data to SimConnect. in its replies. This mechanism will be needed for every request. So we’ll maintain an
// Request the position of the user’s aircraft enumeration which is assured to grow up shortly:
void Request_User_Position(SIMCONNECT_OBJECT_ID SimObject) // Unique IDs of requests to SimConnect
{ static enum REQUESTS {
SimConnect_RequestDataOnSimObject( REQUEST_INIT_POSITION,
hSimConnect, };
REQUEST_INIT_POSITION,
DATA_ACFT_POSITION, As you can see, this is the second parameter we used in our request.
SimObject,
SIMCONNECT_PERIOD_ONCE);
Now that our request for the user’s aircraft position has been sent, what do we do? Well,
printf(“\nInitial position requested for %d.”, SimObject); we can’t do anything before receiving the reply. It will come thru a call to our callback
// (the position will be received later) procedure. But we need to handle it. It will have the type SIMCONNECT_RECV_ID_
} SIMOBJECT_DATA. So let’s just add another case in our MyDispatchProc() routine:

We provide the handle to our connection hSimConnect, this will be the case for all case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
// An answer to a data request on an object
functions. SimConnect needs to know who is talking and check we are not unknown
Process_Data(pData);
strangers. Let’s forget the second parameter for a couple of minutes. We also provide the break;
ID of the data we want. With that, SimConnect knows we want latitude, longitude, etc
as well as which units to use for the answer. We then provide the ID of an object in the
simulation. When we called Request_User_Position(), we provided the ID of the Receiving the aircraft position
user’s aircraft as a paramater, we are using it here. The last parameter says how many
times we want the data to be returned by SimConnect. As you remember SimConnect is Let’s also add this new function:
asynchronous and will not provide the data we want during the call to SimConnect_
// A reply to a data request
RequestDataOnSimObject. Instead it will post a message when ready. In addition void Process_Data(SIMCONNECT_RECV *pData) {
it can post this message at interval. We just need to tell what we want. The different SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData =
possibilities are defined in the header file: SIMCONNECT_RECV_SIMOBJECT_DATA *)pData;
DWORD ObjectID = pObjData->dwObjectID;
// Object Data Request Period values
switch(pObjData->dwRequestID)
enum SIMCONNECT_PERIOD {
{
SIMCONNECT_PERIOD_NEVER,
case REQUEST_INIT_POSITION:
SIMCONNECT_PERIOD_ONCE,
// answer to the initial request for user position
SIMCONNECT_PERIOD_VISUAL_FRAME,
HRESULT hr;
SIMCONNECT_PERIOD_SIM_FRAME,
ACFT_PARAM *pS = (ACFT_PARAM*)&pObjData->dwData;
SIMCONNECT_PERIOD_SECOND,
printf(“\nUser %.4f %.4f %.f Hdg=%.f Knts=%.f”,
}; ObjectID, pS->latitude, pS->longitude,
pS->altitude, pS->heading, pS->airspeed);
The difference between “visual frame” and “sim frame” is about including or not the
// AI position related to the user’s aircraft
frames that are not rendered. However the documentation is not so clear about that. {
Anyway SimConnect will send us a nice message with the info we want. We need to double AIdistance = .2; // NM from user’s position
be reminded of our request at that time, because we’ll have so many requests that we double AIRelBearing = 270; // degrees clockwise
can’t remember for sure... so we’ll provide a number now, and SimConnect will include double AIHeight = -50; // meters above
double AIRelSpeed = 0;// kts above user’s speed SIMCONNECT_RECV_SIMOBJECT_DATA *)pData;
// compute position as Lat / Lon
ACFT_POSITION result = Compute_Location( These data are related to an object which ID is in dwObjectID:
pS->latitude, pS->longitude, pS->altitude, DWORD ObjectID = pObjData->dwObjectID;
AIdistance, We could check it is actually the same object than the one for which we requested data,
fmod(pS->heading + AIRelBearing, 360), but that’s assumed here. We may limit our check to the request ID:
AIHeight);
switch(pObjData->dwRequestID)
// Prepare data for the creation
{
SIMCONNECT_DATA_INITPOSITION Init;
case REQUEST_INIT_POSITION:
Init.Altitude = result.alt;
Init.Latitude = result.lat; Should we receive data not related to our own request, it would be ignored. What is of
Init.Longitude = result.lon;
interest here is the values of the requested aircraft parameters. There is a pointer to them
Init.Pitch = 0.0;
Init.Bank = 0.0; in dwData. This is a pointer to a structure crafted by SimConnect from our data definition
Init.Heading = pS->heading; (DATA_ACFT_POSITION). We need to define somewhere in our program the same data
Init.OnGround = 0; structure:
Init.Airspeed =
// returned after a data request based on DATA_ACFT_POSITION
(DWORD)(pS->airspeed + AIRelSpeed);
struct ACFT_PARAM
hr = SimConnect_AICreateNonATCAircraft(
{
hSimConnect,
double altitude;
Container_Title,
double latitude;
Tail_Number,
double longitude;
Init,
double heading;
REQUEST_CREATE_AI);
double airspeed;
if (hr == S_OK)
};
{
printf(“\nAI aircraft requested”); and now we can cast the pointer returned to a pointer to this type:
AI_Acft_Requested = true;
} ACFT_PARAM *pS = (ACFT_PARAM*)&pObjData->dwData;
else
{
// problem while creating the AI aircraft Creating a new aircraft
printf(“\AI creation request. Error %d”,
hr);
}
At this point in time we have the parameters of the user’s aircraft, but what we want is
} to create another aircraft near the user’s aircraft. “Near” will be defined by a distance and
break; a bearing relative to the user (so that we can place the second aircraft “at 1 NM, and
} 30°” for instance). Since SimConnect will insist on having this point defined by its lat
} and lon, we’ll need to perform some spherical calculation. We’ll leave this duty to a short
So what do we do here? First we cast our message pointer to the type used with a routine not described here. To avoid collisions, we’ll also allow to define the second aircraft
SIMCONNECT_RECV_ID_SIMOBJECT_DATA message: altitude relative to our. And to allow some tuning, we’ll also allow to add some kts to its
speed. This give us these variables that can be adjusted to have some fun.
SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData =
double AIdistance = .2; // NM from user’s position REQUEST_CREATE_AI);
double AIRelBearing = 270; // degrees clockwise
double AIHeight = -50; // meters above Container_Title is the name of the AI object to create (i.e. the type of object to
double AIRelSpeed = 0; // kts above user’s speed create), while Tail_Number is obviousy the call sign. As usual for us now, we need to
provide a request identifier to SimConnect. Our enumeration for request looks now like
Then, as mentionned we call Compute_Location(), our spherical routine:
this:
// compute position as Lat / Lon
ACFT_POSITION result = Compute_Location( static enum REQUESTS {
pS->latitude, pS->longitude, pS->altitude, REQUEST_INIT_POSITION,
AIdistance, REQUEST_CREATE_AI,
fmod(pS->heading + AIRelBearing, 360), }
AIHeight);
How to you find the container title? Another gap in FSX SDK documentation. The list is not
ACFT_POSITION is a structure defined elsewhere in the program:
provided, instead you have to look, as explained, into FSX description of objects in your
struct ACFT_POSITION FSX folders.
{
double lat; If you started coding in C# or any MS managed code, bear in mind that you need to
double lon; “marshal” your data structures, with an additional effort for the strings.
double alt;
};
The creation function returns a result that is S_OK (defined in the API header file) or
something else. We just check S_OK to confirm the request was accepted.
We are now nearly ready to create this awaited new aircraft. We just need to put the data
needed for the creation into the structure requested by the SimConnect API. (this structure We have submitted our creation request, the next step is to receive the notification of its
SIMCONNECT_DATA_INITPOSITION is defined in the API header).
completion. It will arrive thru the callback procedure and will be tagged with message
type SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE. We need to add some
// Prepare data for the creation of a SimConnect AI object
code in our callback procedure.
SIMCONNECT_DATA_INITPOSITION Init;
Init.Altitude = result.alt;
Init.Latitude = result.lat;
Init.Longitude = result.lon; Receiving a notification for the creation of an object
Init.Pitch = 0.0;
Init.Bank = 0.0; Each time an AI object is created into or deleted from the simulation, the clients are
Init.Heading = pS->heading;
Init.OnGround = 0;
informed by calling their callback procedure with a SIMCONNECT_RECV_ID_EVENT_
Init.Airspeed = (DWORD)(pS->airspeed + AIRelSpeed); OBJECT_ADDREMOVE message (this is the same message both the creation and the
removal). However they need to subscribe to this information. So basically, if you create
We ask SimConnect to create the aircraft:
an AI object, you are not informed of its creation or removal unless you have specifically
char Container_Title[] = “Mooney Bravo”; asked for.
char Tail_Number[] = “N0800”;
HRESULT hr = SimConnect_AICreateNonATCAircraft( That may seem strange, but you need to consider the number of objects created by FSX
hSimConnect, itself, and by other clients as well. What we are really interested in is a way to be informed
Container_Title, only about the creation of objects we requested ourselves. SimConnect will notify a client,
Tail_Number,
Init,
without a need for prior subscription, each time an object created by the client is assigned
an object ID. Note that there is no corresponding notifications when these objects are repeat the string in the message, only EventID. That means EventID is another unique
destructed. This ID assignment notification will help us. However we’ll need also to be number that will be defined in an enumeration:
informed of our aircraft removal, because we need to stop sending request to FSX as soon // Unique IDs for notified events
as our little client has no purpose anymore (in this case maybe we should re-create it? It’s static enum EVENTS{
left up to you...). EVENT_ADDED,
EVENT_REMOVED,
The AI aircraft should be removed after a collision (actually not) or some other event FSX };
would jugde critical (we lack information on that). It will also be removed if it leaves the
“reality bubble” of the user, that if it is further than something around 100 km. The SDK We need to handle both notifications by adding this code in our callback procedure:
documentation talks about that. case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
// AI object added or removed
It is clear we have to implement the handler for removals. For the same price we’ll have a Process_Object_Added_Removed(pData);
handler for additions. break;
Somewhere in our code, we need to subscribe to object addition and object removal To know if the notification is for an addition or for a removal, we need to check the
events. This has to be before the first creation request is submitted. A valid time is when uEventID reported in the message and compare it with the event ID provided for the
we send our data definition (function Prepare_Data). We can add the code to subscribe subscription to this class of events. We know what was the request for. Let’s put that
here and rename the function: toghether in the function that we’ll call when notified:
void Prepare_Data_And_Events() // An object has been added or removed
{ void Process_Object_Added_Removed(SIMCONNECT_RECV *pData)
// set up the data definitions {
... SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *pAddRem =
(SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE *) pData;
// subscribe to events switch (pAddRem->uEventID)
printf(“\nSubscribing to events.”); {
SimConnect_SubscribeToSystemEvent(hSimConnect, case EVENT_ADDED:
EVENT_ADDED, “ObjectAdded”); // AI addition
SimConnect_SubscribeToSystemEvent(hSimConnect, printf(
EVENT_REMOVED, “ObjectRemoved”); “\nNew AI object added to FSX (%d, type %d)”,
} pAddRem->dwData, pAddRem->eObjType);
break;
SimConnect_SubscribeToSystemEvent is declared this way: case EVENT_REMOVED:
HRESULT SimConnect_SubscribeToSystemEvent( // AI object removed
HANDLE hSimConnect, printf(
SIMCONNECT_CLIENT_EVENT_ID EventID, “\nAI object removed (%d)”, pAddRem->dwData);
const char* SystemEventName // let’s check if it is ours
); if (pAddRem->dwData == AI_Acft_ID)
{
It needs two parameters in addition of the connection handle: the SystemEventName // Our AI acft has died
which is a string, and the EventID which is a number. The string tells SimConnect what keep_going = false;
we want to be notified for. However when SimConnect sends the notification, it won’t AI_Acft_Valid = false;
printf(“, our AI aircraft!”);
printf(“\nRestart me.”); case REQUEST_CREATE_AI:
} // Our AI aircraft
break; printf(“\nAI Aircraft created (%d)”
} pAssObjData->dwObjectID);
} AI_Acft_ID = pAssObjData->dwObjectID;
AI_Acft_Valid = true;
In the code above, we cast the message pointer to the actual structure for this event. Then // Start a timer
we look at uEventID to sort additions and removals based on the event ID provided at SimConnect_SubscribeToSystemEvent(hSimConnect,
the subscription. For the additions we do nothing except leaving a debug trace. For the EVENT_TIMER, “4sec”);
removal, we check if the object removed isn’t our aircraft. As we will see in a couple of printf(“\nSubscribing to Timer ticks.”);
break;
minutes, this object ID has been stored in the variable AI_Acft_ID. If the comparaison
}
confirms the removal, then we reset the flag keep_going that was set when the aircraft }
was confirmed as created. We also reset the flag which indicates the AI_Acft_ID
variable contains a valid ID. Again what we do is pretty straightforward. We compare the message Request ID
(dwRequestID) with our request ID used to create the AI aircraft (REQUEST_CREATE_
You may wonder why we don’t use the EVENT_ADDED case to confirm our AI aircraft AI). If they match, we have now a second aircraft up and running. We store its object
was created. The reason is that we can’t, because the SIMCONNECT_RECV_EVENT_ ID for later use (removal detection mentioned previously). We also set a flag to indicate
OBJECT_ADDREMOVE notification doesn’t carry the Request ID that led to the creation. AI_Acft_ID is now valid.
Without this information, we cannot know if the new object is the one we are expecting...
AI_Acft_ID = pAssObjData->dwObjectID;
So we are bound to handle also the notification of the ID assignment. AI_Acft_Valid = true;
Then we subscribe to a timer event sent every 4 seconds. We’ll use it to send a new
Detecting when a new object is assigned its ID by FSX. waypoint to the AI aircraft. This way we’ll feed the aircraft with waypoints that keep it in
the vicinity.
SimConnect_SubscribeToSystemEvent(hSimConnect,
In our tutorial, a new category of notifications to be handled means a new case in our
EVENT_TIMER, “4sec”);
callback procedure, and a new function to do the actual work.
case SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID:
// an answer to a AI creation request Requesting periodically a new user’s position
Process_Object_ID_Assignment(pData);
break;
So let’s see the second part of this tutorial, where we have to receive ticks, create a
The new function: waypoint, and send it to the AI aircraft. Fortunately enough, a non ATC aircraft still knows
// An object has been created and assigned an ID how to keep flying and how to move towards a waypoint. And the simulation takes care of
void Process_Object_ID_Assignment(SIMCONNECT_RECV *pData) this job very smoothly.
{
SIMCONNECT_RECV_ASSIGNED_OBJECT_ID *pAssObjData = (SIMCONNECT_ The last touch of fun will be to add markers at the location of the waypoints, to have a
RECV_ASSIGNED_OBJECT_ID *)pData; visual clue of where the AI aircraft is expected to go.
// We need to confirm this is one of our objects
switch (pAssObjData->dwRequestID) To receive the timer events, we need to upgrade our callback procedure, and to create
{ another handler it can call. Those simple events like timer (we’ll also use a pause
detection later) are all received under the same message type, SIMCONNECT_RECV_ID_ for asking the initial position. However when we are notified of the user’s position, we
EVENT. need to know which request is answered to execute the related actions set which are
case SIMCONNECT_RECV_ID_EVENT: different. It means we need to have different request IDs, we cannot reuse REQUEST_
// One of the FSX events this client has subscribed to INIT_POSITION. We will insert another ID, REQUEST_NEXT_POSITION, in our
Process_Event(pData); enumeration of request IDs. To tell Request_User_Position() which request ID
break; needs to be used, we use a boolean flag, set only for the initial request. This is our handler,
The handler we add now contains this code: modified to suit both types of requests:
// Message received is a RECV_EVENT // Request the position of the user’s aircraft
void Process_Event(SIMCONNECT_RECV *pData) { void Request_User_Position(
SIMCONNECT_RECV_EVENT *evt= (SIMCONNECT_RECV_EVENT *)pData; SIMCONNECT_OBJECT_ID SimObject, bool Initial)
switch(evt->uEventID) {
{ SimConnect_RequestDataOnSimObject(
case EVENT_TIMER: hSimConnect,
// time to update things... Initial ?
if (keep_going && !Paused) REQUEST_INIT_POSITION : REQUEST_NEXT_POSITION,
{ DATA_ACFT_POSITION,
// request the new position of the user SimObject,
Request_User_Position( SIMCONNECT_PERIOD_ONCE);
SIMCONNECT_OBJECT_ID_USER, false); printf(“\n%s position requested (%d).”,
} Initial ? “Initial” : “Current”, SimObject);
break; // (the position will be received later)
} }
} We mentionned our need to capture pauses so that we don’t send useless waypoints to
}
the AI aircraft when the simulation is paused. The waypoints would be all identical, since
We check the message is for EVENT_TIMER. At this time we should request the current the user’s aircraft is not moving, and in addition the AI aircraft would receive them all at
position of the user aircraft. We would then wait for the server reply, and in the handler the same time, when the simulation is restarted. Being notified of Pause is a matter of
of the reply we would create a new waypoint based on the current position of the user’s subscribing to the appropriate event and again to handle the notifications. Let’s add a
aircraft. This is what we’ll do except that we need to stop creating waypoints when the subscription at the same location than the previous ones:
simulation is paused or when the AI aircraft has been removed: void Prepare_Data_And_Events()
if (keep_going && !Paused) {
{ // set up the data definitions
// request the new position of the user ...
Request_User_Position(
SIMCONNECT_OBJECT_ID_USER, false); // subscribe to events
} ...
keep_going will be reset in the handler for object removal notifications, while toogling
Paused needs to be linked to a new event from SimConnect. We’ll take care of that soon. HRESULT hr = SimConnect_SubscribeToSystemEvent(
hSimConnect, EVENT_PAUSE_TOGGLE, “Pause”);
Regarding requesting the current position, it would make sense to use the code we wrote Paused = (hr == 0) ? false : true;
}
case REQUEST_NEXT_POSITION:
We are subscribing to SimConnect event “Pause”, and we use the event ID EVENT_ // Periodic update of the user’s position
PAUSE_TOGGLE to remember what is this event. (for later, when we’ll be notified and // We send the coordinates of a waypoint nearby
provided this event ID) This new ID has been added to our EVENTS enumeration (this // But only if the AI aircraft is flying...
is straightforward, not need to show you the updated code). Note that the order of the if (AI_Acft_Valid && keep_going)
{
symbols in the enum definition has no importance, only their uniqueness is meaningful.
printf(“\nNew position received.”);
When we subscribe to events like Pause, the call to SimConnect returns the current status {
of the parameter. Here it returns 0 if the simulation is currently unpaused. this allow us to // WP position and desired speed at the WP
double WPdistance = .5; // NM
set our Paused flag to the appropriate value immediately. double WPRelBearing = 0; // degrees clockwise
The notifications are handled this way, reusing the existing event handler already used to double WPHeight = -50; // meters above
double WPRelSpeed = 5; // kts above
handle EVENT_TIMER:
switch(evt->uEventID) // Compute Lat / Lon of the WP
{ ACFT_POSITION result = Compute_Location(
case EVENT_TIMER: pS->latitude, pS->longitude,
... pS->altitude, WPdistance,
fmod(pS->heading + WPRelBearing, 360),
case EVENT_PAUSE_TOGGLE: WPHeight);
// the simulation is paused or unpaused
Paused = evt->dwData == 0 ? false : true; // force VS calculation
break; unsigned long flags =
} SIMCONNECT_WAYPOINT_SPEED_REQUESTED |
SIMCONNECT_WAYPOINT_COMPUTE_VERTICAL_SPEED;
We get the new status of the simulation in dwData. We update our Paused flag // (code to create the WP goes here)
accordingly. // (see next section)

When we receive the user’s aircraft position from SimConnect, we check we really need to
Computing the position of the next waypoint create a waypoint:
if (AI_Acft_Valid && keep_going)
We need to create a waypoint when we are notified of the current user’s position. The i.e. the AI aircraft creation has taken place (confirmed by AI_Acft_Valid) and the AI
corresponding data request is submitted every 4 sec by the EVENT_TIMER handler. We aircraft hasn’t been destructed (confirmed by keep_going). The position of the waypoint
already have a handler that processes notifications of data delivery (Process_Data()), will be relative to the position of the user’s aircraft: distance in NM, relative angle, altitude
we just need to adapt it so that it can handle replies to REQUEST_NEXT_POSITION difference, speed difference.
requests in addition to replies to REQUEST_INIT_POSITION requests.
// WP position and desired speed at the WP
case REQUEST_INIT_POSITION: double WPdistance = .5; // NM
... double WPRelBearing = 0; // degrees clockwise
... double WPHeight = -50; // meters above
break; double WPRelSpeed = 5; // kts above
Since we need to provide a latitude and longitude rather than a distance and a bearing // Unique IDs of data definitions hosted by SimConnect
static enum DATA {
for the creation of the waypoint, we call again our spherical routine. We ensure the
DATA_ACFT_POSITION,
absoulte bearing is in the range 0-360° by calling a fmod. DATA_NEXT_POSITION,
When creating the waypoint, we’ll specify a speed. This is the desired speed for the aircraft };
when it reaches the waypoint. Similarly we want FSX simulation to compute whatever Let’s now define these WP data:
vertical speed is required to reach the altitude at the waypoint. When calling the function void Prepare_Data_And_Events()
that creates the WP, we’ll need to provide flags that ask for the speed and the vertical {
speed to be used. They are ORed: // set up the data definitions
...
flags =
...
SIMCONNECT_WAYPOINT_SPEED_REQUESTED |
SimConnect_AddToDataDefinition(
SIMCONNECT_WAYPOINT_COMPUTE_VERTICAL_SPEED;
hSimConnect, DATA_NEXT_POSITION,
We have collected all the info we need to create the WP. “AI Waypoint List”, “number”,
SIMCONNECT_DATATYPE_WAYPOINT);
...
...
Creating a waypoint }

First, we goup the WP data in an array of SIMCONNECT_DATA_WAYPOINT elements. If To define DATA_NEXT_POSITION, we send only one piece of data “AI Waypoint
we want to send multiple waypoints, then we describe one WP per array element. List”, the unit has to be tagged “number” and the structure of the data is
SIMCONNECT_DATATYPE_WAYPOINT.
// Prepare data for the creation of a WP
SIMCONNECT_DATA_WAYPOINT waypoint[1]; Let’s send our WP as a “data” using SimConnect_SetDataOnSimObject(). The
waypoint[0].Latitude = result.lat; object we target is our AI aircraft, which Id is AI_Acft_ID. We need to tell SimConnect
waypoint[0].Longitude = result.lon; how many elements there is in the array (ARRAYSIZE(waypoint)), and what is the
waypoint[0].Altitude = result.alt;
waypoint[0].Flags = flags; total size of the data. We also need to provide a pointer to the data (waypoint) as well
waypoint[0].ktsSpeed = pS->airspeed + WPRelSpeed; as the the data definition (DATA_NEXT_POSITION).
waypoint[0].percentThrottle = 0; // Send the WP to the AI aircraft
The percentThrottle value is the quantity of power we want for the aircraft when hr = SimConnect_SetDataOnSimObject(
hSimConnect, DATA_NEXT_POSITION,
it reaches the WP. We don’t use it here (we should force its use by adding a flag to our AI_Acft_ID, 0, ARRAYSIZE(waypoint),
flags value). sizeof(waypoint[0]), waypoint);
printf(“\nNew waypoint sent to AI aircraft);
To send the WP to the aircraft, we’ll just use the function that “sends data” to an object.
Sending data to an object requires a data definition to tell SimConnect how to understand That’s it. The AI aircraft is now flying towards this WP. It would be nice to visualize it. This
what we are sending. We already defined data, we’ll just add the new definition to the will be the icing on the cake.
existing ones.
As the data definition needs to be assigned a unique ID, we’ll also update our data Creating a marker
definition enumeration to add the symbol DATA_NEXT_POSITION:
The green vertical arrow or the point of interest objects used in missions would be previous WPs are cancelled from its flight plan. Only the most recent parachute is actually
perfect markers to visualize the location of a waypoint. However, I don’t know how to materializing an active WP. If you want, you can add some code to your program, to
use them in a SimConnect application. It seems the only objects we can create are those remove the previous parachute when a new one is created. It should be in the section
found in the SimObjects folder of FSX. So the best I can think of is a parachute. of code that is called when we are notified that an ID has been assigned to our last
Like we did for the AI aircraft, we need to tell SimConnect where to create the object. This parachute (Process_Object_ID_Assignment()):
will be described in a structure SIMCONNECT_DATA_INITPOSITION. case REQUEST_ADD_MARKER:
// no more than one marker in the simulation
// Prepare data for a SimConnect AI object Previous_Marker = Last_Marker;
SIMCONNECT_DATA_INITPOSITION Init; Last_Marker = pAssObjData->dwObjectID;
Init.Altitude = result.alt; if (Previous_Marker != NULL)
Init.Latitude = result.lat; {
Init.Longitude = result.lon; // remove the previous marker
Init.Pitch = 0.0; SimConnect_AIRemoveObject(hSimConnect,
Init.Bank = 0.0; Previous_Marker, REQUEST_REMOVE_MARKER);
Init.Heading = pS->heading;
}
Init.OnGround = 0;
Init.Airspeed = 0; Also add these two declarations somewhere:
With this set of data, we can create the little parachute. We’ll use the SimConnect function DWORD Previous_Marker;
SimConnect_AICreateSimulatedObject which allow us to create objects that
DWORD Last_Marker;
don’t fly.
// Create an AI object to mark the position
SimConnect_AICreateSimulatedObject(hSimConnect,
“Food_pallet”,
Init, With that last set of instructions, we have completed the tutorial. The full code
REQUEST_ADD_MARKER);
contains non significant additions that you will be able to discover by reading thru
Note that we used the new request ID REQUEST_ADD_MARKER which will allow us FollowThisPlane.cpp source.
to detect the creation completion. As usual, the enumeration of request IDs has been
updated.
PS: the code used to compute the coordinates of a point based on its distance and
The name of the container to create is found in the configuration file of the object in FSX bearing relative to a point with known coordinates is:
SimObjects folder. There is no list of objects in the SDK help file, only some examples.
const double PI = 3.14159265358979323846;
If you run this program now, it’ll work. Ensure you launch it while you’re already flying const double DEG_TO_RAD = 3.14159265358979323846 / 180.0;
your aircraft in FSX. If this is not the case, the AI aircraft will be created with a speed const double EARTH_RADIUS_NM = 6371.008 / 1.852;
of 0 kt. And maybe below the ground level, depending on the settings you chose for the double lat1 = refLat * DEG_TO_RAD;
double lon1 = refLon * DEG_TO_RAD;
position where to create the AI.... double d_R = dist / EARTH_RADIUS_NM;
You’ll see the AI aircraft poping up nearby out of nowhere. And also those magenta double brng = bearing * DEG_TO_RAD;
parachutes dropped every four seconds.They will materialize your route on the ground result.lat = (
asin(sin(lat1)*cos(d_R) +
(actually a route parallel to your route). When we send a single WP to the aircraft, the cos(lat1)*sin(d_R)*cos(brng)))
/ DEG_TO_RAD;
result.lon = (
lon1 +
atan2(
sin(brng)*sin(d_R)*cos(lat1),
cos(d_R)-sin(lat1)*sin(result.lat)))
/ DEG_TO_RAD;
The result is not good, though for the purpose it’s sufficient. You’ll see the effect when
setting the waypoint position at some distance (1/2 NM) and the bearing at 360°. The WP
is not created directly ahead, but on a side. Regardless of the bearing set, when your turn
your aircraft, the position of the parachute creation will move related to the heading, it
will change from left to right or right to left.
I’ve no clue about what’s wrong, if you see where is the problem... send me your tested
code. Thanks.

( c ) 2009, RWY Ahead


You can do whatever you want with this tutorial and its code, it’s public domain.
This code is for simulation only, do not use it in nuclear plants without permission.

Você também pode gostar