private void ExecutionThread_DoWork(object obj)
 {
     try
     {
         CancellationToken token = (CancellationToken)obj;
         while (!token.IsCancellationRequested)
         {
             if (Command == null)
             {
                 Debug.WriteLine("EecutionThread " + Thread.CurrentThread.GetHashCode() + ": Execution Signal, wait for next...");
                 ExecutionSignal.WaitOne();
                 Debug.WriteLine("EecutionThread " + Thread.CurrentThread.GetHashCode() + ": Execution Signal received.");
             }
             else
             {
                 Debug.WriteLine("EecutionThread " + Thread.CurrentThread.GetHashCode() + ": Processing command...");
                 Command?.ExecuteCallback();
                 Command = null;
                 ExecutionWatchDog.Set(); //Notify Watch dog execution completed.
                 Debug.WriteLine("EecutionThread " + Thread.CurrentThread.GetHashCode() + ": Completed.");
             }
         }
         if (token.IsCancellationRequested)
         {
             Trace.WriteLine("EecutionThread " + Thread.CurrentThread.GetHashCode() + ": Thread Cancel and Terminated!");
         }
     }
     catch (Exception ex) { Trace.WriteLine("[ERROR] ExecutionThread Crahsed: " + ex.Message); }
 }
Exemplo n.º 2
0
 private void VerifyUserSignedIn(TcpAppInputCommand command)
 {
     if (command.AppClient.SignedIn)
     {
         return;
     }
     throw new InvalidOperationException("Client not signed in!");
 }
Exemplo n.º 3
0
        private void WriteResultToClient(TcpServerConnection client, TcpAppInputCommand input)
        {
            string returnMsg = "#TCP# " + input.Command.Keyword + " " + input.Status.ToString();

            if (!string.IsNullOrEmpty(input.OutputMessage))
            {
                returnMsg += " " + input.OutputMessage;
            }
            client.WriteLineToClient(returnMsg);
        }
Exemplo n.º 4
0
        private void WriteResultToClient(TcpAppServerConnection client, TcpAppInputCommand input)
        {
            string returnMsg = input.Status.ToString() + " ";

            if (!string.IsNullOrEmpty(input.OutputMessage))
            {
                returnMsg += input.OutputMessage;
            }
            Debug.WriteLine("AppServer[" + client.Name + "]-TX: " + returnMsg);
            client.Connection.WriteLineToClient(returnMsg);
        }
        private void WriteResultToClient(TcpServerConnection client, TcpAppInputCommand input)
        {
            string returnMsg = input.Command.Keyword + " " + input.Status.ToString();

            if (!string.IsNullOrEmpty(input.OutputMessage))
            {
                returnMsg += " " + input.OutputMessage;
            }
            System.Diagnostics.Trace.WriteLine("Write To Client: " + returnMsg);
            client.WriteLineToClient(returnMsg);
        }
Exemplo n.º 6
0
        public static TcpAppInputCommand CreateInputCommand(List <TcpAppCommand> commandList, string[] commandArguments)
        {
            TcpAppInputCommand result = null;

            //Process Command Keyword
            TcpAppCommand cmdHandler = commandList.FirstOrDefault(x => x.Keyword.Equals(commandArguments[0], StringComparison.InvariantCultureIgnoreCase));

            if (cmdHandler == null)
            {
                return(null);
            }

            result = new TcpAppInputCommand()
            {
                Command = cmdHandler.Clone() as TcpAppCommand
            };
            result.Arguments = commandArguments.Skip(1).ToArray(); //Arguments exclude command keyword

            //Process Parameters
            cmdHandler.ResetParametersValue();
            int argID = 0; //First Parameter

            foreach (TcpAppParameter item in cmdHandler.Parameters)
            {
                if (argID >= result.Arguments.Length)
                {
                    //Argument with no input
                    if (!item.IsOptional)
                    {
                        //Error - Missing required parameter
                        throw new ArgumentException("Missing required parameter: " + item.Name + "!");
                    }
                }
                else if (item.IsArray)
                {
                    item.Values.Clear();
                    //Parameter Array is last parameters consume all arguments in command
                    for (int m = argID; m < result.Arguments.Length; m++)
                    {
                        item.Values.Add(result.Arguments[m]);
                    }
                    break;
                }
                else
                {
                    item.Value = result.Arguments[argID]; //Assign parameter value
                }
                argID++;
            }
            return(result);
        }
