Sunday, December 17, 2006

Weekend Report

So, I haven't vanished, as those of you who are on AVSIM might know.  Due to the unexplained disappearance of Ken Salter, the guy who ran the forums over there, Geof Applegate (Geofa) and I have been asked to take over the admin duties for the AVSIM forums.   Besides the spam that the forum email gets, there's been a period of getting used to the new digs, and a few moments of me learning restraints, as I've been pegged as a FSX apologist. 

Speaking of apologies, you might have noticed that I haven't put up the next part of the SimConnect tutorial.  Everything is done for it except for the long process of writing out the blog post.  Welcome to the holidays.  Mostly, I've been out with family and friends, being social.  I did get a chance on Thursday to see ACES team member "Beatle" (aka Tim G.) in his new band down in Fredericksburg, VA.  It was an awesome show, and I'm glad I got to go.  We talked SimConnect, XNA, FSX multiplayer and the upcoming service packs.

In regards to SimConnect, I've been crunching to update TrackerX to version 0.5, which is pretty much a complete code rewrite on the website side.  It was messy as hell, and really needed to be consolidated.  I had a few bugs popping up in the way that icons resized when you zoomed in or out in the Google Map.  This behavior was only occurring after you used the Refresh function on the TrackerX page, so I went in and started rebuilding the whole process to clean it up.  I'm hoping that maybe tonight I can get something workable up on the blog here.  Also, I want to be able to offer a TrackerX API for use on VA websites.  That's down the road, but I want a clean code base to work from.

I've also been doing a lot of investigating into the way that the AI works for Icarus.   There are certainly some interesting things that go on when bringing an AI plane in to land.  My little tool for this can pretty easily be converted into a traffic viewer, so I might throw that up too, somewhere along the way.

So, I'm not gone, I'm still working on stuff, and I'll be full speed ahead after I get back from holiday travelling.

Monday, December 11, 2006

The Reason I Don't Make Promises

I got cranking on TrackerX this weekend, and I'm happy to say that shortly it will be ready for Beta.

To give you an idea of how soon, I'm just a bit too tired to finish up the last little section of the site, package up the installer, create an icon and put it up tonight, so I'll get that done tomorrow.

I really like how it all works. Nothing complicated, just location details being uploaded from the .NET program to a database and then displayed on an interactive googlemap on the website.

All in all, I think its kind of slick. In the future, I'll be adding in support for viewing the path the flight has taken and take-off and destination spots if you have a flight plan filed. It's one of those "side-project, add stuff when you have the chance" type of programs, but I think it looks nice.

I've got some bugs involving the PDMarkers and Google maps, but I think when I do a big clean up of the code down the line, some of those will be resolved. They are all on the webpage, not the program itself, so that's good.

Labels: ,

Thursday, December 7, 2006

FSX SDK Service Pack 1

And Other Updates From the Front

So, ACES has stuck to their guns with their promise to do quarterly updates to the SDK, releasing the first service pack.  It doesn't do much for SimConnect users, but all around it seems like a healthy dose of new stuff.

Get it here

In other news, I've added a side bar with all of the links to the SimConnect tutorials.  Eventually, the concept would be to put these up on the site somewhere by themselves, but for now, this will have to do.  It seems like I might end up with a somewhat free weekend, so I hopefully can get two new tutorials done.  I just don't want to make any promises.

Again, thanks for the support so far.  It's good to know that people are reading.

A Ten-Thousand Foot View of SimConnect - Part 3

By this point, we have a basic operation that connects to SimConnect at the click of a button...and does nothing. So it's about time we start looking at the framework of how to actually interact with FSX.

You may have read, heard or gotten involved in the conversations about multithreading, hyperthreading, fibers, etc., but all you really need to know about the whole concept is the message queue. As we said before, your SimConnect program (like other Windows applications) is Event Driven. This means that the program acts on events sent to it from the system. Say for instance, you click on our "Connect" button. The operating system registers that mouse click, and sends a message to the program. The program gets the message, containing the click event, and fires off our function in response. A lot of this is transparent, because it is built into Windows and the .NET framework, but we have the ability to expose some of the functionality and expand upon it, which is exactly what we plan to do. In SimConnect, events and data are sent to our program via system messages.

For an in-depth look at the Windows System Message system, check out this page:
Messages and Message Queues

The core of our application's message system is the "Windows Procedures" or WinProc. It is a function that receives system messages and figures out what to do with them. We want to have our program handle the SimConnect messages when they come down the pipe, so we use an ability in programming to "override" the default function. This just means that we are taking an already defined function and creating our own version of it. In the case of the WinProc, it will look something like this:

