Advertisement
Guest User

Untitled

a guest
Jul 23rd, 2017
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.34 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using System.Text;
  9. using System.Windows.Forms;
  10. using System.Diagnostics;
  11. using FSUIPC;
  12.  
  13. namespace FSUIPCClientExample_CSharp
  14. {
  15. public partial class Form1 : Form
  16. {
  17. // Private Static Members
  18. private static readonly string AppTitle = "FSUIPCClientApplication_CSharp";
  19.  
  20. // Create the Offsets we're interested in for this Application
  21. private Offset<int> airspeed = new Offset<int>(0x02BC); // Basic integer read example
  22. private Offset<int> avionics = new Offset<int>(0x2E80); // Basic integer read and write example
  23. private Offset<byte[]> fsLocalDateTime = new Offset<byte[]>(0x0238, 10); // Example of reading an arbitary set of bytes.
  24. private Offset<string> aircraftType = new Offset<string>("AircraftInfo", 0x3160, 24); // Example of string and use of a group
  25. private Offset<BitArray> lights = new Offset<BitArray>(0x0D0C, 2); // Example of BitArray used to manage a bit field type offset.
  26. private Offset<Double> compass = new Offset<double>(0x02CC); // Example for disconnecting/reconnecting
  27. private Offset<short> pause = new Offset<short>(0x0262, true); // Example of a write only offset.
  28. private Offset<short> com2bcd = new Offset<short>(0x3118); // Example of reading a frequency coded in Binary Coded Decimal
  29. private Offset<long> playerLatitude = new Offset<long>(0x0560); // Offset for Lat/Lon features
  30. private Offset<long> playerLongitude = new Offset<long>(0x0568); // Offset for Lat/Lon features
  31. private Offset<short> onGround = new Offset<short>(0x0366); // Offset for Lat/Lon features
  32. private Offset<short> magVar = new Offset<short>(0x02A0); // Offset for Lat/Lon features
  33. private Offset<uint> playerHeadingTrue = new Offset<uint>(0x0580); // Offset for moving the plane
  34. private Offset<long> playerAltitude = new Offset<long>(0x0570); // Offset for moving the plane
  35. private Offset<short> slewMode = new Offset<short>(0x05DC, true); // Offset for moving the plane
  36. private Offset<int> sendControl = new Offset<int>(0x3110, true); // Offset for moving the plane
  37.  
  38. private readonly int REFRESH_SCENERY = 65562; // Control number to refresh the scenery
  39.  
  40. private FsLatLonPoint EGLL; // Holds the position of London Heathrow (EGLL)
  41. private FsLatLonQuadrilateral runwayQuad; // defines the four corners of the runway (27L at EGLL)
  42. private AITrafficServices AI; // Holds a reference to the AI Traffic Services object
  43.  
  44. public Form1()
  45. {
  46. InitializeComponent();
  47. // Setup the example data for London Heathrow
  48. // 1. The position
  49. // This shows an FsLongitude and FsLatitude class made from the Degrees/Minutes/Seconds constructor.
  50. // The Timer1_Tick() method shows a different contructor (using the RAW FSUIPC values).
  51. FsLatitude lat = new FsLatitude(51, 28, 39.0d);
  52. FsLongitude lon = new FsLongitude(0, -27, -41.0d);
  53. EGLL = new FsLatLonPoint(lat, lon);
  54. // Now define the Quadrangle for the 27L (09R) runway.
  55. // We could just define the four corner Lat/Lon points if we knew them.
  56. // In this example however we're using the helper function to calculate the points
  57. // from the runway information. This is the kind of info you can find in the output files
  58. // from Pete Dowson's MakeRunways program.
  59. FsLatitude rwyThresholdLat = new FsLatitude(51.464943d);
  60. FsLongitude rwyThresholdLon = new FsLongitude(-0.434046d);
  61. double rwyMagHeading = 272.7d;
  62. double rwyMagVariation = -3d;
  63. double rwyLength = 11978d;
  64. double rwyWidth = 164d;
  65. // Call the static helper on the FsLatLonQuarangle class to generate the Quadrangle for this runway...
  66. FsLatLonPoint thresholdCentre = new FsLatLonPoint(rwyThresholdLat, rwyThresholdLon);
  67. double trueHeading = rwyMagHeading + rwyMagVariation;
  68. runwayQuad = FsLatLonQuadrilateral.ForRunway(thresholdCentre, trueHeading, rwyWidth, rwyLength);
  69. // Set the default value for the distance units and AI Radar range
  70. this.cbxDistanceUnits.Text = "Nautical Miles";
  71. this.cbxRadarRange.Text = "50";
  72. }
  73.  
  74. // Application started so try to open the connection to FSUIPC
  75. private void Form1_Load(object sender, EventArgs e)
  76. {
  77. openFSUIPC();
  78. }
  79.  
  80. // User pressed connect button so try again...
  81. private void btnStart_Click(object sender, EventArgs e)
  82. {
  83. openFSUIPC();
  84. }
  85.  
  86. // Opens FSUIPC - if all goes well then starts the
  87. // timer to drive start the main application cycle.
  88. // If can't open display the error message.
  89. private void openFSUIPC()
  90. {
  91. try
  92. {
  93. // Attempt to open a connection to FSUIPC (running on any version of Flight Sim)
  94. FSUIPCConnection.Open();
  95. // Opened OK so disable the Connect button
  96. this.btnStart.Enabled = false;
  97. this.chkEnableAIRadar.Enabled = true;
  98. // Start the timer ticking to drive the rest of the application
  99. this.timer1.Interval = 200;
  100. this.timer1.Enabled = true;
  101. // Set the AI object
  102. AI = FSUIPCConnection.AITrafficServices;
  103. }
  104. catch (Exception ex)
  105. {
  106. // Badness occurred - show the error message
  107. MessageBox.Show(ex.Message, AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
  108. FSUIPCConnection.Close();
  109. Debug.WriteLine(ex.Message);
  110. }
  111. }
  112.  
  113. // Application is unloading so call close to cleanup the
  114. // UNMANAGED memory used by FSUIPC.
  115. private void Form1_FormClosed(object sender, FormClosedEventArgs e)
  116. {
  117. FSUIPCConnection.Close();
  118. }
  119.  
  120. // The timer handles the real-time updating of the Form.
  121. // The default group (ie, no group specified) is
  122. // Processed and every Offset in the default group is updated.
  123. private void timer1_Tick(object sender, EventArgs e)
  124. {
  125. // Process the default group
  126. try
  127. {
  128. FSUIPCConnection.Process();
  129.  
  130.  
  131. // IAS - Simple integer returned so just divide as per the
  132. // FSUIPC documentation for this offset and display the result.
  133. double airpeedKnots = ((double)airspeed.Value / 128d);
  134. this.txtIAS.Text = airpeedKnots.ToString("f1");
  135.  
  136.  
  137. // Avionics Master Switch
  138. this.chkAvionics.Checked = (avionics.Value > 0); // 0 = Off, 1 = On.
  139.  
  140.  
  141. // Advanced Concept: Reading Raw Blocks of Data.
  142. // FS Local Date and Time
  143. // This demonstrates getting back an arbitrary number of bytes from an offset.
  144. // Here we're getting 10 back from Offset 0x0328 which contain info about the
  145. // Local date and time in FS.
  146. // Because it's returned as a byte array we need to handle everything ourselves...
  147. // 1. Year (starts at Byte 8) for 2 bytes. (Int16)
  148. // Use the BitConverter class to get it into a native Int16 variable
  149. short year = BitConverter.ToInt16(fsLocalDateTime.Value, 8);
  150. // You could also do it manually if you know about such things...
  151. // short year = (short)(fsLocalDateTime.Value[8] + (fsLocalDateTime.Value[9] * 0x100));
  152. // 2. Make new datetime with the the time value at 01/01 of the year...
  153. // Time - in bytes 0,1 and 2. (Hour, Minute, Second):
  154. DateTime fsTime = new DateTime(year, 1, 1, fsLocalDateTime.Value[0], fsLocalDateTime.Value[1], fsLocalDateTime.Value[2]);
  155. // 3. Get the Day of the Year back (not given as Day and Month)
  156. // and add this on to the Jan 1 date we created above
  157. // to give the final date:
  158. short dayNo = BitConverter.ToInt16(fsLocalDateTime.Value, 6);
  159. fsTime = fsTime.Add(new TimeSpan(dayNo - 1, 0, 0, 0));
  160. // Now print it out
  161. this.txtFSDateTime.Text = fsTime.ToString("dddd, MMMM dd yyyy hh:mm:ss");
  162.  
  163.  
  164. // Lights
  165. // This demonstrates using the BitArray type to handle
  166. // a bit field type offset. The lights are a 2 byte (16bit) bit field
  167. // starting in offset 0D0C.
  168. // To make the code clearer and easier to write in the first
  169. // place - I created a LightType Enum (bottom of this file).
  170. // You could of course just use the literal values 0-9 if you prefer.
  171. // For the first three, I've put alternative lines in comments
  172. // that use a literal indexer instead of the enum.
  173. // Update each checkbox according to the relevent bit in the BitArray...
  174. this.chkBeacon.Checked = lights.Value[(int)LightType.Beacon];
  175. //this.chkBeacon.Checked = lights.Value[1];
  176. this.chkCabin.Checked = lights.Value[(int)LightType.Cabin];
  177. //this.chkCabin.Checked = lights.Value[9];
  178. this.chkInstuments.Checked = lights.Value[(int)LightType.Instruments];
  179. //this.chkInstuments.Checked = lights.Value[5];
  180. this.chkLanding.Checked = lights.Value[(int)LightType.Landing];
  181. this.chkLogo.Checked = lights.Value[(int)LightType.Logo];
  182. this.chkNavigation.Checked = lights.Value[(int)LightType.Navigation];
  183. this.chkRecognition.Checked = lights.Value[(int)LightType.Recognition];
  184. this.chkStrobes.Checked = lights.Value[(int)LightType.Strobes];
  185. this.chkTaxi.Checked = lights.Value[(int)LightType.Taxi];
  186. this.chkWing.Checked = lights.Value[(int)LightType.Wing];
  187.  
  188. // Compass heading
  189. // Used to demonstrate disconnecting and reconnecting an Offset.
  190. // We display the data in the field regardless of whether
  191. // it's been updated or not.
  192. this.txtCompass.Text = compass.Value.ToString("F2");
  193.  
  194. // COM2 frequency
  195. // Shows decoding a DCD frequency to a string
  196. // a. Convert to a string in Hexadecimal format
  197. string com2String = com2bcd.Value.ToString("X");
  198. // b. Add the assumed '1' and insert the decimal point
  199. com2String = "1" + com2String.Substring(0, 2) + "." + com2String.Substring(2, 2);
  200. this.txtCOM2.Text = com2String;
  201.  
  202. // Latitude and Longitude
  203. // Shows using the FsLongitude and FsLatitude classes to easily work with Lat/Lon
  204. // Create new instances of FsLongitude and FsLatitude using the raw 8-Byte data from the FSUIPC Offsets
  205. FsLongitude lon = new FsLongitude(playerLongitude.Value);
  206. FsLatitude lat = new FsLatitude(playerLatitude.Value);
  207. // Use the ToString() method to output in human readable form:
  208. // (note that many other properties are avilable to get the Lat/Lon in different numerical formats)
  209. this.txtLatitude.Text = lat.ToString();
  210. this.txtLongitude.Text = lon.ToString();
  211.  
  212. // Using fsLonLatPoint to calculate distance and bearing between two points
  213. // First get the point for the current plane position
  214. FsLatLonPoint currentPosition = new FsLatLonPoint(lat, lon);
  215. // Get the distance between here and EGLL
  216. double distance = 0;
  217. switch (this.cbxDistanceUnits.Text)
  218. {
  219. case "Nautical Miles":
  220. distance = currentPosition.DistanceFromInNauticalMiles(EGLL);
  221. break;
  222. case "Statute Miles":
  223. distance = currentPosition.DistanceFromInFeet(EGLL) / 5280d;
  224. break;
  225. case "Kilometres":
  226. distance = currentPosition.DistanceFromInMetres(EGLL) / 1000d;
  227. break;
  228. }
  229. // Write the distance to the text box formatting to 2 decimal places
  230. this.txtDistance.Text = distance.ToString("N2");
  231. // Get the bearing (True)
  232. double bearing = currentPosition.BearingTo(EGLL);
  233. // Get the magnetic variation
  234. double variation = (double)magVar.Value * 360d / 65536d;
  235. // convert bearing to magnetic bearing by subtracting the magnetic variation
  236. bearing -= variation;
  237. // Display the bearing in whole numbers and tag on a degree symbol
  238. this.txtBearing.Text = bearing.ToString("F0") + "\u00B0";
  239.  
  240. // Now check if the player is on the runway:
  241. // Test is the plane is on the ground and if the current position is in the bounds of
  242. // the runway Quadrangle we calculated in the constructor above.
  243. chkPlaneOnRunway.Checked = (this.onGround.Value == 1 && runwayQuad.ContainsPoint(currentPosition));
  244. }
  245. catch (FSUIPCException ex)
  246. {
  247. if (ex.FSUIPCErrorCode == FSUIPCError.FSUIPC_ERR_SENDMSG)
  248. {
  249. // Send message error - connection to FSUIPC lost.
  250. // Show message, disable the main timer loop and relight the
  251. // connection button:
  252. // Also Close the broken connection.
  253. this.timer1.Enabled = false;
  254. this.btnStart.Enabled = true;
  255. this.chkEnableAIRadar.Enabled = false;
  256. this.chkEnableAIRadar.Checked = false;
  257. this.AIRadarTimer.Enabled = false;
  258. FSUIPCConnection.Close();
  259. MessageBox.Show("The connection to Flight Sim has been lost.", AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
  260. }
  261. else
  262. {
  263. // not the disonnect error so some other baddness occured.
  264. // just rethrow to halt the application
  265. throw ex;
  266. }
  267. }
  268. catch (Exception)
  269. {
  270. // Sometime when the connection is lost, bad data gets returned
  271. // and causes problems with some of the other lines.
  272. // This catch block just makes sure the user doesn't see any
  273. // other Exceptions apart from FSUIPCExceptions.
  274. }
  275. }
  276.  
  277. private void CheckPlayerIsOnRunway()
  278. {
  279. FsLongitude lon = new FsLongitude(playerLongitude.Value);
  280. FsLatitude lat = new FsLatitude(playerLatitude.Value);
  281. // Get the point for the current plane position
  282. FsLatLonPoint currentPosition = new FsLatLonPoint(lat, lon);
  283.  
  284. // Now define the Quadrangle for the 27L (09R) runway.
  285. // We could just define the four corner Lat/Lon points if we knew them.
  286. // In this example however we're using the helper function to calculate the points
  287. // from the runway information. This is the kind of info you can find in the output files
  288. // from Pete Dowson's MakeRunways program.
  289. FsLatitude rwyThresholdLat = new FsLatitude(51.464943d);
  290. FsLongitude rwyThresholdLon = new FsLongitude(-0.434046d);
  291. double rwyMagHeading = 272.7d;
  292. double rwyMagVariation = -3d;
  293. double rwyLength = 11978d;
  294. double rwyWidth = 164d;
  295.  
  296. // Call the static helper on the FsLatLonQuarangle class to generate the Quadrangle for this runway...
  297. FsLatLonPoint thresholdCentre = new FsLatLonPoint(rwyThresholdLat, rwyThresholdLon);
  298. double trueHeading = rwyMagHeading + rwyMagVariation;
  299. runwayQuad = FsLatLonQuadrilateral.ForRunway(thresholdCentre, trueHeading, rwyWidth, rwyLength);
  300.  
  301. // Now check if the player is on the runway:
  302. // Test is the plane is on the ground and if the current position is in the bounds of
  303. // the runway Quadrangle we calculated in the constructor above.
  304. if (this.onGround.Value == 1 && runwayQuad.ContainsPoint(currentPosition))
  305. {
  306. // Player is on the runway
  307. // Do Stuff
  308. }
  309. }
  310.  
  311. // Demonstrates the Grouping facility and also returning a string.
  312. // The AircraftType Offset is in a Group called "AircraftInfo".
  313. // With the Group system you can gain control over which
  314. // Offsets are processed.
  315. private void btnGetAircraftType_Click(object sender, EventArgs e)
  316. {
  317. // Aircraft type is in the "AircraftInfo" data group so we only want to proccess that here.
  318. try
  319. {
  320. FSUIPCConnection.Process("AircraftInfo");
  321. // OK so display the string
  322. // With strings the DLL automatically handles the
  323. // ASCII/Unicode conversion and deals with the
  324. // zero terminators.
  325. this.txtAircraftType.Text = aircraftType.Value;
  326. }
  327. catch (Exception ex)
  328. {
  329. MessageBox.Show(ex.Message, AppTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
  330. }
  331. }
  332.  
  333.  
  334. // Next few eventhandlers deal with writing the lights.
  335. // Again we use the BitArray to manage the individual bits.
  336. // I've also used the LightType enum again for the bit numbers, although
  337. // that's not to everyone's taste. Some alternative lines using literal index
  338. // numbers are included as comments for the first two.
  339. private void chkNavigation_CheckedChanged(object sender, EventArgs e)
  340. {
  341. this.lights.Value[(int)LightType.Navigation] = this.chkNavigation.Checked;
  342. //this.lights.Value[0] = this.chkNavigation.Checked;
  343. }
  344.  
  345. private void chkBeacon_CheckedChanged(object sender, EventArgs e)
  346. {
  347. this.lights.Value[(int)LightType.Beacon] = this.chkBeacon.Checked;
  348. //this.lights.Value[1] = this.chkBeacon.Checked;
  349. }
  350.  
  351. private void chkLanding_CheckedChanged(object sender, EventArgs e)
  352. {
  353. this.lights.Value[(int)LightType.Landing] = this.chkLanding.Checked;
  354. }
  355.  
  356. private void chkTaxi_CheckedChanged(object sender, EventArgs e)
  357. {
  358. this.lights.Value[(int)LightType.Taxi] = this.chkTaxi.Checked;
  359. }
  360.  
  361. private void chkStrobes_CheckedChanged(object sender, EventArgs e)
  362. {
  363. this.lights.Value[(int)LightType.Strobes] = this.chkStrobes.Checked;
  364. }
  365.  
  366. private void chkInstuments_CheckedChanged(object sender, EventArgs e)
  367. {
  368. this.lights.Value[(int)LightType.Instruments] = this.chkInstuments.Checked;
  369. }
  370.  
  371. private void chkRecognition_CheckedChanged(object sender, EventArgs e)
  372. {
  373. this.lights.Value[(int)LightType.Recognition] = this.chkRecognition.Checked;
  374. }
  375.  
  376. private void chkWing_CheckedChanged(object sender, EventArgs e)
  377. {
  378. this.lights.Value[(int)LightType.Wing] = this.chkWing.Checked;
  379. }
  380.  
  381. private void chkLogo_CheckedChanged(object sender, EventArgs e)
  382. {
  383. this.lights.Value[(int)LightType.Logo] = this.chkLogo.Checked;
  384. }
  385.  
  386. private void chkCabin_CheckedChanged(object sender, EventArgs e)
  387. {
  388. this.lights.Value[(int)LightType.Cabin] = this.chkCabin.Checked;
  389. }
  390.  
  391. // Demonstrates a simple 'write' to an Offset.
  392. // To send a value to FSUIPC, just change the Value property.
  393. // The new data will be written during the next Process().
  394. private void chkAvionics_CheckedChanged(object sender, EventArgs e)
  395. {
  396. this.avionics.Value = chkAvionics.Checked ? 1 : 0;
  397. }
  398.  
  399. // This demonstrates disconnecting an individual Offset.
  400. // After it's disconnected it doesn't get updated from FSUIPC
  401. // and changed to the value of this Offset do not get written
  402. // when Process() is called.
  403. private void btnDisconnect_Click(object sender, EventArgs e)
  404. {
  405. // Disconnect immediatley.
  406. this.compass.Disconnect();
  407. }
  408.  
  409. // Same as disconnect, except the disconnect happens after
  410. // the next Process. So one more read/write is done and then
  411. // the Offset is disconnected.
  412. private void btnDisconnectAfterNext_Click(object sender, EventArgs e)
  413. {
  414. // Diconnect after the next process.
  415. this.compass.Disconnect(true);
  416. }
  417.  
  418. // This demonstrates reconnecting an Offset. It's value
  419. // will be read/written during the subsequent Process()
  420. // calls.
  421. private void btnReconnect_Click(object sender, EventArgs e)
  422. {
  423. // Reconnect
  424. this.compass.Reconnect();
  425. }
  426.  
  427. // Same as reconnect except the the Offset will be disconnected
  428. // again after the next Process() call.
  429. private void btnReconnectOnce_Click(object sender, EventArgs e)
  430. {
  431. // Reconnect, but only for one Process().
  432. // The Offset is then disconnected again.
  433. this.compass.Reconnect(true);
  434. }
  435.  
  436. // The pause Offset is Write only. It's value is never updated from
  437. // FSUIPC. When it's value changes the new value is written
  438. // to FSUIPC during the next Process().
  439. private void chkPause_CheckedChanged(object sender, EventArgs e)
  440. {
  441. pause.Value = (short)(this.chkPause.Checked ? 1 : 0);
  442. }
  443.  
  444. private enum LightType
  445. {
  446. Navigation,
  447. Beacon,
  448. Landing,
  449. Taxi,
  450. Strobes,
  451. Instruments,
  452. Recognition,
  453. Wing,
  454. Logo,
  455. Cabin
  456. }
  457.  
  458. private void chkEnableAIRadar_CheckedChanged(object sender, EventArgs e)
  459. {
  460. // Turn on/off the radar timer to update the radar info (runs every second)
  461. this.AIRadarTimer.Enabled = this.chkEnableAIRadar.Checked;
  462. // Force the panel to redraw now rather than wait one second
  463. this.pnlAIRadar.Invalidate();
  464. }
  465.  
  466. private void AIRadarTimer_Tick(object sender, EventArgs e)
  467. {
  468. // Every second we update the Ground and Airborne AI trafic info
  469. AI.RefreshAITrafficInformation(this.chkShowGroundAI.Checked, this.chkShowAirborneAI.Checked);
  470. // Apply a filter according to the range set by the user
  471. // Filtering ground and airborne traffic, no bearing filter (include from 0-360)
  472. // No altitude filter (passing nulls)
  473. // Range as set by the combo box.
  474. AI.ApplyFilter(true, true, 0, 360, null, null, double.Parse(this.cbxRadarRange.Text));
  475. // Invalidate the radar panel so it redraws.
  476. this.pnlAIRadar.Invalidate();
  477. }
  478.  
  479. private void pnlAIRadar_Paint(object sender, PaintEventArgs e)
  480. {
  481. // This gets called whenever the panel needs to draw itself.
  482. if (this.chkEnableAIRadar.Checked)
  483. {
  484. // First Clear the panel and make a black background
  485. e.Graphics.Clear(Color.Black);
  486. // Start by working out the centre of the radar and draw the centre cross
  487. Point centre = new Point(this.pnlAIRadar.ClientSize.Width / 2, this.pnlAIRadar.ClientSize.Height / 2);
  488. e.Graphics.DrawLine(Pens.White, centre.X - 4, centre.Y, centre.X + 4, centre.Y);
  489. e.Graphics.DrawLine(Pens.White, centre.X, centre.Y - 4, centre.X, centre.Y + 4);
  490. double range = double.Parse(this.cbxRadarRange.Text);
  491. // work out the scale using the range and the smallest size of the panel
  492. double scale = range / (double)(this.pnlAIRadar.ClientSize.Width < this.pnlAIRadar.ClientSize.Height ? this.pnlAIRadar.ClientSize.Width : this.pnlAIRadar.ClientSize.Height) * 2d;
  493. // Go through each plane and draw it on the radar
  494. // Note: We are using the seperate collections for the ground and airborne
  495. // There is a collection of all AI traffic called 'AllTraffic' which can be used if
  496. // you do not want to deal with these seperatley.
  497. // First, draw the ground AI if required
  498. if (this.chkShowGroundAI.Checked)
  499. {
  500. // Loop through the collection of plane objects in the GroundTraffic collection.
  501. foreach (AIPlaneInfo plane in AI.GroundTraffic)
  502. {
  503. // Here we just pass the planeInfo off to the draw routine.
  504. // There is quite a lot of information available in the AIPlaneInfo object.
  505. // See the reference manual or Intellisense for details.
  506. drawTarget(e.Graphics, scale, centre, plane);
  507. }
  508. }
  509. // Next, draw the Airborne AI if required
  510. if (this.chkShowAirborneAI.Checked)
  511. {
  512. // Loop through the collection of plane objects in the GroundTraffic collection.
  513. foreach (AIPlaneInfo plane in AI.AirbourneTraffic)
  514. {
  515. drawTarget(e.Graphics, scale, centre, plane);
  516. }
  517. }
  518. }
  519. else
  520. {
  521. // Radar turned off so just clear it with white
  522. e.Graphics.Clear(Color.White);
  523. }
  524. }
  525.  
  526. private void drawTarget(Graphics graphics, double scale, Point centre, AIPlaneInfo plane)
  527. {
  528. // We are going to use some of the info from the plane object to draw the target.
  529. // Lots more info is avilable for other application.
  530. // See the reference manual or Intellisense for details.
  531. // Work out the range of the target in pixels by multiplying by the scale
  532. double distancePixels = plane.DistanceNM / scale;
  533. // Work out the position from the centre using this distance and the bearing
  534. double dx = Math.Cos(degreeToRadian(plane.BearingTo)) * distancePixels;
  535. double dy = Math.Sin(degreeToRadian(plane.BearingTo)) * distancePixels;
  536. PointF target = new PointF((float)centre.X + (float)dx, (float)centre.Y + (float)dy);
  537. // Draw the target circle around this point oriented to the plane's heading
  538. graphics.DrawEllipse(Pens.LightGreen, target.X - 4f, target.Y - 4f, 8f, 8f);
  539. // Draw a line from the circle to indicate heading
  540. double tailHeading = 180d + plane.HeadingDegrees;
  541. dx = Math.Cos(degreeToRadian(tailHeading)) * 12;
  542. dy = Math.Sin(degreeToRadian(tailHeading)) * 12;
  543. PointF tailEnd = new PointF(target.X + (float)dx, target.Y + (float)dy);
  544. graphics.DrawLine(new Pen(new LinearGradientBrush(target, tailEnd, Color.LightGreen, Color.DarkGreen)), target, tailEnd);
  545. // Work out the position of the data block
  546. PointF dataBlock = new PointF(target.X + 20, target.Y - 20);
  547. // Draw the line to the datablock
  548. graphics.DrawLine(Pens.LightGreen, new PointF(target.X + 5, target.Y - 5), new PointF(dataBlock.X - 5, dataBlock.Y + 7));
  549. // Draw the data block
  550. // Line 1 - the Callsign
  551. graphics.DrawString(plane.ATCIdentifier, this.pnlAIRadar.Font, Brushes.LightGreen, dataBlock);
  552. // Line 2 - the Altitude (hundreds of feet) and speed
  553. string line2 = "";
  554. line2 += ((int)(plane.AltitudeFeet / 100d)).ToString("d3");
  555. // Put a +,- or = depending on if the plane is decending, climbing or level
  556. if (plane.VirticalSpeedFeet < 0)
  557. {
  558. line2 += "-";
  559. }
  560. else if (plane.VirticalSpeedFeet > 0)
  561. {
  562. line2 += "+";
  563. }
  564. else
  565. {
  566. line2 += "=";
  567. }
  568. graphics.DrawString(line2, this.pnlAIRadar.Font, Brushes.LightGreen, new PointF(dataBlock.X, dataBlock.Y + 12));
  569. // Line 3 - origin, destination and assigned runway
  570. graphics.DrawString(plane.DepartureICAO + "->" + plane.DestinationICAO + " " + plane.RunwayAssigned.ToString(), this.pnlAIRadar.Font, Brushes.LightGreen, new PointF(dataBlock.X, dataBlock.Y + 24));
  571. }
  572.  
  573. private double degreeToRadian(double angle)
  574. {
  575. return Math.PI * angle / 180.0;
  576. }
  577.  
  578. private void btnMoveToEGLL_Click(object sender, EventArgs e)
  579. {
  580. // Suspend the timers
  581. if (this.timer1.Enabled)
  582. {
  583. this.timer1.Enabled = false;
  584. if (this.chkEnableAIRadar.Checked)
  585. {
  586. this.AIRadarTimer.Enabled = false;
  587. }
  588. // Put the sim into Slew mode
  589. slewMode.Value = 1;
  590. FSUIPCConnection.Process();
  591. // Make a new point representing the centre of the threshold for 27L
  592. FsLatitude lat = new FsLatitude(51.464943d);
  593. FsLongitude lon = new FsLongitude(-0.434046d);
  594. FsLatLonPoint newPos = new FsLatLonPoint(lat, lon);
  595. // Now move this point 150 metres up the runway
  596. // Use one of the OffsetBy methods of the FsLatLonPoint class
  597. double rwyTrueHeading = 269.7d;
  598. newPos = newPos.OffsetByMetres(rwyTrueHeading, 150);
  599. // Set the new position
  600. playerLatitude.Value = newPos.Latitude.ToFSUnits8();
  601. playerLongitude.Value = newPos.Longitude.ToFSUnits8();
  602. // set the heading and altitude
  603. playerAltitude.Value = 0;
  604. playerHeadingTrue.Value = (uint)(rwyTrueHeading * (65536d * 65536d) / 360d);
  605. FSUIPCConnection.Process();
  606. // Turn off the slew mode
  607. slewMode.Value = 0;
  608. FSUIPCConnection.Process();
  609. // Refresh the scenery
  610. sendControl.Value = REFRESH_SCENERY;
  611. FSUIPCConnection.Process();
  612. // Reenable the timers
  613. this.timer1.Enabled = true;
  614. this.AIRadarTimer.Enabled = this.chkEnableAIRadar.Checked;
  615. }
  616. }
  617. }
  618.  
  619. // A double buffered panel used for the radar scope.
  620. // The normal panel that .NET supplies doesn't use double buffering
  621. // and therefore suffers from massive flickering issues.
  622. public class DoubleBufferPanel : Panel
  623. {
  624. public DoubleBufferPanel()
  625. {
  626. // Set the value of the double-buffering style bits to true.
  627. this.SetStyle(ControlStyles.DoubleBuffer |
  628. ControlStyles.UserPaint |
  629. ControlStyles.AllPaintingInWmPaint,
  630. true);
  631.  
  632. this.UpdateStyles();
  633. }
  634. }
  635. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement