using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Configuration; using System.Xml; namespace UEUserGuide { public partial class UEUserGuideFrm : Form { /////////////////////////////////////////////////////////////////////// #region Delegates public delegate void UpdateUICallBack(string trigger, IList paramsList); #endregion Delegates /////////////////////////////////////////////////////////////////////// #region Internal fields /////////////////////////////////////////////////////////////////////// #region Application settings /// /// IP address. /// private string _address; /// /// IP port. /// private int _port = -1; /// /// IP address. /// private Uri _messagesFilePath; #endregion Application settings /////////////////////////////////////////////////////////////////////// #region Network part /// /// Server instance. /// private NetworkHelper _server = null; /// /// Event set to terminate the background thread. /// System.Threading.ManualResetEvent _terminated; #endregion Network part /// User guides messages values list. /// private IDictionary _parms; /// /// Event set when user action is achieved. /// private System.Threading.ManualResetEvent _userActionCompleted; private DialogResult _userResponse; #endregion Internal fields /////////////////////////////////////////////////////////////////////// #region ctors /// /// Default ctor. /// public UEUserGuideFrm() { // Initialize logger. Logger.Instance.Initialize("UEUserGuide"); _userActionCompleted = new System.Threading.ManualResetEvent(false); InitializeComponent(); } #endregion ctors private void UEUserGuideFrm_Load(object sender, EventArgs e) { // Set UI state. ResetUI(); // Load application settings. try { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings"); _address = appSettings.Settings["IpAddress"].Value; _port = int.Parse(appSettings.Settings["IpPort"].Value); _messagesFilePath = new Uri(appSettings.Settings["MessagesFile"].Value); Text = appSettings.Settings["Title"].Value; // Sanity check. if (!System.IO.File.Exists(_messagesFilePath.LocalPath)) { throw new System.IO.FileNotFoundException(_messagesFilePath.LocalPath); } } catch (Exception x) { MessageBox.Show( string.Format("Failed to load application settings.\r\n{0}", x), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); Close(); return; } // Load message database. ReloadParameters(); if ((_parms == null) || (_parms.Count == 0)) { MessageBox.Show("Invalid content of 'UserGuidesMessages.xml' file", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); Close(); return; } // Start TCP/IP listener. try { _server = new NetworkHelper(); if (!_server.Initialize(_address, _port)) { MessageBox.Show("Failed to initialize networl communications", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); Close(); return; } _serverParms.Text = string.Format("Listen on {0}:{1}", _address, _port); _server.Start(); } catch (Exception x) { MessageBox.Show( string.Format("Failed to start listener, please check IP address/port number information in application cconfiguration file.\r\n{0}", x), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); Close(); return; } _terminated = new System.Threading.ManualResetEvent(false); _processNetworkClient.RunWorkerAsync(); _btReload.Visible = true; _messagesFile.Text = _messagesFilePath.LocalPath; } private void _btReload_Click(object sender, EventArgs e) { ReloadParameters(); ResetUI(); } private void _btOK_Click(object sender, EventArgs e) { _btOK.Enabled = false; _btFailed.Enabled = false; _userResponse = DialogResult.OK; _userActionCompleted.Set(); } private void _btYes_Click(object sender, EventArgs e) { _btYes.Enabled = false; _btNo.Enabled = false; _userResponse = DialogResult.Yes; _userActionCompleted.Set(); } private void _btNo_Click(object sender, EventArgs e) { _btYes.Enabled = false; _btNo.Enabled = false; _userResponse = DialogResult.No; _userActionCompleted.Set(); } private void _btFailed_Click(object sender, EventArgs e) { _btOK.Enabled = false; _btFailed.Enabled = false; _userResponse = DialogResult.Cancel; _userActionCompleted.Set(); } private void _btResetUI_Click(object sender, EventArgs e) { ResetUI(); } private void _btQuit_Click(object sender, EventArgs e) { _userActionCompleted.Set(); // Stop TCP/IP listener. if (_server != null) { _server.Stop(); _server = null; } // Terminate application. Close(); } /// /// Method called to relaod User guides messages. /// public void ReloadParameters() { XmlDocument parmsDoc = new XmlDocument(); parmsDoc.Load(_messagesFilePath.LocalPath); // Get the strategy to use. XmlNodeList nodes = parmsDoc.SelectNodes("descendant::UserGuideMessages/Strategies").Item(0).ChildNodes; string strategy = null; foreach (XmlNode node in nodes) { if ((node.Attributes != null) && (node.Attributes.Count != 0) && !string.IsNullOrEmpty(node.Attributes[0].Value)) { strategy = node.Attributes[0].Value; break; } } if (!string.IsNullOrEmpty(strategy)) { // Apply the strategy. nodes = parmsDoc.SelectNodes(string.Format("descendant::UserGuideMessages/{0}", strategy)).Item(0).ChildNodes; if ((nodes == null) || (nodes.Count == 0)) { // The response is empty. _parms = null; } else { _parms = new Dictionary(); foreach (XmlNode item in nodes) { if ((item.NodeType == XmlNodeType.Comment) || (item.Attributes == null) || (item.Attributes.Count != 2)) { // TODO: Add logs. continue; } _parms.Add(item.Name, new MessageParms(item.Attributes.GetNamedItem("Text").Value.Replace("\\n", System.Environment.NewLine).Replace("\\t", " "), item.Attributes.GetNamedItem("Actions").Value)); } if (_parms.Count == 0) { _parms = null; } } } else { _parms = null; } } /// /// /// /// /// private void _processNetworkClient_DoWork(object sender, DoWorkEventArgs e) { try { // Enter the listening loop. while (!_terminated.WaitOne(5, false)) { if (Logger.Instance.IsDebugLevelSet) Logger.Instance.DebugLogger("_processNetworkClient_DoWork: Waiting for a connection..."); // Perform a blocking call to accept requests. NetworkHelper client = _server.AcceptClient(); System.Threading.ThreadPool.QueueUserWorkItem(ProcessNewConnection, new ProcessNewConnectionArgs(client)); } } catch (System.Net.Sockets.SocketException s) { Logger.Instance.ErrorLogger("_processNetworkClient_DoWork: {0}", s); } e.Result = (object)true; if (Logger.Instance.IsInfoLevelSet) Logger.Instance.InfoLogger("_processNetworkClient_DoWork: Terminated."); } /// /// Process the new accepted connection. /// /// Connection instance. private void ProcessNewConnection(object parms) { // Sanity check. if (!(parms is ProcessNewConnectionArgs)) { Logger.Instance.ErrorLogger("ProcessNewConnection: Wrong parameters."); return; } ProcessNewConnectionArgs args = parms as ProcessNewConnectionArgs; if (Logger.Instance.IsDebugLevelSet) Logger.Instance.DebugLogger("ProcessNewConnection: Connected."); try { // Buffer for reading data. string readBytes; // Read data. int length = args.Client.Receive(out readBytes); byte[] buffer = System.Text.Encoding.ASCII.GetBytes(readBytes.ToCharArray(), 0, length); if (length == -1) { // Exit loop. Logger.Instance.ErrorLogger("ProcessNewConnection: Receive datas failed."); } else if (length != 0) { if (Logger.Instance.IsDebugLevelSet) Logger.Instance.DebugLogger("ProcessNewConnection: Receive datas ({0}).", length); // Convert byte into string. int index = 0; // Get ID int id = BitConverter.ToInt16(buffer, index); index += sizeof(short); // Decode full length. int totalLength = BitConverter.ToInt16(buffer, index); index += sizeof(int); // Decode the trigger command. length = BitConverter.ToInt32(buffer, index); // Decode length; index += sizeof(int); string trigger = Encoding.ASCII.GetString(buffer, index, length); // Decode datas. index += trigger.Length; // Decode the parameters. int paramsNum = BitConverter.ToInt32(buffer, index); // Decode length; index += sizeof(int); // Read the parameters. IList parmsList = new List(); for (int i = 0; i < paramsNum; i++) { length = BitConverter.ToInt32(buffer, index); // Decode length; index += sizeof(int); string str = Encoding.ASCII.GetString(buffer, index, length); // Decode datas. index += str.Length; parmsList.Add(str); } if (!string.IsNullOrEmpty(trigger)) { // Update UI in multi-threaded mode. IAsyncResult result = this.BeginInvoke(new UpdateUICallBack(UpdateUI), trigger, parmsList); // Wait for the WaitHandle to become signaled. result.AsyncWaitHandle.WaitOne(); // Wait for user action done. _userActionCompleted.WaitOne(30000); } else { _userResponse = DialogResult.Cancel; } // Send response message byte[] response = new byte[1] { (byte)_userResponse }; args.Client.Send(BitConverter.ToString(response)); // Wait for disconnection args.Client.Receive(out buffer); } } catch (Exception x) { Logger.Instance.ErrorLogger("ProcessNewConnection: {0}", x); } finally { // Shutdown and end connection. if (Logger.Instance.IsDebugLevelSet) Logger.Instance.DebugLogger("ProcessNewConnection: Close connection."); args.Client.Close(); } } private void UpdateUI(string trigger, IList paramsList) { _message.Focus(); _userActionCompleted.Reset(); string action = "OkCancel"; if (_parms.ContainsKey(trigger)) { string message = _parms[trigger].Text; if (!((paramsList.Count == 0) || ((paramsList.Count == 1) && (paramsList[0] == "*")))) { message += "\n\tParameters:\n"; foreach (string item in paramsList) { message += string.Format("\t\t{0}\n", item); } } _message.Text = message; action = _parms[trigger].Action; } else { _message.Text = trigger; } switch (action) { case "OkCancel": _btOK.Enabled = true; _btOK.Visible = true; _btFailed.Visible = true; _btFailed.Enabled = true; _btYes.Visible = false; _btNo.Visible = false; break; case "YesNo": _btOK.Visible = false; _btFailed.Visible = false; _btYes.Visible = true; _btYes.Enabled = true; _btNo.Visible = true; _btNo.Enabled = true; break; default: ResetUI(); break; } } private void ResetUI() { _message.Text = ""; _message.Lines = null; _btOK.Visible = false; _btFailed.Visible = false; _btYes.Visible = false; _btNo.Visible = false; _btOK.Enabled = true; _btFailed.Enabled = true; _btYes.Enabled = true; _btNo.Enabled = true; } } /////////////////////////////////////////////////////////////////////// #region Class helpers part /// /// This class describes the user guides messages associated to each trigger. /// class MessageParms { /////////////////////////////////////////////////////////////////////// #region Internal part /// /// Parameter type. /// private string _text; /// /// Parameter value. /// private string _action; #endregion Internal part /////////////////////////////////////////////////////////////////////// #region Accessors part /// /// Gets the parameter type. /// public string Text { get { return _text; } } /// /// Gets the parameter value. /// public string Action { get { return _action; } } #endregion Accessors part /////////////////////////////////////////////////////////////////////// #region Ctors part /// /// Default ctor. /// public MessageParms() { _text = null; _action = null; } /// /// Builder ctor. /// /// /// public MessageParms(string text, string action) { _text = text; _action = action; } #endregion Ctors part } /// /// Internal class helper. /// class ProcessNewConnectionArgs { /// /// New accepted connection reference. /// private NetworkHelper _client; /// /// Gets the new accepted connection reference. /// public NetworkHelper Client { get { return _client; } } /// /// Builder ctor. /// /// New accepted connection reference. public ProcessNewConnectionArgs(NetworkHelper client) { _client = client; } } #endregion Class helpers part }