protected override void DefWndProc(ref Message m)
{
  if (m.Msg == WM_USER_SIMCONNECT)
  {
    if (simconnect != null)
    {
      simconnect.ReceiveMessage();
    }
  }
  else
  {
    base.DefWndProc(ref m);
  }
}

As you can see, we are defining the function called DefWndProc, or Default Windows Procedures. "Protected" refers to the accessibility of the function in a way, meaning who can access or alter it. We have already talked about override, that tells us that we are making our own version of an existing function, and void is the return type. Functions can return a value, such as if you have a function that adds two integers together, the return value could be the sum. When you see void, it means that there is no return value.


Looking closer at the meat of the function, we discover our old friend WM_USER_SIMCONNECT. You will recall that we defined this constant variable back at the beginning and sent him along with our connection function. This variable identifies the system messages coming from FSX. When we connect to SimConnect, we pass along this identifier, and whenever FSX sends a message back to us, it has that same value attached to it. So by looking for that value, we know that it has come from SimConnect. Then, the function continues: "if the simconnect object is not null (meaning we have connected to FSX already), then run the method simconnect.ReceiveMessage( )". This is the function within the SimConnect framework that processes the messages sent to us and figures out what we want to do with them. If it's not a message from FSX, process it as normal. Basically, there's no reason why we would ever need to change this. We can just stick it in our Form1 class under the button click function and know that it will do what we want.


So now we are ready to handle messages from SimConnect. The first one we want to look for is the message telling us that our add-on has successfully connected to FSX. The messages contain data structures, which are just variables grouped together into a consolidated object. Think of them like classes, but without the functions. All we need to know about them is that they are little bundles of data wrapped into one object. The SIMCONNECT_RECV_OPEN data structure contains a whole slew of information about the connection, the version of SimConnect, all of that lovely nonsense. Right now, we don't need any of that, we just need to know what happens, and when.


Luckily, ACES likes us, and has put that functionality into the SimConnect wrapper. After we have our connection to SimConnect, and therefore, our simconnect object is defined, we can start adding stuff to it. The first thing is event handlers. This doesn't require a whole lot of explanation. It's just a statement of what function to run when a certain event is received.


NOTE: You may have noticed already that Visual C# Express has an "autocomplete" function. This is incredibly useful (although sometimes it gets in your way when you are coding quickly), in that it automates certain procedures based on what you are probably trying to do. In the case of these event handlers, they will allow you to press the tab key to enter the default values, and will even create the new function for you, like what happened with our button click function.


Speaking of our button click function, here it is as it stands right now:


        private void button_Connect_Click(object sender, EventArgs e)
        {
            try
            {
                simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);
                textBox_Connect.Text = "Connected to FSX";
            }
            catch (COMException ex)
            {
                textBox_Connect.Text = "Unable to Connect";
            }
        }

Right after our the line that sets the textbox text to "Connected to FSX" but before the } (curly bracket), we are going to enter this line:

simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);



As soon as you type "simconnect.", a drop down box will appear with all of the properties and methods of this object, as I mentioned above. You can type in "OnRecvOpen" or find it on the list. So far, we've only used the equal sign by itself (=), but here we use plus sign equal sign (+=). This adds a value to a definition, as opposed to just assigning a value. In this case, it is adding something new to the function that processes when the "Open" message is received (which happens when SimConnect is connected). Immediately after typing "+=", you should be presented with an option to press tab to enter the default value, and then to press tab again to create the new function automagically. That's the method I followed here. As you can see, we are adding a new event handler to the OnRecvOpen event, that fires off the function "simconnect_OnRecvOpen". In this function, we define what we want to do when FSX has told us that we are connected.


Everyone with me still? Just in case more clarification is needed, I mapped it out in another of the "Worst Flowcharts Ever" (click for full-size)



As you can see, SimConnect sends messages (events) into the message queue, which the OS sends to our program. The DefWndProc passes them along to the simconnect object, which sees that there is an event handler for this event. On top of that, we have added our own event handler to the mix, and in that we do whatever we need to do.


If you tabbed through the creation of our event handler line, you will notice that it added the function "simconnect_OnRecvOpen" to the code. There's an exception in there right now, telling us that we haven't done anything with it yet. We want to remove that. In it's place, just cut and paste the following line from our button click function:

textBox_Connect.Text = "Connected to FSX";