Exemplo n.º 7
0
        internal void Client_MessageReceived(object sender, MessageReceivedEventArgs e)
        {
            //Process incoming message from Client
            if (!e.ReceivedMessage.StartsWith("#TCP#"))
            {
                return;                                         //Drop message which is not using defined format.
            }
            //Parse and Execute Commands
            string[]      cmdArg     = e.ReceivedMessage.Trim().Split(' ');
            TcpAppCommand cmdHandler = GetCommand(cmdArg[1]);

            if (cmdHandler == null)
            {
                //Error - Unrecognized command.
                e.Client.WriteLineToClient(string.Format("#TCP# {0} {1} Invalid Command!",
                                                         cmdArg[1], TcpAppCommandStatus.ERR.ToString()));
                return;
            }

            TcpAppInputCommand cmdInput = new TcpAppInputCommand()
            {
                Command = cmdHandler
            };

            cmdHandler.ResetArgumentsValue();
            int argID = 2; //First Argument

            foreach (TcpAppArgument item in cmdHandler.Arguments)
            {
                if (argID >= cmdArg.Length)
                {
                    //Argument with no input
                    if (!item.IsOptional)
                    {
                        //Error - Missing required argument
                        cmdInput.OutputMessage = "Missing required argument: " + item.Name + "!";
                        WriteResultToClient(e.Client, cmdInput);
                        return;
                    }
                }
                else
                {
                    item.Value = cmdArg[argID]; //Assign argument value
                }
                argID++;
            }
            cmdInput.Command.ExecuteCallback(cmdInput); //Send result back to client.
            WriteResultToClient(e.Client, cmdInput);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Execute Plugin Callback. Call by ITcpAppServerPlugin
        /// </summary>
        /// <param name="commandArguments">Command keyword and arguments in string array.</param>
        public TcpAppInputCommand GetPluginCommand(string [] commandArguments)
        {
            if (commandArguments == null)
            {
                throw new ArgumentNullException(nameof(commandArguments), "Invalid command / empty string!");
            }

            TcpAppInputCommand command = TcpAppCommon.CreateInputCommand(Commands, commandArguments);

            if (command == null)
            {
                throw new ArgumentException("Unknown command: " + commandArguments.FirstOrDefault());
            }
            return(command);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    Command = null;
                    StopExecutionThread();
                    ExecutionSignal.Dispose();
                    ExecutionWatchDog.Dispose();
                }

                // free unmanaged resources (unmanaged objects) and override finalizer
                // set large fields to null
                disposedValue = true;
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Execute Plugin Callback. Call by ITcpAppServerPlugin
        /// </summary>
        /// <param name="sender"></param>
        public void ExecutePluginCommand(TcpAppInputCommand sender)
        {
            sender.Status = TcpAppCommandStatus.ERR;
            string[] cmdArg = sender.Arguments.Skip(1).ToArray();

            //Process Command Keyword
            TcpAppCommand cmdHandler = GetCommand(cmdArg[0]);

            if (cmdHandler == null)
            {
                //Error - Unrecognized command.
                sender.OutputMessage = string.Format("Invalid Command: {0}!", cmdArg[0]);
                return;
            }
            sender.Command = cmdHandler;

            //Process Parameters
            cmdHandler.ResetParametersValue();
            int argID = 1; //First Parameter

            foreach (TcpAppParameter item in cmdHandler.Parameters)
            {
                if (argID >= cmdArg.Length)
                {
                    //Argument with no input
                    if (!item.IsOptional)
                    {
                        //Error - Missing required parameter
                        sender.OutputMessage = "Missing required parameter: " + item.Name + "!";
                        return;
                    }
                }
                else
                {
                    item.Value = cmdArg[argID]; //Assign parameter value
                }
                argID++;
            }

            //Execute Command.
            //Note: Error handling not required. Will handle by TcpAppServer class.
            sender.Command.ExecuteCallback(sender);
        }
        private void ShowHelp(TcpAppInputCommand sender)
        {
            string        aliasName = sender.Command.Parameter("Alias").Value;
            List <string> lines     = null;

            if (aliasName == "-")
            {
                //Print Help Screen - Write to sender.OutputMessage
                lines = new List <string>
                {
                    "TCP Aplication Server Version " + Version.ToString(),
                    " ",
                    "==== USAGE ====",
                    "  SEND: <Command> [Param0] ... [ParamN]",
                    "  RECV: <Command> <Status> [Return Message]",
                    " ",
                    " Notes:",
                    "  <> = Required parameters",
                    "  [] = Optional parameters",
                    " ",
                };

                lines.AddRange(TcpAppCommon.PrintCommandHelpContents(Commands));
                sender.OutputMessage = string.Join("\r\n", lines.ToArray());
                sender.Status        = TcpAppCommandStatus.OK;
            }
            else
            {
                //Get Help Content for selected object.
                ITcpAppServerPlugin plugin = Plugins.FirstOrDefault(x => string.Compare(x.Alias, sender.Command.Parameter("Alias").Value, true) == 0);
                if (plugin == null)
                {
                    sender.Status        = TcpAppCommandStatus.ERR;
                    sender.OutputMessage = "Object [" + aliasName + "] not exist!";
                }
                else
                {
                    plugin.ShowHelp(sender);
                }
            }
        }
        /// <summary>
        /// Execute command with timeout handling
        /// </summary>
        /// <param name="command"></param>
        /// <param name="timeout_ms"></param>
        internal void ExecuteCommandAsync(TcpAppInputCommand command, int timeout_ms)
        {
            Command = command;
            Debug.WriteLine("ExecuteCommandAsync: Start");
            ExecutionWatchDog.Reset();
            ExecutionSignal.Set();
            bool done = ExecutionWatchDog.WaitOne(timeout_ms);

            if (!done)
            {
                Debug.WriteLine("[ERROR]AppServer-ExecuteCommandAsync: Timeout! Recreate execution thread!");
                StopExecutionThread();
                Command.OutputMessage = "Server Timeout";
                Command.Status        = TcpAppCommandStatus.ERR;
                Command = null;
                CreateAndStartExecutionThread();
            }
            else
            {
                Debug.WriteLine("ExecuteCommandAsync: Completed");
            }
        }
Exemplo n.º 13
0
        private void ShowHelp(TcpAppInputCommand sender)
        {
            //Print Help Screen - Write to sender.OutputMessage
            List <string> lines = new List <string>();

            lines.Add("TCP Aplication Server Version " + Version.ToString());
            lines.Add(" ");
            lines.Add("== USAGE ==");
            lines.Add("  SEND: #TCP# <Command> [Param0] ... [ParamN]");
            lines.Add("  RECV: #TCP# <Command> <Status> [Return Message]");
            lines.Add(" ");
            lines.Add("== COMMAND ==");
            foreach (TcpAppCommand cmd in Commands)
            {
                lines.Add(String.Format(" {0,-20}  {1}", cmd.Keyword, cmd.Description));
                foreach (TcpAppArgument arg in cmd.Arguments)
                {
                    lines.Add(String.Format(" {0,-10} {1,-10}  {2}", " ", "<" + arg.Name + ">", arg.Description));
                }
            }

            sender.OutputMessage = string.Join("\r\n", lines.ToArray());
            sender.Status        = TcpAppCommandStatus.OK;
        }
Exemplo n.º 14
0
 /// <summary>
 /// Print help screen for selected plugin components.
 /// </summary>
 /// <param name="sender"></param>
 public void ShowHelp(TcpAppInputCommand sender)
 {
     sender.OutputMessage = string.Join("\r\n", TcpAppCommon.PrintCommandHelpContents(Commands));
     sender.Status        = TcpAppCommandStatus.OK;
 }
Exemplo n.º 15
0
        private void ShowHelp(TcpAppInputCommand sender)
        {
            string        aliasName = sender.Command.Parameter("Plugin").Value;
            List <string> lines     = null;

            if (aliasName == "-")
            {
                lines = new List <string>
                {
                    "[ TCP Aplication Server V" + Version.ToString() + " ]",
                    " ",
                    "==== USAGE ====",
                    "  Execute Tcp App Server Command:",
                    "  SEND: <Command> [Param0] ... [ParamN]",
                    "  RECV: <Status> [Return Message]",
                    " ",
                    "  Execute plugin Command:",
                    "  SEND: <Alias> <Command> [Param0] ... [ParamN]",
                    "  RECV: <Status> [Return Message]",
                    " ",
                    "  Notes:",
                    "   Command are not case sensitive.",
                    "   <> = Required parameters",
                    "   [] = Optional parameters",
                    " ",
                };

                //Insert Application Header if defined.
                string header = OnShowHelpGetApplicationHeader();
                if (!string.IsNullOrEmpty(header))
                {
                    lines.Insert(0, header);
                }

                lines.AddRange(TcpAppCommon.PrintCommandHelpContents(Commands));

                //Print registered plugins
                if (PluginTypes.Count > 0)
                {
                    lines.Add("[ PLUGINS ]");
                    lines.AddRange(PluginTypes.Select(x => string.Format(" {0, -20}  {1}", x.Name, x.Description)).ToArray());
                    lines.Add(" ");
                }

                if (_Plugins.Count > 0)
                {
                    lines.Add("[ OBJECTS ]");
                    lines.AddRange(_Plugins.Select(x => string.Format(" {0, -20}  {1}", x.Alias, "(" + PluginTypes.FirstOrDefault(n => n.Type == x.GetType())?.Name + ")")).ToArray());
                }

                sender.OutputMessage = string.Join(TcpAppCommon.NewLine, lines.ToArray());
                sender.Status        = TcpAppCommandStatus.OK;
            }
            else
            {
                //Get Help Content for selected object.
                string pluginName              = sender.Command.Parameter("Plugin").Value;
                ITcpAppServerPlugin    plugin  = null;
                TcpAppServerPluginType ptrType = PluginTypes.FirstOrDefault(x => x.Name.Equals(pluginName, StringComparison.InvariantCultureIgnoreCase));
                if (ptrType != null)
                {
                    //Get help by type
                    Type pluginType = ptrType.Type;
                    plugin = _Plugins.FirstOrDefault(x => x.GetType() == pluginType);
                    if (plugin != null)
                    {
                        //Show Help using existing object
                        plugin.ShowHelp(sender);
                        return;
                    }

                    //Create instance and show help, dispose after use
                    plugin = Activator.CreateInstance(pluginType) as ITcpAppServerPlugin;
                    plugin.ShowHelp(sender);
                    plugin.DisposeRequest();
                    plugin = null;
                    return;
                }
                else
                {
                    //Get help by alias name
                    plugin = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, pluginName, true) == 0);
                    if (plugin != null)
                    {
                        plugin.ShowHelp(sender);
                        return;
                    }
                }
                sender.Status        = TcpAppCommandStatus.ERR;
                sender.OutputMessage = "Object [" + aliasName + "] not exist!";
            }
        }
