Monitor
MonitorUI.cs
Go to the documentation of this file.
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 
22 
23 using System;
24 using Gtk;
25 using Gdk;
26 
27 using monitor;
28 
32 public partial class MainWindow : Gtk.Window
33 {
38 
42  private Pixbuf drawingareaCameraPixbuf;
43 
48  {
49  NotConnected,
50  ServerConnected,
51  RobotConnected
52  };
53 
57  private SystemState systemState = SystemState.NotConnected;
58 
62  private System.Timers.Timer batteryTimer;
63 
67  public MainWindow() : base(Gtk.WindowType.Toplevel)
68  {
69  Build();
70 
72 
73  // create new timer for battery request, every 10s
74  batteryTimer = new System.Timers.Timer(10000.0);
76 
77  // Customize controls
79  }
80 
84  public void AdjustControls()
85  {
86  // Change state of system, and grey every controls not needed
87  ChangeState(SystemState.NotConnected);
88 
89  //drawingareaCameraPixbuf = new Pixbuf((string)null);
90  // Load "no picture" image from disque
91  drawingareaCameraPixbuf = Pixbuf.LoadFromResource("monitor.ressources.missing_picture.png");
92 
93  // setup server controls
94  entryServerName.Text = Client.defaultIP;
95  entryServerPort.Text = Client.defaultPort.ToString();
96  entryTimeout.Text = "100";
97  }
98 
103  private void ChangeState(SystemState newState)
104  {
105  switch (newState)
106  {
107  case SystemState.NotConnected:
108  labelRobot.Sensitive = false;
109  gtkAlignmentRobot.Sensitive = false;
110 
111  labelRobotControl.Sensitive = false;
112  gtkAlignmentRobotControl.Sensitive = false;
113  boxCamera.Sensitive = false;
114 
115  buttonServerConnection.Label = "Connect";
116  buttonRobotActivation.Label = "Activate";
117  labelBatteryLevel.Text = "Unknown";
118 
119  checkButtonCameraOn.Active = false;
120  checkButtonRobotPosition.Active = false;
121  if (cmdManager != null) cmdManager.Close();
122 
123  batteryTimer.Stop();
124  break;
125  case SystemState.ServerConnected:
126  buttonServerConnection.Label = "Disconnect";
127  buttonRobotActivation.Label = "Activate";
128  labelBatteryLevel.Text = "Unknown";
129 
130  labelRobot.Sensitive = true;
131  gtkAlignmentRobot.Sensitive = true;
132  boxCamera.Sensitive = true;
133 
134  labelRobotControl.Sensitive = false;
135  gtkAlignmentRobotControl.Sensitive = false;
136 
137  batteryTimer.Stop();
138  break;
139  case SystemState.RobotConnected:
140  buttonRobotActivation.Label = "Reset";
141  labelRobotControl.Sensitive = true;
142  gtkAlignmentRobotControl.Sensitive = true;
143 
144  batteryTimer.Start();
145  break;
146  default:
147  labelRobot.Sensitive = false;
148  gtkAlignmentRobot.Sensitive = false;
149 
150  labelRobotControl.Sensitive = false;
151  gtkAlignmentRobotControl.Sensitive = false;
152  boxCamera.Sensitive = false;
153 
154  buttonServerConnection.Label = "Connect";
155  buttonRobotActivation.Label = "Activate";
156  labelBatteryLevel.Text = "Unknown";
157 
158  checkButtonCameraOn.Active = false;
159  checkButtonRobotPosition.Active = false;
160 
161  systemState = SystemState.NotConnected;
162 
163  return;
164  }
165 
166  systemState = newState;
167  }
168 
176  private void MessagePopup(MessageType type, ButtonsType buttons, string title, string message)
177  {
178  MessageDialog md = new MessageDialog(this, DialogFlags.DestroyWithParent, type, buttons, message)
179  {
180  Title = title
181  };
182 
183  md.Run();
184  md.Destroy();
185  }
186 
192  protected void OnDeleteEvent(object sender, DeleteEventArgs a)
193  {
194  Console.WriteLine("Bye bye");
195 
196  if (cmdManager != null) cmdManager.Close();
197  Application.Quit();
198  a.RetVal = true;
199  }
200 
207  public void OnCommandReceivedEvent(string header, string data, byte[] buffer)
208  {
209  // if we have received a valid message
210  if (header != null)
211  {
212  // print message content
213  Console.WriteLine("Received header (" + header.Length + "): " + header);
214  if (header.ToUpper() != DestijlCommandList.HeaderStmImage)
215  {
216  if (data != null) Console.WriteLine("Received data (" + data.Length + "): " + data);
217  }
218 
219  // depending on message received (based on header)
220  // launch correponding action
221  if (header.ToUpper() == DestijlCommandList.HeaderStmBat)
222  {
223  switch (data[0])
224  {
225  case '2':
226  labelBatteryLevel.Text = "High";
227  break;
228  case '1':
229  labelBatteryLevel.Text = "Low";
230  break;
231  case '0':
232  labelBatteryLevel.Text = "Empty";
233  break;
234  default:
235  labelBatteryLevel.Text = "Invalid value";
236  break;
237  }
238  }
239  else if (header.ToUpper() == DestijlCommandList.HeaderStmImage)
240  {
241  // if message is an image, convert it to a pixbuf
242  // that can be displayed
243  byte[] image = new byte[buffer.Length - 4];
244  System.Buffer.BlockCopy(buffer, 4, image, 0, image.Length);
245 
246  drawingareaCameraPixbuf = new Pixbuf(image);
247  drawingAreaCamera.QueueDraw();
248  }
249  }
250  }
251 
257  protected void OnQuitActionActivated(object sender, EventArgs e)
258  {
259  Console.WriteLine("Bye bye 2");
260  if (cmdManager != null) cmdManager.Close();
261  this.Destroy();
262  Application.Quit();
263  }
264 
270  protected void OnShowLogWindowActionActivated(object sender, EventArgs e)
271  {
272  MessagePopup(MessageType.Info,
273  ButtonsType.Ok, "Info",
274  "Logger not yet implemented");
275  }
276 
282  protected void OnButtonServerConnectionClicked(object sender, EventArgs e)
283  {
285 
286  // if we are currently connected
287  if (buttonServerConnection.Label == "Disconnect")
288  {
289  // Change state to disconnect and close connection
290  ChangeState(SystemState.NotConnected);
291  }
292  else // we are not currently connected to server
293  {
294  // if information about hostname or port are invalid, show a popup error
295  if ((entryServerName.Text == "") || (entryServerPort.Text == ""))
296  {
297  MessagePopup(MessageType.Error,
298  ButtonsType.Ok, "Error",
299  "Server name or port is invalid");
300  }
301  else
302  {
303  Console.WriteLine("Connecting to " + entryServerName.Text + ":" + entryServerPort.Text);
304  bool status = false;
305 
306  // try to convert timout string value to double. If that failed, default to 100 ms
307  try
308  {
309  cmdManager.timeout = Convert.ToDouble(entryTimeout.Text);
310  }
311  catch (Exception)
312  {
313  cmdManager.timeout = 100;
314  entryTimeout.Text = cmdManager.timeout.ToString();
315  }
316 
317  // try to connect to givn server.
318  try
319  {
320  status = cmdManager.Open(entryServerName.Text, Convert.ToInt32(entryServerPort.Text));
321  }
322  catch (Exception)
323  {
324  Console.WriteLine("Something went wrong during connection");
325  return;
326  }
327 
328  //if connection status is not ok, show an error popup
329  if (status != true)
330  {
331  MessagePopup(MessageType.Error,
332  ButtonsType.Ok, "Error",
333  "Unable to connect to server " + entryServerName.Text + ":" + Convert.ToInt32(entryServerPort.Text));
334  }
335  else // if we succed in connecting, open communication with robot
336  {
337  Console.Write("Send command RobotOpenCom: ");
338  statusCmd = cmdManager.RobotOpenCom();
339  Console.WriteLine(statusCmd.ToString());
340 
341  if (statusCmd == DestijlCommandManager.CommandStatus.Success)
342  {
343  ChangeState(SystemState.ServerConnected);
344  }
345  else // if communication with robot is not possible, show error
346  {
347  MessagePopup(MessageType.Error,
348  ButtonsType.Ok, "Error",
349  "Unable to open communication with robot.\nCheck that supervisor is accepting OPEN_COM_DMB command");
350 
351  cmdManager.Close();
352  }
353  }
354  }
355  }
356  }
357 
363  protected void OnButtonRobotActivationClicked(object sender, EventArgs e)
364  {
366 
367  //if robot is not activated
368  if (buttonRobotActivation.Label == "Activate")
369  {
370  // if a startup with watchdog is requested
371  if (radioButtonWithWatchdog.Active)
372  {
373  status = cmdManager.RobotStartWithWatchdog();
374  }
375  else // startup without watchdog
376  {
377  status = cmdManager.RobotStartWithoutWatchdog();
378  }
379 
380  // if status of command is ok, change state of system, enabling robot control
381  if (status == DestijlCommandManager.CommandStatus.Success)
382  {
383  ChangeState(SystemState.RobotConnected);
384  }
385  else // if status is not ok, depending of error, show appropriate error
386  {
387  if (status == DestijlCommandManager.CommandStatus.CommunicationLostWithServer)
388  {
389  MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Connection lost with server");
390  ChangeState(SystemState.NotConnected);
391  }
392  else
393  {
394  MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Command rejected\nCheck that supervisor accept \nDMB_START_WITH_WD and/or DMB_START_WITHOUT_WD");
395  }
396  }
397  }
398  else // If robot is already activated, request reset of robot
399  {
400  status = cmdManager.RobotReset();
401 
402  // if status of command is ok, change state of system, disabling robot control
403  if (status == DestijlCommandManager.CommandStatus.Success)
404  {
405  ChangeState(SystemState.ServerConnected);
406  }
407  else // if status is not ok, depending of error, show appropriate error
408  {
409  if (status == DestijlCommandManager.CommandStatus.CommunicationLostWithServer)
410  {
411  MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Connection lost with server");
412  ChangeState(SystemState.NotConnected);
413  }
414  else
415  {
416  MessagePopup(MessageType.Error, ButtonsType.Ok, "Error", "Unknown error");
417  }
418  }
419  }
420  }
421 
427  protected void OnButtonMouvClicked(object sender, EventArgs e)
428  {
429  // depending on button clicked, launch appropriate action
430  if (sender == buttonRight)
431  {
432  cmdManager.RobotTurn(90);
433  }
434  else if (sender == buttonLeft)
435  {
436  cmdManager.RobotTurn(-90);
437  }
438  else if (sender == buttonForward)
439  {
440  cmdManager.RobotMove(100);
441  }
442  else if (sender == buttonDown)
443  {
444  cmdManager.RobotMove(-100);
445  }
446  else
447  {
448  MessagePopup(MessageType.Warning, ButtonsType.Ok, "Abnormal behavior", "Callback OnButtonMouvClicked called by unknown sender");
449  }
450  }
451 
457  void OnBatteryTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
458  {
460  batteryTimer.Stop();
461 
462  // if battery checkbox is checked, a request for battery level is done
463  if (checkButtonGetBattery.Active)
464  {
465  status = cmdManager.RobotGetBattery();
466 
467  // if status is not ok, show appropriate message and print "Unknown" for battery level
468  switch (status)
469  {
471  batteryTimer.Start();
472  break;
473  case DestijlCommandManager.CommandStatus.CommunicationLostWithServer:
474  Console.WriteLine("Error: Connection lost with server");
475  batteryTimer.Stop();
476  labelBatteryLevel.Text = "Unknown";
477 
478  ChangeState(SystemState.NotConnected);
479  break;
480  case DestijlCommandManager.CommandStatus.CommunicationLostWithRobot:
481  Console.WriteLine("Error: Connection lost with robot");
482  batteryTimer.Stop();
483  labelBatteryLevel.Text = "Unknown";
484 
485  ChangeState(SystemState.ServerConnected);
486  break;
487  default:
488  labelBatteryLevel.Text = "Unknown";
489  batteryTimer.Start();
490  break;
491  }
492  }
493  else batteryTimer.Start();
494  }
495 
501  protected void OnCheckButtonCameraOnClicked(object sender, EventArgs e)
502  {
503  // if camera is already active, switch it off
504  if (!checkButtonCameraOn.Active)
505  {
506  if (cmdManager.CameraClose() != DestijlCommandManager.CommandStatus.Success)
507  {
508  MessagePopup(MessageType.Error,
509  ButtonsType.Ok, "Error",
510  "Error when closing camera: bad answer for supervisor or timeout");
511  }
512  }
513  else // camera is not active, switch it on
514  {
515  if (cmdManager.CameraOpen() != DestijlCommandManager.CommandStatus.Success)
516  {
517  MessagePopup(MessageType.Error,
518  ButtonsType.Ok, "Error",
519  "Error when opening camera: bad answer for supervisor or timeout");
520  checkButtonCameraOn.Active = false;
521  }
522  }
523  }
524 
530  protected void OnCheckButtonRobotPositionClicked(object sender, EventArgs e)
531  {
532  // if server already send robot position, stop it
533  if (!checkButtonRobotPosition.Active)
534  {
536  {
537  MessagePopup(MessageType.Error,
538  ButtonsType.Ok, "Error",
539  "Error when stopping position reception: bad answer for supervisor or timeout");
540  }
541  }
542  else // start reception of robot position
543  {
544  if (cmdManager.CameraComputePosition() != DestijlCommandManager.CommandStatus.Success)
545  {
546  MessagePopup(MessageType.Error,
547  ButtonsType.Ok, "Error",
548  "Error when starting getting robot position: bad answer for supervisor or timeout");
549 
550  checkButtonRobotPosition.Active = false;
551  }
552  }
553  }
554 
560  protected void OnDrawingAreaCameraExposeEvent(object o, ExposeEventArgs args)
561  {
562  //Console.WriteLine("Event expose. Args = " + args.ToString());
563 
564  DrawingArea area = (DrawingArea)o;
565  Gdk.Pixbuf displayPixbuf;
566  int areaWidth, areaHeight;
567 
568  // Get graphic context for background
569  Gdk.GC gc = area.Style.BackgroundGC(Gtk.StateType.Normal);
570 
571  // get size of drawingarea widget
572  area.GdkWindow.GetSize(out areaWidth, out areaHeight);
573  int width = drawingareaCameraPixbuf.Width;
574  int height = drawingareaCameraPixbuf.Height;
575  float ratio = (float)width / (float)height;
576 
577  // if widget is smaller than image, reduce it
578  if (areaWidth <= width)
579  {
580  width = areaWidth;
581  height = (int)(width / ratio);
582  }
583 
584  // if image is smaller than widget, enlarge it
585  if (width > areaWidth)
586  {
587  width = areaWidth;
588  }
589 
590  if (height > areaHeight)
591  {
592  height = areaHeight;
593  }
594 
595  //scale original picture and copy result in local pixbuf
596  displayPixbuf = drawingareaCameraPixbuf.ScaleSimple(width, height, InterpType.Bilinear);
597 
598  // draw local pixbuff centered on drawingarea
599  area.GdkWindow.DrawPixbuf(gc, displayPixbuf,
600  0, 0,
601  (areaWidth - displayPixbuf.Width) / 2,
602  (areaHeight - displayPixbuf.Height) / 2,
603  displayPixbuf.Width, displayPixbuf.Height,
604  RgbDither.Normal, 0, 0);
605  }
606 
610  protected void DetectArena()
611  {
613  MessageDialog md = new MessageDialog(this, DialogFlags.DestroyWithParent,
614  MessageType.Question, ButtonsType.YesNo, "Arena is correct ?");
615  {
616  Title = "Check arena";
617  };
618 
619  ResponseType result = (ResponseType)md.Run();
620  md.Destroy();
621 
622  if (result == ResponseType.Yes)
623  {
624  status = cmdManager.CameraArenaConfirm();
625  }
626  else
627  {
628  status = cmdManager.CameraArenaInfirm();
629  }
630 
631  if (status != DestijlCommandManager.CommandStatus.Success)
632  {
633  MessagePopup(MessageType.Error,
634  ButtonsType.Ok, "Error",
635  "Unable to send Confirm or Infirm arena command to supervisor");
636  }
637  }
638 
644  protected void OnButtonAskArenaClicked(object sender, EventArgs e)
645  {
646  // Send command to server for arean rendering
647  if (cmdManager.CameraAskArena() != DestijlCommandManager.CommandStatus.Success)
648  {
649  MessagePopup(MessageType.Error,
650  ButtonsType.Ok, "Error",
651  "Error when asking for arena rendering");
652  return;
653  }
654 
655  // show popup and wait for user to say if arena is ok or not
656  DetectArena();
657  }
658 }
CommandStatus RobotStartWithoutWatchdog()
Start robot, without enabling watchdog
const int defaultPort
Default server port number
Definition: Client.cs:41
void OnButtonServerConnectionClicked(object sender, EventArgs e)
Callback called by "buttonServerConnection" button
Definition: MonitorUI.cs:282
CommandStatus CameraArenaInfirm()
Reject arena detected (after requesting image of detected arena, using CameraAskArena ...
void Close()
Close connection to server
SystemState
List of availble state for the application
Definition: MonitorUI.cs:47
void OnButtonRobotActivationClicked(object sender, EventArgs e)
Callback called when "buttonRobotactivation" is clicked
Definition: MonitorUI.cs:363
DestijlCommandManager cmdManager
Destijl command manager reference
Definition: MonitorUI.cs:37
CommandStatus RobotGetBattery()
Request robot battery level
void OnCheckButtonCameraOnClicked(object sender, EventArgs e)
Callback called when checkbutton for camera is clicked
Definition: MonitorUI.cs:501
CommandStatus RobotStartWithWatchdog()
Start robot, enabling watchdog
void DetectArena()
Show a popup asking user to tell if arena is correct or not
Definition: MonitorUI.cs:610
Pixbuf drawingareaCameraPixbuf
Pixbuffer used for displaying image
Definition: MonitorUI.cs:42
void OnDeleteEvent(object sender, DeleteEventArgs a)
Callback called when delete event is sent by window
Definition: MonitorUI.cs:192
Specialization class for command manager, which implemnent destijl protocol between monitor and serve...
CommandStatus CameraComputePosition()
Request robot position computing
CommandStatus CameraStopComputePosition()
Stop robot position computing
const string defaultIP
Default server name
Definition: Client.cs:36
CommandStatus CameraArenaConfirm()
Confirm arena detection (after requesting image of detected arena, using CameraAskArena ...
CommandStatus
List of available return status
void OnBatteryTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
Callback called when battery timer expired
Definition: MonitorUI.cs:457
void OnButtonMouvClicked(object sender, EventArgs e)
Callback called when user click on direction button
Definition: MonitorUI.cs:427
void OnShowLogWindowActionActivated(object sender, EventArgs e)
Callback called by "show log" menu
Definition: MonitorUI.cs:270
void OnCheckButtonRobotPositionClicked(object sender, EventArgs e)
Callback called when checkbutton robot position is clicked
Definition: MonitorUI.cs:530
CommandStatus CameraAskArena()
Request still image of detected arena
CommandStatus RobotReset()
Reset robot and let it in idle mode
void ChangeState(SystemState newState)
Method used to change controls visibility (greyed or not) depending on current state ...
Definition: MonitorUI.cs:103
void MessagePopup(MessageType type, ButtonsType buttons, string title, string message)
Display a popup message window
Definition: MonitorUI.cs:176
void OnQuitActionActivated(object sender, EventArgs e)
Callback called by "quit" menu
Definition: MonitorUI.cs:257
MainWindow()
Initializes a new instance of the MainWindow class.
Definition: MonitorUI.cs:67
CommandStatus CameraClose()
Close camera on remote device
CommandStatus RobotTurn(int angle)
Make robot turn left or right, for a given angle
CommandStatus CameraOpen()
Open camera on remote device
void OnDrawingAreaCameraExposeEvent(object o, ExposeEventArgs args)
Callback called when drawingarea need refresh
Definition: MonitorUI.cs:560
void OnButtonAskArenaClicked(object sender, EventArgs e)
Callback called when "detect Arena " button is clicked
Definition: MonitorUI.cs:644
Static class for TCP client
Definition: Client.cs:31
System.Timers.Timer batteryTimer
Timer for battery request
Definition: MonitorUI.cs:62
void AdjustControls()
Make some adjustement to controls, like disabling some controls
Definition: MonitorUI.cs:84
CommandStatus RobotMove(int distance)
Move robot forward or backward, for a distance expressed in millimeter
SystemState systemState
The state of the system. Can take a value from SystemState
Definition: MonitorUI.cs:57
Main part of the program, behavior of main window
Definition: MonitorUI.cs:32
CommandStatus RobotOpenCom()
Open communication with robot and wait acknowledge
Commands and options parameters used in Destijl project when communicating with server ...
void OnCommandReceivedEvent(string header, string data, byte[] buffer)
Callback called when new message is received from server
Definition: MonitorUI.cs:207
bool Open(string hostname)
Open the specified hostname server, using default port number.
double timeout
Timeout used for command with acknowledge