/// <summary>
 /// Initializes a new instance of the <see cref="ReadBufferIoResult"/> class.
 /// Constructor, given a plain IoResult, plus type and origin.
 /// </summary>
 /// <param name="r">Plain IoResult.</param>
 /// <param name="type">ASCII or EBCDIC mode.</param>
 /// <param name="origin">Coordinate origin.</param>
 public ReadBufferIoResult(IoResult r, ReadBufferType type, int origin)
     : base(r)
 {
     // Initialize the ReadBufferType.
     this.ReadBufferType = type;
     this.Origin         = origin;
 }
Beispiel #2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="IoResult"/> class.
 /// Cloning constructor.
 /// </summary>
 /// <param name="r">IoResult to clone.</param>
 public IoResult(IoResult r)
 {
     this.Success       = r.Success;
     this.Result        = r.Result;
     this.Command       = r.Command;
     this.StatusLine    = r.StatusLine;
     this.ExecutionTime = r.ExecutionTime;
     this.Encoding      = r.Encoding;
 }
        /// <summary>
        /// Store a new command status.
        /// </summary>
        /// <param name="result">Result to store.</param>
        /// <returns>The <paramref name="result"/> parameter.</returns>
        private IoResult SaveRecentCommand(IoResult result)
        {
            lock (this.recentCommandsLock)
            {
                this.recentCommands.AddFirst(result);
                if (this.recentCommands.Count() > MaxCommands)
                {
                    this.recentCommands.RemoveLast();
                }
            }

            return(result);
        }
        /// <summary>
        /// Run the emulator Query action, asynchronous version.
        /// </summary>
        /// <param name="queryType">Type of query.</param>
        /// <returns>Success/failure and failure text.</returns>
        /// <exception cref="InvalidOperationException">Session is not started.</exception>
        /// <exception cref="X3270ifCommandException"><see cref="ExceptionMode"/> is enabled and the command fails.</exception>
        public async Task <IoResult> QueryAsync(QueryType queryType)
        {
            var result = await this.IoAsync("Query(" + queryType.ToString() + ")").ConfigureAwait(continueOnCapturedContext: false);

            if (queryType == QueryType.Cursor && result.Success)
            {
                // Translate the cursor coordinates.
                // This violates the idea that the ioResult always contains the actual value returned by the emulator,
                // but because we clone the result here, the history will have a reference to the original value.
                var rowCol = result.Result[0].Split(' ');
                result        = new IoResult(result);
                result.Result = new[] { string.Format("{0} {1}", int.Parse(rowCol[0]) + this.Config.Origin, int.Parse(rowCol[1]) + this.Config.Origin) };
            }

            return(result);
        }