Exemplo n.º 16
0
        private void Client_ProcessReceivedMessage(TcpServerConnection client, string message, byte[] messageBytes)
        {
            //Parse and Execute Commands
            string[] cmdArg = TcpAppCommon.ParseCommand(message.Trim());
            try
            {
                //Register Client Connection
                TcpAppServerConnection ptrClient = AppClients.FirstOrDefault(x => x.Connection == client);
                if (ptrClient == null)
                {
                    //Reconstruct device which had already signed out
                    ptrClient = AddClientToAppClientsList(client);
                }
                Debug.WriteLine("AppServer[" + ptrClient.Name + "]-RX: " + message);

                TcpAppInputCommand inputCommand = TcpAppCommon.CreateInputCommand(Commands, cmdArg);
                if (inputCommand != null)
                {
                    inputCommand.AppClient = ptrClient;
                }
                else//Command keyword not exist
                {
                    //Check if command keyword is alias name
                    ITcpAppServerPlugin plugin = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, cmdArg[0], true) == 0);
                    if (plugin != null)
                    {
                        //Execute plugin command
                        inputCommand           = plugin.GetPluginCommand(cmdArg.Skip(1).ToArray());
                        inputCommand.AppClient = ptrClient;
                        BeforeExecutePluginCommand?.Invoke(this, new TcpAppServerExEventArgs(ptrClient)
                        {
                            Plugin = plugin
                        });
                    }
                    else
                    {
                        //Error - Unrecognized command.
                        inputCommand = new TcpAppInputCommand()
                        {
                            Status        = TcpAppCommandStatus.ERR,
                            OutputMessage = "Invalid Command " + cmdArg[0]
                        };
                        WriteResultToClient(ptrClient, inputCommand);
                        return;
                    }
                }

                //Verify Client had signed in.
                if (!inputCommand.AppClient.SignedIn && !inputCommand.Command.IsSystemCommand)
                {
                    throw new Exception("Client not signed in! Execute SignIn first.");
                }

                if (inputCommand.Command.UseMessageQueue && !inputCommand.Command.IsSystemCommand)
                {
                    //Single thread execution, post message to message queue.
                    lock (CommandQueue)
                    {
                        inputCommand.ID = inputCommand.GetHashCode();
                        if (ptrClient.NextQueuedCommand == null)
                        {
                            ptrClient.NextQueuedCommand = inputCommand;                                      //Set pointer to next queued command.
                        }
                        //Add Command to Queue
                        CommandQueue.Add(inputCommand);
                        CommandQueueWaitSignal?.Set();
                        inputCommand.OutputMessage = inputCommand.ID.ToString();
                        inputCommand.Status        = TcpAppCommandStatus.QUEUED;
                    }
                }
                else if (inputCommand.Command.IsSystemCommand)
                {
                    inputCommand.ExecuteCallback();
                }
                else
                {
                    //Execute command, wait until return
                    if (ExecutionTimeout == 0)
                    {
                        inputCommand.ExecuteCallback();
                    }
                    //Execute command, terminate on timeout
                    else
                    {
                        ptrClient.ExecuteCommandAsync(inputCommand, ExecutionTimeout);
                    }
                }

                WriteResultToClient(ptrClient, inputCommand); //Send result back to client.
            }
            catch (Exception ex)
            {
                WriteExceptionErrorToClient(client, ex);
            }
        }