Because, really, we don't really want to tell the user that we are connected until we are sure that we are connected. This waits until we receive notification from SimConnect that the connection is made, then changes the status. Make sure you remove that line from it's original position as well. If you want to be fancy, you can leave it in, but change the text to "Connecting..." or something like that. In most cases, it won't be up very long anyways.


We're going to add another event handler right under our first one. This time, it's to catch the event that SimConnect sends out if the user quits out of FSX. The handler is OnRecvQuit, and the line looks like this:

simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);


If we use tab and autocomplete again, it will create another function called simconnect_OnRecvQuit. We're just going to add a line in there to tell the user that we have been disconnected.


        void simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
        {
            textBox_Connect.Text = "Disconnected";
        }


Now, run the program (once again, click the green arrow or use the menu option Debug -> Start Debugging. The shortcut key is F5) without FSX running. If we press the connect button, we should get a message telling us "Unable to Connect". Load FSX, start a flight, and press connect again. Now it should report that we are connected. Leave the program running, and exit out of FSX. Now we are informed that SimConnect has disconnected.


And then we get an exception.


Go back up to the debug menu and select "Stop Debugging". VC# will take you back to the basic code view. What went wrong? Well, we don't have SimConnect running anymore, but we still have the simconnect object waiting for messages. So we need to get rid of the simconnect object within our program. Luckily, again, they give us a simple way to do this, called the Dispose method. Add these two lines of code to dispose of the simconnect object and set it back to null where we started:


        void simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
        {
            textBox_Connect.Text = "Disconnected";
            simconnect.Dispose();
            simconnect = null;
        }


Try it again and everything should work as expected.


Now we have a good framework for the program, have the connection and some events running, but we also want to think about what the user might want to do. For our last step tonight, we are going to expand our button click function to turn the connect button into a disconnect button when we connect.


This is going to take a few lines of code throughout our program. First, in our simconnect_OnRecvOpen function, we want to change the text of the button to "Disconnect". When the program connects to FSX, this will just change what the button says. Right after the textbox line, add the following line of code:

button_Connect.Text = "Disconnect";


Same deal with the simconnect_OnRecvQuit function:

button_Connect.Text = "Connect";


So now, when the program connects or disconnects, the button changes. Now to implement the functionality, we use a if - then - else loop. This is called a "conditional". If this is true, do this, else do this. I'll paste the new button click code and then explain it.


        private void button_Connect_Click(object sender, EventArgs e)
        {
            if (simconnect == null)
            {
                try
                {
                    simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);
                    textBox_Connect.Text = "Connecting...";
 
                    simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
                    simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);
                }
                catch (COMException ex)
                {
                    textBox_Connect.Text = "Unable to Connect";
                }
            }
            else
            {
                textBox_Connect.Text = "Disconnected";
                button_Connect.Text = "Connect";
                simconnect.Dispose();
                simconnect = null;
            }
        }


If our simconnect object equals (== compares two values, as opposed to = which just sets a value) null, meaning that we do not yet have a connected simconnect object, then run through our connection procedure. ELSE, if it does not equals null, meaning that we are connected, then do the same thing as if FSX just quit. Set our text to show that there is no connection and dispose of the SimConnect object.


Try it out. Run your program and FSX and you should find yourself able to connect and disconnect as many times as you want, both by using the button and by closing FSX.


By now, hopefully a lot of the theory behind this is understood. I've tried to put a lot of detail into a very small bit of actual operation so that those who aren't familiar with the procedures of programming or C# or even just SimConnect will be able to ride along smoothly down the line.


As of now, we have a program that is able to connect and disconnect from SimConnect, can detect and handle events sent from FSX, and has the beginnings of a useable interface. Not bad for a few lessons, I think. Now that we have a framework for events, we are going to use that ability to send and listen to events, so that we can control the toggling of landing gear from outside the program, and then to detect when the gear are up or down.


Could this happen tomorrow? Maybe. I'll certainly try my best to get it done by the end of the day (Friday).


And finally, here is the full code as I have it right now for Form1.cs:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;
 
namespace SimConnect_Tutorial
{
    public partial class Form1 : Form
    {
        SimConnect simconnect;
        const int WM_USER_SIMCONNECT = 0x0402;
 
        public Form1()
        { 
            InitializeComponent();
        }
 
