Sample: AI_Waypoints.cs

//Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Managed AI Waypoints sample
//
// Ensure that your aircraft is at the start of the main runway at Sea-Tac airport (KSEA).
//
// Click on Connect to try and connect to a running version of ESP
// Click on Create AI objects once to create two aircraft and a fuel truck
// Click on Send AI Waypoints once to send waypoints to the two AI objects
// Watch the Extra 300S and fuel truck go into motion
// Click on Send smoke request to see a smoke trail from the Extra 300S
// Click on Disconnect to close the connection, and then you will
// be able to click on Connect and restart the process
//

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

// Add these two statements to all SimConnect clients
using Microsoft.ESP.SimConnect;
using System.Runtime.InteropServices;

namespace Managed_AI_Waypoints
{
    public partial class AI_Waypoints : Form
    {

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

        // SimConnect object
        SimConnect simconnect = null;

        // Object IDs
        uint Extra300SID = 0;
        uint TruckID = 0;
        uint DouglasID = 0;

        enum DEFINITIONS
        {
            Extra300SWaypoints,
            FuelTruckWaypoints,
            ExtraSmoke,
        }

        enum DATA_REQUESTS
        {
            REQUEST_DOUGLAS,
            REQUEST_Extra300S,
            REQUEST_TRUCK,
        };

        enum EVENTS
        {
            PAUSED,
            SEND_UNPAUSE,
        };

        enum GROUPID
        {
            FLAG = 2000000000,
        };

        // this is how you declare a data structure so that
        // simconnect knows how to fill it/read it.
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        struct ExtraSmoke
        {
            public bool smokeEnable;
            // Add more data here if necessary
          
        };

        // Declare the actual structure
        ExtraSmoke es;
        
        public AI_Waypoints()
        {
            InitializeComponent();

            setButtons(true, false, false, false, false);

            es.smokeEnable = false;
        }
        // Simconnect client will send a win32 message when there is
        // a packet to process. ReceiveMessage must be called to
        // trigger the events. This model keeps simconnect processing on the main thread.

        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 setButtons(bool bConnect, bool bCreate, bool bSend, bool bSmoke, bool bDisconnect)
        {
            buttonConnect.Enabled = bConnect;
            buttonCreateAIObjects.Enabled = bCreate;
            buttonSendWaypoints.Enabled = bSend;
            buttonSmoke.Enabled = bSmoke;
            buttonDisconnect.Enabled = bDisconnect;
        }

        private void closeConnection()
        {
            if (simconnect != null)
            {
                // Dispose serves the same purpose as SimConnect_Close()
                simconnect.Dispose();
                simconnect = null;
                es.smokeEnable = false;
                displayText("Connection closed");
            }
        }

        // Set up the SimConnect event handlers
        private void initComms()
        {
            try
            {
                // listen to connect and quit msgs
                simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
                simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);

                // listen to exceptions
                simconnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(simconnect_OnRecvException);

                // catch the assigned object IDs
                simconnect.OnRecvAssignedObjectId += new SimConnect.RecvAssignedObjectIdEventHandler(simconnect_OnRecvAssignedObjectId);

                // set up the data definiton for the Extra smoke
                simconnect.AddToDataDefinition(DEFINITIONS.ExtraSmoke, "SMOKE ENABLE", "bool", SIMCONNECT_DATATYPE.INT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                // IMPORTANT: register it with the simconnect managed wrapper marshaller
                // if you skip this step, you will only receive a uint in the .dwData field.
                simconnect.RegisterDataDefineStruct<ExtraSmoke>(DEFINITIONS.ExtraSmoke);

                // Subscribe to system event Pause

                simconnect.SubscribeToSystemEvent(EVENTS.PAUSED, "Pause");
                simconnect.OnRecvEvent += new SimConnect.RecvEventEventHandler(simconnect_OnRecvEvent);

                // Map an event to the EventID: PAUSE_OFF
                simconnect.MapClientEventToSimEvent(EVENTS.SEND_UNPAUSE, "PAUSE_OFF");
            
            }
            catch (COMException ex)
            {
                displayText(ex.Message);
            }
        }

        // The simulation will pause each time a key is selected in the addon, so unpause the sim each time this happens

        void simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data)
        {
            displayText("Pause event received");

            switch ((EVENTS)data.uEventID)
            {
                case EVENTS.PAUSED:

                    simconnect.TransmitClientEvent((uint) SimConnect.SIMCONNECT_OBJECT_ID_USER, EVENTS.SEND_UNPAUSE, (uint) 0, GROUPID.FLAG, SIMCONNECT_EVENT_FLAG.GROUPID_IS_PRIORITY);
                    displayText("Unpause request sent...");
                    break;

            }
        }