Exemplo n.º 17
0
        /// <summary>
        /// Constructor
        /// </summary>
        public TcpAppServer() : base()
        {
            MessageDelimiter         = Convert.ToByte(Convert.ToChar(TcpAppCommon.Delimiter));
            base.ClientConnected    += TcpAppServer_ClientConnected;
            base.ClientDisconnected += TcpAppServer_ClientDisconnected;
            base.ServerStopped      += TcpAppServer_ServerStopped;
            CommandQueueThread       = new Thread(ExecuteQueuedCommandsAsync);
            AbortCommandQueueThread  = false;
            CommandQueueThread.Start();

            //TcpAppServer Format:
            // TX: TCP: <Command> [-Param0] [-Param1] ... [-ParamN]
            // RX: TCP: <Status> [Return Message]
            // Source - [email protected]:23
            // Command - Registered Command.

            //Register System Commands
            //--- INIT (Commands used by TcpAppClient) ---
            RegisterSystemCommand("Help", "Show help screen. Include plugin type or object alias name to show commands for selected plugin.", ShowHelp,
                                  TcpAppParameter.CreateOptionalParameter("Plugin", "Plugin type or Alias", "-"));
            RegisterSystemCommand("SignIn", "Sign in to TcpAppServer. Server will verify connection id and return unique ID.", delegate(TcpAppInputCommand sender)
            {
                //Assign Name
                string machineName = sender.Command.Parameter("ConnectionID").Value?.Replace(" ", "_");
                if (string.IsNullOrEmpty(machineName))
                {
                    machineName = sender.AppClient.Connection.ClientIPAddress.ToString();
                }

                if (sender.AppClient.SignedIn)
                {
                    //Client already signed in, verify connection ID.
                    if (sender.AppClient.Name.Equals(machineName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        sender.OutputMessage = sender.AppClient.Name;
                        sender.Status        = TcpAppCommandStatus.OK;
                        return;
                    }
                    else
                    {
                        sender.AppClient.SignedIn = false;
                    }
                }

                TcpAppServerExEventArgs signInArg = new TcpAppServerExEventArgs(sender.AppClient)
                {
                    Value = machineName
                };
                ClientSigningIn?.Invoke(this, signInArg);
                if (signInArg.Cancel == true)
                {
                    sender.OutputMessage = signInArg.Reason;
                    if (string.IsNullOrEmpty(sender.OutputMessage))
                    {
                        sender.OutputMessage = "Access Denied!";
                    }
                    sender.Status = TcpAppCommandStatus.ERR;
                    return;
                }

                string uniqueName = machineName;
                lock (AppClients)
                {
                    //Cleanup instance with same name but already disconnected without signout
                    if (AppClients.FirstOrDefault(x => x.Name == uniqueName) != null)
                    {
                        for (int x = 0; x < AppClients.Count;)
                        {
                            if (!AppClients[x].Connection.Connected && AppClients[x].Name.StartsWith(uniqueName))
                            {
                                AppClients[x].Dispose();
                                AppClients.RemoveAt(x);
                            }
                            else
                            {
                                x++;
                            }
                        }
                    }
                    while (AppClients.FirstOrDefault(x => x.Name == uniqueName) != null)
                    {
                        uniqueName = machineName + "_" + (++Counter).ToString();
                    }
                    sender.AppClient.Name     = uniqueName;
                    sender.OutputMessage      = uniqueName;
                    sender.Status             = TcpAppCommandStatus.OK;
                    sender.AppClient.SignedIn = true;
                    ClientSignedIn?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient));     //Event
                }
            },
                                  TcpAppParameter.CreateParameter("ConnectionID", "Connection ID. If already exist, server will return an updated unique ID."));
            RegisterSystemCommand("SignOut", "Signout TcpAppClient.", delegate(TcpAppInputCommand sender)
            {
                ClientSigningOut?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient));
                sender.AppClient.Dispose();
                lock (AppClients) { AppClients.Remove(sender.AppClient); }
                sender.AppClient.SignedIn = false;
                sender.Status             = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Version?", "Get TcpAppServer Library Version", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = Version.ToString();
                sender.Status        = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Terminate", "Terminate Application. Command only valid after client Signin. Default Exit Code = -99", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                int exitCode = -99;
                try
                {
                    exitCode = Convert.ToInt32(sender.Command.Parameter("ExitCode").Value);
                }
                catch { }
                sender.Status = TcpAppCommandStatus.OK;

                Environment.ExitCode = exitCode;
                Thread ptrThread     = new Thread(TerminateApplication);
                ptrThread.Start();
            },
                                  TcpAppParameter.CreateOptionalParameter("ExitCode", "Assign Exit Code for application termination.", "-99"));

            //--- Execution ---
            RegisterSystemCommand("FunctionList?", "Get list of registered functions.", delegate(TcpAppInputCommand sender)
            {
                foreach (TcpAppCommand x in Commands)
                {
                    sender.OutputMessage += x.Keyword;
                    sender.OutputMessage += " ";
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("Execute", "Execute plugin's command. Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                ITcpAppServerPlugin plugin = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, sender.Command.Parameter("Alias").Value, true) == 0);
                if (plugin == null)
                {
                    throw new ArgumentException("Plugin not exists!");
                }

                TcpAppInputCommand pluginCommand = plugin.GetPluginCommand(sender.Arguments.Skip(1).ToArray());
                TcpAppServerExEventArgs pluginExecuteEventArgs = new TcpAppServerExEventArgs(sender.AppClient)
                {
                    Plugin = plugin
                };
                BeforeExecutePluginCommand?.Invoke(this, pluginExecuteEventArgs);
                if (pluginExecuteEventArgs.Cancel)
                {
                    //Command execution cancelled by server, return error with reason.
                    sender.Status        = TcpAppCommandStatus.ERR;
                    sender.OutputMessage = pluginExecuteEventArgs.Reason;
                }
                else
                {
                    //Proceed with execution.
                    pluginCommand.ExecuteCallback();
                    sender.Status        = pluginCommand.Status;
                    sender.OutputMessage = pluginCommand.OutputMessage;
                }
            },
                                  TcpAppParameter.CreateParameter("Alias", "Plugin Alias Name."));
            RegisterSystemCommand("CheckStatus", "Check execution status for queued command. RETURN: Command status if executed, else BUSY. ERR if no queued message.",
                                  delegate(TcpAppInputCommand sender)
            {
                int queueID = Convert.ToInt32(sender.Command.Parameter("QueueID").Value);
                if (queueID == 0)
                {
                    //ID not specified, get status for next queued command.
                    if (sender.AppClient.NextQueuedCommand == null)
                    {
                        sender.Status        = TcpAppCommandStatus.ERR;
                        sender.OutputMessage = "No queued message!";
                        return;
                    }

                    sender.Status = sender.AppClient.NextQueuedCommand.Status;
                    switch (sender.AppClient.NextQueuedCommand.Status)
                    {
                    case TcpAppCommandStatus.BUSY:
                    case TcpAppCommandStatus.QUEUED:
                        sender.OutputMessage = string.Empty;
                        break;

                    default:
                        lock (CommandQueue)
                        {
                            //Return result for all queued message except executing one.
                            TcpAppInputCommand[] results = ResultQueue.Where(x => x.AppClient == sender.AppClient).ToArray();
                            TcpAppInputCommand nextQueue = null;
                            foreach (TcpAppInputCommand cmd in results)
                            {
                                if (cmd.Status == TcpAppCommandStatus.BUSY || cmd.Status == TcpAppCommandStatus.QUEUED)
                                {
                                    nextQueue = cmd;
                                    break;
                                }
                                if (cmd.Status == TcpAppCommandStatus.ERR)
                                {
                                    cmd.Status         = sender.Status;
                                    cmd.OutputMessage += "! ";         //Prefix for command with error status.
                                }
                                ResultQueue.Remove(cmd);
                                sender.OutputMessage += cmd.OutputMessage + "\n";
                            }

                            //Return number of remaining queued commands
                            sender.OutputMessage += CommandQueue.Where(x => x.AppClient == sender.AppClient).Count().ToString();
                            sender.AppClient.NextQueuedCommand = nextQueue;
                        }
                        break;
                    }
                }
                else
                {
                    //Return status of specific message.
                    TcpAppInputCommand ptrCmd = CommandQueue.FirstOrDefault(x => x.AppClient == sender.AppClient && x.ID == queueID);
                    if (ptrCmd == null)
                    {
                        ptrCmd = ResultQueue.FirstOrDefault(x => x.AppClient == sender.AppClient);
                        if (ptrCmd != null)
                        {
                            ResultQueue.Remove(ptrCmd);
                        }
                        else
                        {
                            sender.Status        = TcpAppCommandStatus.ERR;
                            sender.OutputMessage = "Invalid ID: " + queueID.ToString();
                            return;
                        }
                    }

                    sender.OutputMessage = ptrCmd.OutputMessage;
                    sender.Status        = ptrCmd.Status;
                }
            },
                                  TcpAppParameter.CreateOptionalParameter("QueueID", "Get status of specific message.", "0"));
            RegisterSystemCommand("Abort", "Abort last queued command.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                if (sender.AppClient.NextQueuedCommand != null)
                {
                    lock (CommandQueue)
                    {
                        CommandQueue.Remove(sender.AppClient.NextQueuedCommand);
                        ResultQueue.Remove(sender.AppClient.NextQueuedCommand);
                        sender.AppClient.NextQueuedCommand = null;
                    }
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("FlushQueue", "Flush message queue for calling client.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                lock (CommandQueue)
                {
                    CommandQueue.RemoveAll(x => x.AppClient == sender.AppClient);
                    ResultQueue.RemoveAll(x => x.AppClient == sender.AppClient);
                    sender.AppClient.NextQueuedCommand = null;
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("FlushAllQueue", "Flush message queue for all clients.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                lock (CommandQueue)
                {
                    CommandQueue.Clear();
                    ResultQueue.Clear();
                    lock (AppClients)
                    {
                        foreach (TcpAppServerConnection client in AppClients)
                        {
                            client.NextQueuedCommand = null;
                        }
                    }
                }
                sender.Status = TcpAppCommandStatus.OK;
            });

            //--- PLUGIN ---
            RegisterSystemCommand("PluginTypes?", "Get list of plugin class type. Use CreatePlugins command to instantiate type.", delegate(TcpAppInputCommand sender)
            {
                if (PluginTypes.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(" ", PluginTypes.Select(x => x.Name).ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("CreatePlugin", "Create an plugin object from listed plugin types.  Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                string typeName = sender.Command.Parameter("TypeName").Value;

                //Sanity Check - Type Name
                if (PluginTypes.FirstOrDefault(x => x.Name.Equals(typeName, StringComparison.InvariantCultureIgnoreCase)) == null)
                {
                    throw new ArgumentException("Unknown plugin type: " + typeName);
                }

                Type pluginType = PluginTypes.FirstOrDefault(x => x.Name.Equals(typeName, StringComparison.InvariantCultureIgnoreCase)).Type;

                string aliasName;
                sender.OutputMessage = "Plugin created:";
                foreach (string value in sender.Command.Parameter("Alias").Values)
                {
                    aliasName = value;
                    if (Commands.FirstOrDefault(x => string.Compare(x.Keyword, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Name already registered as command!");
                    }

                    //Sanity Check - Verify alias name is not plugin type name
                    if (PluginTypes.FirstOrDefault(x => string.Compare(x.Name, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Name already registered plugin type!");
                    }

                    //Sanity Check - Alias Name
                    if (_Plugins.FirstOrDefault(x => string.Compare(x.Alias, aliasName, true) == 0) != null)
                    {
                        throw new ArgumentException("Unable to create plugin with alias '" + aliasName + "'. Plugin already exists!");
                    }

                    ITcpAppServerPlugin pluginInstance = Activator.CreateInstance(pluginType) as ITcpAppServerPlugin;
                    pluginInstance.Alias = aliasName;
                    _Plugins.Add(pluginInstance);
                    PluginCreated?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient)
                    {
                        Plugin = pluginInstance
                    });
                    sender.OutputMessage += " " + aliasName;
                }
                sender.Status = TcpAppCommandStatus.OK;
            },
                                  TcpAppParameter.CreateParameter("TypeName", "Plugin type name."),
                                  TcpAppParameter.CreateParameterArray("Alias", "Plugin object name, case insensitive.", false));
            RegisterSystemCommand("Plugins?", "Return plugins list by alias name.", delegate(TcpAppInputCommand sender)
            {
                if (_Plugins.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(TcpAppCommon.NewLine, _Plugins.Select(x => x.Alias + "(" + PluginTypes.FirstOrDefault(n => n.Type == x.GetType())?.Name + ")").ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterSystemCommand("DisposePlugin", "Delete plugin by alias name. Command only valid after client Signin.", delegate(TcpAppInputCommand sender)
            {
                VerifyUserSignedIn(sender);
                string alias             = sender.Command.Parameter("Alias").Value;
                ITcpAppServerPlugin item = _Plugins.FirstOrDefault(x => string.Compare(x.Alias, alias, true) == 0);
                if (item == null)
                {
                    sender.OutputMessage = "Plugin [" + alias + "] not found / disposed.";
                    sender.Status        = TcpAppCommandStatus.OK; return;
                }
                else
                {
                    if (item.DisposeRequest() == true)
                    {
                        _Plugins.Remove(item);
                        PluginDisposed?.Invoke(this, new TcpAppServerEventArgs(sender.AppClient)
                        {
                            Plugin = item
                        });
                        sender.OutputMessage = alias + " disposed.";
                        sender.Status        = TcpAppCommandStatus.OK;
                    }
                    else
                    {
                        sender.Status        = TcpAppCommandStatus.ERR;
                        sender.OutputMessage = "Unable to dispose " + alias;
                    }
                }
            },
                                  TcpAppParameter.CreateParameter("Alias", "Plugin alias name."));
        }
        private void Client_ProcessReceivedMessage(TcpServerConnection client, string message, byte[] messageBytes)
        {
            //Parse and Execute Commands
            string[] cmdArg = TcpAppCommon.ParseCommand(message.Trim());

            //Process Command Keyword
            TcpAppCommand cmdHandler = GetCommand(cmdArg[0]);

            if (cmdHandler == null)
            {
                //Error - Unrecognized command.
                client.WriteLineToClient(string.Format("{0} {1} Invalid Command!",
                                                       cmdArg[0], TcpAppCommandStatus.ERR.ToString()));
                return;
            }
            TcpAppInputCommand cmdInput = new TcpAppInputCommand()
            {
                Command = cmdHandler
            };

            cmdInput.Arguments = cmdArg.Skip(1).ToArray(); //Move to TcpAppInputCommand

            //Process Parameters
            cmdHandler.ResetParametersValue();
            int argID = 1; //First Parameter

            foreach (TcpAppParameter item in cmdHandler.Parameters)
            {
                if (argID >= cmdArg.Length)
                {
                    //Argument with no input
                    if (!item.IsOptional)
                    {
                        //Error - Missing required parameter
                        cmdInput.OutputMessage = "Missing required parameter: " + item.Name + "!";
                        WriteResultToClient(client, cmdInput);
                        return;
                    }
                }
                else
                {
                    item.Value = cmdArg[argID]; //Assign parameter value
                }
                argID++;
            }

            //Execute Commands
            try
            {
                cmdInput.Command.ExecuteCallback(cmdInput);
            }
            catch (Exception ex)
            {
                //Catch and report all execution error
                cmdInput.OutputMessage = "Exception Raised! " + ex.Message;
                cmdInput.Status        = TcpAppCommandStatus.ERR; //Force status to error, make sure no surprise.
            }
            finally
            {
                WriteResultToClient(client, cmdInput); //Send result back to client.
            }
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="mainForm">Main Form Handle</param>
        public TcpAppServer(Form mainForm) : base()
        {
            MainForm                 = mainForm;
            MessageDelimiter         = Convert.ToByte(Convert.ToChar(TcpAppCommon.Delimiter));
            base.ClientConnected    += TcpAppServer_ClientConnected;
            base.ClientDisconnected += TcpAppServer_ClientDisconnected;

            //TcpAppServer Format:
            // TX: TCP: <Command> [-Param0] [-Param1] ... [-ParamN]
            // RX: TCP: <Command> <Status> [Return Message]
            // Source - [email protected]:23
            // Command - Registered Command.

            //Register System Commands
            //--- INIT (Commands used by TcpAppClient) ---
            RegisterCommand("TcpAppInit", "Initialize TCP Application.", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = string.IsNullOrEmpty(WelcomeMessage) ? Application.ProductName + " " + Application.ProductVersion : WelcomeMessage;
                sender.Status        = TcpAppCommandStatus.OK;
            });
            RegisterCommand("TcpAppVersion?", "Get TcpAppServer Library Version", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = Version.ToString();
                sender.Status        = TcpAppCommandStatus.OK;
            });
            RegisterCommand("FunctionList?", "Get list of registered functions.", delegate(TcpAppInputCommand sender)
            {
                foreach (TcpAppCommand x in Commands)
                {
                    sender.OutputMessage += x.Keyword;
                    sender.OutputMessage += " ";
                }
                sender.Status = TcpAppCommandStatus.OK;
            });

            //-- APP INFO --
            RegisterCommand("ApplicationName?", "Get Application Name.", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = Application.ProductName;
                sender.Status        = TcpAppCommandStatus.OK;
            });
            RegisterCommand("ApplicationVersion?", "Get Application Version.", delegate(TcpAppInputCommand sender)
            {
                sender.OutputMessage = Application.ProductVersion;
                sender.Status        = TcpAppCommandStatus.OK;
            });

            //--- PLUGIN ---
            RegisterCommand("PluginTypes?", "Get list of plugin class type. Use CreateObject command to instantiate type.", delegate(TcpAppInputCommand sender)
            {
                if (PluginTypes.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(" ", PluginTypes.Keys.ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterCommand("CreateObject", "Create an object from listed plugin types.", delegate(TcpAppInputCommand sender)
            {
                string typeName  = sender.Command.Parameter("TypeName").Value;
                string aliasName = sender.Command.Parameter("Alias").Value;

                //Sanity Check - Type Name
                Type pluginType = PluginTypes[PluginTypes.Keys.FirstOrDefault(x => string.Compare(x, typeName, true) == 0)];

                if (!PluginTypes.Keys.Contains(typeName, StringComparer.InvariantCultureIgnoreCase))
                {
                    throw new ArgumentException("Unknown plugin type: " + typeName);
                }

                //Sanity Check - Alias Name
                if (Plugins.FirstOrDefault(x => string.Compare(x.Alias, aliasName, true) == 0) != null)
                {
                    throw new ArgumentException("Unable to create object with alias '" + aliasName + "'. Object already exists!");
                }

                ITcpAppServerPlugin pluginInstance = Activator.CreateInstance(pluginType) as ITcpAppServerPlugin;
                pluginInstance.Alias = aliasName;
                Plugins.Add(pluginInstance);

                sender.OutputMessage = "Object created " + aliasName;
                sender.Status        = TcpAppCommandStatus.OK;
            },
                            new TcpAppParameter("TypeName", "Plugin type name."),
                            new TcpAppParameter("Alias", "Plugin object Alias Name. Alias name is case insensitive"));
            RegisterCommand("Objects?", "Return object list by alias name.", delegate(TcpAppInputCommand sender)
            {
                if (Plugins.Count == 0)
                {
                    sender.OutputMessage = "-NONE-";
                }
                else
                {
                    sender.OutputMessage = string.Join(" ", Plugins.Select(x => x.Alias).ToArray());
                }
                sender.Status = TcpAppCommandStatus.OK;
            });
            RegisterCommand("Execute", "Exceute plugin's command.", delegate(TcpAppInputCommand sender)
            {
                ITcpAppServerPlugin plugin = Plugins.FirstOrDefault(x => string.Compare(x.Alias, sender.Command.Parameter("Alias").Value, true) == 0);
                if (plugin == null)
                {
                    throw new ArgumentNullException("Plugin not exists!");
                }
                TcpAppInputCommand pluginCommand = new TcpAppInputCommand()
                {
                    Arguments = sender.Arguments
                };
                plugin.ExecutePluginCommand(pluginCommand);

                sender.Status        = pluginCommand.Status;
                sender.OutputMessage = pluginCommand.OutputMessage;
            },
                            new TcpAppParameter("Alias", "Plugin object Alias Name."));
            RegisterCommand("DisposeObject", "Delete object by alias name.", delegate(TcpAppInputCommand sender)
            {
                string alias             = sender.Command.Parameter("Alias").Value;
                ITcpAppServerPlugin item = Plugins.FirstOrDefault(x => string.Compare(x.Alias, alias, true) == 0);
                if (item == null)
                {
                    sender.OutputMessage = "Object [" + alias + "] not found / disposed.";
                    sender.Status        = TcpAppCommandStatus.OK; return;
                }
                else
                {
                    if (item.DisposeRequest() == true)
                    {
                        Plugins.Remove(item);
                        sender.OutputMessage = alias + " disposed.";
                        sender.Status        = TcpAppCommandStatus.OK;
                    }
                    else
                    {
                        sender.Status        = TcpAppCommandStatus.ERR;
                        sender.OutputMessage = "Unable to dispose " + alias;
                    }
                }
            },
                            new TcpAppParameter("Alias", "Object alias name."));

            //-- GUI Control --
            string ErrMainFormNull = "Main Form not assigned!";

            RegisterCommand("MaximizeWindow", "Maximize Application Window.", delegate(TcpAppInputCommand sender)
            {
                if (MainForm != null)
                {
                    MainForm.WindowState = FormWindowState.Maximized;
                    sender.Status        = TcpAppCommandStatus.OK;
                }
                else
                {
                    sender.OutputMessage = ErrMainFormNull;
                }
            });
            RegisterCommand("MinimizeWindow", "Minimize Application Window.", delegate(TcpAppInputCommand sender)
            {
                if (MainForm != null)
                {
                    MainForm.WindowState = FormWindowState.Minimized;
                    sender.Status        = TcpAppCommandStatus.OK;
                }
                else
                {
                    sender.OutputMessage = ErrMainFormNull;
                }
            });
            RegisterCommand("RestoreWindow", "Restore Application Window.", delegate(TcpAppInputCommand sender)
            {
                if (MainForm != null)
                {
                    MainForm.WindowState = FormWindowState.Normal;
                    sender.Status        = TcpAppCommandStatus.OK;
                }
                else
                {
                    sender.OutputMessage = ErrMainFormNull;
                }
            });
            RegisterCommand("BringToFront", "Set Application Window as Top Most.", delegate(TcpAppInputCommand sender)
            {
                if (MainForm != null)
                {
                    MainForm.BringToFront();
                    sender.Status = TcpAppCommandStatus.OK;
                }
                else
                {
                    sender.OutputMessage = ErrMainFormNull;
                }
            });
            RegisterCommand("SetWindowPosition", "Set Window Position.", delegate(TcpAppInputCommand sender)
            {
                if (MainForm != null)
                {
                    MainForm.Location = new System.Drawing.Point(
                        Convert.ToInt16(sender.Command.Parameter("X").Value),
                        Convert.ToInt16(sender.Command.Parameter("Y").Value));
                    sender.Status = TcpAppCommandStatus.OK;
                }
                else
                {
                    sender.OutputMessage = ErrMainFormNull;
                }
            },
                            new TcpAppParameter("X", "Upper left X coordinate of main form."),
                            new TcpAppParameter("Y", "Upper left Y coordinate of main form."));

            RegisterCommand("Terminate", "Terminate Application. Default Exit Code = -99", delegate(TcpAppInputCommand sender)
            {
                int exitCode = -99;
                try
                {
                    exitCode = Convert.ToInt32(sender.Command.Parameter("ExitCode").Value);
                }
                catch { }
                sender.Status = TcpAppCommandStatus.OK;

                Environment.ExitCode = exitCode;
                System.Threading.Thread ptrThread = new System.Threading.Thread(TerminateApplication);
                ptrThread.Start();
            },
                            new TcpAppParameter("ExitCode", "Assign Exit Code for application termination.", "-99"));

            //-- User Interaction --
            RegisterCommand("Help", "Show help screen.", ShowHelp,
                            new TcpAppParameter("Alias", "Object Alias Name", "-"));
        }