        private void button_Connect_Click(object sender, EventArgs e)
        {
            if (simconnect == null)
            {
                try
                {
                    simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);
                    textBox_Connect.Text = "Connecting...";
 
                    simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
                    simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);
                }
                catch (COMException ex)
                {
                    textBox_Connect.Text = "Unable to Connect";
                }
            }
            else
            {
                textBox_Connect.Text = "Disconnected";
                button_Connect.Text = "Connect";
                simconnect.Dispose();
                simconnect = null;
            }
        }
 
        void simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
        {
            textBox_Connect.Text = "Disconnected";
            button_Connect.Text = "Connect";
            simconnect.Dispose();
            simconnect = null;
        }
 
        void simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
        {
            textBox_Connect.Text = "Connected to FSX";
            button_Connect.Text = "Disconnect";
        }
 
        protected override void DefWndProc(ref Message m)
        {
            if (m.Msg == WM_USER_SIMCONNECT)
            {
                if (simconnect != null)
                {
                    simconnect.ReceiveMessage();
                }
            }
            else
            {
                base.DefWndProc(ref m);
            }
        }
    }
}

Labels: , ,

Craziness

As expected, some things got in the way during my reformat and reinstall.  Needed to clean up my system a bit, and partition out some new space, so I decided to start fresh.

It always happens that something ends up not quite like expected, so it took a bit longer than expected.  Didn't get Visual Studio and FSX installed until late last night.  So new tutorial post today.

On a related note, FSX reactivated without a hitch.

Technorati tags: , ,

Wednesday, December 6, 2006

Windows Live Writer Update

I posted about Live Writer the other day, and expressed a desire to have built in support for code or pre HTML tags.

Turns out, of course, that there is a plugin available that gives you a slick little tool within the program for just this purpose.

You can get it here

Labels: , ,

Updating from Work

