1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8
9 using System.Runtime.InteropServices;
10 using System.Threading;
11 using Microsoft.FlightSimulator.SimConnect;
12
13 namespace WinFSX
14 {
15 public partial class frmMonitor : Form
16 {
17 SimConnect sc = null;
18 const int WM_USER_SIMCONNECT = 0x0402;
19
20 int ResponseNo = 0;
21 string Output = "\n\n\n\n\n\n\n\n\n\n";
22 const string URLMap = "http://localhost:53003/GoogleMapsAjax.aspx";
23 bool IsFirstMap = true;
24 bool IsMapInitialized = false;
25 int MapUpdateInterval = 5000;
26
27 Thread thBrowser = null;
28
29 DataDefinition1 dd1;
30 TimeSpan tsETA;
31
32 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
33 struct DataDefinition1
34 {
35 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
36 public string ATCID;
37
38 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
39 public string AircraftTitle;
40
41 public double Latitude;
42 public double Longitude;
43 public double Altitude;
44 public double Airspeed;
45 public double Heading;
46 public double TotalFuelLBS;
47 public double ETESeconds;
48 public int WindsDirection;
49 public int WindsVelocity;
50 public int Temperature;
51 public int RealismPercent;
52 };
53
54 enum DATA_DEFINITION
55 {
56 DataDefinition1
57 };
58
59 enum DATA_REQUESTS
60 {
61 REQUEST_WINFSX
62 };
63
64
65
66 #region EVENT HANDLERS
67
68
69 void sc_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
70 {
71 this.DisplaySimObjectMessage("Error occured! - " + data.dwException.ToString());
72 this.CloseConnection();
73 }
74
75 void sc_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
76 {
77 lblStatus.Text = "Connected";
78 string Message = "Flight Simulator has connected. Version " + data.dwApplicationVersionMajor.ToString() + "." + data.dwApplicationVersionMinor.ToString();
79 this.DisplaySimObjectMessage(Message);
80 }
81
82 void sc_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
83 {
84 if (sc != null)
85 {
86 sc.Dispose();
87 sc = null;
88 }
89
90 IsFirstMap = true;
91
92 timerDisplayInfo.Enabled = false;
93 timerStatus.Enabled = false;
94 timerDisplayMap.Enabled = false;
95
96 SetButtons(false);
97
98 if (thBrowser != null)
99 {
100 thBrowser.Interrupt();
101 thBrowser = null;
102 }
103
104 lblStatus.Text = "Not Connected";
105 this.DisplaySimObjectMessage("Flight Simulator has disconnected.");
106 }
107
108 void sc_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
109 {
110 switch ((DATA_REQUESTS)data.dwRequestID)
111 {
112 case DATA_REQUESTS.REQUEST_WINFSX :
113 if (timerStatus.Enabled == false)
114 {
115 timerStatus.Enabled = true;
116 }
117
118 if (timerDisplayMap.Enabled == false)
119 {
120 timerDisplayMap.Enabled = true;
121 }
122
123 dd1 = (DataDefinition1)data.dwData[0];
124
125 lblTitleData.Text = "[" + dd1.ATCID + "] " + dd1.AircraftTitle;
126 lblLatitudeData.Text = dd1.Latitude.ToString() + " `";
127 lblLongitudeData.Text = dd1.Longitude.ToString() + " `";
128 lblAltitudeData.Text = ((int)dd1.Altitude).ToString() + " Feet / " + ((int)(dd1.Altitude * 0.3048)).ToString() + " Meter";
129 lblTASData.Text = ((int)dd1.Airspeed).ToString("D3") + " Knots / " + ((int)(dd1.Airspeed * 1.8)).ToString("D3") + " KM/H";
130 lblHDGData.Text = ((int)dd1.Heading).ToString("D3") + " `";
131 lblTotalFuelLBSData.Text = dd1.TotalFuelLBS.ToString("N4") + " Pounds";
132 lblWindsData.Text = dd1.WindsDirection.ToString() + " @ " + dd1.WindsVelocity.ToString() + " Knots";
133 lblTemperatureData.Text = dd1.Temperature.ToString() + " `C / " + (((dd1.Temperature) * 9 / 5) + 32).ToString() + " `F";
134 lblRealismData.Text = dd1.RealismPercent.ToString() + " %";
135
136 tsETA = TimeSpan.FromSeconds(dd1.ETESeconds);
137 lblETEData.Text = tsETA.Hours.ToString() + " H " + tsETA.Minutes.ToString() + " M";
138
139 if (IsFirstMap == true)
140 {
141 DisplayMap();
142 IsFirstMap = false;
143 }
144
145 break;
146
147 default :
148 this.DisplaySimObjectMessage("Unknown Request ID - " + data.dwRequestID.ToString());
149 break;
150 }
151 }
152
153
154
155 #endregion
156
157
158
159 public frmMonitor()
160 {
161 InitializeComponent();
162 }
163
164
165
166 protected override void DefWndProc(ref Message m)
167 {
168 if (m.Msg == WM_USER_SIMCONNECT)
169 {
170 if (sc != null)
171 {
172 sc.ReceiveMessage();
173 }
174 }
175 else
176 {
177 base.DefWndProc(ref m);
178 }
179 }
180
181 private void InitializeBrowserThread()
182 {
183 if (thBrowser != null)
184 {
185 thBrowser.Interrupt();
186 thBrowser = null;
187 }
188
189 thBrowser = new Thread(new ThreadStart(DisplayMap));
190 thBrowser.Name = "MapBrowserWorker";
191 thBrowser.IsBackground = true;
192 thBrowser.Priority = ThreadPriority.Highest;
193 }
194
195 private void InitializeDataRequest()
196 {
197 if (sc == null)
198 {
199 if (this.OpenConnection() == false)
200 {
201 MessageBox.Show("[ERROR] Sorry, the Monitor couldn't connect your FSX. Please retry next time.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
202 return;
203 }
204 }
205
206 try
207 {
208 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "ATC ID", null, SIMCONNECT_DATATYPE.STRING32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
209 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Title", null, SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED);
210 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Plane Latitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
211 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Plane Longitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
212 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Plane Altitude", "feet", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
213 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Airspeed True", "knots", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
214 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Plane Heading Degrees Magnetic", "degrees", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
215 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "FUEL TOTAL QUANTITY WEIGHT", "pounds", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
216 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "GPS ETE", "seconds", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
217 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "AMBIENT WIND DIRECTION", "degrees", SIMCONNECT_DATATYPE.INT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
218 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "AMBIENT WIND VELOCITY", "knots", SIMCONNECT_DATATYPE.INT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
219 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "AMBIENT TEMPERATURE", "Celsius", SIMCONNECT_DATATYPE.INT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
220 sc.AddToDataDefinition(DATA_DEFINITION.DataDefinition1, "Realism", "percent", SIMCONNECT_DATATYPE.INT32, 0.0f, SimConnect.SIMCONNECT_UNUSED);
221
222 sc.RegisterDataDefineStruct<DataDefinition1>(DATA_DEFINITION.DataDefinition1);
223 }
224 catch (Exception ex)
225 {
226 this.DisplaySimObjectMessage("[ERROR] An error occured while initializing data request in SimConnect. Please retry next time. - " + ex.Message);
227 }
228
229 timerDisplayInfo.Enabled = true;
230 }
231
232 private void CloseConnection()
233 {
234 this.sc_OnRecvQuit(sc, null);
235 }
236
237 private DialogResult ConfirmFormClosing()
238 {
239 DialogResult UserAnswer = MessageBox.Show("Would you really quit the Flight Simulator System Monitor?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
240
241 return UserAnswer;
242 }
243
244 private void DisplayInformation()
245 {
246 if (sc != null)
247 {
248 try
249 {
250 sc.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_WINFSX, DATA_DEFINITION.DataDefinition1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
251 }
252 catch (Exception ex)
253 {
254 this.CloseConnection();
255 DisplaySimObjectMessage("[ERROR] " + ex.Message);
256 MessageBox.Show("Error occured!", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Stop);
257 return;
258 }
259 }
260 }
261
262 private void DisplayMap()
263 {
264 if (sc == null)
265 {
266 webBrowser1.Navigate(URLMap);
267 return;
268 }
269 else
270 {
271 string AircraftInformation = "<font face=Verdana size=2><b>" + dd1.ATCID + "</b></font><br /><font face=Arial size=1>Altitude " + ((int)dd1.Altitude).ToString("N0") + " (Feet)<br />Airspeed " + ((int)dd1.Airspeed).ToString() + " (Knots) / Heading " + ((int)dd1.Heading).ToString("D3") + " `<br />Estimated Time to Enroute " + lblETEData.Text + "</font>";
272 webBrowser1.Navigate("javascript:SetPosition(" + dd1.Latitude.ToString("N6") + "," + dd1.Longitude.ToString("N6") + ");");
273 }
274 }
275
276 private void DisplaySimObjectMessage(string Message)
277 {
278 ResponseNo = ResponseNo + 1;
279 Output = Output.Substring(Output.IndexOf("\n") + 1);
280 Output = Output + "\n" + ResponseNo.ToString() + " : [" + DateTime.Now.ToUniversalTime().ToString() + "] " + Message + "\n";
281 txtSimConnectMessage.Text = Output;
282 txtSimConnectMessage.Focus();
283 }
284
285 private void ClearDisplayedData()
286 {
287 lblTitleData.Text = "";
288 lblLatitudeData.Text = "";
289 lblLongitudeData.Text = "";
290 lblAltitudeData.Text = "";
291 lblTASData.Text = "";
292 lblHDGData.Text = "";
293 lblTotalFuelLBSData.Text = "";
294 lblETEData.Text = "";
295 lblWindsData.Text = "";
296 lblTemperatureData.Text = "";
297 lblRealismData.Text = "";
298
299 if (webBrowser1.Url != null && webBrowser1.Url.AbsoluteUri == URLMap)
300 {
301 webBrowser1.Stop();
302 webBrowser1.Navigate("javascript:ClearOverlays();");
303 }
304 }
305
306 private bool OpenConnection()
307 {
308 bool blResult = false;
309
310 if (sc == null)
311 {
312 try
313 {
314 sc = new SimConnect(Application.ProductName + " - " + this.Text, this.Handle, WM_USER_SIMCONNECT, null, 0);
315
316 if (sc != null)
317 {
318 blResult = true;
319
320 sc.OnRecvException += new SimConnect.RecvExceptionEventHandler(sc_OnRecvException);
321 sc.OnRecvOpen += new SimConnect.RecvOpenEventHandler(sc_OnRecvOpen);
322 sc.OnRecvQuit += new SimConnect.RecvQuitEventHandler(sc_OnRecvQuit);
323 sc.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(sc_OnRecvSimobjectDataBytype);
324 }
325 else
326 {
327 blResult = false;
328 }
329 }
330 catch (COMException exCom)
331 {
332 this.DisplaySimObjectMessage("[ERROR] The Monitor couldn't find any Flight Simulator X process. Please confirm your Flight Simulator X is running or not.\n - " + exCom.Message);
333 blResult = false;
334 }
335 catch (Exception ex)
336 {
337 this.DisplaySimObjectMessage("[ERROR] " + ex.Message);
338 blResult = false;
339 }
340 }
341 else
342 {
343 blResult = true;
344 }
345
346 this.SetButtons(blResult);
347
348 return blResult;
349 }
350
351 private void SetButtons(bool IsConnected)
352 {
353 if (IsConnected == true)
354 {
355 btnConnect.Enabled = false;
356 btnDisconnect.Enabled = true;
357 }
358 else
359 {
360 btnConnect.Enabled = true;
361 btnDisconnect.Enabled = false;
362
363 this.ClearDisplayedData();
364 }
365 }
366
367
368
369 private void frmMonitor_FormClosed(object sender, FormClosedEventArgs e)
370 {
371 if (sc != null)
372 {
373 this.CloseConnection();
374 }
375 }
376
377 private void frmMonitor_FormClosing(object sender, FormClosingEventArgs e)
378 {
379 if (this.ConfirmFormClosing() == DialogResult.Yes)
380 {
381 this.CloseConnection();
382 return;
383 }
384 else
385 {
386 e.Cancel = true;
387 }
388 }
389
390 private void frmMonitor_Load(object sender, EventArgs e)
391 {
392 this.DisplaySimObjectMessage("Welcome to Flight Simulator System Monitor [Beta Test]");
393
394 btnConnect.Enabled = true;
395 btnDisconnect.Enabled = false;
396 txtMapUpdateInterval.Text = timerDisplayMap.Interval.ToString();
397
398 this.ClearDisplayedData();
399
400 this.timer3_Tick(this, e);
401 }
402
403 private void frmMonitor_Resize(object sender, EventArgs e)
404 {
405 if (this.Width < 800)
406 {
407 this.Width = 800;
408 }
409
410 if (this.Height < 600)
411 {
412 this.Height = 600;
413 }
414
415 webBrowser1.Width = this.Width - 495;
416 webBrowser1.Height = this.Height - 98;
417 txtSimConnectMessage.Height = this.Height - 377;
418
419 if (this.Width >= Screen.PrimaryScreen.WorkingArea.Width)
420 {
421 this.frmMonitor_ResizeEnd(sender, e);
422 }
423 }
424
425 private void frmMonitor_ResizeEnd(object sender, EventArgs e)
426 {
427 if (IsMapInitialized == true)
428 {
429 webBrowser1.Navigate("javascript:SetMapSize(" + (webBrowser1.Width - 30).ToString() + "," + (webBrowser1.Height - 30).ToString() + ");");
430 }
431 }
432
433 private void timer1_Tick(object sender, EventArgs e)
434 {
435 if (sc != null)
436 {
437 this.DisplayInformation();
438 }
439 }
440
441 private void timer2_Tick(object sender, EventArgs e)
442 {
443 if (lblStatus.Text == "")
444 {
445 lblStatus.Text = "Receiving...";
446 }
447 else
448 {
449 lblStatus.Text = "";
450 }
451
452 if (txtMapUpdateInterval.Focused == false)
453 {
454 txtMapUpdateInterval.Text = timerDisplayMap.Interval.ToString();
455 }
456 }
457
458 private void timer3_Tick(object sender, EventArgs e)
459 {
460 InitializeBrowserThread();
461 thBrowser.Start();
462 }
463
464 private void timer4_Tick(object sender, EventArgs e)
465 {
466 lblGMT.Text = DateTime.Now.ToUniversalTime().Hour.ToString("D2") + ":" + DateTime.Now.ToUniversalTime().Minute.ToString("D2") + "." + DateTime.Now.ToUniversalTime().Second.ToString("D2");
467 }
468
469 private void btnConnect_Click(object sender, EventArgs e)
470 {
471 this.InitializeDataRequest();
472 }
473
474 private void btnDisconnect_Click(object sender, EventArgs e)
475 {
476 DialogResult UserAnswer = MessageBox.Show("Would you really disconnect?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
477
478 if (UserAnswer == DialogResult.Yes)
479 {
480 this.CloseConnection();
481 }
482
483 return;
484 }
485
486 private void btnClose_Click(object sender, EventArgs e)
487 {
488 this.Close();
489 }
490
491 private void btnClear_Click(object sender, EventArgs e)
492 {
493 txtSimConnectMessage.Clear();
494 }
495
496 private void chkAlwaysOnTop_CheckedChanged(object sender, EventArgs e)
497 {
498 if (chkAlwaysOnTop.Checked == true)
499 {
500 this.TopMost = true;
501 }
502 else
503 {
504 this.TopMost = false;
505 }
506 }
507
508 private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
509 {
510 if (IsFirstMap == true)
511 {
512 if (webBrowser1.ReadyState == WebBrowserReadyState.Complete && webBrowser1.Url.AbsoluteUri == URLMap)
513 {
514 IsMapInitialized = true;
515 btnConnect.Enabled = true;
516
517 webBrowser1.Navigate("javascript:SetMapSize(" + (webBrowser1.Width - 30).ToString() + "," + (webBrowser1.Height - 30).ToString() + ");");
518 }
519 else
520 {
521 IsFirstMap = true;
522 IsMapInitialized = false;
523 btnConnect.Enabled = false;
524
525 this.DisplaySimObjectMessage("[MAP BROWSER] Unable to initialize the map. Please exit and run this application again or retry. - " + webBrowser1.ReadyState.ToString() + " : " + webBrowser1.Url.ToString() + " :: " + webBrowser1.StatusText);
526 }
527 }
528
529 this.DisplaySimObjectMessage("[MAP BROWSER] " + webBrowser1.ReadyState.ToString() + " : " + webBrowser1.Url.ToString() + " :: " + webBrowser1.StatusText);
530 }
531
532 private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
533 {
534 this.DisplaySimObjectMessage("[MAP BROWSER] " + webBrowser1.ReadyState.ToString() + " : " + webBrowser1.Url.ToString() + " :: " + webBrowser1.StatusText);
535 }
536
537 private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
538 {
539 if (IsFirstMap == true)
540 {
541 if (IsMapInitialized == false)
542 {
543 btnConnect.Enabled = false;
544 }
545 else
546 {
547 btnConnect.Enabled = true;
548 }
549 }
550
551 // this.DisplaySimObjectMessage("[MAP BROWSER] " + webBrowser1.ReadyState.ToString() + " : " + webBrowser1.Url.ToString() + " :: " + webBrowser1.StatusText);
552 }
553
554 private void txtMapUpdateInterval_Enter(object sender, EventArgs e)
555 {
556 btnSetInterval_Click(sender, e);
557 }
558
559 private void txtMapUpdateInterval_Leave(object sender, EventArgs e)
560 {
561 txtMapUpdateInterval_Enter(sender, e);
562 }
563
564 private void txtMapUpdateInterval_TextChanged(object sender, EventArgs e)
565 {
566
567 }
568
569 private void btnSetInterval_Click(object sender, EventArgs e)
570 {
571 if (int.TryParse(txtMapUpdateInterval.Text, out MapUpdateInterval) == true)
572 {
573 if (MapUpdateInterval < 1000)
574 {
575 MapUpdateInterval = 1000;
576 }
577 else
578 {
579 MapUpdateInterval = int.Parse(txtMapUpdateInterval.Text);
580 }
581 }
582 else
583 {
584 MapUpdateInterval = 10000;
585 }
586
587 timerDisplayMap.Interval = MapUpdateInterval;
588
589 txtMapUpdateInterval.Text = MapUpdateInterval.ToString();
590 }
591 }
592 }