No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MonitorUI.cs 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. //
  2. // MonitorUI.cs
  3. //
  4. // Author:
  5. // Di MERCURIO Sébastien <dimercur@insa-toulouse.fr>
  6. //
  7. // Copyright (c) 2018 INSA - DGEI
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. using System;
  22. using Gtk;
  23. using Gdk;
  24. using Cairo;
  25. using monitor;
  26. using System.Timers;
  27. /// <summary>
  28. /// Main part of the program, behavior of main window
  29. /// </summary>
  30. public partial class MainWindow : Gtk.Window
  31. {
  32. /// <summary>
  33. /// Destijl command manager reference
  34. /// </summary>
  35. private DestijlCommandManager cmdManager;
  36. /// <summary>
  37. /// Pixbuffer used for displaying image
  38. /// </summary>
  39. private Pixbuf drawingareaCameraPixbuf;
  40. /// <summary>
  41. /// Position used for displaying position
  42. /// </summary>
  43. private DestijlCommandManager.Position position=new DestijlCommandManager.Position();
  44. /// <summary>
  45. /// List of availble state for the application
  46. /// </summary>
  47. enum SystemState
  48. {
  49. NotConnected,
  50. ServerConnected,
  51. RobotConnected
  52. };
  53. /// <summary>
  54. /// The state of the system. Can take a value from SystemState
  55. /// </summary>
  56. private SystemState systemState = SystemState.NotConnected;
  57. /// <summary>
  58. /// Timer for battery request
  59. /// </summary>
  60. private System.Timers.Timer batteryTimer;
  61. private int imageReceivedCounter = 0;
  62. private int badImageReceivedCounter = 0;
  63. private int imageFPS = 0;
  64. private int imageFPScounter = 0;
  65. /// <summary>
  66. /// Timer for FPS request
  67. /// </summary>
  68. private System.Timers.Timer fpsTimer;
  69. /// <summary>
  70. /// Initializes a new instance of the <see cref="MainWindow"/> class.
  71. /// </summary>
  72. public MainWindow() : base(Gtk.WindowType.Toplevel)
  73. {
  74. Build();
  75. cmdManager = new DestijlCommandManager(OnCommandReceivedEvent);
  76. // create new timer for battery request, every 10s
  77. batteryTimer = new System.Timers.Timer(10000.0);
  78. batteryTimer.Elapsed += OnBatteryTimerElapsed;
  79. // create new timer for FPS , every 1s
  80. fpsTimer = new System.Timers.Timer(1000.0);
  81. fpsTimer.Elapsed += OnFpsTimerElapsed;
  82. // Customize controls
  83. AdjustControls();
  84. }
  85. /// <summary>
  86. /// Make some adjustement to controls, like disabling some controls
  87. /// </summary>
  88. public void AdjustControls()
  89. {
  90. // Change state of system, and grey every controls not needed
  91. ChangeState(SystemState.NotConnected);
  92. // Load "no picture" image from disque
  93. drawingareaCameraPixbuf = Pixbuf.LoadFromResource("monitor.ressources.missing_picture.png");
  94. // setup server controls
  95. entryServerName.Text = Client.defaultIP;
  96. entryServerPort.Text = Client.defaultPort.ToString();
  97. entryTimeout.Text = "1000";
  98. }
  99. /// <summary>
  100. /// Method used to change controls visibility (greyed or not) depending on current state
  101. /// </summary>
  102. /// <param name="newState">New state</param>
  103. private void ChangeState(SystemState newState)
  104. {
  105. switch (newState)
  106. {
  107. case SystemState.NotConnected:
  108. labelRobot.Sensitive = false;
  109. gtkAlignmentRobot.Sensitive = false;
  110. labelRobotControl.Sensitive = false;
  111. gtkAlignmentRobotControl.Sensitive = false;
  112. boxCamera.Sensitive = false;
  113. buttonServerConnection.Label = "Connect";
  114. buttonRobotActivation.Label = "Activate";
  115. labelBatteryLevel.Text = "Unknown";
  116. checkButtonCameraOn.Active = false;
  117. checkButtonRobotPosition.Active = false;
  118. checkButtonFPS.Active = false;
  119. if (cmdManager != null) cmdManager.Close();
  120. batteryTimer.Stop();
  121. break;
  122. case SystemState.ServerConnected:
  123. buttonServerConnection.Label = "Disconnect";
  124. buttonRobotActivation.Label = "Activate";
  125. labelBatteryLevel.Text = "Unknown";
  126. labelRobot.Sensitive = true;
  127. gtkAlignmentRobot.Sensitive = true;
  128. boxCamera.Sensitive = true;
  129. labelRobotControl.Sensitive = false;
  130. gtkAlignmentRobotControl.Sensitive = false;
  131. batteryTimer.Stop();
  132. break;
  133. case SystemState.RobotConnected:
  134. buttonRobotActivation.Label = "Reset";
  135. labelRobotControl.Sensitive = true;
  136. gtkAlignmentRobotControl.Sensitive = true;
  137. batteryTimer.Start();
  138. break;
  139. default:
  140. labelRobot.Sensitive = false;
  141. gtkAlignmentRobot.Sensitive = false;
  142. labelRobotControl.Sensitive = false;
  143. gtkAlignmentRobotControl.Sensitive = false;
  144. boxCamera.Sensitive = false;
  145. buttonServerConnection.Label = "Connect";
  146. buttonRobotActivation.Label = "Activate";
  147. labelBatteryLevel.Text = "Unknown";
  148. checkButtonCameraOn.Active = false;
  149. checkButtonRobotPosition.Active = false;
  150. checkButtonFPS.Active = false;
  151. systemState = SystemState.NotConnected;
  152. return;
  153. }
  154. systemState = newState;
  155. }
  156. /// <summary>
  157. /// Display a popup message window
  158. /// </summary>
  159. /// <param name="type">Type of popup window (question, error, information,...)</param>
  160. /// <param name="buttons">Buttons available on popup window</param>
  161. /// <param name="title">Title of window</param>
  162. /// <param name="message">Message</param>
  163. private void MessagePopup(MessageType type, ButtonsType buttons, string title, string message)
  164. {
  165. MessageDialog md = new MessageDialog(this, DialogFlags.DestroyWithParent, type, buttons, message);
  166. md.Title = title;
  167. md.Run();
  168. md.Destroy();
  169. }
  170. /// <summary>
  171. /// Callback called when delete event is sent by window
  172. /// </summary>
  173. /// <param name="sender">Sender object</param>
  174. /// <param name="a">Not really sure of what it is...</param>
  175. protected void OnDeleteEvent(object sender, DeleteEventArgs a)
  176. {
  177. Console.WriteLine("Bye bye");
  178. if (cmdManager != null) cmdManager.Close();
  179. Application.Quit();
  180. a.RetVal = true;
  181. }
  182. /// <summary>
  183. /// Callback called when new message is received from server
  184. /// </summary>
  185. /// <param name="header">Header of message</param>
  186. /// <param name="data">Data of message</param>
  187. public void OnCommandReceivedEvent(string header, string data)
  188. {
  189. if (header == null)
  190. {
  191. // we have lost server
  192. ChangeState(SystemState.NotConnected);
  193. Gtk.Application.Invoke(delegate
  194. {
  195. MessagePopup(MessageType.Error,
  196. ButtonsType.Ok, "Server lost",
  197. "Server is down: disconnecting");
  198. cmdManager.Close();
  199. });
  200. }
  201. // if we have received a valid message
  202. if (header != null)
  203. {
  204. #if DEBUG
  205. // print message content
  206. if (header.Length > 4)
  207. {
  208. Console.WriteLine("Bad header(" + header.Length + ")");
  209. }
  210. #endif
  211. // Depending on message received (based on header), launch correponding action
  212. header = header.ToUpper();
  213. if (header == DestijlCommandList.ROBOT_BATTERY_LEVEL)
  214. {
  215. string batLevel = "";
  216. switch (data[0])
  217. {
  218. case '2':
  219. batLevel = "High";
  220. break;
  221. case '1':
  222. batLevel = "Low";
  223. break;
  224. case '0':
  225. batLevel = "Empty";
  226. break;
  227. default:
  228. batLevel = "Invalid value";
  229. break;
  230. }
  231. Gtk.Application.Invoke(delegate
  232. {
  233. labelBatteryLevel.Text = batLevel;
  234. });
  235. }
  236. else if (header == DestijlCommandList.CAMERA_IMAGE)
  237. {
  238. imageReceivedCounter++;
  239. byte[] image = new byte[2];
  240. try
  241. {
  242. image = Convert.FromBase64String(data);
  243. }
  244. catch (FormatException)
  245. {
  246. badImageReceivedCounter++;
  247. Console.WriteLine("Unable to convert from base64 ");
  248. }
  249. try
  250. {
  251. drawingareaCameraPixbuf = new Pixbuf(image);
  252. imageFPScounter++;
  253. Gtk.Application.Invoke(delegate
  254. {
  255. drawingAreaCamera.QueueDraw();
  256. });
  257. }
  258. catch (GLib.GException)
  259. {
  260. badImageReceivedCounter++;
  261. #if DEBUG
  262. Console.WriteLine("Bad Image: " + badImageReceivedCounter +
  263. " / " + imageReceivedCounter +
  264. " (" + badImageReceivedCounter * 100 / imageReceivedCounter + "%)");
  265. #endif
  266. }
  267. //}
  268. }
  269. else if (header == DestijlCommandList.CAMERA_POSITION)
  270. {
  271. position = DestijlCommandManager.DecodePosition(data);
  272. Gtk.Application.Invoke(delegate
  273. {
  274. drawingAreaCamera.QueueDraw();
  275. });
  276. }
  277. }
  278. }
  279. /// <summary>
  280. /// Callback called by "quit" menu
  281. /// </summary>
  282. /// <param name="sender">Sender object</param>
  283. /// <param name="e">Event</param>
  284. protected void OnQuitActionActivated(object sender, EventArgs e)
  285. {
  286. Console.WriteLine("Bye bye 2");
  287. if (cmdManager != null) cmdManager.Close();
  288. this.Destroy();
  289. Application.Quit();
  290. }
  291. /// <summary>
  292. /// Callback called by "show log" menu
  293. /// </summary>
  294. /// <param name="sender">Sender object</param>
  295. /// <param name="e">Event</param>
  296. protected void OnShowLogWindowActionActivated(object sender, EventArgs e)
  297. {
  298. MessagePopup(MessageType.Info,
  299. ButtonsType.Ok, "Info",
  300. "Logger not yet implemented");
  301. }
  302. /// <summary>
  303. /// Callback called by "buttonServerConnection" button
  304. /// </summary>
  305. /// <param name="sender">Sender object</param>
  306. /// <param name="e">Event</param>
  307. protected void OnButtonServerConnectionClicked(object sender, EventArgs e)
  308. {
  309. DestijlCommandManager.CommandStatus statusCmd;
  310. // if we are currently connected
  311. if (buttonServerConnection.Label == "Disconnect")
  312. {
  313. // Change state to disconnect and close connection
  314. ChangeState(SystemState.NotConnected);
  315. }
  316. else // we are not currently connected to server
  317. {
  318. // if information about hostname or port are invalid, show a popup error
  319. if ((entryServerName.Text == "") || (entryServerPort.Text == ""))
  320. {
  321. MessagePopup(MessageType.Error,
  322. ButtonsType.Ok, "Error",
  323. "Server name or port is invalid");
  324. }
  325. else
  326. {
  327. Console.WriteLine("Connecting to " + entryServerName.Text + ":" + entryServerPort.Text);
  328. bool status = false;
  329. // try to convert timout string value to double. If that failed, default to 100 ms
  330. try
  331. {
  332. cmdManager.timeout = Convert.ToDouble(entryTimeout.Text);
  333. }
  334. catch (Exception)
  335. {
  336. cmdManager.timeout = 100;
  337. entryTimeout.Text = cmdManager.timeout.ToString();
  338. }
  339. // try to connect to givn server.
  340. try
  341. {
  342. status = cmdManager.Open(entryServerName.Text, Convert.ToInt32(entryServerPort.Text));
  343. }
  344. catch (Exception)
  345. {
  346. Console.WriteLine("Something went wrong during connection");
  347. return;
  348. }
  349. //if connection status is not ok, show an error popup
  350. if (status != true)
  351. {
  352. MessagePopup(MessageType.Error,
  353. ButtonsType.Ok, "Error",
  354. "Unable to connect to server " + entryServerName.Text + ":" + Convert.ToInt32(entryServerPort.Text));
  355. }
  356. else // if we succed in connecting, open communication with robot
  357. {
  358. Console.Write("Send command RobotOpenCom: ");
  359. statusCmd = cmdManager.RobotOpenCom();
  360. Console.WriteLine(statusCmd.ToString());
  361. if (statusCmd == DestijlCommandManager.CommandStatus.Success)
  362. {
  363. ChangeState(SystemState.ServerConnected);
  364. }
  365. else // if communication with robot is not possible, show error
  366. {
  367. MessagePopup(MessageType.Error,
  368. ButtonsType.Ok, "Error",
  369. "Unable to open communication with robot.\nCheck that supervisor is accepting OPEN_COM_DMB command");
  370. cmdManager.Close();
  371. }
  372. }
  373. }
  374. }
  375. }
  376. /// <summary>
  377. /// Callback called when "buttonRobotactivation" is clicked
  378. /// </summary>
  379. /// <param name="sender">Sender object</param>
  380. /// <param name="e">Event</param>
  381. protected void OnButtonRobotActivationClicked(object sender, EventArgs e)
  382. {
  383. DestijlCommandManager.CommandStatus status;
  384. //if robot is not activated
  385. if (buttonRobotActivation.Label == "Activate")
  386. {
  387. // if a startup with watchdog is requested
  388. if (radioButtonWithWatchdog.Active)
  389. {
  390. status = cmdManager.RobotStartWithWatchdog();
  391. }
  392. else // startup without watchdog
  393. {
  394. status = cmdManager.RobotStartWithoutWatchdog();
  395. }
  396. // if status of command is ok, change state of system, enabling robot control
  397. if (status == DestijlCommandManager.CommandStatus.Success)
  398. {
  399. ChangeState(SystemState.RobotConnected);
  400. }
  401. else // if status is not ok, depending of error, show appropriate error
  402. {
  403. if (status == DestijlCommandManager.CommandStatus.CommunicationLostWithServer)
  404. {
  405. MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Connection lost with server");
  406. ChangeState(SystemState.NotConnected);
  407. }
  408. else
  409. {
  410. MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Command rejected\nCheck that supervisor accept \nDMB_START_WITH_WD and/or DMB_START_WITHOUT_WD");
  411. }
  412. }
  413. }
  414. else // If robot is already activated, request reset of robot
  415. {
  416. status = cmdManager.RobotReset();
  417. // if status of command is ok, change state of system, disabling robot control
  418. if (status == DestijlCommandManager.CommandStatus.Success)
  419. {
  420. ChangeState(SystemState.ServerConnected);
  421. }
  422. else // if status is not ok, depending of error, show appropriate error
  423. {
  424. if (status == DestijlCommandManager.CommandStatus.CommunicationLostWithServer)
  425. {
  426. MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Connection lost with server");
  427. ChangeState(SystemState.NotConnected);
  428. }
  429. else
  430. {
  431. MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Unknown error");
  432. }
  433. }
  434. }
  435. }
  436. /// <summary>
  437. /// Callback called when user click on direction button
  438. /// </summary>
  439. /// <param name="sender">Sender button</param>
  440. /// <param name="e">Event</param>
  441. protected void OnButtonMouvClicked(object sender, EventArgs e)
  442. {
  443. // depending on button clicked, launch appropriate action
  444. if (sender == buttonRight)
  445. {
  446. cmdManager.RobotTurn(90);
  447. }
  448. else if (sender == buttonLeft)
  449. {
  450. cmdManager.RobotTurn(-90);
  451. }
  452. else if (sender == buttonForward)
  453. {
  454. cmdManager.RobotMove(100);
  455. }
  456. else if (sender == buttonDown)
  457. {
  458. cmdManager.RobotMove(-100);
  459. }
  460. else
  461. {
  462. MessagePopup(MessageType.Warning, ButtonsType.Ok, "Abnormal behavior", "Callback OnButtonMouvClicked called by unknown sender");
  463. }
  464. }
  465. /// <summary>
  466. /// Callback called when battery timer expired
  467. /// </summary>
  468. /// <param name="sender">Sender object</param>
  469. /// <param name="e">Event</param>
  470. void OnBatteryTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
  471. {
  472. DestijlCommandManager.CommandStatus status;
  473. batteryTimer.Stop();
  474. // if battery checkbox is checked, a request for battery level is done
  475. if (checkButtonGetBattery.Active)
  476. {
  477. status = cmdManager.RobotGetBattery();
  478. // if status is not ok, show appropriate message and print "Unknown" for battery level
  479. switch (status)
  480. {
  481. case DestijlCommandManager.CommandStatus.Success:
  482. batteryTimer.Start();
  483. break;
  484. case DestijlCommandManager.CommandStatus.CommunicationLostWithServer:
  485. Console.WriteLine("Error: Connection lost with server");
  486. batteryTimer.Stop();
  487. labelBatteryLevel.Text = "Unknown";
  488. ChangeState(SystemState.NotConnected);
  489. break;
  490. case DestijlCommandManager.CommandStatus.CommunicationLostWithRobot:
  491. Console.WriteLine("Error: Connection lost with robot");
  492. batteryTimer.Stop();
  493. labelBatteryLevel.Text = "Unknown";
  494. ChangeState(SystemState.ServerConnected);
  495. break;
  496. default:
  497. labelBatteryLevel.Text = "Unknown";
  498. batteryTimer.Start();
  499. break;
  500. }
  501. }
  502. else batteryTimer.Start();
  503. }
  504. /// <summary>
  505. /// Callback called when checkbutton for camera is clicked
  506. /// </summary>
  507. /// <param name="sender">Sender object</param>
  508. /// <param name="e">Event</param>
  509. protected void OnCheckButtonCameraOnClicked(object sender, EventArgs e)
  510. {
  511. // if camera is already active, switch it off
  512. if (!checkButtonCameraOn.Active)
  513. {
  514. if (cmdManager.CameraClose() != DestijlCommandManager.CommandStatus.Success)
  515. {
  516. Console.WriteLine("Error when closing camera: bad answer for supervisor or timeout");
  517. }
  518. }
  519. else // camera is not active, switch it on
  520. {
  521. badImageReceivedCounter = 0;
  522. imageReceivedCounter = 0;
  523. if (cmdManager.CameraOpen() != DestijlCommandManager.CommandStatus.Success)
  524. {
  525. Console.WriteLine("Error when opening camera: bad answer for supervisor or timeout");
  526. //checkButtonCameraOn.Active = false;
  527. }
  528. }
  529. }
  530. /// <summary>
  531. /// Callback called when checkbutton robot position is clicked
  532. /// </summary>
  533. /// <param name="sender">Sender object</param>
  534. /// <param name="e">Event</param>
  535. protected void OnCheckButtonRobotPositionClicked(object sender, EventArgs e)
  536. {
  537. // if server already send robot position, stop it
  538. if (!checkButtonRobotPosition.Active)
  539. {
  540. if (cmdManager.CameraStopComputePosition() != DestijlCommandManager.CommandStatus.Success)
  541. {
  542. Console.WriteLine("Error when stopping position reception: bad answer for supervisor or timeout");
  543. }
  544. }
  545. else // start reception of robot position
  546. {
  547. if (cmdManager.CameraComputePosition() != DestijlCommandManager.CommandStatus.Success)
  548. {
  549. Console.WriteLine("Error when starting getting robot position: bad answer for supervisor or timeout");
  550. //checkButtonRobotPosition.Active = false;
  551. }
  552. }
  553. }
  554. /// <summary>
  555. /// Callback called when drawingarea need refresh
  556. /// </summary>
  557. /// <param name="o">Sender object</param>
  558. /// <param name="args">Expose arguments</param>
  559. protected void OnDrawingAreaCameraExposeEvent(object o, ExposeEventArgs args)
  560. {
  561. //Console.WriteLine("Event expose. Args = " + args.ToString());
  562. DrawingArea area = (DrawingArea)o;
  563. Gdk.Pixbuf displayPixbuf;
  564. int areaWidth, areaHeight;
  565. // Get graphic context for background
  566. Gdk.GC gc = area.Style.BackgroundGC(Gtk.StateType.Normal);
  567. // get size of drawingarea widget
  568. area.GdkWindow.GetSize(out areaWidth, out areaHeight);
  569. int width = drawingareaCameraPixbuf.Width;
  570. int height = drawingareaCameraPixbuf.Height;
  571. float ratio = (float)width / (float)height;
  572. // if widget is smaller than image, reduce it
  573. if (areaWidth <= width)
  574. {
  575. width = areaWidth;
  576. height = (int)(width / ratio);
  577. }
  578. // if image is smaller than widget, enlarge it
  579. if (width > areaWidth)
  580. {
  581. width = areaWidth;
  582. }
  583. if (height > areaHeight)
  584. {
  585. height = areaHeight;
  586. }
  587. //scale original picture and copy result in local pixbuf
  588. displayPixbuf = drawingareaCameraPixbuf.ScaleSimple(width, height, InterpType.Bilinear);
  589. // draw local pixbuff centered on drawingarea
  590. area.GdkWindow.DrawPixbuf(gc, displayPixbuf,
  591. 0, 0,
  592. (areaWidth - displayPixbuf.Width) / 2,
  593. (areaHeight - displayPixbuf.Height) / 2,
  594. displayPixbuf.Width, displayPixbuf.Height,
  595. RgbDither.Normal, 0, 0);
  596. if (checkButtonRobotPosition.Active) {
  597. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  598. Cairo.Color textFontColor = new Cairo.Color(0.8, 0, 0);
  599. cr.SelectFontFace("Cantarell", FontSlant.Normal, FontWeight.Bold);
  600. cr.SetSourceColor(textFontColor);
  601. cr.SetFontSize(16);
  602. double space = 0.0;
  603. string text = "Direction (" + position.direction.x.ToString("0.##") + " ; " + position.direction.y.ToString("0.##") +")";
  604. TextExtents te = cr.TextExtents(text);
  605. cr.MoveTo(areaWidth - te.Width-5,
  606. areaHeight - te.Height -5);
  607. space = te.Height;
  608. cr.ShowText(text);
  609. text = "Centre (" + position.centre.x.ToString("0.##") + " ; " + position.centre.y.ToString("0.##") + ")";
  610. te = cr.TextExtents(text);
  611. cr.MoveTo(areaWidth - te.Width - 5,
  612. areaHeight - te.Height - 5 - space-5);
  613. space = space+ te.Height+5;
  614. cr.ShowText(text);
  615. text = "Angle: " + position.angle.ToString("0.##");
  616. te = cr.TextExtents(text);
  617. cr.MoveTo(areaWidth - te.Width - 5,
  618. areaHeight - te.Height - 5 - space - 5);
  619. space = space+ te.Height+5;
  620. cr.ShowText(text);
  621. text = "ID: " + position.robotID;
  622. te = cr.TextExtents(text);
  623. cr.MoveTo(areaWidth - te.Width - 5,
  624. areaHeight - te.Height - 5 - space-5);
  625. cr.ShowText(text);
  626. ((IDisposable)cr.GetTarget()).Dispose();
  627. ((IDisposable)cr).Dispose();
  628. }
  629. if (checkButtonFPS.Active) {
  630. Cairo.Context cr = Gdk.CairoHelper.Create(area.GdkWindow);
  631. Cairo.Color textFontColor = new Cairo.Color(0.8, 0, 0);
  632. cr.SelectFontFace("Cantarell", FontSlant.Normal, FontWeight.Bold);
  633. cr.SetSourceColor(textFontColor);
  634. cr.SetFontSize(16);
  635. string text = "FPS= " + imageFPS.ToString() ;
  636. TextExtents te = cr.TextExtents(text);
  637. cr.MoveTo( 10,10 + te.Height);
  638. cr.ShowText(text);
  639. ((IDisposable)cr.GetTarget()).Dispose();
  640. ((IDisposable)cr).Dispose();
  641. }
  642. }
  643. /// <summary>
  644. /// Show a popup asking user to tell if arena is correct or not
  645. /// </summary>
  646. protected void DetectArena()
  647. {
  648. DestijlCommandManager.CommandStatus status;
  649. MessageDialog md = new MessageDialog(this, DialogFlags.DestroyWithParent,
  650. MessageType.Question, ButtonsType.YesNo, "Arena is correct ?");
  651. md.Title = "Check arena";
  652. ResponseType result = (ResponseType)md.Run();
  653. md.Destroy();
  654. if (result == ResponseType.Yes)
  655. {
  656. status = cmdManager.CameraArenaConfirm();
  657. }
  658. else
  659. {
  660. status = cmdManager.CameraArenaInfirm();
  661. }
  662. if (status != DestijlCommandManager.CommandStatus.Success)
  663. {
  664. MessagePopup(MessageType.Error,
  665. ButtonsType.Ok, "Error",
  666. "Unable to send Confirm or Infirm arena command to supervisor");
  667. }
  668. }
  669. /// <summary>
  670. /// Callback called when "detect Arena " button is clicked
  671. /// </summary>
  672. /// <param name="sender">Sender object</param>
  673. /// <param name="e">Event</param>
  674. protected void OnButtonAskArenaClicked(object sender, EventArgs e)
  675. {
  676. // Send command to server for arean rendering
  677. if (cmdManager.CameraAskArena() != DestijlCommandManager.CommandStatus.Success)
  678. {
  679. MessagePopup(MessageType.Error,
  680. ButtonsType.Ok, "Error",
  681. "Error when asking for arena rendering");
  682. return;
  683. }
  684. // show popup and wait for user to say if arena is ok or not
  685. DetectArena();
  686. }
  687. protected void OnCheckButtonFPSToggled(object sender, EventArgs e)
  688. {
  689. if (checkButtonFPS.Active) { // User ask for FPS
  690. imageFPS = 0;
  691. imageFPScounter = 0;
  692. fpsTimer.Start();
  693. }
  694. else { // stop computing FPS
  695. fpsTimer.Stop();
  696. Gtk.Application.Invoke(delegate
  697. {
  698. drawingAreaCamera.QueueDraw();
  699. });
  700. }
  701. }
  702. private void OnFpsTimerElapsed(object sender, ElapsedEventArgs e)
  703. {
  704. imageFPS = imageFPScounter;
  705. imageFPScounter = 0;
  706. Gtk.Application.Invoke(delegate
  707. {
  708. drawingAreaCamera.QueueDraw();
  709. });
  710. }
  711. }