A few quick hits:

  • I'm thinking about what would be better: longer, more in-depth tutorials that cover more ground and more detail, or short feature blogs that would take one step and then stop. Obviously, there are benefits to both methods. The short blogs could be posted more often, and would be easy to implement. The longer tutorials would have a greater sense of accomplishment and would have a greater lens on the fine details and inner workings of what you are doing. The purpose is, of course, not to just have you copy my code, but to understand why it works the way it works. The downside to the longer posts is that they probably would be less frequent than the daily updates I had planned on.
  • This project is completely open to suggestions. I have my own goals to accomplish with my programs, but this is separate from that. If you want to learn how to do something, let me know, and I'll incorporate it, assuming I can.
  • Icarus (my FSX add-on) is officially in-development. I even bought a big whiteboard to map everything out on. In case you don't know, Icarus is a program that replaces a good portion of the AI handling in FSX. Currently, I am hoping that I won't have to take over the actual flying of the planes (FSX actually does a rather good job of that), but mostly the way they are vectored via the ATC and how they act on the ground. This includes SIDS and STARS, multiple runways for takeoffs and landing (including crossing runways) better spacing (hopefully resulting in no go-arounds) and a number of other little features.
  • I will be looking for beta testers sometime early next year. The guys I really need are the people who have traffic packages (MyTrafficX, etc) installed. The goal is to accurately and smoothly integrate with those add-ons.
  • Although Icarus is going to be payware (it's a pretty complex undertaking), I am absolutely open to doing as many freeware tools as I can. First off, some of this stuff is not that difficult. Some is, but it's fun to make, so I'll do it anyways. Any ideas?
  • I definitely need beta testers soon for a project that I am almost finished with. It's called TrackerX, and it's basically a FlightAware.com style webpage for FSX. All you do is run a tiny little tray app while you fly, and it uploads your positional info to a database and displays it on a map with everyone else. So far, it's looking really cool, but once the webpage is close to finished, I want to be able to run with it.

That's all for now. Part Three of the tutorial will go up tonight, barring any craziness. I'll also be adding a link section on the sidebar for these tutorials, so you don't have to go digging around everywhere.

(P.S. - I like readers. Tell your friends )

Labels: , , ,

Monday, December 4, 2006

A Ten-Thousand Foot View of SimConnect - Part 2

At this point, our program does very little.  It just connects to SimConnect when you open it.  When we are done with this tutorial, it will do the exact same thing, but it will do it with style.

Hopefully (since I didn't mention it last time) you saved your project when you when you were done.  If so, when you open up Visual C#, it should be listed right there in the "Recent Projects" page.  Open it, and double click on the file "Form1.cs" in the Solution Explorer.  You will be presented with our blank form.

The Design screen in VC# allows us to layout our elements to a Windows Form and adjust the properties of those items.  The easiest way of placing elements is opening the toolbox.  It's an icon in the toolbar at top that looks like a pair of crossed hammers.  Press it and you should get the Toolbox window on the left side of your screen.  Expand the section titled "Common Controls".  Under this you should see Pointer, Button, Checkbox, etc.  All of the things you'd expect to see in a window. 

First things first. click on button and drag over to your form.  A button should appear.  Place it near the top of the form.

 In the properties at the bottom right, find "Name" and "Text".  Both of these should be set to "button1".  We're going to change name to "button_Connect" and text to "Connect to FSX".  You'll notice that the text doesn't fit on the button, so grab one of the square handles surrounding the button and drag it right until it's large enough.

Now, find "TextBox" in the toolbox and drag that next to the button.  Grab a handle and drag to the right, so that the textbox goes almost to the edge of the window.  In the properties, change the Name to "textbox_Connect", border style to "None", background color to "Control" and text to "Not Connected".  If you click on your form now to take away the box around the textbox, you should just see the text.

 

To wrap up the Design view, make sure the form is selected (you can tell because it will have the control handles around it) and on properties, change Text to whatever you want your application to be named.  For me, it's "SimConnect Tutorial".   Press Enter and it should appear in the title bar of your form.

Now we have some pretty elements, but we have to link them into the code to make them do fun stuff. 

Accomplishing this could be very difficult, drawn out and painful.  Instead, MS has made it so that we just double click on our button in the designer.  The program takes us back to the code view, and we see that this has been added to Form1.cs

private void button_Connect_Click(object sender, EventArgs e)
{

}

Remember last time when I was talking about the event system?  Here's a good example.  The program isn't going to be constantly checking to see if you've pressed the button.  Instead, the button click gets linked to a function that fires off when pressed.  That's what we have here.

It could be easy enough just to move our SimConnect statements down into this function, but we want to do a little bit more with it.  For starters, we have no error checking.  If you have tried to run your program while FSX isn't running, you'll notice that it throws an exception, meaning that the program has run into an error it doesn't know how to handle.  There's a way around that, of course, and that's with the try-catch statement.  It looks like this:

try
{
// Run your code here
}
catch
{
// Handle any exceptions here
}

 So in our case, we want it to look like this:

try
{
simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);

}
catch (COMException ex)
{
// Handle any exceptions here
}

 the "COMException ex" part is the exception that gets sent back to our program.   There's some information we could get from it if we wanted to, but we don't really need to worry about that now.  Instead, we are going to use that text box that we created in the Design view. 


A little bit of quick background on Properties and Methods.  Pretty much everything in C# has properties and methods.  Properties, as expected, contain information about the object, methods are actions or functions that it can do.  For example, our text box (textbox_Connect) has a method called Clear, which simply clears the text from the textbox.  To call that method for textbox_Connect, you would write the following:

textbox_Connect.Clear( );

This is called "Dot Syntax" and is used and many forms of programming.  You access a property in the same way:

textBox_Connect.Text = "Your Text Here";

 In this case, we are setting the property "Text" (the actual text that is displayed in the textbox) to whatever we have after the equals sign.  That's what we are going to do in our button click function.

try
{
simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);
textbox_Connect.Text = "Connected to FSX";
}
catch (COMException ex)
{
textbox_Connect.Text = "Unable to Connect";
}

 As you can see, we have text for both possibilities.  If it connects, we get a message saying so, and if it throws an exception (if FSX isn't running) we say so as well.  By setting the text property of the box, we will replace any text that is already in there.  Before giving it a try, make sure you go up to the Form1 constructor and take out the SimConnect line you put up there earlier. 


Form1.cs should now look like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;

namespace SimConnect_Tutorial
{
public partial class Form1 : Form
{
SimConnect simconnect;
const int WM_USER_SIMCONNECT = 0x0402;

public Form1()
{
InitializeComponent();
}

private void button_Connect_Click(object sender, EventArgs e)
{
try
{
simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);
textBox_Connect.Text = "Connected to FSX";
}
catch (COMException ex)
{
textBox_Connect.Text = "Unable to Connect";
}
}
}
}

 This one was a little bit shorter, but the goal is to establish a good framework to go off of.  The next post in this series will add the ability to disconnect using the same button and an overview of SimConnect Event Handlers and detecting in-game events.


Once again, I appreciate any comments or critiques.

An Easier Way to Blog

As a small interim post before the big one tonight:

If you aren't using Windows Live Writer for your blogging, then you are missing out. I don't want to bash the Blogger interface (which I still use at work), but the ease and versatility of Live Writer far surpasses it. For example, it automagically downloads your style information from your blog so that you can write in a "layout" preview mode that puts all of your text in as it would (style -wise) in the blog.