Beispiel #5
0
        /// <summary>
        /// Basic emulator I/O function, asynchronous version.
        /// Given a command string and an optional timeout, send it and return the reply.
        /// The emulator status is saved in the session and can be queried after.
        /// </summary>
        /// <param name="command">The command and parameters to pass to the emulator.
        /// Must be formatted correctly for the emulator. This method does no translation.</param>
        /// <param name="timeoutMsec">Optional timeout. The emulator session will be stopped if the timeout expires, so this
        /// is a dead-man timer, not to be used casually.</param>
        /// <param name="isModify">True if command modifies the host</param>
        /// <returns>I/O result.</returns>
        /// <exception cref="InvalidOperationException">Session is not started.</exception>
        /// <exception cref="X3270ifCommandException"><see cref="ExceptionMode"/> is enabled and the command fails.</exception>
        /// <exception cref="ArgumentException"><paramref name="command"/> contains control characters.</exception>
        public async Task <IoResult> IoAsync(string command, int?timeoutMsec = null, bool isModify = false)
        {
            string[] reply = null;

            if (!this.EmulatorRunning)
            {
                throw new InvalidOperationException("Not running");
            }

            // If this is a screen-modifying command, see if a forced failure is in order.
            if (isModify)
            {
                var result = this.ForceModifyFailure();
                if (!result.Success)
                {
                    if (this.ExceptionMode)
                    {
                        throw new X3270ifCommandException(result.Result[0]);
                    }

                    return(result);
                }
            }

            // Control characters are verboten.
            if (command.Any(c => char.IsControl(c)))
            {
                throw new ArgumentException("command contains control character(s)");
            }

            Util.Log("Io: command '{0}'", command);

            // Write out the command.
            byte[] nl        = this.encoding.GetBytes(command + "\n");
            var    stopwatch = new Stopwatch();

            stopwatch.Start();

            var state = IoStates.Waiting;
            var accum = new StringBuilder();

            var  tokenSource = new CancellationTokenSource();
            Task timeoutTask = null;

            try
            {
                var ns = this.Client.GetStream();
                await ns.WriteAsync(nl, 0, nl.Length).ConfigureAwait(continueOnCapturedContext: false);

                // Create a task to time out the read operations after <n> seconds, in case the emulator hangs or we get confused.
                int thisTimeoutMsec = timeoutMsec ?? this.Config.DefaultTimeoutMsec;
                if (thisTimeoutMsec > 0)
                {
                    timeoutTask = Task.Run(async delegate
                    {
                        // When the timeout expires, close the socket.
                        await Task.Delay(thisTimeoutMsec, tokenSource.Token).ConfigureAwait(continueOnCapturedContext: false);
                        this.Client.Close();
                    });
                }

                // Read until we get a prompt.
                var buf = new byte[1024];
                while (state == IoStates.Waiting)
                {
                    var nr = await ns.ReadAsync(buf, 0, buf.Length).ConfigureAwait(continueOnCapturedContext: false);

                    if (nr == 0)
                    {
                        state = IoStates.Crashed;
                        break;
                    }

                    accum.Append(this.encoding.GetString(buf, 0, nr));
                    if (accum.ToString().EndsWith("\nok\n"))
                    {
                        state = IoStates.Succeeded;
                    }
                    else if (accum.ToString().EndsWith("\nerror\n"))
                    {
                        state = IoStates.Failed;
                    }
                }
            }
            catch (ObjectDisposedException)
            {
                // This seems to happen when we Close a TcpClient.
                reply = new[] { "Operation timed out" };
                state = IoStates.Crashed;
            }
            catch (SocketException)
            {
                // Server process died, for example.
                reply = new[] { "Socket exception" };
                state = IoStates.Crashed;
            }
            catch (InvalidOperationException)
            {
                // Also happens when the server process dies; the socket is no longer valid.
                reply = new[] { "Invalid operation exception" };
                state = IoStates.Crashed;
            }
            catch (System.IO.IOException)
            {
                // Also happens when the server process dies; the socket is no longer valid.
                reply = new[] { "I/O exception" };
                state = IoStates.Crashed;
            }

            // All done talking to the server. Stop timing.
            stopwatch.Stop();

            if (timeoutTask != null)
            {
                // Cancel the timeout. Yes, there is a race here, but if we lose, it means that
                // the emulator took a long time to answer, and something is wrong.
                tokenSource.Cancel();

                // Collect the status of the timeout task.
                try
                {
                    timeoutTask.Wait();
                }
                catch (Exception)
                {
                }
            }

            Util.Log("Io: {0}, got '{1}'", state, accum.ToString().Replace("\n", "<\\n>"));

            string statusLine = null;

            if (state != IoStates.Crashed)
            {
                // The array looks like:
                //  data: xxx
                //  data: ...
                //  status line
                //  ok or error
                //  (empty line)

                // Remember the status.
                reply = accum.ToString().Split('\n');
                var nlines = reply.Length;
                statusLine = reply[nlines - 3];

                // Return everything else, removing the "data: " from the beginning.
                Array.Resize(ref reply, nlines - 3);
                for (int i = 0; i < reply.Length; i++)
                {
                    const string DataPrefix = "data: ";
                    if (reply[i].StartsWith(DataPrefix))
                    {
                        reply[i] = reply[i].Substring(DataPrefix.Length);
                    }
                }
            }

            // In Exception Mode, throw a descriptive error if anything ever fails.
            try
            {
                if (this.ExceptionMode && state != IoStates.Succeeded)
                {
                    var    commandAndArgs = command.Split(new char[] { ' ', '(', ')' }, StringSplitOptions.RemoveEmptyEntries);
                    string commandName;
                    if (commandAndArgs.Length > 0 && !string.IsNullOrEmpty(commandAndArgs[0]))
                    {
                        commandName = commandAndArgs[0];
                    }
                    else
                    {
                        commandName = "(empty)";
                    }

                    string failureMessage = string.Format("Command {0} failed:", commandName);
                    if (state == IoStates.Failed)
                    {
                        foreach (string r in reply)
                        {
                            failureMessage += " " + r;
                        }
                    }
                    else
                    {
                        failureMessage += " Timeout or socket EOF";
                    }

                    throw new X3270ifCommandException(failureMessage);
                }
            }
            finally
            {
                // Close the session, whether or not we throw an exception (but after
                // we test for an exception), if the server crashed.
                if (state == IoStates.Crashed)
                {
                    this.Close(saveHistory: true);
                }
            }

            // Save the original response in history.
            var ioResult = new IoResult
            {
                Success       = state == IoStates.Succeeded,
                Result        = reply,
                Command       = command,
                StatusLine    = statusLine,
                ExecutionTime = stopwatch.Elapsed,
                Encoding      = this.encoding
            };

            this.SaveRecentCommand(ioResult);
            this.lastStatusLine = statusLine;

            if (this.Config.Origin == 0 || statusLine == null)
            {
                return(ioResult);
            }

            // Edit the cursor position in the status line for Origin and return it.
            var statusFields = statusLine.Split(' ');

            statusFields[(int)StatusLineField.CursorRow]    = (int.Parse(statusFields[(int)StatusLineField.CursorRow]) + this.Config.Origin).ToString();
            statusFields[(int)StatusLineField.CursorColumn] = (int.Parse(statusFields[(int)StatusLineField.CursorColumn]) + this.Config.Origin).ToString();
            this.lastStatusLine = string.Join(" ", statusFields);
            var retIoResult = new IoResult(ioResult);

            retIoResult.StatusLine = this.lastStatusLine;
            return(retIoResult);
        }
Beispiel #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="EbcdicIoResult"/> class.
 /// </summary>
 /// <param name="r">Result from an <c>Ebcdic</c> call.</param>
 public EbcdicIoResult(IoResult r)
     : base(r)
 {
 }