// // CommandManager.cs // // Author: // Di MERCURIO Sébastien // // Copyright (c) 2018 INSA - DGEI // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System.Threading; namespace monitor { /// /// Command Manager. Use for timeout managment during reception of data /// Used as intermediate layer between TCP client class (Client) and application level /// managment of command and answers /// public class CommandManager { /// /// Callback for sending received data to upper level /// public delegate void CommandReceivedEvent(string msg); public CommandReceivedEvent commandReceivedEvent = null; /// /// Timer for managing timeout /// private System.Timers.Timer waitTimer = new System.Timers.Timer(); private ManualResetEvent waitEvent = new ManualResetEvent(false); /// /// Flag to tell rogram to wait for an acknowledge from server /// private bool waitForAcknowledge = false; /// /// received message /// private string messageReceived = null; /// /// flag indicating command manager is currently busy waiting an acknowledge /// private bool isBusy = false; /// /// Available status when sending command /// public enum CommandManagerStatus { AnswerReceived, Timeout, Busy }; /// /// Initializes a new instance of the class. /// /// Callback used when new message are received public CommandManager(CommandReceivedEvent callback) { Client.readEvent += this.OnMessageReception; this.commandReceivedEvent += callback; waitTimer.Elapsed += OnMessageTimeout; } /// /// Releases unmanaged resources and performs other cleanup operations before the /// is reclaimed by garbage collection. /// ~CommandManager() { Client.Close(); } /// /// Open the specified hostname server, using default port number. /// /// true if connection succeded, false otherwise /// Hostname to connect to public bool Open(string hostname) { return this.Open(hostname, Client.defaultPort); } /// /// Open connection to server "host", with port number "port" /// /// true if connection succeded, false otherwise /// Hostname to connect to /// Port number for connection public bool Open(string hostname, int port) { return Client.Open(hostname, port); } /// /// Close connection to server /// public void Close() { Client.Close(); } /// /// Callback called by Client class after reception of new message /// /// Message received from server /// Raw buffer reived from server private void OnMessageReception(string message) { waitTimer.Stop(); // Stop timeout stopwatch this.messageReceived = message; isBusy = false; // if SendCommand wait for an acknowledge, release semaphore waitEvent // so that SendCommand will be able to read received answer // Received answer will not be sent to upper level if (waitForAcknowledge) { waitForAcknowledge = false; waitEvent.Set(); // Envoi de l'evenement } else // if sendCommand doesn't wait for an acknowledge, message received // is for upper level, so call callback { waitForAcknowledge = false; this.commandReceivedEvent?.Invoke(message); } } /// /// Callback called by stopwatch on timeout /// /// Sender object /// Information on elapsed condition private void OnMessageTimeout(object sender, System.Timers.ElapsedEventArgs e) { messageReceived = null; // set buffer and message as null to indicate that no message was received // and call to OnMessagereception is due to timeout OnMessageReception(messageReceived); } /// /// Sends a command to TCP server /// /// status that is part of CommandManagerStatus enumerate /// Command message to send to server /// Answer from server, in case of acknowledge /// Timeout (ms) waiting an acknowledge, 0 if no acknowledge needed public CommandManagerStatus SendCommand(string cmd, out string answer, double timeout) { CommandManagerStatus status = CommandManagerStatus.AnswerReceived; answer = null; if (isBusy) status = CommandManagerStatus.Busy; else { isBusy = true; // Send command to server Client.Write(cmd); if (timeout > 0) // Command request an acknowledge { waitForAcknowledge = true; // Flag used in OnMessageReception callback to avoid // sending acknowledge message to upper level waitTimer.Interval = timeout; waitTimer.Start(); // Start timeout timer waitEvent.WaitOne(); // Stop current thread, waiting for waitEvent semaphore // produced in OnMessageReception when either a message is received // or a timeout occur waitEvent.Reset(); // reset semaphore for next message if (this.messageReceived == null) // timeout: server connection error { status = CommandManagerStatus.Timeout; } } else isBusy = false; // return received answer, null in case of timeout answer = this.messageReceived; this.messageReceived = null; } return status; } } }