Just a click of the menu and you can get quick previews of how the post will look in your blog, or edit the HTML.

All in all, it's easy to setup, easy to use, and allows for the sort of confidence in use that an application holds above a webpage. Anyone who has written a whole long post to have it disappear knows what I am talking about.

The only thing I would ask for is built in <code>or <pre> support. Sure, you can drop it in the HTML manually, but you'd think that MS would realize how many people out there are blogging about development.

Labels: ,

Saturday, December 2, 2006

A Ten-Thousand Foot View of SimConnect - Part 1

The title of this blog series is credited to Mike Powell, of www.mikesflightdeck.com, probably one of the most informative sites about building hardware for your home flight deck project. Mike and I were talking about what is needed for people who want to use SimConnect but are stymied by the IDE, .NET, the data format, etc.

So, A Ten-Thousand Foot View will try to correct that, by starting from scratch and building a fully-usable program with as much explanation as I can manage.

I am not assuming much in the way of programming knowledge, but at the same time, I'm trying to keep my explanations simple.  The goal is be just a way for people to get off the ground, so to speak.

Prologue: How it all works

Presenting the world's worst flowchart. 

As you can see, SimConnect acts as a transport between your programs or DLLs and FSX.  Having this step in the middle means that not only can you transfer data with FSX, but also with other add-ons.  Input can go directly into the sim like normal, or can intercepted by your program.

There are two basic ways to interface with SimConnect, through C++ or managed code, such as C# or Visual Basic.  In these examples, I use C#.  Managed code provides a lot of tools that I feel makes programming, especially for someone starting off, a lot easier.

SimConnect is based on an event system.  I'll try to keep this as concise as possible, because there are some really good tutorials and books on asynchronous programming out there.  Imagine you are at an office, and you want to put together a presentation on financial performance over the last year.  So you call Accounting and request a report on the ledger.  Then you go about your job, doing other things that need to be done, until they send you the report.  Now you can process that information and use it in your presentation.  SimConnect works in a similar fashion.  You send a request to FSX through the SimConnect layer and set up an event to fire off when you receive something back.  That event handles the data you get from the program.  This is different than synchronous programming where you would send your request and then have to wait around before you could do anything else. 

All of that is just background though and should become clearer as we start working with the API.

Part One: Setting Up For SimConnect

There's a few things you'll need before you get started:

  • Microsoft Flight Simulator X Deluxe Edition
  • Visual C# 2005 Express Edition (available for free here)
  • The .NET Framework (this should be installed with FSX)

Once you have these three things, you're ready to start setting up your workspace.  Go ahead and download and install Visual C# Express if you haven't already. 

Then, pop in your FSX DVD (disk 1) and if autorun is turned on, you'll get the installer popping up.  Cancel out of that and browse through my computer to the files on the DVD.

In the /SDK folder on your DVD, you find "Microsoft Flight Simulator X SDK.msi".  Run that to install the SDK.

NOTE: I don't know if there are conflicts or anything to worry about, but I made it a point to not install the SDK into the same folder as FSX.  Mostly I just like to have everything separate. 

Scrolling down to entries in this blog, you'll see a post titled "SimConnect Debug Console" which tells you how to set up the SimConnect.ini file to get the console.  I'll be referring to it from time to time, so you'll want to do this too.

That does it, you're set up for our first SimConnect project.

Part Two: Hello SimConnect!

Open up Visual C# Express, and you should see a window similar to this (click on the image links for larger versions)

Selecting File -> New Project will bring up, not surprisingly, the New Project dialog.  We just want to pick "Windows Application" and give it a name.  I'm calling it "SimConnect Tutorial" but anything will work.  Click ok and the project will load up, showing you what is called the "Designer" or the "Design View".  This feature of Visual C# Express allows you to visually design your Windows application, and will help us out extremely in the long run.

To the right is the Solution Explorer, which has your project and a file hierarchy below it.  There some properties and references that we don't need to worry about, then our two code files, Form1.cs and Program.cs.  All C# code files should have the .cs extension.

Right-click on Form1.cs and select "View Code".  Program.cs just acts as the entry point into our application and runs our form (the term to describe the window we are creating) so we won't even be bothering with it.

Form1.cs looks like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace SimConnect_Tutorial
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}

The "using" statements at the top are, in this case, references for namespace inclusion.  When you code in C#, you put your code inside these namespace blocks so that they can be logically organized or accessed without any confusion.  As you can see above, our code is all under the "SimConnect_Tutorial" namespace because it is wrapped in that namespace statement.  The "using" statements at the top include the namespaces of some of the standard .NET components, so that we can use them in our program.  For example:

