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.

CommandManager.cs 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //
  2. // CommandManager.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.Threading;
  22. namespace monitor
  23. {
  24. /// <summary>
  25. /// Command Manager. Use for timeout managment during reception of data
  26. /// Used as intermediate layer between TCP client class (Client) and application level
  27. /// managment of command and answers
  28. /// </summary>
  29. public class CommandManager
  30. {
  31. /// <summary>
  32. /// Callback for sending received data to upper level
  33. /// </summary>
  34. public delegate void CommandReceivedEvent(string msg, byte[] buffer);
  35. public CommandReceivedEvent commandReceivedEvent = null;
  36. /// <summary>
  37. /// Timer for managing timeout
  38. /// </summary>
  39. private System.Timers.Timer waitTimer = new System.Timers.Timer();
  40. private ManualResetEvent waitEvent = new ManualResetEvent(false);
  41. /// <summary>
  42. /// Flag to tell rogram to wait for an acknowledge from server
  43. /// </summary>
  44. private bool waitForAcknowledge = false;
  45. /// <summary>
  46. /// received message
  47. /// </summary>
  48. private string messageReceived = null;
  49. /// <summary>
  50. /// flag indicating command manager is currently busy waiting an acknowledge
  51. /// </summary>
  52. private bool isBusy = false;
  53. /// <summary>
  54. /// Available status when sending command
  55. /// </summary>
  56. public enum CommandManagerStatus
  57. {
  58. AnswerReceived,
  59. Timeout,
  60. Busy
  61. };
  62. /// <summary>
  63. /// Initializes a new instance of the <see cref="T:monitor.CommandManager"/> class.
  64. /// </summary>
  65. /// <param name="callback">Callback used when new message are received</param>
  66. public CommandManager(CommandReceivedEvent callback)
  67. {
  68. Client.readEvent += this.OnMessageReception;
  69. this.commandReceivedEvent += callback;
  70. waitTimer.Elapsed += OnMessageTimeout;
  71. }
  72. /// <summary>
  73. /// Releases unmanaged resources and performs other cleanup operations before the
  74. /// <see cref="T:monitor.CommandManager"/> is reclaimed by garbage collection.
  75. /// </summary>
  76. ~CommandManager()
  77. {
  78. Client.Close();
  79. }
  80. /// <summary>
  81. /// Open the specified hostname server, using default port number.
  82. /// </summary>
  83. /// <returns>true if connection succeded, false otherwise</returns>
  84. /// <param name="hostname">Hostname to connect to</param>
  85. public bool Open(string hostname)
  86. {
  87. return this.Open(hostname, Client.defaultPort);
  88. }
  89. /// <summary>
  90. /// Open connection to server "host", with port number "port"
  91. /// </summary>
  92. /// <returns>true if connection succeded, false otherwise</returns>
  93. /// <param name="hostname">Hostname to connect to</param>
  94. /// <param name="port">Port number for connection</param>
  95. public bool Open(string hostname, int port)
  96. {
  97. return Client.Open(hostname, port);
  98. }
  99. /// <summary>
  100. /// Close connection to server
  101. /// </summary>
  102. public void Close()
  103. {
  104. Client.Close();
  105. }
  106. /// <summary>
  107. /// Callback called by Client class after reception of new message
  108. /// </summary>
  109. /// <param name="message">Message received from server</param>
  110. /// <param name="buffer">Raw buffer reived from server</param>
  111. private void OnMessageReception(string message, byte[] buffer)
  112. {
  113. waitTimer.Stop(); // Stop timeout stopwatch
  114. this.messageReceived = message;
  115. isBusy = false;
  116. // if SendCommand wait for an acknowledge, release semaphore waitEvent
  117. // so that SendCommand will be able to read received answer
  118. // Received answer will not be sent to upper level
  119. if (waitForAcknowledge)
  120. {
  121. waitForAcknowledge = false;
  122. waitEvent.Set(); // Envoi de l'evenement
  123. }
  124. else
  125. // if sendCommand doesn't wait for an acknowledge, message received
  126. // is for upper level, so call callback
  127. {
  128. waitForAcknowledge = false;
  129. this.commandReceivedEvent?.Invoke(message, buffer);
  130. }
  131. }
  132. /// <summary>
  133. /// Callback called by stopwatch on timeout
  134. /// </summary>
  135. /// <param name="sender">Sender object</param>
  136. /// <param name="e">Information on elapsed condition</param>
  137. private void OnMessageTimeout(object sender, System.Timers.ElapsedEventArgs e)
  138. {
  139. messageReceived = null;
  140. // set buffer and message as null to indicate that no message was received
  141. // and call to OnMessagereception is due to timeout
  142. OnMessageReception(messageReceived, null);
  143. }
  144. /// <summary>
  145. /// Sends a command to TCP server
  146. /// </summary>
  147. /// <returns>status that is part of CommandManagerStatus enumerate</returns>
  148. /// <param name="cmd">Command message to send to server</param>
  149. /// <param name="answer">Answer from server, in case of acknowledge</param>
  150. /// <param name="timeout">Timeout (ms) waiting an acknowledge, 0 if no acknowledge needed</param>
  151. public CommandManagerStatus SendCommand(string cmd, out string answer, double timeout)
  152. {
  153. CommandManagerStatus status = CommandManagerStatus.AnswerReceived;
  154. answer = null;
  155. if (isBusy) status = CommandManagerStatus.Busy;
  156. else
  157. {
  158. isBusy = true;
  159. // Send command to server
  160. Client.Write(cmd);
  161. if (timeout > 0) // Command request an acknowledge
  162. {
  163. waitForAcknowledge = true; // Flag used in OnMessageReception callback to avoid
  164. // sending acknowledge message to upper level
  165. waitTimer.Interval = timeout;
  166. waitTimer.Start(); // Start timeout timer
  167. waitEvent.WaitOne(); // Stop current thread, waiting for waitEvent semaphore
  168. // produced in OnMessageReception when either a message is received
  169. // or a timeout occur
  170. waitEvent.Reset(); // reset semaphore for next message
  171. if (this.messageReceived == null) // timeout: server connection error
  172. {
  173. status = CommandManagerStatus.Timeout;
  174. }
  175. }
  176. else isBusy = false;
  177. // return received answer, null in case of timeout
  178. answer = this.messageReceived;
  179. this.messageReceived = null;
  180. }
  181. return status;
  182. }
  183. }
  184. }