        void simconnect_OnRecvAssignedObjectId(SimConnect sender, SIMCONNECT_RECV_ASSIGNED_OBJECT_ID data)
        {
            switch ((DATA_REQUESTS)data.dwRequestID)
            {
                case DATA_REQUESTS.REQUEST_DOUGLAS:

                    DouglasID = (uint) (DATA_REQUESTS)data.dwObjectID;
                    displayText("Received Douglas ID");
                    break;

                case DATA_REQUESTS.REQUEST_Extra300S:

                    Extra300SID = (uint) (DATA_REQUESTS)data.dwObjectID;
                    displayText("Received Extra 300S ID");
                    break;

                case DATA_REQUESTS.REQUEST_TRUCK:
                    TruckID = (uint) (DATA_REQUESTS)data.dwObjectID;
                    displayText("Received Truck ID");
                    break;

                default:
                    displayText("Unknown Request ID received: " + (DATA_REQUESTS)data.dwRequestID);
                    break;

            }
        }

        void simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
        {
            displayText("Connected to ESP");
        }

        // The case where the user closes ESP
        void simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
        {
            displayText("ESP has exited");
            closeConnection();
        }

        void simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
        {
            displayText("Exception received: " + data.dwException);
        }

        // The case where the user closes the client
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            closeConnection();
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (simconnect == null)
            {
                try
                {
                    // the constructor is similar to SimConnect_Open in the native API
                    simconnect = new SimConnect("Managed AI Waypoints", this.Handle, WM_USER_SIMCONNECT, null, 0);

                    setButtons(false, true, false, false, true);

                    initComms();

                }
                catch
                {
                    displayText("Unable to connect to ESP!");
                }
            }
            else
            {
                displayText("Error - try again!");
                closeConnection();

                setButtons(true, false, false, false, false);
            }
        }

        private void buttonDisconnect_Click(object sender, EventArgs e)
        {
            closeConnection();
            setButtons(true, false, false, false, false);
        }

        private void buttonSmoke_Click(object sender, EventArgs e)
        {
            if (Extra300SID != 0)
            {
                // Toggle the smoke
                es.smokeEnable = !es.smokeEnable;

                simconnect.SetDataOnSimObject(DEFINITIONS.ExtraSmoke, Extra300SID, 0, es);

                if (es.smokeEnable)
                    displayText("Smoke on requested...");
                else
                    displayText("Smoke off requesteed...");
            }
            else
                displayText("Extra 300S ID not set!");
        }

        private void buttonSendWaypoints_Click(object sender, EventArgs e)
        {
            if (Extra300SID != 0 && TruckID != 0)
            {
                SIMCONNECT_DATA_WAYPOINT[] wp = new SIMCONNECT_DATA_WAYPOINT[3];
                SIMCONNECT_DATA_WAYPOINT[] ft = new SIMCONNECT_DATA_WAYPOINT[2];

                simconnect.AddToDataDefinition(DEFINITIONS.Extra300SWaypoints, "AI WAYPOINT LIST", "number", SIMCONNECT_DATATYPE.WAYPOINT, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                simconnect.AddToDataDefinition(DEFINITIONS.FuelTruckWaypoints, "AI WAYPOINT LIST", "number", SIMCONNECT_DATATYPE.WAYPOINT, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                // Extra300S aircraft should fly in circles across the North end of the runway

                wp[0].Flags = (uint)SIMCONNECT_WAYPOINT_FLAGS.SPEED_REQUESTED;
                wp[0].Altitude = 800;
                wp[0].Latitude = 47 + (27.79 / 60);
                wp[0].Longitude = -122 - (18.46 / 60);
                wp[0].ktsSpeed = 100;

                wp[1].Flags = (uint)SIMCONNECT_WAYPOINT_FLAGS.SPEED_REQUESTED;
                wp[1].Altitude = 600;
                wp[1].Latitude = 47 + (27.79 / 60);
                wp[1].Longitude = -122 - (17.37 / 60);
                wp[1].ktsSpeed = 100;

                wp[2].Flags = (uint)(SIMCONNECT_WAYPOINT_FLAGS.WRAP_TO_FIRST | SIMCONNECT_WAYPOINT_FLAGS.SPEED_REQUESTED);
                wp[2].Altitude = 800;
                wp[2].Latitude = 47 + (27.79 / 60);
                wp[2].Longitude = -122 - (19.92 / 60);
                wp[2].ktsSpeed = 100;

                // Create a polymorphic array

                Object[] objv1 = new Object[wp.Length];
                wp.CopyTo(objv1, 0);

                // Send the three waypoints to the Extra300S
                simconnect.SetDataOnSimObject(DEFINITIONS.Extra300SWaypoints, Extra300SID, 0, objv1);

                // Truck goes down the runway
                ft[0].Flags = (uint)SIMCONNECT_WAYPOINT_FLAGS.SPEED_REQUESTED;
                ft[0].Altitude = 433;
                ft[0].Latitude = 47 + (25.93 / 60);
                ft[0].Longitude = -122 - (18.46 / 60);
                ft[0].ktsSpeed = 75;

                ft[1].Flags = (uint)(SIMCONNECT_WAYPOINT_FLAGS.WRAP_TO_FIRST | SIMCONNECT_WAYPOINT_FLAGS.SPEED_REQUESTED);
                ft[1].Altitude = 433;
                ft[1].Latitude = 47 + (26.25 / 60);
                ft[1].Longitude = -122 - (18.46 / 60);
                ft[1].ktsSpeed = 55;

                // Create a polymorphic array

                Object[] objv2 = new Object[ft.Length];
                ft.CopyTo(objv2, 0);

                // Send the waypoints to the fuel truck
                simconnect.SetDataOnSimObject(DEFINITIONS.FuelTruckWaypoints, TruckID, 0, objv2);

                displayText("Waypoint lists sent...");
                setButtons(false, false, false, true, true);
            } else 
                displayText("Extra 300S or Truck IDs not set!");
        }

        private void buttonCreateAIObjects_Click(object sender, EventArgs e)
        {
            SIMCONNECT_DATA_INITPOSITION Init;

            // Add a parked museum aircraft, just west of the runway

            Init.Altitude = 433.0;                    // Altitude of Sea-tac is 433 feet
            Init.Latitude = 47 + (25.97 / 60);        // Convert from 47 25.97 N
            Init.Longitude = -122 - (18.51 / 60);    // Convert from 122 18.51 W
            Init.Pitch = 0.0;
            Init.Bank = 0.0;
            Init.Heading = 90.0;
            Init.OnGround = 1;
            Init.Airspeed = 0;
            simconnect.AICreateSimulatedObject("Douglas DC-3", Init, DATA_REQUESTS.REQUEST_DOUGLAS);
            
            // Initialize Extra300S aircraft just in front of user aircraft, at 47 25.89 N, 122 18.48 W

            Init.Altitude = 433.0;                    // Altitude of Sea-tac is 433 feet
            Init.Latitude = 47 + (25.91 / 60);        // Convert from 47 25.90 N
            Init.Longitude = -122 - (18.48 / 60);    // Convert from 122 18.48 W
            Init.Pitch = 0.0;
            Init.Bank = 0.0;
            Init.Heading = 360.0;
            Init.OnGround = 1;
            Init.Airspeed = 1;

            simconnect.AICreateNonATCAircraft("Extra 300S Paint1", "N1001", Init, DATA_REQUESTS.REQUEST_Extra300S);
            
            // Initialize truck just in front of user aircraft
            // User aircraft is at 47 25.89 N, 122 18.48 W

            Init.Altitude = 433.0;                    // Altitude of Sea-tac is 433 feet
            Init.Latitude = 47 + (25.91 / 60);        // Convert from 47 25.90 N
            Init.Longitude = -122 - (18.47 / 60);    // Convert from 122 18.48 W
            Init.Pitch = 0.0;
            Init.Bank = 0.0;
            Init.Heading = 360.0;
            Init.OnGround = 1;
            Init.Airspeed = 0;

            simconnect.AICreateSimulatedObject("VEH_jetTruck", Init, DATA_REQUESTS.REQUEST_TRUCK);

            displayText("Request to create objects sent...");
            setButtons(false, false, true, true, true);
        }

        // Response number
        int response = 1;

        // Output text - display a maximum of 10 lines
        string output = "\n\n\n\n\n\n\n\n\n\n";

        void displayText(string s)
        {
            // remove first string from output
            output = output.Substring(output.IndexOf("\n") + 1);

            // add the new string
            output += "\n" + response++ + ": " + s;

            // display it
            richResponse.Text = output;
        }
    }
}
// End of sample