public partial class Form1 : Form 

is the statement to create our Form1 class, the basic object that is going to control our whole program.  It is extending a basic Form class that is part of the .NET framework.  The full name is "System.Windows.Forms.Form".   Without the line "using System.Windows.Forms;" we wouldn't be able to access that element.


The program, at this time, doesn't know how to talk to SimConnect, though.  We want to add that ability in.  In our Solution Explorer (to the right of the code window) right click on "References" and select "Add Reference".  When the window opens up, go to the "Browse" tab and find your SDK installation.  From the directory you installed in, browse through to the following folder:
"/SDK/Core Utilities Kit/SimConnect SDK/lib/managed/"
and you should find a file called "Microsoft.FlightSimulator.SimConnect.dll".  Select this file and click Ok.  You should see the References folder expand in the Solution Explorer and that file will be in there.  Now that the project knows to use SimConnect, we tell the code that we are working on to use it as well.  Right under the line "using System.Windows.Forms;", add these two lines:

using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;

The first adds our SimConnect functions, the second is to allow our managed code (.NET) to talk to unmanaged code (C++).


From here we are ready to work on our Form1 class.


A class is a description of an object in programming.  The example I see most often is an address book.  In an address book, or a contact list in Outlook, you have a pre-defined set of fields that you can put data into.  No matter how many entries you put in the contact list, each one starts with the same fields to fill in.  A class is like this.  It defines what data, and what functions, each instance of an object will have.   In this case, it is defining what our window does.


Before anything else, we want to define a few member variables, or data that the class can use.  For this intro, we only need two variables.  After the line "public partial class Form1 : Form" and the curly bracket under it, type in these lines:

SimConnect simconnect;
const int WM_USER_SIMCONNECT = 0x0402;

These lines define two variables.  The first is our SimConnect object.  The format is Type   Name  =  Value;  The value is optional.  In the case of the SimConnect variable, we will define the value of it later.  The TYPE is the SimConnect Object, the NAME is simconnect (real original, right?) and the Value, as I said, comes later.   Right now, we are just letting our code know that it is there.  The second one is a constant (const) integer (int) which means that it doesn't change in the program.  Its value is static.  


SimConnect works by sending system messages to our program with the data or information we request inside the message.  To identify which system messages are coming from SimConnect, we create our own indentifier to let the program know.  That's what WM_USER_SIMCONNECT is.  That's straight from the SDK docs, you don't really ever have to change it, just remember to include it.


Now, our Form1 class looks like this:

public partial class Form1 : Form
{
SimConnect simconnect;
const int WM_USER_SIMCONNECT = 0x0402;

public Form1()
{

InitializeComponent();
}
}

 The brackets after "public Form1()" contains a function, called the Constructor.  This function executes ("is called") when an object of that class is created.  In this case, when our window opens.  You can identify a constructor because it has the same name as the class (i.e. Form1).


Inside the constructor, above the InitializeComponent() line, we are going to place our function to create a connection to SimConnect.  The line looks like this:

simconnect = new SimConnect("SimConnect Tutorial", this.Handle, WM_USER_SIMCONNECT, null, 0);

 This is creating a new object (of the class SimConnect) by calling its constructor.  The arguments in the parentheses aren't terribly important right now, except to know that the first one in the quotes is where you identify the name of your program to SimConnect, and our buddy WM_USER_SIMCONNECT is in there as well.


Normally, we'd want error checking and a lot of other things in there, but this is just a first step.  Leave Visual C# open and load up FSX.  You don't have to go any farther than the Main Menu.  If you copied over the SimConnect.ini file, you should see a SimConnect Diagnostic Window pop up.  Go back over to your code window and either click the green arrow in the toolbar or press F5 on your keyboard.  If you did everything right, it should think for a second and then pop up a blank grey window.


More importantly, a new line should have appear in the SimConnect console:


> 370.81695 [63, 1]Open: Version=0x00000002 Name="SimConnect Tutorial"


If you see something like this, it means your program has successfully made a connection to SimConnect and is ready to talk to FSX.  To exit out, just click on the x in the corner to close out the program.  Save your work, pat yourself on the back, and I'll move on to the next part in the next few days.


This is my first big tutorial like this.  If you have any questions or comments, please comment below and let me know. 

Friday, December 1, 2006

Engine Fires and Oil Leakage

Over at the Avsim SimConnect Forum, Endre brought up the question of getting engine fires and oil leaks to occur with SimConnect. He said that neither worked properly, so I set out to put together a program to show how it would indeed work as it should.

Problem is, it didn't.

I haven't tried this solution in C++, so it may just be an issue with the managed wrapper. But I think it's more likely that something doesn't work or we aren't implementing it right.

First off, the variables:

ENG ON FIRE:index On fire state
GENERAL ENG OIL LEAKED
PERCENT:index
Percent of max oil
capacity leaked


As you can see, ENG ON FIRE is a simple on/off switch for the failure (so it seems) and our oil var is just a percentage of how much oil has been lost. However, our second variable is read-only.

The ever-brilliant Pete Dowson ( http://www.schiratti.com/dowson.html ) has told us that most of the time in that case, we have to find an event that mirrors the effect. So far, I haven't found an event for either of these. Maybe someone has and can point me in the right direction. Because without an event and with the variable non-settable, we won't be able to do anything with it.

So the code is as follows (just for the engine part of things)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

// Include Simconnect and Interop References
// Remember to reference the SDK .DLL in the project
using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;

namespace Engine_Fire
{
// Standard Windows Form, see the designer for placement
public partial class Form1 : Form
{
// SimConnect Object
SimConnect simconnect;

// User-defined win32 event
const int WM_USER_SIMCONNECT = 0x0402;

// Data definitions
enum DEFINITIONS
{
EngineData,
}

// Requests
enum DATA_REQUESTS
{
SET_ENGINE_FIRE,
}

// Our engine data struct.
// Standard marshalling message
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct EngineData
{
public bool engine1fire;
public bool engine2fire;
};

// Our initializer
// To-do: Initialize Simconnect object,
// Set up data definitions,
// Add struct to marshaller
public Form1()
{
InitializeComponent();

// Initialize SimConnect (this is straight from the SDK, just changed the app name)
simconnect = null;
try
{
simconnect = new SimConnect("Engine Fire Demo", this.Handle, WM_USER_SIMCONNECT, null, 0);
}
catch (COMException ex)
{
// A connection to the SimConnect server could not be established
// Normally you would handle the exception here
}

// Set up data definitions
simconnect.AddToDataDefinition(DEFINITIONS.EngineData, "Eng On Fire:0", "bool", SIMCONNECT_DATATYPE.FLOAT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
simconnect.AddToDataDefinition(DEFINITIONS.EngineData, "Eng On Fire:1", "bool", SIMCONNECT_DATATYPE.FLOAT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);

// Add struct to marshaller
simconnect.RegisterDataDefineStruct(DEFINITIONS.EngineData);

}

// Default System Message Handler
protected override void DefWndProc(ref Message m)
{
if (m.Msg == WM_USER_SIMCONNECT)
{
if (simconnect != null)
{
simconnect.ReceiveMessage();
}
}
else
{
base.DefWndProc(ref m);
}
}

private void button1_Click(object sender, EventArgs e)
{
EngineData sendEngFire = new EngineData();

sendEngFire.engine1fire = true;
sendEngFire.engine2fire = true;
simconnect.SetDataOnSimObject(DEFINITIONS.EngineData, SimConnect.SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG.DEFAULT, sendEngFire);
}
}
}


The key is the button1_Click function. I slapped a button my form and all it's doing is sending a message to FSX to set the ENG ON FIRE vars for engines 1 and 2 to "True". So I fire up FSX, run the code, press the button,

and nothing. No response. I check the console window, and the data certainly is being sent.

Just to make sure something wasn't backwards, I ran a test to pull the variables instead of setting them, and in normal flight, they are set to false, even after trying to set them to "true" with my program.

Here's a shot "in the thick of it" (click for larger version)



Even after sending the variables, they still come back as false.

My assumption right now is that I'm doing it wrong, and I'm certainly open to suggestions.

Labels: , , , , ,

SimConnect Debug Console

For those of you getting started with the SimConnect SDK, there is a message console that allows you to see pretty verbose output about what's happening with your program.

To get it running, copy the SimConnect.ini file from the SDK directory, located under Core Utilities Kit/SimConnect SDK/config and paste it into your My Documents/Flight Simulator X Files directory.

The file looks like this and turns on the console by default:

[SimConnect]
level=verbose
console=1
;RedirectStdOutToConsole=1
;OutputDebugString=1
;file=c:\simconnect%03u.log
;file_next_index=0
;file_max_index=9

For programs where you are doing a lot of data transfer or waiting on events, the log feature can be helpful as well. The SimConnect section of the SDK docs tells you in detail what each line does, but it should be pretty self explanatory.

Labels: ,